0.3.1 • Published 10 months ago

react-ic-wallet v0.3.1

Weekly downloads
-
License
MIT
Repository
github
Last release
10 months ago

React IC Wallet

NPM CI

React IC Wallet is a Simplistic Context provider in order to manage Internet Computer wallets in the browser. It is heavily inspired by Metamask react.

Supported wallets

Currently, React-ic-wallet supports the following browser wallets:

Usage

Wrap your application

Wrap your application around the IcWalletProvider

import { IcWalletProvider } from 'react-ic-wallet';

<IcWalletProvider>
  <App />
</IcWalletProvider>

Use a specific wallet

import { IcWalletProvider, WalletProvider } from 'react-ic-wallet';

<IcWalletProvider provider={WalletProvider.Bitfinity}>
  <App />
</IcWalletProvider>

Create a component to handle the connection

import * as React from 'react';
import { useIcWallet } from 'react-ic-wallet';

import Logo from './ConnectButton/Logo';
import Button from './reusable/Button';
import Container from './reusable/Container';

const ConnectButton = () => {
  const { status, connect, disconnect, account, principal } = useIcWallet();

  const disabled = ['initializing', 'unavailable', 'connecting'].includes(
    status,
  );

  React.useEffect(() => {
    console.log('status from ctx', status);
  }, [status]);

  const onClick = () => {
    if (status === 'notConnected') {
      return connect();
    } else if (status === 'connected') {
      return disconnect();
    }
    return undefined;
  };

  const text = () => {
    if (status === 'initializing') return 'Initializing...';
    if (status === 'unavailable') return 'IC Wallet not available';
    if (status === 'notConnected') return 'Connect to IC';
    if (status === 'connecting') return 'Connecting...';
    if (status === 'connected') return principal;
    return undefined;
  };

  return (
    <Container.FlexRow className="items-center gap-8">
      <Button.Alternative
        className="my-0 !mb-0"
        onClick={onClick}
        disabled={disabled}
      >
        <Logo className="inline w-[32px] mr-2" />
        {text()}
      </Button.Alternative>
    </Container.FlexRow>
  );
};

export default ConnectButton;

Create actor to interact with canisters

import * as React from 'react';
import { ActorMethod, ActorSubclass } from '@dfinity/agent';
import { useIcWallet } from 'react-ic-wallet';

import { icpLedgerIdlFactory } from './IcpLedger';

interface Context {
  icpLedger?: ActorSubclass<Record<string, ActorMethod>>;
}

export const AgentContext = React.createContext<Context>({
  icpLedger: undefined,
});

export const icpLedgerIdlFactory = ({ IDL: IDL }) => {
  ...
  return IDL.Service({
    icrc1_balance_of: IDL.Func([Account], [IDL.Nat], ['query']),
    icrc1_decimals: IDL.Func([], [IDL.Nat8], ['query']),
    icrc1_fee: IDL.Func([], [IDL.Nat], ['query']),
    icrc1_metadata: IDL.Func(
      [],
      [IDL.Vec(IDL.Tuple(IDL.Text, MetadataValue))],
      ['query'],
    ),
    icrc1_name: IDL.Func([], [IDL.Text], ['query']),
    icrc1_supported_standards: IDL.Func(
      [],
      [IDL.Vec(TokenExtension)],
      ['query'],
    ),
    icrc1_symbol: IDL.Func([], [IDL.Text], ['query']),
    icrc1_total_supply: IDL.Func([], [IDL.Nat], ['query']),
    icrc1_transfer: IDL.Func([TransferArg], [Result_5], []),
    icrc2_allowance: IDL.Func([AllowanceArgs], [Allowance], ['query']),
    icrc2_approve: IDL.Func([ApproveArgs], [Result_6], []),
    icrc2_transfer_from: IDL.Func([TransferFromArgs], [Result_7], []),
  });
}

const AgentContextProvider = ({ children }: { children: React.ReactNode }) => {
  const [icpLedger, setIcpLedger] =
    React.useState<ActorSubclass<Record<string, ActorMethod>>>();
  const { createActor, status } = useIcWallet();

  React.useEffect(() => {
    if (status === 'connected') {
      createActor('ryjl3-tyaaa-aaaaa-aaaba-cai', icpLedgerIdlFactory)
        .then((actor) => {
          if (actor) {
            setIcpLedger(actor);
          }
        })
        .catch((err) => {
          console.error(err);
        });
    }
  }, [status]);

  return (
    <AgentContext.Provider
      value={{
        icpLedger,
      }}
    >
      {children}
    </AgentContext.Provider>
  );
};

export default AgentContextProvider;

Access methods once connected

import * as React from 'react';
import { useIcWallet } from 'react-ic-wallet';

const Header = () => {
  const { icpLedger } = React.useContext(AgentContext);
  const { principal } = useConnectedIcWallet();
  const [balance, setBalance] = React.useState<string>('0');

  React.useEffect(() => {
    if (icpLedger) {
      icpLedger
        .icrc1_balance_of({
          owner: Principal.fromText(principal),
          subaccount: [],
        })
        .then((balance) => {
          setBalance((balance as bigint).toString());
        })
        .catch((e) => {
          console.error(e);
        });
    }
  }, [icpLedger]);

  return (
    <Container.FlexCols>
      <span>User balance (ICP): {balance}</span>
    </Container.FlexCols>
  );
};

export default Header;

Example

Find more in the example in the examples/ directory, where we use the ic wallet context provider to query the ICP ledger canister.

License

react-ic-wallet is licensed under MIT.

See full license HERE.

0.3.0

10 months ago

0.3.1

10 months ago

0.2.1

10 months ago

0.2.0

10 months ago

0.1.1

10 months ago

0.1.0

10 months ago