four-flap-meme-sdk 1.5.17 → 1.5.19

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.
@@ -16,7 +16,9 @@ import { type GeneratedWallet } from './wallet.js';
16
16
  export declare class NonceManager {
17
17
  private provider;
18
18
  private tempNonceCache;
19
+ private chainIdPromise?;
19
20
  constructor(provider: JsonRpcProvider);
21
+ private getChainId;
20
22
  /**
21
23
  * 获取下一个可用的 nonce
22
24
  * @param wallet 钱包对象或地址
@@ -4,6 +4,47 @@
4
4
  */
5
5
  import { ethers, Wallet } from 'ethers';
6
6
  import { generateWallets } from './wallet.js';
7
+ const GLOBAL_NONCE_TTL_MS = 60 * 1000; // 1 分钟:只用于短时间“pending 不同步”兜底,避免长时间缓存造成 nonce 过高
8
+ const globalNonceCursorByChain = new Map();
9
+ function isFlakyNonceChain(chainId) {
10
+ // ✅ XLayer: 196(OKX X Layer)
11
+ return Number(chainId) === 196;
12
+ }
13
+ function getCursorMap(chainId) {
14
+ let m = globalNonceCursorByChain.get(chainId);
15
+ if (!m) {
16
+ m = new Map();
17
+ globalNonceCursorByChain.set(chainId, m);
18
+ }
19
+ return m;
20
+ }
21
+ function getGlobalNextNonce(chainId, addrLower) {
22
+ const m = globalNonceCursorByChain.get(chainId);
23
+ if (!m)
24
+ return undefined;
25
+ const e = m.get(addrLower);
26
+ if (!e)
27
+ return undefined;
28
+ if (Date.now() - e.updatedAt > GLOBAL_NONCE_TTL_MS) {
29
+ m.delete(addrLower);
30
+ return undefined;
31
+ }
32
+ return e.nextNonce;
33
+ }
34
+ function setGlobalNextNonce(chainId, addrLower, nextNonce) {
35
+ const m = getCursorMap(chainId);
36
+ const cur = m.get(addrLower);
37
+ const next = Number(nextNonce);
38
+ if (!Number.isFinite(next) || next < 0)
39
+ return;
40
+ if (!cur || next > cur.nextNonce || (Date.now() - cur.updatedAt > GLOBAL_NONCE_TTL_MS)) {
41
+ m.set(addrLower, { nextNonce: next, updatedAt: Date.now() });
42
+ }
43
+ else {
44
+ // 刷新时间戳,避免短时间内频繁淘汰
45
+ cur.updatedAt = Date.now();
46
+ }
47
+ }
7
48
  /**
8
49
  * Nonce 管理器
9
50
  * 用于在 bundle 交易中管理多个钱包的 nonce
@@ -19,6 +60,12 @@ export class NonceManager {
19
60
  this.tempNonceCache = new Map();
20
61
  this.provider = provider;
21
62
  }
63
+ async getChainId() {
64
+ if (!this.chainIdPromise) {
65
+ this.chainIdPromise = this.provider.getNetwork().then(n => Number(n.chainId)).catch(() => 0);
66
+ }
67
+ return this.chainIdPromise;
68
+ }
22
69
  /**
23
70
  * 获取下一个可用的 nonce
24
71
  * @param wallet 钱包对象或地址
@@ -27,18 +74,34 @@ export class NonceManager {
27
74
  async getNextNonce(wallet) {
28
75
  const address = typeof wallet === 'string' ? wallet : wallet.address;
29
76
  const key = address.toLowerCase();
77
+ const chainId = await this.getChainId();
78
+ const flaky = isFlakyNonceChain(chainId);
30
79
  // 检查临时缓存
31
80
  if (this.tempNonceCache.has(key)) {
32
81
  const cachedNonce = this.tempNonceCache.get(key);
33
82
  this.tempNonceCache.set(key, cachedNonce + 1);
83
+ if (flaky)
84
+ setGlobalNextNonce(chainId, key, cachedNonce + 1);
34
85
  return cachedNonce;
35
86
  }
36
87
  // ✅ 使用 'pending' 获取 nonce(包含待处理交易)
37
88
  // 由于前端已移除 nonce 缓存,SDK 每次都从链上获取最新状态
38
89
  const onchainNonce = await this.provider.getTransactionCount(address, 'pending');
90
+ // ✅ XLayer:如果链上 pending nonce 暂时没跟上,短时间内允许使用“全局游标”兜底(只允许小幅领先)
91
+ let effectiveNonce = onchainNonce;
92
+ if (flaky) {
93
+ const globalNext = getGlobalNextNonce(chainId, key);
94
+ if (globalNext !== undefined) {
95
+ const delta = globalNext - onchainNonce;
96
+ if (delta > 0 && delta <= 10) {
97
+ effectiveNonce = globalNext;
98
+ }
99
+ }
100
+ setGlobalNextNonce(chainId, key, effectiveNonce + 1);
101
+ }
39
102
  // 缓存下一个值(仅在当前批次内有效)
40
- this.tempNonceCache.set(key, onchainNonce + 1);
41
- return onchainNonce;
103
+ this.tempNonceCache.set(key, effectiveNonce + 1);
104
+ return effectiveNonce;
42
105
  }
43
106
  /**
44
107
  * 批量获取连续 nonce(推荐用于同一地址的批量交易)
@@ -49,6 +112,8 @@ export class NonceManager {
49
112
  async getNextNonceBatch(wallet, count) {
50
113
  const address = typeof wallet === 'string' ? wallet : wallet.address;
51
114
  const key = address.toLowerCase();
115
+ const chainId = await this.getChainId();
116
+ const flaky = isFlakyNonceChain(chainId);
52
117
  let startNonce;
53
118
  if (this.tempNonceCache.has(key)) {
54
119
  startNonce = this.tempNonceCache.get(key);
@@ -56,8 +121,18 @@ export class NonceManager {
56
121
  else {
57
122
  startNonce = await this.provider.getTransactionCount(address, 'pending');
58
123
  }
124
+ if (flaky) {
125
+ const globalNext = getGlobalNextNonce(chainId, key);
126
+ if (globalNext !== undefined) {
127
+ const delta = globalNext - startNonce;
128
+ if (delta > 0 && delta <= 10)
129
+ startNonce = globalNext;
130
+ }
131
+ }
59
132
  // 更新缓存
60
133
  this.tempNonceCache.set(key, startNonce + count);
134
+ if (flaky)
135
+ setGlobalNextNonce(chainId, key, startNonce + count);
61
136
  // 返回连续 nonce 数组
62
137
  return Array.from({ length: count }, (_, i) => startNonce + i);
63
138
  }
@@ -82,6 +157,8 @@ export class NonceManager {
82
157
  async getNextNoncesForWallets(wallets) {
83
158
  const addresses = wallets.map(w => typeof w === 'string' ? w : w.address);
84
159
  const keys = addresses.map(a => a.toLowerCase());
160
+ const chainId = await this.getChainId();
161
+ const flaky = isFlakyNonceChain(chainId);
85
162
  // 分离:已缓存的 vs 需要查询的
86
163
  const needQuery = [];
87
164
  const results = new Array(wallets.length);
@@ -91,6 +168,8 @@ export class NonceManager {
91
168
  const cachedNonce = this.tempNonceCache.get(key);
92
169
  results[i] = cachedNonce;
93
170
  this.tempNonceCache.set(key, cachedNonce + 1);
171
+ if (flaky)
172
+ setGlobalNextNonce(chainId, key, cachedNonce + 1);
94
173
  }
95
174
  else {
96
175
  needQuery.push({ index: i, address: addresses[i] });
@@ -127,9 +206,20 @@ export class NonceManager {
127
206
  // 填充结果并更新缓存
128
207
  for (let i = 0; i < needQuery.length; i++) {
129
208
  const { index, address } = needQuery[i];
130
- const nonce = queryResults[i];
131
- results[index] = nonce;
132
- this.tempNonceCache.set(address.toLowerCase(), nonce + 1);
209
+ const key = address.toLowerCase();
210
+ const onchainNonce = queryResults[i];
211
+ let effectiveNonce = onchainNonce;
212
+ if (flaky) {
213
+ const globalNext = getGlobalNextNonce(chainId, key);
214
+ if (globalNext !== undefined) {
215
+ const delta = globalNext - onchainNonce;
216
+ if (delta > 0 && delta <= 10)
217
+ effectiveNonce = globalNext;
218
+ }
219
+ setGlobalNextNonce(chainId, key, effectiveNonce + 1);
220
+ }
221
+ results[index] = effectiveNonce;
222
+ this.tempNonceCache.set(key, effectiveNonce + 1);
133
223
  }
134
224
  }
135
225
  return results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.17",
3
+ "version": "1.5.19",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",