Skip to content
This repository has been archived by the owner on Jun 7, 2024. It is now read-only.

Latest commit

Β 

History

History
390 lines (283 loc) Β· 13.5 KB

README.md

File metadata and controls

390 lines (283 loc) Β· 13.5 KB

πŸ–‡ Eth-hooks Overview

Commonly used Ethereum hooks to supercharge your web3 dev!

Used by 🏭 scaffold-eth-typescript, πŸ— scaffold-eth , eth-components and many other web3 projects!

Created by 🏰 BuidlGuidl.eth

Author

@shravansunder

Documentation

πŸ“š Check out the documentation at Eth-hooks-documentation

Features

See this video summary on v4 features!

EthersAppContext

A context that allows you to access the current ethers.js context and information such as provider, signer, account. This allows your user to easily log into web3 account using web3modal. You can setup up overrides and multiple providers.

ContractAppContext

Gives you a contractContextFactory that allows you to easily setup typed contracts, load typed contracts, create connectors, and access them with hooks anywhere in your app.

Caching & optional updates

Caches the network RPC calls so that unecessary requests to the network is prevented. You can setup an update interval from every block, every (n) blocks, polling, onMount, onWindow focus and other react-query update options.

API Documentation

Check out the documentation at the eth-hooks page!

Hooks!

Ethers App Context

  • useBlockNumberContext
  • useEthersContext

Network

  • useBalance
  • useBlockNumber
  • useContractExistsAtAddress
  • useEventListener
  • useGasPrice
  • useNonce
  • useSignerAddress

Contracts

  • useContractReader
  • contractContextFactory
    • useLoadAppContracts
    • useConnectAppContracts
    • useConnectAppContracts

ERC

  • useTokenBalance

Dapps

  • useDexEthPrice
  • useDexTokenList
  • useResolveEnsAddress
  • useResolveEnsName

Utilities

  • useBurnerSigner

Quickstart

Install

yarn add eth-hooks

Setting up the context for eth-hooks

Add the contexts to your app

<ContractsAppContext>
  <EthersAppContext>
    <YourMainPage />
  </EthersAppContext>
</ContractsAppContext>

You can see an example of providers in scaffold-eth-typescript app.tsx

Using the ethersAppContext & hooks

An example of using the context

const ethersContext = useEthersContext();
// you now have access to signer, provider, account (address), etc...  See IEthersContext for details

An example of using a hook

// ---------------------
// 🏦 get your balance
// ---------------------
// This instance uses the provider from the context in useBalance internally
const [yourLocalBalance, update, status] = useBalance(ethersContext.account);
Z;

An example of changing an update interval

// normally the hooks update every block
const [yourLocalBalance, update, status] = useBalance(ethersContext.account);
// you can change the update schedule to every 10 blocks, the default is every 1 block:
const [yourLocalBalance, update, status] = useBalance(ethersContext.account, { blockNumberInterval: 10 });
// you can change the update schedule to every polling, min is 10000ms
const [yourLocalBalance, update, status] = useBalance(ethersContext.account, {
  refetchInterval: 100000,
  blockNumberInterval: undefined,
});
// you can use advanced react-query update options
const [yourLocalBalance, update, status] = useBalance(ethersContext.account, {
  blockNumberInterval: 1,
  query: { refetchOnWindowFocus: true },
});

An example of overriding the provider from the context

// get an adaptor from a provider or signer
const [mainnetAdaptor] = useEthersAdaptorFromProviderOrSigners(exampleMainnetProvider);
// pass in the override variable
const [yourMainnetBalance] = useBalance(ethersContext.account, mergeDefaultUpdateOptions(), {
  adaptorEnabled: true,
  adaptor: mainnetAdaptor,
});

Check out examples in scaffold-eth-typescript in useScaffoldHooksExamples.tsx

Pass in a provider directly into ethersAppContext

You can pass a provider into EthersAppContext directly if you don't want to use EthersModalConnect and web3Modal. This would be a way to override the default mechansim if you have your own login UI.

<EthersAppContext customGetEthersAppProviderLibrary={customFunction}>
  <YourMainPage />
</EthersAppContext>

In the above example customFunction should be a function that returns a TEthersProvider:

// a simple example
export type TGetEthersAppProviderLibrary = () => TEthersProvider;

// a function that transforms a provider into a TEthersProvider
export type TGetEthersAppProviderLibrary = (
  provider: TEthersProvider | ExternalProvider | JsonRpcFetchFunc | any
) => TEthersProvider;

Using ethersAppContext with web3Modal

Ethers context will automatically give you a way to integrate web3Modal into your app.

1. Create your web3Config

The first create a web3Config. Check out their github repo for a detailed explanation: web3 modal. You could also see the example in scaffold-eth-typescript, scaffold-eth-typescript web3ModalConfig.ts

2. Create a function that returns a TEthersModalConnector

This function should have a signature that returns TEthersModalConnector which is an interface that is implemented by EthersModalConnector

type TCreateEthersModalConnector = (id?: string) => TEthersModalConnector | undefined;

For example in scaffold-eth-typescript createLoginConnector

// theme: can be 'light' or 'dark'
// web3Config: is for web3Modal configuration
// id: allows you to programatically to a provider defined for the modal, see the web3Modal for details.
const createLoginConnector: TCreateEthersModalConnector = useCallback(
  (id?: string) => {
    if (web3Config) {
      const connector = new EthersModalConnector({ ...web3Config, theme: currentTheme }, id);
      return connector;
    }
  },
  [web3Config, currentTheme]
);

You can find the details for EthersModalConnector in the api docs.

3. Create a login event handler

You can then call the function we created above in the the event handler of your login button anywhere in your app.

  ...
  const ethersContext = useEthersContext();

  // to handle a login
  const handleLoginClick = (): void => {
    if (createLoginConnector != null && ethersContext?.openModal != null) {
      const connector = createLoginConnector();
      ethersContext.openModal(connector);
    }
  };

  // to handle a log out
  const handleLogoutClick = (): void => {
    if (ethersContext?.disconnectModal != null) {
      ethersContext.disconnectModal();
    }
  };

Using ContractAppContext

1. Generating types for your contract

The first thing you'll have to do is generate your contract types for hardhat and external contracts. Add eth-sdk or typechain with hardhat to generate that to a folder such as generated/contract-types. Pull scaffold-eth-typescript for an example of this.

An example on how to use eth-sdk for external contracts

scaffold-eth-typescript uses eth-sdk to generate types and abi for external contracts using. See the excellent documentation there for this at eth-sdk github. πŸ“ Note that this would a dev dependency on your project.

An example of using hardhat with typechain

Check out the excellent typechain docs. You can find an example in scaffold-eth-typescript hardhat.config.ts

2. Creating the context with contractsContextFactory

You'll have to create a config that returns a config of your contracts. This would be heterogeneous key value pair. Each value is generated by the helper functions in eth-hooks.

For example:

// a function that generates the config. Note that your types have to exist already!
export const contractConnectorConfig = () => {
  try {
    const result = {
      // πŸ™‹πŸ½β€β™‚οΈ Add your hadrdhat contracts here
      YourContract: createConnectorForHardhatContract(
        'YourContract',
        hardhatContracts.YourContract__factory,
        hardhatContractsJson
      ),

      // πŸ™‹πŸ½β€β™‚οΈ Add your external contracts here, make sure to define the address in `externalContractsConfig.ts`
      DAI: createConnectorForExternalContract('DAI', externalContracts.DAI__factory, externalContractsAddressMap),
      UNI: createConnectorForExternalContract('UNI', externalContracts.UNI__factory, externalContractsAddressMap),

      // πŸ™‹πŸ½β€β™‚οΈ Add your external abi here (unverified contracts)`
      // DAI: createConnectorForExternalAbi('DAI', { 1: {address: 'xxxx'}}, abi),
    } as const;

    return result;
  } catch (e) {
    console.error(
      '❌ contractConnectorConfig: ERROR with loading contracts please run `yarn contracts:build or yarn contracts:rebuild`.  Then run `yarn deploy`!',
      e
    );
  }

  return undefined;
};

// create a type from the return value of the function above
export type TAppConnectorList = NonNullable<ReturnType<typeof contractConnectorConfig>>;

Use contractContextFactory to create your hooks and context in your app from the above configuration. You could just copy the code below and use it.

// you're passing in function `contractConnectorConfig` from above into the factory.  You then have to use the type we defined to type the factory outputs.
export const {
  ContractsAppContext,
  useAppContractsActions,
  useAppContracts,
  useLoadAppContracts,
  useConnectAppContracts,
} = contractsContextFactory<
  /* the contractNames (keys) in config output */
  keyof TAppConnectorList,
  /* the type of the config output  */
  TAppConnectorList,
  /* A type that infers the value of each contractName: contract pair*/
  TTypedContract<keyof TAppConnectorList, TAppConnectorList>
>(contractConnectorConfig);

See scaffold-eth-typescript contractContext.tsx and contractConnectorConfig.ts for full examples on how to do this.

3. Using hooks to get your contracts

Now that you've created the context and hooks above you can use them in your app. The first step is to load your contracts using the hooks you've created with the factory.

// πŸ›» load contracts
useLoadAppContracts();

Next you'll want to connect the contracts.

// 🏭 connect to  contracts for current network & signer
useConnectAppContracts(asEthersAdaptor(ethersContext));

// 🏭 connect to contracts for mainnet network & signer
const [mainnetAdaptor] = useEthersAdaptorFromProviderOrSigners(mainnetProvider);
useConnectAppContracts(mainnetAdaptor);

Now you can get typed contracts anywhere in your app

const yourContract = useAppContracts('YourContract', ethersContext.chainId);
const mainnetDai = useAppContracts('DAI', NETWORKS.mainnet.chainId);

You can read values from the contracts using the useContractReader hook

// keep track of a variable from the contract in the local React state:
const [purpose, update] = useContractReader(
  /* the contract */
  yourContract,
  /* the contract variable or function to read */
  yourContract?.purpose,
  /* the arguments, they are typed tuple */
  [],
  /* optional: if you want your contracts to only update on event */
  yourContract?.filters.SetPurpose()
);

// keep track of a variable from the contract in the local React state:
const [purpose, update] = useContractReader(
  /* the contract */
  yourContract,
  /* the contract variable or function to read */
  yourContract?.purpose,
  /* the arguments, they are typed tuple */
  [],
  undefined,
  /* optional: update every 10 blocks */
  { blockNumberInterval: 10 }
);

Notes

API Documentation

Check out the documentation at the eth-hooks page!

Dependencies

Main package dependencies

  • ethers.js
  • @uniswap/token-lists
  • @web3-react: core, abstractconnector, types
  • web3modal
  • react-query

Peer dependencies

  • react, react/dom
  • uniswap/sdk