@cfxdevkit/defi-react 0.1.0
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.cjs +720 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +377 -0
- package/dist/index.d.ts +377 -0
- package/dist/index.js +684 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
AUTOMATION_MANAGER_ABI: () => AUTOMATION_MANAGER_ABI,
|
|
24
|
+
AUTOMATION_MANAGER_ADDRESS: () => AUTOMATION_MANAGER_ADDRESS,
|
|
25
|
+
CFX_NATIVE_ADDRESS: () => CFX_NATIVE_ADDRESS,
|
|
26
|
+
ERC20_ABI: () => ERC20_ABI,
|
|
27
|
+
MAX_UINT256: () => MAX_UINT256,
|
|
28
|
+
WCFX_ABI: () => WCFX_ABI,
|
|
29
|
+
getPairedTokens: () => getPairedTokens,
|
|
30
|
+
resolveTokenInAddress: () => resolveTokenInAddress,
|
|
31
|
+
usePoolTokens: () => usePoolTokens,
|
|
32
|
+
wcfxAddress: () => wcfxAddress
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(src_exports);
|
|
35
|
+
|
|
36
|
+
// src/contracts.ts
|
|
37
|
+
var AUTOMATION_MANAGER_ADDRESS = process.env.NEXT_PUBLIC_AUTOMATION_MANAGER_ADDRESS ?? "0x0000000000000000000000000000000000000000";
|
|
38
|
+
var ERC20_ABI = [
|
|
39
|
+
{
|
|
40
|
+
type: "function",
|
|
41
|
+
name: "allowance",
|
|
42
|
+
stateMutability: "view",
|
|
43
|
+
inputs: [
|
|
44
|
+
{ name: "owner", type: "address" },
|
|
45
|
+
{ name: "spender", type: "address" }
|
|
46
|
+
],
|
|
47
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: "function",
|
|
51
|
+
name: "approve",
|
|
52
|
+
stateMutability: "nonpayable",
|
|
53
|
+
inputs: [
|
|
54
|
+
{ name: "spender", type: "address" },
|
|
55
|
+
{ name: "amount", type: "uint256" }
|
|
56
|
+
],
|
|
57
|
+
outputs: [{ name: "", type: "bool" }]
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
var WCFX_ABI = [
|
|
61
|
+
{
|
|
62
|
+
type: "function",
|
|
63
|
+
name: "balanceOf",
|
|
64
|
+
stateMutability: "view",
|
|
65
|
+
inputs: [{ name: "account", type: "address" }],
|
|
66
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "function",
|
|
70
|
+
name: "allowance",
|
|
71
|
+
stateMutability: "view",
|
|
72
|
+
inputs: [
|
|
73
|
+
{ name: "owner", type: "address" },
|
|
74
|
+
{ name: "spender", type: "address" }
|
|
75
|
+
],
|
|
76
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: "function",
|
|
80
|
+
name: "approve",
|
|
81
|
+
stateMutability: "nonpayable",
|
|
82
|
+
inputs: [
|
|
83
|
+
{ name: "spender", type: "address" },
|
|
84
|
+
{ name: "amount", type: "uint256" }
|
|
85
|
+
],
|
|
86
|
+
outputs: [{ name: "", type: "bool" }]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: "function",
|
|
90
|
+
name: "deposit",
|
|
91
|
+
stateMutability: "payable",
|
|
92
|
+
inputs: [],
|
|
93
|
+
outputs: []
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: "function",
|
|
97
|
+
name: "withdraw",
|
|
98
|
+
stateMutability: "nonpayable",
|
|
99
|
+
inputs: [{ name: "wad", type: "uint256" }],
|
|
100
|
+
outputs: []
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
104
|
+
var AUTOMATION_MANAGER_ABI = [
|
|
105
|
+
// ─── Custom Errors ─────────────────────────────────────────────────────────
|
|
106
|
+
{
|
|
107
|
+
type: "error",
|
|
108
|
+
name: "DCACompleted",
|
|
109
|
+
inputs: [{ name: "jobId", type: "bytes32" }]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
type: "error",
|
|
113
|
+
name: "DCAIntervalNotReached",
|
|
114
|
+
inputs: [{ name: "nextExecution", type: "uint256" }]
|
|
115
|
+
},
|
|
116
|
+
{ type: "error", name: "EnforcedPause", inputs: [] },
|
|
117
|
+
{ type: "error", name: "ExpectedPause", inputs: [] },
|
|
118
|
+
{
|
|
119
|
+
type: "error",
|
|
120
|
+
name: "InvalidParams",
|
|
121
|
+
inputs: [{ name: "reason", type: "string" }]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: "error",
|
|
125
|
+
name: "JobExpiredError",
|
|
126
|
+
inputs: [{ name: "jobId", type: "bytes32" }]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
type: "error",
|
|
130
|
+
name: "JobNotActive",
|
|
131
|
+
inputs: [{ name: "jobId", type: "bytes32" }]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
type: "error",
|
|
135
|
+
name: "JobNotFound",
|
|
136
|
+
inputs: [{ name: "jobId", type: "bytes32" }]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: "error",
|
|
140
|
+
name: "OwnableInvalidOwner",
|
|
141
|
+
inputs: [{ name: "owner", type: "address" }]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
type: "error",
|
|
145
|
+
name: "OwnableUnauthorizedAccount",
|
|
146
|
+
inputs: [{ name: "account", type: "address" }]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: "error",
|
|
150
|
+
name: "PriceConditionNotMet",
|
|
151
|
+
inputs: [{ name: "jobId", type: "bytes32" }]
|
|
152
|
+
},
|
|
153
|
+
{ type: "error", name: "ReentrancyGuardReentrantCall", inputs: [] },
|
|
154
|
+
{
|
|
155
|
+
type: "error",
|
|
156
|
+
name: "SafeERC20FailedOperation",
|
|
157
|
+
inputs: [{ name: "token", type: "address" }]
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
type: "error",
|
|
161
|
+
name: "SlippageTooHigh",
|
|
162
|
+
inputs: [
|
|
163
|
+
{ name: "requested", type: "uint256" },
|
|
164
|
+
{ name: "maxAllowed", type: "uint256" }
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: "error",
|
|
169
|
+
name: "TooManyJobs",
|
|
170
|
+
inputs: [{ name: "user", type: "address" }]
|
|
171
|
+
},
|
|
172
|
+
{ type: "error", name: "Unauthorized", inputs: [] },
|
|
173
|
+
{ type: "error", name: "ZeroAddress", inputs: [] },
|
|
174
|
+
// ─── Events ────────────────────────────────────────────────────────────────
|
|
175
|
+
{
|
|
176
|
+
type: "event",
|
|
177
|
+
name: "JobCreated",
|
|
178
|
+
anonymous: false,
|
|
179
|
+
inputs: [
|
|
180
|
+
{ indexed: true, name: "jobId", type: "bytes32" },
|
|
181
|
+
{ indexed: true, name: "owner", type: "address" },
|
|
182
|
+
{ indexed: false, name: "jobType", type: "uint8" }
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
// ─── createLimitOrder ──────────────────────────────────────────────────────
|
|
186
|
+
{
|
|
187
|
+
type: "function",
|
|
188
|
+
name: "createLimitOrder",
|
|
189
|
+
stateMutability: "nonpayable",
|
|
190
|
+
inputs: [
|
|
191
|
+
{
|
|
192
|
+
name: "params",
|
|
193
|
+
type: "tuple",
|
|
194
|
+
components: [
|
|
195
|
+
{ name: "tokenIn", type: "address" },
|
|
196
|
+
{ name: "tokenOut", type: "address" },
|
|
197
|
+
{ name: "amountIn", type: "uint256" },
|
|
198
|
+
{ name: "minAmountOut", type: "uint256" },
|
|
199
|
+
{ name: "targetPrice", type: "uint256" },
|
|
200
|
+
{ name: "triggerAbove", type: "bool" }
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{ name: "slippageBps", type: "uint256" },
|
|
204
|
+
{ name: "expiresAt", type: "uint256" }
|
|
205
|
+
],
|
|
206
|
+
outputs: [{ name: "jobId", type: "bytes32" }]
|
|
207
|
+
},
|
|
208
|
+
// ─── createDCAJob ──────────────────────────────────────────────────────────
|
|
209
|
+
{
|
|
210
|
+
type: "function",
|
|
211
|
+
name: "createDCAJob",
|
|
212
|
+
stateMutability: "nonpayable",
|
|
213
|
+
inputs: [
|
|
214
|
+
{
|
|
215
|
+
name: "params",
|
|
216
|
+
type: "tuple",
|
|
217
|
+
components: [
|
|
218
|
+
{ name: "tokenIn", type: "address" },
|
|
219
|
+
{ name: "tokenOut", type: "address" },
|
|
220
|
+
{ name: "amountPerSwap", type: "uint256" },
|
|
221
|
+
{ name: "intervalSeconds", type: "uint256" },
|
|
222
|
+
{ name: "totalSwaps", type: "uint256" },
|
|
223
|
+
{ name: "swapsCompleted", type: "uint256" },
|
|
224
|
+
{ name: "nextExecution", type: "uint256" }
|
|
225
|
+
]
|
|
226
|
+
},
|
|
227
|
+
{ name: "slippageBps", type: "uint256" },
|
|
228
|
+
{ name: "expiresAt", type: "uint256" }
|
|
229
|
+
],
|
|
230
|
+
outputs: [{ name: "jobId", type: "bytes32" }]
|
|
231
|
+
},
|
|
232
|
+
// ─── cancelJob ─────────────────────────────────────────────────────────────
|
|
233
|
+
{
|
|
234
|
+
type: "function",
|
|
235
|
+
name: "cancelJob",
|
|
236
|
+
stateMutability: "nonpayable",
|
|
237
|
+
inputs: [{ name: "jobId", type: "bytes32" }],
|
|
238
|
+
outputs: []
|
|
239
|
+
},
|
|
240
|
+
// ─── JobCancelled event ────────────────────────────────────────────────────
|
|
241
|
+
{
|
|
242
|
+
type: "event",
|
|
243
|
+
name: "JobCancelled",
|
|
244
|
+
anonymous: false,
|
|
245
|
+
inputs: [
|
|
246
|
+
{ indexed: true, name: "jobId", type: "bytes32" },
|
|
247
|
+
{ indexed: true, name: "canceller", type: "address" }
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
// src/usePoolTokens.ts
|
|
253
|
+
var import_react = require("react");
|
|
254
|
+
var import_viem = require("viem");
|
|
255
|
+
var import_chains = require("viem/chains");
|
|
256
|
+
var CFX_NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
257
|
+
var WCFX_ADDRESSES = {
|
|
258
|
+
testnet: "0x2ED3dddae5B2F321AF0806181FBFA6D049Be47d8",
|
|
259
|
+
mainnet: "0x14b2D3bC65e74DAE1030EAFd8ac30c533c976A9b"
|
|
260
|
+
// EIP-55 checksum: last char is lowercase b
|
|
261
|
+
};
|
|
262
|
+
function wcfxAddress(network = process.env.NEXT_PUBLIC_NETWORK ?? "testnet") {
|
|
263
|
+
return WCFX_ADDRESSES[network] ?? WCFX_ADDRESSES.testnet;
|
|
264
|
+
}
|
|
265
|
+
function resolveTokenInAddress(address) {
|
|
266
|
+
if (address.toLowerCase() === CFX_NATIVE_ADDRESS.toLowerCase()) {
|
|
267
|
+
return wcfxAddress();
|
|
268
|
+
}
|
|
269
|
+
return address;
|
|
270
|
+
}
|
|
271
|
+
var CACHE_KEY = "cas_pool_meta_v2";
|
|
272
|
+
var CACHE_TTL = 10 * 60 * 1e3;
|
|
273
|
+
function readCache() {
|
|
274
|
+
if (typeof window === "undefined") return null;
|
|
275
|
+
try {
|
|
276
|
+
const raw = localStorage.getItem(CACHE_KEY);
|
|
277
|
+
if (!raw) return null;
|
|
278
|
+
const c = JSON.parse(raw);
|
|
279
|
+
c.tokens = c.tokens.map((t) => ({
|
|
280
|
+
...t,
|
|
281
|
+
address: t.address.toLowerCase()
|
|
282
|
+
}));
|
|
283
|
+
c.pairs = c.pairs.map((p) => ({
|
|
284
|
+
...p,
|
|
285
|
+
address: p.address.toLowerCase(),
|
|
286
|
+
token0: p.token0.toLowerCase(),
|
|
287
|
+
token1: p.token1.toLowerCase()
|
|
288
|
+
}));
|
|
289
|
+
if (Date.now() - c.cachedAt > CACHE_TTL) return null;
|
|
290
|
+
return c;
|
|
291
|
+
} catch {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function readCacheIgnoreTTL() {
|
|
296
|
+
if (typeof window === "undefined") return null;
|
|
297
|
+
try {
|
|
298
|
+
const raw = localStorage.getItem(CACHE_KEY);
|
|
299
|
+
if (!raw) return null;
|
|
300
|
+
const c = JSON.parse(raw);
|
|
301
|
+
c.tokens = c.tokens.map((t) => ({
|
|
302
|
+
...t,
|
|
303
|
+
address: t.address.toLowerCase()
|
|
304
|
+
}));
|
|
305
|
+
c.pairs = c.pairs.map((p) => ({
|
|
306
|
+
...p,
|
|
307
|
+
address: p.address.toLowerCase(),
|
|
308
|
+
token0: p.token0.toLowerCase(),
|
|
309
|
+
token1: p.token1.toLowerCase()
|
|
310
|
+
}));
|
|
311
|
+
return c;
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function writeCache(tokens, pairs) {
|
|
317
|
+
try {
|
|
318
|
+
localStorage.setItem(
|
|
319
|
+
CACHE_KEY,
|
|
320
|
+
JSON.stringify({
|
|
321
|
+
tokens,
|
|
322
|
+
pairs,
|
|
323
|
+
cachedAt: Date.now()
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function mergeTokens(known, fresh) {
|
|
330
|
+
for (const t of fresh) {
|
|
331
|
+
known.set(t.address.toLowerCase(), {
|
|
332
|
+
...t,
|
|
333
|
+
address: t.address.toLowerCase()
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function mergePairs(known, fresh) {
|
|
338
|
+
for (const p of fresh) {
|
|
339
|
+
const t0 = p.token0.toLowerCase();
|
|
340
|
+
const t1 = p.token1.toLowerCase();
|
|
341
|
+
const key = [t0, t1].sort().join("|");
|
|
342
|
+
known.set(key, {
|
|
343
|
+
address: p.address.toLowerCase(),
|
|
344
|
+
token0: t0,
|
|
345
|
+
token1: t1
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
var BALANCE_OF_ABI = [
|
|
350
|
+
{
|
|
351
|
+
name: "balanceOf",
|
|
352
|
+
type: "function",
|
|
353
|
+
stateMutability: "view",
|
|
354
|
+
inputs: [{ name: "account", type: "address" }],
|
|
355
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
356
|
+
}
|
|
357
|
+
];
|
|
358
|
+
function getPairedTokens(pairs, allTokens, tokenInAddress) {
|
|
359
|
+
if (!tokenInAddress) return allTokens;
|
|
360
|
+
const incoming = tokenInAddress.toLowerCase();
|
|
361
|
+
const wcfx = wcfxAddress().toLowerCase();
|
|
362
|
+
const cfxNative = CFX_NATIVE_ADDRESS.toLowerCase();
|
|
363
|
+
const resolved = incoming === cfxNative ? wcfx : incoming;
|
|
364
|
+
const counterparts = /* @__PURE__ */ new Set();
|
|
365
|
+
for (const p of pairs) {
|
|
366
|
+
const t0 = p.token0.toLowerCase();
|
|
367
|
+
const t1 = p.token1.toLowerCase();
|
|
368
|
+
if (t0 === resolved) counterparts.add(t1);
|
|
369
|
+
if (t1 === resolved) counterparts.add(t0);
|
|
370
|
+
}
|
|
371
|
+
const wCfxPaired = counterparts.has(wcfx);
|
|
372
|
+
counterparts.delete(wcfx);
|
|
373
|
+
counterparts.delete(cfxNative);
|
|
374
|
+
const results = allTokens.filter(
|
|
375
|
+
(t) => counterparts.has(t.address.toLowerCase()) && t.address.toLowerCase() !== cfxNative && t.address.toLowerCase() !== wcfx
|
|
376
|
+
);
|
|
377
|
+
if (wCfxPaired && incoming !== cfxNative) {
|
|
378
|
+
const cfxEntry = allTokens.find(
|
|
379
|
+
(t) => t.address.toLowerCase() === cfxNative
|
|
380
|
+
);
|
|
381
|
+
if (cfxEntry) return [cfxEntry, ...results];
|
|
382
|
+
}
|
|
383
|
+
return results;
|
|
384
|
+
}
|
|
385
|
+
function metaToTokens(rawTokens) {
|
|
386
|
+
const wcfx = wcfxAddress().toLowerCase();
|
|
387
|
+
return rawTokens.map((t) => ({
|
|
388
|
+
...t,
|
|
389
|
+
address: t.address.toLowerCase(),
|
|
390
|
+
symbol: t.address.toLowerCase() === wcfx ? "wCFX" : t.symbol,
|
|
391
|
+
name: t.address.toLowerCase() === wcfx ? "Wrapped CFX" : t.name,
|
|
392
|
+
logoURI: t.logoURI,
|
|
393
|
+
balanceWei: "0",
|
|
394
|
+
balanceFormatted: "0"
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
var CFX_ENTRY_ZERO = {
|
|
398
|
+
address: CFX_NATIVE_ADDRESS,
|
|
399
|
+
symbol: "CFX",
|
|
400
|
+
name: "Conflux (native)",
|
|
401
|
+
decimals: 18,
|
|
402
|
+
balanceWei: "0",
|
|
403
|
+
balanceFormatted: "0"
|
|
404
|
+
};
|
|
405
|
+
function cfxEntryFrom(knownTokens) {
|
|
406
|
+
const wcfx = wcfxAddress().toLowerCase();
|
|
407
|
+
const logo = knownTokens.get(wcfx)?.logoURI;
|
|
408
|
+
return logo ? { ...CFX_ENTRY_ZERO, logoURI: logo } : CFX_ENTRY_ZERO;
|
|
409
|
+
}
|
|
410
|
+
function withTimeout(promise, ms, silent = false) {
|
|
411
|
+
return new Promise((resolve) => {
|
|
412
|
+
const t = setTimeout(() => {
|
|
413
|
+
if (!silent)
|
|
414
|
+
console.warn("[usePoolTokens] RPC call timed out after", ms, "ms");
|
|
415
|
+
resolve(null);
|
|
416
|
+
}, ms);
|
|
417
|
+
promise.then(
|
|
418
|
+
(v) => {
|
|
419
|
+
clearTimeout(t);
|
|
420
|
+
resolve(v);
|
|
421
|
+
},
|
|
422
|
+
(err) => {
|
|
423
|
+
clearTimeout(t);
|
|
424
|
+
if (!silent) {
|
|
425
|
+
const msg = err?.message ?? String(err);
|
|
426
|
+
console.warn("[usePoolTokens] RPC call rejected:", msg);
|
|
427
|
+
}
|
|
428
|
+
resolve(null);
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function usePoolTokens(userAddress) {
|
|
434
|
+
const [tokens, setTokens] = (0, import_react.useState)([]);
|
|
435
|
+
const [pairs, setPairs] = (0, import_react.useState)([]);
|
|
436
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
437
|
+
const [balancesLoading, setBalancesLoading] = (0, import_react.useState)(false);
|
|
438
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
439
|
+
const [rpcWarning, setRpcWarning] = (0, import_react.useState)(null);
|
|
440
|
+
const [_tick, setTick] = (0, import_react.useState)(0);
|
|
441
|
+
const refresh = (0, import_react.useCallback)(() => setTick((t) => t + 1), []);
|
|
442
|
+
(0, import_react.useEffect)(() => {
|
|
443
|
+
if (!userAddress) return;
|
|
444
|
+
const id = setInterval(() => setTick((t) => t + 1), 3e4);
|
|
445
|
+
return () => clearInterval(id);
|
|
446
|
+
}, [userAddress]);
|
|
447
|
+
const knownTokensRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
448
|
+
const knownPairsRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
449
|
+
const applyKnown = (0, import_react.useCallback)(() => {
|
|
450
|
+
const tokenArr = Array.from(knownTokensRef.current.values());
|
|
451
|
+
const pairArr = Array.from(knownPairsRef.current.values());
|
|
452
|
+
setPairs(pairArr);
|
|
453
|
+
return { tokenArr, pairArr };
|
|
454
|
+
}, []);
|
|
455
|
+
const fetchBalances = (0, import_react.useCallback)(
|
|
456
|
+
async (owner, signal) => {
|
|
457
|
+
const pairAddrs = new Set(
|
|
458
|
+
Array.from(knownPairsRef.current.values()).map(
|
|
459
|
+
(p) => p.address.toLowerCase()
|
|
460
|
+
)
|
|
461
|
+
);
|
|
462
|
+
const rawTokens = Array.from(knownTokensRef.current.values()).filter(
|
|
463
|
+
(t) => !pairAddrs.has(t.address.toLowerCase())
|
|
464
|
+
);
|
|
465
|
+
setBalancesLoading(true);
|
|
466
|
+
try {
|
|
467
|
+
const network = process.env.NEXT_PUBLIC_NETWORK ?? "testnet";
|
|
468
|
+
const rpcUrl = network === "testnet" ? "https://evmtestnet.confluxrpc.com" : "https://evm.confluxrpc.com";
|
|
469
|
+
const chain = network === "testnet" ? import_chains.confluxESpaceTestnet : import_chains.confluxESpace;
|
|
470
|
+
const client = (0, import_viem.createPublicClient)({ chain, transport: (0, import_viem.http)(rpcUrl) });
|
|
471
|
+
const CALL_TIMEOUT = 3e4;
|
|
472
|
+
console.log("[usePoolTokens] fetchBalances start", {
|
|
473
|
+
owner,
|
|
474
|
+
network,
|
|
475
|
+
rpcUrl,
|
|
476
|
+
ercTokenCount: rawTokens.length
|
|
477
|
+
});
|
|
478
|
+
const nativeResult = await withTimeout(
|
|
479
|
+
client.getBalance({ address: owner }),
|
|
480
|
+
CALL_TIMEOUT
|
|
481
|
+
);
|
|
482
|
+
if (signal.aborted) return;
|
|
483
|
+
console.log(
|
|
484
|
+
"[usePoolTokens] fetchBalances nativeResult:",
|
|
485
|
+
nativeResult?.toString() ?? "null (failed)"
|
|
486
|
+
);
|
|
487
|
+
const MULTICALL3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
488
|
+
let ercResults = [];
|
|
489
|
+
let multicallResult = null;
|
|
490
|
+
if (rawTokens.length > 0) {
|
|
491
|
+
multicallResult = await withTimeout(
|
|
492
|
+
client.multicall({
|
|
493
|
+
contracts: rawTokens.map((t) => ({
|
|
494
|
+
address: t.address,
|
|
495
|
+
abi: BALANCE_OF_ABI,
|
|
496
|
+
functionName: "balanceOf",
|
|
497
|
+
args: [owner]
|
|
498
|
+
})),
|
|
499
|
+
allowFailure: true,
|
|
500
|
+
multicallAddress: MULTICALL3
|
|
501
|
+
}),
|
|
502
|
+
CALL_TIMEOUT,
|
|
503
|
+
/* silent */
|
|
504
|
+
true
|
|
505
|
+
);
|
|
506
|
+
ercResults = multicallResult ? multicallResult.map(
|
|
507
|
+
(r) => r.status === "success" ? r.result : null
|
|
508
|
+
) : rawTokens.map(() => null);
|
|
509
|
+
const successCount = ercResults.filter((r) => r !== null).length;
|
|
510
|
+
console.log(
|
|
511
|
+
`[usePoolTokens] fetchBalances multicall: ${successCount}/${rawTokens.length} succeeded, full timeout: ${multicallResult === null}`
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
if (signal.aborted) return;
|
|
515
|
+
if (nativeResult === null) {
|
|
516
|
+
const msg = "CFX balance fetch failed \u2014 RPC may be unavailable.";
|
|
517
|
+
console.warn("[usePoolTokens] fetchBalances:", msg);
|
|
518
|
+
setRpcWarning(msg);
|
|
519
|
+
} else if (rawTokens.length > 0 && multicallResult === null) {
|
|
520
|
+
const msg = "Token balance fetch timed out \u2014 RPC may be overloaded. Showing last known balances.";
|
|
521
|
+
console.warn("[usePoolTokens] fetchBalances:", msg);
|
|
522
|
+
setRpcWarning(msg);
|
|
523
|
+
} else {
|
|
524
|
+
setRpcWarning(null);
|
|
525
|
+
}
|
|
526
|
+
const wcfx = wcfxAddress().toLowerCase();
|
|
527
|
+
const enriched = rawTokens.map((t, i) => {
|
|
528
|
+
const raw = ercResults[i];
|
|
529
|
+
return {
|
|
530
|
+
...t,
|
|
531
|
+
symbol: t.address.toLowerCase() === wcfx ? "wCFX" : t.symbol,
|
|
532
|
+
name: t.address.toLowerCase() === wcfx ? "Wrapped CFX" : t.name,
|
|
533
|
+
// null means the call timed out — balanceWei stays '0' for now;
|
|
534
|
+
// the merge step below reinstates any previously-known balance.
|
|
535
|
+
balanceWei: raw !== null ? raw.toString() : "0",
|
|
536
|
+
balanceFormatted: raw !== null ? (0, import_viem.formatUnits)(raw, t.decimals ?? 18) : "0"
|
|
537
|
+
};
|
|
538
|
+
});
|
|
539
|
+
enriched.sort((a, b) => {
|
|
540
|
+
const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);
|
|
541
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : a.symbol.localeCompare(b.symbol);
|
|
542
|
+
});
|
|
543
|
+
const nativeBal = nativeResult ?? 0n;
|
|
544
|
+
const cfxWithBalance = {
|
|
545
|
+
...cfxEntryFrom(knownTokensRef.current),
|
|
546
|
+
balanceWei: nativeBal.toString(),
|
|
547
|
+
balanceFormatted: (0, import_viem.formatUnits)(nativeBal, 18)
|
|
548
|
+
};
|
|
549
|
+
if (!signal.aborted) {
|
|
550
|
+
setTokens((prev) => {
|
|
551
|
+
const prevMap = new Map(
|
|
552
|
+
prev.map((t) => [t.address.toLowerCase(), t.balanceWei])
|
|
553
|
+
);
|
|
554
|
+
const timedOut = ercResults.map((r) => r === null);
|
|
555
|
+
const merged = [
|
|
556
|
+
// CFX: keep previous balance if native call timed out
|
|
557
|
+
nativeResult !== null ? cfxWithBalance : prev.find((t) => t.address === CFX_NATIVE_ADDRESS) ?? cfxWithBalance,
|
|
558
|
+
...enriched.map((t, i) => {
|
|
559
|
+
if (timedOut[i]) {
|
|
560
|
+
const prevBal = prevMap.get(t.address.toLowerCase()) ?? "0";
|
|
561
|
+
if (prevBal !== "0") {
|
|
562
|
+
return {
|
|
563
|
+
...t,
|
|
564
|
+
balanceWei: prevBal,
|
|
565
|
+
balanceFormatted: (0, import_viem.formatUnits)(
|
|
566
|
+
BigInt(prevBal),
|
|
567
|
+
t.decimals ?? 18
|
|
568
|
+
)
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return t;
|
|
573
|
+
})
|
|
574
|
+
];
|
|
575
|
+
const [cfxEntry, ...rest] = merged;
|
|
576
|
+
rest.sort((a, b) => {
|
|
577
|
+
const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);
|
|
578
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : a.symbol.localeCompare(b.symbol);
|
|
579
|
+
});
|
|
580
|
+
return [cfxEntry, ...rest];
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
} catch (err) {
|
|
584
|
+
console.error(
|
|
585
|
+
"[usePoolTokens] fetchBalances threw:",
|
|
586
|
+
err?.message ?? err
|
|
587
|
+
);
|
|
588
|
+
setRpcWarning("Balance fetch failed \u2014 see browser console for details");
|
|
589
|
+
} finally {
|
|
590
|
+
if (!signal.aborted) setBalancesLoading(false);
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
[]
|
|
594
|
+
);
|
|
595
|
+
const tickMountedRef = (0, import_react.useRef)(false);
|
|
596
|
+
(0, import_react.useEffect)(() => {
|
|
597
|
+
if (!tickMountedRef.current) {
|
|
598
|
+
tickMountedRef.current = true;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (!userAddress) return;
|
|
602
|
+
const ctrl = new AbortController();
|
|
603
|
+
void fetchBalances(userAddress, ctrl.signal);
|
|
604
|
+
return () => ctrl.abort();
|
|
605
|
+
}, [_tick, userAddress, fetchBalances]);
|
|
606
|
+
(0, import_react.useEffect)(() => {
|
|
607
|
+
const abortCtrl = new AbortController();
|
|
608
|
+
const { signal } = abortCtrl;
|
|
609
|
+
const persisted = readCacheIgnoreTTL();
|
|
610
|
+
if (persisted) {
|
|
611
|
+
mergeTokens(knownTokensRef.current, persisted.tokens);
|
|
612
|
+
mergePairs(knownPairsRef.current, persisted.pairs);
|
|
613
|
+
}
|
|
614
|
+
const cached = readCache();
|
|
615
|
+
if (cached) {
|
|
616
|
+
const { tokenArr } = applyKnown();
|
|
617
|
+
const cfxBase = cfxEntryFrom(knownTokensRef.current);
|
|
618
|
+
const freshMeta = metaToTokens(tokenArr);
|
|
619
|
+
setTokens((prev) => {
|
|
620
|
+
if (prev.length === 0) {
|
|
621
|
+
return [cfxBase, ...freshMeta];
|
|
622
|
+
}
|
|
623
|
+
const existing = new Map(prev.map((t) => [t.address.toLowerCase(), t]));
|
|
624
|
+
const prevCfx = existing.get(CFX_NATIVE_ADDRESS.toLowerCase());
|
|
625
|
+
const cfxEntry = prevCfx ? {
|
|
626
|
+
...cfxBase,
|
|
627
|
+
balanceWei: prevCfx.balanceWei,
|
|
628
|
+
balanceFormatted: prevCfx.balanceFormatted
|
|
629
|
+
} : cfxBase;
|
|
630
|
+
const rest = freshMeta.map(
|
|
631
|
+
(t) => existing.get(t.address.toLowerCase()) ?? t
|
|
632
|
+
);
|
|
633
|
+
rest.sort((a, b) => {
|
|
634
|
+
const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);
|
|
635
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : a.symbol.localeCompare(b.symbol);
|
|
636
|
+
});
|
|
637
|
+
return [cfxEntry, ...rest];
|
|
638
|
+
});
|
|
639
|
+
setLoading(false);
|
|
640
|
+
if (userAddress) {
|
|
641
|
+
fetchBalances(userAddress, signal);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
(async () => {
|
|
645
|
+
try {
|
|
646
|
+
const res = await fetch("/api/pools", { signal });
|
|
647
|
+
if (!res.ok) throw new Error(`Backend returned ${res.status}`);
|
|
648
|
+
const data = await res.json();
|
|
649
|
+
if (signal.aborted) return;
|
|
650
|
+
mergeTokens(knownTokensRef.current, data.tokens);
|
|
651
|
+
mergePairs(knownPairsRef.current, data.pairs);
|
|
652
|
+
const mergedTokens = Array.from(knownTokensRef.current.values());
|
|
653
|
+
const mergedPairs = Array.from(knownPairsRef.current.values());
|
|
654
|
+
writeCache(mergedTokens, mergedPairs);
|
|
655
|
+
setPairs(mergedPairs);
|
|
656
|
+
const freshMeta = metaToTokens(mergedTokens);
|
|
657
|
+
const cfxBase = cfxEntryFrom(knownTokensRef.current);
|
|
658
|
+
setTokens((prev) => {
|
|
659
|
+
if (prev.length === 0) {
|
|
660
|
+
return [cfxBase, ...freshMeta];
|
|
661
|
+
}
|
|
662
|
+
const existing = new Map(
|
|
663
|
+
prev.map((t) => [t.address.toLowerCase(), t])
|
|
664
|
+
);
|
|
665
|
+
const prevCfx = existing.get(CFX_NATIVE_ADDRESS.toLowerCase());
|
|
666
|
+
const cfxEntry = prevCfx ? {
|
|
667
|
+
...cfxBase,
|
|
668
|
+
balanceWei: prevCfx.balanceWei,
|
|
669
|
+
balanceFormatted: prevCfx.balanceFormatted
|
|
670
|
+
} : cfxBase;
|
|
671
|
+
const rest = freshMeta.map(
|
|
672
|
+
(t) => existing.get(t.address.toLowerCase()) ?? t
|
|
673
|
+
);
|
|
674
|
+
rest.sort((a, b) => {
|
|
675
|
+
const diff = BigInt(b.balanceWei) - BigInt(a.balanceWei);
|
|
676
|
+
return diff > 0n ? 1 : diff < 0n ? -1 : a.symbol.localeCompare(b.symbol);
|
|
677
|
+
});
|
|
678
|
+
return [cfxEntry, ...rest];
|
|
679
|
+
});
|
|
680
|
+
setLoading(false);
|
|
681
|
+
if (!userAddress) return;
|
|
682
|
+
await fetchBalances(userAddress, signal);
|
|
683
|
+
} catch (err) {
|
|
684
|
+
if (signal.aborted) return;
|
|
685
|
+
const msg = err?.message ?? String(err);
|
|
686
|
+
console.warn("[usePoolTokens] /api/pools fetch failed:", msg);
|
|
687
|
+
if (cached || persisted) {
|
|
688
|
+
setLoading(false);
|
|
689
|
+
} else {
|
|
690
|
+
setError(msg);
|
|
691
|
+
setLoading(false);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
})();
|
|
695
|
+
return () => abortCtrl.abort();
|
|
696
|
+
}, [userAddress, applyKnown, fetchBalances]);
|
|
697
|
+
return {
|
|
698
|
+
tokens,
|
|
699
|
+
pairs,
|
|
700
|
+
loading,
|
|
701
|
+
balancesLoading,
|
|
702
|
+
error,
|
|
703
|
+
rpcWarning,
|
|
704
|
+
refresh
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
708
|
+
0 && (module.exports = {
|
|
709
|
+
AUTOMATION_MANAGER_ABI,
|
|
710
|
+
AUTOMATION_MANAGER_ADDRESS,
|
|
711
|
+
CFX_NATIVE_ADDRESS,
|
|
712
|
+
ERC20_ABI,
|
|
713
|
+
MAX_UINT256,
|
|
714
|
+
WCFX_ABI,
|
|
715
|
+
getPairedTokens,
|
|
716
|
+
resolveTokenInAddress,
|
|
717
|
+
usePoolTokens,
|
|
718
|
+
wcfxAddress
|
|
719
|
+
});
|
|
720
|
+
//# sourceMappingURL=index.cjs.map
|