aelf-web-login: Modular React wallet collection and components for aelf applications.
website: https://aelf-web-login.vercel.app/
| package | Tests | Coverage |
|---|---|---|
| @aelf-web-login/utils | ||
| @aelf-web-login/base | ||
| @aelf-web-login/bridge | ||
| @aelf-web-login/react |
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 @aelf-web-login/utils @portkey/did-ui-reactThen the package.json will be like this
"dependencies": { "@aelf-web-login/wallet-adapter-night-elf": "^0.1.7", "@aelf-web-login/wallet-adapter-portkey-aa": "^0.1.7", "@aelf-web-login/wallet-adapter-portkey-discover": "^0.1.7", "@aelf-web-login/wallet-adapter-react": "^0.1.7", "@aelf-web-login/wallet-adapter-base": "^0.1.7", "@aelf-web-login/wallet-adapter-bridge": "^0.1.7", "@aelf-web-login/utils": "^0.1.7", "@portkey/did-ui-react": "^2.13.2", }- 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/aefinder-v2/api/app/graphql/portkey'; const CONNECT_SERVER = 'https://auth-aa-portkey-test.portkey.finance'; const SERVICE_SERVER = 'https://aa-portkey-test.portkey.finance'; const TELEGRAM_BOT_ID = 'xx'; const didConfig = { graphQLUrl: GRAPHQL_SERVER, connectUrl: CONNECT_SERVER, serviceUrl: SERVICE_SERVER, requestDefaults: { baseURL: SERVICE_SERVER, timeout: 30000, }, socialLogin: { Portkey: { websiteName: APP_NAME, websiteIcon: WEBSITE_ICON, }, Telegram: { botId: TELEGRAM_BOT_ID, }, }, // customNetworkType: NETWORK_TYPE === 'TESTNET' ? 'offline' : 'online', // loginConfig: { // loginMethodsOrder: [ "Email", "Google" , "Apple" , "Scan"] // } }; const baseConfig = { // ConfirmLogoutDialog: CustomizedConfirmLogoutDialog, // SignInComponent: SignInProxy, // defaultPin: '111111', // PortkeyProviderProps: { // theme: 'light' as any, // }, // omitTelegramScript: false, // cancelAutoLoginInTelegram: false, enableAcceleration: false, networkType: NETWORK_TYPE, showVconsole: false, chainId: CHAIN_ID, keyboard: true, design: SignInDesignEnum.CryptoDesign, // "SocialDesign" | "CryptoDesign" | "Web2Design" }; const wallets = [ new PortkeyAAWallet({ appName: APP_NAME, chainId: CHAIN_ID, autoShowUnlock: true, disconnectConfirm: false, }), new PortkeyDiscoverWallet({ networkType: NETWORK_TYPE, chainId: CHAIN_ID, autoRequestAccount: true, // If set to true, please contact Portkey to add whitelist @Rachel 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, };IConfigProps Clarifications
import { GlobalConfigProps } from '@portkey/did-ui-react/dist/_types/src/components/config-provider/types'; import { SignInProps, ISignIn, PortkeyProvider } from '@portkey/did-ui-react'; import { WalletAdapter } from '@aelf-web-login/wallet-adapter-base'; interface IConfirmLogoutDialogProps { title: string; subTitle: string[]; okTxt: string; cancelTxt: string; visible: boolean; onOk: () => void; onCancel: () => void; width: number; mobileWidth: number; } interface IConfigProps { didConfig: GlobalConfigProps; baseConfig: IBaseConfig; wallets: WalletAdapter[]; } interface IBaseConfig { networkType: NetworkEnum; chainId: TChainId; keyboard?: boolean; design?: SignInDesignEnum; titleForSocialDesign?: string; showVconsole?: boolean; SignInComponent?: React.FC<SignInProps & RefAttributes<ISignIn>>; PortkeyProviderProps?: Partial<Omit<React.ComponentProps<typeof PortkeyProvider>, 'children'>>; ConfirmLogoutDialog?: React.FC<Partial<IConfirmLogoutDialogProps>>; }- 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(); };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: () => Promise<void>;Disconnect wallet
import { Button } from 'aelf-design'; const Demo = () => { const { disConnectWallet } = useConnectWallet(); const onDisConnectBtnClickHandler = () => { disConnectWallet(); }; return <Button onClick={onDisConnectBtnClickHandler}>disConnect</Button>; };lock: () => voidLock wallet, only portkeyAA wallet take effect
import { Button } from 'aelf-design'; const Demo = () => { const { lock } = useConnectWallet(); return <Button onClick={lock}>lock</Button>; };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: (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> ) }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: <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: <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> ); };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 }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; };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>; };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> ); };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; };- Install dependencies in the project root directory
pnpm install- cd to demo directory and execute dev command
cd packages/starter pnpm devOR directly filter workspace package pnpm --filter "@aelf-web-login/doc-site" dev
- Upgrade the version numbers of each sub package
- execute release command in the project root directory
pnpm release