four-flap-meme-sdk 1.6.34 → 1.6.35
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/flap/portal-bundle-merkle/encryption.d.ts +16 -0
- package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
- package/dist/xlayer/aa-account.d.ts +0 -20
- package/dist/xlayer/aa-account.js +1 -57
- package/dist/xlayer/bundle.js +2 -2
- package/dist/xlayer/bundler.d.ts +0 -21
- package/dist/xlayer/bundler.js +0 -96
- package/dist/xlayer/types.d.ts +0 -6
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECDH + AES-GCM 加密工具(浏览器兼容)
|
|
3
|
+
* 用于将签名交易用服务器公钥加密
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 用服务器公钥加密签名交易(ECDH + AES-GCM)
|
|
7
|
+
*
|
|
8
|
+
* @param signedTransactions 签名后的交易数组
|
|
9
|
+
* @param publicKeyBase64 服务器提供的公钥(Base64 格式)
|
|
10
|
+
* @returns JSON 字符串 {e: 临时公钥, i: IV, d: 密文}
|
|
11
|
+
*/
|
|
12
|
+
export declare function encryptWithPublicKey(signedTransactions: string[], publicKeyBase64: string): Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* 验证公钥格式(Base64)
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePublicKey(publicKeyBase64: string): boolean;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECDH + AES-GCM 加密工具(浏览器兼容)
|
|
3
|
+
* 用于将签名交易用服务器公钥加密
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 获取全局 crypto 对象(最简单直接的方式)
|
|
7
|
+
*/
|
|
8
|
+
function getCryptoAPI() {
|
|
9
|
+
// 尝试所有可能的全局对象,优先浏览器环境
|
|
10
|
+
const cryptoObj = (typeof window !== 'undefined' && window.crypto) ||
|
|
11
|
+
(typeof self !== 'undefined' && self.crypto) ||
|
|
12
|
+
(typeof global !== 'undefined' && global.crypto) ||
|
|
13
|
+
(typeof globalThis !== 'undefined' && globalThis.crypto);
|
|
14
|
+
if (!cryptoObj) {
|
|
15
|
+
const env = typeof window !== 'undefined' ? 'Browser' : 'Node.js';
|
|
16
|
+
const protocol = typeof location !== 'undefined' ? location.protocol : 'unknown';
|
|
17
|
+
throw new Error(`❌ Crypto API 不可用。环境: ${env}, 协议: ${protocol}. ` +
|
|
18
|
+
'请确保在 HTTPS 或 localhost 下运行');
|
|
19
|
+
}
|
|
20
|
+
return cryptoObj;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 获取 SubtleCrypto(用于加密操作)
|
|
24
|
+
*/
|
|
25
|
+
function getSubtleCrypto() {
|
|
26
|
+
const crypto = getCryptoAPI();
|
|
27
|
+
if (!crypto.subtle) {
|
|
28
|
+
const protocol = typeof location !== 'undefined' ? location.protocol : 'unknown';
|
|
29
|
+
const hostname = typeof location !== 'undefined' ? location.hostname : 'unknown';
|
|
30
|
+
throw new Error(`❌ SubtleCrypto API 不可用。协议: ${protocol}, 主机: ${hostname}. ` +
|
|
31
|
+
'请确保:1) 使用 HTTPS (或 localhost);2) 浏览器支持 Web Crypto API;' +
|
|
32
|
+
'3) 不在无痕/隐私浏览模式下');
|
|
33
|
+
}
|
|
34
|
+
return crypto.subtle;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Base64 转 ArrayBuffer(优先使用浏览器 API)
|
|
38
|
+
*/
|
|
39
|
+
function base64ToArrayBuffer(base64) {
|
|
40
|
+
// 浏览器环境(优先)
|
|
41
|
+
if (typeof atob !== 'undefined') {
|
|
42
|
+
const binaryString = atob(base64);
|
|
43
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
44
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
45
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
46
|
+
}
|
|
47
|
+
return bytes.buffer;
|
|
48
|
+
}
|
|
49
|
+
// Node.js 环境(fallback)
|
|
50
|
+
if (typeof Buffer !== 'undefined') {
|
|
51
|
+
return Buffer.from(base64, 'base64').buffer;
|
|
52
|
+
}
|
|
53
|
+
throw new Error('❌ Base64 解码不可用');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ArrayBuffer 转 Base64(优先使用浏览器 API)
|
|
57
|
+
*/
|
|
58
|
+
function arrayBufferToBase64(buffer) {
|
|
59
|
+
// 浏览器环境(优先)
|
|
60
|
+
if (typeof btoa !== 'undefined') {
|
|
61
|
+
const bytes = new Uint8Array(buffer);
|
|
62
|
+
let binary = '';
|
|
63
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
64
|
+
binary += String.fromCharCode(bytes[i]);
|
|
65
|
+
}
|
|
66
|
+
return btoa(binary);
|
|
67
|
+
}
|
|
68
|
+
// Node.js 环境(fallback)
|
|
69
|
+
if (typeof Buffer !== 'undefined') {
|
|
70
|
+
return Buffer.from(buffer).toString('base64');
|
|
71
|
+
}
|
|
72
|
+
throw new Error('❌ Base64 编码不可用');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 生成随机 Hex 字符串
|
|
76
|
+
*/
|
|
77
|
+
function randomHex(length) {
|
|
78
|
+
const crypto = getCryptoAPI();
|
|
79
|
+
const array = new Uint8Array(length);
|
|
80
|
+
crypto.getRandomValues(array);
|
|
81
|
+
return Array.from(array)
|
|
82
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
83
|
+
.join('');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 用服务器公钥加密签名交易(ECDH + AES-GCM)
|
|
87
|
+
*
|
|
88
|
+
* @param signedTransactions 签名后的交易数组
|
|
89
|
+
* @param publicKeyBase64 服务器提供的公钥(Base64 格式)
|
|
90
|
+
* @returns JSON 字符串 {e: 临时公钥, i: IV, d: 密文}
|
|
91
|
+
*/
|
|
92
|
+
export async function encryptWithPublicKey(signedTransactions, publicKeyBase64) {
|
|
93
|
+
try {
|
|
94
|
+
// 0. 获取 SubtleCrypto 和 Crypto API
|
|
95
|
+
const subtle = getSubtleCrypto();
|
|
96
|
+
const crypto = getCryptoAPI();
|
|
97
|
+
// 1. 准备数据
|
|
98
|
+
const payload = {
|
|
99
|
+
signedTransactions,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
nonce: randomHex(8)
|
|
102
|
+
};
|
|
103
|
+
const plaintext = JSON.stringify(payload);
|
|
104
|
+
// 2. 生成临时 ECDH 密钥对
|
|
105
|
+
const ephemeralKeyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey']);
|
|
106
|
+
// 3. 导入服务器公钥
|
|
107
|
+
const publicKeyBuffer = base64ToArrayBuffer(publicKeyBase64);
|
|
108
|
+
const publicKey = await subtle.importKey('raw', publicKeyBuffer, { name: 'ECDH', namedCurve: 'P-256' }, false, []);
|
|
109
|
+
// 4. 派生共享密钥(AES-256)
|
|
110
|
+
const sharedKey = await subtle.deriveKey({ name: 'ECDH', public: publicKey }, ephemeralKeyPair.privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
|
|
111
|
+
// 5. AES-GCM 加密
|
|
112
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
113
|
+
const encrypted = await subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, new TextEncoder().encode(plaintext));
|
|
114
|
+
// 6. 导出临时公钥
|
|
115
|
+
const ephemeralPublicKeyRaw = await subtle.exportKey('raw', ephemeralKeyPair.publicKey);
|
|
116
|
+
// 7. 返回加密包(JSON 格式)
|
|
117
|
+
return JSON.stringify({
|
|
118
|
+
e: arrayBufferToBase64(ephemeralPublicKeyRaw), // 临时公钥
|
|
119
|
+
i: arrayBufferToBase64(iv.buffer), // IV
|
|
120
|
+
d: arrayBufferToBase64(encrypted) // 密文
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw new Error(`加密失败: ${error?.message || String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 验证公钥格式(Base64)
|
|
129
|
+
*/
|
|
130
|
+
export function validatePublicKey(publicKeyBase64) {
|
|
131
|
+
try {
|
|
132
|
+
if (!publicKeyBase64)
|
|
133
|
+
return false;
|
|
134
|
+
// Base64 字符集验证
|
|
135
|
+
if (!/^[A-Za-z0-9+/=]+$/.test(publicKeyBase64))
|
|
136
|
+
return false;
|
|
137
|
+
// ECDH P-256 公钥固定长度 65 字节(未压缩)
|
|
138
|
+
// Base64 编码后约 88 字符
|
|
139
|
+
if (publicKeyBase64.length < 80 || publicKeyBase64.length > 100)
|
|
140
|
+
return false;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -31,7 +31,6 @@ export declare class AAAccountManager {
|
|
|
31
31
|
private bundler;
|
|
32
32
|
private paymaster?;
|
|
33
33
|
private paymasterData?;
|
|
34
|
-
private useParticlePaymaster;
|
|
35
34
|
private gasLimitMultiplier;
|
|
36
35
|
private senderCache;
|
|
37
36
|
private deployedSenderSet;
|
|
@@ -64,27 +63,8 @@ export declare class AAAccountManager {
|
|
|
64
63
|
* 而临时生成的 hop 钱包余额为 0,会导致 AA21 验证失败。
|
|
65
64
|
*
|
|
66
65
|
* 如果配置了 Paymaster,prefund = 0,hop 钱包不需要余额即可通过验证。
|
|
67
|
-
*
|
|
68
|
-
* 支持两种模式:
|
|
69
|
-
* 1. 静态配置:config.paymaster 指定 Paymaster 合约地址
|
|
70
|
-
* 2. Particle Paymaster:config.useParticlePaymaster = true,自动请求 pm_sponsorUserOperation
|
|
71
66
|
*/
|
|
72
67
|
hasPaymaster(): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* 是否使用 Particle Paymaster(动态请求 paymasterAndData)
|
|
75
|
-
*/
|
|
76
|
-
usesParticlePaymaster(): boolean;
|
|
77
|
-
/**
|
|
78
|
-
* 请求 Particle Paymaster 赞助 UserOp
|
|
79
|
-
*
|
|
80
|
-
* @param userOp 未签名的 UserOperation
|
|
81
|
-
* @returns paymasterAndData,如果失败则返回 '0x'
|
|
82
|
-
*/
|
|
83
|
-
requestParticlePaymasterSponsor(userOp: UserOperation): Promise<string>;
|
|
84
|
-
/**
|
|
85
|
-
* 批量请求 Particle Paymaster 赞助
|
|
86
|
-
*/
|
|
87
|
-
requestParticlePaymasterSponsorBatch(userOps: UserOperation[]): Promise<string[]>;
|
|
88
68
|
/**
|
|
89
69
|
* 获取当前 Fee Data
|
|
90
70
|
*/
|
|
@@ -42,7 +42,6 @@ export class AAAccountManager {
|
|
|
42
42
|
this.salt = config.salt ?? DEFAULT_SALT;
|
|
43
43
|
this.paymaster = config.paymaster;
|
|
44
44
|
this.paymasterData = config.paymasterData;
|
|
45
|
-
this.useParticlePaymaster = config.useParticlePaymaster ?? false;
|
|
46
45
|
this.gasLimitMultiplier = config.gasLimitMultiplier ?? GAS_LIMIT_MULTIPLIER;
|
|
47
46
|
// 默认 fixed:最大化降低 RPC / bundler 请求;如需更稳可显式传 bundlerEstimate
|
|
48
47
|
this.defaultGasPolicy = config.gasPolicy ?? 'fixed';
|
|
@@ -101,40 +100,9 @@ export class AAAccountManager {
|
|
|
101
100
|
* 而临时生成的 hop 钱包余额为 0,会导致 AA21 验证失败。
|
|
102
101
|
*
|
|
103
102
|
* 如果配置了 Paymaster,prefund = 0,hop 钱包不需要余额即可通过验证。
|
|
104
|
-
*
|
|
105
|
-
* 支持两种模式:
|
|
106
|
-
* 1. 静态配置:config.paymaster 指定 Paymaster 合约地址
|
|
107
|
-
* 2. Particle Paymaster:config.useParticlePaymaster = true,自动请求 pm_sponsorUserOperation
|
|
108
103
|
*/
|
|
109
104
|
hasPaymaster() {
|
|
110
|
-
return !!this.paymaster
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* 是否使用 Particle Paymaster(动态请求 paymasterAndData)
|
|
114
|
-
*/
|
|
115
|
-
usesParticlePaymaster() {
|
|
116
|
-
return this.useParticlePaymaster;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* 请求 Particle Paymaster 赞助 UserOp
|
|
120
|
-
*
|
|
121
|
-
* @param userOp 未签名的 UserOperation
|
|
122
|
-
* @returns paymasterAndData,如果失败则返回 '0x'
|
|
123
|
-
*/
|
|
124
|
-
async requestParticlePaymasterSponsor(userOp) {
|
|
125
|
-
if (!this.useParticlePaymaster)
|
|
126
|
-
return '0x';
|
|
127
|
-
const result = await this.bundler.sponsorUserOperation(userOp);
|
|
128
|
-
return result ?? '0x';
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* 批量请求 Particle Paymaster 赞助
|
|
132
|
-
*/
|
|
133
|
-
async requestParticlePaymasterSponsorBatch(userOps) {
|
|
134
|
-
if (!this.useParticlePaymaster)
|
|
135
|
-
return userOps.map(() => '0x');
|
|
136
|
-
const results = await this.bundler.sponsorUserOperationBatch(userOps);
|
|
137
|
-
return results.map(r => r ?? '0x');
|
|
105
|
+
return !!this.paymaster;
|
|
138
106
|
}
|
|
139
107
|
/**
|
|
140
108
|
* 获取当前 Fee Data
|
|
@@ -588,18 +556,6 @@ export class AAAccountManager {
|
|
|
588
556
|
userOp = result.userOp;
|
|
589
557
|
prefundWei = result.prefundWei;
|
|
590
558
|
}
|
|
591
|
-
// ✅ Particle Paymaster:请求赞助并填入 paymasterAndData
|
|
592
|
-
if (this.useParticlePaymaster && userOp.paymasterAndData === '0x') {
|
|
593
|
-
const sponsorData = await this.requestParticlePaymasterSponsor(userOp);
|
|
594
|
-
if (sponsorData && sponsorData !== '0x') {
|
|
595
|
-
userOp = { ...userOp, paymasterAndData: sponsorData };
|
|
596
|
-
prefundWei = 0n; // Paymaster 代付,无需 prefund
|
|
597
|
-
console.log('[AAAccountManager] ✅ Particle Paymaster 赞助成功');
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
console.warn('[AAAccountManager] ⚠️ Particle Paymaster 赞助失败,回退为自付模式');
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
559
|
const signed = await this.signUserOp(userOp, params.ownerWallet);
|
|
604
560
|
return { ...signed, prefundWei };
|
|
605
561
|
}
|
|
@@ -649,14 +605,6 @@ export class AAAccountManager {
|
|
|
649
605
|
userOp = result.userOp;
|
|
650
606
|
prefundWei = result.prefundWei;
|
|
651
607
|
}
|
|
652
|
-
// ✅ Particle Paymaster:请求赞助并填入 paymasterAndData
|
|
653
|
-
if (this.useParticlePaymaster && userOp.paymasterAndData === '0x') {
|
|
654
|
-
const sponsorData = await this.requestParticlePaymasterSponsor(userOp);
|
|
655
|
-
if (sponsorData && sponsorData !== '0x') {
|
|
656
|
-
userOp = { ...userOp, paymasterAndData: sponsorData };
|
|
657
|
-
prefundWei = 0n; // Paymaster 代付,无需 prefund
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
608
|
// 只有非 signOnly 模式才执行 ensureSenderBalance
|
|
661
609
|
if (!signOnly) {
|
|
662
610
|
await this.ensureSenderBalance(ownerWallet, sender, prefundWei, 'buildUserOpWithState');
|
|
@@ -674,10 +622,6 @@ export class AAAccountManager {
|
|
|
674
622
|
* @returns 是否进行了转账
|
|
675
623
|
*/
|
|
676
624
|
async ensureSenderBalance(ownerWallet, sender, requiredWei, tag) {
|
|
677
|
-
// 如果有 Paymaster(静态配置或 Particle),无需检查余额
|
|
678
|
-
if (this.hasPaymaster()) {
|
|
679
|
-
return { funded: false };
|
|
680
|
-
}
|
|
681
625
|
if (this.paymaster) {
|
|
682
626
|
const code = await this.provider.getCode(this.paymaster);
|
|
683
627
|
if (code && code !== '0x') {
|
package/dist/xlayer/bundle.js
CHANGED
|
@@ -1534,8 +1534,8 @@ export class BundleExecutor {
|
|
|
1534
1534
|
name: tokenInfo.name,
|
|
1535
1535
|
symbol: tokenInfo.symbol,
|
|
1536
1536
|
meta: tokenInfo.meta,
|
|
1537
|
-
dexThresh: params.dexThresh ??
|
|
1538
|
-
salt: params.salt ?? ethers.
|
|
1537
|
+
dexThresh: (params.dexThresh ?? 1) & 0xff, // ✅ 与 BSC 保持一致,默认 1
|
|
1538
|
+
salt: params.salt ?? ethers.ZeroHash, // ✅ 修复:与 bundleCreateBuySign 保持一致,使用 ZeroHash 确保代币地址预计算一致
|
|
1539
1539
|
taxRate: params.taxRate ?? 0,
|
|
1540
1540
|
migratorType: params.migratorType ?? 0,
|
|
1541
1541
|
quoteToken: params.quoteToken ?? ZERO_ADDRESS,
|
package/dist/xlayer/bundler.d.ts
CHANGED
|
@@ -116,27 +116,6 @@ export declare class BundlerClient {
|
|
|
116
116
|
* 获取当前链 ID
|
|
117
117
|
*/
|
|
118
118
|
getChainId(): number;
|
|
119
|
-
/**
|
|
120
|
-
* 请求 Particle Paymaster 赞助 UserOperation
|
|
121
|
-
*
|
|
122
|
-
* Particle 的 Paymaster 服务会返回 paymasterAndData,
|
|
123
|
-
* 将其填入 UserOp 后,用户无需支付 prefund(gas 由 Paymaster 代付)
|
|
124
|
-
*
|
|
125
|
-
* @param userOp 未签名的 UserOperation(paymasterAndData 为空)
|
|
126
|
-
* @returns paymasterAndData 字符串,如果不支持则返回 null
|
|
127
|
-
*/
|
|
128
|
-
sponsorUserOperation(userOp: UserOperation): Promise<string | null>;
|
|
129
|
-
/**
|
|
130
|
-
* 批量请求 Paymaster 赞助
|
|
131
|
-
*
|
|
132
|
-
* @param userOps 未签名的 UserOperations
|
|
133
|
-
* @returns paymasterAndData 数组,不支持的返回 null
|
|
134
|
-
*/
|
|
135
|
-
sponsorUserOperationBatch(userOps: UserOperation[]): Promise<(string | null)[]>;
|
|
136
|
-
/**
|
|
137
|
-
* 检查 Paymaster 是否可用
|
|
138
|
-
*/
|
|
139
|
-
isPaymasterAvailable(): Promise<boolean>;
|
|
140
119
|
}
|
|
141
120
|
/**
|
|
142
121
|
* 创建默认的 Bundler 客户端
|
package/dist/xlayer/bundler.js
CHANGED
|
@@ -245,102 +245,6 @@ export class BundlerClient {
|
|
|
245
245
|
getChainId() {
|
|
246
246
|
return this.chainId;
|
|
247
247
|
}
|
|
248
|
-
// ============================================================================
|
|
249
|
-
// Paymaster API(Particle Network 特有)
|
|
250
|
-
// ============================================================================
|
|
251
|
-
/**
|
|
252
|
-
* 请求 Particle Paymaster 赞助 UserOperation
|
|
253
|
-
*
|
|
254
|
-
* Particle 的 Paymaster 服务会返回 paymasterAndData,
|
|
255
|
-
* 将其填入 UserOp 后,用户无需支付 prefund(gas 由 Paymaster 代付)
|
|
256
|
-
*
|
|
257
|
-
* @param userOp 未签名的 UserOperation(paymasterAndData 为空)
|
|
258
|
-
* @returns paymasterAndData 字符串,如果不支持则返回 null
|
|
259
|
-
*/
|
|
260
|
-
async sponsorUserOperation(userOp) {
|
|
261
|
-
try {
|
|
262
|
-
// Particle 使用 pm_sponsorUserOperation 方法
|
|
263
|
-
const result = await this.rpc('pm_sponsorUserOperation', [userOp, this.entryPoint]);
|
|
264
|
-
if (typeof result === 'string') {
|
|
265
|
-
return result;
|
|
266
|
-
}
|
|
267
|
-
if (result && typeof result === 'object' && result.paymasterAndData) {
|
|
268
|
-
return result.paymasterAndData;
|
|
269
|
-
}
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
catch (err) {
|
|
273
|
-
// Paymaster 不可用或不愿意赞助,返回 null
|
|
274
|
-
console.warn('[Bundler] Paymaster 请求失败(可能不支持或拒绝赞助):', err);
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* 批量请求 Paymaster 赞助
|
|
280
|
-
*
|
|
281
|
-
* @param userOps 未签名的 UserOperations
|
|
282
|
-
* @returns paymasterAndData 数组,不支持的返回 null
|
|
283
|
-
*/
|
|
284
|
-
async sponsorUserOperationBatch(userOps) {
|
|
285
|
-
if (userOps.length === 0)
|
|
286
|
-
return [];
|
|
287
|
-
try {
|
|
288
|
-
const results = await this.rpcBatch(userOps.map(op => ({
|
|
289
|
-
method: 'pm_sponsorUserOperation',
|
|
290
|
-
params: [op, this.entryPoint],
|
|
291
|
-
})));
|
|
292
|
-
return results.map(r => {
|
|
293
|
-
if (typeof r === 'string')
|
|
294
|
-
return r;
|
|
295
|
-
if (r && typeof r === 'object' && r.paymasterAndData) {
|
|
296
|
-
return r.paymasterAndData;
|
|
297
|
-
}
|
|
298
|
-
return null;
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
catch (err) {
|
|
302
|
-
console.warn('[Bundler] 批量 Paymaster 请求失败,尝试单个请求:', err);
|
|
303
|
-
// 降级为单个请求
|
|
304
|
-
const out = [];
|
|
305
|
-
for (const op of userOps) {
|
|
306
|
-
out.push(await this.sponsorUserOperation(op));
|
|
307
|
-
}
|
|
308
|
-
return out;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* 检查 Paymaster 是否可用
|
|
313
|
-
*/
|
|
314
|
-
async isPaymasterAvailable() {
|
|
315
|
-
try {
|
|
316
|
-
// 尝试调用一个空的 sponsor 请求,看是否返回错误
|
|
317
|
-
// 如果返回 "method not found" 则不支持
|
|
318
|
-
const testOp = {
|
|
319
|
-
sender: '0x0000000000000000000000000000000000000001',
|
|
320
|
-
nonce: '0x0',
|
|
321
|
-
initCode: '0x',
|
|
322
|
-
callData: '0x',
|
|
323
|
-
callGasLimit: '0x5208',
|
|
324
|
-
verificationGasLimit: '0x5208',
|
|
325
|
-
preVerificationGas: '0x5208',
|
|
326
|
-
maxFeePerGas: '0x1',
|
|
327
|
-
maxPriorityFeePerGas: '0x1',
|
|
328
|
-
paymasterAndData: '0x',
|
|
329
|
-
signature: '0x',
|
|
330
|
-
};
|
|
331
|
-
await this.rpc('pm_sponsorUserOperation', [testOp, this.entryPoint]);
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
catch (err) {
|
|
335
|
-
const msg = String(err?.message || err || '').toLowerCase();
|
|
336
|
-
// 如果是 "method not found" 则不支持
|
|
337
|
-
if (msg.includes('method not found') || msg.includes('not supported') || msg.includes('unknown method')) {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
// 其他错误(如拒绝赞助)说明方法存在,只是不愿意赞助这个特定请求
|
|
341
|
-
return true;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
248
|
}
|
|
345
249
|
// ============================================================================
|
|
346
250
|
// 工具函数
|
package/dist/xlayer/types.d.ts
CHANGED
|
@@ -72,12 +72,6 @@ export interface XLayerConfig {
|
|
|
72
72
|
paymaster?: string;
|
|
73
73
|
/** Paymaster Data(与 paymaster 一起使用) */
|
|
74
74
|
paymasterData?: string;
|
|
75
|
-
/**
|
|
76
|
-
* 使用 Particle Network 的 Paymaster 服务(自动请求赞助)
|
|
77
|
-
* 如果启用,SDK 会调用 pm_sponsorUserOperation 获取 paymasterAndData
|
|
78
|
-
* 这样 hop 钱包无需 prefund,可以支持多跳
|
|
79
|
-
*/
|
|
80
|
-
useParticlePaymaster?: boolean;
|
|
81
75
|
/** 默认超时时间(毫秒) */
|
|
82
76
|
timeoutMs?: number;
|
|
83
77
|
/** Gas 估算安全余量倍数 */
|