@agirails/sdk 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -8
- package/dist/ACTPClient.d.ts +35 -1
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +156 -26
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/AdapterRouter.d.ts.map +1 -1
- package/dist/adapters/AdapterRouter.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +10 -1
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +36 -1
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +210 -18
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.d.ts +16 -0
- package/dist/cli/commands/register.d.ts.map +1 -0
- package/dist/cli/commands/register.js +211 -0
- package/dist/cli/commands/register.js.map +1 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/config.d.ts +6 -0
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/networks.d.ts +20 -4
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +59 -27
- package/dist/config/networks.js.map +1 -1
- package/dist/config/publishPipeline.d.ts +14 -0
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +2 -1
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
- package/dist/erc8004/ERC8004Bridge.js +6 -5
- package/dist/erc8004/ERC8004Bridge.js.map +1 -1
- package/dist/erc8004/ReputationReporter.d.ts.map +1 -1
- package/dist/erc8004/ReputationReporter.js +9 -12
- package/dist/erc8004/ReputationReporter.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/level1/Agent.js +4 -4
- package/dist/level1/Agent.js.map +1 -1
- package/dist/protocol/ACTPKernel.d.ts +7 -1
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +13 -10
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +14 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +14 -0
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +5 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +1 -1
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.d.ts.map +1 -1
- package/dist/storage/ArchiveBundleBuilder.js.map +1 -1
- package/dist/storage/ArweaveClient.d.ts.map +1 -1
- package/dist/storage/ArweaveClient.js +2 -0
- package/dist/storage/ArweaveClient.js.map +1 -1
- package/dist/storage/FilebaseClient.d.ts.map +1 -1
- package/dist/storage/FilebaseClient.js +2 -0
- package/dist/storage/FilebaseClient.js.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.js +3 -2
- package/dist/utils/ErrorRecoveryGuide.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -2
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +7 -5
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/utils/computeTypeHash.js +1 -3
- package/dist/utils/computeTypeHash.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +0 -1
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/validation.d.ts +2 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +2 -2
- package/dist/utils/validation.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +77 -0
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -0
- package/dist/wallet/AutoWalletProvider.js +197 -0
- package/dist/wallet/AutoWalletProvider.js.map +1 -0
- package/dist/wallet/EOAWalletProvider.d.ts +21 -0
- package/dist/wallet/EOAWalletProvider.d.ts.map +1 -0
- package/dist/wallet/EOAWalletProvider.js +57 -0
- package/dist/wallet/EOAWalletProvider.js.map +1 -0
- package/dist/wallet/IWalletProvider.d.ts +115 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -0
- package/dist/wallet/IWalletProvider.js +12 -0
- package/dist/wallet/IWalletProvider.js.map +1 -0
- package/dist/wallet/aa/BundlerClient.d.ts +70 -0
- package/dist/wallet/aa/BundlerClient.d.ts.map +1 -0
- package/dist/wallet/aa/BundlerClient.js +183 -0
- package/dist/wallet/aa/BundlerClient.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +55 -0
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -0
- package/dist/wallet/aa/DualNonceManager.js +131 -0
- package/dist/wallet/aa/DualNonceManager.js.map +1 -0
- package/dist/wallet/aa/PaymasterClient.d.ts +52 -0
- package/dist/wallet/aa/PaymasterClient.d.ts.map +1 -0
- package/dist/wallet/aa/PaymasterClient.js +115 -0
- package/dist/wallet/aa/PaymasterClient.js.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts +87 -0
- package/dist/wallet/aa/TransactionBatcher.d.ts.map +1 -0
- package/dist/wallet/aa/TransactionBatcher.js +148 -0
- package/dist/wallet/aa/TransactionBatcher.js.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts +71 -0
- package/dist/wallet/aa/UserOpBuilder.d.ts.map +1 -0
- package/dist/wallet/aa/UserOpBuilder.js +196 -0
- package/dist/wallet/aa/UserOpBuilder.js.map +1 -0
- package/dist/wallet/aa/constants.d.ts +54 -0
- package/dist/wallet/aa/constants.d.ts.map +1 -0
- package/dist/wallet/aa/constants.js +18 -0
- package/dist/wallet/aa/constants.js.map +1 -0
- package/package.json +4 -2
- package/src/ACTPClient.ts +217 -31
- package/src/adapters/AdapterRouter.ts +0 -1
- package/src/adapters/BasicAdapter.ts +41 -1
- package/src/cli/commands/init.ts +247 -19
- package/src/cli/commands/publish.ts +1 -2
- package/src/cli/commands/register.ts +233 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/utils/config.ts +9 -0
- package/src/config/networks.ts +82 -27
- package/src/config/publishPipeline.ts +2 -2
- package/src/erc8004/ERC8004Bridge.ts +6 -5
- package/src/erc8004/ReputationReporter.ts +14 -18
- package/src/index.ts +12 -0
- package/src/level1/Agent.ts +5 -5
- package/src/protocol/ACTPKernel.ts +20 -10
- package/src/protocol/EventMonitor.ts +14 -0
- package/src/runtime/BlockchainRuntime.ts +7 -1
- package/src/storage/ArchiveBundleBuilder.ts +0 -2
- package/src/storage/ArweaveClient.ts +2 -1
- package/src/storage/FilebaseClient.ts +3 -3
- package/src/utils/ErrorRecoveryGuide.ts +4 -2
- package/src/utils/IPFSClient.ts +9 -7
- package/src/utils/computeTypeHash.ts +1 -3
- package/src/utils/retry.ts +0 -1
- package/src/utils/validation.ts +2 -2
- package/src/wallet/AutoWalletProvider.ts +294 -0
- package/src/wallet/EOAWalletProvider.ts +69 -0
- package/src/wallet/IWalletProvider.ts +133 -0
- package/src/wallet/aa/BundlerClient.ts +273 -0
- package/src/wallet/aa/DualNonceManager.ts +163 -0
- package/src/wallet/aa/PaymasterClient.ts +173 -0
- package/src/wallet/aa/TransactionBatcher.ts +240 -0
- package/src/wallet/aa/UserOpBuilder.ts +246 -0
- package/src/wallet/aa/constants.ts +60 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionBatcher — Encodes ACTP multi-call batches.
|
|
3
|
+
*
|
|
4
|
+
* An ACTP payment requires 3 contract calls:
|
|
5
|
+
* 1. USDC.approve(escrowVault, amount)
|
|
6
|
+
* 2. ACTPKernel.createTransaction(provider, requester, amount, deadline, disputeWindow, serviceHash, agentId)
|
|
7
|
+
* 3. ACTPKernel.linkEscrow(txId, escrowVault, escrowId)
|
|
8
|
+
*
|
|
9
|
+
* TransactionBatcher encodes all 3 as SmartWalletCall[] for executeBatch.
|
|
10
|
+
* It pre-computes the txId using the same keccak256 formula as the contract.
|
|
11
|
+
*
|
|
12
|
+
* @module wallet/aa/TransactionBatcher
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ethers } from 'ethers';
|
|
16
|
+
import { SmartWalletCall } from './constants';
|
|
17
|
+
import { ServiceDescriptor } from '../../types/agent';
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// ABI fragments — minimal for encoding only
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
const ERC20_APPROVE_ABI = [
|
|
24
|
+
'function approve(address spender, uint256 amount)',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const ACTP_KERNEL_ABI = [
|
|
28
|
+
'function createTransaction(address provider, address requester, uint256 amount, uint256 deadline, uint256 disputeWindow, bytes32 serviceHash, uint256 agentId)',
|
|
29
|
+
'function linkEscrow(bytes32 txId, address escrowVault, bytes32 escrowId)',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Types
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
export interface ACTPBatchParams {
|
|
37
|
+
/** Provider address */
|
|
38
|
+
provider: string;
|
|
39
|
+
/** Requester address (= Smart Wallet address) */
|
|
40
|
+
requester: string;
|
|
41
|
+
/** Amount in USDC wei (e.g., "1000000" for 1 USDC) */
|
|
42
|
+
amount: string;
|
|
43
|
+
/** Unix timestamp deadline */
|
|
44
|
+
deadline: number;
|
|
45
|
+
/** Dispute window in seconds */
|
|
46
|
+
disputeWindow: number;
|
|
47
|
+
/** Service hash (bytes32) */
|
|
48
|
+
serviceHash: string;
|
|
49
|
+
/** ERC-8004 agent ID (0 if none) */
|
|
50
|
+
agentId: string;
|
|
51
|
+
/** Current ACTP nonce for the requester (from ACTPKernel.requesterNonces) */
|
|
52
|
+
actpNonce: bigint;
|
|
53
|
+
/** Contract addresses */
|
|
54
|
+
contracts: {
|
|
55
|
+
usdc: string;
|
|
56
|
+
actpKernel: string;
|
|
57
|
+
escrowVault: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ACTPBatchResult {
|
|
62
|
+
/** Encoded SmartWalletCall[] ready for executeBatch */
|
|
63
|
+
calls: SmartWalletCall[];
|
|
64
|
+
/** Pre-computed transaction ID */
|
|
65
|
+
txId: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Public API
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Pre-compute ACTP transaction ID.
|
|
74
|
+
*
|
|
75
|
+
* Matches ACTPKernel.sol:
|
|
76
|
+
* transactionId = keccak256(abi.encodePacked(requester, provider, amount, serviceHash, nonce))
|
|
77
|
+
*/
|
|
78
|
+
export function computeTransactionId(
|
|
79
|
+
requester: string,
|
|
80
|
+
provider: string,
|
|
81
|
+
amount: string,
|
|
82
|
+
serviceHash: string,
|
|
83
|
+
nonce: bigint
|
|
84
|
+
): string {
|
|
85
|
+
return ethers.keccak256(
|
|
86
|
+
ethers.solidityPacked(
|
|
87
|
+
['address', 'address', 'uint256', 'bytes32', 'uint256'],
|
|
88
|
+
[requester, provider, BigInt(amount), serviceHash, nonce]
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build the 3-call ACTP payment batch.
|
|
95
|
+
*
|
|
96
|
+
* Returns SmartWalletCall[] for executeBatch and the pre-computed txId.
|
|
97
|
+
*/
|
|
98
|
+
export function buildACTPPayBatch(params: ACTPBatchParams): ACTPBatchResult {
|
|
99
|
+
const amount = BigInt(params.amount);
|
|
100
|
+
|
|
101
|
+
// Pre-compute txId
|
|
102
|
+
const txId = computeTransactionId(
|
|
103
|
+
params.requester,
|
|
104
|
+
params.provider,
|
|
105
|
+
params.amount,
|
|
106
|
+
params.serviceHash,
|
|
107
|
+
params.actpNonce
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const erc20Iface = new ethers.Interface(ERC20_APPROVE_ABI);
|
|
111
|
+
const kernelIface = new ethers.Interface(ACTP_KERNEL_ABI);
|
|
112
|
+
|
|
113
|
+
// Call 1: USDC.approve(escrowVault, amount)
|
|
114
|
+
const approveData = erc20Iface.encodeFunctionData('approve', [
|
|
115
|
+
params.contracts.escrowVault,
|
|
116
|
+
amount,
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
// Call 2: ACTPKernel.createTransaction(...)
|
|
120
|
+
const createTxData = kernelIface.encodeFunctionData('createTransaction', [
|
|
121
|
+
params.provider,
|
|
122
|
+
params.requester,
|
|
123
|
+
amount,
|
|
124
|
+
params.deadline,
|
|
125
|
+
params.disputeWindow,
|
|
126
|
+
params.serviceHash,
|
|
127
|
+
BigInt(params.agentId || '0'),
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
// Call 3: ACTPKernel.linkEscrow(txId, escrowVault, escrowId)
|
|
131
|
+
// escrowId = txId (ACTP standard)
|
|
132
|
+
const linkEscrowData = kernelIface.encodeFunctionData('linkEscrow', [
|
|
133
|
+
txId,
|
|
134
|
+
params.contracts.escrowVault,
|
|
135
|
+
txId,
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const calls: SmartWalletCall[] = [
|
|
139
|
+
{ target: params.contracts.usdc, value: 0n, data: approveData },
|
|
140
|
+
{ target: params.contracts.actpKernel, value: 0n, data: createTxData },
|
|
141
|
+
{ target: params.contracts.actpKernel, value: 0n, data: linkEscrowData },
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
return { calls, txId };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* ABI fragment for AgentRegistry.registerAgent.
|
|
149
|
+
*
|
|
150
|
+
* Matches IAgentRegistry.sol:
|
|
151
|
+
* registerAgent(string endpoint, ServiceDescriptor[] serviceDescriptors)
|
|
152
|
+
*
|
|
153
|
+
* ServiceDescriptor = (bytes32, string, string, uint256, uint256, uint256, string)
|
|
154
|
+
*/
|
|
155
|
+
const AGENT_REGISTRY_ABI = [
|
|
156
|
+
'function registerAgent(string endpoint, (bytes32 serviceTypeHash, string serviceType, string schemaURI, uint256 minPrice, uint256 maxPrice, uint256 avgCompletionTime, string metadataCID)[] serviceDescriptors)',
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build a register-agent batch for AgentRegistry.
|
|
161
|
+
*
|
|
162
|
+
* Used for bootstrap registration (gasless even before registration — chicken-and-egg).
|
|
163
|
+
*
|
|
164
|
+
* @param agentRegistryAddress - AgentRegistry contract address
|
|
165
|
+
* @param endpoint - Agent webhook / IPFS gateway URL
|
|
166
|
+
* @param serviceDescriptors - At least 1 service descriptor (contract requirement)
|
|
167
|
+
*/
|
|
168
|
+
export function buildRegisterAgentBatch(
|
|
169
|
+
agentRegistryAddress: string,
|
|
170
|
+
endpoint: string,
|
|
171
|
+
serviceDescriptors: ServiceDescriptor[]
|
|
172
|
+
): SmartWalletCall[] {
|
|
173
|
+
if (serviceDescriptors.length === 0) {
|
|
174
|
+
throw new Error('At least one service descriptor is required for registration');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const iface = new ethers.Interface(AGENT_REGISTRY_ABI);
|
|
178
|
+
|
|
179
|
+
// Convert to contract format (bigint fields already correct)
|
|
180
|
+
const descriptors = serviceDescriptors.map(sd => ({
|
|
181
|
+
serviceTypeHash: sd.serviceTypeHash,
|
|
182
|
+
serviceType: sd.serviceType,
|
|
183
|
+
schemaURI: sd.schemaURI,
|
|
184
|
+
minPrice: sd.minPrice,
|
|
185
|
+
maxPrice: sd.maxPrice,
|
|
186
|
+
avgCompletionTime: sd.avgCompletionTime,
|
|
187
|
+
metadataCID: sd.metadataCID,
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
const data = iface.encodeFunctionData('registerAgent', [endpoint, descriptors]);
|
|
191
|
+
|
|
192
|
+
return [
|
|
193
|
+
{ target: agentRegistryAddress, value: 0n, data },
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Build a MockUSDC mint call (testnet only).
|
|
199
|
+
*/
|
|
200
|
+
export function buildTestnetMintBatch(
|
|
201
|
+
mockUsdcAddress: string,
|
|
202
|
+
recipient: string,
|
|
203
|
+
amount: string
|
|
204
|
+
): SmartWalletCall[] {
|
|
205
|
+
const iface = new ethers.Interface([
|
|
206
|
+
'function mint(address to, uint256 amount)',
|
|
207
|
+
]);
|
|
208
|
+
const data = iface.encodeFunctionData('mint', [recipient, BigInt(amount)]);
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
{ target: mockUsdcAddress, value: 0n, data },
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Build a combined register + mint batch for testnet init.
|
|
217
|
+
*
|
|
218
|
+
* Single UserOp: register on AgentRegistry + mint test USDC.
|
|
219
|
+
* Both are bootstrap-allowed (gasless without prior registration).
|
|
220
|
+
*/
|
|
221
|
+
export function buildTestnetInitBatch(params: {
|
|
222
|
+
agentRegistryAddress: string;
|
|
223
|
+
endpoint: string;
|
|
224
|
+
serviceDescriptors: ServiceDescriptor[];
|
|
225
|
+
mockUsdcAddress: string;
|
|
226
|
+
recipient: string;
|
|
227
|
+
mintAmount: string;
|
|
228
|
+
}): SmartWalletCall[] {
|
|
229
|
+
const registerCalls = buildRegisterAgentBatch(
|
|
230
|
+
params.agentRegistryAddress,
|
|
231
|
+
params.endpoint,
|
|
232
|
+
params.serviceDescriptors
|
|
233
|
+
);
|
|
234
|
+
const mintCalls = buildTestnetMintBatch(
|
|
235
|
+
params.mockUsdcAddress,
|
|
236
|
+
params.recipient,
|
|
237
|
+
params.mintAmount
|
|
238
|
+
);
|
|
239
|
+
return [...registerCalls, ...mintCalls];
|
|
240
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UserOpBuilder — Constructs ERC-4337 v0.6 UserOperations.
|
|
3
|
+
*
|
|
4
|
+
* Builds UserOps for CoinbaseSmartWallet:
|
|
5
|
+
* - Encodes executeBatch(Call[]) as callData
|
|
6
|
+
* - Adds initCode for first-time wallet deployment
|
|
7
|
+
* - Signs with owner's private key (EIP-191 over UserOp hash)
|
|
8
|
+
*
|
|
9
|
+
* Uses ethers v6 for ABI encoding (no viem dependency yet — see Phase 2 note).
|
|
10
|
+
* viem+permissionless will be added if needed for paymaster integration.
|
|
11
|
+
*
|
|
12
|
+
* @module wallet/aa/UserOpBuilder
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ethers } from 'ethers';
|
|
16
|
+
import {
|
|
17
|
+
UserOperationV06,
|
|
18
|
+
SmartWalletCall,
|
|
19
|
+
ENTRYPOINT_V06,
|
|
20
|
+
SMART_WALLET_FACTORY,
|
|
21
|
+
DEFAULT_WALLET_NONCE,
|
|
22
|
+
} from './constants';
|
|
23
|
+
|
|
24
|
+
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// ABI fragments
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/** CoinbaseSmartWallet.executeBatch(Call[]) */
|
|
31
|
+
const EXECUTE_BATCH_ABI = [
|
|
32
|
+
'function executeBatch((address target, uint256 value, bytes data)[] calls)',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** CoinbaseSmartWalletFactory.createAccount(bytes[],uint256) */
|
|
36
|
+
const FACTORY_CREATE_ABI = [
|
|
37
|
+
'function createAccount(bytes[] owners, uint256 nonce)',
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/** CoinbaseSmartWalletFactory.getAddress(bytes[],uint256) */
|
|
41
|
+
const FACTORY_GET_ADDRESS_ABI = [
|
|
42
|
+
'function getAddress(bytes[] owners, uint256 nonce) view returns (address)',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// Typed factory interface to avoid ethers v6 getAddress() collision
|
|
46
|
+
const FACTORY_ABI = [
|
|
47
|
+
...FACTORY_CREATE_ABI,
|
|
48
|
+
...FACTORY_GET_ADDRESS_ABI,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Public API
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compute the counterfactual Smart Wallet address for a given signer.
|
|
57
|
+
*
|
|
58
|
+
* This address is deterministic (CREATE2) and can be computed off-chain
|
|
59
|
+
* without deploying the wallet.
|
|
60
|
+
*/
|
|
61
|
+
export async function computeSmartWalletAddress(
|
|
62
|
+
signerAddress: string,
|
|
63
|
+
provider: ethers.JsonRpcProvider,
|
|
64
|
+
nonce: bigint = DEFAULT_WALLET_NONCE
|
|
65
|
+
): Promise<string> {
|
|
66
|
+
const factory = new ethers.Contract(
|
|
67
|
+
SMART_WALLET_FACTORY,
|
|
68
|
+
FACTORY_ABI,
|
|
69
|
+
provider
|
|
70
|
+
);
|
|
71
|
+
// CoinbaseSmartWallet encodes owners as bytes[] — EOA address is abi.encode(address)
|
|
72
|
+
const ownerBytes = abiCoder.encode(['address'], [signerAddress]);
|
|
73
|
+
// Use getFunction to avoid collision with ethers v6 BaseContract.getAddress()
|
|
74
|
+
const fn = factory.getFunction('getAddress');
|
|
75
|
+
return await fn([ownerBytes], nonce);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build initCode for first-time wallet deployment.
|
|
80
|
+
*
|
|
81
|
+
* initCode = factory address + createAccount calldata
|
|
82
|
+
* When the wallet already exists, pass '0x' as initCode.
|
|
83
|
+
*/
|
|
84
|
+
export function buildInitCode(
|
|
85
|
+
signerAddress: string,
|
|
86
|
+
nonce: bigint = DEFAULT_WALLET_NONCE
|
|
87
|
+
): string {
|
|
88
|
+
const iface = new ethers.Interface(FACTORY_CREATE_ABI);
|
|
89
|
+
const ownerBytes = abiCoder.encode(['address'], [signerAddress]);
|
|
90
|
+
const calldata = iface.encodeFunctionData('createAccount', [
|
|
91
|
+
[ownerBytes],
|
|
92
|
+
nonce,
|
|
93
|
+
]);
|
|
94
|
+
// initCode = factory address (20 bytes) + calldata
|
|
95
|
+
return SMART_WALLET_FACTORY + calldata.slice(2);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Encode executeBatch calldata from an array of calls.
|
|
100
|
+
*/
|
|
101
|
+
export function encodeExecuteBatch(calls: SmartWalletCall[]): string {
|
|
102
|
+
const iface = new ethers.Interface(EXECUTE_BATCH_ABI);
|
|
103
|
+
return iface.encodeFunctionData('executeBatch', [
|
|
104
|
+
calls.map((c) => ({
|
|
105
|
+
target: c.target,
|
|
106
|
+
value: c.value,
|
|
107
|
+
data: c.data,
|
|
108
|
+
})),
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build a full UserOperation (unsigned).
|
|
114
|
+
*
|
|
115
|
+
* Gas limits and paymasterAndData must be filled by the caller
|
|
116
|
+
* (via BundlerClient.estimateGas and PaymasterClient.sponsor).
|
|
117
|
+
*/
|
|
118
|
+
export function buildUserOp(params: {
|
|
119
|
+
sender: string;
|
|
120
|
+
nonce: bigint;
|
|
121
|
+
calls: SmartWalletCall[];
|
|
122
|
+
isFirstDeploy: boolean;
|
|
123
|
+
signerAddress: string;
|
|
124
|
+
}): UserOperationV06 {
|
|
125
|
+
const callData = encodeExecuteBatch(params.calls);
|
|
126
|
+
const initCode = params.isFirstDeploy
|
|
127
|
+
? buildInitCode(params.signerAddress)
|
|
128
|
+
: '0x';
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
sender: params.sender,
|
|
132
|
+
nonce: params.nonce,
|
|
133
|
+
initCode,
|
|
134
|
+
callData,
|
|
135
|
+
// Placeholder values — filled by gas estimation
|
|
136
|
+
callGasLimit: 0n,
|
|
137
|
+
verificationGasLimit: 0n,
|
|
138
|
+
preVerificationGas: 0n,
|
|
139
|
+
maxFeePerGas: 0n,
|
|
140
|
+
maxPriorityFeePerGas: 0n,
|
|
141
|
+
paymasterAndData: '0x',
|
|
142
|
+
signature: '0x',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Compute the UserOperation hash for signing (v0.6).
|
|
148
|
+
*
|
|
149
|
+
* hash = keccak256(abi.encode(
|
|
150
|
+
* keccak256(pack(userOp)),
|
|
151
|
+
* entryPoint,
|
|
152
|
+
* chainId
|
|
153
|
+
* ))
|
|
154
|
+
*/
|
|
155
|
+
export function getUserOpHash(
|
|
156
|
+
userOp: UserOperationV06,
|
|
157
|
+
chainId: number
|
|
158
|
+
): string {
|
|
159
|
+
// Pack all fields except signature
|
|
160
|
+
const packed = abiCoder.encode(
|
|
161
|
+
[
|
|
162
|
+
'address', // sender
|
|
163
|
+
'uint256', // nonce
|
|
164
|
+
'bytes32', // keccak256(initCode)
|
|
165
|
+
'bytes32', // keccak256(callData)
|
|
166
|
+
'uint256', // callGasLimit
|
|
167
|
+
'uint256', // verificationGasLimit
|
|
168
|
+
'uint256', // preVerificationGas
|
|
169
|
+
'uint256', // maxFeePerGas
|
|
170
|
+
'uint256', // maxPriorityFeePerGas
|
|
171
|
+
'bytes32', // keccak256(paymasterAndData)
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
userOp.sender,
|
|
175
|
+
userOp.nonce,
|
|
176
|
+
ethers.keccak256(userOp.initCode),
|
|
177
|
+
ethers.keccak256(userOp.callData),
|
|
178
|
+
userOp.callGasLimit,
|
|
179
|
+
userOp.verificationGasLimit,
|
|
180
|
+
userOp.preVerificationGas,
|
|
181
|
+
userOp.maxFeePerGas,
|
|
182
|
+
userOp.maxPriorityFeePerGas,
|
|
183
|
+
ethers.keccak256(userOp.paymasterAndData),
|
|
184
|
+
]
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const packedHash = ethers.keccak256(packed);
|
|
188
|
+
|
|
189
|
+
return ethers.keccak256(
|
|
190
|
+
abiCoder.encode(
|
|
191
|
+
['bytes32', 'address', 'uint256'],
|
|
192
|
+
[packedHash, ENTRYPOINT_V06, chainId]
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Sign a UserOperation with the owner's private key.
|
|
199
|
+
*
|
|
200
|
+
* CoinbaseSmartWallet expects the signature to be:
|
|
201
|
+
* abi.encode(SignatureWrapper(0, abi.encodePacked(r,s,v)))
|
|
202
|
+
*
|
|
203
|
+
* where ownerIndex=0 for single-owner wallets.
|
|
204
|
+
*/
|
|
205
|
+
export async function signUserOp(
|
|
206
|
+
userOp: UserOperationV06,
|
|
207
|
+
signer: ethers.Wallet,
|
|
208
|
+
chainId: number
|
|
209
|
+
): Promise<string> {
|
|
210
|
+
const hash = getUserOpHash(userOp, chainId);
|
|
211
|
+
// ethers signMessage adds EIP-191 prefix. CoinbaseSmartWallet expects raw ECDSA.
|
|
212
|
+
// We use signingKey.sign() for raw signature over the hash bytes.
|
|
213
|
+
const sig = signer.signingKey.sign(hash);
|
|
214
|
+
// Pack r + s + v
|
|
215
|
+
const rawSig = ethers.concat([sig.r, sig.s, new Uint8Array([sig.v])]);
|
|
216
|
+
|
|
217
|
+
// CoinbaseSmartWallet SignatureWrapper: abi.encode(uint256 ownerIndex, bytes signatureData)
|
|
218
|
+
return abiCoder.encode(
|
|
219
|
+
['uint256', 'bytes'],
|
|
220
|
+
[0, rawSig]
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Serialize UserOp for JSON-RPC (bundler API).
|
|
226
|
+
* Converts bigints to hex strings.
|
|
227
|
+
*/
|
|
228
|
+
export function serializeUserOp(userOp: UserOperationV06): Record<string, string> {
|
|
229
|
+
return {
|
|
230
|
+
sender: userOp.sender,
|
|
231
|
+
nonce: toHex(userOp.nonce),
|
|
232
|
+
initCode: userOp.initCode,
|
|
233
|
+
callData: userOp.callData,
|
|
234
|
+
callGasLimit: toHex(userOp.callGasLimit),
|
|
235
|
+
verificationGasLimit: toHex(userOp.verificationGasLimit),
|
|
236
|
+
preVerificationGas: toHex(userOp.preVerificationGas),
|
|
237
|
+
maxFeePerGas: toHex(userOp.maxFeePerGas),
|
|
238
|
+
maxPriorityFeePerGas: toHex(userOp.maxPriorityFeePerGas),
|
|
239
|
+
paymasterAndData: userOp.paymasterAndData,
|
|
240
|
+
signature: userOp.signature,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function toHex(n: bigint): string {
|
|
245
|
+
return '0x' + n.toString(16);
|
|
246
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account Abstraction constants for CoinbaseSmartWallet on Base.
|
|
3
|
+
*
|
|
4
|
+
* EntryPoint v0.6 — CoinbaseSmartWallet hardcodes this version.
|
|
5
|
+
* Factory address is canonical across all Base networks.
|
|
6
|
+
*
|
|
7
|
+
* @module wallet/aa/constants
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** ERC-4337 EntryPoint v0.6 (canonical, all EVM chains) */
|
|
11
|
+
export const ENTRYPOINT_V06 = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';
|
|
12
|
+
|
|
13
|
+
/** CoinbaseSmartWallet factory (canonical, all Base networks) */
|
|
14
|
+
export const SMART_WALLET_FACTORY = '0xBA5ED110eFDBa3D005bfC882d75358ACBbB85842';
|
|
15
|
+
|
|
16
|
+
/** Default nonce for first Smart Wallet per owner */
|
|
17
|
+
export const DEFAULT_WALLET_NONCE = 0n;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* UserOperation v0.6 struct — 11 unpacked fields.
|
|
21
|
+
* CoinbaseSmartWallet does NOT support v0.7 packed format.
|
|
22
|
+
*/
|
|
23
|
+
export interface UserOperationV06 {
|
|
24
|
+
sender: string;
|
|
25
|
+
nonce: bigint;
|
|
26
|
+
initCode: string;
|
|
27
|
+
callData: string;
|
|
28
|
+
callGasLimit: bigint;
|
|
29
|
+
verificationGasLimit: bigint;
|
|
30
|
+
preVerificationGas: bigint;
|
|
31
|
+
maxFeePerGas: bigint;
|
|
32
|
+
maxPriorityFeePerGas: bigint;
|
|
33
|
+
paymasterAndData: string;
|
|
34
|
+
signature: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* CoinbaseSmartWallet Call struct for executeBatch.
|
|
39
|
+
*/
|
|
40
|
+
export interface SmartWalletCall {
|
|
41
|
+
target: string;
|
|
42
|
+
value: bigint;
|
|
43
|
+
data: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gas estimation result from bundler.
|
|
48
|
+
*/
|
|
49
|
+
export interface GasEstimate {
|
|
50
|
+
callGasLimit: bigint;
|
|
51
|
+
verificationGasLimit: bigint;
|
|
52
|
+
preVerificationGas: bigint;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Paymaster sponsorship response.
|
|
57
|
+
*/
|
|
58
|
+
export interface PaymasterResponse {
|
|
59
|
+
paymasterAndData: string;
|
|
60
|
+
}
|