aiia-vault-sdk 1.0.1
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/SeedRoundFundraiser.ts +742 -0
- package/TradingVault.ts +863 -0
- package/abis/SeedRoundFundraiser.json +1519 -0
- package/abis/TradingVault.json +1647 -0
- package/common.ts +131 -0
- package/contracts/SeedRoundFundraiser.ts +1670 -0
- package/contracts/TradingVault.ts +1752 -0
- package/contracts/common.ts +131 -0
- package/contracts/factories/SeedRoundFundraiser__factory.ts +1495 -0
- package/contracts/factories/index.ts +4 -0
- package/contracts/index.ts +6 -0
- package/contracts.json +28 -0
- package/dist/SeedRoundFundraiser.d.ts +130 -0
- package/dist/SeedRoundFundraiser.js +445 -0
- package/dist/TradingVault.d.ts +175 -0
- package/dist/TradingVault.js +521 -0
- package/dist/abis/SeedRoundFundraiser.json +1519 -0
- package/dist/abis/TradingVault.json +1647 -0
- package/dist/common.d.ts +50 -0
- package/dist/common.js +2 -0
- package/dist/contracts/SeedRoundFundraiser.d.ts +936 -0
- package/dist/contracts/SeedRoundFundraiser.js +2 -0
- package/dist/contracts/TradingVault.d.ts +930 -0
- package/dist/contracts/TradingVault.js +2 -0
- package/dist/contracts.json +28 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/types.d.ts +291 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +95 -0
- package/dist/utils.js +370 -0
- package/dist/whitelist-tokens.json +14 -0
- package/index.ts +3 -0
- package/package.json +21 -0
- package/temp/aiia-vault-sdk-1.0.0.tgz +0 -0
- package/tsconfig.json +15 -0
- package/types.ts +301 -0
- package/utils.ts +576 -0
- package/whitelist-tokens.json +14 -0
@@ -0,0 +1,742 @@
|
|
1
|
+
import { ethers } from "ethers";
|
2
|
+
import { SeedRoundFundraiser } from "./contracts/SeedRoundFundraiser";
|
3
|
+
import SeedRoundFundraiserABI from "./abis/SeedRoundFundraiser.json";
|
4
|
+
import contracts from "./contracts.json";
|
5
|
+
import {
|
6
|
+
ParsedSeedRoundFundraiserEvent,
|
7
|
+
ParsedSeedRoundFundraiserEventRaw,
|
8
|
+
} from "./types";
|
9
|
+
import { type SendTransactionMutateAsync } from "@wagmi/core/query";
|
10
|
+
import { Config } from "@wagmi/core/dist/types/createConfig";
|
11
|
+
import {
|
12
|
+
getRandomProvider,
|
13
|
+
checkRpcHealth,
|
14
|
+
getTransactionStatus,
|
15
|
+
getTokenDecimals,
|
16
|
+
getTokenSymbol,
|
17
|
+
resolveContractAddress,
|
18
|
+
streamEvents,
|
19
|
+
getAllEvents,
|
20
|
+
signAndSendTransaction,
|
21
|
+
} from "./utils";
|
22
|
+
import whitelistData from "./whitelist-tokens.json";
|
23
|
+
|
24
|
+
export interface WhitelistedToken {
|
25
|
+
isWhitelisted: boolean;
|
26
|
+
price: number; // Price in USD with PRICE_PRECISION decimals
|
27
|
+
}
|
28
|
+
|
29
|
+
export interface WhitelistedTokenInfo {
|
30
|
+
address: string;
|
31
|
+
isWhitelisted: boolean;
|
32
|
+
price: number;
|
33
|
+
symbol: string;
|
34
|
+
maxContribution: number;
|
35
|
+
maxContributionRaw: number;
|
36
|
+
decimals: number;
|
37
|
+
}
|
38
|
+
|
39
|
+
export interface RoundConfig {
|
40
|
+
startTime: number;
|
41
|
+
endTime: number;
|
42
|
+
targetFund: number; // In USD with PRICE_PRECISION decimals
|
43
|
+
totalAllocation: number; // Total tokens to be allocated
|
44
|
+
maxFundPerAccount: number; // Max fund per account in USD with PRICE_PRECISION decimals
|
45
|
+
exists: boolean;
|
46
|
+
ended: boolean;
|
47
|
+
claimingEnabled: boolean; // Flag to control token claiming for this round
|
48
|
+
refundEnabled: boolean; // Flag to control refunds for this round
|
49
|
+
}
|
50
|
+
|
51
|
+
export interface UserContribution {
|
52
|
+
fundAmount: number; // In USD with PRICE_PRECISION decimals
|
53
|
+
tokenAllocation: number; // Token allocation
|
54
|
+
claimed: boolean;
|
55
|
+
refunded: boolean; // Flag to track if user has been refunded
|
56
|
+
contributedToken: string; // The token address used for contribution
|
57
|
+
contributedAmount: number; // The actual token amount contributed
|
58
|
+
}
|
59
|
+
|
60
|
+
export class SeedRoundFundraiserSDK {
|
61
|
+
name = "SeedRoundFundraiser";
|
62
|
+
private contract: SeedRoundFundraiser;
|
63
|
+
private providers: ethers.Provider[];
|
64
|
+
private contractAddress: string;
|
65
|
+
private isBootstrapped: boolean = false;
|
66
|
+
private PRICE_PRECISION = 1e18;
|
67
|
+
private whitelistedTokensArray: string[] = [];
|
68
|
+
private networkName: string;
|
69
|
+
|
70
|
+
constructor(rpcUrls: string | string[], contractAddress?: string) {
|
71
|
+
// Convert single RPC to array if needed
|
72
|
+
const rpcArray = Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls];
|
73
|
+
|
74
|
+
// Create providers for each RPC
|
75
|
+
this.providers = rpcArray.map((rpc) => new ethers.JsonRpcProvider(rpc));
|
76
|
+
|
77
|
+
// Get a random provider for initial setup
|
78
|
+
const initialProvider = getRandomProvider(this.providers);
|
79
|
+
|
80
|
+
// Determine contract address based on RPC URL if not provided
|
81
|
+
this.contractAddress = resolveContractAddress(
|
82
|
+
rpcArray[0],
|
83
|
+
this.name,
|
84
|
+
contracts,
|
85
|
+
contractAddress
|
86
|
+
);
|
87
|
+
|
88
|
+
// Initialize contract with initial provider
|
89
|
+
this.contract = new ethers.Contract(
|
90
|
+
this.contractAddress,
|
91
|
+
SeedRoundFundraiserABI.abi,
|
92
|
+
initialProvider
|
93
|
+
) as unknown as SeedRoundFundraiser;
|
94
|
+
|
95
|
+
// Determine network name from RPC URL
|
96
|
+
this.networkName = this.getNetworkNameFromRpc(rpcArray[0]);
|
97
|
+
|
98
|
+
// Import whitelisted tokens from JSON file
|
99
|
+
this.importWhitelistedTokens();
|
100
|
+
}
|
101
|
+
|
102
|
+
private getNetworkNameFromRpc(rpcUrl: string): string {
|
103
|
+
const rpcLower = rpcUrl.toLowerCase();
|
104
|
+
// Extract network name from RPC URL or use a default method
|
105
|
+
if (rpcLower.includes("sepolia.base")) {
|
106
|
+
return "base-sepolia";
|
107
|
+
} else if (rpcLower.includes("sepolia")) {
|
108
|
+
return "sepolia";
|
109
|
+
} else if (rpcLower.includes("mainnet")) {
|
110
|
+
return "mainnet";
|
111
|
+
} else if (
|
112
|
+
rpcLower.includes("localhost") ||
|
113
|
+
rpcLower.includes("127.0.0.1")
|
114
|
+
) {
|
115
|
+
return "hardhat";
|
116
|
+
}
|
117
|
+
// Default to sepolia if can't determine
|
118
|
+
return "sepolia";
|
119
|
+
}
|
120
|
+
|
121
|
+
private importWhitelistedTokens() {
|
122
|
+
try {
|
123
|
+
// Try to read the whitelist-tokens.json file
|
124
|
+
|
125
|
+
// Check if the network exists in the whitelist data
|
126
|
+
if ((whitelistData as any)[this.networkName]) {
|
127
|
+
this.whitelistedTokensArray = (whitelistData as any)[
|
128
|
+
this.networkName
|
129
|
+
].map((address: string) => address.toLowerCase());
|
130
|
+
console.log(
|
131
|
+
`Imported ${this.whitelistedTokensArray.length} whitelisted tokens for ${this.networkName} network`
|
132
|
+
);
|
133
|
+
} else {
|
134
|
+
console.log(
|
135
|
+
`No whitelisted tokens found for ${this.networkName} network`
|
136
|
+
);
|
137
|
+
}
|
138
|
+
} catch (error) {
|
139
|
+
console.error("Error importing whitelisted tokens:", error);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
// Get whitelisted token info from the array
|
144
|
+
async getWhitelistedTokenInfo(
|
145
|
+
tokenAddress: string
|
146
|
+
): Promise<WhitelistedTokenInfo | null> {
|
147
|
+
// Convert to lowercase for case-insensitive comparison
|
148
|
+
const normalizedAddress = tokenAddress.toLowerCase();
|
149
|
+
|
150
|
+
// Check if token is in the whitelisted array
|
151
|
+
if (!this.whitelistedTokensArray.includes(normalizedAddress)) {
|
152
|
+
return null;
|
153
|
+
}
|
154
|
+
|
155
|
+
// Get token info from contract
|
156
|
+
const tokenInfo = await this.getWhitelistedToken(normalizedAddress);
|
157
|
+
|
158
|
+
// Skip if not whitelisted in contract
|
159
|
+
if (!tokenInfo.isWhitelisted) {
|
160
|
+
return null;
|
161
|
+
}
|
162
|
+
|
163
|
+
let symbol = "ETH";
|
164
|
+
let decimals = 18;
|
165
|
+
|
166
|
+
// Get token symbol and decimals
|
167
|
+
if (normalizedAddress !== ethers.ZeroAddress) {
|
168
|
+
[symbol, decimals] = await Promise.all([
|
169
|
+
getTokenSymbol(normalizedAddress, this.getRandomProvider()),
|
170
|
+
getTokenDecimals(normalizedAddress, this.getRandomProvider()),
|
171
|
+
]);
|
172
|
+
}
|
173
|
+
|
174
|
+
// Get latest active round to determine max contribution
|
175
|
+
const latestRoundId = await this.getLatestRoundId();
|
176
|
+
let maxTokenContribution = 0;
|
177
|
+
|
178
|
+
if (latestRoundId >= 0) {
|
179
|
+
const roundInfo = await this.getRound(latestRoundId);
|
180
|
+
if (roundInfo.exists && !roundInfo.ended) {
|
181
|
+
// Calculate max token contribution based on maxFundPerAccount and token price
|
182
|
+
// maxTokens = (maxFundPerAccount / tokenPrice) * 10^decimals
|
183
|
+
maxTokenContribution = roundInfo.maxFundPerAccount / tokenInfo.price;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
return {
|
188
|
+
address: normalizedAddress,
|
189
|
+
isWhitelisted: tokenInfo.isWhitelisted,
|
190
|
+
price: tokenInfo.price,
|
191
|
+
symbol: symbol,
|
192
|
+
maxContribution: maxTokenContribution,
|
193
|
+
maxContributionRaw: Math.floor(
|
194
|
+
maxTokenContribution * Math.pow(10, decimals)
|
195
|
+
),
|
196
|
+
decimals: decimals,
|
197
|
+
};
|
198
|
+
}
|
199
|
+
|
200
|
+
// Get all whitelisted tokens info
|
201
|
+
async getAllWhitelistedTokensInfo(): Promise<WhitelistedTokenInfo[]> {
|
202
|
+
const results: WhitelistedTokenInfo[] = [];
|
203
|
+
|
204
|
+
for (const tokenAddress of this.whitelistedTokensArray) {
|
205
|
+
const tokenInfo = await this.getWhitelistedTokenInfo(tokenAddress);
|
206
|
+
if (tokenInfo) {
|
207
|
+
results.push(tokenInfo);
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
return results;
|
212
|
+
}
|
213
|
+
|
214
|
+
private getRandomProvider(): ethers.Provider {
|
215
|
+
return getRandomProvider(this.providers);
|
216
|
+
}
|
217
|
+
|
218
|
+
private getContractWithRandomProvider(): SeedRoundFundraiser {
|
219
|
+
return new ethers.Contract(
|
220
|
+
this.contractAddress,
|
221
|
+
SeedRoundFundraiserABI.abi,
|
222
|
+
this.getRandomProvider()
|
223
|
+
) as unknown as SeedRoundFundraiser;
|
224
|
+
}
|
225
|
+
|
226
|
+
async bootstrap() {
|
227
|
+
if (this.isBootstrapped) {
|
228
|
+
return;
|
229
|
+
}
|
230
|
+
|
231
|
+
// Check health of all RPCs and remove inactive ones
|
232
|
+
const healthChecks = await Promise.all(
|
233
|
+
this.providers.map((provider, index) => checkRpcHealth(provider, index))
|
234
|
+
);
|
235
|
+
|
236
|
+
// Filter out inactive providers
|
237
|
+
this.providers = this.providers.filter((_, index) => healthChecks[index]);
|
238
|
+
|
239
|
+
if (this.providers.length === 0) {
|
240
|
+
throw new Error("No active RPC providers available");
|
241
|
+
}
|
242
|
+
|
243
|
+
this.isBootstrapped = true;
|
244
|
+
}
|
245
|
+
|
246
|
+
async signAndSendTransaction(
|
247
|
+
tx: ethers.ContractTransaction,
|
248
|
+
wallet: ethers.Wallet | SendTransactionMutateAsync<Config, any>,
|
249
|
+
callbacks: {
|
250
|
+
onSubmit?: (tx: string) => void | Promise<void>;
|
251
|
+
onFinally?: (status: {
|
252
|
+
status: boolean | null;
|
253
|
+
confirmations: number;
|
254
|
+
txHash: string;
|
255
|
+
isCompleted: boolean;
|
256
|
+
attempts: number;
|
257
|
+
}) => void | Promise<void>;
|
258
|
+
onError?: (error: Error) => void | Promise<void>;
|
259
|
+
} = {}
|
260
|
+
): Promise<{
|
261
|
+
transaction: {
|
262
|
+
hash: string;
|
263
|
+
};
|
264
|
+
status: {
|
265
|
+
status: boolean | null;
|
266
|
+
confirmations: number;
|
267
|
+
isCompleted: boolean;
|
268
|
+
attempts: number;
|
269
|
+
};
|
270
|
+
}> {
|
271
|
+
return signAndSendTransaction(
|
272
|
+
tx,
|
273
|
+
wallet,
|
274
|
+
() => this.getRandomProvider(),
|
275
|
+
callbacks,
|
276
|
+
this.contract
|
277
|
+
);
|
278
|
+
}
|
279
|
+
|
280
|
+
// Transaction builders
|
281
|
+
async buildAddWhitelistedTokenTx(token: string, price: number) {
|
282
|
+
const priceBigInt = BigInt(Math.floor(price * this.PRICE_PRECISION));
|
283
|
+
return await this.contract.addWhitelistedToken.populateTransaction(
|
284
|
+
token,
|
285
|
+
priceBigInt
|
286
|
+
);
|
287
|
+
}
|
288
|
+
|
289
|
+
async buildUpdateTokenPriceTx(token: string, newPrice: number) {
|
290
|
+
const priceBigInt = BigInt(Math.floor(newPrice * this.PRICE_PRECISION));
|
291
|
+
return await this.contract.updateTokenPrice.populateTransaction(
|
292
|
+
token,
|
293
|
+
priceBigInt
|
294
|
+
);
|
295
|
+
}
|
296
|
+
|
297
|
+
async buildRemoveWhitelistedTokenTx(token: string) {
|
298
|
+
return await this.contract.removeWhitelistedToken.populateTransaction(
|
299
|
+
token
|
300
|
+
);
|
301
|
+
}
|
302
|
+
|
303
|
+
async buildCreateRoundTx(
|
304
|
+
startTime: number,
|
305
|
+
endTime: number,
|
306
|
+
targetFund: number,
|
307
|
+
totalAllocation: number,
|
308
|
+
maxFundPerAccount: number
|
309
|
+
) {
|
310
|
+
const targetFundBigInt = BigInt(
|
311
|
+
Math.floor(targetFund * this.PRICE_PRECISION)
|
312
|
+
);
|
313
|
+
const maxFundPerAccountBigInt = BigInt(
|
314
|
+
Math.floor(maxFundPerAccount * this.PRICE_PRECISION)
|
315
|
+
);
|
316
|
+
const decimals = await getTokenDecimals(
|
317
|
+
await this.getProjectToken(),
|
318
|
+
this.getRandomProvider()
|
319
|
+
);
|
320
|
+
const totalAllocationBigInt = BigInt(
|
321
|
+
Math.floor(totalAllocation * Math.pow(10, decimals))
|
322
|
+
);
|
323
|
+
return await this.contract.createRound.populateTransaction(
|
324
|
+
startTime,
|
325
|
+
endTime,
|
326
|
+
targetFundBigInt,
|
327
|
+
totalAllocationBigInt,
|
328
|
+
maxFundPerAccountBigInt
|
329
|
+
);
|
330
|
+
}
|
331
|
+
|
332
|
+
async buildUpdateRoundTx(
|
333
|
+
roundId: number,
|
334
|
+
startTime: number,
|
335
|
+
endTime: number,
|
336
|
+
targetFund: number,
|
337
|
+
totalAllocation: number,
|
338
|
+
maxFundPerAccount: number
|
339
|
+
) {
|
340
|
+
const targetFundBigInt = BigInt(
|
341
|
+
Math.floor(targetFund * this.PRICE_PRECISION)
|
342
|
+
);
|
343
|
+
const maxFundPerAccountBigInt = BigInt(
|
344
|
+
Math.floor(maxFundPerAccount * this.PRICE_PRECISION)
|
345
|
+
);
|
346
|
+
return await this.contract.updateRound.populateTransaction(
|
347
|
+
roundId,
|
348
|
+
startTime,
|
349
|
+
endTime,
|
350
|
+
targetFundBigInt,
|
351
|
+
totalAllocation,
|
352
|
+
maxFundPerAccountBigInt
|
353
|
+
);
|
354
|
+
}
|
355
|
+
|
356
|
+
async buildEndRoundTx(roundId: number) {
|
357
|
+
return await this.contract.endRound.populateTransaction(roundId);
|
358
|
+
}
|
359
|
+
|
360
|
+
async buildSetClaimingEnabledTx(roundId: number, enabled: boolean) {
|
361
|
+
return await this.contract.setClaimingEnabled.populateTransaction(
|
362
|
+
roundId,
|
363
|
+
enabled
|
364
|
+
);
|
365
|
+
}
|
366
|
+
|
367
|
+
async buildSetRefundEnabledTx(roundId: number, enabled: boolean) {
|
368
|
+
return await this.contract.setRefundEnabled.populateTransaction(
|
369
|
+
roundId,
|
370
|
+
enabled
|
371
|
+
);
|
372
|
+
}
|
373
|
+
|
374
|
+
async buildContributeTx(roundId: number, token: string, amount: number) {
|
375
|
+
let decimals = 18;
|
376
|
+
|
377
|
+
if (token !== ethers.ZeroAddress) {
|
378
|
+
decimals = await getTokenDecimals(token, this.getRandomProvider());
|
379
|
+
}
|
380
|
+
|
381
|
+
const amountWei = ethers.parseUnits(amount.toString(), decimals);
|
382
|
+
|
383
|
+
const tx = await this.contract.contribute.populateTransaction(
|
384
|
+
roundId,
|
385
|
+
token,
|
386
|
+
token === ethers.ZeroAddress ? 0 : amountWei
|
387
|
+
);
|
388
|
+
|
389
|
+
if (token === ethers.ZeroAddress) {
|
390
|
+
tx.value = amountWei;
|
391
|
+
}
|
392
|
+
|
393
|
+
return tx;
|
394
|
+
}
|
395
|
+
|
396
|
+
async buildClaimTokensTx() {
|
397
|
+
return await this.contract.claimTokens.populateTransaction();
|
398
|
+
}
|
399
|
+
|
400
|
+
async buildRefundTx() {
|
401
|
+
return await this.contract.refund.populateTransaction();
|
402
|
+
}
|
403
|
+
|
404
|
+
async buildWithdrawFundsTx(token: string, amount: number) {
|
405
|
+
const decimals = await getTokenDecimals(token, this.getRandomProvider());
|
406
|
+
const amountBigInt = BigInt(Math.floor(amount * Math.pow(10, decimals)));
|
407
|
+
return await this.contract.withdrawFunds.populateTransaction(
|
408
|
+
token,
|
409
|
+
amountBigInt
|
410
|
+
);
|
411
|
+
}
|
412
|
+
|
413
|
+
async buildClaimTokensByRoundIdTx(roundId: number) {
|
414
|
+
return await this.contract.claimTokensByRoundId.populateTransaction(
|
415
|
+
roundId
|
416
|
+
);
|
417
|
+
}
|
418
|
+
|
419
|
+
// Read methods
|
420
|
+
async getWhitelistedToken(token: string): Promise<WhitelistedToken> {
|
421
|
+
const result = await this.contract.whitelistedTokens(token);
|
422
|
+
return {
|
423
|
+
isWhitelisted: result.isWhitelisted,
|
424
|
+
price: Number(result.price) / this.PRICE_PRECISION,
|
425
|
+
};
|
426
|
+
}
|
427
|
+
|
428
|
+
async getRound(roundId: number): Promise<RoundConfig> {
|
429
|
+
const result = await this.contract.rounds(roundId);
|
430
|
+
return {
|
431
|
+
startTime: Number(result.startTime),
|
432
|
+
endTime: Number(result.endTime),
|
433
|
+
targetFund: Number(result.targetFund) / this.PRICE_PRECISION,
|
434
|
+
totalAllocation: Number(result.totalAllocation),
|
435
|
+
maxFundPerAccount:
|
436
|
+
Number(result.maxFundPerAccount) / this.PRICE_PRECISION,
|
437
|
+
exists: result.exists,
|
438
|
+
ended: result.ended,
|
439
|
+
claimingEnabled: result.claimingEnabled,
|
440
|
+
refundEnabled: result.refundEnabled,
|
441
|
+
};
|
442
|
+
}
|
443
|
+
|
444
|
+
async getUserContribution(
|
445
|
+
roundId: number,
|
446
|
+
user: string
|
447
|
+
): Promise<UserContribution> {
|
448
|
+
const result = await this.contract.userContributions(roundId, user);
|
449
|
+
return {
|
450
|
+
fundAmount: Number(result.fundAmount) / this.PRICE_PRECISION,
|
451
|
+
tokenAllocation: Number(result.tokenAllocation),
|
452
|
+
claimed: result.claimed,
|
453
|
+
refunded: result.refunded,
|
454
|
+
contributedToken: result.contributedToken,
|
455
|
+
contributedAmount: Number(result.contributedAmount),
|
456
|
+
};
|
457
|
+
}
|
458
|
+
|
459
|
+
async getRoundRaisedFunds(roundId: number): Promise<number> {
|
460
|
+
const result = await this.contract.roundRaisedFunds(roundId);
|
461
|
+
return Number(result) / this.PRICE_PRECISION;
|
462
|
+
}
|
463
|
+
|
464
|
+
async getRoundParticipants(roundId: number): Promise<number> {
|
465
|
+
const result = await this.contract.roundParticipants(roundId);
|
466
|
+
return Number(result);
|
467
|
+
}
|
468
|
+
|
469
|
+
async getUserParticipatedRound(user: string): Promise<number> {
|
470
|
+
const result = await this.contract.userParticipatedRound(user);
|
471
|
+
return Number(result);
|
472
|
+
}
|
473
|
+
|
474
|
+
async getProjectToken(): Promise<string> {
|
475
|
+
return await this.contract.projectToken();
|
476
|
+
}
|
477
|
+
|
478
|
+
async getTotalRounds(): Promise<number> {
|
479
|
+
const result = await this.contract.totalRounds();
|
480
|
+
return Number(result);
|
481
|
+
}
|
482
|
+
|
483
|
+
async getTotalRaisedFunds(): Promise<number> {
|
484
|
+
const result = await this.contract.totalRaisedFunds();
|
485
|
+
return Number(result) / this.PRICE_PRECISION;
|
486
|
+
}
|
487
|
+
|
488
|
+
async isRoundActive(roundId: number): Promise<boolean> {
|
489
|
+
return await this.contract.isRoundActive(roundId);
|
490
|
+
}
|
491
|
+
|
492
|
+
/**
|
493
|
+
* Get the ID of the latest round
|
494
|
+
* @returns The ID of the latest round or -1 if no rounds exist
|
495
|
+
*/
|
496
|
+
async getLatestRoundId(): Promise<number> {
|
497
|
+
// Get total number of rounds
|
498
|
+
const totalRounds = await this.getTotalRounds();
|
499
|
+
|
500
|
+
// If no rounds exist, return -1
|
501
|
+
if (totalRounds === 0) {
|
502
|
+
return -1;
|
503
|
+
}
|
504
|
+
|
505
|
+
// Latest round ID (0-indexed, so subtract 1)
|
506
|
+
return totalRounds - 1;
|
507
|
+
}
|
508
|
+
|
509
|
+
/**
|
510
|
+
* Get comprehensive information about a specific round
|
511
|
+
* @param roundId The ID of the round to get information for
|
512
|
+
* @returns An object containing all information about the specified round
|
513
|
+
*/
|
514
|
+
async getRoundInfo(roundId: number): Promise<{
|
515
|
+
roundId: number;
|
516
|
+
config: RoundConfig;
|
517
|
+
raisedFunds: number;
|
518
|
+
participants: number;
|
519
|
+
isActive: boolean;
|
520
|
+
}> {
|
521
|
+
// Get total number of rounds
|
522
|
+
const totalRounds = await this.getTotalRounds();
|
523
|
+
|
524
|
+
// Check if roundId is valid
|
525
|
+
if (roundId < 0 || roundId >= totalRounds) {
|
526
|
+
throw new Error(`Invalid round ID: ${roundId}`);
|
527
|
+
}
|
528
|
+
|
529
|
+
// Get round configuration
|
530
|
+
const roundConfig = await this.getRound(roundId);
|
531
|
+
|
532
|
+
// Get additional information
|
533
|
+
const raisedFunds = await this.getRoundRaisedFunds(roundId);
|
534
|
+
const participants = await this.getRoundParticipants(roundId);
|
535
|
+
const isActive = await this.isRoundActive(roundId);
|
536
|
+
|
537
|
+
return {
|
538
|
+
roundId,
|
539
|
+
config: roundConfig,
|
540
|
+
raisedFunds,
|
541
|
+
participants,
|
542
|
+
isActive,
|
543
|
+
};
|
544
|
+
}
|
545
|
+
|
546
|
+
async getUserTotalContribution(user: string): Promise<{
|
547
|
+
totalFundAmount: number;
|
548
|
+
totalTokenAllocation: number;
|
549
|
+
}> {
|
550
|
+
const result = await this.contract.getUserTotalContribution(user);
|
551
|
+
return {
|
552
|
+
totalFundAmount: Number(result.totalFundAmount) / this.PRICE_PRECISION,
|
553
|
+
totalTokenAllocation: Number(result.totalTokenAllocation),
|
554
|
+
};
|
555
|
+
}
|
556
|
+
|
557
|
+
async getTransactionStatus(
|
558
|
+
txHash: string,
|
559
|
+
maxRetries: number = 10
|
560
|
+
): Promise<{
|
561
|
+
hash: string;
|
562
|
+
status: boolean | null;
|
563
|
+
confirmations: number;
|
564
|
+
isCompleted: boolean;
|
565
|
+
attempts: number;
|
566
|
+
}> {
|
567
|
+
return await getTransactionStatus(
|
568
|
+
this.getRandomProvider(),
|
569
|
+
txHash,
|
570
|
+
maxRetries
|
571
|
+
);
|
572
|
+
}
|
573
|
+
|
574
|
+
getContractAddress(): string {
|
575
|
+
return this.contractAddress;
|
576
|
+
}
|
577
|
+
|
578
|
+
async getAllEvents(
|
579
|
+
fromBlock: number,
|
580
|
+
toBlock: number
|
581
|
+
): Promise<ParsedSeedRoundFundraiserEventRaw[]> {
|
582
|
+
await this.bootstrap();
|
583
|
+
return getAllEvents<ParsedSeedRoundFundraiserEventRaw, SeedRoundFundraiser>(
|
584
|
+
this.contract,
|
585
|
+
() => this.getRandomProvider(),
|
586
|
+
() => this.getContractWithRandomProvider(),
|
587
|
+
fromBlock,
|
588
|
+
toBlock
|
589
|
+
);
|
590
|
+
}
|
591
|
+
|
592
|
+
async streamEvents(
|
593
|
+
fromBlock: number,
|
594
|
+
onEvent: (event: ParsedSeedRoundFundraiserEvent) => Promise<void>,
|
595
|
+
saveLatestBlock: (blockNumber: number) => Promise<void>,
|
596
|
+
batchSize: number = 1000,
|
597
|
+
sleepTime: number = 5000
|
598
|
+
) {
|
599
|
+
await this.bootstrap();
|
600
|
+
|
601
|
+
return streamEvents<
|
602
|
+
ParsedSeedRoundFundraiserEventRaw,
|
603
|
+
ParsedSeedRoundFundraiserEvent
|
604
|
+
>({
|
605
|
+
getProvider: () => this.getRandomProvider(),
|
606
|
+
getAllEvents: (fromBlock, toBlock) =>
|
607
|
+
this.getAllEvents(fromBlock, toBlock),
|
608
|
+
formatEvent: (event) => this.formatEventArgs(event),
|
609
|
+
onEvent,
|
610
|
+
saveLatestBlock,
|
611
|
+
fromBlock,
|
612
|
+
batchSize,
|
613
|
+
sleepTime,
|
614
|
+
});
|
615
|
+
}
|
616
|
+
|
617
|
+
formatEventArgs = (
|
618
|
+
event: ParsedSeedRoundFundraiserEventRaw
|
619
|
+
): ParsedSeedRoundFundraiserEvent => {
|
620
|
+
const { eventName, args } = event;
|
621
|
+
|
622
|
+
switch (eventName) {
|
623
|
+
case "TokenWhitelisted":
|
624
|
+
return {
|
625
|
+
...event,
|
626
|
+
args: {
|
627
|
+
...args,
|
628
|
+
token: args.token.toLowerCase(),
|
629
|
+
price: Number(args.price) / this.PRICE_PRECISION,
|
630
|
+
},
|
631
|
+
};
|
632
|
+
|
633
|
+
case "TokenPriceUpdated":
|
634
|
+
return {
|
635
|
+
...event,
|
636
|
+
args: {
|
637
|
+
...args,
|
638
|
+
token: args.token.toLowerCase(),
|
639
|
+
oldPrice: Number(args.oldPrice) / this.PRICE_PRECISION,
|
640
|
+
newPrice: Number(args.newPrice) / this.PRICE_PRECISION,
|
641
|
+
},
|
642
|
+
};
|
643
|
+
|
644
|
+
case "TokenRemovedFromWhitelist":
|
645
|
+
return {
|
646
|
+
...event,
|
647
|
+
args: {
|
648
|
+
...args,
|
649
|
+
token: args.token.toLowerCase(),
|
650
|
+
},
|
651
|
+
};
|
652
|
+
|
653
|
+
case "RoundCreated":
|
654
|
+
case "RoundUpdated":
|
655
|
+
return {
|
656
|
+
...event,
|
657
|
+
args: {
|
658
|
+
...args,
|
659
|
+
roundId: Number(args.roundId),
|
660
|
+
startTime: Number(args.startTime),
|
661
|
+
endTime: Number(args.endTime),
|
662
|
+
targetFund: Number(args.targetFund) / this.PRICE_PRECISION,
|
663
|
+
totalAllocation: Number(args.totalAllocation),
|
664
|
+
maxFundPerAccount:
|
665
|
+
Number(args.maxFundPerAccount) / this.PRICE_PRECISION,
|
666
|
+
},
|
667
|
+
};
|
668
|
+
|
669
|
+
case "RoundEnded":
|
670
|
+
return {
|
671
|
+
...event,
|
672
|
+
args: {
|
673
|
+
...args,
|
674
|
+
roundId: Number(args.roundId),
|
675
|
+
raisedFunds: Number(args.raisedFunds) / this.PRICE_PRECISION,
|
676
|
+
participants: Number(args.participants),
|
677
|
+
},
|
678
|
+
};
|
679
|
+
|
680
|
+
case "Contribution":
|
681
|
+
return {
|
682
|
+
...event,
|
683
|
+
args: {
|
684
|
+
...args,
|
685
|
+
roundId: Number(args.roundId),
|
686
|
+
contributor: args.contributor.toLowerCase(),
|
687
|
+
token: args.token.toLowerCase(),
|
688
|
+
amount: Number(args.amount),
|
689
|
+
fundAmount: Number(args.fundAmount) / this.PRICE_PRECISION,
|
690
|
+
tokenAllocation: Number(args.tokenAllocation),
|
691
|
+
},
|
692
|
+
};
|
693
|
+
|
694
|
+
case "TokensClaimed":
|
695
|
+
return {
|
696
|
+
...event,
|
697
|
+
args: {
|
698
|
+
...args,
|
699
|
+
roundId: Number(args.roundId),
|
700
|
+
user: args.user.toLowerCase(),
|
701
|
+
amount: Number(args.amount),
|
702
|
+
},
|
703
|
+
};
|
704
|
+
|
705
|
+
case "ProjectTokenUpdated":
|
706
|
+
return {
|
707
|
+
...event,
|
708
|
+
args: {
|
709
|
+
...args,
|
710
|
+
oldToken: args.oldToken.toLowerCase(),
|
711
|
+
newToken: args.newToken.toLowerCase(),
|
712
|
+
},
|
713
|
+
};
|
714
|
+
|
715
|
+
case "ClaimingEnabledUpdated":
|
716
|
+
case "RefundEnabledUpdated":
|
717
|
+
return {
|
718
|
+
...event,
|
719
|
+
args: {
|
720
|
+
...args,
|
721
|
+
roundId: Number(args.roundId),
|
722
|
+
enabled: args.enabled,
|
723
|
+
},
|
724
|
+
};
|
725
|
+
|
726
|
+
case "Refunded":
|
727
|
+
return {
|
728
|
+
...event,
|
729
|
+
args: {
|
730
|
+
...args,
|
731
|
+
roundId: Number(args.roundId),
|
732
|
+
user: args.user.toLowerCase(),
|
733
|
+
token: args.token.toLowerCase(),
|
734
|
+
amount: Number(args.amount),
|
735
|
+
},
|
736
|
+
};
|
737
|
+
|
738
|
+
default:
|
739
|
+
return event;
|
740
|
+
}
|
741
|
+
};
|
742
|
+
}
|