four-flap-meme-sdk 1.4.91 → 1.4.92
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.ts +3 -0
- package/dist/index.js +24 -0
- package/dist/sol/constants.d.ts +126 -0
- package/dist/sol/constants.js +145 -0
- package/dist/sol/dex/blockrazor/client.d.ts +51 -0
- package/dist/sol/dex/blockrazor/client.js +96 -0
- package/dist/sol/dex/blockrazor/constants.d.ts +34 -0
- package/dist/sol/dex/blockrazor/constants.js +55 -0
- package/dist/sol/dex/blockrazor/geyser.d.ts +128 -0
- package/dist/sol/dex/blockrazor/geyser.js +530 -0
- package/dist/sol/dex/blockrazor/index.d.ts +18 -0
- package/dist/sol/dex/blockrazor/index.js +23 -0
- package/dist/sol/dex/blockrazor/send.d.ts +135 -0
- package/dist/sol/dex/blockrazor/send.js +254 -0
- package/dist/sol/dex/blockrazor/types.d.ts +191 -0
- package/dist/sol/dex/blockrazor/types.js +5 -0
- package/dist/sol/dex/index.d.ts +10 -0
- package/dist/sol/dex/index.js +16 -0
- package/dist/sol/dex/jup/client.d.ts +33 -0
- package/dist/sol/dex/jup/client.js +110 -0
- package/dist/sol/dex/jup/index.d.ts +16 -0
- package/dist/sol/dex/jup/index.js +148 -0
- package/dist/sol/dex/jup/legacy.d.ts +623 -0
- package/dist/sol/dex/jup/legacy.js +416 -0
- package/dist/sol/dex/jup/lend.d.ts +640 -0
- package/dist/sol/dex/jup/lend.js +603 -0
- package/dist/sol/dex/jup/portfolio.d.ts +362 -0
- package/dist/sol/dex/jup/portfolio.js +367 -0
- package/dist/sol/dex/jup/price.d.ts +173 -0
- package/dist/sol/dex/jup/price.js +220 -0
- package/dist/sol/dex/jup/recurring.d.ts +437 -0
- package/dist/sol/dex/jup/recurring.js +320 -0
- package/dist/sol/dex/jup/send.d.ts +282 -0
- package/dist/sol/dex/jup/send.js +295 -0
- package/dist/sol/dex/jup/studio.d.ts +457 -0
- package/dist/sol/dex/jup/studio.js +488 -0
- package/dist/sol/dex/jup/tokens.d.ts +767 -0
- package/dist/sol/dex/jup/tokens.js +697 -0
- package/dist/sol/dex/jup/trigger.d.ts +511 -0
- package/dist/sol/dex/jup/trigger.js +397 -0
- package/dist/sol/dex/jup/types.d.ts +433 -0
- package/dist/sol/dex/jup/types.js +5 -0
- package/dist/sol/dex/jup/ultra.d.ts +646 -0
- package/dist/sol/dex/jup/ultra.js +853 -0
- package/dist/sol/dex/meteora/client.d.ts +76 -0
- package/dist/sol/dex/meteora/client.js +219 -0
- package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +61 -0
- package/dist/sol/dex/meteora/damm-v1-bundle.js +112 -0
- package/dist/sol/dex/meteora/damm-v1.d.ts +118 -0
- package/dist/sol/dex/meteora/damm-v1.js +315 -0
- package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +82 -0
- package/dist/sol/dex/meteora/damm-v2-bundle.js +242 -0
- package/dist/sol/dex/meteora/damm-v2.d.ts +172 -0
- package/dist/sol/dex/meteora/damm-v2.js +632 -0
- package/dist/sol/dex/meteora/dbc-bundle.d.ts +123 -0
- package/dist/sol/dex/meteora/dbc-bundle.js +304 -0
- package/dist/sol/dex/meteora/dbc.d.ts +192 -0
- package/dist/sol/dex/meteora/dbc.js +619 -0
- package/dist/sol/dex/meteora/dlmm-bundle.d.ts +39 -0
- package/dist/sol/dex/meteora/dlmm-bundle.js +189 -0
- package/dist/sol/dex/meteora/dlmm.d.ts +157 -0
- package/dist/sol/dex/meteora/dlmm.js +671 -0
- package/dist/sol/dex/meteora/index.d.ts +25 -0
- package/dist/sol/dex/meteora/index.js +65 -0
- package/dist/sol/dex/meteora/types.d.ts +787 -0
- package/dist/sol/dex/meteora/types.js +110 -0
- package/dist/sol/dex/orca/index.d.ts +10 -0
- package/dist/sol/dex/orca/index.js +16 -0
- package/dist/sol/dex/orca/orca-bundle.d.ts +41 -0
- package/dist/sol/dex/orca/orca-bundle.js +173 -0
- package/dist/sol/dex/orca/orca.d.ts +65 -0
- package/dist/sol/dex/orca/orca.js +474 -0
- package/dist/sol/dex/orca/types.d.ts +263 -0
- package/dist/sol/dex/orca/types.js +38 -0
- package/dist/sol/dex/orca/wavebreak-bundle.d.ts +34 -0
- package/dist/sol/dex/orca/wavebreak-bundle.js +198 -0
- package/dist/sol/dex/orca/wavebreak-types.d.ts +227 -0
- package/dist/sol/dex/orca/wavebreak-types.js +23 -0
- package/dist/sol/dex/orca/wavebreak.d.ts +78 -0
- package/dist/sol/dex/orca/wavebreak.js +497 -0
- package/dist/sol/dex/pump/index.d.ts +9 -0
- package/dist/sol/dex/pump/index.js +14 -0
- package/dist/sol/dex/pump/pump-bundle.d.ts +92 -0
- package/dist/sol/dex/pump/pump-bundle.js +383 -0
- package/dist/sol/dex/pump/pump-swap-bundle.d.ts +103 -0
- package/dist/sol/dex/pump/pump-swap-bundle.js +380 -0
- package/dist/sol/dex/pump/pump-swap.d.ts +46 -0
- package/dist/sol/dex/pump/pump-swap.js +199 -0
- package/dist/sol/dex/pump/pump.d.ts +35 -0
- package/dist/sol/dex/pump/pump.js +352 -0
- package/dist/sol/dex/pump/types.d.ts +215 -0
- package/dist/sol/dex/pump/types.js +5 -0
- package/dist/sol/dex/raydium/index.d.ts +8 -0
- package/dist/sol/dex/raydium/index.js +12 -0
- package/dist/sol/dex/raydium/launchlab.d.ts +68 -0
- package/dist/sol/dex/raydium/launchlab.js +210 -0
- package/dist/sol/dex/raydium/raydium-bundle.d.ts +64 -0
- package/dist/sol/dex/raydium/raydium-bundle.js +324 -0
- package/dist/sol/dex/raydium/raydium.d.ts +40 -0
- package/dist/sol/dex/raydium/raydium.js +366 -0
- package/dist/sol/dex/raydium/types.d.ts +240 -0
- package/dist/sol/dex/raydium/types.js +5 -0
- package/dist/sol/index.d.ts +10 -0
- package/dist/sol/index.js +16 -0
- package/dist/sol/jito/bundle.d.ts +90 -0
- package/dist/sol/jito/bundle.js +263 -0
- package/dist/sol/jito/index.d.ts +7 -0
- package/dist/sol/jito/index.js +7 -0
- package/dist/sol/jito/tip.d.ts +51 -0
- package/dist/sol/jito/tip.js +83 -0
- package/dist/sol/jito/types.d.ts +100 -0
- package/dist/sol/jito/types.js +5 -0
- package/dist/sol/token/create-complete.d.ts +115 -0
- package/dist/sol/token/create-complete.js +235 -0
- package/dist/sol/token/create-token.d.ts +57 -0
- package/dist/sol/token/create-token.js +230 -0
- package/dist/sol/token/index.d.ts +9 -0
- package/dist/sol/token/index.js +14 -0
- package/dist/sol/token/metadata-upload.d.ts +86 -0
- package/dist/sol/token/metadata-upload.js +173 -0
- package/dist/sol/token/metadata.d.ts +92 -0
- package/dist/sol/token/metadata.js +274 -0
- package/dist/sol/token/types.d.ts +153 -0
- package/dist/sol/token/types.js +5 -0
- package/dist/sol/types.d.ts +176 -0
- package/dist/sol/types.js +7 -0
- package/dist/sol/utils/balance.d.ts +160 -0
- package/dist/sol/utils/balance.js +638 -0
- package/dist/sol/utils/connection.d.ts +78 -0
- package/dist/sol/utils/connection.js +168 -0
- package/dist/sol/utils/index.d.ts +9 -0
- package/dist/sol/utils/index.js +9 -0
- package/dist/sol/utils/lp-inspect.d.ts +129 -0
- package/dist/sol/utils/lp-inspect.js +900 -0
- package/dist/sol/utils/transfer.d.ts +196 -0
- package/dist/sol/utils/transfer.js +307 -0
- package/dist/sol/utils/wallet.d.ts +107 -0
- package/dist/sol/utils/wallet.js +210 -0
- package/dist/xlayer/aa-account.d.ts +240 -0
- package/dist/xlayer/aa-account.js +510 -0
- package/dist/xlayer/bundle.d.ts +93 -0
- package/dist/xlayer/bundle.js +472 -0
- package/dist/xlayer/bundler.d.ts +111 -0
- package/dist/xlayer/bundler.js +162 -0
- package/dist/xlayer/constants.d.ts +75 -0
- package/dist/xlayer/constants.js +145 -0
- package/dist/xlayer/dex.d.ts +132 -0
- package/dist/xlayer/dex.js +335 -0
- package/dist/xlayer/examples/bundle-buy-sell.d.ts +11 -0
- package/dist/xlayer/examples/bundle-buy-sell.js +194 -0
- package/dist/xlayer/index.d.ts +75 -0
- package/dist/xlayer/index.js +130 -0
- package/dist/xlayer/portal-ops.d.ts +152 -0
- package/dist/xlayer/portal-ops.js +239 -0
- package/dist/xlayer/types.d.ts +298 -0
- package/dist/xlayer/types.js +6 -0
- package/dist/xlayer/volume.d.ts +65 -0
- package/dist/xlayer/volume.js +235 -0
- package/package.json +38 -3
- package/README.zh-CN.pdf +0 -0
- package/dist/flap/portal-bundle-merkle/encryption.d.ts +0 -16
- package/dist/flap/portal-bundle-merkle/encryption.js +0 -146
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLayer AA 账户管理器
|
|
3
|
+
*
|
|
4
|
+
* 核心功能:
|
|
5
|
+
* - AA 账户(Sender)地址预测和部署
|
|
6
|
+
* - UserOperation 构建和签名
|
|
7
|
+
* - 余额管理和资金充值
|
|
8
|
+
* - initCode 生成
|
|
9
|
+
*/
|
|
10
|
+
import { ethers, JsonRpcProvider, Wallet, Contract, Interface } from 'ethers';
|
|
11
|
+
import { XLAYER_CHAIN_ID, DEFAULT_RPC_URL, ENTRYPOINT_V06, SIMPLE_ACCOUNT_FACTORY, DEFAULT_SALT, DEFAULT_GAS_PRICE, GAS_LIMIT_MULTIPLIER, VERIFICATION_GAS_LIMIT_DEPLOY, VERIFICATION_GAS_LIMIT_NORMAL, PRE_VERIFICATION_GAS, FACTORY_ABI, ENTRYPOINT_ABI, SIMPLE_ACCOUNT_ABI, } from './constants.js';
|
|
12
|
+
import { BundlerClient } from './bundler.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// AA 账户管理器
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* AA 账户管理器
|
|
18
|
+
*
|
|
19
|
+
* 管理 ERC-4337 SimpleAccount:
|
|
20
|
+
* - 地址预测(无需部署即可知道地址)
|
|
21
|
+
* - UserOp 构建和签名
|
|
22
|
+
* - Gas 估算和 Prefund 计算
|
|
23
|
+
* - 支持 Paymaster(免 Gas)
|
|
24
|
+
*/
|
|
25
|
+
export class AAAccountManager {
|
|
26
|
+
constructor(config = {}) {
|
|
27
|
+
this.chainId = config.chainId ?? XLAYER_CHAIN_ID;
|
|
28
|
+
const rpcUrl = config.rpcUrl ?? DEFAULT_RPC_URL;
|
|
29
|
+
this.provider = new JsonRpcProvider(rpcUrl, {
|
|
30
|
+
chainId: this.chainId,
|
|
31
|
+
name: 'xlayer',
|
|
32
|
+
});
|
|
33
|
+
this.factoryAddress = config.factory ?? SIMPLE_ACCOUNT_FACTORY;
|
|
34
|
+
this.entryPointAddress = config.entryPoint ?? ENTRYPOINT_V06;
|
|
35
|
+
this.salt = config.salt ?? DEFAULT_SALT;
|
|
36
|
+
this.paymaster = config.paymaster;
|
|
37
|
+
this.paymasterData = config.paymasterData;
|
|
38
|
+
this.gasLimitMultiplier = config.gasLimitMultiplier ?? GAS_LIMIT_MULTIPLIER;
|
|
39
|
+
this.factory = new Contract(this.factoryAddress, FACTORY_ABI, this.provider);
|
|
40
|
+
this.entryPoint = new Contract(this.entryPointAddress, ENTRYPOINT_ABI, this.provider);
|
|
41
|
+
this.bundler = new BundlerClient({
|
|
42
|
+
url: config.bundlerUrl,
|
|
43
|
+
chainId: this.chainId,
|
|
44
|
+
entryPoint: this.entryPointAddress,
|
|
45
|
+
timeoutMs: config.timeoutMs,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 获取 Provider
|
|
50
|
+
*/
|
|
51
|
+
getProvider() {
|
|
52
|
+
return this.provider;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 获取 EntryPoint 合约
|
|
56
|
+
*/
|
|
57
|
+
getEntryPoint() {
|
|
58
|
+
return this.entryPoint;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 获取 EntryPoint 地址
|
|
62
|
+
*/
|
|
63
|
+
getEntryPointAddress() {
|
|
64
|
+
return this.entryPointAddress;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 获取 Bundler 客户端
|
|
68
|
+
*/
|
|
69
|
+
getBundler() {
|
|
70
|
+
return this.bundler;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 获取当前 Fee Data
|
|
74
|
+
*/
|
|
75
|
+
async getFeeData() {
|
|
76
|
+
return await this.provider.getFeeData();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 预测 AA Sender 地址
|
|
80
|
+
*
|
|
81
|
+
* @param ownerAddress Owner EOA 地址
|
|
82
|
+
* @param salt 可选的 salt 覆盖
|
|
83
|
+
* @returns 预测的 Sender 地址
|
|
84
|
+
*/
|
|
85
|
+
async predictSenderAddress(ownerAddress, salt) {
|
|
86
|
+
const useSalt = salt ?? this.salt;
|
|
87
|
+
// 使用 staticCall 模拟 createAccount 获取预测地址
|
|
88
|
+
// 这比 getAddress 更可靠(某些链上 getAddress 有问题)
|
|
89
|
+
return await this.factory.createAccount.staticCall(ownerAddress, useSalt);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 获取完整的 AA 账户信息
|
|
93
|
+
*/
|
|
94
|
+
async getAccountInfo(ownerAddress, salt) {
|
|
95
|
+
const sender = await this.predictSenderAddress(ownerAddress, salt);
|
|
96
|
+
const [code, balance, nonce] = await Promise.all([
|
|
97
|
+
this.provider.getCode(sender),
|
|
98
|
+
this.provider.getBalance(sender),
|
|
99
|
+
this.entryPoint.getNonce(sender, 0),
|
|
100
|
+
]);
|
|
101
|
+
return {
|
|
102
|
+
owner: ownerAddress,
|
|
103
|
+
sender,
|
|
104
|
+
deployed: code !== null && code !== '0x',
|
|
105
|
+
balance,
|
|
106
|
+
nonce,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 生成 initCode(用于在 UserOp 中部署账户)
|
|
111
|
+
*/
|
|
112
|
+
generateInitCode(ownerAddress, salt) {
|
|
113
|
+
const useSalt = salt ?? this.salt;
|
|
114
|
+
const factoryIface = new Interface(FACTORY_ABI);
|
|
115
|
+
const calldata = factoryIface.encodeFunctionData('createAccount', [ownerAddress, useSalt]);
|
|
116
|
+
return ethers.concat([this.factoryAddress, calldata]);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 构建 paymasterAndData
|
|
120
|
+
*/
|
|
121
|
+
buildPaymasterAndData() {
|
|
122
|
+
if (!this.paymaster)
|
|
123
|
+
return '0x';
|
|
124
|
+
if (this.paymasterData) {
|
|
125
|
+
return ethers.concat([this.paymaster, this.paymasterData]);
|
|
126
|
+
}
|
|
127
|
+
return this.paymaster;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 构建未签名的 UserOperation(使用 Bundler 估算 Gas)
|
|
131
|
+
*/
|
|
132
|
+
async buildUserOpWithBundlerEstimate(params) {
|
|
133
|
+
const feeData = await this.provider.getFeeData();
|
|
134
|
+
const legacyGasPrice = feeData.gasPrice ?? feeData.maxFeePerGas ?? DEFAULT_GAS_PRICE;
|
|
135
|
+
const paymasterAndData = this.buildPaymasterAndData();
|
|
136
|
+
// 构建初步 UserOp(Gas 字段为 0,等待估算)
|
|
137
|
+
let userOp = {
|
|
138
|
+
sender: params.sender,
|
|
139
|
+
nonce: ethers.toBeHex(params.nonce),
|
|
140
|
+
initCode: params.initCode ?? '0x',
|
|
141
|
+
callData: params.callData,
|
|
142
|
+
callGasLimit: '0x0',
|
|
143
|
+
verificationGasLimit: '0x0',
|
|
144
|
+
preVerificationGas: '0x0',
|
|
145
|
+
maxFeePerGas: ethers.toBeHex(legacyGasPrice * 2n),
|
|
146
|
+
maxPriorityFeePerGas: ethers.toBeHex(legacyGasPrice),
|
|
147
|
+
paymasterAndData,
|
|
148
|
+
signature: '0x',
|
|
149
|
+
};
|
|
150
|
+
// 调用 Bundler 估算 Gas
|
|
151
|
+
const estimate = await this.bundler.estimateUserOperationGas(userOp);
|
|
152
|
+
userOp = {
|
|
153
|
+
...userOp,
|
|
154
|
+
callGasLimit: estimate.callGasLimit,
|
|
155
|
+
verificationGasLimit: estimate.verificationGasLimit,
|
|
156
|
+
preVerificationGas: estimate.preVerificationGas,
|
|
157
|
+
};
|
|
158
|
+
// 使用 Bundler 建议的费率
|
|
159
|
+
if (estimate.maxFeePerGas && estimate.maxPriorityFeePerGas) {
|
|
160
|
+
userOp = {
|
|
161
|
+
...userOp,
|
|
162
|
+
maxFeePerGas: estimate.maxFeePerGas,
|
|
163
|
+
maxPriorityFeePerGas: estimate.maxPriorityFeePerGas,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// 计算 prefund(如果使用 Paymaster 则不需要)
|
|
167
|
+
const gasTotal = BigInt(userOp.callGasLimit) +
|
|
168
|
+
BigInt(userOp.verificationGasLimit) +
|
|
169
|
+
BigInt(userOp.preVerificationGas);
|
|
170
|
+
const prefundWei = paymasterAndData !== '0x' ? 0n : gasTotal * BigInt(userOp.maxFeePerGas);
|
|
171
|
+
return { userOp, prefundWei };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 构建未签名的 UserOperation(本地估算 Gas,不依赖 Bundler)
|
|
175
|
+
*
|
|
176
|
+
* 适用于无法通过 Bundler 估算的场景(如尚未授权的 sell 操作)
|
|
177
|
+
*/
|
|
178
|
+
async buildUserOpWithLocalEstimate(params) {
|
|
179
|
+
const feeData = await this.provider.getFeeData();
|
|
180
|
+
const legacyGasPrice = feeData.gasPrice ?? feeData.maxFeePerGas ?? DEFAULT_GAS_PRICE;
|
|
181
|
+
const paymasterAndData = this.buildPaymasterAndData();
|
|
182
|
+
// 估算 callGasLimit
|
|
183
|
+
let callGasLimit = params.callGasLimit;
|
|
184
|
+
if (!callGasLimit) {
|
|
185
|
+
try {
|
|
186
|
+
// EntryPoint 调用 sender.execute 时 msg.value 恒为 0
|
|
187
|
+
const estimated = await this.provider.estimateGas({
|
|
188
|
+
from: this.entryPointAddress,
|
|
189
|
+
to: params.sender,
|
|
190
|
+
data: params.callData,
|
|
191
|
+
value: 0n,
|
|
192
|
+
});
|
|
193
|
+
callGasLimit = BigInt(Math.ceil(Number(estimated) * this.gasLimitMultiplier));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// 兜底值
|
|
197
|
+
callGasLimit = 500000n;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const hasInitCode = params.initCode && params.initCode !== '0x';
|
|
201
|
+
const verificationGasLimit = hasInitCode ? VERIFICATION_GAS_LIMIT_DEPLOY : VERIFICATION_GAS_LIMIT_NORMAL;
|
|
202
|
+
const preVerificationGas = PRE_VERIFICATION_GAS;
|
|
203
|
+
const userOp = {
|
|
204
|
+
sender: params.sender,
|
|
205
|
+
nonce: ethers.toBeHex(params.nonce),
|
|
206
|
+
initCode: params.initCode ?? '0x',
|
|
207
|
+
callData: params.callData,
|
|
208
|
+
callGasLimit: ethers.toBeHex(callGasLimit),
|
|
209
|
+
verificationGasLimit: ethers.toBeHex(verificationGasLimit),
|
|
210
|
+
preVerificationGas: ethers.toBeHex(preVerificationGas),
|
|
211
|
+
// XLayer 更像 legacy gasPrice 语义
|
|
212
|
+
maxFeePerGas: ethers.toBeHex(legacyGasPrice),
|
|
213
|
+
maxPriorityFeePerGas: ethers.toBeHex(legacyGasPrice),
|
|
214
|
+
paymasterAndData,
|
|
215
|
+
signature: '0x',
|
|
216
|
+
};
|
|
217
|
+
const gasTotal = callGasLimit + verificationGasLimit + preVerificationGas;
|
|
218
|
+
const prefundWei = paymasterAndData !== '0x' ? 0n : gasTotal * legacyGasPrice;
|
|
219
|
+
return { userOp, prefundWei };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 签名 UserOperation
|
|
223
|
+
*/
|
|
224
|
+
async signUserOp(userOp, ownerWallet) {
|
|
225
|
+
const userOpHash = await this.entryPoint.getUserOpHash(userOp);
|
|
226
|
+
const signature = await ownerWallet.signMessage(ethers.getBytes(userOpHash));
|
|
227
|
+
return {
|
|
228
|
+
userOp: { ...userOp, signature },
|
|
229
|
+
userOpHash,
|
|
230
|
+
prefundWei: 0n, // 已在构建时计算
|
|
231
|
+
sender: userOp.sender,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* 构建并签名 UserOperation(完整流程)
|
|
236
|
+
*/
|
|
237
|
+
async buildAndSignUserOp(params) {
|
|
238
|
+
const ownerAddress = params.ownerWallet.address;
|
|
239
|
+
const accountInfo = await this.getAccountInfo(ownerAddress);
|
|
240
|
+
const initCode = accountInfo.deployed ? '0x' : this.generateInitCode(ownerAddress);
|
|
241
|
+
const buildParams = {
|
|
242
|
+
ownerWallet: params.ownerWallet,
|
|
243
|
+
sender: accountInfo.sender,
|
|
244
|
+
callData: params.callData,
|
|
245
|
+
nonce: accountInfo.nonce,
|
|
246
|
+
initCode,
|
|
247
|
+
};
|
|
248
|
+
let userOp;
|
|
249
|
+
let prefundWei;
|
|
250
|
+
if (params.useBundlerEstimate !== false) {
|
|
251
|
+
const result = await this.buildUserOpWithBundlerEstimate(buildParams);
|
|
252
|
+
userOp = result.userOp;
|
|
253
|
+
prefundWei = result.prefundWei;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
const result = await this.buildUserOpWithLocalEstimate({
|
|
257
|
+
...buildParams,
|
|
258
|
+
callGasLimit: params.callGasLimit,
|
|
259
|
+
});
|
|
260
|
+
userOp = result.userOp;
|
|
261
|
+
prefundWei = result.prefundWei;
|
|
262
|
+
}
|
|
263
|
+
const signed = await this.signUserOp(userOp, params.ownerWallet);
|
|
264
|
+
return { ...signed, prefundWei };
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 确保 Sender 有足够的 OKB 余额
|
|
268
|
+
*
|
|
269
|
+
* @param ownerWallet Owner 钱包(用于转账)
|
|
270
|
+
* @param sender Sender 地址
|
|
271
|
+
* @param requiredWei 所需余额(wei)
|
|
272
|
+
* @param tag 日志标签
|
|
273
|
+
* @returns 是否进行了转账
|
|
274
|
+
*/
|
|
275
|
+
async ensureSenderBalance(ownerWallet, sender, requiredWei, tag) {
|
|
276
|
+
// 如果使用 Paymaster,不需要给 sender 转账
|
|
277
|
+
if (this.paymaster) {
|
|
278
|
+
return { funded: false };
|
|
279
|
+
}
|
|
280
|
+
const balance = await this.provider.getBalance(sender);
|
|
281
|
+
if (balance >= requiredWei) {
|
|
282
|
+
return { funded: false };
|
|
283
|
+
}
|
|
284
|
+
const missing = requiredWei - balance;
|
|
285
|
+
console.log(`[${tag ?? 'ensureSenderBalance'}] sender OKB 不足,owner 补充:${ethers.formatEther(missing)} OKB`);
|
|
286
|
+
const tx = await ownerWallet.sendTransaction({ to: sender, value: missing });
|
|
287
|
+
console.log(` - fund tx: ${tx.hash}`);
|
|
288
|
+
await tx.wait();
|
|
289
|
+
return { funded: true, txHash: tx.hash };
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* 批量获取多个 owner 的 AA 账户信息
|
|
293
|
+
*/
|
|
294
|
+
async getMultipleAccountInfo(ownerAddresses) {
|
|
295
|
+
return Promise.all(ownerAddresses.map((addr) => this.getAccountInfo(addr)));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// 工具函数
|
|
300
|
+
// ============================================================================
|
|
301
|
+
/**
|
|
302
|
+
* 编码 SimpleAccount.execute 调用
|
|
303
|
+
*/
|
|
304
|
+
export function encodeExecute(dest, value, data) {
|
|
305
|
+
const iface = new Interface(SIMPLE_ACCOUNT_ABI);
|
|
306
|
+
return iface.encodeFunctionData('execute', [dest, value, data]);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* 编码 SimpleAccount.executeBatch 调用
|
|
310
|
+
*/
|
|
311
|
+
export function encodeExecuteBatch(dests, values, datas) {
|
|
312
|
+
const iface = new Interface(SIMPLE_ACCOUNT_ABI);
|
|
313
|
+
return iface.encodeFunctionData('executeBatch', [dests, values, datas]);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 创建 AA 账户管理器
|
|
317
|
+
*/
|
|
318
|
+
export function createAAAccountManager(config) {
|
|
319
|
+
return new AAAccountManager(config);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* 快速预测 Sender 地址
|
|
323
|
+
*/
|
|
324
|
+
export async function predictSender(ownerAddress, config) {
|
|
325
|
+
const manager = createAAAccountManager(config);
|
|
326
|
+
return manager.predictSenderAddress(ownerAddress);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 从私钥创建连接了 Provider 的 Wallet
|
|
330
|
+
*/
|
|
331
|
+
export function createWallet(privateKey, config) {
|
|
332
|
+
const rpcUrl = config?.rpcUrl ?? DEFAULT_RPC_URL;
|
|
333
|
+
const chainId = config?.chainId ?? XLAYER_CHAIN_ID;
|
|
334
|
+
const provider = new JsonRpcProvider(rpcUrl, { chainId, name: 'xlayer' });
|
|
335
|
+
return new Wallet(privateKey, provider);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 批量生成随机钱包并预测 AA 地址
|
|
339
|
+
*
|
|
340
|
+
* @param params 生成参数
|
|
341
|
+
* @returns 钱包列表和汇总信息
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* const result = await generateAAWallets({ count: 5 });
|
|
346
|
+
*
|
|
347
|
+
* console.log(result.formatted);
|
|
348
|
+
* // 输出:
|
|
349
|
+
* // === XLayer AA Wallets ===
|
|
350
|
+
* // #1
|
|
351
|
+
* // Owner: 0x1234...
|
|
352
|
+
* // Sender: 0xabcd...
|
|
353
|
+
* // Key: 0x...
|
|
354
|
+
* // ...
|
|
355
|
+
*
|
|
356
|
+
* // 直接使用私钥列表
|
|
357
|
+
* await bundleBuy({
|
|
358
|
+
* tokenAddress: '0x...',
|
|
359
|
+
* privateKeys: result.privateKeys,
|
|
360
|
+
* buyAmounts: ['0.01', '0.01', '0.01', '0.01', '0.01'],
|
|
361
|
+
* });
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
export async function generateAAWallets(params) {
|
|
365
|
+
const { count, config } = params;
|
|
366
|
+
const manager = createAAAccountManager(config);
|
|
367
|
+
const wallets = [];
|
|
368
|
+
const owners = [];
|
|
369
|
+
const privateKeys = [];
|
|
370
|
+
const senders = [];
|
|
371
|
+
console.log(`\n生成 ${count} 个 XLayer AA 钱包...\n`);
|
|
372
|
+
for (let i = 0; i < count; i++) {
|
|
373
|
+
// 生成随机钱包
|
|
374
|
+
const wallet = Wallet.createRandom();
|
|
375
|
+
const owner = wallet.address;
|
|
376
|
+
const privateKey = wallet.privateKey;
|
|
377
|
+
// 预测 AA 地址
|
|
378
|
+
const sender = await manager.predictSenderAddress(owner);
|
|
379
|
+
wallets.push({
|
|
380
|
+
index: i + 1,
|
|
381
|
+
owner,
|
|
382
|
+
privateKey,
|
|
383
|
+
sender,
|
|
384
|
+
});
|
|
385
|
+
owners.push(owner);
|
|
386
|
+
privateKeys.push(privateKey);
|
|
387
|
+
senders.push(sender);
|
|
388
|
+
console.log(`#${i + 1}`);
|
|
389
|
+
console.log(` Owner: ${owner}`);
|
|
390
|
+
console.log(` Sender: ${sender}`);
|
|
391
|
+
}
|
|
392
|
+
// 生成格式化输出
|
|
393
|
+
const formatted = formatWalletOutput(wallets);
|
|
394
|
+
return {
|
|
395
|
+
wallets,
|
|
396
|
+
owners,
|
|
397
|
+
privateKeys,
|
|
398
|
+
senders,
|
|
399
|
+
formatted,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 从助记词批量派生钱包并预测 AA 地址
|
|
404
|
+
*
|
|
405
|
+
* @param mnemonic 助记词
|
|
406
|
+
* @param count 派生数量
|
|
407
|
+
* @param startIndex 起始索引(默认 0)
|
|
408
|
+
* @param config 配置
|
|
409
|
+
*/
|
|
410
|
+
export async function generateAAWalletsFromMnemonic(mnemonic, count, startIndex = 0, config) {
|
|
411
|
+
const manager = createAAAccountManager(config);
|
|
412
|
+
const wallets = [];
|
|
413
|
+
const owners = [];
|
|
414
|
+
const privateKeys = [];
|
|
415
|
+
const senders = [];
|
|
416
|
+
console.log(`\n从助记词派生 ${count} 个 XLayer AA 钱包 (起始索引: ${startIndex})...\n`);
|
|
417
|
+
for (let i = 0; i < count; i++) {
|
|
418
|
+
const index = startIndex + i;
|
|
419
|
+
const path = `m/44'/60'/0'/0/${index}`;
|
|
420
|
+
// 从助记词派生
|
|
421
|
+
const hdNode = ethers.HDNodeWallet.fromPhrase(mnemonic, undefined, path);
|
|
422
|
+
const owner = hdNode.address;
|
|
423
|
+
const privateKey = hdNode.privateKey;
|
|
424
|
+
// 预测 AA 地址
|
|
425
|
+
const sender = await manager.predictSenderAddress(owner);
|
|
426
|
+
wallets.push({
|
|
427
|
+
index: i + 1,
|
|
428
|
+
owner,
|
|
429
|
+
privateKey,
|
|
430
|
+
sender,
|
|
431
|
+
mnemonic: i === 0 ? mnemonic : undefined, // 只在第一个记录助记词
|
|
432
|
+
derivationPath: path,
|
|
433
|
+
});
|
|
434
|
+
owners.push(owner);
|
|
435
|
+
privateKeys.push(privateKey);
|
|
436
|
+
senders.push(sender);
|
|
437
|
+
console.log(`#${i + 1} (path: ${path})`);
|
|
438
|
+
console.log(` Owner: ${owner}`);
|
|
439
|
+
console.log(` Sender: ${sender}`);
|
|
440
|
+
}
|
|
441
|
+
const formatted = formatWalletOutput(wallets, mnemonic);
|
|
442
|
+
return {
|
|
443
|
+
wallets,
|
|
444
|
+
owners,
|
|
445
|
+
privateKeys,
|
|
446
|
+
senders,
|
|
447
|
+
formatted,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* 从私钥列表预测 AA 地址
|
|
452
|
+
*
|
|
453
|
+
* @param privateKeys 私钥列表
|
|
454
|
+
* @param config 配置
|
|
455
|
+
*/
|
|
456
|
+
export async function predictSendersFromPrivateKeys(privateKeys, config) {
|
|
457
|
+
const manager = createAAAccountManager(config);
|
|
458
|
+
const wallets = [];
|
|
459
|
+
const owners = [];
|
|
460
|
+
const senders = [];
|
|
461
|
+
console.log(`\n预测 ${privateKeys.length} 个 AA 地址...\n`);
|
|
462
|
+
for (let i = 0; i < privateKeys.length; i++) {
|
|
463
|
+
const wallet = new Wallet(privateKeys[i]);
|
|
464
|
+
const owner = wallet.address;
|
|
465
|
+
const sender = await manager.predictSenderAddress(owner);
|
|
466
|
+
wallets.push({
|
|
467
|
+
index: i + 1,
|
|
468
|
+
owner,
|
|
469
|
+
privateKey: privateKeys[i],
|
|
470
|
+
sender,
|
|
471
|
+
});
|
|
472
|
+
owners.push(owner);
|
|
473
|
+
senders.push(sender);
|
|
474
|
+
console.log(`#${i + 1}`);
|
|
475
|
+
console.log(` Owner: ${owner}`);
|
|
476
|
+
console.log(` Sender: ${sender}`);
|
|
477
|
+
}
|
|
478
|
+
const formatted = formatWalletOutput(wallets);
|
|
479
|
+
return {
|
|
480
|
+
wallets,
|
|
481
|
+
owners,
|
|
482
|
+
privateKeys,
|
|
483
|
+
senders,
|
|
484
|
+
formatted,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* 格式化钱包输出
|
|
489
|
+
*/
|
|
490
|
+
function formatWalletOutput(wallets, mnemonic) {
|
|
491
|
+
let output = '=== XLayer AA Wallets ===\n\n';
|
|
492
|
+
if (mnemonic) {
|
|
493
|
+
output += `Mnemonic: ${mnemonic}\n\n`;
|
|
494
|
+
}
|
|
495
|
+
for (const w of wallets) {
|
|
496
|
+
output += `#${w.index}\n`;
|
|
497
|
+
output += ` Owner: ${w.owner}\n`;
|
|
498
|
+
output += ` Sender: ${w.sender}\n`;
|
|
499
|
+
output += ` Key: ${w.privateKey}\n`;
|
|
500
|
+
if (w.derivationPath) {
|
|
501
|
+
output += ` Path: ${w.derivationPath}\n`;
|
|
502
|
+
}
|
|
503
|
+
output += '\n';
|
|
504
|
+
}
|
|
505
|
+
output += '=== 汇总 ===\n\n';
|
|
506
|
+
output += `Owners (EOA):\n${wallets.map(w => w.owner).join('\n')}\n\n`;
|
|
507
|
+
output += `Senders (AA):\n${wallets.map(w => w.sender).join('\n')}\n\n`;
|
|
508
|
+
output += `Private Keys:\n${wallets.map(w => w.privateKey).join('\n')}\n`;
|
|
509
|
+
return output;
|
|
510
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLayer 捆绑交易 SDK
|
|
3
|
+
*
|
|
4
|
+
* 通过 ERC-4337 handleOps 实现多个地址的原子化交易:
|
|
5
|
+
* - 捆绑买入:多个 sender 同一笔 handleOps 买入
|
|
6
|
+
* - 捆绑卖出:多个 sender 同一笔 handleOps 卖出
|
|
7
|
+
* - 买卖一体化:买入 -> 授权 -> 卖出 -> 归集
|
|
8
|
+
* - OKB 归集:将 sender 的 OKB 转回 owner
|
|
9
|
+
*/
|
|
10
|
+
import type { XLayerConfig, BundleBuyParams, BundleBuyResult, BundleSellParams, BundleSellResult, BundleBuySellParams, BundleBuySellResult } from './types.js';
|
|
11
|
+
import { AAAccountManager } from './aa-account.js';
|
|
12
|
+
import { PortalQuery } from './portal-ops.js';
|
|
13
|
+
/**
|
|
14
|
+
* XLayer 捆绑交易执行器
|
|
15
|
+
*
|
|
16
|
+
* 核心设计原则:
|
|
17
|
+
* 1. 用户只需操控 AA 地址(Sender)
|
|
18
|
+
* 2. 如果指定了 Paymaster,不需要往 AA 转手续费
|
|
19
|
+
* 3. 多个 UserOp 放入同一笔 handleOps 保证原子性
|
|
20
|
+
*/
|
|
21
|
+
export declare class BundleExecutor {
|
|
22
|
+
private aaManager;
|
|
23
|
+
private portalQuery;
|
|
24
|
+
private config;
|
|
25
|
+
private portalAddress;
|
|
26
|
+
constructor(config?: XLayerConfig);
|
|
27
|
+
/**
|
|
28
|
+
* 获取 AA 管理器
|
|
29
|
+
*/
|
|
30
|
+
getAAManager(): AAAccountManager;
|
|
31
|
+
/**
|
|
32
|
+
* 获取 Portal 查询器
|
|
33
|
+
*/
|
|
34
|
+
getPortalQuery(): PortalQuery;
|
|
35
|
+
/**
|
|
36
|
+
* 执行 handleOps 并解析结果
|
|
37
|
+
*/
|
|
38
|
+
private runHandleOps;
|
|
39
|
+
/**
|
|
40
|
+
* 构建买入 UserOp
|
|
41
|
+
*/
|
|
42
|
+
private buildBuyUserOp;
|
|
43
|
+
/**
|
|
44
|
+
* 构建授权 UserOp
|
|
45
|
+
*/
|
|
46
|
+
private buildApproveUserOp;
|
|
47
|
+
/**
|
|
48
|
+
* 构建卖出 UserOp
|
|
49
|
+
*/
|
|
50
|
+
private buildSellUserOp;
|
|
51
|
+
/**
|
|
52
|
+
* 构建归集 UserOp(将 OKB 从 sender 转回 owner)
|
|
53
|
+
*/
|
|
54
|
+
private buildWithdrawUserOp;
|
|
55
|
+
/**
|
|
56
|
+
* 构建代币转账 UserOp(将代币从 sender 转回 owner)
|
|
57
|
+
*/
|
|
58
|
+
private buildTransferTokenUserOp;
|
|
59
|
+
/**
|
|
60
|
+
* 捆绑买入
|
|
61
|
+
*
|
|
62
|
+
* 多个地址同一笔 handleOps 买入代币
|
|
63
|
+
*/
|
|
64
|
+
bundleBuy(params: BundleBuyParams): Promise<BundleBuyResult>;
|
|
65
|
+
/**
|
|
66
|
+
* 捆绑卖出
|
|
67
|
+
*
|
|
68
|
+
* 多个地址同一笔 handleOps 卖出代币
|
|
69
|
+
*/
|
|
70
|
+
bundleSell(params: BundleSellParams): Promise<BundleSellResult>;
|
|
71
|
+
/**
|
|
72
|
+
* 捆绑买卖(先买后卖)
|
|
73
|
+
*
|
|
74
|
+
* 完整流程:买入 -> 授权 -> 卖出 -> 归集
|
|
75
|
+
*/
|
|
76
|
+
bundleBuySell(params: BundleBuySellParams): Promise<BundleBuySellResult>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 创建捆绑交易执行器
|
|
80
|
+
*/
|
|
81
|
+
export declare function createBundleExecutor(config?: XLayerConfig): BundleExecutor;
|
|
82
|
+
/**
|
|
83
|
+
* 快速捆绑买入
|
|
84
|
+
*/
|
|
85
|
+
export declare function bundleBuy(params: BundleBuyParams): Promise<BundleBuyResult>;
|
|
86
|
+
/**
|
|
87
|
+
* 快速捆绑卖出
|
|
88
|
+
*/
|
|
89
|
+
export declare function bundleSell(params: BundleSellParams): Promise<BundleSellResult>;
|
|
90
|
+
/**
|
|
91
|
+
* 快速捆绑买卖
|
|
92
|
+
*/
|
|
93
|
+
export declare function bundleBuySell(params: BundleBuySellParams): Promise<BundleBuySellResult>;
|