Skip to content

AElfProject/aelf-web-login

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

aelf-web-login: Modular React wallet collection and components for aelf applications.

website: https://aelf-web-login.vercel.app/

Node version NPM Commitizen friendly coverage

package Tests Coverage
@aelf-web-login/utils GitHub Workflow Test Status Coverage
@aelf-web-login/base GitHub Workflow Test Status Coverage
@aelf-web-login/bridge GitHub Workflow Test Status Coverage
@aelf-web-login/react GitHub Workflow Test Status Coverage

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 @aelf-web-login/utils @portkey/did-ui-react

Then 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",
}

Config

  1. Import PortkeyDiscoverWallet, PortkeyAAWallet and NightElfWallet and generate instance
  2. Create didConfig (internal invoke: ConfigProvider.setGlobalConfig(didConfig))
  3. Create baseConfig for SignIn component
  4. Create wallets by wallet instance
  5. 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>>;
}

Usage

  1. Import WebLoginProvider, init and useConnectWallet
  2. invoke init with upper config as params
  3. pass the return value bridgeAPI to WebLoginProvider
  4. use useConnectWallet to consume bridgeAPI
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 TWalletInfo from @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 WalletTypeEnum from @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

  1. Install dependencies in the project root directory
pnpm install
  1. cd to demo directory and execute dev command
cd packages/starter
pnpm dev

OR directly filter workspace package pnpm --filter "@aelf-web-login/doc-site" dev

Publish

  1. Upgrade the version numbers of each sub package
  2. execute release command in the project root directory
pnpm release

About

Modular React wallet collection and components for aelf applications.

Resources

License

Security policy

Stars

Watchers

Forks

Contributors 11