@1llet.xyz/erc4337-gasless-sdk 0.4.70 → 0.4.72
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/dist/index.d.mts +168 -87
- package/dist/index.d.ts +168 -87
- package/dist/index.js +1596 -967
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1582 -953
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseAbi, createPublicClient, http, encodeFunctionData, createWalletClient, decodeErrorResult, getAddress, parseUnits, formatUnits, erc20Abi as erc20Abi$1, encodeAbiParameters, keccak256, maxUint256, padHex, toHex } from 'viem';
|
|
2
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
|
+
import { monad, unichain, arbitrum, polygon, bsc, avalanche, optimism, gnosis, baseSepolia, base, optimismSepolia, mainnet, worldchain } from 'viem/chains';
|
|
2
4
|
import * as StellarSdk from 'stellar-sdk';
|
|
3
5
|
import { Networks } from 'stellar-sdk';
|
|
4
6
|
import axios from 'axios';
|
|
5
|
-
import {
|
|
6
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
7
|
-
import { OpenAPI, OneClickService, QuoteRequest } from '@defuse-protocol/one-click-sdk-typescript';
|
|
7
|
+
import { OpenAPI, QuoteRequest, OneClickService } from '@defuse-protocol/one-click-sdk-typescript';
|
|
8
8
|
|
|
9
9
|
var __defProp = Object.defineProperty;
|
|
10
10
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -16,6 +16,858 @@ var __export = (target, all) => {
|
|
|
16
16
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
// src/constants.ts
|
|
20
|
+
var factoryAbi, entryPointAbi, smartAccountAbi, erc20Abi;
|
|
21
|
+
var init_constants = __esm({
|
|
22
|
+
"src/constants.ts"() {
|
|
23
|
+
factoryAbi = [
|
|
24
|
+
{
|
|
25
|
+
inputs: [
|
|
26
|
+
{ name: "owner", type: "address" },
|
|
27
|
+
{ name: "salt", type: "uint256" }
|
|
28
|
+
],
|
|
29
|
+
name: "getAccountAddress",
|
|
30
|
+
outputs: [{ name: "", type: "address" }],
|
|
31
|
+
stateMutability: "view",
|
|
32
|
+
type: "function"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
inputs: [
|
|
36
|
+
{ name: "owner", type: "address" },
|
|
37
|
+
{ name: "salt", type: "uint256" }
|
|
38
|
+
],
|
|
39
|
+
name: "isAccountDeployed",
|
|
40
|
+
outputs: [{ name: "", type: "bool" }],
|
|
41
|
+
stateMutability: "view",
|
|
42
|
+
type: "function"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
inputs: [
|
|
46
|
+
{ name: "owner", type: "address" },
|
|
47
|
+
{ name: "salt", type: "uint256" }
|
|
48
|
+
],
|
|
49
|
+
name: "createAccount",
|
|
50
|
+
outputs: [{ name: "account", type: "address" }],
|
|
51
|
+
stateMutability: "nonpayable",
|
|
52
|
+
type: "function"
|
|
53
|
+
}
|
|
54
|
+
];
|
|
55
|
+
entryPointAbi = [
|
|
56
|
+
{
|
|
57
|
+
inputs: [
|
|
58
|
+
{ name: "sender", type: "address" },
|
|
59
|
+
{ name: "key", type: "uint192" }
|
|
60
|
+
],
|
|
61
|
+
name: "getNonce",
|
|
62
|
+
outputs: [{ name: "nonce", type: "uint256" }],
|
|
63
|
+
stateMutability: "view",
|
|
64
|
+
type: "function"
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
smartAccountAbi = [
|
|
68
|
+
{
|
|
69
|
+
inputs: [
|
|
70
|
+
{ name: "target", type: "address" },
|
|
71
|
+
{ name: "value", type: "uint256" },
|
|
72
|
+
{ name: "data", type: "bytes" }
|
|
73
|
+
],
|
|
74
|
+
name: "execute",
|
|
75
|
+
outputs: [],
|
|
76
|
+
stateMutability: "nonpayable",
|
|
77
|
+
type: "function"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
inputs: [
|
|
81
|
+
{ name: "targets", type: "address[]" },
|
|
82
|
+
{ name: "values", type: "uint256[]" },
|
|
83
|
+
{ name: "datas", type: "bytes[]" }
|
|
84
|
+
],
|
|
85
|
+
name: "executeBatch",
|
|
86
|
+
outputs: [],
|
|
87
|
+
stateMutability: "nonpayable",
|
|
88
|
+
type: "function"
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
erc20Abi = [
|
|
92
|
+
{
|
|
93
|
+
inputs: [{ name: "account", type: "address" }],
|
|
94
|
+
name: "balanceOf",
|
|
95
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
96
|
+
stateMutability: "view",
|
|
97
|
+
type: "function"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
inputs: [
|
|
101
|
+
{ name: "to", type: "address" },
|
|
102
|
+
{ name: "amount", type: "uint256" }
|
|
103
|
+
],
|
|
104
|
+
name: "transfer",
|
|
105
|
+
outputs: [{ name: "", type: "bool" }],
|
|
106
|
+
stateMutability: "nonpayable",
|
|
107
|
+
type: "function"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
inputs: [
|
|
111
|
+
{ name: "spender", type: "address" },
|
|
112
|
+
{ name: "amount", type: "uint256" }
|
|
113
|
+
],
|
|
114
|
+
name: "approve",
|
|
115
|
+
outputs: [{ name: "", type: "bool" }],
|
|
116
|
+
stateMutability: "nonpayable",
|
|
117
|
+
type: "function"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
inputs: [
|
|
121
|
+
{ name: "owner", type: "address" },
|
|
122
|
+
{ name: "spender", type: "address" }
|
|
123
|
+
],
|
|
124
|
+
name: "allowance",
|
|
125
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
126
|
+
stateMutability: "view",
|
|
127
|
+
type: "function"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
inputs: [
|
|
131
|
+
{ name: "from", type: "address" },
|
|
132
|
+
{ name: "to", type: "address" },
|
|
133
|
+
{ name: "amount", type: "uint256" }
|
|
134
|
+
],
|
|
135
|
+
name: "transferFrom",
|
|
136
|
+
outputs: [{ name: "", type: "bool" }],
|
|
137
|
+
stateMutability: "nonpayable",
|
|
138
|
+
type: "function"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
inputs: [],
|
|
142
|
+
name: "decimals",
|
|
143
|
+
outputs: [{ name: "", type: "uint8" }],
|
|
144
|
+
stateMutability: "view",
|
|
145
|
+
type: "function"
|
|
146
|
+
}
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// src/BundlerClient.ts
|
|
152
|
+
var BundlerClient;
|
|
153
|
+
var init_BundlerClient = __esm({
|
|
154
|
+
"src/BundlerClient.ts"() {
|
|
155
|
+
BundlerClient = class {
|
|
156
|
+
constructor(config, entryPointAddress) {
|
|
157
|
+
this.bundlerUrl = config.bundlerUrl;
|
|
158
|
+
this.entryPointAddress = entryPointAddress;
|
|
159
|
+
}
|
|
160
|
+
async call(method, params) {
|
|
161
|
+
const response = await fetch(this.bundlerUrl, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: { "Content-Type": "application/json" },
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
jsonrpc: "2.0",
|
|
166
|
+
id: 1,
|
|
167
|
+
method,
|
|
168
|
+
params
|
|
169
|
+
})
|
|
170
|
+
});
|
|
171
|
+
const result = await response.json();
|
|
172
|
+
if (result.error) {
|
|
173
|
+
throw new Error(result.error.message);
|
|
174
|
+
}
|
|
175
|
+
return result.result;
|
|
176
|
+
}
|
|
177
|
+
async estimateGas(userOp) {
|
|
178
|
+
const result = await this.call("eth_estimateUserOperationGas", [
|
|
179
|
+
{
|
|
180
|
+
sender: userOp.sender,
|
|
181
|
+
nonce: userOp.nonce ? "0x" + userOp.nonce.toString(16) : "0x0",
|
|
182
|
+
initCode: userOp.initCode || "0x",
|
|
183
|
+
callData: userOp.callData || "0x",
|
|
184
|
+
paymasterAndData: userOp.paymasterAndData || "0x",
|
|
185
|
+
signature: "0x"
|
|
186
|
+
},
|
|
187
|
+
this.entryPointAddress
|
|
188
|
+
]);
|
|
189
|
+
console.log("DEBUG: estimateGas result:", result);
|
|
190
|
+
return {
|
|
191
|
+
callGasLimit: result.callGasLimit,
|
|
192
|
+
verificationGasLimit: result.verificationGasLimit,
|
|
193
|
+
preVerificationGas: result.preVerificationGas,
|
|
194
|
+
maxFeePerGas: result.maxFeePerGas,
|
|
195
|
+
maxPriorityFeePerGas: result.maxPriorityFeePerGas,
|
|
196
|
+
paymasterAndData: result.paymasterAndData
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
async sendUserOperation(userOp) {
|
|
200
|
+
return await this.call("eth_sendUserOperation", [
|
|
201
|
+
{
|
|
202
|
+
sender: userOp.sender,
|
|
203
|
+
nonce: "0x" + userOp.nonce.toString(16),
|
|
204
|
+
initCode: userOp.initCode,
|
|
205
|
+
callData: userOp.callData,
|
|
206
|
+
callGasLimit: "0x" + userOp.callGasLimit.toString(16),
|
|
207
|
+
verificationGasLimit: "0x" + userOp.verificationGasLimit.toString(16),
|
|
208
|
+
preVerificationGas: "0x" + userOp.preVerificationGas.toString(16),
|
|
209
|
+
maxFeePerGas: "0x" + userOp.maxFeePerGas.toString(16),
|
|
210
|
+
maxPriorityFeePerGas: "0x" + userOp.maxPriorityFeePerGas.toString(16),
|
|
211
|
+
paymasterAndData: userOp.paymasterAndData,
|
|
212
|
+
signature: userOp.signature
|
|
213
|
+
},
|
|
214
|
+
this.entryPointAddress
|
|
215
|
+
]);
|
|
216
|
+
}
|
|
217
|
+
async waitForUserOperation(userOpHash, timeout = 6e4) {
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
while (Date.now() - startTime < timeout) {
|
|
220
|
+
const result = await this.call("eth_getUserOperationReceipt", [userOpHash]);
|
|
221
|
+
if (result) {
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
225
|
+
}
|
|
226
|
+
throw new Error("Timeout waiting for UserOperation");
|
|
227
|
+
}
|
|
228
|
+
async requestApprovalSupport(token, owner, spender, amount) {
|
|
229
|
+
return await this.call("pm_requestApprovalSupport", [
|
|
230
|
+
token,
|
|
231
|
+
owner,
|
|
232
|
+
spender,
|
|
233
|
+
amount.toString()
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
236
|
+
async scheduleUserOp(userOp, condition) {
|
|
237
|
+
return await this.call("bundler_scheduleUserOp", [
|
|
238
|
+
{
|
|
239
|
+
sender: userOp.sender,
|
|
240
|
+
nonce: "0x" + userOp.nonce.toString(16),
|
|
241
|
+
initCode: userOp.initCode,
|
|
242
|
+
callData: userOp.callData,
|
|
243
|
+
callGasLimit: "0x" + userOp.callGasLimit.toString(16),
|
|
244
|
+
verificationGasLimit: "0x" + userOp.verificationGasLimit.toString(16),
|
|
245
|
+
preVerificationGas: "0x" + userOp.preVerificationGas.toString(16),
|
|
246
|
+
maxFeePerGas: "0x" + userOp.maxFeePerGas.toString(16),
|
|
247
|
+
maxPriorityFeePerGas: "0x" + userOp.maxPriorityFeePerGas.toString(16),
|
|
248
|
+
paymasterAndData: userOp.paymasterAndData,
|
|
249
|
+
signature: userOp.signature
|
|
250
|
+
},
|
|
251
|
+
condition
|
|
252
|
+
]);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
var TokenService;
|
|
258
|
+
var init_TokenService = __esm({
|
|
259
|
+
"src/TokenService.ts"() {
|
|
260
|
+
init_constants();
|
|
261
|
+
TokenService = class {
|
|
262
|
+
constructor(chainConfig, publicClient) {
|
|
263
|
+
this.tokens = /* @__PURE__ */ new Map();
|
|
264
|
+
this.publicClient = publicClient;
|
|
265
|
+
chainConfig.tokens.forEach((token) => {
|
|
266
|
+
this.tokens.set(token.symbol.toUpperCase(), token);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Resolve token address from symbol or return address if provided
|
|
271
|
+
*/
|
|
272
|
+
getTokenAddress(token) {
|
|
273
|
+
if (token.startsWith("0x")) return token;
|
|
274
|
+
const info = this.tokens.get(token.toUpperCase());
|
|
275
|
+
if (!info) throw new Error(`Token ${token} not found in chain config`);
|
|
276
|
+
return info.address;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get balance of a token for an account
|
|
280
|
+
*/
|
|
281
|
+
async getBalance(token, account) {
|
|
282
|
+
const address = this.getTokenAddress(token);
|
|
283
|
+
if (address === "0x0000000000000000000000000000000000000000") {
|
|
284
|
+
return await this.publicClient.getBalance({ address: account });
|
|
285
|
+
}
|
|
286
|
+
return await this.publicClient.readContract({
|
|
287
|
+
address,
|
|
288
|
+
abi: erc20Abi,
|
|
289
|
+
functionName: "balanceOf",
|
|
290
|
+
args: [account]
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get allowance (ERC-20 only)
|
|
295
|
+
*/
|
|
296
|
+
async getAllowance(token, owner, spender) {
|
|
297
|
+
const address = this.getTokenAddress(token);
|
|
298
|
+
if (address === "0x0000000000000000000000000000000000000000") {
|
|
299
|
+
return 0n;
|
|
300
|
+
}
|
|
301
|
+
return await this.publicClient.readContract({
|
|
302
|
+
address,
|
|
303
|
+
abi: erc20Abi,
|
|
304
|
+
functionName: "allowance",
|
|
305
|
+
args: [owner, spender]
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Encode transfer data
|
|
310
|
+
*/
|
|
311
|
+
encodeTransfer(recipient, amount) {
|
|
312
|
+
return encodeFunctionData({
|
|
313
|
+
abi: erc20Abi,
|
|
314
|
+
functionName: "transfer",
|
|
315
|
+
args: [recipient, amount]
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Encode transferFrom data
|
|
320
|
+
*/
|
|
321
|
+
encodeTransferFrom(sender, recipient, amount) {
|
|
322
|
+
return encodeFunctionData({
|
|
323
|
+
abi: erc20Abi,
|
|
324
|
+
functionName: "transferFrom",
|
|
325
|
+
args: [sender, recipient, amount]
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Encode approve data
|
|
330
|
+
*/
|
|
331
|
+
encodeApprove(spender, amount) {
|
|
332
|
+
return encodeFunctionData({
|
|
333
|
+
abi: erc20Abi,
|
|
334
|
+
functionName: "approve",
|
|
335
|
+
args: [spender, amount]
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
var UserOpBuilder;
|
|
342
|
+
var init_UserOpBuilder = __esm({
|
|
343
|
+
"src/UserOpBuilder.ts"() {
|
|
344
|
+
init_constants();
|
|
345
|
+
UserOpBuilder = class {
|
|
346
|
+
constructor(chainConfig, bundlerClient, publicClient) {
|
|
347
|
+
this.chainConfig = chainConfig;
|
|
348
|
+
this.bundlerClient = bundlerClient;
|
|
349
|
+
this.publicClient = publicClient;
|
|
350
|
+
this.entryPointAddress = chainConfig.entryPointAddress;
|
|
351
|
+
this.factoryAddress = chainConfig.factoryAddress;
|
|
352
|
+
}
|
|
353
|
+
async getNonce(smartAccountAddress) {
|
|
354
|
+
return await this.publicClient.readContract({
|
|
355
|
+
address: this.entryPointAddress,
|
|
356
|
+
abi: entryPointAbi,
|
|
357
|
+
functionName: "getNonce",
|
|
358
|
+
args: [smartAccountAddress, 0n]
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
buildInitCode(owner) {
|
|
362
|
+
const createAccountData = encodeFunctionData({
|
|
363
|
+
abi: factoryAbi,
|
|
364
|
+
functionName: "createAccount",
|
|
365
|
+
args: [owner, 0n]
|
|
366
|
+
});
|
|
367
|
+
return `${this.factoryAddress}${createAccountData.slice(2)}`;
|
|
368
|
+
}
|
|
369
|
+
async isAccountDeployed(smartAccountAddress) {
|
|
370
|
+
const code = await this.publicClient.getCode({
|
|
371
|
+
address: smartAccountAddress
|
|
372
|
+
});
|
|
373
|
+
return code !== void 0 && code !== "0x";
|
|
374
|
+
}
|
|
375
|
+
async buildUserOperationBatch(owner, smartAccountAddress, transactions) {
|
|
376
|
+
const isDeployed = await this.isAccountDeployed(smartAccountAddress);
|
|
377
|
+
const initCode = isDeployed ? "0x" : this.buildInitCode(owner);
|
|
378
|
+
const targets = transactions.map((tx) => tx.target);
|
|
379
|
+
const values = transactions.map((tx) => tx.value);
|
|
380
|
+
const datas = transactions.map((tx) => tx.data);
|
|
381
|
+
const callData = encodeFunctionData({
|
|
382
|
+
abi: smartAccountAbi,
|
|
383
|
+
functionName: "executeBatch",
|
|
384
|
+
args: [targets, values, datas]
|
|
385
|
+
});
|
|
386
|
+
const nonce = await this.getNonce(smartAccountAddress);
|
|
387
|
+
const partialOp = {
|
|
388
|
+
sender: smartAccountAddress,
|
|
389
|
+
nonce,
|
|
390
|
+
initCode,
|
|
391
|
+
callData,
|
|
392
|
+
paymasterAndData: this.chainConfig.paymasterAddress || "0x"
|
|
393
|
+
};
|
|
394
|
+
const gasEstimate = await this.bundlerClient.estimateGas(partialOp);
|
|
395
|
+
return {
|
|
396
|
+
...partialOp,
|
|
397
|
+
callGasLimit: BigInt(gasEstimate.callGasLimit),
|
|
398
|
+
verificationGasLimit: BigInt(gasEstimate.verificationGasLimit),
|
|
399
|
+
preVerificationGas: BigInt(gasEstimate.preVerificationGas),
|
|
400
|
+
maxFeePerGas: BigInt(gasEstimate.maxFeePerGas),
|
|
401
|
+
maxPriorityFeePerGas: BigInt(gasEstimate.maxPriorityFeePerGas),
|
|
402
|
+
paymasterAndData: gasEstimate.paymasterAndData || partialOp.paymasterAndData,
|
|
403
|
+
signature: "0x"
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async buildDeployUserOp(owner, smartAccountAddress) {
|
|
407
|
+
const isDeployed = await this.isAccountDeployed(smartAccountAddress);
|
|
408
|
+
if (isDeployed) throw new Error("Account already deployed");
|
|
409
|
+
const initCode = this.buildInitCode(owner);
|
|
410
|
+
const callData = "0x";
|
|
411
|
+
const nonce = await this.getNonce(smartAccountAddress);
|
|
412
|
+
const partialOp = {
|
|
413
|
+
sender: smartAccountAddress,
|
|
414
|
+
nonce,
|
|
415
|
+
initCode,
|
|
416
|
+
callData,
|
|
417
|
+
paymasterAndData: this.chainConfig.paymasterAddress || "0x"
|
|
418
|
+
};
|
|
419
|
+
const gasEstimate = await this.bundlerClient.estimateGas(partialOp);
|
|
420
|
+
return {
|
|
421
|
+
...partialOp,
|
|
422
|
+
callGasLimit: BigInt(gasEstimate.callGasLimit),
|
|
423
|
+
verificationGasLimit: BigInt(gasEstimate.verificationGasLimit),
|
|
424
|
+
preVerificationGas: BigInt(gasEstimate.preVerificationGas),
|
|
425
|
+
maxFeePerGas: BigInt(gasEstimate.maxFeePerGas),
|
|
426
|
+
maxPriorityFeePerGas: BigInt(gasEstimate.maxPriorityFeePerGas),
|
|
427
|
+
paymasterAndData: gasEstimate.paymasterAndData || partialOp.paymasterAndData,
|
|
428
|
+
signature: "0x"
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
getUserOpHash(userOp) {
|
|
432
|
+
const packed = encodeAbiParameters(
|
|
433
|
+
[
|
|
434
|
+
{ type: "address" },
|
|
435
|
+
{ type: "uint256" },
|
|
436
|
+
{ type: "bytes32" },
|
|
437
|
+
{ type: "bytes32" },
|
|
438
|
+
{ type: "uint256" },
|
|
439
|
+
{ type: "uint256" },
|
|
440
|
+
{ type: "uint256" },
|
|
441
|
+
{ type: "uint256" },
|
|
442
|
+
{ type: "uint256" },
|
|
443
|
+
{ type: "bytes32" }
|
|
444
|
+
],
|
|
445
|
+
[
|
|
446
|
+
userOp.sender,
|
|
447
|
+
userOp.nonce,
|
|
448
|
+
keccak256(userOp.initCode),
|
|
449
|
+
keccak256(userOp.callData),
|
|
450
|
+
userOp.callGasLimit,
|
|
451
|
+
userOp.verificationGasLimit,
|
|
452
|
+
userOp.preVerificationGas,
|
|
453
|
+
userOp.maxFeePerGas,
|
|
454
|
+
userOp.maxPriorityFeePerGas,
|
|
455
|
+
keccak256(userOp.paymasterAndData)
|
|
456
|
+
]
|
|
457
|
+
);
|
|
458
|
+
const packedHash = keccak256(packed);
|
|
459
|
+
return keccak256(
|
|
460
|
+
encodeAbiParameters(
|
|
461
|
+
[{ type: "bytes32" }, { type: "address" }, { type: "uint256" }],
|
|
462
|
+
[packedHash, this.entryPointAddress, BigInt(this.chainConfig.chain.id)]
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
var AccountAbstraction;
|
|
470
|
+
var init_AccountAbstraction = __esm({
|
|
471
|
+
"src/AccountAbstraction.ts"() {
|
|
472
|
+
init_constants();
|
|
473
|
+
init_BundlerClient();
|
|
474
|
+
init_TokenService();
|
|
475
|
+
init_UserOpBuilder();
|
|
476
|
+
AccountAbstraction = class {
|
|
477
|
+
constructor(chainConfig) {
|
|
478
|
+
this.owner = null;
|
|
479
|
+
this.smartAccountAddress = null;
|
|
480
|
+
this.walletClient = null;
|
|
481
|
+
this.chainConfig = chainConfig;
|
|
482
|
+
if (!chainConfig.entryPointAddress) throw new Error("EntryPoint address required");
|
|
483
|
+
this.entryPointAddress = chainConfig.entryPointAddress;
|
|
484
|
+
if (!chainConfig.factoryAddress) throw new Error("Factory address required");
|
|
485
|
+
this.factoryAddress = chainConfig.factoryAddress;
|
|
486
|
+
const rpcUrl = chainConfig.rpcUrl || chainConfig.chain.rpcUrls.default.http[0];
|
|
487
|
+
this.publicClient = createPublicClient({
|
|
488
|
+
chain: chainConfig.chain,
|
|
489
|
+
transport: http(rpcUrl)
|
|
490
|
+
});
|
|
491
|
+
this.bundlerClient = new BundlerClient(chainConfig, this.entryPointAddress);
|
|
492
|
+
this.tokenService = new TokenService(chainConfig, this.publicClient);
|
|
493
|
+
this.userOpBuilder = new UserOpBuilder(chainConfig, this.bundlerClient, this.publicClient);
|
|
494
|
+
}
|
|
495
|
+
getBundlerClient() {
|
|
496
|
+
return this.bundlerClient;
|
|
497
|
+
}
|
|
498
|
+
getChainId() {
|
|
499
|
+
return this.chainConfig.chain.id;
|
|
500
|
+
}
|
|
501
|
+
getPublicClient() {
|
|
502
|
+
return this.publicClient;
|
|
503
|
+
}
|
|
504
|
+
async buildUserOperation(transaction) {
|
|
505
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
506
|
+
return this.userOpBuilder.buildUserOperationBatch(this.owner, this.smartAccountAddress, [transaction]);
|
|
507
|
+
}
|
|
508
|
+
async buildBatchUserOperation(transactions) {
|
|
509
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
510
|
+
return this.userOpBuilder.buildUserOperationBatch(this.owner, this.smartAccountAddress, transactions);
|
|
511
|
+
}
|
|
512
|
+
async connect(signer) {
|
|
513
|
+
if (typeof signer === "string") {
|
|
514
|
+
const account = privateKeyToAccount(signer);
|
|
515
|
+
this.owner = account.address;
|
|
516
|
+
const rpcUrl = this.chainConfig.rpcUrl || this.chainConfig.chain.rpcUrls.default.http[0];
|
|
517
|
+
this.walletClient = createWalletClient({
|
|
518
|
+
account,
|
|
519
|
+
chain: this.chainConfig.chain,
|
|
520
|
+
transport: http(rpcUrl)
|
|
521
|
+
});
|
|
522
|
+
} else if (signer && typeof signer === "object") {
|
|
523
|
+
this.walletClient = signer;
|
|
524
|
+
if (!this.walletClient.account) throw new Error("WalletClient must have an account");
|
|
525
|
+
this.owner = this.walletClient.account.address;
|
|
526
|
+
} else {
|
|
527
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
528
|
+
throw new Error("MetaMask is not installed and no private key provided");
|
|
529
|
+
}
|
|
530
|
+
const accounts = await window.ethereum.request({
|
|
531
|
+
method: "eth_requestAccounts"
|
|
532
|
+
});
|
|
533
|
+
if (!accounts || accounts.length === 0) throw new Error("No accounts found");
|
|
534
|
+
const chainId = await window.ethereum.request({
|
|
535
|
+
method: "eth_chainId"
|
|
536
|
+
});
|
|
537
|
+
const targetChainId = this.chainConfig.chain.id;
|
|
538
|
+
if (parseInt(chainId, 16) !== targetChainId) {
|
|
539
|
+
try {
|
|
540
|
+
await window.ethereum.request({
|
|
541
|
+
method: "wallet_switchEthereumChain",
|
|
542
|
+
params: [{ chainId: "0x" + targetChainId.toString(16) }]
|
|
543
|
+
});
|
|
544
|
+
} catch (switchError) {
|
|
545
|
+
const error = switchError;
|
|
546
|
+
if (error.code === 4902) {
|
|
547
|
+
await window.ethereum.request({
|
|
548
|
+
method: "wallet_addEthereumChain",
|
|
549
|
+
params: [
|
|
550
|
+
{
|
|
551
|
+
chainId: "0x" + targetChainId.toString(16),
|
|
552
|
+
chainName: this.chainConfig.chain.name,
|
|
553
|
+
nativeCurrency: this.chainConfig.chain.nativeCurrency,
|
|
554
|
+
rpcUrls: [this.chainConfig.rpcUrl || this.chainConfig.chain.rpcUrls.default.http[0]],
|
|
555
|
+
blockExplorerUrls: this.chainConfig.chain.blockExplorers?.default?.url ? [this.chainConfig.chain.blockExplorers.default.url] : []
|
|
556
|
+
}
|
|
557
|
+
]
|
|
558
|
+
});
|
|
559
|
+
} else {
|
|
560
|
+
throw switchError;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
this.owner = accounts[0];
|
|
565
|
+
}
|
|
566
|
+
this.smartAccountAddress = await this.getSmartAccountAddress(this.owner);
|
|
567
|
+
return {
|
|
568
|
+
owner: this.owner,
|
|
569
|
+
smartAccount: this.smartAccountAddress
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
// --- Account Management ---
|
|
573
|
+
async isAccountDeployed() {
|
|
574
|
+
if (!this.smartAccountAddress) return false;
|
|
575
|
+
const code = await this.publicClient.getBytecode({ address: this.smartAccountAddress });
|
|
576
|
+
return code !== void 0;
|
|
577
|
+
}
|
|
578
|
+
async deployAccount() {
|
|
579
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
580
|
+
return this.sendTransaction({
|
|
581
|
+
target: this.smartAccountAddress,
|
|
582
|
+
value: 0n,
|
|
583
|
+
data: "0x"
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Get the Smart Account address for an owner
|
|
588
|
+
*/
|
|
589
|
+
async getSmartAccountAddress(owner) {
|
|
590
|
+
try {
|
|
591
|
+
const address = await this.publicClient.readContract({
|
|
592
|
+
address: this.factoryAddress,
|
|
593
|
+
abi: factoryAbi,
|
|
594
|
+
functionName: "getAccountAddress",
|
|
595
|
+
args: [owner, 0n]
|
|
596
|
+
});
|
|
597
|
+
return address;
|
|
598
|
+
} catch (e) {
|
|
599
|
+
try {
|
|
600
|
+
const { result } = await this.publicClient.simulateContract({
|
|
601
|
+
address: this.factoryAddress,
|
|
602
|
+
abi: factoryAbi,
|
|
603
|
+
functionName: "createAccount",
|
|
604
|
+
args: [owner, 0n],
|
|
605
|
+
account: "0x0000000000000000000000000000000000000000"
|
|
606
|
+
// Zero address or random for simulation
|
|
607
|
+
});
|
|
608
|
+
return result;
|
|
609
|
+
} catch (inner) {
|
|
610
|
+
throw e;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// --- Token Methods (Delegated) ---
|
|
615
|
+
getTokenAddress(token) {
|
|
616
|
+
return this.tokenService.getTokenAddress(token);
|
|
617
|
+
}
|
|
618
|
+
async getBalance(token) {
|
|
619
|
+
if (!this.smartAccountAddress) throw new Error("Not connected");
|
|
620
|
+
return this.tokenService.getBalance(token, this.smartAccountAddress);
|
|
621
|
+
}
|
|
622
|
+
async getEoaBalance(token) {
|
|
623
|
+
if (!this.owner) throw new Error("Not connected");
|
|
624
|
+
return this.tokenService.getBalance(token, this.owner);
|
|
625
|
+
}
|
|
626
|
+
async getAllowance(token = "USDC") {
|
|
627
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
628
|
+
return this.tokenService.getAllowance(token, this.owner, this.smartAccountAddress);
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get comprehensive state of the account for a specific token
|
|
632
|
+
* Useful for UI initialization
|
|
633
|
+
*/
|
|
634
|
+
async getAccountState(token) {
|
|
635
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
636
|
+
const tokenAddress = this.getTokenAddress(token);
|
|
637
|
+
const isNative = tokenAddress === "0x0000000000000000000000000000000000000000";
|
|
638
|
+
const [balance, eoaBalance, allowance, isDeployed] = await Promise.all([
|
|
639
|
+
this.getBalance(token),
|
|
640
|
+
this.getEoaBalance(token),
|
|
641
|
+
isNative ? 0n : this.getAllowance(token).catch(() => 0n),
|
|
642
|
+
// Handle native/error gracefully
|
|
643
|
+
this.isAccountDeployed()
|
|
644
|
+
]);
|
|
645
|
+
return {
|
|
646
|
+
owner: this.owner,
|
|
647
|
+
smartAccount: this.smartAccountAddress,
|
|
648
|
+
balance,
|
|
649
|
+
eoaBalance,
|
|
650
|
+
allowance,
|
|
651
|
+
isDeployed
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
// --- Transactions ---
|
|
655
|
+
async sendTransaction(tx) {
|
|
656
|
+
return this.sendBatchTransaction([tx]);
|
|
657
|
+
}
|
|
658
|
+
async sendBatchTransaction(txs) {
|
|
659
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
660
|
+
const transactions = txs.map((tx) => ({
|
|
661
|
+
target: tx.target,
|
|
662
|
+
value: tx.value ?? 0n,
|
|
663
|
+
data: tx.data ?? "0x"
|
|
664
|
+
}));
|
|
665
|
+
try {
|
|
666
|
+
const userOp = await this.userOpBuilder.buildUserOperationBatch(
|
|
667
|
+
this.owner,
|
|
668
|
+
this.smartAccountAddress,
|
|
669
|
+
transactions
|
|
670
|
+
);
|
|
671
|
+
const signed = await this.signUserOperation(userOp);
|
|
672
|
+
const hash = await this.sendUserOperation(signed);
|
|
673
|
+
return await this.waitForUserOperation(hash);
|
|
674
|
+
} catch (error) {
|
|
675
|
+
throw this.decodeError(error);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async deposit(amount) {
|
|
679
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
680
|
+
if (this.walletClient) {
|
|
681
|
+
return await this.walletClient.sendTransaction({
|
|
682
|
+
account: this.walletClient.account,
|
|
683
|
+
to: this.smartAccountAddress,
|
|
684
|
+
value: amount,
|
|
685
|
+
chain: this.chainConfig.chain
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
return await window.ethereum.request({
|
|
689
|
+
method: "eth_sendTransaction",
|
|
690
|
+
params: [{
|
|
691
|
+
from: this.owner,
|
|
692
|
+
to: this.smartAccountAddress,
|
|
693
|
+
value: "0x" + amount.toString(16)
|
|
694
|
+
}]
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Smart Transfer: Automatically chooses best method (SA vs EOA) based on balances.
|
|
699
|
+
* Supports strict fee handling.
|
|
700
|
+
*/
|
|
701
|
+
async smartTransfer(token, recipient, amount, fee) {
|
|
702
|
+
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
703
|
+
const tokenAddress = this.getTokenAddress(token);
|
|
704
|
+
const isNative = tokenAddress === "0x0000000000000000000000000000000000000000";
|
|
705
|
+
const [saBal, eoaBal, allowance] = await Promise.all([
|
|
706
|
+
this.getBalance(token),
|
|
707
|
+
this.getEoaBalance(token),
|
|
708
|
+
isNative ? 0n : this.getAllowance(token).catch(() => 0n)
|
|
709
|
+
]);
|
|
710
|
+
const totalNeeded = amount + (fee?.amount || 0n);
|
|
711
|
+
if (saBal >= totalNeeded) {
|
|
712
|
+
const txs = [];
|
|
713
|
+
if (isNative) {
|
|
714
|
+
txs.push({ target: recipient, value: amount, data: "0x" });
|
|
715
|
+
} else {
|
|
716
|
+
txs.push({ target: tokenAddress, value: 0n, data: this.tokenService.encodeTransfer(recipient, amount) });
|
|
717
|
+
}
|
|
718
|
+
if (fee && fee.amount > 0n) {
|
|
719
|
+
if (isNative) {
|
|
720
|
+
txs.push({ target: fee.recipient, value: fee.amount, data: "0x" });
|
|
721
|
+
} else {
|
|
722
|
+
txs.push({ target: tokenAddress, value: 0n, data: this.tokenService.encodeTransfer(fee.recipient, fee.amount) });
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
console.log("SmartTransfer: Using Smart Account");
|
|
726
|
+
return this.sendBatchTransaction(txs);
|
|
727
|
+
}
|
|
728
|
+
if (eoaBal >= totalNeeded) {
|
|
729
|
+
if (isNative) {
|
|
730
|
+
console.log("SmartTransfer: Using EOA (Native)");
|
|
731
|
+
if (this.walletClient) {
|
|
732
|
+
const hash2 = await this.walletClient.sendTransaction({
|
|
733
|
+
account: this.walletClient.account,
|
|
734
|
+
to: recipient,
|
|
735
|
+
value: amount,
|
|
736
|
+
chain: this.chainConfig.chain
|
|
737
|
+
});
|
|
738
|
+
return { receipt: { transactionHash: hash2 } };
|
|
739
|
+
}
|
|
740
|
+
const hash = await window.ethereum.request({
|
|
741
|
+
method: "eth_sendTransaction",
|
|
742
|
+
params: [{ from: this.owner, to: recipient, value: "0x" + amount.toString(16) }]
|
|
743
|
+
});
|
|
744
|
+
return { receipt: { transactionHash: hash } };
|
|
745
|
+
} else {
|
|
746
|
+
console.log("SmartTransfer: Using EOA (Pull)");
|
|
747
|
+
if (allowance < totalNeeded) {
|
|
748
|
+
throw new Error(`Approval required. Please approve ${token} usage.`);
|
|
749
|
+
}
|
|
750
|
+
const txs = [];
|
|
751
|
+
txs.push({
|
|
752
|
+
target: tokenAddress,
|
|
753
|
+
value: 0n,
|
|
754
|
+
data: this.tokenService.encodeTransferFrom(this.owner, recipient, amount)
|
|
755
|
+
});
|
|
756
|
+
if (fee && fee.amount > 0n) {
|
|
757
|
+
txs.push({
|
|
758
|
+
target: tokenAddress,
|
|
759
|
+
value: 0n,
|
|
760
|
+
data: this.tokenService.encodeTransferFrom(this.owner, fee.recipient, fee.amount)
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
return this.sendBatchTransaction(txs);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
throw new Error(`Insufficient funds.`);
|
|
767
|
+
}
|
|
768
|
+
async transfer(token, recipient, amount) {
|
|
769
|
+
const tokenAddress = this.getTokenAddress(token);
|
|
770
|
+
if (tokenAddress === "0x0000000000000000000000000000000000000000") {
|
|
771
|
+
return this.sendTransaction({
|
|
772
|
+
target: recipient,
|
|
773
|
+
value: amount,
|
|
774
|
+
data: "0x"
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
const data = this.tokenService.encodeTransfer(recipient, amount);
|
|
778
|
+
return this.sendTransaction({
|
|
779
|
+
target: tokenAddress,
|
|
780
|
+
value: 0n,
|
|
781
|
+
data
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Approve a token for the Smart Account
|
|
786
|
+
*/
|
|
787
|
+
async approveToken(token, spender, amount = 115792089237316195423570985008687907853269984665640564039457584007913129639935n) {
|
|
788
|
+
if (!this.owner) throw new Error("Not connected");
|
|
789
|
+
const support = await this.requestApprovalSupport(token, spender, amount);
|
|
790
|
+
if (support.type === "approve") {
|
|
791
|
+
const data = this.tokenService.encodeApprove(spender, amount);
|
|
792
|
+
if (this.walletClient) {
|
|
793
|
+
return await this.walletClient.sendTransaction({
|
|
794
|
+
account: this.walletClient.account,
|
|
795
|
+
to: token,
|
|
796
|
+
data,
|
|
797
|
+
chain: this.chainConfig.chain
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
return await window.ethereum.request({
|
|
801
|
+
method: "eth_sendTransaction",
|
|
802
|
+
params: [{
|
|
803
|
+
from: this.owner,
|
|
804
|
+
to: token,
|
|
805
|
+
data
|
|
806
|
+
}]
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
if (support.type === "permit") throw new Error("Permit not yet supported");
|
|
810
|
+
return "NOT_NEEDED";
|
|
811
|
+
}
|
|
812
|
+
// --- Core Bridge to Bundler/UserOp ---
|
|
813
|
+
async signUserOperation(userOp) {
|
|
814
|
+
if (!this.owner) throw new Error("Not connected");
|
|
815
|
+
const userOpHash = this.userOpBuilder.getUserOpHash(userOp);
|
|
816
|
+
let signature;
|
|
817
|
+
if (this.walletClient) {
|
|
818
|
+
signature = await this.walletClient.signMessage({
|
|
819
|
+
account: this.walletClient.account,
|
|
820
|
+
message: { raw: userOpHash }
|
|
821
|
+
// Sign hash directly
|
|
822
|
+
});
|
|
823
|
+
} else {
|
|
824
|
+
signature = await window.ethereum.request({
|
|
825
|
+
method: "personal_sign",
|
|
826
|
+
params: [userOpHash, this.owner]
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
return { ...userOp, signature };
|
|
830
|
+
}
|
|
831
|
+
async sendUserOperation(userOp) {
|
|
832
|
+
return this.bundlerClient.sendUserOperation(userOp);
|
|
833
|
+
}
|
|
834
|
+
async waitForUserOperation(hash, timeout = 6e4) {
|
|
835
|
+
return this.bundlerClient.waitForUserOperation(hash, timeout);
|
|
836
|
+
}
|
|
837
|
+
// Internal but exposed via BundlerClient originally
|
|
838
|
+
async requestApprovalSupport(token, spender, amount) {
|
|
839
|
+
if (!this.owner) throw new Error("Not connected");
|
|
840
|
+
return this.bundlerClient.requestApprovalSupport(token, this.owner, spender, amount);
|
|
841
|
+
}
|
|
842
|
+
// Error Decoding (Private)
|
|
843
|
+
decodeError(error) {
|
|
844
|
+
const msg = error?.message || "";
|
|
845
|
+
const hexMatch = msg.match(/(0x[0-9a-fA-F]+)/);
|
|
846
|
+
if (hexMatch) {
|
|
847
|
+
try {
|
|
848
|
+
const decoded = decodeErrorResult({
|
|
849
|
+
abi: [{ inputs: [{ name: "message", type: "string" }], name: "Error", type: "error" }],
|
|
850
|
+
data: hexMatch[0]
|
|
851
|
+
});
|
|
852
|
+
if (decoded.errorName === "Error") return new Error(`Smart Account Error: ${decoded.args[0]}`);
|
|
853
|
+
} catch (e) {
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (msg.includes("AA21")) return new Error("Smart Account: Native transfer failed (ETH missing?)");
|
|
857
|
+
if (msg.includes("AA25")) return new Error("Smart Account: Invalid account nonce");
|
|
858
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
859
|
+
}
|
|
860
|
+
// Getters
|
|
861
|
+
getOwner() {
|
|
862
|
+
return this.owner;
|
|
863
|
+
}
|
|
864
|
+
getSmartAccount() {
|
|
865
|
+
return this.smartAccountAddress;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
|
|
19
871
|
// src/constants/bundler.ts
|
|
20
872
|
var DEFAULT_BUNDLER_URL, BUNDLER_URL;
|
|
21
873
|
var init_bundler = __esm({
|
|
@@ -48,7 +900,7 @@ var init_Base = __esm({
|
|
|
48
900
|
chain: base,
|
|
49
901
|
rpcUrl: "https://base-mainnet.g.alchemy.com/v2/49fUGmuW05ynCui0VEvDN",
|
|
50
902
|
supports7702: true,
|
|
51
|
-
erc4337:
|
|
903
|
+
erc4337: true,
|
|
52
904
|
bundlerUrl: `${BUNDLER_URL}/rpc?chain=base`,
|
|
53
905
|
entryPointAddress: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
|
|
54
906
|
factoryAddress: "0xe2584152891E4769025807DEa0cD611F135aDC68",
|
|
@@ -86,103 +938,74 @@ var init_Base = __esm({
|
|
|
86
938
|
};
|
|
87
939
|
}
|
|
88
940
|
});
|
|
89
|
-
var
|
|
90
|
-
var
|
|
91
|
-
"src/chains/Evm/
|
|
941
|
+
var AVALANCHE;
|
|
942
|
+
var init_Avalanche = __esm({
|
|
943
|
+
"src/chains/Evm/Avalanche.ts"() {
|
|
92
944
|
init_bundler();
|
|
93
|
-
|
|
945
|
+
AVALANCHE = {
|
|
94
946
|
assets: [
|
|
95
947
|
{
|
|
96
948
|
name: "USDC",
|
|
97
949
|
decimals: 6,
|
|
98
|
-
address: "
|
|
99
|
-
coingeckoId: "usd-coin"
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
name: "USDT",
|
|
103
|
-
decimals: 6,
|
|
104
|
-
address: "0x4ECaBa5870353805a9F068101A40E0f32ed605C6",
|
|
105
|
-
coingeckoId: "tether"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: "EURe",
|
|
109
|
-
decimals: 18,
|
|
110
|
-
address: "0x420CA0f9B9b604cE0fd9C18EF134C705e5Fa3430",
|
|
111
|
-
coingeckoId: "monerium-eur-money"
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "GNO",
|
|
115
|
-
decimals: 18,
|
|
116
|
-
address: "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb",
|
|
117
|
-
coingeckoId: "gnosis"
|
|
950
|
+
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
951
|
+
coingeckoId: "usd-coin",
|
|
952
|
+
supportsStargate: true
|
|
118
953
|
},
|
|
119
954
|
{
|
|
120
|
-
name: "
|
|
955
|
+
name: "AVAX",
|
|
121
956
|
decimals: 18,
|
|
122
|
-
address: "
|
|
123
|
-
coingeckoId: "
|
|
957
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
958
|
+
coingeckoId: "avalanche-2"
|
|
124
959
|
},
|
|
125
960
|
{
|
|
126
|
-
name: "
|
|
127
|
-
decimals:
|
|
128
|
-
address: "
|
|
129
|
-
coingeckoId: "
|
|
961
|
+
name: "USDT",
|
|
962
|
+
decimals: 6,
|
|
963
|
+
address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
|
964
|
+
coingeckoId: "tether"
|
|
130
965
|
}
|
|
131
966
|
],
|
|
132
967
|
evm: {
|
|
133
|
-
chain:
|
|
134
|
-
rpcUrl:
|
|
135
|
-
supports7702:
|
|
136
|
-
|
|
137
|
-
|
|
968
|
+
chain: avalanche,
|
|
969
|
+
rpcUrl: avalanche.rpcUrls.default.http[0],
|
|
970
|
+
supports7702: false,
|
|
971
|
+
bundlerUrl: `${BUNDLER_URL}/rpc?chain=avalanche`,
|
|
972
|
+
erc4337: false,
|
|
138
973
|
entryPointAddress: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
|
|
139
|
-
factoryAddress: "
|
|
140
|
-
paymasterAddress: "
|
|
974
|
+
factoryAddress: "0x5D1D71FE2De5D1C52c7c11311332eC7f0CBf88aF",
|
|
975
|
+
paymasterAddress: "0x6c0de464F2203FE089FF719Acf425dFfE6ac1EE5"
|
|
141
976
|
},
|
|
142
977
|
crossChainInformation: {
|
|
143
978
|
circleInformation: {
|
|
144
|
-
supportCirclePaymaster:
|
|
145
|
-
aproxFromFee: 0,
|
|
979
|
+
supportCirclePaymaster: true,
|
|
146
980
|
cCTPInformation: {
|
|
147
|
-
supportCCTP:
|
|
148
|
-
domain:
|
|
149
|
-
}
|
|
981
|
+
supportCCTP: true,
|
|
982
|
+
domain: 1
|
|
983
|
+
},
|
|
984
|
+
aproxFromFee: 0
|
|
150
985
|
},
|
|
151
986
|
nearIntentInformation: {
|
|
152
987
|
support: true,
|
|
153
988
|
assetsId: [
|
|
154
989
|
{
|
|
155
|
-
assetId: "
|
|
990
|
+
assetId: "nep245:v2_1.omni.hot.tg:43114_3atVJH3r5c4GqiSYmg9fECvjc47o",
|
|
156
991
|
name: "USDC",
|
|
157
992
|
decimals: 6
|
|
158
993
|
},
|
|
159
994
|
{
|
|
160
|
-
assetId: "
|
|
161
|
-
name: "
|
|
162
|
-
decimals: 6
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
assetId: "nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near",
|
|
166
|
-
name: "EURe",
|
|
167
|
-
decimals: 18
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
assetId: "nep141:gnosis-0x9c58bacc331c9aa871afd802db6379a98e80cedb.omft.near",
|
|
171
|
-
name: "GNO",
|
|
172
|
-
decimals: 18
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
assetId: "nep141:gnosis-0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1.omft.near",
|
|
176
|
-
name: "WETH",
|
|
995
|
+
assetId: "nep245:v2_1.omni.hot.tg:43114_11111111111111111111",
|
|
996
|
+
name: "AVAX",
|
|
177
997
|
decimals: 18
|
|
178
998
|
},
|
|
179
999
|
{
|
|
180
|
-
assetId: "
|
|
181
|
-
name: "
|
|
182
|
-
decimals:
|
|
1000
|
+
assetId: "nep245:v2_1.omni.hot.tg:43114_372BeH7ENZieCaabwkbWkBiTTgXp",
|
|
1001
|
+
name: "USDT",
|
|
1002
|
+
decimals: 6
|
|
183
1003
|
}
|
|
184
1004
|
],
|
|
185
1005
|
needMemo: false
|
|
1006
|
+
},
|
|
1007
|
+
stargateInformation: {
|
|
1008
|
+
support: true
|
|
186
1009
|
}
|
|
187
1010
|
}
|
|
188
1011
|
};
|
|
@@ -221,7 +1044,7 @@ var init_Optimism = __esm({
|
|
|
221
1044
|
],
|
|
222
1045
|
evm: {
|
|
223
1046
|
chain: optimism,
|
|
224
|
-
rpcUrl: "https://
|
|
1047
|
+
rpcUrl: "https://mainnet.optimism.io",
|
|
225
1048
|
supports7702: false,
|
|
226
1049
|
erc4337: false,
|
|
227
1050
|
bundlerUrl: `${BUNDLER_URL}/rpc?chain=optimism`,
|
|
@@ -268,74 +1091,103 @@ var init_Optimism = __esm({
|
|
|
268
1091
|
};
|
|
269
1092
|
}
|
|
270
1093
|
});
|
|
271
|
-
var
|
|
272
|
-
var
|
|
273
|
-
"src/chains/Evm/
|
|
1094
|
+
var GNOSIS;
|
|
1095
|
+
var init_Gnosis = __esm({
|
|
1096
|
+
"src/chains/Evm/Gnosis.ts"() {
|
|
274
1097
|
init_bundler();
|
|
275
|
-
|
|
1098
|
+
GNOSIS = {
|
|
276
1099
|
assets: [
|
|
277
1100
|
{
|
|
278
1101
|
name: "USDC",
|
|
279
1102
|
decimals: 6,
|
|
280
|
-
address: "
|
|
281
|
-
coingeckoId: "usd-coin"
|
|
282
|
-
supportsStargate: true
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
name: "AVAX",
|
|
286
|
-
decimals: 18,
|
|
287
|
-
address: "0x0000000000000000000000000000000000000000",
|
|
288
|
-
coingeckoId: "avalanche-2"
|
|
1103
|
+
address: "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0",
|
|
1104
|
+
coingeckoId: "usd-coin"
|
|
289
1105
|
},
|
|
290
1106
|
{
|
|
291
1107
|
name: "USDT",
|
|
292
1108
|
decimals: 6,
|
|
293
|
-
address: "
|
|
1109
|
+
address: "0x4ECaBa5870353805a9F068101A40E0f32ed605C6",
|
|
294
1110
|
coingeckoId: "tether"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "EURe",
|
|
1114
|
+
decimals: 18,
|
|
1115
|
+
address: "0x420CA0f9B9b604cE0fd9C18EF134C705e5Fa3430",
|
|
1116
|
+
coingeckoId: "monerium-eur-money"
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
name: "GNO",
|
|
1120
|
+
decimals: 18,
|
|
1121
|
+
address: "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb",
|
|
1122
|
+
coingeckoId: "gnosis"
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
name: "WETH",
|
|
1126
|
+
decimals: 18,
|
|
1127
|
+
address: "0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1",
|
|
1128
|
+
coingeckoId: "ethereum"
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
name: "XDAI",
|
|
1132
|
+
decimals: 18,
|
|
1133
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
1134
|
+
coingeckoId: "xdai"
|
|
295
1135
|
}
|
|
296
1136
|
],
|
|
297
1137
|
evm: {
|
|
298
|
-
chain:
|
|
299
|
-
rpcUrl:
|
|
300
|
-
supports7702:
|
|
301
|
-
|
|
302
|
-
|
|
1138
|
+
chain: gnosis,
|
|
1139
|
+
rpcUrl: gnosis.rpcUrls.default.http[0],
|
|
1140
|
+
supports7702: true,
|
|
1141
|
+
erc4337: true,
|
|
1142
|
+
bundlerUrl: `${BUNDLER_URL}/rpc?chain=gnosis`,
|
|
303
1143
|
entryPointAddress: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
|
|
304
|
-
factoryAddress: "
|
|
305
|
-
paymasterAddress: "
|
|
1144
|
+
factoryAddress: "0xC8a2Fb1f2E686417A131E09be3320cb5431CcD90",
|
|
1145
|
+
paymasterAddress: "0x4C36C70d68a7c26326711e8268bb163E3784fA96"
|
|
306
1146
|
},
|
|
307
1147
|
crossChainInformation: {
|
|
308
1148
|
circleInformation: {
|
|
309
|
-
supportCirclePaymaster:
|
|
1149
|
+
supportCirclePaymaster: false,
|
|
1150
|
+
aproxFromFee: 0,
|
|
310
1151
|
cCTPInformation: {
|
|
311
|
-
supportCCTP:
|
|
312
|
-
domain:
|
|
313
|
-
}
|
|
314
|
-
aproxFromFee: 0
|
|
1152
|
+
supportCCTP: false,
|
|
1153
|
+
domain: 0
|
|
1154
|
+
}
|
|
315
1155
|
},
|
|
316
1156
|
nearIntentInformation: {
|
|
317
1157
|
support: true,
|
|
318
1158
|
assetsId: [
|
|
319
1159
|
{
|
|
320
|
-
assetId: "
|
|
1160
|
+
assetId: "nep141:gnosis-0x2a22f9c3b484c3629090feed35f17ff8f88f76f0.omft.near",
|
|
321
1161
|
name: "USDC",
|
|
322
1162
|
decimals: 6
|
|
323
1163
|
},
|
|
324
1164
|
{
|
|
325
|
-
assetId: "
|
|
326
|
-
name: "
|
|
1165
|
+
assetId: "nep141:gnosis-0x4ecaba5870353805a9f068101a40e0f32ed605c6.omft.near",
|
|
1166
|
+
name: "USDT",
|
|
1167
|
+
decimals: 6
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
assetId: "nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near",
|
|
1171
|
+
name: "EURe",
|
|
327
1172
|
decimals: 18
|
|
328
1173
|
},
|
|
329
1174
|
{
|
|
330
|
-
assetId: "
|
|
331
|
-
name: "
|
|
332
|
-
decimals:
|
|
1175
|
+
assetId: "nep141:gnosis-0x9c58bacc331c9aa871afd802db6379a98e80cedb.omft.near",
|
|
1176
|
+
name: "GNO",
|
|
1177
|
+
decimals: 18
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
assetId: "nep141:gnosis-0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1.omft.near",
|
|
1181
|
+
name: "WETH",
|
|
1182
|
+
decimals: 18
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
assetId: "nep141:gnosis.omft.near",
|
|
1186
|
+
name: "XDAI",
|
|
1187
|
+
decimals: 18
|
|
333
1188
|
}
|
|
334
1189
|
],
|
|
335
1190
|
needMemo: false
|
|
336
|
-
},
|
|
337
|
-
stargateInformation: {
|
|
338
|
-
support: true
|
|
339
1191
|
}
|
|
340
1192
|
}
|
|
341
1193
|
};
|
|
@@ -835,6 +1687,17 @@ var init_facilitator = __esm({
|
|
|
835
1687
|
return isDev ? PlatformFees.DEV : PlatformFees.DEFAULT;
|
|
836
1688
|
};
|
|
837
1689
|
FACILITATOR_NETWORKS = {
|
|
1690
|
+
Ethereum: {
|
|
1691
|
+
chainId: mainnet.id,
|
|
1692
|
+
chain: mainnet,
|
|
1693
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
1694
|
+
usdcName: "USD Coin",
|
|
1695
|
+
usdcVersion: "2",
|
|
1696
|
+
domain: 0,
|
|
1697
|
+
tokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",
|
|
1698
|
+
messageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",
|
|
1699
|
+
rpcUrl: mainnet.rpcUrls.default.http[0]
|
|
1700
|
+
},
|
|
838
1701
|
Base: {
|
|
839
1702
|
chainId: 8453,
|
|
840
1703
|
chain: base,
|
|
@@ -1103,6 +1966,68 @@ var init_WorldChain = __esm({
|
|
|
1103
1966
|
};
|
|
1104
1967
|
}
|
|
1105
1968
|
});
|
|
1969
|
+
var ETHEREUM;
|
|
1970
|
+
var init_Ethereum = __esm({
|
|
1971
|
+
"src/chains/Evm/Ethereum.ts"() {
|
|
1972
|
+
init_bundler();
|
|
1973
|
+
ETHEREUM = {
|
|
1974
|
+
assets: [
|
|
1975
|
+
{
|
|
1976
|
+
name: "USDC",
|
|
1977
|
+
decimals: 6,
|
|
1978
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
1979
|
+
coingeckoId: "usd-coin",
|
|
1980
|
+
supportsStargate: true
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
name: "ETH",
|
|
1984
|
+
decimals: 18,
|
|
1985
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
1986
|
+
coingeckoId: "ethereum"
|
|
1987
|
+
}
|
|
1988
|
+
],
|
|
1989
|
+
evm: {
|
|
1990
|
+
chain: mainnet,
|
|
1991
|
+
rpcUrl: mainnet.rpcUrls.default.http[0],
|
|
1992
|
+
supports7702: true,
|
|
1993
|
+
erc4337: true,
|
|
1994
|
+
bundlerUrl: `${BUNDLER_URL}/rpc?chain=ethereum`,
|
|
1995
|
+
entryPointAddress: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
|
|
1996
|
+
factoryAddress: "0xe2584152891E4769025807DEa0cD611F135aDC68",
|
|
1997
|
+
paymasterAddress: "0x1e13Eb16C565E3f3FDe49A011755e50410bb1F95"
|
|
1998
|
+
},
|
|
1999
|
+
crossChainInformation: {
|
|
2000
|
+
circleInformation: {
|
|
2001
|
+
supportCirclePaymaster: true,
|
|
2002
|
+
cCTPInformation: {
|
|
2003
|
+
supportCCTP: true,
|
|
2004
|
+
domain: 0
|
|
2005
|
+
},
|
|
2006
|
+
aproxFromFee: 0
|
|
2007
|
+
},
|
|
2008
|
+
nearIntentInformation: {
|
|
2009
|
+
support: true,
|
|
2010
|
+
assetsId: [
|
|
2011
|
+
{
|
|
2012
|
+
assetId: "nep141:eth-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.omft.near",
|
|
2013
|
+
name: "USDC",
|
|
2014
|
+
decimals: 6
|
|
2015
|
+
},
|
|
2016
|
+
{
|
|
2017
|
+
assetId: "nep141:eth.omft.near",
|
|
2018
|
+
name: "ETH",
|
|
2019
|
+
decimals: 18
|
|
2020
|
+
}
|
|
2021
|
+
],
|
|
2022
|
+
needMemo: false
|
|
2023
|
+
},
|
|
2024
|
+
stargateInformation: {
|
|
2025
|
+
support: false
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
1106
2031
|
|
|
1107
2032
|
// src/chains/index.ts
|
|
1108
2033
|
var init_chains = __esm({
|
|
@@ -1118,6 +2043,7 @@ var init_chains = __esm({
|
|
|
1118
2043
|
init_Monad();
|
|
1119
2044
|
init_Binance();
|
|
1120
2045
|
init_Gnosis();
|
|
2046
|
+
init_Ethereum();
|
|
1121
2047
|
}
|
|
1122
2048
|
});
|
|
1123
2049
|
|
|
@@ -1137,7 +2063,8 @@ var init_chainsInformation = __esm({
|
|
|
1137
2063
|
Stellar: STELLAR,
|
|
1138
2064
|
Monad,
|
|
1139
2065
|
BNB,
|
|
1140
|
-
Gnosis: GNOSIS
|
|
2066
|
+
Gnosis: GNOSIS,
|
|
2067
|
+
Ethereum: ETHEREUM
|
|
1141
2068
|
};
|
|
1142
2069
|
}
|
|
1143
2070
|
});
|
|
@@ -1447,6 +2374,14 @@ var init_cctp2 = __esm({
|
|
|
1447
2374
|
};
|
|
1448
2375
|
}
|
|
1449
2376
|
});
|
|
2377
|
+
|
|
2378
|
+
// src/services/near.ts
|
|
2379
|
+
var near_exports = {};
|
|
2380
|
+
__export(near_exports, {
|
|
2381
|
+
NearStrategy: () => NearStrategy,
|
|
2382
|
+
getNearQuote: () => getNearQuote,
|
|
2383
|
+
getNearSimulation: () => getNearSimulation
|
|
2384
|
+
});
|
|
1450
2385
|
async function getNearQuote(sourceChain, destChain, amount, destToken, sourceToken, recipient, senderAddress, options) {
|
|
1451
2386
|
const sourceConfig = NETWORKS[sourceChain];
|
|
1452
2387
|
const destConfig = NETWORKS[destChain];
|
|
@@ -1650,15 +2585,15 @@ var init_near = __esm({
|
|
|
1650
2585
|
return { success: false, errorReason: `Stellar Verification Failed: ${e.message}` };
|
|
1651
2586
|
}
|
|
1652
2587
|
}
|
|
1653
|
-
const { createPublicClient:
|
|
2588
|
+
const { createPublicClient: createPublicClient5, http: http5 } = await import('viem');
|
|
1654
2589
|
const { FACILITATOR_NETWORKS: FACILITATOR_NETWORKS2 } = await Promise.resolve().then(() => (init_facilitator(), facilitator_exports));
|
|
1655
2590
|
const networkConfig = FACILITATOR_NETWORKS2[sourceChain];
|
|
1656
2591
|
if (!networkConfig) {
|
|
1657
2592
|
return { success: false, errorReason: `Unsupported source chain for verification: ${sourceChain}` };
|
|
1658
2593
|
}
|
|
1659
|
-
const publicClient =
|
|
2594
|
+
const publicClient = createPublicClient5({
|
|
1660
2595
|
chain: networkConfig.chain,
|
|
1661
|
-
transport:
|
|
2596
|
+
transport: http5(networkConfig.rpcUrl)
|
|
1662
2597
|
});
|
|
1663
2598
|
try {
|
|
1664
2599
|
console.log(`[NearStrategy] Waiting for receipt...`);
|
|
@@ -1859,6 +2794,7 @@ var init_stargate = __esm({
|
|
|
1859
2794
|
success: true,
|
|
1860
2795
|
transactionHash: "PENDING_USER_SIGNATURE",
|
|
1861
2796
|
netAmount: amount,
|
|
2797
|
+
estimatedReceived: selectedQuote.dstAmount,
|
|
1862
2798
|
data: {
|
|
1863
2799
|
strategy: "Stargate",
|
|
1864
2800
|
quote: selectedQuote,
|
|
@@ -1875,11 +2811,306 @@ var init_stargate = __esm({
|
|
|
1875
2811
|
}
|
|
1876
2812
|
};
|
|
1877
2813
|
} catch (e) {
|
|
1878
|
-
console.error("[StargateStrategy] Execution Error:", e);
|
|
1879
|
-
return { success: false, errorReason: e.message };
|
|
2814
|
+
console.error("[StargateStrategy] Execution Error:", e);
|
|
2815
|
+
return { success: false, errorReason: e.message };
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
var STACKS_BRIDGE_ADDRESS_ETH, stacksBridgeAbi, StacksStrategy;
|
|
2822
|
+
var init_stacks = __esm({
|
|
2823
|
+
"src/services/stacks.ts"() {
|
|
2824
|
+
init_chainsInformation();
|
|
2825
|
+
STACKS_BRIDGE_ADDRESS_ETH = "0x8888888199b2Df864bf678259607d6D5EBb4e3Ce";
|
|
2826
|
+
stacksBridgeAbi = [
|
|
2827
|
+
{
|
|
2828
|
+
"inputs": [
|
|
2829
|
+
{ "internalType": "uint256", "name": "value", "type": "uint256" },
|
|
2830
|
+
{ "internalType": "uint32", "name": "remoteDomain", "type": "uint32" },
|
|
2831
|
+
{ "internalType": "bytes32", "name": "remoteRecipient", "type": "bytes32" },
|
|
2832
|
+
{ "internalType": "address", "name": "localToken", "type": "address" },
|
|
2833
|
+
{ "internalType": "uint256", "name": "maxFee", "type": "uint256" }
|
|
2834
|
+
],
|
|
2835
|
+
"name": "depositToRemote",
|
|
2836
|
+
"outputs": [],
|
|
2837
|
+
"stateMutability": "nonpayable",
|
|
2838
|
+
"type": "function"
|
|
2839
|
+
}
|
|
2840
|
+
];
|
|
2841
|
+
StacksStrategy = class {
|
|
2842
|
+
constructor() {
|
|
2843
|
+
this.name = "Stacks";
|
|
2844
|
+
}
|
|
2845
|
+
canHandle(context) {
|
|
2846
|
+
return context.sourceChain === "Ethereum" && context.destChain === "Stacks";
|
|
2847
|
+
}
|
|
2848
|
+
async execute(context) {
|
|
2849
|
+
const { amount, recipient, facilitatorPrivateKey, sourceChain } = context;
|
|
2850
|
+
if (sourceChain !== "Ethereum") {
|
|
2851
|
+
return { success: false, errorReason: "Stacks Strategy only supports financing from Ethereum" };
|
|
2852
|
+
}
|
|
2853
|
+
if (!facilitatorPrivateKey) {
|
|
2854
|
+
return { success: false, errorReason: "Private Key required for Stacks Bridge execution" };
|
|
2855
|
+
}
|
|
2856
|
+
const networkConfig = NETWORKS[sourceChain];
|
|
2857
|
+
if (!networkConfig || !networkConfig.evm) {
|
|
2858
|
+
return { success: false, errorReason: `Unsupported chain or missing EVM config: ${sourceChain}` };
|
|
2859
|
+
}
|
|
2860
|
+
try {
|
|
2861
|
+
const account = privateKeyToAccount(facilitatorPrivateKey);
|
|
2862
|
+
const client = createWalletClient({
|
|
2863
|
+
account,
|
|
2864
|
+
chain: networkConfig.evm.chain,
|
|
2865
|
+
transport: http(networkConfig.evm.rpcUrl)
|
|
2866
|
+
});
|
|
2867
|
+
const publicClient = createPublicClient({
|
|
2868
|
+
chain: networkConfig.evm.chain,
|
|
2869
|
+
transport: http(networkConfig.evm.rpcUrl)
|
|
2870
|
+
});
|
|
2871
|
+
const usdcAddress = getAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
|
|
2872
|
+
const amountBigInt = parseUnits(amount, 6);
|
|
2873
|
+
const allowance = await publicClient.readContract({
|
|
2874
|
+
address: usdcAddress,
|
|
2875
|
+
abi: erc20Abi$1,
|
|
2876
|
+
functionName: "allowance",
|
|
2877
|
+
args: [account.address, STACKS_BRIDGE_ADDRESS_ETH]
|
|
2878
|
+
});
|
|
2879
|
+
if (allowance < amountBigInt) {
|
|
2880
|
+
console.log("[Stacks] Approving bridge...");
|
|
2881
|
+
const approveHash = await client.writeContract({
|
|
2882
|
+
address: usdcAddress,
|
|
2883
|
+
abi: erc20Abi$1,
|
|
2884
|
+
functionName: "approve",
|
|
2885
|
+
args: [STACKS_BRIDGE_ADDRESS_ETH, amountBigInt],
|
|
2886
|
+
chain: networkConfig.evm.chain
|
|
2887
|
+
});
|
|
2888
|
+
await publicClient.waitForTransactionReceipt({ hash: approveHash });
|
|
2889
|
+
console.log("[Stacks] Approved.");
|
|
2890
|
+
}
|
|
2891
|
+
const REMOTE_DOMAIN_STACKS = 10003;
|
|
2892
|
+
let recipientBytes32;
|
|
2893
|
+
if (recipient.startsWith("0x")) {
|
|
2894
|
+
recipientBytes32 = padHex(recipient, { size: 32 });
|
|
2895
|
+
} else {
|
|
2896
|
+
try {
|
|
2897
|
+
recipientBytes32 = padHex(toHex(recipient), { size: 32 });
|
|
2898
|
+
} catch (e) {
|
|
2899
|
+
throw new Error("Invalid Stacks Recipient Format. Must be Hex or convertible.");
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
console.log(`[Stacks] Depositing ${amount} USDC to ${recipient} (Domain ${REMOTE_DOMAIN_STACKS})...`);
|
|
2903
|
+
const hash = await client.writeContract({
|
|
2904
|
+
address: STACKS_BRIDGE_ADDRESS_ETH,
|
|
2905
|
+
abi: stacksBridgeAbi,
|
|
2906
|
+
functionName: "depositToRemote",
|
|
2907
|
+
args: [
|
|
2908
|
+
amountBigInt,
|
|
2909
|
+
REMOTE_DOMAIN_STACKS,
|
|
2910
|
+
recipientBytes32,
|
|
2911
|
+
usdcAddress,
|
|
2912
|
+
0n
|
|
2913
|
+
// maxFee (0 for now, or estimated)
|
|
2914
|
+
],
|
|
2915
|
+
chain: networkConfig.evm.chain
|
|
2916
|
+
});
|
|
2917
|
+
console.log(`[Stacks] Deposit Tx: ${hash}`);
|
|
2918
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
2919
|
+
if (receipt.status === "success") {
|
|
2920
|
+
return {
|
|
2921
|
+
success: true,
|
|
2922
|
+
transactionHash: hash,
|
|
2923
|
+
netAmount: amountBigInt.toString()
|
|
2924
|
+
};
|
|
2925
|
+
} else {
|
|
2926
|
+
return {
|
|
2927
|
+
success: false,
|
|
2928
|
+
transactionHash: hash,
|
|
2929
|
+
errorReason: "Stacks Deposit Transaction Reverted"
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
} catch (error) {
|
|
2933
|
+
return {
|
|
2934
|
+
success: false,
|
|
2935
|
+
errorReason: error instanceof Error ? error.message : "Using Stacks Bridge failed"
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
});
|
|
2942
|
+
|
|
2943
|
+
// src/services/Router.ts
|
|
2944
|
+
var RouterService;
|
|
2945
|
+
var init_Router = __esm({
|
|
2946
|
+
"src/services/Router.ts"() {
|
|
2947
|
+
RouterService = class {
|
|
2948
|
+
/**
|
|
2949
|
+
* Orchestrates a Multi-Hop transfer by signing ALL UserOps upfront (Reverse Order),
|
|
2950
|
+
* and then executing them sequentially (Forward Order) with polling support.
|
|
2951
|
+
*
|
|
2952
|
+
* @param steps Array of MultiHopStep ordered by execution (Source -> Intermediate -> Dest)
|
|
2953
|
+
* @param onLog Optional callback for logging progress
|
|
2954
|
+
*/
|
|
2955
|
+
async executeMultiHop(steps, onLog = console.log) {
|
|
2956
|
+
onLog("[Router] Orchestrating Multi-Hop Sequence...");
|
|
2957
|
+
const signedOps = [];
|
|
2958
|
+
onLog("[Router] Phase 1: Preparation & Upfront Signing (Reverse Order)...");
|
|
2959
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
2960
|
+
const step = steps[i];
|
|
2961
|
+
onLog(`[Router] Preparing Step ${i + 1}: ${step.description}...`);
|
|
2962
|
+
try {
|
|
2963
|
+
const userOp = await step.buildUserOp();
|
|
2964
|
+
onLog(`[Router] Please sign Step ${i + 1} (${step.description})...`);
|
|
2965
|
+
const signedOp = await step.aa.signUserOperation(userOp);
|
|
2966
|
+
signedOps.unshift({ stepIndex: i, signedOp });
|
|
2967
|
+
onLog(`[Router] Step ${i + 1} Signed Successfully.`);
|
|
2968
|
+
} catch (err) {
|
|
2969
|
+
onLog(`[Router] Error preparing Step ${i + 1}: ${err.message}`);
|
|
2970
|
+
throw err;
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
onLog("[Router] Phase 2: Sequential Execution (Forward Order)...");
|
|
2974
|
+
for (let i = 0; i < steps.length; i++) {
|
|
2975
|
+
const step = steps[i];
|
|
2976
|
+
const signedOp = signedOps[i].signedOp;
|
|
2977
|
+
if (step.waitCondition) {
|
|
2978
|
+
onLog(`[Router] Step ${i + 1} requires waiting (e.g. for funds)...`);
|
|
2979
|
+
const success = await this.pollCondition(step.waitCondition, onLog);
|
|
2980
|
+
if (!success) throw new Error(`Timeout waiting for condition at Step ${i + 1}`);
|
|
2981
|
+
}
|
|
2982
|
+
onLog(`[Router] Executing Step ${i + 1}...`);
|
|
2983
|
+
try {
|
|
2984
|
+
const hash = await step.aa.sendUserOperation(signedOp);
|
|
2985
|
+
onLog(`[Router] Step ${i + 1} Sent! Hash: ${hash}`);
|
|
2986
|
+
onLog(`[Router] Waiting for Step ${i + 1} confirmation...`);
|
|
2987
|
+
const receipt = await step.aa.waitForUserOperation(hash);
|
|
2988
|
+
if (!receipt.success) {
|
|
2989
|
+
throw new Error(`Step ${i + 1} Failed on-chain!`);
|
|
2990
|
+
}
|
|
2991
|
+
onLog(`[Router] Step ${i + 1} Confirmed.`);
|
|
2992
|
+
} catch (err) {
|
|
2993
|
+
onLog(`[Router] Execution Failed at Step ${i + 1}: ${err.message}`);
|
|
2994
|
+
throw err;
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
onLog("[Router] Multi-Hop Sequence Completed Successfully! \u{1F680}");
|
|
2998
|
+
return true;
|
|
2999
|
+
}
|
|
3000
|
+
async pollCondition(condition, onLog) {
|
|
3001
|
+
const timeout = 6e4 * 10;
|
|
3002
|
+
const interval = 5e3;
|
|
3003
|
+
let elapsed = 0;
|
|
3004
|
+
while (elapsed < timeout) {
|
|
3005
|
+
try {
|
|
3006
|
+
const passed = await condition();
|
|
3007
|
+
if (passed) return true;
|
|
3008
|
+
} catch (e) {
|
|
3009
|
+
onLog(`[Router] Polling check failed (retrying): ${e.message}`);
|
|
3010
|
+
}
|
|
3011
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
3012
|
+
elapsed += interval;
|
|
3013
|
+
if (elapsed % 2e4 === 0) onLog(`[Router] Still waiting... (${Math.floor(elapsed / 1e3)}s)`);
|
|
3014
|
+
}
|
|
3015
|
+
return false;
|
|
3016
|
+
}
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
|
|
3021
|
+
// src/services/uniswap.ts
|
|
3022
|
+
var uniswap_exports = {};
|
|
3023
|
+
__export(uniswap_exports, {
|
|
3024
|
+
UniswapService: () => UniswapService,
|
|
3025
|
+
uniswapService: () => uniswapService
|
|
3026
|
+
});
|
|
3027
|
+
var UNISWAP_V3_ROUTER, UNISWAP_V3_QUOTER, USDC_ADDRESS, WETH_ADDRESS, QUOTER_ABI, ROUTER_ABI, UniswapService, uniswapService;
|
|
3028
|
+
var init_uniswap = __esm({
|
|
3029
|
+
"src/services/uniswap.ts"() {
|
|
3030
|
+
UNISWAP_V3_ROUTER = "0x2626664c2603336E57B271c5C0b26F421741e481";
|
|
3031
|
+
UNISWAP_V3_QUOTER = "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a";
|
|
3032
|
+
USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
3033
|
+
WETH_ADDRESS = "0x4200000000000000000000000000000000000006";
|
|
3034
|
+
QUOTER_ABI = parseAbi([
|
|
3035
|
+
"function quoteExactOutputSingle((address tokenIn, address tokenOut, uint256 amount, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)"
|
|
3036
|
+
]);
|
|
3037
|
+
ROUTER_ABI = parseAbi([
|
|
3038
|
+
"function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn)"
|
|
3039
|
+
]);
|
|
3040
|
+
UniswapService = class {
|
|
3041
|
+
constructor() {
|
|
3042
|
+
this.publicClient = createPublicClient({
|
|
3043
|
+
chain: base,
|
|
3044
|
+
transport: http("https://mainnet.base.org")
|
|
3045
|
+
// Default public RPC
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Get amount of USDC needed to buy exact amount of ETH
|
|
3050
|
+
* @param amountETHWei Amount of ETH (Wei) needed
|
|
3051
|
+
*/
|
|
3052
|
+
async quoteUSDCForETH(amountETHWei) {
|
|
3053
|
+
try {
|
|
3054
|
+
const params = {
|
|
3055
|
+
tokenIn: USDC_ADDRESS,
|
|
3056
|
+
tokenOut: WETH_ADDRESS,
|
|
3057
|
+
amount: amountETHWei,
|
|
3058
|
+
fee: 500,
|
|
3059
|
+
sqrtPriceLimitX96: 0n
|
|
3060
|
+
};
|
|
3061
|
+
const result = await this.publicClient.readContract({
|
|
3062
|
+
address: UNISWAP_V3_QUOTER,
|
|
3063
|
+
abi: QUOTER_ABI,
|
|
3064
|
+
functionName: "quoteExactOutputSingle",
|
|
3065
|
+
args: [params]
|
|
3066
|
+
});
|
|
3067
|
+
const amountIn = result[0];
|
|
3068
|
+
return amountIn * 105n / 100n;
|
|
3069
|
+
} catch (e) {
|
|
3070
|
+
console.error("Quote failed", e);
|
|
3071
|
+
throw new Error("Failed to quote USDC for ETH swap");
|
|
1880
3072
|
}
|
|
1881
3073
|
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Build tx data for swapping USDC -> ETH
|
|
3076
|
+
* Uses SwapRouter02.exactOutputSingle + unwrapWETH9
|
|
3077
|
+
*/
|
|
3078
|
+
buildSwapData(recipient, amountOutETH, maxAmountInUSDC) {
|
|
3079
|
+
const swapParams = {
|
|
3080
|
+
tokenIn: USDC_ADDRESS,
|
|
3081
|
+
tokenOut: WETH_ADDRESS,
|
|
3082
|
+
fee: 500,
|
|
3083
|
+
recipient: UNISWAP_V3_ROUTER,
|
|
3084
|
+
// Router must hold WETH to unwrap it
|
|
3085
|
+
amountOut: amountOutETH,
|
|
3086
|
+
amountInMaximum: maxAmountInUSDC,
|
|
3087
|
+
sqrtPriceLimitX96: 0n
|
|
3088
|
+
};
|
|
3089
|
+
const swapCalldata = encodeFunctionData({
|
|
3090
|
+
abi: ROUTER_ABI,
|
|
3091
|
+
functionName: "exactOutputSingle",
|
|
3092
|
+
args: [swapParams]
|
|
3093
|
+
});
|
|
3094
|
+
const unwrapCalldata = encodeFunctionData({
|
|
3095
|
+
abi: parseAbi(["function unwrapWETH9(uint256 amountMinimum, address recipient) payable"]),
|
|
3096
|
+
functionName: "unwrapWETH9",
|
|
3097
|
+
args: [amountOutETH, recipient]
|
|
3098
|
+
});
|
|
3099
|
+
const multicallCalldata = encodeFunctionData({
|
|
3100
|
+
abi: parseAbi(["function multicall(bytes[] data) payable returns (bytes[])"]),
|
|
3101
|
+
functionName: "multicall",
|
|
3102
|
+
args: [[swapCalldata, unwrapCalldata]]
|
|
3103
|
+
});
|
|
3104
|
+
return multicallCalldata;
|
|
3105
|
+
}
|
|
3106
|
+
getRouterAddress() {
|
|
3107
|
+
return UNISWAP_V3_ROUTER;
|
|
3108
|
+
}
|
|
3109
|
+
getUSDCAddress() {
|
|
3110
|
+
return USDC_ADDRESS;
|
|
3111
|
+
}
|
|
1882
3112
|
};
|
|
3113
|
+
uniswapService = new UniswapService();
|
|
1883
3114
|
}
|
|
1884
3115
|
});
|
|
1885
3116
|
|
|
@@ -1888,23 +3119,46 @@ var TransferManager_exports = {};
|
|
|
1888
3119
|
__export(TransferManager_exports, {
|
|
1889
3120
|
TransferManager: () => TransferManager
|
|
1890
3121
|
});
|
|
1891
|
-
var TransferManager;
|
|
3122
|
+
var OP_SEPOLIA_CONFIG, TransferManager;
|
|
1892
3123
|
var init_TransferManager = __esm({
|
|
1893
3124
|
"src/services/TransferManager.ts"() {
|
|
1894
3125
|
init_cctp2();
|
|
1895
3126
|
init_near();
|
|
1896
3127
|
init_stargate();
|
|
3128
|
+
init_stacks();
|
|
3129
|
+
init_Router();
|
|
3130
|
+
init_AccountAbstraction();
|
|
3131
|
+
init_Optimism();
|
|
3132
|
+
OP_SEPOLIA_CONFIG = {
|
|
3133
|
+
chain: {
|
|
3134
|
+
id: 11155420,
|
|
3135
|
+
name: "Optimism Sepolia",
|
|
3136
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
3137
|
+
rpcUrls: { default: { http: ["https://sepolia.optimism.io"] } },
|
|
3138
|
+
testnet: true
|
|
3139
|
+
},
|
|
3140
|
+
rpcUrl: "https://sepolia.optimism.io",
|
|
3141
|
+
// bundlerUrl will be constructed dynamically
|
|
3142
|
+
entryPointAddress: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
|
|
3143
|
+
factoryAddress: "0xe2584152891E4769025807DEa0cD611F135aDC68",
|
|
3144
|
+
paymasterAddress: OPTIMISM.evm?.paymasterAddress,
|
|
3145
|
+
tokens: []
|
|
3146
|
+
};
|
|
1897
3147
|
TransferManager = class {
|
|
1898
3148
|
constructor() {
|
|
1899
3149
|
this.strategies = [
|
|
1900
3150
|
new CCTPStrategy(),
|
|
1901
3151
|
new StargateStrategy(),
|
|
1902
|
-
new
|
|
3152
|
+
new StargateStrategy(),
|
|
3153
|
+
new NearStrategy(),
|
|
3154
|
+
new StacksStrategy()
|
|
1903
3155
|
];
|
|
3156
|
+
this.router = new RouterService();
|
|
1904
3157
|
}
|
|
1905
|
-
async execute(context) {
|
|
3158
|
+
async execute(context, logCallback) {
|
|
3159
|
+
const log = logCallback || console.log;
|
|
1906
3160
|
if (context.sourceChain === context.destChain && context.sourceToken === context.destToken) {
|
|
1907
|
-
|
|
3161
|
+
log(`[TransferManager] Same Chain detected. Signal Direct Transfer.`);
|
|
1908
3162
|
return {
|
|
1909
3163
|
success: true,
|
|
1910
3164
|
transactionHash: "DIRECT_TRANSFER_REQUIRED",
|
|
@@ -1919,17 +3173,17 @@ var init_TransferManager = __esm({
|
|
|
1919
3173
|
const strategies = this.strategies;
|
|
1920
3174
|
const stargateStrategy = strategies.find((s) => s instanceof StargateStrategy);
|
|
1921
3175
|
if (stargateStrategy && stargateStrategy.canHandle(context)) {
|
|
1922
|
-
|
|
3176
|
+
log(`[TransferManager] Routing to: ${stargateStrategy.name} (Stargate)`);
|
|
1923
3177
|
return stargateStrategy.execute(context);
|
|
1924
3178
|
}
|
|
1925
3179
|
const cctpStrategy = strategies.find((s) => s instanceof CCTPStrategy);
|
|
1926
3180
|
if (cctpStrategy && cctpStrategy.canHandle(context)) {
|
|
1927
|
-
|
|
3181
|
+
log(`[TransferManager] Routing to: ${cctpStrategy.name} (CCTP)`);
|
|
1928
3182
|
return cctpStrategy.execute(context);
|
|
1929
3183
|
}
|
|
1930
3184
|
const nearStrategy = strategies.find((s) => s instanceof NearStrategy);
|
|
1931
3185
|
if (nearStrategy && nearStrategy.canHandle(context)) {
|
|
1932
|
-
|
|
3186
|
+
log(`[TransferManager] Routing to: ${nearStrategy.name} (Near)`);
|
|
1933
3187
|
return nearStrategy.execute(context);
|
|
1934
3188
|
}
|
|
1935
3189
|
return {
|
|
@@ -1937,781 +3191,218 @@ var init_TransferManager = __esm({
|
|
|
1937
3191
|
errorReason: `No suitable transfer strategy found for ${context.sourceChain} -> ${context.destChain}`
|
|
1938
3192
|
};
|
|
1939
3193
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
],
|
|
1961
|
-
name: "isAccountDeployed",
|
|
1962
|
-
outputs: [{ name: "", type: "bool" }],
|
|
1963
|
-
stateMutability: "view",
|
|
1964
|
-
type: "function"
|
|
1965
|
-
},
|
|
1966
|
-
{
|
|
1967
|
-
inputs: [
|
|
1968
|
-
{ name: "owner", type: "address" },
|
|
1969
|
-
{ name: "salt", type: "uint256" }
|
|
1970
|
-
],
|
|
1971
|
-
name: "createAccount",
|
|
1972
|
-
outputs: [{ name: "account", type: "address" }],
|
|
1973
|
-
stateMutability: "nonpayable",
|
|
1974
|
-
type: "function"
|
|
1975
|
-
}
|
|
1976
|
-
];
|
|
1977
|
-
var entryPointAbi = [
|
|
1978
|
-
{
|
|
1979
|
-
inputs: [
|
|
1980
|
-
{ name: "sender", type: "address" },
|
|
1981
|
-
{ name: "key", type: "uint192" }
|
|
1982
|
-
],
|
|
1983
|
-
name: "getNonce",
|
|
1984
|
-
outputs: [{ name: "nonce", type: "uint256" }],
|
|
1985
|
-
stateMutability: "view",
|
|
1986
|
-
type: "function"
|
|
1987
|
-
}
|
|
1988
|
-
];
|
|
1989
|
-
var smartAccountAbi = [
|
|
1990
|
-
{
|
|
1991
|
-
inputs: [
|
|
1992
|
-
{ name: "target", type: "address" },
|
|
1993
|
-
{ name: "value", type: "uint256" },
|
|
1994
|
-
{ name: "data", type: "bytes" }
|
|
1995
|
-
],
|
|
1996
|
-
name: "execute",
|
|
1997
|
-
outputs: [],
|
|
1998
|
-
stateMutability: "nonpayable",
|
|
1999
|
-
type: "function"
|
|
2000
|
-
},
|
|
2001
|
-
{
|
|
2002
|
-
inputs: [
|
|
2003
|
-
{ name: "targets", type: "address[]" },
|
|
2004
|
-
{ name: "values", type: "uint256[]" },
|
|
2005
|
-
{ name: "datas", type: "bytes[]" }
|
|
2006
|
-
],
|
|
2007
|
-
name: "executeBatch",
|
|
2008
|
-
outputs: [],
|
|
2009
|
-
stateMutability: "nonpayable",
|
|
2010
|
-
type: "function"
|
|
2011
|
-
}
|
|
2012
|
-
];
|
|
2013
|
-
var erc20Abi = [
|
|
2014
|
-
{
|
|
2015
|
-
inputs: [{ name: "account", type: "address" }],
|
|
2016
|
-
name: "balanceOf",
|
|
2017
|
-
outputs: [{ name: "", type: "uint256" }],
|
|
2018
|
-
stateMutability: "view",
|
|
2019
|
-
type: "function"
|
|
2020
|
-
},
|
|
2021
|
-
{
|
|
2022
|
-
inputs: [
|
|
2023
|
-
{ name: "to", type: "address" },
|
|
2024
|
-
{ name: "amount", type: "uint256" }
|
|
2025
|
-
],
|
|
2026
|
-
name: "transfer",
|
|
2027
|
-
outputs: [{ name: "", type: "bool" }],
|
|
2028
|
-
stateMutability: "nonpayable",
|
|
2029
|
-
type: "function"
|
|
2030
|
-
},
|
|
2031
|
-
{
|
|
2032
|
-
inputs: [
|
|
2033
|
-
{ name: "spender", type: "address" },
|
|
2034
|
-
{ name: "amount", type: "uint256" }
|
|
2035
|
-
],
|
|
2036
|
-
name: "approve",
|
|
2037
|
-
outputs: [{ name: "", type: "bool" }],
|
|
2038
|
-
stateMutability: "nonpayable",
|
|
2039
|
-
type: "function"
|
|
2040
|
-
},
|
|
2041
|
-
{
|
|
2042
|
-
inputs: [
|
|
2043
|
-
{ name: "owner", type: "address" },
|
|
2044
|
-
{ name: "spender", type: "address" }
|
|
2045
|
-
],
|
|
2046
|
-
name: "allowance",
|
|
2047
|
-
outputs: [{ name: "", type: "uint256" }],
|
|
2048
|
-
stateMutability: "view",
|
|
2049
|
-
type: "function"
|
|
2050
|
-
},
|
|
2051
|
-
{
|
|
2052
|
-
inputs: [
|
|
2053
|
-
{ name: "from", type: "address" },
|
|
2054
|
-
{ name: "to", type: "address" },
|
|
2055
|
-
{ name: "amount", type: "uint256" }
|
|
2056
|
-
],
|
|
2057
|
-
name: "transferFrom",
|
|
2058
|
-
outputs: [{ name: "", type: "bool" }],
|
|
2059
|
-
stateMutability: "nonpayable",
|
|
2060
|
-
type: "function"
|
|
2061
|
-
},
|
|
2062
|
-
{
|
|
2063
|
-
inputs: [],
|
|
2064
|
-
name: "decimals",
|
|
2065
|
-
outputs: [{ name: "", type: "uint8" }],
|
|
2066
|
-
stateMutability: "view",
|
|
2067
|
-
type: "function"
|
|
2068
|
-
}
|
|
2069
|
-
];
|
|
2070
|
-
|
|
2071
|
-
// src/BundlerClient.ts
|
|
2072
|
-
var BundlerClient = class {
|
|
2073
|
-
constructor(config, entryPointAddress) {
|
|
2074
|
-
this.bundlerUrl = config.bundlerUrl;
|
|
2075
|
-
this.entryPointAddress = entryPointAddress;
|
|
2076
|
-
}
|
|
2077
|
-
async call(method, params) {
|
|
2078
|
-
const response = await fetch(this.bundlerUrl, {
|
|
2079
|
-
method: "POST",
|
|
2080
|
-
headers: { "Content-Type": "application/json" },
|
|
2081
|
-
body: JSON.stringify({
|
|
2082
|
-
jsonrpc: "2.0",
|
|
2083
|
-
id: 1,
|
|
2084
|
-
method,
|
|
2085
|
-
params
|
|
2086
|
-
})
|
|
2087
|
-
});
|
|
2088
|
-
const result = await response.json();
|
|
2089
|
-
if (result.error) {
|
|
2090
|
-
throw new Error(result.error.message);
|
|
2091
|
-
}
|
|
2092
|
-
return result.result;
|
|
2093
|
-
}
|
|
2094
|
-
async estimateGas(userOp) {
|
|
2095
|
-
const result = await this.call("eth_estimateUserOperationGas", [
|
|
2096
|
-
{
|
|
2097
|
-
sender: userOp.sender,
|
|
2098
|
-
nonce: userOp.nonce ? "0x" + userOp.nonce.toString(16) : "0x0",
|
|
2099
|
-
initCode: userOp.initCode || "0x",
|
|
2100
|
-
callData: userOp.callData || "0x",
|
|
2101
|
-
paymasterAndData: userOp.paymasterAndData || "0x",
|
|
2102
|
-
signature: "0x"
|
|
2103
|
-
},
|
|
2104
|
-
this.entryPointAddress
|
|
2105
|
-
]);
|
|
2106
|
-
console.log("DEBUG: estimateGas result:", result);
|
|
2107
|
-
return {
|
|
2108
|
-
callGasLimit: result.callGasLimit,
|
|
2109
|
-
verificationGasLimit: result.verificationGasLimit,
|
|
2110
|
-
preVerificationGas: result.preVerificationGas,
|
|
2111
|
-
maxFeePerGas: result.maxFeePerGas,
|
|
2112
|
-
maxPriorityFeePerGas: result.maxPriorityFeePerGas,
|
|
2113
|
-
paymasterAndData: result.paymasterAndData
|
|
2114
|
-
};
|
|
2115
|
-
}
|
|
2116
|
-
async sendUserOperation(userOp) {
|
|
2117
|
-
return await this.call("eth_sendUserOperation", [
|
|
2118
|
-
{
|
|
2119
|
-
sender: userOp.sender,
|
|
2120
|
-
nonce: "0x" + userOp.nonce.toString(16),
|
|
2121
|
-
initCode: userOp.initCode,
|
|
2122
|
-
callData: userOp.callData,
|
|
2123
|
-
callGasLimit: "0x" + userOp.callGasLimit.toString(16),
|
|
2124
|
-
verificationGasLimit: "0x" + userOp.verificationGasLimit.toString(16),
|
|
2125
|
-
preVerificationGas: "0x" + userOp.preVerificationGas.toString(16),
|
|
2126
|
-
maxFeePerGas: "0x" + userOp.maxFeePerGas.toString(16),
|
|
2127
|
-
maxPriorityFeePerGas: "0x" + userOp.maxPriorityFeePerGas.toString(16),
|
|
2128
|
-
paymasterAndData: userOp.paymasterAndData,
|
|
2129
|
-
signature: userOp.signature
|
|
2130
|
-
},
|
|
2131
|
-
this.entryPointAddress
|
|
2132
|
-
]);
|
|
2133
|
-
}
|
|
2134
|
-
async waitForUserOperation(userOpHash, timeout = 6e4) {
|
|
2135
|
-
const startTime = Date.now();
|
|
2136
|
-
while (Date.now() - startTime < timeout) {
|
|
2137
|
-
const result = await this.call("eth_getUserOperationReceipt", [userOpHash]);
|
|
2138
|
-
if (result) {
|
|
2139
|
-
return result;
|
|
2140
|
-
}
|
|
2141
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2142
|
-
}
|
|
2143
|
-
throw new Error("Timeout waiting for UserOperation");
|
|
2144
|
-
}
|
|
2145
|
-
async requestApprovalSupport(token, owner, spender, amount) {
|
|
2146
|
-
return await this.call("pm_requestApprovalSupport", [
|
|
2147
|
-
token,
|
|
2148
|
-
owner,
|
|
2149
|
-
spender,
|
|
2150
|
-
amount.toString()
|
|
2151
|
-
]);
|
|
2152
|
-
}
|
|
2153
|
-
};
|
|
2154
|
-
var TokenService = class {
|
|
2155
|
-
constructor(chainConfig, publicClient) {
|
|
2156
|
-
this.tokens = /* @__PURE__ */ new Map();
|
|
2157
|
-
this.publicClient = publicClient;
|
|
2158
|
-
chainConfig.tokens.forEach((token) => {
|
|
2159
|
-
this.tokens.set(token.symbol.toUpperCase(), token);
|
|
2160
|
-
});
|
|
2161
|
-
}
|
|
2162
|
-
/**
|
|
2163
|
-
* Resolve token address from symbol or return address if provided
|
|
2164
|
-
*/
|
|
2165
|
-
getTokenAddress(token) {
|
|
2166
|
-
if (token.startsWith("0x")) return token;
|
|
2167
|
-
const info = this.tokens.get(token.toUpperCase());
|
|
2168
|
-
if (!info) throw new Error(`Token ${token} not found in chain config`);
|
|
2169
|
-
return info.address;
|
|
2170
|
-
}
|
|
2171
|
-
/**
|
|
2172
|
-
* Get balance of a token for an account
|
|
2173
|
-
*/
|
|
2174
|
-
async getBalance(token, account) {
|
|
2175
|
-
const address = this.getTokenAddress(token);
|
|
2176
|
-
if (address === "0x0000000000000000000000000000000000000000") {
|
|
2177
|
-
return await this.publicClient.getBalance({ address: account });
|
|
2178
|
-
}
|
|
2179
|
-
return await this.publicClient.readContract({
|
|
2180
|
-
address,
|
|
2181
|
-
abi: erc20Abi,
|
|
2182
|
-
functionName: "balanceOf",
|
|
2183
|
-
args: [account]
|
|
2184
|
-
});
|
|
2185
|
-
}
|
|
2186
|
-
/**
|
|
2187
|
-
* Get allowance (ERC-20 only)
|
|
2188
|
-
*/
|
|
2189
|
-
async getAllowance(token, owner, spender) {
|
|
2190
|
-
const address = this.getTokenAddress(token);
|
|
2191
|
-
if (address === "0x0000000000000000000000000000000000000000") {
|
|
2192
|
-
return 0n;
|
|
2193
|
-
}
|
|
2194
|
-
return await this.publicClient.readContract({
|
|
2195
|
-
address,
|
|
2196
|
-
abi: erc20Abi,
|
|
2197
|
-
functionName: "allowance",
|
|
2198
|
-
args: [owner, spender]
|
|
2199
|
-
});
|
|
2200
|
-
}
|
|
2201
|
-
/**
|
|
2202
|
-
* Encode transfer data
|
|
2203
|
-
*/
|
|
2204
|
-
encodeTransfer(recipient, amount) {
|
|
2205
|
-
return encodeFunctionData({
|
|
2206
|
-
abi: erc20Abi,
|
|
2207
|
-
functionName: "transfer",
|
|
2208
|
-
args: [recipient, amount]
|
|
2209
|
-
});
|
|
2210
|
-
}
|
|
2211
|
-
/**
|
|
2212
|
-
* Encode transferFrom data
|
|
2213
|
-
*/
|
|
2214
|
-
encodeTransferFrom(sender, recipient, amount) {
|
|
2215
|
-
return encodeFunctionData({
|
|
2216
|
-
abi: erc20Abi,
|
|
2217
|
-
functionName: "transferFrom",
|
|
2218
|
-
args: [sender, recipient, amount]
|
|
2219
|
-
});
|
|
2220
|
-
}
|
|
2221
|
-
/**
|
|
2222
|
-
* Encode approve data
|
|
2223
|
-
*/
|
|
2224
|
-
encodeApprove(spender, amount) {
|
|
2225
|
-
return encodeFunctionData({
|
|
2226
|
-
abi: erc20Abi,
|
|
2227
|
-
functionName: "approve",
|
|
2228
|
-
args: [spender, amount]
|
|
2229
|
-
});
|
|
2230
|
-
}
|
|
2231
|
-
};
|
|
2232
|
-
var UserOpBuilder = class {
|
|
2233
|
-
constructor(chainConfig, bundlerClient, publicClient) {
|
|
2234
|
-
this.chainConfig = chainConfig;
|
|
2235
|
-
this.bundlerClient = bundlerClient;
|
|
2236
|
-
this.publicClient = publicClient;
|
|
2237
|
-
this.entryPointAddress = chainConfig.entryPointAddress;
|
|
2238
|
-
this.factoryAddress = chainConfig.factoryAddress;
|
|
2239
|
-
}
|
|
2240
|
-
async getNonce(smartAccountAddress) {
|
|
2241
|
-
return await this.publicClient.readContract({
|
|
2242
|
-
address: this.entryPointAddress,
|
|
2243
|
-
abi: entryPointAbi,
|
|
2244
|
-
functionName: "getNonce",
|
|
2245
|
-
args: [smartAccountAddress, 0n]
|
|
2246
|
-
});
|
|
2247
|
-
}
|
|
2248
|
-
buildInitCode(owner) {
|
|
2249
|
-
const createAccountData = encodeFunctionData({
|
|
2250
|
-
abi: factoryAbi,
|
|
2251
|
-
functionName: "createAccount",
|
|
2252
|
-
args: [owner, 0n]
|
|
2253
|
-
});
|
|
2254
|
-
return `${this.factoryAddress}${createAccountData.slice(2)}`;
|
|
2255
|
-
}
|
|
2256
|
-
async isAccountDeployed(smartAccountAddress) {
|
|
2257
|
-
const code = await this.publicClient.getCode({
|
|
2258
|
-
address: smartAccountAddress
|
|
2259
|
-
});
|
|
2260
|
-
return code !== void 0 && code !== "0x";
|
|
2261
|
-
}
|
|
2262
|
-
async buildUserOperationBatch(owner, smartAccountAddress, transactions) {
|
|
2263
|
-
const isDeployed = await this.isAccountDeployed(smartAccountAddress);
|
|
2264
|
-
const initCode = isDeployed ? "0x" : this.buildInitCode(owner);
|
|
2265
|
-
const targets = transactions.map((tx) => tx.target);
|
|
2266
|
-
const values = transactions.map((tx) => tx.value);
|
|
2267
|
-
const datas = transactions.map((tx) => tx.data);
|
|
2268
|
-
const callData = encodeFunctionData({
|
|
2269
|
-
abi: smartAccountAbi,
|
|
2270
|
-
functionName: "executeBatch",
|
|
2271
|
-
args: [targets, values, datas]
|
|
2272
|
-
});
|
|
2273
|
-
const nonce = await this.getNonce(smartAccountAddress);
|
|
2274
|
-
const partialOp = {
|
|
2275
|
-
sender: smartAccountAddress,
|
|
2276
|
-
nonce,
|
|
2277
|
-
initCode,
|
|
2278
|
-
callData,
|
|
2279
|
-
paymasterAndData: this.chainConfig.paymasterAddress || "0x"
|
|
2280
|
-
};
|
|
2281
|
-
const gasEstimate = await this.bundlerClient.estimateGas(partialOp);
|
|
2282
|
-
return {
|
|
2283
|
-
...partialOp,
|
|
2284
|
-
callGasLimit: BigInt(gasEstimate.callGasLimit),
|
|
2285
|
-
verificationGasLimit: BigInt(gasEstimate.verificationGasLimit),
|
|
2286
|
-
preVerificationGas: BigInt(gasEstimate.preVerificationGas),
|
|
2287
|
-
maxFeePerGas: BigInt(gasEstimate.maxFeePerGas),
|
|
2288
|
-
maxPriorityFeePerGas: BigInt(gasEstimate.maxPriorityFeePerGas),
|
|
2289
|
-
paymasterAndData: gasEstimate.paymasterAndData || partialOp.paymasterAndData,
|
|
2290
|
-
signature: "0x"
|
|
2291
|
-
};
|
|
2292
|
-
}
|
|
2293
|
-
async buildDeployUserOp(owner, smartAccountAddress) {
|
|
2294
|
-
const isDeployed = await this.isAccountDeployed(smartAccountAddress);
|
|
2295
|
-
if (isDeployed) throw new Error("Account already deployed");
|
|
2296
|
-
const initCode = this.buildInitCode(owner);
|
|
2297
|
-
const callData = "0x";
|
|
2298
|
-
const nonce = await this.getNonce(smartAccountAddress);
|
|
2299
|
-
const partialOp = {
|
|
2300
|
-
sender: smartAccountAddress,
|
|
2301
|
-
nonce,
|
|
2302
|
-
initCode,
|
|
2303
|
-
callData,
|
|
2304
|
-
paymasterAndData: this.chainConfig.paymasterAddress || "0x"
|
|
2305
|
-
};
|
|
2306
|
-
const gasEstimate = await this.bundlerClient.estimateGas(partialOp);
|
|
2307
|
-
return {
|
|
2308
|
-
...partialOp,
|
|
2309
|
-
callGasLimit: BigInt(gasEstimate.callGasLimit),
|
|
2310
|
-
verificationGasLimit: BigInt(gasEstimate.verificationGasLimit),
|
|
2311
|
-
preVerificationGas: BigInt(gasEstimate.preVerificationGas),
|
|
2312
|
-
maxFeePerGas: BigInt(gasEstimate.maxFeePerGas),
|
|
2313
|
-
maxPriorityFeePerGas: BigInt(gasEstimate.maxPriorityFeePerGas),
|
|
2314
|
-
paymasterAndData: gasEstimate.paymasterAndData || partialOp.paymasterAndData,
|
|
2315
|
-
signature: "0x"
|
|
2316
|
-
};
|
|
2317
|
-
}
|
|
2318
|
-
getUserOpHash(userOp) {
|
|
2319
|
-
const packed = encodeAbiParameters(
|
|
2320
|
-
[
|
|
2321
|
-
{ type: "address" },
|
|
2322
|
-
{ type: "uint256" },
|
|
2323
|
-
{ type: "bytes32" },
|
|
2324
|
-
{ type: "bytes32" },
|
|
2325
|
-
{ type: "uint256" },
|
|
2326
|
-
{ type: "uint256" },
|
|
2327
|
-
{ type: "uint256" },
|
|
2328
|
-
{ type: "uint256" },
|
|
2329
|
-
{ type: "uint256" },
|
|
2330
|
-
{ type: "bytes32" }
|
|
2331
|
-
],
|
|
2332
|
-
[
|
|
2333
|
-
userOp.sender,
|
|
2334
|
-
userOp.nonce,
|
|
2335
|
-
keccak256(userOp.initCode),
|
|
2336
|
-
keccak256(userOp.callData),
|
|
2337
|
-
userOp.callGasLimit,
|
|
2338
|
-
userOp.verificationGasLimit,
|
|
2339
|
-
userOp.preVerificationGas,
|
|
2340
|
-
userOp.maxFeePerGas,
|
|
2341
|
-
userOp.maxPriorityFeePerGas,
|
|
2342
|
-
keccak256(userOp.paymasterAndData)
|
|
2343
|
-
]
|
|
2344
|
-
);
|
|
2345
|
-
const packedHash = keccak256(packed);
|
|
2346
|
-
return keccak256(
|
|
2347
|
-
encodeAbiParameters(
|
|
2348
|
-
[{ type: "bytes32" }, { type: "address" }, { type: "uint256" }],
|
|
2349
|
-
[packedHash, this.entryPointAddress, BigInt(this.chainConfig.chain.id)]
|
|
2350
|
-
)
|
|
2351
|
-
);
|
|
2352
|
-
}
|
|
2353
|
-
};
|
|
2354
|
-
|
|
2355
|
-
// src/AccountAbstraction.ts
|
|
2356
|
-
var AccountAbstraction = class {
|
|
2357
|
-
constructor(chainConfig) {
|
|
2358
|
-
this.owner = null;
|
|
2359
|
-
this.smartAccountAddress = null;
|
|
2360
|
-
this.walletClient = null;
|
|
2361
|
-
this.chainConfig = chainConfig;
|
|
2362
|
-
if (!chainConfig.entryPointAddress) throw new Error("EntryPoint address required");
|
|
2363
|
-
this.entryPointAddress = chainConfig.entryPointAddress;
|
|
2364
|
-
if (!chainConfig.factoryAddress) throw new Error("Factory address required");
|
|
2365
|
-
this.factoryAddress = chainConfig.factoryAddress;
|
|
2366
|
-
const rpcUrl = chainConfig.rpcUrl || chainConfig.chain.rpcUrls.default.http[0];
|
|
2367
|
-
this.publicClient = createPublicClient({
|
|
2368
|
-
chain: chainConfig.chain,
|
|
2369
|
-
transport: http(rpcUrl)
|
|
2370
|
-
});
|
|
2371
|
-
this.bundlerClient = new BundlerClient(chainConfig, this.entryPointAddress);
|
|
2372
|
-
this.tokenService = new TokenService(chainConfig, this.publicClient);
|
|
2373
|
-
this.userOpBuilder = new UserOpBuilder(chainConfig, this.bundlerClient, this.publicClient);
|
|
2374
|
-
}
|
|
2375
|
-
async connect(signer) {
|
|
2376
|
-
if (typeof signer === "string") {
|
|
2377
|
-
const account = privateKeyToAccount(signer);
|
|
2378
|
-
this.owner = account.address;
|
|
2379
|
-
const rpcUrl = this.chainConfig.rpcUrl || this.chainConfig.chain.rpcUrls.default.http[0];
|
|
2380
|
-
this.walletClient = createWalletClient({
|
|
2381
|
-
account,
|
|
2382
|
-
chain: this.chainConfig.chain,
|
|
2383
|
-
transport: http(rpcUrl)
|
|
2384
|
-
});
|
|
2385
|
-
} else if (signer && typeof signer === "object") {
|
|
2386
|
-
this.walletClient = signer;
|
|
2387
|
-
if (!this.walletClient.account) throw new Error("WalletClient must have an account");
|
|
2388
|
-
this.owner = this.walletClient.account.address;
|
|
2389
|
-
} else {
|
|
2390
|
-
if (typeof window === "undefined" || !window.ethereum) {
|
|
2391
|
-
throw new Error("MetaMask is not installed and no private key provided");
|
|
2392
|
-
}
|
|
2393
|
-
const accounts = await window.ethereum.request({
|
|
2394
|
-
method: "eth_requestAccounts"
|
|
2395
|
-
});
|
|
2396
|
-
if (!accounts || accounts.length === 0) throw new Error("No accounts found");
|
|
2397
|
-
const chainId = await window.ethereum.request({
|
|
2398
|
-
method: "eth_chainId"
|
|
2399
|
-
});
|
|
2400
|
-
const targetChainId = this.chainConfig.chain.id;
|
|
2401
|
-
if (parseInt(chainId, 16) !== targetChainId) {
|
|
2402
|
-
try {
|
|
2403
|
-
await window.ethereum.request({
|
|
2404
|
-
method: "wallet_switchEthereumChain",
|
|
2405
|
-
params: [{ chainId: "0x" + targetChainId.toString(16) }]
|
|
2406
|
-
});
|
|
2407
|
-
} catch (switchError) {
|
|
2408
|
-
const error = switchError;
|
|
2409
|
-
if (error.code === 4902) {
|
|
2410
|
-
await window.ethereum.request({
|
|
2411
|
-
method: "wallet_addEthereumChain",
|
|
2412
|
-
params: [
|
|
2413
|
-
{
|
|
2414
|
-
chainId: "0x" + targetChainId.toString(16),
|
|
2415
|
-
chainName: this.chainConfig.chain.name,
|
|
2416
|
-
nativeCurrency: this.chainConfig.chain.nativeCurrency,
|
|
2417
|
-
rpcUrls: [this.chainConfig.rpcUrl || this.chainConfig.chain.rpcUrls.default.http[0]],
|
|
2418
|
-
blockExplorerUrls: this.chainConfig.chain.blockExplorers?.default?.url ? [this.chainConfig.chain.blockExplorers.default.url] : []
|
|
2419
|
-
}
|
|
2420
|
-
]
|
|
2421
|
-
});
|
|
2422
|
-
} else {
|
|
2423
|
-
throw switchError;
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
this.owner = accounts[0];
|
|
2428
|
-
}
|
|
2429
|
-
this.smartAccountAddress = await this.getSmartAccountAddress(this.owner);
|
|
2430
|
-
return {
|
|
2431
|
-
owner: this.owner,
|
|
2432
|
-
smartAccount: this.smartAccountAddress
|
|
2433
|
-
};
|
|
2434
|
-
}
|
|
2435
|
-
// --- Account Management ---
|
|
2436
|
-
async isAccountDeployed() {
|
|
2437
|
-
if (!this.smartAccountAddress) return false;
|
|
2438
|
-
const code = await this.publicClient.getBytecode({ address: this.smartAccountAddress });
|
|
2439
|
-
return code !== void 0;
|
|
2440
|
-
}
|
|
2441
|
-
async deployAccount() {
|
|
2442
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2443
|
-
return this.sendTransaction({
|
|
2444
|
-
target: this.smartAccountAddress,
|
|
2445
|
-
value: 0n,
|
|
2446
|
-
data: "0x"
|
|
2447
|
-
});
|
|
2448
|
-
}
|
|
2449
|
-
/**
|
|
2450
|
-
* Get the Smart Account address for an owner
|
|
2451
|
-
*/
|
|
2452
|
-
async getSmartAccountAddress(owner) {
|
|
2453
|
-
const address = await this.publicClient.readContract({
|
|
2454
|
-
address: this.factoryAddress,
|
|
2455
|
-
abi: factoryAbi,
|
|
2456
|
-
functionName: "getAccountAddress",
|
|
2457
|
-
args: [owner, 0n]
|
|
2458
|
-
});
|
|
2459
|
-
return address;
|
|
2460
|
-
}
|
|
2461
|
-
// --- Token Methods (Delegated) ---
|
|
2462
|
-
getTokenAddress(token) {
|
|
2463
|
-
return this.tokenService.getTokenAddress(token);
|
|
2464
|
-
}
|
|
2465
|
-
async getBalance(token) {
|
|
2466
|
-
if (!this.smartAccountAddress) throw new Error("Not connected");
|
|
2467
|
-
return this.tokenService.getBalance(token, this.smartAccountAddress);
|
|
2468
|
-
}
|
|
2469
|
-
async getEoaBalance(token) {
|
|
2470
|
-
if (!this.owner) throw new Error("Not connected");
|
|
2471
|
-
return this.tokenService.getBalance(token, this.owner);
|
|
2472
|
-
}
|
|
2473
|
-
async getAllowance(token = "USDC") {
|
|
2474
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2475
|
-
return this.tokenService.getAllowance(token, this.owner, this.smartAccountAddress);
|
|
2476
|
-
}
|
|
2477
|
-
/**
|
|
2478
|
-
* Get comprehensive state of the account for a specific token
|
|
2479
|
-
* Useful for UI initialization
|
|
2480
|
-
*/
|
|
2481
|
-
async getAccountState(token) {
|
|
2482
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2483
|
-
const tokenAddress = this.getTokenAddress(token);
|
|
2484
|
-
const isNative = tokenAddress === "0x0000000000000000000000000000000000000000";
|
|
2485
|
-
const [balance, eoaBalance, allowance, isDeployed] = await Promise.all([
|
|
2486
|
-
this.getBalance(token),
|
|
2487
|
-
this.getEoaBalance(token),
|
|
2488
|
-
isNative ? 0n : this.getAllowance(token).catch(() => 0n),
|
|
2489
|
-
// Handle native/error gracefully
|
|
2490
|
-
this.isAccountDeployed()
|
|
2491
|
-
]);
|
|
2492
|
-
return {
|
|
2493
|
-
owner: this.owner,
|
|
2494
|
-
smartAccount: this.smartAccountAddress,
|
|
2495
|
-
balance,
|
|
2496
|
-
eoaBalance,
|
|
2497
|
-
allowance,
|
|
2498
|
-
isDeployed
|
|
2499
|
-
};
|
|
2500
|
-
}
|
|
2501
|
-
// --- Transactions ---
|
|
2502
|
-
async sendTransaction(tx) {
|
|
2503
|
-
return this.sendBatchTransaction([tx]);
|
|
2504
|
-
}
|
|
2505
|
-
async sendBatchTransaction(txs) {
|
|
2506
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2507
|
-
const transactions = txs.map((tx) => ({
|
|
2508
|
-
target: tx.target,
|
|
2509
|
-
value: tx.value ?? 0n,
|
|
2510
|
-
data: tx.data ?? "0x"
|
|
2511
|
-
}));
|
|
2512
|
-
try {
|
|
2513
|
-
const userOp = await this.userOpBuilder.buildUserOperationBatch(
|
|
2514
|
-
this.owner,
|
|
2515
|
-
this.smartAccountAddress,
|
|
2516
|
-
transactions
|
|
2517
|
-
);
|
|
2518
|
-
const signed = await this.signUserOperation(userOp);
|
|
2519
|
-
const hash = await this.sendUserOperation(signed);
|
|
2520
|
-
return await this.waitForUserOperation(hash);
|
|
2521
|
-
} catch (error) {
|
|
2522
|
-
throw this.decodeError(error);
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
async deposit(amount) {
|
|
2526
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2527
|
-
if (this.walletClient) {
|
|
2528
|
-
return await this.walletClient.sendTransaction({
|
|
2529
|
-
account: this.walletClient.account,
|
|
2530
|
-
to: this.smartAccountAddress,
|
|
2531
|
-
value: amount,
|
|
2532
|
-
chain: this.chainConfig.chain
|
|
2533
|
-
});
|
|
2534
|
-
}
|
|
2535
|
-
return await window.ethereum.request({
|
|
2536
|
-
method: "eth_sendTransaction",
|
|
2537
|
-
params: [{
|
|
2538
|
-
from: this.owner,
|
|
2539
|
-
to: this.smartAccountAddress,
|
|
2540
|
-
value: "0x" + amount.toString(16)
|
|
2541
|
-
}]
|
|
2542
|
-
});
|
|
2543
|
-
}
|
|
2544
|
-
/**
|
|
2545
|
-
* Smart Transfer: Automatically chooses best method (SA vs EOA) based on balances.
|
|
2546
|
-
* Supports strict fee handling.
|
|
2547
|
-
*/
|
|
2548
|
-
async smartTransfer(token, recipient, amount, fee) {
|
|
2549
|
-
if (!this.owner || !this.smartAccountAddress) throw new Error("Not connected");
|
|
2550
|
-
const tokenAddress = this.getTokenAddress(token);
|
|
2551
|
-
const isNative = tokenAddress === "0x0000000000000000000000000000000000000000";
|
|
2552
|
-
const [saBal, eoaBal, allowance] = await Promise.all([
|
|
2553
|
-
this.getBalance(token),
|
|
2554
|
-
this.getEoaBalance(token),
|
|
2555
|
-
isNative ? 0n : this.getAllowance(token).catch(() => 0n)
|
|
2556
|
-
]);
|
|
2557
|
-
const totalNeeded = amount + (fee?.amount || 0n);
|
|
2558
|
-
if (saBal >= totalNeeded) {
|
|
2559
|
-
const txs = [];
|
|
2560
|
-
if (isNative) {
|
|
2561
|
-
txs.push({ target: recipient, value: amount, data: "0x" });
|
|
2562
|
-
} else {
|
|
2563
|
-
txs.push({ target: tokenAddress, value: 0n, data: this.tokenService.encodeTransfer(recipient, amount) });
|
|
2564
|
-
}
|
|
2565
|
-
if (fee && fee.amount > 0n) {
|
|
2566
|
-
if (isNative) {
|
|
2567
|
-
txs.push({ target: fee.recipient, value: fee.amount, data: "0x" });
|
|
3194
|
+
/**
|
|
3195
|
+
* Special method to orchestrate the Base -> Optimism -> Base demo flow
|
|
3196
|
+
* entirely within the SDK.
|
|
3197
|
+
*/
|
|
3198
|
+
async executeMultiHopDemo(context, sourceAA, logCallback, overrides) {
|
|
3199
|
+
const log = logCallback || console.log;
|
|
3200
|
+
log("[TransferManager] Calculating Multi-Hop Route...");
|
|
3201
|
+
context.sourceChain === "Base" && (sourceAA.getChainId() === 84532 || context.sourceChainId === 84532);
|
|
3202
|
+
const sourceChainId = await sourceAA.getPublicClient().getChainId();
|
|
3203
|
+
const isSepolia = sourceChainId === 84532;
|
|
3204
|
+
const baseUrl = overrides?.bundlerUrl || "https://bundler.1llet.xyz";
|
|
3205
|
+
let intermediateConfig;
|
|
3206
|
+
if (isSepolia) {
|
|
3207
|
+
log(`[TransferManager] Detected Testnet (Base Sepolia). Using Optimism Sepolia.`);
|
|
3208
|
+
intermediateConfig = {
|
|
3209
|
+
tokens: [],
|
|
3210
|
+
...OP_SEPOLIA_CONFIG,
|
|
3211
|
+
bundlerUrl: `${baseUrl}/rpc?chain=optimismSepolia`
|
|
3212
|
+
};
|
|
3213
|
+
getAddress("0x5fd84259d66Cd46123540766Be93DFE6D43130D7");
|
|
2568
3214
|
} else {
|
|
2569
|
-
|
|
3215
|
+
log(`[TransferManager] Detected Mainnet (Base). Using Optimism Mainnet.`);
|
|
3216
|
+
intermediateConfig = {
|
|
3217
|
+
tokens: [],
|
|
3218
|
+
// Default empty
|
|
3219
|
+
...OPTIMISM.evm,
|
|
3220
|
+
bundlerUrl: `${baseUrl}/rpc?chain=optimism`
|
|
3221
|
+
};
|
|
3222
|
+
if (!intermediateConfig.tokens) intermediateConfig.tokens = [];
|
|
3223
|
+
getAddress("0x0b2C639c533813f4Aa9D7837CAf992c92bdE5162");
|
|
2570
3224
|
}
|
|
3225
|
+
const intermediateAA = new AccountAbstraction(intermediateConfig);
|
|
3226
|
+
await intermediateAA.connect();
|
|
3227
|
+
const interAddress = await intermediateAA.getSmartAccount();
|
|
3228
|
+
log(`[TransferManager] Refund Address (Intermediate SA): ${interAddress}`);
|
|
3229
|
+
const stargate = new StargateStrategy();
|
|
3230
|
+
const { getNearQuote: getNearQuote2 } = await Promise.resolve().then(() => (init_near(), near_exports));
|
|
3231
|
+
const { uniswapService: uniswapService2 } = await Promise.resolve().then(() => (init_uniswap(), uniswap_exports));
|
|
3232
|
+
const parsedInputAmount = parseUnits(context.amount, 6);
|
|
3233
|
+
const estimatedOpAmount = parsedInputAmount * 99n / 100n;
|
|
3234
|
+
const humanEstimatedOpAmount = formatUnits(estimatedOpAmount, 6);
|
|
3235
|
+
log(`[TransferManager] Planning Optimism -> Base (Near) for ~${humanEstimatedOpAmount} USDC...`);
|
|
3236
|
+
const nearQuote = await getNearQuote2(
|
|
3237
|
+
"Optimism",
|
|
3238
|
+
"Base",
|
|
3239
|
+
humanEstimatedOpAmount,
|
|
3240
|
+
"USDC",
|
|
3241
|
+
"USDC",
|
|
3242
|
+
context.recipient || context.senderAddress || await sourceAA.getSmartAccount(),
|
|
3243
|
+
// Final Recipient on Base
|
|
3244
|
+
interAddress
|
|
3245
|
+
// Refund Address on Optimism (Intermediate SA)
|
|
3246
|
+
);
|
|
3247
|
+
if (!nearQuote?.depositAddress) throw new Error("Planning Failed (No Near Deposit Address)");
|
|
3248
|
+
log(`[TransferManager] Near Deposit Address: ${nearQuote.depositAddress}`);
|
|
3249
|
+
log("[TransferManager] Planning Base -> Optimism (Stargate Direct)...");
|
|
3250
|
+
const sgResult = await stargate.execute({
|
|
3251
|
+
...context,
|
|
3252
|
+
destChain: "Optimism",
|
|
3253
|
+
recipient: nearQuote.depositAddress
|
|
3254
|
+
// DIRECTLY to Near
|
|
3255
|
+
});
|
|
3256
|
+
if (!sgResult.success) throw new Error(`Step 1 Planning Failed: ${sgResult.errorReason}`);
|
|
3257
|
+
const { txTarget, txData, txValue, approvalRequired } = sgResult.data;
|
|
3258
|
+
const buildStep1 = async () => {
|
|
3259
|
+
const requiredValue = BigInt(txValue || 0);
|
|
3260
|
+
let autoSwapTxs = [];
|
|
3261
|
+
if (requiredValue > 0n) {
|
|
3262
|
+
const nativeBalance = await sourceAA.getBalance("0x0000000000000000000000000000000000000000");
|
|
3263
|
+
if (nativeBalance < requiredValue) {
|
|
3264
|
+
log(`[TransferManager] Auto-Swap: Need ETH for fee.`);
|
|
3265
|
+
const missingETH = requiredValue * 110n / 100n - nativeBalance;
|
|
3266
|
+
const usdcNeeded = await uniswapService2.quoteUSDCForETH(missingETH);
|
|
3267
|
+
const routerAddress = uniswapService2.getRouterAddress();
|
|
3268
|
+
if (!routerAddress) throw new Error("Uniswap Router not found");
|
|
3269
|
+
const tokenAddr = getAddress(context.sourceToken === "USDC" ? "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" : "0x0");
|
|
3270
|
+
const parsedTransferAmount = parseUnits(context.amount, 6);
|
|
3271
|
+
const tokenBalance = await sourceAA.getBalance(tokenAddr);
|
|
3272
|
+
const totalRequiredUSDC = parsedTransferAmount + usdcNeeded;
|
|
3273
|
+
const amountToPull = totalRequiredUSDC > tokenBalance ? totalRequiredUSDC - tokenBalance : 0n;
|
|
3274
|
+
if (amountToPull > 0n) {
|
|
3275
|
+
const owner = sourceAA.getOwner();
|
|
3276
|
+
const smartAccount = await sourceAA.getSmartAccount();
|
|
3277
|
+
if (!owner || !smartAccount) throw new Error("Owner/SA not found for Pull");
|
|
3278
|
+
const allowance = await sourceAA.getAllowance(tokenAddr);
|
|
3279
|
+
if (allowance < amountToPull) throw new Error(`Auto-Swap: Approval needed (${formatUnits(amountToPull, 6)} USDC).`);
|
|
3280
|
+
log(`[TransferManager] Auto-Swap: Pulling ${formatUnits(amountToPull, 6)} USDC...`);
|
|
3281
|
+
autoSwapTxs.push({
|
|
3282
|
+
target: tokenAddr,
|
|
3283
|
+
data: encodeFunctionData({
|
|
3284
|
+
abi: erc20Abi$1,
|
|
3285
|
+
functionName: "transferFrom",
|
|
3286
|
+
args: [owner, smartAccount, amountToPull]
|
|
3287
|
+
}),
|
|
3288
|
+
value: 0n
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
const swapData = uniswapService2.buildSwapData(await sourceAA.getSmartAccount(), missingETH, usdcNeeded);
|
|
3292
|
+
const approveSwapData = encodeFunctionData({
|
|
3293
|
+
abi: erc20Abi$1,
|
|
3294
|
+
functionName: "approve",
|
|
3295
|
+
args: [routerAddress, usdcNeeded]
|
|
3296
|
+
});
|
|
3297
|
+
autoSwapTxs.push({ target: tokenAddr, data: approveSwapData, value: 0n });
|
|
3298
|
+
autoSwapTxs.push({ target: routerAddress, data: swapData, value: 0n });
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
const batch = [...autoSwapTxs];
|
|
3302
|
+
if (approvalRequired) {
|
|
3303
|
+
batch.push({
|
|
3304
|
+
target: approvalRequired.target,
|
|
3305
|
+
data: approvalRequired.data,
|
|
3306
|
+
value: BigInt(approvalRequired.value || 0)
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
batch.push({
|
|
3310
|
+
target: txTarget,
|
|
3311
|
+
data: txData,
|
|
3312
|
+
value: BigInt(txValue || 0)
|
|
3313
|
+
});
|
|
3314
|
+
return sourceAA.buildBatchUserOperation(batch);
|
|
3315
|
+
};
|
|
3316
|
+
const steps = [
|
|
3317
|
+
{
|
|
3318
|
+
aa: sourceAA,
|
|
3319
|
+
buildUserOp: buildStep1,
|
|
3320
|
+
description: "Base -> Optimism (Stargate -> Near Direct)"
|
|
3321
|
+
}
|
|
3322
|
+
];
|
|
3323
|
+
return this.router.executeMultiHop(steps, log);
|
|
2571
3324
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
3325
|
+
/**
|
|
3326
|
+
* Specialized method for Unichain -> Stacks flow.
|
|
3327
|
+
* 1. Unichain -> Ethereum (CCTP)
|
|
3328
|
+
* 2. Ethereum -> Stacks (Stacks Bridge)
|
|
3329
|
+
*/
|
|
3330
|
+
async executeUnichainToStacks(context, sourceAA, ethPrivateKey, logCallback) {
|
|
3331
|
+
const log = logCallback || console.log;
|
|
3332
|
+
log("[TransferManager] Starting Unichain -> Stacks Flow...");
|
|
3333
|
+
const cctpStrategy = this.strategies.find((s) => s instanceof CCTPStrategy);
|
|
3334
|
+
const stacksStrategy = this.strategies.find((s) => s instanceof StacksStrategy);
|
|
3335
|
+
if (context.sourceChain === "Ethereum") {
|
|
3336
|
+
log("[TransferManager] Source is Ethereum. Executing Stacks Bridge Step...");
|
|
3337
|
+
return stacksStrategy.execute({
|
|
3338
|
+
...context,
|
|
3339
|
+
facilitatorPrivateKey: ethPrivateKey
|
|
2584
3340
|
});
|
|
2585
|
-
return { receipt: { transactionHash: hash2 } };
|
|
2586
3341
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
target: tokenAddress,
|
|
2600
|
-
value: 0n,
|
|
2601
|
-
data: this.tokenService.encodeTransferFrom(this.owner, recipient, amount)
|
|
2602
|
-
});
|
|
2603
|
-
if (fee && fee.amount > 0n) {
|
|
2604
|
-
txs.push({
|
|
2605
|
-
target: tokenAddress,
|
|
2606
|
-
value: 0n,
|
|
2607
|
-
data: this.tokenService.encodeTransferFrom(this.owner, fee.recipient, fee.amount)
|
|
3342
|
+
if (context.sourceChain === "Unichain") {
|
|
3343
|
+
log("[TransferManager] Step 1: Unichain -> Ethereum (via CCTP)");
|
|
3344
|
+
const ethAccount = privateKeyToAccount(ethPrivateKey);
|
|
3345
|
+
const ethAddress = ethAccount.address;
|
|
3346
|
+
const cctpResult = await cctpStrategy.execute({
|
|
3347
|
+
...context,
|
|
3348
|
+
destChain: "Ethereum",
|
|
3349
|
+
destToken: "USDC",
|
|
3350
|
+
recipient: ethAddress,
|
|
3351
|
+
// Send to Facilitator's ETH Wallet
|
|
3352
|
+
facilitatorPrivateKey: ethPrivateKey
|
|
3353
|
+
// Using same key for Unichain CCTP signing if compatible
|
|
2608
3354
|
});
|
|
3355
|
+
if (!cctpResult.success) {
|
|
3356
|
+
return cctpResult;
|
|
3357
|
+
}
|
|
3358
|
+
log("[TransferManager] CCTP Step Initiated.");
|
|
3359
|
+
if (cctpResult.mintTransactionHash) {
|
|
3360
|
+
log("[TransferManager] CCTP Complete (Funds on Ethereum). Proceeding to Stacks Step...");
|
|
3361
|
+
const stacksResult = await stacksStrategy.execute({
|
|
3362
|
+
...context,
|
|
3363
|
+
sourceChain: "Ethereum",
|
|
3364
|
+
// Context update for next leg
|
|
3365
|
+
amount: cctpResult.netAmount || context.amount,
|
|
3366
|
+
// Use actual net amount received
|
|
3367
|
+
facilitatorPrivateKey: ethPrivateKey
|
|
3368
|
+
});
|
|
3369
|
+
return {
|
|
3370
|
+
success: stacksResult.success,
|
|
3371
|
+
transactionHash: stacksResult.transactionHash,
|
|
3372
|
+
// The final Stacks Tx
|
|
3373
|
+
data: {
|
|
3374
|
+
cctpStep: cctpResult,
|
|
3375
|
+
stacksStep: stacksResult
|
|
3376
|
+
},
|
|
3377
|
+
netAmount: stacksResult.netAmount,
|
|
3378
|
+
errorReason: stacksResult.errorReason
|
|
3379
|
+
};
|
|
3380
|
+
} else {
|
|
3381
|
+
log("[TransferManager] CCTP Pending (Attestation/Mint not final yet). Please resume later.");
|
|
3382
|
+
return {
|
|
3383
|
+
...cctpResult,
|
|
3384
|
+
attestation: {
|
|
3385
|
+
message: "CCTP Initiated but not finalized (Timeout or Pending). Resume with sourceChain='Ethereum' once funds arrive.",
|
|
3386
|
+
attestation: cctpResult.attestation?.attestation || ""
|
|
3387
|
+
}
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
2609
3390
|
}
|
|
2610
|
-
return
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
}
|
|
2615
|
-
async transfer(token, recipient, amount) {
|
|
2616
|
-
const tokenAddress = this.getTokenAddress(token);
|
|
2617
|
-
if (tokenAddress === "0x0000000000000000000000000000000000000000") {
|
|
2618
|
-
return this.sendTransaction({
|
|
2619
|
-
target: recipient,
|
|
2620
|
-
value: amount,
|
|
2621
|
-
data: "0x"
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
const data = this.tokenService.encodeTransfer(recipient, amount);
|
|
2625
|
-
return this.sendTransaction({
|
|
2626
|
-
target: tokenAddress,
|
|
2627
|
-
value: 0n,
|
|
2628
|
-
data
|
|
2629
|
-
});
|
|
2630
|
-
}
|
|
2631
|
-
/**
|
|
2632
|
-
* Approve a token for the Smart Account
|
|
2633
|
-
*/
|
|
2634
|
-
async approveToken(token, spender, amount = 115792089237316195423570985008687907853269984665640564039457584007913129639935n) {
|
|
2635
|
-
if (!this.owner) throw new Error("Not connected");
|
|
2636
|
-
const support = await this.requestApprovalSupport(token, spender, amount);
|
|
2637
|
-
if (support.type === "approve") {
|
|
2638
|
-
const data = this.tokenService.encodeApprove(spender, amount);
|
|
2639
|
-
if (this.walletClient) {
|
|
2640
|
-
return await this.walletClient.sendTransaction({
|
|
2641
|
-
account: this.walletClient.account,
|
|
2642
|
-
to: token,
|
|
2643
|
-
data,
|
|
2644
|
-
chain: this.chainConfig.chain
|
|
2645
|
-
});
|
|
2646
|
-
}
|
|
2647
|
-
return await window.ethereum.request({
|
|
2648
|
-
method: "eth_sendTransaction",
|
|
2649
|
-
params: [{
|
|
2650
|
-
from: this.owner,
|
|
2651
|
-
to: token,
|
|
2652
|
-
data
|
|
2653
|
-
}]
|
|
2654
|
-
});
|
|
2655
|
-
}
|
|
2656
|
-
if (support.type === "permit") throw new Error("Permit not yet supported");
|
|
2657
|
-
return "NOT_NEEDED";
|
|
2658
|
-
}
|
|
2659
|
-
// --- Core Bridge to Bundler/UserOp ---
|
|
2660
|
-
async signUserOperation(userOp) {
|
|
2661
|
-
if (!this.owner) throw new Error("Not connected");
|
|
2662
|
-
const userOpHash = this.userOpBuilder.getUserOpHash(userOp);
|
|
2663
|
-
let signature;
|
|
2664
|
-
if (this.walletClient) {
|
|
2665
|
-
signature = await this.walletClient.signMessage({
|
|
2666
|
-
account: this.walletClient.account,
|
|
2667
|
-
message: { raw: userOpHash }
|
|
2668
|
-
// Sign hash directly
|
|
2669
|
-
});
|
|
2670
|
-
} else {
|
|
2671
|
-
signature = await window.ethereum.request({
|
|
2672
|
-
method: "personal_sign",
|
|
2673
|
-
params: [userOpHash, this.owner]
|
|
2674
|
-
});
|
|
2675
|
-
}
|
|
2676
|
-
return { ...userOp, signature };
|
|
2677
|
-
}
|
|
2678
|
-
async sendUserOperation(userOp) {
|
|
2679
|
-
return this.bundlerClient.sendUserOperation(userOp);
|
|
2680
|
-
}
|
|
2681
|
-
async waitForUserOperation(hash, timeout = 6e4) {
|
|
2682
|
-
return this.bundlerClient.waitForUserOperation(hash, timeout);
|
|
2683
|
-
}
|
|
2684
|
-
// Internal but exposed via BundlerClient originally
|
|
2685
|
-
async requestApprovalSupport(token, spender, amount) {
|
|
2686
|
-
if (!this.owner) throw new Error("Not connected");
|
|
2687
|
-
return this.bundlerClient.requestApprovalSupport(token, this.owner, spender, amount);
|
|
2688
|
-
}
|
|
2689
|
-
// Error Decoding (Private)
|
|
2690
|
-
decodeError(error) {
|
|
2691
|
-
const msg = error?.message || "";
|
|
2692
|
-
const hexMatch = msg.match(/(0x[0-9a-fA-F]+)/);
|
|
2693
|
-
if (hexMatch) {
|
|
2694
|
-
try {
|
|
2695
|
-
const decoded = decodeErrorResult({
|
|
2696
|
-
abi: [{ inputs: [{ name: "message", type: "string" }], name: "Error", type: "error" }],
|
|
2697
|
-
data: hexMatch[0]
|
|
2698
|
-
});
|
|
2699
|
-
if (decoded.errorName === "Error") return new Error(`Smart Account Error: ${decoded.args[0]}`);
|
|
2700
|
-
} catch (e) {
|
|
3391
|
+
return {
|
|
3392
|
+
success: false,
|
|
3393
|
+
errorReason: "Unsupported Route for Unichain->Stacks Helper"
|
|
3394
|
+
};
|
|
2701
3395
|
}
|
|
2702
|
-
}
|
|
2703
|
-
if (msg.includes("AA21")) return new Error("Smart Account: Native transfer failed (ETH missing?)");
|
|
2704
|
-
if (msg.includes("AA25")) return new Error("Smart Account: Invalid account nonce");
|
|
2705
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
2706
|
-
}
|
|
2707
|
-
// Getters
|
|
2708
|
-
getOwner() {
|
|
2709
|
-
return this.owner;
|
|
2710
|
-
}
|
|
2711
|
-
getSmartAccount() {
|
|
2712
|
-
return this.smartAccountAddress;
|
|
3396
|
+
};
|
|
2713
3397
|
}
|
|
2714
|
-
};
|
|
3398
|
+
});
|
|
3399
|
+
|
|
3400
|
+
// src/index.ts
|
|
3401
|
+
init_AccountAbstraction();
|
|
3402
|
+
init_BundlerClient();
|
|
3403
|
+
init_Base();
|
|
3404
|
+
init_Avalanche();
|
|
3405
|
+
init_Optimism();
|
|
2715
3406
|
|
|
2716
3407
|
// src/chains.ts
|
|
2717
3408
|
init_Base();
|
|
@@ -2805,7 +3496,11 @@ var CHAIN_CONFIGS = {
|
|
|
2805
3496
|
[monad.id]: MONAD_MAINNET,
|
|
2806
3497
|
9e3: STELLAR_MAINNET
|
|
2807
3498
|
};
|
|
3499
|
+
|
|
3500
|
+
// src/index.ts
|
|
3501
|
+
init_constants();
|
|
2808
3502
|
var CHAIN_ID_TO_KEY = {
|
|
3503
|
+
[mainnet.id]: "Ethereum",
|
|
2809
3504
|
[base.id]: "Base",
|
|
2810
3505
|
[baseSepolia.id]: "Base",
|
|
2811
3506
|
[gnosis.id]: "Gnosis",
|
|
@@ -2827,75 +3522,9 @@ init_TransferManager();
|
|
|
2827
3522
|
init_near();
|
|
2828
3523
|
init_cctp2();
|
|
2829
3524
|
init_stargate();
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
2833
|
-
var WETH_ADDRESS = "0x4200000000000000000000000000000000000006";
|
|
2834
|
-
var QUOTER_ABI = parseAbi([
|
|
2835
|
-
"function quoteExactOutputSingle((address tokenIn, address tokenOut, uint256 amount, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)"
|
|
2836
|
-
]);
|
|
2837
|
-
var ROUTER_ABI = parseAbi([
|
|
2838
|
-
"function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn)"
|
|
2839
|
-
]);
|
|
2840
|
-
var UniswapService = class {
|
|
2841
|
-
constructor(rpcUrl) {
|
|
2842
|
-
this.publicClient = createPublicClient({
|
|
2843
|
-
transport: http(rpcUrl)
|
|
2844
|
-
});
|
|
2845
|
-
}
|
|
2846
|
-
/**
|
|
2847
|
-
* Quote USDC needed for exact ETH output using Uniswap V3
|
|
2848
|
-
*/
|
|
2849
|
-
async quoteUSDCForETH(amountOutETH) {
|
|
2850
|
-
try {
|
|
2851
|
-
const result = await this.publicClient.readContract({
|
|
2852
|
-
address: QUOTER_V2,
|
|
2853
|
-
abi: QUOTER_ABI,
|
|
2854
|
-
functionName: "quoteExactOutputSingle",
|
|
2855
|
-
args: [{
|
|
2856
|
-
tokenIn: USDC_ADDRESS,
|
|
2857
|
-
tokenOut: WETH_ADDRESS,
|
|
2858
|
-
amount: amountOutETH,
|
|
2859
|
-
fee: 500,
|
|
2860
|
-
// 0.05%
|
|
2861
|
-
sqrtPriceLimitX96: 0n
|
|
2862
|
-
}]
|
|
2863
|
-
});
|
|
2864
|
-
const amountIn = result[0];
|
|
2865
|
-
return amountIn * 105n / 100n;
|
|
2866
|
-
} catch (error) {
|
|
2867
|
-
console.error("Failed to quote USDC for ETH swap on Uniswap V3", error);
|
|
2868
|
-
throw new Error(`Failed to quote USDC for ETH swap on Uniswap V3`);
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
/**
|
|
2872
|
-
* Build tx data for swapping USDC -> ETH using Uniswap V3 SwapRouter02
|
|
2873
|
-
*/
|
|
2874
|
-
buildSwapData(recipient, amountInMax, amountOutETH) {
|
|
2875
|
-
return encodeFunctionData({
|
|
2876
|
-
abi: ROUTER_ABI,
|
|
2877
|
-
functionName: "exactOutputSingle",
|
|
2878
|
-
args: [{
|
|
2879
|
-
tokenIn: USDC_ADDRESS,
|
|
2880
|
-
tokenOut: WETH_ADDRESS,
|
|
2881
|
-
fee: 500,
|
|
2882
|
-
// 0.05%
|
|
2883
|
-
recipient,
|
|
2884
|
-
amountOut: amountOutETH,
|
|
2885
|
-
amountInMaximum: amountInMax,
|
|
2886
|
-
sqrtPriceLimitX96: 0n
|
|
2887
|
-
}]
|
|
2888
|
-
});
|
|
2889
|
-
}
|
|
2890
|
-
getRouterAddress() {
|
|
2891
|
-
return SWAP_ROUTER_02;
|
|
2892
|
-
}
|
|
2893
|
-
getUSDCAddress() {
|
|
2894
|
-
return USDC_ADDRESS;
|
|
2895
|
-
}
|
|
2896
|
-
};
|
|
2897
|
-
var uniswapService = new UniswapService("https://mainnet.base.org");
|
|
3525
|
+
init_uniswap();
|
|
3526
|
+
init_Router();
|
|
2898
3527
|
|
|
2899
|
-
export { AccountAbstraction, BASE_MAINNET, BASE_SEPOLIA2 as BASE_SEPOLIA, BundlerClient, CCTPStrategy, CHAIN_CONFIGS, CHAIN_ID_TO_KEY, GNOSIS_MAINNET, NearStrategy, OPTIMISM_MAINNET, STELLAR, STELLAR_MAINNET, StargateStrategy, StellarService, TransferManager, UniswapService, entryPointAbi, erc20Abi, getNearSimulation, getStargateSimulation, smartAccountAbi, uniswapService };
|
|
3528
|
+
export { ARBITRUM_MAINNET, AVALANCHE, AVALANCHE_MAINNET, AccountAbstraction, BASE, BASE_MAINNET, BASE_SEPOLIA2 as BASE_SEPOLIA, BSC_MAINNET, BundlerClient, CCTPStrategy, CHAIN_CONFIGS, CHAIN_ID_TO_KEY, GNOSIS_MAINNET, MONAD_MAINNET, NearStrategy, OPTIMISM, OPTIMISM_MAINNET, POLYGON_MAINNET, RouterService, STELLAR, STELLAR_MAINNET, StargateStrategy, StellarService, TransferManager, UNICHAIN_MAINNET, UniswapService, entryPointAbi, erc20Abi, getNearQuote, getNearSimulation, getStargateSimulation, smartAccountAbi, uniswapService };
|
|
2900
3529
|
//# sourceMappingURL=index.mjs.map
|
|
2901
3530
|
//# sourceMappingURL=index.mjs.map
|