ContractAppContext
How to use ContractAppContext
🚩 1. Generating types for your contract
First, you'll have to generate your contract types for Hardhat and external contracts.
To do this, you can use eth-sdk
or typechain with hardhat
to generate your contracts. Then add them to a folder, such as generated/contract-types
.
You can see how this is done in the scaffold-eth-typescript repo
How to use eth-sdk
for external contracts
Scaffold-eth-typescript uses eth-sdk
to generate types and an ABI for use by external contracts.
📝 Note: This would be a dev dependency on your project.
- a config of
{contractNames: address}
externalContractConfig.ts - a config for eth-sdk-config.ts
- and calling the
eth-sdk
with the folder of your config file as a parameter e.g.yarn eth-sdk -p ./src/generated
You can learn more at the eth-sdk Github
How to use Hardhat with typechain
Check out the excellent typechain docs. You can also find an example in scaffold-eth-typescript hardhat.config.ts.
🚩 2. Creating the context with contractsContextFactory
You need to create a config that returns a config of your contracts. This would be heterogenous 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 can copy the code below and use it to get started fast:
// 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 of how to do this
🚩 3. Using hooks to get your contracts
Now that you've created the context, you can use hooks 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);
And you can do cool stuff like read values from your 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()
);
Or like this:
// 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 }
);