diff --git a/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md b/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md index e79a2680d09..f5171d5b5de 100644 --- a/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md +++ b/packages/sdk/contractkit/MIGRATION-TO-ETHERS.md @@ -60,7 +60,7 @@ provider.getTransaction(...) I'll show the most "basic" interaction, which is a transfer. On CELO, it comes with a twist, you can transfer 4 currencies, CELO, cUSD, cEUR, and cREAL. -You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). +You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). You can also use the [`@celo/abis`](https://www.npmjs.com/package/@celo/abis) package to get the ABIs directly. ```ts // this address is constant diff --git a/packages/sdk/contractkit/MIGRATION-TO-VIEM.md b/packages/sdk/contractkit/MIGRATION-TO-VIEM.md index 27cc837b286..1770582ce71 100644 --- a/packages/sdk/contractkit/MIGRATION-TO-VIEM.md +++ b/packages/sdk/contractkit/MIGRATION-TO-VIEM.md @@ -13,7 +13,7 @@ Hello devs 🌱 this is a migration path away from contractkit following the [pu + import { createPublicClient, http } from 'viem' + import { celo, celoAlfajores } from 'viem/chains' + -+ const client = createPublicClient({ ++ const publicClient = createPublicClient({ + chain: celoAlfajores, // or celo for celo's mainnet + transport: http() + }) @@ -29,7 +29,7 @@ With viem: ```diff - const accounts = await kit.web3.eth.getAccounts(); -+ const accounts = await client.getAddresses() ++ const accounts = await publicClient.getAddresses() const defaultAccount = accounts[0]; ``` @@ -37,16 +37,21 @@ const defaultAccount = accounts[0]; With viem: -> [viem does not currently support](<[source](https://viem.sh/docs/ethers-migration.html#viem-11)>) client-side signing (it's coming shortly!) – until then, you can use an Ethers Wallet +> [viem does not full support](<[source](https://viem.sh/docs/ethers-migration.html#viem-11)>) client-side signing (it's coming shortly!) – until then, you can use an Ethers Wallet, however it does support `signMessage` and `signTypedData` already. ```diff ++ import { privateKeyToAccount } from 'viem/accounts' ++ ++ const privateKey = "0x..."; + const walletClient = createWalletClient({ + transport: http(celoAlfajores.rpcUrls.default.http[0] as string), + chain: celoAlfajores, + }); -+ const provider = new JsonRpcProvider(celoAlfajores.rpcUrls.default.http[0]); -+ const wallet = new Wallet(privateKey, provider); -+ const account = getAccount(wallet); ++ const account = privateKeyToAccount(privateKey); ++ await walletClient.signMessage({ ++ account, ++ message: 'hello world', ++ }) ``` ### Provider methods @@ -55,8 +60,26 @@ With viem: - const provider = kit.connection.web3.currentProvider - kit.connection.getBlock(...) - kit.connection.getTransaction(...) -+ client.getBlock(...) -+ client.getTransaction(...) ++ const block = await publicClient.getBlock() ++ /** ++ * { ++ * baseFeePerGas: 10000n, ++ * number: 1234n, ++ * parentHash: "0x....", ++ * ... ++ * } ++ */ ++ const tx = await publicClient.getTransaction({ ++ hash: "0x...", ++ }) ++ /** ++ * { ++ * blockHash: '0x...', ++ * blockNumber: 1234n, ++ * from: '0x...', ++ * ... ++ * } ++ */ ``` ### Signer methods @@ -64,31 +87,33 @@ With viem: ```diff - const provider = kit.connection.web3.currentProvider - const signer = provider.getSigner(kit.connection.defaultAccount) -+ const [address] = await client.getAddresses() -+ const account = getAccount(address) -+ client.sendTransaction({ account, ... }) ++ const [account] = await walletClient.getAddresses() ++ const hash = await walletClient.sendTransaction({ account, to: "0x...", value: 1000n }) ``` ### Contract interaction I'll show the most "basic" interaction, which is a transfer. On CELO, it comes with a twist, you can transfer 4 currencies, CELO, cUSD, cEUR, and cREAL. -You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). +You can get the addresses on these tokens by heading to the explorer and getting their abi and addresses, or you can also use our [registry contract](https://docs.celo.org/developer/contractkit/contracts-wrappers-registry). You can also use the [`@celo/abis`](https://www.npmjs.com/package/@celo/abis) package to get the ABIs directly. ```ts +import { getContract } from 'viem' +import { registryABI } from '@celo/abis/types/viem' + // this address is constant const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10' -const registry = new Contract(REGISTRY_CONTRACT_ADDRESS, registryAbi, wallet) +const registryContract = getContract({ + address: REGISTRY_CONTRACT_ADDRESS, + abi: registryABI, + publicClient, +}) -async function getToken(token: string) { - const tokenAddress = await registry.getAddressForString(token) - return tokenAddress -} async function CeloTokens(): Promise<[string, string][]> { return Promise.all( ['GoldToken', 'StableToken', 'StableTokenEUR', 'StableTokenBRL'].map(async (token) => [ token, - await getToken(token), + await registryContract.read.getAddressForString(token), ]) ) } @@ -97,14 +122,15 @@ async function CeloTokens(): Promise<[string, string][]> { #### Balance ```diff -+ import { tokenAbi } from './abi.json' ++ import { stableTokenABI } from '@celo/abis/types/viem' - const contract = await kit.contracts.getGoldToken(); - const balance = await contract.balanceOf(wallet.address); -+ const tokenAddress = '0x...' // Grabbed from the registry or from the explorer ++ const tokenAddresses = await CeloTokens(); ++ const cUSD = tokenAddresses["StableToken] + const balance = await client.readContract({ + abi: tokenAbi, -+ address: tokenAddress, ++ address: cUSD, + functionName: "balanceOf", + args: [account.address], + }); @@ -115,11 +141,63 @@ async function CeloTokens(): Promise<[string, string][]> { Then, use the address of the token that you need and call the transfer method of the contract. ```diff -+ import { tokenAbi } from './abi.json' ++ import { stableTokenABI } from '@celo/abis/types/viem' - const CELO = await kit.contracts.getGoldToken(); - const txReceipt = await CELO.transfer('0x...', amount) -+ const tokenAddress = '0x...' -+ const transfer = await walletClient.simulateContract({abi, address: tokenAddress, functionName: 'transfer', args: ['0x...', amount] }) ++ const tokenAddresses = await CeloTokens(); ++ const cUSD = tokenAddresses["StableToken] ++ const { request } = await walletClient.simulateContract({ ++ abi, ++ address: cUSD, ++ functionName: 'transfer', ++ args: [ ++ '0x...', // to address ++ amount: 1000n, ++ ], ++ account: '0x...', // from address ++ }) ++ const hash = await walletClient.sendTransaction(request); ``` -For more in depth examples, I highly recommend checking out the extensive documentations of both [ethers](https://docs.ethers.org/) and [viem](https://viem.sh/). +#### Multicall + +While contractkit didn't directly support multicall, you could use libraries such as `@dopex-io/web3-multicall` as such: + +```ts +import Multicall from '@dopex-io/web3-multicall' +const MULTICALL_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11' // same on mainnet and alfajores +const multicall = new Multicall({ + provider, + chainId, + multicallAddress: MULTICALL_ADDRESS, +}) +const governance = await kit.contracts._web3Contracts.getGovernance() +const stageTxs = _dequeue.map((proposalId) => governance.methods.getProposalStage(proposalId)) +const stages: string[] = await multicall.aggregate(stageTxs) +``` + +You now can use `viem` directly to multicall since they have the address configured in the `viem/chains` files. + +```ts +const governanceAddress = await registryContract.read.getAddressForString(['Governance']) +const governanceContract = getContract({ + address: governanceAddress, + abi: governanceABI, + publicClient, +}) +const _dequeue = await governanceContract.read.getDequeue() +const stageCalls = _dequeue.map((proposalId) => ({ + address: governanceAddress, + abi: governanceABI, + functionName: 'getProposalStage', + args: [proposalId], +})) +const stages = (await publicClient.multicall({ contracts: stageCalls })).map((x) => x.result) +``` + +### Further reading + +For more in depth examples and documentation about viem specifically, I highly recommend checking out the extensive documentations of [viem](https://viem.sh/). + +Another interesting application to help you migrate could be StCelo-v2. +You can checkout the changes going from `react-celo` + `contractkit` to `rainbowkit` + `wagmi` + `viem` in [this pull-request](https://github.com/celo-org/staked-celo-web-app/pull/129).