Skip to content

Commit

Permalink
chore: add migration notes from contractkit (#10228)
Browse files Browse the repository at this point in the history
* chore: add migration notes from contractkit

* chore: split into two files

* docs: adjust viem docs using stcelo as feedback

* chore: remove deprecation note
  • Loading branch information
nicolasbrugneaux authored Sep 26, 2023
1 parent 33eb441 commit 1c57785
Show file tree
Hide file tree
Showing 2 changed files with 340 additions and 0 deletions.
108 changes: 108 additions & 0 deletions packages/sdk/contractkit/MIGRATION-TO-ETHERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Draft: Migration document from Contractkit

Hello devs 🌱 this is a migration path away from contractkit. This aims to give examples to help you move to [ethers](https://docs.ethers.org/).

## Initialization

```diff
- import Web3 from "web3";
- import { newKitFromWeb3 } from "@celo/contractkit";

- const web3 = new Web3("https://alfajores-forno.celo-testnet.org");
- const kit = newKitFromWeb3(web3);
+ import { providers } from 'ethers'
+
+ const provider = new providers.JsonRpcProvider('https://alfajores-forno.celo-testnet.org')
```

## Basic usage

While we cannot here show all the use-cases of contrackit or ethers or viem, let's try to give an overview of how they can be used for different goals.

### Get address

```diff
- const accounts = await kit.web3.eth.getAccounts();
+ const accounts = await provider.listAccounts();
const defaultAccount = accounts[0];
```

### Get wallet

```diff
+ import { Wallet } from 'ethers'

- const wallet = kit.getWallet();
+ const wallet = new Wallet('0x...', provider);
```

### Provider methods

```diff
- const provider = kit.connection.web3.currentProvider
- kit.connection.getBlock(...)
- kit.connection.getTransaction(...)
provider.getBlock(...)
provider.getTransaction(...)
```

### Signer methods

```diff
- const provider = kit.connection.web3.currentProvider
- const signer = provider.getSigner(kit.connection.defaultAccount)
+ const signer = provider.getSigner(address)
+ signer.sendTransaction(...)
+ signer.signMessage(...)
```

### 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 also use the [`@celo/abis`](https://www.npmjs.com/package/@celo/abis) package to get the ABIs directly.

```ts
// this address is constant
const REGISTRY_CONTRACT_ADDRESS = '0x000000000000000000000000000000000000ce10'
const registry = new Contract(REGISTRY_CONTRACT_ADDRESS, registryAbi, wallet)

async function getToken(token: string) {
const goldTokenAddress = await registry.getAddressForString(token)
return goldTokenAddress
}
async function CeloTokens(): Promise<[string, string][]> {
return Promise.all(
['GoldToken', 'StableToken', 'StableTokenEUR', 'StableTokenBRL'].map(async (token) => [
token,
await getToken(token),
])
)
}
```

#### Balance

```diff
+ import { tokenAbi } from './abi.json'

- const contract = await kit.contracts.getGoldToken();
+ const tokenAddress = '0x...' // Grabbed from the registry or from the explorer
+ const contract = new ethers.Contract(tokenAddress, tokenAbi, signer);
const balance = await contract.balanceOf(wallet.address);
```

#### Transfer

Then, use the address of the token that you need and call the transfer method of the contract.

```diff
+ import { tokenAbi } from './abi.json'

- const contract = await kit.contracts.getGoldToken();
+ const tokenAddress = '0x...' // Grabbed from the registry or from the explorer
+ const contract = new ethers.Contract(tokenAddress, tokenAbi, signer);
const txReceipt = await contract.transfer('0x...', amount);
```

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/).
232 changes: 232 additions & 0 deletions packages/sdk/contractkit/MIGRATION-TO-VIEM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# Draft: Migration document from Contractkit

Hello devs 🌱 this is a migration path away from contractkit. This aims to give examples to help you move to [viem](https://viem.sh/).

## Initialization

```diff
- import Web3 from "web3";
- import { newKitFromWeb3 } from "@celo/contractkit";
-
- const web3 = new Web3("https://alfajores-forno.celo-testnet.org");
- const kit = newKitFromWeb3(web3);
+ import { createPublicClient, http } from 'viem'
+ import { celo, celoAlfajores } from 'viem/chains'
+
+ const publicClient = createPublicClient({
+ chain: celoAlfajores, // or celo for celo's mainnet
+ transport: http()
+ })
```

## Basic usage

While we cannot here show all the use-cases of contrackit or ethers or viem, let's try to give an overview of how they can be used for different goals.

### Get address

With viem:

```diff
- const accounts = await kit.web3.eth.getAccounts();
+ const accounts = await publicClient.getAddresses()
const defaultAccount = accounts[0];
```

### Get wallet

With viem:

> [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 account = privateKeyToAccount(privateKey);
+ await walletClient.signMessage({
+ account,
+ message: 'hello world',
+ })
```

### Provider methods

```diff
- const provider = kit.connection.web3.currentProvider
- kit.connection.getBlock(...)
- kit.connection.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

```diff
- const provider = kit.connection.web3.currentProvider
- const signer = provider.getSigner(kit.connection.defaultAccount)
+ 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 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 registryContract = getContract({
address: REGISTRY_CONTRACT_ADDRESS,
abi: registryABI,
publicClient,
})

async function CeloTokens(): Promise<[string, string][]> {
return Promise.all(
['GoldToken', 'StableToken', 'StableTokenEUR', 'StableTokenBRL'].map(async (token) => [
token,
await registryContract.read.getAddressForString(token),
])
)
}
```

#### Balance

```diff
+ import { stableTokenABI } from '@celo/abis/types/viem'

- const contract = await kit.contracts.getGoldToken();
- const balance = await contract.balanceOf(wallet.address);
+ const tokenAddresses = await CeloTokens();
+ const cUSD = tokenAddresses["StableToken]
+ const balance = await client.readContract({
+ abi: tokenAbi,
+ address: cUSD,
+ functionName: "balanceOf",
+ args: [account.address],
+ });
```

#### Transfer

Then, use the address of the token that you need and call the transfer method of the contract.

```diff
+ import { stableTokenABI } from '@celo/abis/types/viem'
- const CELO = await kit.contracts.getGoldToken();
- const txReceipt = await CELO.transfer('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);
```

#### 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)
```

#### Fee Currency

With Viem's built in Celo transaction serializer and Celo block/transaction formatters it is easy to build a wallet that supports Celo's ability to pay gas fees with various erc20 tokens. Simply, import a Celo chain from `viem/chain` and pass it to Viem's `createWalletClient`. Once the client is created you can add the feeCurrency field to your transaction with the address of the token you want to use for gas.

```ts
import { celo } from 'viem/chains'
import { createWalletClient, privateKeyToAccount, type SendTransactionParameters, http } from 'viem'

const account = privateKeyToAccount(PRIVATE_KEY)

// ALFAJORES ADDRESS: Celo Mainnet can be fetched from the registry
const cUSDAddress = '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1'

const localAccountClient = createWalletClient({
account,
chain: celo,
})

const sendTransaction = (tx: SendTransactionParameters<typeof celo>) => {
return localAccountClient.sendTransaction(tx)
}

const hash = await sendTransaction({
feeCurrency: cUSDAddress,
value: BigInt(100000000),
to: '0x22579CA45eE22E2E16dDF72D955D6cf4c767B0eF',
})
```

### 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).

0 comments on commit 1c57785

Please sign in to comment.