Integration Guide
Description: aelf-web-login: A modular React wallet collection and components library for aelf applications.
Purpose: To provide installation and configuration instructions for integrating various aelf wallets into a React application using the aelf-web-login library.
Introduction
aelf-web-login: Modular React wallet collection and components for aelf applications.
website: https://aelf-web-login.vercel.app/
Install
yarn add @aelf-web-login/wallet-adapter-night-elf @aelf-web-login/wallet-adapter-portkey-aa @aelf-web-login/wallet-adapter-portkey-discover @aelf-web-login/wallet-adapter-react @aelf-web-login/wallet-adapter-base @aelf-web-login/wallet-adapter-bridge
Then the package.json will be like this
"dependencies": {
    "@aelf-web-login/wallet-adapter-night-elf": "^0.0.2-alpha.7",
    "@aelf-web-login/wallet-adapter-portkey-aa": "^0.0.2-alpha.7",
    "@aelf-web-login/wallet-adapter-portkey-discover": "^0.0.2-alpha.7",
    "@aelf-web-login/wallet-adapter-react": "^0.0.2-alpha.7",
    "@aelf-web-login/wallet-adapter-base": "^0.0.2-alpha.7",
    "@aelf-web-login/wallet-adapter-bridge": "^0.0.2-alpha.7",
}
Config
- Import 
PortkeyDiscoverWallet,PortkeyAAWalletandNightElfWalletand generate instance - Create 
didConfig(internal invoke:ConfigProvider.setGlobalConfig(didConfig)) - Create 
baseConfigforSignIncomponent - Create 
walletsby wallet instance - Combine them into a whole as 
config 
import { PortkeyDiscoverWallet } from '@aelf-web-login/wallet-adapter-portkey-discover';
import { PortkeyAAWallet } from '@aelf-web-login/wallet-adapter-portkey-aa';
import { NightElfWallet } from '@aelf-web-login/wallet-adapter-night-elf';
import { IConfigProps } from '@aelf-web-login/wallet-adapter-bridge';
import { TChainId, SignInDesignEnum, NetworkEnum } from '@aelf-web-login/wallet-adapter-base';
const APP_NAME = 'explorer.aelf.io';
const WEBSITE_ICON = 'https://explorer.aelf.io/favicon.main.ico';
const CHAIN_ID = 'AELF' as TChainId;
const NETWORK_TYPE = NetworkEnum.TESTNET;
const RPC_SERVER_AELF = 'https://aelf-test-node.aelf.io';
const RPC_SERVER_TDVV = 'https://tdvv-public-node.aelf.io';
const RPC_SERVER_TDVW = 'https://tdvw-test-node.aelf.io';
const GRAPHQL_SERVER = 'https://dapp-aa-portkey-test.portkey.finance/Portkey_DID/PortKeyIndexerCASchema/graphql';
const CONNECT_SERVER = 'https://auth-aa-portkey-test.portkey.finance';
const didConfig = {
  graphQLUrl: GRAPHQL_SERVER,
  connectUrl: CONNECT_SERVER,
  requestDefaults: {
    baseURL: 'https://aa-portkey-test.portkey.finance',
    timeout: 30000,
  },
  socialLogin: {
    Portkey: {
      websiteName: APP_NAME,
      websiteIcon: WEBSITE_ICON,
    },
  },
};
const baseConfig = {
  networkType: NETWORK_TYPE,
  chainId: CHAIN_ID,
  keyboard: true,
  noCommonBaseModal: false,
  design: SignInDesignEnum.CryptoDesign, // "SocialDesign" | "CryptoDesign" | "Web2Design"
  titleForSocialDesign: 'Crypto wallet',
  iconSrcForSocialDesign: 'url or base64',
};
const wallets = [
  new PortkeyAAWallet({
    appName: APP_NAME,
    chainId: CHAIN_ID,
    autoShowUnlock: true,
  }),
  new PortkeyDiscoverWallet({
    networkType: NETWORK_TYPE,
    chainId: CHAIN_ID,
    autoRequestAccount: true,
    autoLogoutOnDisconnected: true,
    autoLogoutOnNetworkMismatch: true,
    autoLogoutOnAccountMismatch: true,
    autoLogoutOnChainMismatch: true,
  }),
  new NightElfWallet({
    chainId: CHAIN_ID,
    appName: APP_NAME,
    connectEagerly: true,
    defaultRpcUrl: RPC_SERVER_AELF,
    nodes: {
      AELF: {
        chainId: 'AELF',
        rpcUrl: RPC_SERVER_AELF,
      },
      tDVW: {
        chainId: 'tDVW',
        rpcUrl: RPC_SERVER_TDVW,
      },
      tDVV: {
        chainId: 'tDVV',
        rpcUrl: RPC_SERVER_TDVV,
      },
    },
  }),
]
const config: IConfigProps = {
  didConfig,
  baseConfig,
  wallets
};
Usage
- Import 
WebLoginProvider,initanduseConnectWallet - invoke 
initwith upper config as params - pass the return value 
bridgeAPItoWebLoginProvider - use 
useConnectWalletto consumebridgeAPI 
import { WebLoginProvider, init, useConnectWallet } from '@aelf-web-login/wallet-adapter-react';
const App = () => {
  const bridgeAPI = init(config); // upper config
  return (
    <WebLoginProvider bridgeAPI={bridgeAPI}>
      <Demo />
    </WebLoginProvider>
  );
};
const Demo = () => {
  const {
    connectWallet,
    disConnectWallet,
    walletInfo,
    lock,
    isLocking,
    isConnected,
    loginError,
    walletType,
    getAccountByChainId,
    getWalletSyncIsCompleted,
    getSignature,
    callSendMethod,
    callViewMethod
  } = useConnectWallet();
}
API
connectWallet
connectWallet: () => Promise<TWalletInfo>
Connect wallet and return walletInfo
import { Button } from 'aelf-design';
const Demo = () => {
    const { connectWallet } = useConnectWallet();
    const onConnectBtnClickHandler = async() => {
        try {
          const rs = await connectWallet();
        } catch (e: any) {
          console.log(e.message)
        }
    }
    return (
        <Button onClick={onConnectBtnClickHandler}>connect</Button>
    )
}
disConnectWallet
disConnectWallet: () => Promise<void>
Disconnect wallet
import { Button } from 'aelf-design';
const Demo = () => {
    const { disConnectWallet } = useConnectWallet();
    const onDisConnectBtnClickHandler = () => {
        disConnectWallet()
    }
    return (
        <Button onClick={onDisConnectBtnClickHandler}>disConnect</Button>
    )
}
lock
lock: () => void
Lock wallet, only portkeyAA wallet take effect
import { Button } from 'aelf-design';
const Demo = () => {
    const { lock } = useConnectWallet();
    return (
        <Button onClick={lock}>lock</Button>
    )
}
getAccountByChainId
getAccountByChainId: (chainId: TChainId) => Promise<string>
Get account address of designative chainId
import { Button } from 'aelf-design';
const Demo = () => {
    const { getAccountByChainId } = useConnectWallet();
    const getAelfAccountHandler = async() => {
        const address = await getAccountByChainId('AELF')
        console.log(address)
    }
    const getTdvwAccountHandler = async() => {
        const address = await getAccountByChainId('tDVW')
        console.log(address)
    }
    return (
        <Button onClick={getAelfAccountHandler}>account-AELF</Button>
        <Button onClick={getTdvwAccountHandler}>account-tDVW</Button>
    )
}
getWalletSyncIsCompleted
getWalletSyncIsCompleted: (chainId: TChainId) => Promise<string | boolean>
Return account address of designative chainId if sync is competed, otherwise return false
import { Button } from 'aelf-design';
const Demo = () => {
    const { getWalletSyncIsCompleted } = useConnectWallet();
    const getAelfSyncIsCompletedHandler = async() => {
        const address = await getWalletSyncIsCompleted('AELF')
        console.log(address)
    }
    const getTdvwSyncIsCompletedHandler = async() => {
        const address = await getWalletSyncIsCompleted('tDVW')
        console.log(address)
    }
    return (
        <Button onClick={getAelfSyncIsCompletedHandler}>sync-AELF</Button>
        <Button onClick={getTdvwSyncIsCompletedHandler}>sync-tDVW</Button>
    )
}
getSignature
const getSignature: (params: TSignatureParams) => Promise<{ ``    error: number; ``    errorMessage: string; ``    signature: string; ``    from: string; ``} | null>
Get signature message
import { Button, Input } from 'aelf-design';
type TSignatureParams = {
  appName: string;
  address: string;
  signInfo: string;
  hexToBeSign?: string;
};
const Demo = () => {
    const { getSignature } = useConnectWallet();
    const [signInfo, setSignInfo] = useState('');
    const [signedMessage, setSignedMessage] = useState('');
    const signHandler = async () => {
      const sign = await getSignature({
        signInfo,
        appName: '',
        address: '',
      });
      setSignedMessage(sign.signature);
    };
    return (
        div>
          <div>
            <Button onClick={signHandler}>
              Sign
            </Button>
            <Input value={signInfo} onChange={(e) => setSignInfo(e.target.value)} />
            <div>{signedMessage}</div>
          </div>
        </div>
    )
}
callSendMethod
callSendMethod: <T, R>(props: ICallContractParams<T>) => Promise<R>
Call contract's send method
import { Button } from 'aelf-design';
interface ICallContractParams<T> {
  contractAddress: string;
  methodName: string;
  args: T;
  chainId?: TChainId;
  sendOptions?: SendOptions;
}
const Demo = () => {
    const { callSendMethod } = useConnectWallet();
    const [result, setResult] = useState({});
    const onApproveHandler = async() => {
        const res = await callSendMethod({
          chainId: 'tDVW',
          contractAddress: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE',
          methodName: 'Approve',
          args: {
            symbol: 'ELF',
            spender: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE',
            amount: '100000000',
          },
        });
        setResult(res);
    }
    return (
      <div>
        <Button onClick={onApproveHandler}>Approve in tDVW</Button>
        <div>
          <h4>Result</h4>
          <pre className="result">{JSON.stringify(result, null, '  ')}</pre>
        </div>
      </div>
    )
}
callViewMethod
callViewMethod: <T, R>(props: ICallContractParams<T>) => Promise<R>
Call contract's view method
import { Button } from 'aelf-design';
interface ICallContractParams<T> {
  contractAddress: string;
  methodName: string;
  args: T;
  chainId?: TChainId;
  sendOptions?: SendOptions; // only send method use, ignore in view method
}
const Demo = () => {
    const { callViewMethod, getAccountByChainId } = useConnectWallet();
    const [result, setResult] = useState({});
    const onGetBalanceHandler = async() => {
        const res = await callViewMethod({
          chainId: 'tDVW',
          contractAddress: 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx',
          methodName: 'GetBalance',
          args: {
            symbol: 'ELF',
            owner: await getAccountByChainId('tDVW'),
          },
        });
        setResult(res);
    }
    return (
      <div>
        <Button onClick={onGetBalanceHandler}>GetBalance in tDVW</Button>
        <div>
          <h4>Result</h4>
          <pre className="result">{JSON.stringify(result, null, '  ')}</pre>
        </div>
      </div>
    )
}
walletInfo
const walletInfo: TWalletInfo
Wallet information after connecting wallet, can import
TWalletInfofrom@aelf-web-login/wallet-adapter-base
type TWalletInfo =
  | {
      name?: string;
      address: string;
      extraInfo?: {
        [key: string]: any;
      };
    }
  | undefined;
  // walletInfo returned by nightElf
  {
    name,
    address,
    extraInfo: {
      publicKey,
      nightElfInfo: {
        name,
        appPermission,
        defaultAElfBridge: bridge,
        aelfBridges: bridges,
        nodes,
      },
    },
  }
  // walletInfo returned by portkeyAA
  import { DIDWalletInfo } from '@portkey/did-ui-react';
  {
    name,
    address,
    extraInfo: {
      publicKey,
      portkeyInfo: {
        ...DIDWalletInfo
        accounts: {
          [chainId]: didWalletInfo.caInfo?.caAddress,
        },
        nickName,
      },
    },
  }
  // walletInfo returned by portkeyDiscover
  import type { Accounts, IPortkeyProvider } from '@portkey/provider-types';
  {
    address,
    extraInfo: {
      accounts: Accounts,
      nickName,
      provider: IPortkeyProvider,
    },
  }
const Demo = () => {
    const { walletInfo } = useConnectWallet();
    console.log(walletInfo)
    return null
}
walletType
const walletType: WalletTypeEnum
The currently connected wallet type, can import
WalletTypeEnumfrom@aelf-web-login/wallet-adapter-base
enum WalletTypeEnum {
  unknown = 'Unknown',
  elf = 'NightElf',
  aa = 'PortkeyAA',
  discover = 'PortkeyDiscover',
}
const Demo = () => {
    const { walletType } = useConnectWallet();
    console.log(walletType)
    return null
}
isLocking
const isLocking: boolean
indicate whether the current state is locked, only portkeyAA wallet take effect, other wallets always return false
import { Button } from 'aelf-design';
const Demo = () => {
    const { isLocking } = useConnectWallet();
    return (
      <Button>
        {isLocking ? 'unlock' : 'connect'}
      </Button>
    )
}
isConnected
const isConnected: boolean
indicate whether the current state is connected
import { Button } from 'aelf-design';
const Demo = () => {
    const { isConnected } = useConnectWallet();
    return (
      <div>
        <Button disabled={isConnected}>connect</Button>
        <Button disabled={!isConnected}>disConnect</Button>
      </div>
    )
}
loginError
const loginError: TWalletError | null
indicate are there any errors during the login/logout/unlock process
type TWalletError = {
  name: string;
  code: number;
  message: string;
  nativeError?: any;
}
const Demo = () => {
  const { loginError } = useConnectWallet();
  useEffect(() => {
    if (!loginError) {
      return;
    }
    console.log(loginError.message);
  }, [loginError]);
  return null
}
Development
- Install dependencies in the project root directory
 
pnpm install
- cd to demo directory and execute dev command
 
cd packages/starter
pnpm dev
Publish
- Upgrade the version numbers of each sub package
 - execute release command in the project root directory
 
pnpm release