Skip to content

Commit 48661e5

Browse files
committed
Viem extension
1 parent 78adcdf commit 48661e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3330
-24
lines changed

.changeset/rude-beans-study.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@deroll/viem": minor
3+
---
4+
5+
viem Cartesi extension

packages/viem/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/rollups.ts

packages/viem/README.md

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Cartesi viem extension
2+
3+
Viem provides an [extension mechanism to clients](https://viem.sh/docs/clients/custom) that can provide a convenient and familiar API for application developers based on L2 solutions.
4+
5+
This extension provides extensions for L1 operations and L2 operations related to Cartesi.
6+
7+
## WalletClient L1
8+
9+
Transactions related to Cartesi applications are sent directly to the base layer (L1). This extension provides convenient methods for [sending inputs](https://docs.cartesi.io/cartesi-rollups/1.3/rollups-apis/json-rpc/input-box/#addinput) and interacting with the [portals](https://docs.cartesi.io/cartesi-rollups/1.3/rollups-apis/json-rpc/portals/ERC20Portal/) provided by Cartesi Rollups.
10+
11+
- addInput
12+
- depositEther
13+
- depositERC20Tokens
14+
- depositERC721Token
15+
- depositSingleERC1155Token
16+
- depositBatchERC1155Token
17+
- executeVoucher
18+
- relayDAppAddress
19+
20+
## PublicClient L1
21+
22+
The following methods are provided to interact with the Cartesi Rollups Smart Contracts.
23+
24+
- estimateAddInputGas
25+
- estimateDepositEtherGas
26+
- estimateDepositERC20TokensGas
27+
- estimateDepositERC721TokenGas
28+
- estimateDepositSingleERC1155TokenGas
29+
- estimateDepositBatchERC1155TokenGas
30+
- estimateExecuteVoucherGas
31+
- estimateRelayDAppAddressGas
32+
33+
## PublicClient L2
34+
35+
The following methods are provided to interact with the Cartesi Rollups Node.
36+
37+
- inputNumber
38+
- getInput
39+
- getNoticeCount
40+
- getReportCount
41+
- getVoucherCount
42+
- getNotice
43+
- getReport
44+
- getVoucher
45+
46+
## Utilities
47+
48+
- getInputsAdded: this provides a utility method to get the input(s) added to the InputBox given a `TransactionReceipt`.
49+
50+
```typescript
51+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
52+
const [inputAdded] = getInputsAdded(receipt);
53+
```
54+
55+
## Example
56+
57+
Find below a complete example of depositing ERC-20 tokens and waiting for its effect on L2.
58+
59+
```typescript
60+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
61+
import { privateKeyToAccount } from "viem/accounts";
62+
import { foundry } from "viem/chains";
63+
import { getInputsAdded, publicActionsL2, walletActionsL1 } from "../src";
64+
65+
async function main() {
66+
const chain = foundry;
67+
const account = privateKeyToAccount(
68+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
69+
);
70+
71+
// create public client to chain default url
72+
const publicClient = createPublicClient({ chain, transport: http() });
73+
74+
// create extended public client to L2 with RPC url
75+
// https://github.com/tuler/deroll/tree/main/packages/rpc
76+
const publicClientL2 = createPublicClient({
77+
chain,
78+
transport: http("http://localhost:4000/rpc"),
79+
}).extend(publicActionsL2());
80+
81+
// create extended wallet client to chain default url
82+
const walletClient = createWalletClient({
83+
chain,
84+
transport: http(),
85+
}).extend(walletActionsL1());
86+
87+
// application address
88+
const application = "0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e";
89+
const token = "0x92C6bcA388E99d6B304f1Af3c3Cd749Ff0b591e2";
90+
91+
// send input transaction
92+
const hash = await walletClient.depositERC20Tokens({
93+
account,
94+
application,
95+
chain,
96+
token,
97+
amount: parseUnits("1", 18),
98+
execLayerData: "0x",
99+
});
100+
101+
// wait for receipt
102+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
103+
104+
// get input index from receipt
105+
const [inputAdded] = getInputsAdded(receipt);
106+
if (inputAdded) {
107+
const { inputIndex } = inputAdded;
108+
109+
// wait for input to be processed
110+
const input = await publicClientL2.waitForInput({
111+
inputNumber: Number(inputIndex),
112+
});
113+
console.log(input);
114+
115+
// get notice 0 produced by application
116+
const notice = await publicClientL2.getNotice({
117+
inputNumber: Number(inputIndex),
118+
noticeNumber: 0,
119+
});
120+
121+
console.log(notice);
122+
}
123+
}
124+
125+
main();
126+
```

packages/viem/__tests__/index.test.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {
2+
createPublicClient,
3+
createWalletClient,
4+
http,
5+
zeroAddress,
6+
zeroHash,
7+
} from "viem";
8+
import { describe, it } from "vitest";
9+
import { privateKeyToAccount } from "viem/accounts";
10+
import { foundry } from "viem/chains";
11+
import { getInputsAdded, publicActionsL2, walletActionsL1 } from "../src";
12+
import { inputBoxAbi, inputBoxAddress } from "../src/rollups";
13+
import { AddInput2Parameters } from "../src/decorators/walletL1";
14+
15+
describe("viem extension", () => {
16+
it("getInputIndex", async () => {
17+
const rpcUrl = "http://localhost:8545";
18+
const cartesiRpcUrl = "http://localhost:4000/rpc";
19+
20+
const account = privateKeyToAccount(
21+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
22+
);
23+
const chain = foundry;
24+
25+
const publicClient = createPublicClient({
26+
transport: http(rpcUrl),
27+
});
28+
29+
const publicClientL2 = createPublicClient({
30+
transport: http(cartesiRpcUrl),
31+
}).extend(publicActionsL2());
32+
33+
const walletClient = createWalletClient({ transport: http() }).extend(
34+
walletActionsL1(),
35+
);
36+
37+
const inputNumber = await publicClientL2.inputNumber([]);
38+
39+
/*
40+
const { request: request_ } = await publicClient.simulateAddInput({
41+
args: [zeroAddress, zeroHash],
42+
});
43+
const hash = await walletClient.addInput(request);
44+
*/
45+
46+
// prepare an addInput
47+
const { request } = await publicClient.simulateContract({
48+
account,
49+
abi: inputBoxAbi,
50+
address: inputBoxAddress,
51+
functionName: "addInput",
52+
args: [zeroAddress, zeroHash],
53+
});
54+
55+
// send an input
56+
const hash = await walletClient.writeContract(request);
57+
58+
// wait for the transaction receipt
59+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
60+
61+
// get the input index from the transaction receipt
62+
const [inputAdded] = getInputsAdded(receipt);
63+
64+
if (inputAdded) {
65+
const { inputIndex } = inputAdded;
66+
const notice = await publicClientL2.getNotice([
67+
Number(inputIndex),
68+
0,
69+
]);
70+
}
71+
72+
const p: AddInput2Parameters = {
73+
account,
74+
chain: foundry,
75+
request: {
76+
application: zeroAddress,
77+
payload: zeroHash,
78+
},
79+
};
80+
const hash2 = await walletClient.addInput({
81+
// account,
82+
args: [zeroAddress, zeroHash],
83+
// abi: inputBoxAbi,
84+
// address: inputBoxAddress,
85+
// functionName: "addInput",
86+
// chain: foundry,
87+
});
88+
89+
// addInput
90+
/*
91+
const { request: request2 } = await publicClient.simulateAddInput({
92+
account,
93+
args: [zeroAddress, zeroHash],
94+
});
95+
await walletClient.addInput(request2);
96+
*/
97+
});
98+
});

packages/viem/examples/addInput.ts

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
createPublicClient,
3+
createWalletClient,
4+
http,
5+
stringToHex,
6+
} from "viem";
7+
import { privateKeyToAccount } from "viem/accounts";
8+
import { foundry } from "viem/chains";
9+
import { getInputsAdded, publicActionsL2, walletActionsL1 } from "../src";
10+
11+
async function main() {
12+
const chain = foundry;
13+
const account = privateKeyToAccount(
14+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
15+
);
16+
17+
// create public client to chain default url
18+
const publicClient = createPublicClient({ chain, transport: http() });
19+
20+
// create extended public client to L2 with RPC url
21+
// https://github.com/tuler/deroll/tree/main/packages/rpc
22+
const publicClientL2 = createPublicClient({
23+
chain,
24+
transport: http("http://localhost:4000/rpc"),
25+
}).extend(publicActionsL2());
26+
27+
// create extended wallet client to chain default url
28+
const walletClient = createWalletClient({
29+
chain,
30+
transport: http(),
31+
}).extend(walletActionsL1());
32+
33+
// application address
34+
const application = "0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e";
35+
36+
// input payload
37+
const payload = stringToHex("hello");
38+
39+
// send input transaction
40+
const hash = await walletClient.addInput({
41+
account,
42+
application,
43+
chain,
44+
payload,
45+
});
46+
47+
// wait for receipt
48+
const receipt = await publicClient.waitForTransactionReceipt({
49+
hash,
50+
timeout: 6000,
51+
});
52+
53+
// get input index from receipt
54+
const [inputAdded] = getInputsAdded(receipt);
55+
if (inputAdded) {
56+
const { inputIndex } = inputAdded;
57+
58+
// wait for input to be processed
59+
const input = await publicClientL2.waitForInput({
60+
inputNumber: Number(inputIndex),
61+
});
62+
console.log(input);
63+
64+
// get notice 0 produced by application
65+
const notice = await publicClientL2.getNotice({
66+
inputNumber: Number(inputIndex),
67+
noticeNumber: 0,
68+
});
69+
70+
console.log(notice);
71+
}
72+
}
73+
74+
main();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
2+
import { privateKeyToAccount } from "viem/accounts";
3+
import { foundry } from "viem/chains";
4+
import { getInputsAdded, publicActionsL2, walletActionsL1 } from "../src";
5+
6+
async function main() {
7+
const chain = foundry;
8+
const account = privateKeyToAccount(
9+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
10+
);
11+
12+
// create public client to chain default url
13+
const publicClient = createPublicClient({ chain, transport: http() });
14+
15+
// create extended public client to L2 with RPC url
16+
// https://github.com/tuler/deroll/tree/main/packages/rpc
17+
const publicClientL2 = createPublicClient({
18+
chain,
19+
transport: http("http://localhost:4000/rpc"),
20+
}).extend(publicActionsL2());
21+
22+
// create extended wallet client to chain default url
23+
const walletClient = createWalletClient({
24+
chain,
25+
transport: http(),
26+
}).extend(walletActionsL1());
27+
28+
// application address
29+
const application = "0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e";
30+
const token = "0xc6582A9b48F211Fa8c2B5b16CB615eC39bcA653B";
31+
32+
// send input transaction
33+
const hash = await walletClient.depositBatchERC1155Token({
34+
account,
35+
application,
36+
baseLayerData: "0x",
37+
chain,
38+
execLayerData: "0x",
39+
token,
40+
values: [1n],
41+
tokenIds: [1n],
42+
});
43+
44+
// wait for receipt
45+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
46+
47+
// get input index from receipt
48+
const [inputAdded] = getInputsAdded(receipt);
49+
if (inputAdded) {
50+
const { inputIndex } = inputAdded;
51+
52+
// wait for input to be processed
53+
const input = await publicClientL2.waitForInput({
54+
inputNumber: Number(inputIndex),
55+
});
56+
console.log(input);
57+
58+
// get notice 0 produced by application
59+
const notice = await publicClientL2.getNotice({
60+
inputNumber: Number(inputIndex),
61+
noticeNumber: 0,
62+
});
63+
64+
console.log(notice);
65+
}
66+
}
67+
68+
main();

0 commit comments

Comments
 (0)