Skip to content

Commit

Permalink
update chopsticks example (#906)
Browse files Browse the repository at this point in the history
* update chopsticks example

* update
  • Loading branch information
shunjizhan authored Dec 19, 2023
1 parent 1f01c99 commit 44ae416
Show file tree
Hide file tree
Showing 4 changed files with 506 additions and 1,541 deletions.
100 changes: 28 additions & 72 deletions examples/chopsticks/README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,23 @@
# Bodhi Examples - Chopsticks
This example demonstrates how to use [chopsticks](https://github.com/AcalaNetwork/chopsticks) to fork a substrate network, override it's storage, and test EVM+ transactions in parallel reality.
This example demonstrates how to use [chopsticks](https://github.com/AcalaNetwork/chopsticks) to fork Acala mainnet, override it's storage, and test EVM+ transactions in parallel reality.

## Setup
### prepare storage override
One advantage of chopsticks is that we can override storage to test more conveniently.

Suppose we want to impersonate a big whale calling a contract, we can achieve this by doing the following storage overrides:
- set Alice's balance to `1000000000000000`, whose address is `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`.
- bind a ETH address to Alice, so Alice's balance will sync to it, now this ETH address becomes the whale! The random eth address we choose here is `0xEE1b6e72FC5bC8738150B6bE7564DA887723cCA1` whose private key is `0x8d2d614677b99ee1809eec0967d538f43d3f410e20ee5f5b979dd21d5930d3fe`.

Create a `storage.json` that looks like this:
```json
{
"System": {
"Account": [
[
["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
{
"data": { "free": 1000000000000000 }
}
]
]
},
"EvmAccounts": {
"EvmAddresses": [
[
["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
"0xEE1b6e72FC5bC8738150B6bE7564DA887723cCA1"
]
],
"Accounts": [
[
["0xee1b6e72fc5bc8738150b6be7564da887723cca1"],
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
]
]
}
}
```

### run chopsticks
run the following command in the same folder with `storage.json`
## Setup local acala fork with chopsticks
- run a local fork of Acala (use `npx` or `yarn dlx` if you prefer these over `bunx`)
```
yarn dlx @acala-network/chopsticks dev \
--endpoint wss://mandala-rpc.aca-staging.network/ws \
--import-storage=storage.json
bunx @acala-network/chopsticks@latest -c https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/acala.yml
```

alternatively if you want to clone and build chopsticks yourself
Now we should have a local fork of acala running at `ws://localhost:8000`. **All state changes are local**, so we can do arbitrary experiment, without affecting the acutal public network.

- run an [eth rpc adapter](https://evmdocs.acala.network/tooling/rpc-adapter) to provide local eth rpc interface at `localhost:8545`
```
git clone --recurse-submodules https://github.com/AcalaNetwork/chopsticks.git && cd chopsticks
yarn
yarn build-wasm
yarn start dev \
--endpoint wss://mandala-rpc.aca-staging.network/ws \
--import-storage=storage.json
bunx @acala-network/eth-rpc-adapter@latest -e ws://localhost:8000
```

Now we should have a local fork of mandala running at `ws://localhost:8000`. **All state changes are local**, so we can do arbitrary experiment, without affecting the acutal public network.
### about storage override
One advantage of chopsticks is that we can override storage to test more conveniently. In the above setup, we did a couple storage overrides under the hood:
- set Alice's balance to `1000000000000000`, whose address is `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY` [here](https://github.com/AcalaNetwork/chopsticks/blob/master/configs/acala.yml#L14-L19)
- bind test evm account `0x75E480dB528101a381Ce68544611C169Ad7EB342` to Alice, so this evm account syncs Alice's balance [here](https://github.com/AcalaNetwork/chopsticks/blob/master/configs/acala.yml#L75-L81). Now we will never need to worry about any fees since we are now big whale!

## Run the demo script
It's recommended to get familiar with the [echo contract](https://github.com/AcalaNetwork/hardhat-tutorials/tree/master/echo) before diving into the script.
Expand All @@ -68,34 +28,30 @@ yarn start
```

We will see that in the initial state:
- echo contract local state is the same as the public mandala, since chopsticks forks the state.
- balance of the eth address is 0 on public mandala, since it's a random address. However, it has balance on local fork, since we have overridden the state.
- echo contract local state is the same as the public acala, since chopsticks forks the state.
- balance of the eth address is 0 on public acala, since it's a test account that isn't used on mainnet. However, it has balance on local fork, since we have overridden the state.
```
------------------------ initial state ------------------------
msg from public mandala: [hello from echo at public mandala]
msg from local mandala fork: [hello from echo at public mandala]
balance on public mandala: [0]
balance on local mandala fork: [1000000000000000000000]
msg from acala: [greetings from acala mainnet!]
msg from local acala fork: [greetings from acala mainnet!]
balance on acala: [0]
balance on local acala fork: [1000000000000000000000]
---------------------------------------------------------------
```

And after calling `scream()`, which changes the contract state:
- local state is updated, and public mandala state is unaffected
- local balance is updated, and public mandala balance is unaffected
- local state is updated, and public acala state is unaffected
- local balance is updated, and public acala balance is unaffected
```
------------------------ after calling scream() ------------------------
msg from public mandala: [hello from echo at public mandala]
msg from local mandala fork: [new msg from local mandala]
balance on public mandala: [0]
balance on local mandala fork: [999987553308653000000]
msg from acala: [greetings from acala mainnet!]
msg from local acala fork: [new msg from local acala]
balance on acala: [0]
balance on local acala fork: [999996579991919000000]
---------------------------------------------------------------
```

## Run with other tools
To test with others tools, such as hardhat and truffle, we can run an [eth rpc adapter](https://github.com/AcalaNetwork/bodhi.js/tree/master/eth-rpc-adapter) along with chopsticks, then run scripts normally. All transactions submitted to `http://localhost:8545` will be handled by chopsticks locally.
```
cd ../../eth-rpc-adapter/
rush update
rush build -t .
yarn start -l -e ws://localhost:8000
```
## Note
In this local setup, one of the eth rpc `eth_getLogs` won't work. In order for that to work, we needs either:
- setup a full bodhi stack with subquery, and with chopsticks, this is a little tricky. Please contact Acala team for supporting if you need it.
- until [this issue](https://github.com/AcalaNetwork/bodhi.js/issues/901) is resolved.
40 changes: 19 additions & 21 deletions examples/chopsticks/index.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
import { Contract, Wallet } from 'ethers';
import { EvmRpcProvider } from '@acala-network/eth-providers';
import { AcalaJsonRpcProvider } from '@acala-network/eth-providers';

import echoJson from './Echo.json';

const ECHO_ADDRESS = '0xe8669bfe6fe29dde37d670fbc1cf96365025c242';
const PRIVATE_KEY = '0x8d2d614677b99ee1809eec0967d538f43d3f410e20ee5f5b979dd21d5930d3fe';
const ETH_ADDRESS = '0xEE1b6e72FC5bC8738150B6bE7564DA887723cCA1';
const ECHO_ADDRESS = '0xCDD9460d5d59f059aE17b27ab7C3B45a2C2F1B4d';
const PRIVATE_KEY = 'a872f6cbd25a0e04a08b1e21098017a9e6194d101d75e13111f71410c59cd57f';
const ETH_ADDRESS = '0x75E480dB528101a381Ce68544611C169Ad7EB342'; // bound to Alice by chopsticks storage override

const LOCAL_NODE_URL = 'ws://localhost:8000';
const MANDALA_NODE_URL = 'wss://mandala-rpc.aca-staging.network/ws';
const LOCAL_ETH_RPC = 'http://localhost:8545';
const ACALA_ETH_RPC = 'https://eth-rpc-acala.aca-api.network';

const main = async () => {
const providerMandala = new EvmRpcProvider(MANDALA_NODE_URL);
const providerLocal = new EvmRpcProvider(LOCAL_NODE_URL);
const providerAcala = new AcalaJsonRpcProvider(ACALA_ETH_RPC);
const providerLocal = new AcalaJsonRpcProvider(LOCAL_ETH_RPC);
const signerLocal = new Wallet(PRIVATE_KEY, providerLocal);

const echoMandala = new Contract(ECHO_ADDRESS, echoJson.abi, providerMandala);
const echoAcala = new Contract(ECHO_ADDRESS, echoJson.abi, providerAcala);
const echoLocal = new Contract(ECHO_ADDRESS, echoJson.abi, signerLocal);

const _printState = async (stateName: string) => {
const msgMandala = await echoMandala.callStatic.echo();
const msgLcoal = await echoLocal.callStatic.echo();
const balanceMandala = await providerMandala.getBalance(ETH_ADDRESS);
const msgAcala = await echoAcala.echo();
const msgLcoal = await echoLocal.echo();
const balanceAcala = await providerAcala.getBalance(ETH_ADDRESS);
const balanceLocal = await providerLocal.getBalance(ETH_ADDRESS);

console.log(`------------------------ ${stateName} ------------------------`);
console.log(`msg from public mandala: [${msgMandala}]`);
console.log(`msg from local mandala fork: [${msgLcoal}]`);
console.log(`balance on public mandala: [${balanceMandala.toBigInt()}]`);
console.log(`balance on local mandala fork: [${balanceLocal.toBigInt()}]`);
console.log(`msg from acala: [${msgAcala}]`);
console.log(`msg from local acala fork: [${msgLcoal}]`);
console.log(`balance on acala: [${balanceAcala.toBigInt()}]`);
console.log(`balance on local acala fork: [${balanceLocal.toBigInt()}]`);
console.log(`---------------------------------------------------------------`);
console.log('');
};

await _printState('initial state');

console.log('calling scream() on local mandala fork ...');
console.log('calling scream() on local acala fork ...');
console.log('');
await echoLocal.scream('new msg from local mandala');
await (await echoLocal.scream('new msg from local acala')).wait();

await _printState('after calling scream()');

providerMandala.disconnect();
providerLocal.disconnect();
};

main();
2 changes: 1 addition & 1 deletion examples/chopsticks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"start": "ts-node index.ts"
},
"dependencies": {
"@acala-network/eth-providers": "^2.5.9",
"@acala-network/eth-providers": "~2.7.17",
"ethers": "^5.7.2"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 44ae416

Please sign in to comment.