Skip to content

Commit ae6e8cd

Browse files
committed
added zerodev example and storage write utils
1 parent 75317a5 commit ae6e8cd

11 files changed

Lines changed: 769 additions & 58 deletions

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
PRIVATE_KEY=
22
BASE_SEPOLIA_RPC_URL=
3+
ZERODEV_BUNDLER_URL=

bun.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
"@commitlint/cli": "^20.5.0",
2020
"@commitlint/config-conventional": "^20.5.0",
2121
"@types/bun": "latest",
22+
"@zerodev/ecdsa-validator": "^5.4.9",
23+
"@zerodev/sdk": "^5.5.10",
2224
"vitest": "^4.1.2"
2325
},
2426
"peerDependencies": {
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
export const DUMMY_CONTRACT_ABI = [
2+
{
3+
inputs: [{ internalType: 'uint256', name: 'value', type: 'uint256' }],
4+
name: 'DummyRevert',
5+
type: 'error',
6+
},
7+
{
8+
anonymous: false,
9+
inputs: [
10+
{
11+
indexed: false,
12+
internalType: 'address',
13+
name: 'addr',
14+
type: 'address',
15+
},
16+
],
17+
name: 'AddressEmitted',
18+
type: 'event',
19+
},
20+
{
21+
anonymous: false,
22+
inputs: [{ indexed: false, internalType: 'bool', name: 'flag', type: 'bool' }],
23+
name: 'BoolEmitted',
24+
type: 'event',
25+
},
26+
{
27+
anonymous: false,
28+
inputs: [
29+
{
30+
indexed: false,
31+
internalType: 'bytes32',
32+
name: 'slot',
33+
type: 'bytes32',
34+
},
35+
],
36+
name: 'Bytes32Emitted',
37+
type: 'event',
38+
},
39+
{
40+
anonymous: false,
41+
inputs: [{ indexed: false, internalType: 'bytes', name: 'data', type: 'bytes' }],
42+
name: 'BytesEmitted',
43+
type: 'event',
44+
},
45+
{
46+
anonymous: false,
47+
inputs: [
48+
{
49+
indexed: false,
50+
internalType: 'uint256',
51+
name: 'amount',
52+
type: 'uint256',
53+
},
54+
],
55+
name: 'Received',
56+
type: 'event',
57+
},
58+
{
59+
anonymous: false,
60+
inputs: [
61+
{
62+
indexed: false,
63+
internalType: 'uint256',
64+
name: 'value',
65+
type: 'uint256',
66+
},
67+
],
68+
name: 'Uint256Emitted',
69+
type: 'event',
70+
},
71+
{
72+
anonymous: false,
73+
inputs: [
74+
{
75+
indexed: false,
76+
internalType: 'uint256',
77+
name: 'value1',
78+
type: 'uint256',
79+
},
80+
{
81+
indexed: false,
82+
internalType: 'uint256',
83+
name: 'value2',
84+
type: 'uint256',
85+
},
86+
],
87+
name: 'Uint256Emitted2',
88+
type: 'event',
89+
},
90+
{
91+
inputs: [],
92+
name: 'A',
93+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
94+
stateMutability: 'pure',
95+
type: 'function',
96+
},
97+
{
98+
inputs: [{ internalType: 'uint256', name: 'value', type: 'uint256' }],
99+
name: 'B',
100+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
101+
stateMutability: 'pure',
102+
type: 'function',
103+
},
104+
{
105+
inputs: [
106+
{ internalType: 'uint256', name: 'value1', type: 'uint256' },
107+
{ internalType: 'address', name: 'addr', type: 'address' },
108+
{ internalType: 'bytes32', name: 'slot', type: 'bytes32' },
109+
{ internalType: 'bool', name: 'flag', type: 'bool' },
110+
],
111+
name: 'acceptMultipleValues',
112+
outputs: [],
113+
stateMutability: 'nonpayable',
114+
type: 'function',
115+
},
116+
{
117+
inputs: [
118+
{ internalType: 'uint256', name: 'staticValue', type: 'uint256' },
119+
{ internalType: 'bytes', name: 'dynamicValue', type: 'bytes' },
120+
{ internalType: 'address', name: 'addr', type: 'address' },
121+
],
122+
name: 'acceptStaticAndDynamicValues',
123+
outputs: [],
124+
stateMutability: 'nonpayable',
125+
type: 'function',
126+
},
127+
{
128+
inputs: [
129+
{ internalType: 'uint256', name: 'someValue', type: 'uint256' },
130+
{
131+
components: [
132+
{ internalType: 'address', name: 'tokenIn', type: 'address' },
133+
{ internalType: 'address', name: 'tokenOut', type: 'address' },
134+
{ internalType: 'uint256', name: 'amountIn', type: 'uint256' },
135+
{ internalType: 'uint256', name: 'amountOutMin', type: 'uint256' },
136+
{ internalType: 'uint256', name: 'deadline', type: 'uint256' },
137+
{ internalType: 'uint256', name: 'fee', type: 'uint256' },
138+
],
139+
internalType: 'struct MockSwapStruct',
140+
name: 'swapStruct',
141+
type: 'tuple',
142+
},
143+
],
144+
name: 'acceptStruct',
145+
outputs: [],
146+
stateMutability: 'nonpayable',
147+
type: 'function',
148+
},
149+
{
150+
inputs: [{ internalType: 'uint256', name: 'value', type: 'uint256' }],
151+
name: 'emitUint256',
152+
outputs: [],
153+
stateMutability: 'payable',
154+
type: 'function',
155+
},
156+
{
157+
inputs: [],
158+
name: 'getAddress',
159+
outputs: [{ internalType: 'address', name: '', type: 'address' }],
160+
stateMutability: 'view',
161+
type: 'function',
162+
},
163+
{
164+
inputs: [],
165+
name: 'getBool',
166+
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
167+
stateMutability: 'pure',
168+
type: 'function',
169+
},
170+
{
171+
inputs: [],
172+
name: 'getFoo',
173+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
174+
stateMutability: 'view',
175+
type: 'function',
176+
},
177+
{
178+
inputs: [],
179+
name: 'getNativeValue',
180+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
181+
stateMutability: 'pure',
182+
type: 'function',
183+
},
184+
{
185+
inputs: [],
186+
name: 'payableEmit',
187+
outputs: [],
188+
stateMutability: 'payable',
189+
type: 'function',
190+
},
191+
{
192+
inputs: [],
193+
name: 'returnMultipleValues',
194+
outputs: [
195+
{ internalType: 'uint256', name: '', type: 'uint256' },
196+
{ internalType: 'address', name: '', type: 'address' },
197+
{ internalType: 'bytes32', name: '', type: 'bytes32' },
198+
{ internalType: 'bool', name: '', type: 'bool' },
199+
],
200+
stateMutability: 'view',
201+
type: 'function',
202+
},
203+
{
204+
inputs: [{ internalType: 'uint256', name: 'value', type: 'uint256' }],
205+
name: 'revertWithReason',
206+
outputs: [],
207+
stateMutability: 'pure',
208+
type: 'function',
209+
},
210+
{
211+
inputs: [{ internalType: 'uint256', name: 'value', type: 'uint256' }],
212+
name: 'setFoo',
213+
outputs: [],
214+
stateMutability: 'nonpayable',
215+
type: 'function',
216+
},
217+
{
218+
inputs: [
219+
{ internalType: 'uint256', name: 'toStake', type: 'uint256' },
220+
{ internalType: 'uint256', name: 'param2', type: 'uint256' },
221+
],
222+
name: 'stake',
223+
outputs: [],
224+
stateMutability: 'payable',
225+
type: 'function',
226+
},
227+
{
228+
inputs: [
229+
{ internalType: 'uint256', name: 'exactInput', type: 'uint256' },
230+
{ internalType: 'uint256', name: 'minOutput', type: 'uint256' },
231+
],
232+
name: 'swap',
233+
outputs: [{ internalType: 'uint256', name: 'output1', type: 'uint256' }],
234+
stateMutability: 'payable',
235+
type: 'function',
236+
},
237+
];
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* ERC-7579 modular smart account standard ABI fragments.
3+
*
4+
* Covers the module management interface (installModule / isModuleInstalled)
5+
* used by any ERC-7579-compliant account (Nexus, ZeroDev Kernel, etc.).
6+
*/
7+
export const ERC7579_ABI = [
8+
{
9+
type: 'function',
10+
name: 'installModule',
11+
inputs: [
12+
{ name: 'moduleTypeId', type: 'uint256' },
13+
{ name: 'module', type: 'address' },
14+
{ name: 'initData', type: 'bytes' },
15+
],
16+
outputs: [],
17+
stateMutability: 'nonpayable',
18+
},
19+
{
20+
type: 'function',
21+
name: 'isModuleInstalled',
22+
inputs: [
23+
{ name: 'moduleTypeId', type: 'uint256' },
24+
{ name: 'module', type: 'address' },
25+
{ name: 'additionalContext', type: 'bytes' },
26+
],
27+
outputs: [{ type: 'bool' }],
28+
stateMutability: 'view',
29+
},
30+
] as const;

src/test/integration/batch-execution.test.ts

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import { toBytes32 } from '@biconomy/abstractjs';
2-
import { erc20Abi, getAddress, parseUnits } from 'viem';
2+
import { parseUnits } from 'viem';
33
import { baseSepolia } from 'viem/chains';
44
import { describe, expect, it } from 'vitest';
55
import { createComposableBatch } from '../../core/batch';
6-
import { account, initNexus, publicClient, USDC_ADDRESS, walletClient } from '../utils';
6+
import { account, initNexus, publicClient } from '../utils';
7+
import { fundWithUsdc, USDC } from './helpers';
78

8-
if (!account || !walletClient) throw new Error('PRIVATE_KEY is not set in environment');
9+
if (!account) throw new Error('PRIVATE_KEY is not set in environment');
910

10-
// Narrowed references — the throw above guarantees these are defined
1111
const _account = account;
12-
const _walletClient = walletClient;
1312

14-
const USDC = getAddress(USDC_ADDRESS);
1513
const FUND_AMOUNT = parseUnits('1', 6); // 1 mock USDC
1614

1715
// ---------------------------------------------------------------------------
@@ -23,18 +21,8 @@ describe('Integration — Biconomy abstractjs composable execution', () => {
2321
// 1. Init Nexus SCA on Base Sepolia and resolve its address + MEE client
2422
const { scaAddress, meeClient } = await initNexus();
2523

26-
// 2. Fund SCA: EOA transfers mock USDC to the SCA and waits for 2 confirmations
27-
const fundTxHash = await _walletClient.writeContract({
28-
abi: erc20Abi,
29-
address: USDC,
30-
functionName: 'transfer',
31-
args: [scaAddress, FUND_AMOUNT],
32-
});
33-
34-
await publicClient.waitForTransactionReceipt({
35-
hash: fundTxHash,
36-
confirmations: 2,
37-
});
24+
// 2. Fund SCA: EOA transfers mock USDC to the SCA
25+
await fundWithUsdc(scaAddress, FUND_AMOUNT);
3826

3927
// 3. Build composable batch with pre-check → sweep → post-check
4028
const batch = createComposableBatch(publicClient, scaAddress);
@@ -145,18 +133,8 @@ describe('Integration — Biconomy abstractjs composable execution', () => {
145133
// 1. Init Nexus SCA on Base Sepolia and resolve its address + MEE client
146134
const { scaAddress, meeClient } = await initNexus();
147135

148-
// 2. Fund SCA: EOA transfers mock USDC to the SCA and waits for 2 confirmations
149-
const fundTxHash = await _walletClient.writeContract({
150-
abi: erc20Abi,
151-
address: USDC,
152-
functionName: 'transfer',
153-
args: [scaAddress, FUND_AMOUNT],
154-
});
155-
156-
await publicClient.waitForTransactionReceipt({
157-
hash: fundTxHash,
158-
confirmations: 2,
159-
});
136+
// 2. Fund SCA: EOA transfers mock USDC to the SCA
137+
await fundWithUsdc(scaAddress, FUND_AMOUNT);
160138

161139
// 3. Build composable batch: write storage → check storage → partial transfer → sweep remainder
162140
const batch = createComposableBatch(publicClient, scaAddress);

0 commit comments

Comments
 (0)