@glowlabs-org/utils 0.2.95 → 0.2.97
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/cjs/browser.d.ts +1 -0
- package/dist/cjs/browser.js +6 -1
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/constants/addresses.d.ts +1 -1
- package/dist/cjs/{farms-router-DCCy-acA.js → farms-router-CDsqKkP6.js} +1093 -210
- package/dist/cjs/farms-router-CDsqKkP6.js.map +1 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/abis/offchainFractions.d.ts +473 -0
- package/dist/cjs/lib/control-api/farms-router.d.ts +2 -1
- package/dist/cjs/lib/hooks/use-offchain-fractions.d.ts +60 -0
- package/dist/cjs/lib/types/index.d.ts +64 -0
- package/dist/esm/browser.d.ts +1 -0
- package/dist/esm/browser.js +2 -2
- package/dist/esm/constants/addresses.d.ts +1 -1
- package/dist/esm/{farms-router-XDjQOQ-c.js → farms-router-Da1P3B8g.js} +1093 -211
- package/dist/esm/farms-router-Da1P3B8g.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/lib/abis/offchainFractions.d.ts +473 -0
- package/dist/esm/lib/control-api/farms-router.d.ts +2 -1
- package/dist/esm/lib/hooks/use-offchain-fractions.d.ts +60 -0
- package/dist/esm/lib/types/index.d.ts +64 -0
- package/package.json +1 -1
- package/src/browser.ts +1 -0
- package/src/constants/addresses.ts +7 -1
- package/src/index.ts +1 -0
- package/src/lib/abis/offchainFractions.ts +332 -0
- package/src/lib/control-api/farms-router.ts +37 -2
- package/src/lib/hooks/use-offchain-fractions.ts +737 -0
- package/src/lib/types/index.ts +69 -0
- package/dist/cjs/farms-router-DCCy-acA.js.map +0 -1
- package/dist/esm/farms-router-XDjQOQ-c.js.map +0 -1
|
@@ -207,6 +207,8 @@ const mainnetAddresses = {
|
|
|
207
207
|
USDG_UNISWAP: "0xe010ec500720bE9EF3F82129E7eD2Ee1FB7955F2",
|
|
208
208
|
GLW_UNISWAP: "0xf4fbC617A5733EAAF9af08E1Ab816B103388d8B6",
|
|
209
209
|
FORWARDER: "0x240CBe07a047ce484DCa2E3Ae15d4907Aba41BE2",
|
|
210
|
+
OFFCHAIN_FRACTIONS: "0x1Eca5C4391f10097C3232a0672e83c60A7dab7cD", // TODO: change to mainnet address
|
|
211
|
+
COUNTERFACTUAL_HOLDER_FACTORY: "0x2c3AB887746F6f4a8a4b9Db6aC800eb71945509A", // TODO: change to mainnet address
|
|
210
212
|
FOUNDATION_WALLET: "0xc5174BBf649a92F9941e981af68AaA14Dd814F85",
|
|
211
213
|
UNISWAP_V2_ROUTER: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
|
|
212
214
|
UNISWAP_V2_FACTORY: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
|
|
@@ -221,6 +223,8 @@ const sepoliaAddresses = {
|
|
|
221
223
|
USDG_UNISWAP: "0x2a085A3aEA8982396533327c854753Ce521B666d",
|
|
222
224
|
GLW_UNISWAP: "0x8e27016D0B866a56CE74A1a280c749dD679bb0Fa",
|
|
223
225
|
FORWARDER: "0xDaC24F18171224eeaf6a9B38f5C5081aDbDff969",
|
|
226
|
+
OFFCHAIN_FRACTIONS: "0x1Eca5C4391f10097C3232a0672e83c60A7dab7cD",
|
|
227
|
+
COUNTERFACTUAL_HOLDER_FACTORY: "0x2c3AB887746F6f4a8a4b9Db6aC800eb71945509A",
|
|
224
228
|
FOUNDATION_WALLET: "0x5e230FED487c86B90f6508104149F087d9B1B0A7",
|
|
225
229
|
UNISWAP_V2_ROUTER: "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3",
|
|
226
230
|
UNISWAP_V2_FACTORY: "0xF62c03E08ada871A0bEb309762E260a7a6a880E6",
|
|
@@ -277,7 +281,7 @@ var ForwarderError;
|
|
|
277
281
|
ForwarderError["MISSING_REQUIRED_PARAMS"] = "Missing required parameters";
|
|
278
282
|
})(ForwarderError || (ForwarderError = {}));
|
|
279
283
|
// Utility to extract the most useful revert reason from an ethers error object
|
|
280
|
-
function parseEthersError(error) {
|
|
284
|
+
function parseEthersError$1(error) {
|
|
281
285
|
if (!error)
|
|
282
286
|
return "Unknown error";
|
|
283
287
|
const possibleError = error;
|
|
@@ -304,7 +308,7 @@ function parseEthersError(error) {
|
|
|
304
308
|
return ForwarderError.UNKNOWN_ERROR;
|
|
305
309
|
}
|
|
306
310
|
// Type-guard style helper to ensure a signer exists throughout the rest of the function.
|
|
307
|
-
function assertSigner(maybeSigner) {
|
|
311
|
+
function assertSigner$1(maybeSigner) {
|
|
308
312
|
if (!maybeSigner) {
|
|
309
313
|
throw new Error(ForwarderError.SIGNER_NOT_AVAILABLE);
|
|
310
314
|
}
|
|
@@ -319,7 +323,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
319
323
|
};
|
|
320
324
|
// Returns a contract instance for Forwarder
|
|
321
325
|
function getForwarderContract() {
|
|
322
|
-
assertSigner(signer);
|
|
326
|
+
assertSigner$1(signer);
|
|
323
327
|
return new Contract(ADDRESSES.FORWARDER, FORWARDER_ABI, signer);
|
|
324
328
|
}
|
|
325
329
|
/**
|
|
@@ -381,7 +385,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
381
385
|
* Get the appropriate token contract based on currency
|
|
382
386
|
*/
|
|
383
387
|
function getTokenContract(currency = "USDC") {
|
|
384
|
-
assertSigner(signer);
|
|
388
|
+
assertSigner$1(signer);
|
|
385
389
|
let tokenAddress;
|
|
386
390
|
switch (currency) {
|
|
387
391
|
case "USDC":
|
|
@@ -404,7 +408,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
404
408
|
* @param currency The currency to check allowance for
|
|
405
409
|
*/
|
|
406
410
|
async function checkTokenAllowance(owner, currency = "USDC") {
|
|
407
|
-
assertSigner(signer);
|
|
411
|
+
assertSigner$1(signer);
|
|
408
412
|
try {
|
|
409
413
|
const tokenContract = getTokenContract(currency);
|
|
410
414
|
if (!tokenContract)
|
|
@@ -413,7 +417,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
413
417
|
return allowance;
|
|
414
418
|
}
|
|
415
419
|
catch (error) {
|
|
416
|
-
throw new Error(parseEthersError(error));
|
|
420
|
+
throw new Error(parseEthersError$1(error));
|
|
417
421
|
}
|
|
418
422
|
}
|
|
419
423
|
/**
|
|
@@ -422,7 +426,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
422
426
|
* @param currency The currency to check balance for
|
|
423
427
|
*/
|
|
424
428
|
async function checkTokenBalance(owner, currency = "USDC") {
|
|
425
|
-
assertSigner(signer);
|
|
429
|
+
assertSigner$1(signer);
|
|
426
430
|
try {
|
|
427
431
|
const tokenContract = getTokenContract(currency);
|
|
428
432
|
if (!tokenContract)
|
|
@@ -431,7 +435,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
431
435
|
return balance;
|
|
432
436
|
}
|
|
433
437
|
catch (error) {
|
|
434
|
-
throw new Error(parseEthersError(error));
|
|
438
|
+
throw new Error(parseEthersError$1(error));
|
|
435
439
|
}
|
|
436
440
|
}
|
|
437
441
|
/**
|
|
@@ -440,7 +444,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
440
444
|
* @param currency The currency to approve
|
|
441
445
|
*/
|
|
442
446
|
async function approveToken(amount, currency = "USDC") {
|
|
443
|
-
assertSigner(signer);
|
|
447
|
+
assertSigner$1(signer);
|
|
444
448
|
try {
|
|
445
449
|
const tokenContract = getTokenContract(currency);
|
|
446
450
|
if (!tokenContract)
|
|
@@ -452,7 +456,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
452
456
|
return true;
|
|
453
457
|
}
|
|
454
458
|
catch (error) {
|
|
455
|
-
throw new Error(parseEthersError(error));
|
|
459
|
+
throw new Error(parseEthersError$1(error));
|
|
456
460
|
}
|
|
457
461
|
finally {
|
|
458
462
|
setIsProcessing(false);
|
|
@@ -463,7 +467,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
463
467
|
* @param params Forward parameters including type, amount, and required fields
|
|
464
468
|
*/
|
|
465
469
|
async function forwardTokens(params) {
|
|
466
|
-
assertSigner(signer);
|
|
470
|
+
assertSigner$1(signer);
|
|
467
471
|
try {
|
|
468
472
|
const forwarderContract = getForwarderContract();
|
|
469
473
|
if (!forwarderContract)
|
|
@@ -495,7 +499,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
495
499
|
await approveTx.wait();
|
|
496
500
|
}
|
|
497
501
|
catch (approveError) {
|
|
498
|
-
throw new Error(parseEthersError(approveError) || "Token approval failed");
|
|
502
|
+
throw new Error(parseEthersError$1(approveError) || "Token approval failed");
|
|
499
503
|
}
|
|
500
504
|
}
|
|
501
505
|
// Get the token address based on currency
|
|
@@ -532,213 +536,1064 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
532
536
|
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message, { from: owner });
|
|
533
537
|
}
|
|
534
538
|
}
|
|
539
|
+
catch (staticError) {
|
|
540
|
+
throw new Error(parseEthersError$1(staticError));
|
|
541
|
+
}
|
|
542
|
+
// Execute the forward transaction
|
|
543
|
+
let tx;
|
|
544
|
+
if (!isAuditFees && currency === "USDC") {
|
|
545
|
+
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message);
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
tx = await forwarderContract.getFunction("forward")(tokenAddress, isAuditFees
|
|
549
|
+
? ADDRESSES.AUDIT_FEE_WALLET
|
|
550
|
+
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
551
|
+
}
|
|
552
|
+
await tx.wait();
|
|
553
|
+
return tx.hash;
|
|
554
|
+
}
|
|
555
|
+
catch (txError) {
|
|
556
|
+
throw new Error(parseEthersError$1(txError));
|
|
557
|
+
}
|
|
558
|
+
finally {
|
|
559
|
+
setIsProcessing(false);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Forward tokens for protocol fee payment and GCTL minting with staking
|
|
564
|
+
*/
|
|
565
|
+
async function payProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, regionId, currency = "USDC") {
|
|
566
|
+
assertSigner$1(signer);
|
|
567
|
+
// GCTL minting only supports USDC and USDG
|
|
568
|
+
if (currency === "GLW") {
|
|
569
|
+
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
570
|
+
}
|
|
571
|
+
return forwardTokens({
|
|
572
|
+
amount,
|
|
573
|
+
userAddress,
|
|
574
|
+
type: TRANSFER_TYPES.PayProtocolFeeAndMintGCTLAndStake,
|
|
575
|
+
currency,
|
|
576
|
+
applicationId,
|
|
577
|
+
regionId,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Forward tokens for protocol fee payment and GCTL minting with staking
|
|
582
|
+
*/
|
|
583
|
+
async function sponsorProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, currency = "USDC") {
|
|
584
|
+
assertSigner$1(signer);
|
|
585
|
+
if (currency === "GLW") {
|
|
586
|
+
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
587
|
+
}
|
|
588
|
+
return forwardTokens({
|
|
589
|
+
amount,
|
|
590
|
+
userAddress,
|
|
591
|
+
type: TRANSFER_TYPES.SponsorProtocolFeeAndMintGCTLAndStake,
|
|
592
|
+
currency,
|
|
593
|
+
applicationId,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Forward tokens for protocol fee payment only
|
|
598
|
+
*/
|
|
599
|
+
async function payProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
|
|
600
|
+
assertSigner$1(signer);
|
|
601
|
+
return forwardTokens({
|
|
602
|
+
amount,
|
|
603
|
+
userAddress,
|
|
604
|
+
type: TRANSFER_TYPES.PayProtocolFee,
|
|
605
|
+
currency,
|
|
606
|
+
applicationId,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Forward tokens for protocol fee payment only
|
|
611
|
+
*/
|
|
612
|
+
async function sponsorProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
|
|
613
|
+
assertSigner$1(signer);
|
|
614
|
+
return forwardTokens({
|
|
615
|
+
amount,
|
|
616
|
+
userAddress,
|
|
617
|
+
type: TRANSFER_TYPES.SponsorProtocolFee,
|
|
618
|
+
currency,
|
|
619
|
+
applicationId,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Forward USDC to mint GCTL and stake to a region
|
|
624
|
+
*/
|
|
625
|
+
async function mintGCTLAndStake(amount, userAddress, regionId, currency = "USDC") {
|
|
626
|
+
assertSigner$1(signer);
|
|
627
|
+
// GCTL minting only supports USDC and USDG
|
|
628
|
+
if (currency === "GLW") {
|
|
629
|
+
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
630
|
+
}
|
|
631
|
+
return forwardTokens({
|
|
632
|
+
amount,
|
|
633
|
+
userAddress,
|
|
634
|
+
type: TRANSFER_TYPES.MintGCTLAndStake,
|
|
635
|
+
currency,
|
|
636
|
+
regionId,
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Forward USDC to mint GCTL (existing functionality, keeping for compatibility)
|
|
641
|
+
*/
|
|
642
|
+
async function mintGCTL(amount, userAddress, currency = "USDC") {
|
|
643
|
+
assertSigner$1(signer);
|
|
644
|
+
// GCTL minting only supports USDC and USDG
|
|
645
|
+
if (currency === "GLW") {
|
|
646
|
+
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
647
|
+
}
|
|
648
|
+
return forwardTokens({
|
|
649
|
+
amount,
|
|
650
|
+
userAddress,
|
|
651
|
+
type: TRANSFER_TYPES.MintGCTL,
|
|
652
|
+
currency,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Forward tokens to pay audit fees (USDC only, calls forward())
|
|
657
|
+
*/
|
|
658
|
+
async function payAuditFees(amount, userAddress, applicationId) {
|
|
659
|
+
assertSigner$1(signer);
|
|
660
|
+
return forwardTokens({
|
|
661
|
+
amount,
|
|
662
|
+
userAddress,
|
|
663
|
+
type: TRANSFER_TYPES.PayAuditFees,
|
|
664
|
+
currency: "USDC",
|
|
665
|
+
applicationId,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Forward tokens to buy a solar farm
|
|
670
|
+
*/
|
|
671
|
+
async function buySolarFarm(amount, userAddress, farmId, currency = "USDC") {
|
|
672
|
+
assertSigner$1(signer);
|
|
673
|
+
return forwardTokens({
|
|
674
|
+
amount,
|
|
675
|
+
userAddress,
|
|
676
|
+
type: TRANSFER_TYPES.BuySolarFarm,
|
|
677
|
+
currency,
|
|
678
|
+
farmId,
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Forward tokens to commit to a Kickstarter (USDC or USDG only)
|
|
683
|
+
*/
|
|
684
|
+
async function commitKickstarter(amount, userAddress, kickstarterId, currency = "USDC") {
|
|
685
|
+
assertSigner$1(signer);
|
|
686
|
+
if (currency === "GLW") {
|
|
687
|
+
throw new Error("CommitKickstarter supports only USDC or USDG");
|
|
688
|
+
}
|
|
689
|
+
return forwardTokens({
|
|
690
|
+
amount,
|
|
691
|
+
userAddress,
|
|
692
|
+
type: TRANSFER_TYPES.CommitKickstarter,
|
|
693
|
+
currency,
|
|
694
|
+
kickstarterId,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Estimate gas for forwarding with type-specific handling
|
|
699
|
+
* @param params Forward parameters
|
|
700
|
+
* @param ethPriceInUSD Current ETH price in USD (for cost estimation)
|
|
701
|
+
*/
|
|
702
|
+
async function estimateGasForForward(params, ethPriceInUSD) {
|
|
703
|
+
assertSigner$1(signer);
|
|
704
|
+
try {
|
|
705
|
+
const forwarderContract = getForwarderContract();
|
|
706
|
+
if (!forwarderContract)
|
|
707
|
+
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
708
|
+
const { amount, currency = "USDC" } = params;
|
|
709
|
+
const isAuditFees = params.type === TRANSFER_TYPES.PayAuditFees;
|
|
710
|
+
if (isAuditFees && currency !== "USDC") {
|
|
711
|
+
throw new Error("PayAuditFees only supports USDC");
|
|
712
|
+
}
|
|
713
|
+
// Construct the appropriate message for this forward type
|
|
714
|
+
const message = constructForwardMessage(params);
|
|
715
|
+
// Determine sendToCounterfactualWallet based on currency
|
|
716
|
+
const sendToCounterfactualWallet = currency === "GLW" || currency === "USDG";
|
|
717
|
+
// Get token address
|
|
718
|
+
let tokenAddress;
|
|
719
|
+
switch (currency) {
|
|
720
|
+
case "USDC":
|
|
721
|
+
tokenAddress = ADDRESSES.USDC;
|
|
722
|
+
break;
|
|
723
|
+
case "USDG":
|
|
724
|
+
tokenAddress = ADDRESSES.USDG;
|
|
725
|
+
break;
|
|
726
|
+
case "GLW":
|
|
727
|
+
tokenAddress = ADDRESSES.GLW;
|
|
728
|
+
break;
|
|
729
|
+
default:
|
|
730
|
+
throw new Error(`Unsupported currency for gas estimation: ${currency}`);
|
|
731
|
+
}
|
|
732
|
+
const feeData = await signer.provider?.getFeeData();
|
|
733
|
+
const gasPrice = feeData?.gasPrice ?? feeData?.maxFeePerGas ?? 0n;
|
|
734
|
+
if (gasPrice === 0n) {
|
|
735
|
+
throw new Error("Could not fetch gas price to estimate cost.");
|
|
736
|
+
}
|
|
737
|
+
const estimatedGas = !isAuditFees && currency === "USDC"
|
|
738
|
+
? await forwarderContract
|
|
739
|
+
.getFunction("swapUSDCAndForwardUSDG")
|
|
740
|
+
.estimateGas(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message)
|
|
741
|
+
: await forwarderContract
|
|
742
|
+
.getFunction("forward")
|
|
743
|
+
.estimateGas(tokenAddress, isAuditFees
|
|
744
|
+
? ADDRESSES.AUDIT_FEE_WALLET
|
|
745
|
+
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
746
|
+
const estimatedCost = estimatedGas * gasPrice;
|
|
747
|
+
if (ethPriceInUSD) {
|
|
748
|
+
const estimatedCostInEth = formatEther(estimatedCost);
|
|
749
|
+
const estimatedCostInUSD = (parseFloat(estimatedCostInEth) * ethPriceInUSD).toFixed(2);
|
|
750
|
+
return estimatedCostInUSD;
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
throw new Error("Could not fetch the ETH price to calculate cost in USD.");
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
throw new Error(parseEthersError$1(error));
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Mint test USDC (only works on testnets with mintable USDC contracts)
|
|
762
|
+
* @param amount Amount of USDC to mint (BigNumber, 6 decimals)
|
|
763
|
+
* @param recipient Address to mint USDC to
|
|
764
|
+
*/
|
|
765
|
+
async function mintTestUSDC(amount, recipient) {
|
|
766
|
+
assertSigner$1(signer);
|
|
767
|
+
if (CHAIN_ID !== 11155111) {
|
|
768
|
+
throw new Error("Minting test USDC is only supported on Sepolia");
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
const usdcContract = getTokenContract("USDC"); // Use getTokenContract for consistency
|
|
772
|
+
if (!usdcContract)
|
|
773
|
+
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
774
|
+
setIsProcessing(true);
|
|
775
|
+
// Try to call mint function (common for test tokens)
|
|
776
|
+
const tx = await usdcContract.mint(recipient, amount);
|
|
777
|
+
await tx.wait();
|
|
778
|
+
return tx.hash;
|
|
779
|
+
}
|
|
780
|
+
catch (error) {
|
|
781
|
+
// If mint function doesn't exist or fails, provide helpful error
|
|
782
|
+
const errorMessage = parseEthersError$1(error);
|
|
783
|
+
if (errorMessage.includes("mint")) {
|
|
784
|
+
throw new Error("This USDC contract doesn't support minting");
|
|
785
|
+
}
|
|
786
|
+
throw new Error(errorMessage);
|
|
787
|
+
}
|
|
788
|
+
finally {
|
|
789
|
+
setIsProcessing(false);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
// New methods for different forward types
|
|
794
|
+
forwardTokens,
|
|
795
|
+
payProtocolFeeAndMintGCTLAndStake,
|
|
796
|
+
sponsorProtocolFeeAndMintGCTLAndStake,
|
|
797
|
+
sponsorProtocolFee,
|
|
798
|
+
payProtocolFee,
|
|
799
|
+
mintGCTLAndStake,
|
|
800
|
+
mintGCTL,
|
|
801
|
+
buySolarFarm,
|
|
802
|
+
commitKickstarter,
|
|
803
|
+
payAuditFees,
|
|
804
|
+
// Token operations
|
|
805
|
+
approveToken,
|
|
806
|
+
checkTokenAllowance,
|
|
807
|
+
checkTokenBalance,
|
|
808
|
+
// Utility methods
|
|
809
|
+
estimateGasForForward,
|
|
810
|
+
mintTestUSDC,
|
|
811
|
+
constructForwardMessage,
|
|
812
|
+
// State
|
|
813
|
+
get isProcessing() {
|
|
814
|
+
return isProcessing;
|
|
815
|
+
},
|
|
816
|
+
addresses: ADDRESSES,
|
|
817
|
+
// Signer availability
|
|
818
|
+
isSignerAvailable: !!signer,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const OFFCHAIN_FRACTIONS_ABI = [
|
|
823
|
+
{
|
|
824
|
+
inputs: [
|
|
825
|
+
{
|
|
826
|
+
internalType: "contract CounterfactualHolderFactory",
|
|
827
|
+
name: "_counterfactualHolderFactory",
|
|
828
|
+
type: "address",
|
|
829
|
+
},
|
|
830
|
+
],
|
|
831
|
+
stateMutability: "nonpayable",
|
|
832
|
+
type: "constructor",
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
inputs: [{ internalType: "address", name: "target", type: "address" }],
|
|
836
|
+
name: "AddressEmptyCode",
|
|
837
|
+
type: "error",
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
inputs: [{ internalType: "address", name: "account", type: "address" }],
|
|
841
|
+
name: "AddressInsufficientBalance",
|
|
842
|
+
type: "error",
|
|
843
|
+
},
|
|
844
|
+
{ inputs: [], name: "AlreadyClaimed", type: "error" },
|
|
845
|
+
{ inputs: [], name: "AlreadyClosed", type: "error" },
|
|
846
|
+
{ inputs: [], name: "AlreadyExists", type: "error" },
|
|
847
|
+
{ inputs: [], name: "AlreadySent", type: "error" },
|
|
848
|
+
{
|
|
849
|
+
inputs: [],
|
|
850
|
+
name: "CannotClaimPayoutWhenRoundNotFullyFilled",
|
|
851
|
+
type: "error",
|
|
852
|
+
},
|
|
853
|
+
{ inputs: [], name: "CannotClaimRefundWhenNotExpired", type: "error" },
|
|
854
|
+
{ inputs: [], name: "CannotClaimRefundWhenRoundFullyFilled", type: "error" },
|
|
855
|
+
{ inputs: [], name: "CannotCloseAFullRound", type: "error" },
|
|
856
|
+
{ inputs: [], name: "CannotHaveZeroTotalSteps", type: "error" },
|
|
857
|
+
{ inputs: [], name: "Expired", type: "error" },
|
|
858
|
+
{ inputs: [], name: "FailedInnerCall", type: "error" },
|
|
859
|
+
{ inputs: [], name: "InsufficientSharesAvailable", type: "error" },
|
|
860
|
+
{ inputs: [], name: "InvalidToAddress", type: "error" },
|
|
861
|
+
{ inputs: [], name: "InvalidToken", type: "error" },
|
|
862
|
+
{ inputs: [], name: "MinSharesCannotBeGreaterThanTotalSteps", type: "error" },
|
|
863
|
+
{ inputs: [], name: "NoStepsPurchased", type: "error" },
|
|
864
|
+
{ inputs: [], name: "NotAllOrNothing", type: "error" },
|
|
865
|
+
{ inputs: [], name: "NotFractionsOwner", type: "error" },
|
|
866
|
+
{ inputs: [], name: "RecipientCannotBeSelf", type: "error" },
|
|
867
|
+
{ inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" },
|
|
868
|
+
{
|
|
869
|
+
inputs: [{ internalType: "address", name: "token", type: "address" }],
|
|
870
|
+
name: "SafeERC20FailedOperation",
|
|
871
|
+
type: "error",
|
|
872
|
+
},
|
|
873
|
+
{ inputs: [], name: "StepMustBeGreaterThanZero", type: "error" },
|
|
874
|
+
{ inputs: [], name: "TaxTokenNotSupported", type: "error" },
|
|
875
|
+
{ inputs: [], name: "TotalRaisedOverflow", type: "error" },
|
|
876
|
+
{ inputs: [], name: "ZeroSteps", type: "error" },
|
|
877
|
+
{
|
|
878
|
+
anonymous: false,
|
|
879
|
+
inputs: [
|
|
880
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
881
|
+
{
|
|
882
|
+
indexed: true,
|
|
883
|
+
internalType: "address",
|
|
884
|
+
name: "token",
|
|
885
|
+
type: "address",
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
indexed: true,
|
|
889
|
+
internalType: "address",
|
|
890
|
+
name: "owner",
|
|
891
|
+
type: "address",
|
|
892
|
+
},
|
|
893
|
+
],
|
|
894
|
+
name: "FractionClosed",
|
|
895
|
+
type: "event",
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
anonymous: false,
|
|
899
|
+
inputs: [
|
|
900
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
901
|
+
{
|
|
902
|
+
indexed: true,
|
|
903
|
+
internalType: "address",
|
|
904
|
+
name: "token",
|
|
905
|
+
type: "address",
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
indexed: true,
|
|
909
|
+
internalType: "address",
|
|
910
|
+
name: "owner",
|
|
911
|
+
type: "address",
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
indexed: false,
|
|
915
|
+
internalType: "uint256",
|
|
916
|
+
name: "step",
|
|
917
|
+
type: "uint256",
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
indexed: false,
|
|
921
|
+
internalType: "uint256",
|
|
922
|
+
name: "totalSteps",
|
|
923
|
+
type: "uint256",
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
indexed: false,
|
|
927
|
+
internalType: "uint48",
|
|
928
|
+
name: "expiration",
|
|
929
|
+
type: "uint48",
|
|
930
|
+
},
|
|
931
|
+
{ indexed: false, internalType: "address", name: "to", type: "address" },
|
|
932
|
+
{
|
|
933
|
+
indexed: false,
|
|
934
|
+
internalType: "bool",
|
|
935
|
+
name: "useCounterfactualAddress",
|
|
936
|
+
type: "bool",
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
indexed: false,
|
|
940
|
+
internalType: "uint256",
|
|
941
|
+
name: "minSharesToRaise",
|
|
942
|
+
type: "uint256",
|
|
943
|
+
},
|
|
944
|
+
],
|
|
945
|
+
name: "FractionCreated",
|
|
946
|
+
type: "event",
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
anonymous: false,
|
|
950
|
+
inputs: [
|
|
951
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
952
|
+
{
|
|
953
|
+
indexed: true,
|
|
954
|
+
internalType: "address",
|
|
955
|
+
name: "creator",
|
|
956
|
+
type: "address",
|
|
957
|
+
},
|
|
958
|
+
{ indexed: true, internalType: "address", name: "user", type: "address" },
|
|
959
|
+
{
|
|
960
|
+
indexed: false,
|
|
961
|
+
internalType: "uint256",
|
|
962
|
+
name: "amount",
|
|
963
|
+
type: "uint256",
|
|
964
|
+
},
|
|
965
|
+
],
|
|
966
|
+
name: "FractionRefunded",
|
|
967
|
+
type: "event",
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
anonymous: false,
|
|
971
|
+
inputs: [
|
|
972
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
973
|
+
{
|
|
974
|
+
indexed: true,
|
|
975
|
+
internalType: "address",
|
|
976
|
+
name: "creator",
|
|
977
|
+
type: "address",
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
indexed: true,
|
|
981
|
+
internalType: "address",
|
|
982
|
+
name: "buyer",
|
|
983
|
+
type: "address",
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
indexed: false,
|
|
987
|
+
internalType: "uint256",
|
|
988
|
+
name: "step",
|
|
989
|
+
type: "uint256",
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
indexed: false,
|
|
993
|
+
internalType: "uint256",
|
|
994
|
+
name: "amount",
|
|
995
|
+
type: "uint256",
|
|
996
|
+
},
|
|
997
|
+
],
|
|
998
|
+
name: "FractionSold",
|
|
999
|
+
type: "event",
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
anonymous: false,
|
|
1003
|
+
inputs: [
|
|
1004
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1005
|
+
{
|
|
1006
|
+
indexed: true,
|
|
1007
|
+
internalType: "address",
|
|
1008
|
+
name: "creator",
|
|
1009
|
+
type: "address",
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
indexed: false,
|
|
1013
|
+
internalType: "uint256",
|
|
1014
|
+
name: "minShares",
|
|
1015
|
+
type: "uint256",
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
indexed: false,
|
|
1019
|
+
internalType: "uint256",
|
|
1020
|
+
name: "newTotalSharesSold",
|
|
1021
|
+
type: "uint256",
|
|
1022
|
+
},
|
|
1023
|
+
],
|
|
1024
|
+
name: "MinSharesReached",
|
|
1025
|
+
type: "event",
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
anonymous: false,
|
|
1029
|
+
inputs: [
|
|
1030
|
+
{ indexed: true, internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1031
|
+
{
|
|
1032
|
+
indexed: true,
|
|
1033
|
+
internalType: "address",
|
|
1034
|
+
name: "creator",
|
|
1035
|
+
type: "address",
|
|
1036
|
+
},
|
|
1037
|
+
],
|
|
1038
|
+
name: "RoundFilled",
|
|
1039
|
+
type: "event",
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
inputs: [
|
|
1043
|
+
{ internalType: "address", name: "creator", type: "address" },
|
|
1044
|
+
{ internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1045
|
+
{ internalType: "uint256", name: "stepsToBuy", type: "uint256" },
|
|
1046
|
+
{ internalType: "uint256", name: "minStepsToBuy", type: "uint256" },
|
|
1047
|
+
],
|
|
1048
|
+
name: "buyFractions",
|
|
1049
|
+
outputs: [],
|
|
1050
|
+
stateMutability: "nonpayable",
|
|
1051
|
+
type: "function",
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
inputs: [
|
|
1055
|
+
{ internalType: "address", name: "creator", type: "address" },
|
|
1056
|
+
{ internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1057
|
+
],
|
|
1058
|
+
name: "claimRefund",
|
|
1059
|
+
outputs: [],
|
|
1060
|
+
stateMutability: "nonpayable",
|
|
1061
|
+
type: "function",
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
inputs: [{ internalType: "bytes32", name: "id", type: "bytes32" }],
|
|
1065
|
+
name: "closeFraction",
|
|
1066
|
+
outputs: [],
|
|
1067
|
+
stateMutability: "nonpayable",
|
|
1068
|
+
type: "function",
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
inputs: [
|
|
1072
|
+
{ internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1073
|
+
{ internalType: "address", name: "token", type: "address" },
|
|
1074
|
+
{ internalType: "uint256", name: "step", type: "uint256" },
|
|
1075
|
+
{ internalType: "uint256", name: "totalSteps", type: "uint256" },
|
|
1076
|
+
{ internalType: "uint48", name: "expiration", type: "uint48" },
|
|
1077
|
+
{ internalType: "address", name: "to", type: "address" },
|
|
1078
|
+
{ internalType: "bool", name: "useCounterfactualAddress", type: "bool" },
|
|
1079
|
+
{ internalType: "uint256", name: "minSharesToRaise", type: "uint256" },
|
|
1080
|
+
],
|
|
1081
|
+
name: "createFraction",
|
|
1082
|
+
outputs: [],
|
|
1083
|
+
stateMutability: "nonpayable",
|
|
1084
|
+
type: "function",
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
inputs: [
|
|
1088
|
+
{ internalType: "address", name: "creator", type: "address" },
|
|
1089
|
+
{ internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1090
|
+
],
|
|
1091
|
+
name: "getFraction",
|
|
1092
|
+
outputs: [
|
|
1093
|
+
{
|
|
1094
|
+
components: [
|
|
1095
|
+
{ internalType: "address", name: "token", type: "address" },
|
|
1096
|
+
{ internalType: "uint48", name: "expiration", type: "uint48" },
|
|
1097
|
+
{ internalType: "bool", name: "manuallyClosed", type: "bool" },
|
|
1098
|
+
{
|
|
1099
|
+
internalType: "uint256",
|
|
1100
|
+
name: "minSharesToRaise",
|
|
1101
|
+
type: "uint256",
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
internalType: "bool",
|
|
1105
|
+
name: "useCounterfactualAddress",
|
|
1106
|
+
type: "bool",
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
internalType: "bool",
|
|
1110
|
+
name: "claimedFromMinSharesToRaise",
|
|
1111
|
+
type: "bool",
|
|
1112
|
+
},
|
|
1113
|
+
{ internalType: "address", name: "owner", type: "address" },
|
|
1114
|
+
{ internalType: "uint256", name: "step", type: "uint256" },
|
|
1115
|
+
{ internalType: "address", name: "to", type: "address" },
|
|
1116
|
+
{ internalType: "uint256", name: "soldSteps", type: "uint256" },
|
|
1117
|
+
{ internalType: "uint256", name: "totalSteps", type: "uint256" },
|
|
1118
|
+
],
|
|
1119
|
+
internalType: "struct OffchainFractions.FractionData",
|
|
1120
|
+
name: "",
|
|
1121
|
+
type: "tuple",
|
|
1122
|
+
},
|
|
1123
|
+
],
|
|
1124
|
+
stateMutability: "view",
|
|
1125
|
+
type: "function",
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
inputs: [],
|
|
1129
|
+
name: "i_CFHFactory",
|
|
1130
|
+
outputs: [
|
|
1131
|
+
{
|
|
1132
|
+
internalType: "contract CounterfactualHolderFactory",
|
|
1133
|
+
name: "",
|
|
1134
|
+
type: "address",
|
|
1135
|
+
},
|
|
1136
|
+
],
|
|
1137
|
+
stateMutability: "view",
|
|
1138
|
+
type: "function",
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
inputs: [
|
|
1142
|
+
{ internalType: "address", name: "user", type: "address" },
|
|
1143
|
+
{ internalType: "address", name: "creator", type: "address" },
|
|
1144
|
+
{ internalType: "bytes32", name: "id", type: "bytes32" },
|
|
1145
|
+
],
|
|
1146
|
+
name: "stepsPurchased",
|
|
1147
|
+
outputs: [
|
|
1148
|
+
{ internalType: "uint256", name: "stepsPurchased", type: "uint256" },
|
|
1149
|
+
],
|
|
1150
|
+
stateMutability: "view",
|
|
1151
|
+
type: "function",
|
|
1152
|
+
},
|
|
1153
|
+
];
|
|
1154
|
+
|
|
1155
|
+
var OffchainFractionsError;
|
|
1156
|
+
(function (OffchainFractionsError) {
|
|
1157
|
+
OffchainFractionsError["CONTRACT_NOT_AVAILABLE"] = "Contract not available";
|
|
1158
|
+
OffchainFractionsError["SIGNER_NOT_AVAILABLE"] = "Signer not available";
|
|
1159
|
+
OffchainFractionsError["UNKNOWN_ERROR"] = "Unknown error";
|
|
1160
|
+
OffchainFractionsError["INVALID_PARAMETERS"] = "Invalid parameters";
|
|
1161
|
+
OffchainFractionsError["FRACTION_NOT_FOUND"] = "Fraction not found";
|
|
1162
|
+
OffchainFractionsError["INSUFFICIENT_BALANCE"] = "Insufficient balance";
|
|
1163
|
+
OffchainFractionsError["INSUFFICIENT_ALLOWANCE"] = "Insufficient allowance";
|
|
1164
|
+
})(OffchainFractionsError || (OffchainFractionsError = {}));
|
|
1165
|
+
// Utility to extract the most useful revert reason from an ethers error object
|
|
1166
|
+
function parseEthersError(error) {
|
|
1167
|
+
if (!error)
|
|
1168
|
+
return "Unknown error";
|
|
1169
|
+
const possibleError = error;
|
|
1170
|
+
// If the error originates from a callStatic it will often be found at `error?.error?.body`
|
|
1171
|
+
if (possibleError?.error?.body) {
|
|
1172
|
+
try {
|
|
1173
|
+
const body = JSON.parse(possibleError.error.body);
|
|
1174
|
+
// Hardhat style errors
|
|
1175
|
+
if (body?.error?.message)
|
|
1176
|
+
return body.error.message;
|
|
1177
|
+
}
|
|
1178
|
+
catch { }
|
|
1179
|
+
}
|
|
1180
|
+
// Found on MetaMask/Alchemy shape errors
|
|
1181
|
+
if (possibleError?.data?.message)
|
|
1182
|
+
return possibleError.data.message;
|
|
1183
|
+
if (possibleError?.error?.message)
|
|
1184
|
+
return possibleError.error.message;
|
|
1185
|
+
// Standard ethers v5 message
|
|
1186
|
+
if (possibleError?.reason)
|
|
1187
|
+
return possibleError.reason;
|
|
1188
|
+
if (possibleError?.message)
|
|
1189
|
+
return possibleError.message;
|
|
1190
|
+
return OffchainFractionsError.UNKNOWN_ERROR;
|
|
1191
|
+
}
|
|
1192
|
+
// Type-guard style helper to ensure a signer exists throughout the rest of the function.
|
|
1193
|
+
function assertSigner(maybeSigner) {
|
|
1194
|
+
if (!maybeSigner) {
|
|
1195
|
+
throw new Error(OffchainFractionsError.SIGNER_NOT_AVAILABLE);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
function useOffchainFractions(signer, CHAIN_ID) {
|
|
1199
|
+
// Use dynamic addresses based on chain configuration
|
|
1200
|
+
const ADDRESSES = getAddresses(CHAIN_ID);
|
|
1201
|
+
// Framework-agnostic processing flag
|
|
1202
|
+
let isProcessing = false;
|
|
1203
|
+
const setIsProcessing = (value) => {
|
|
1204
|
+
isProcessing = value;
|
|
1205
|
+
};
|
|
1206
|
+
// Returns a contract instance for OffchainFractions
|
|
1207
|
+
function getOffchainFractionsContract() {
|
|
1208
|
+
assertSigner(signer);
|
|
1209
|
+
return new Contract(ADDRESSES.OFFCHAIN_FRACTIONS, OFFCHAIN_FRACTIONS_ABI, signer);
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Get the appropriate token contract
|
|
1213
|
+
*/
|
|
1214
|
+
function getTokenContract(tokenAddress) {
|
|
1215
|
+
assertSigner(signer);
|
|
1216
|
+
return new Contract(tokenAddress, ERC20_ABI, signer);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Check current token allowance for the offchain fractions contract
|
|
1220
|
+
* @param owner The wallet address to check allowance for
|
|
1221
|
+
* @param tokenAddress The token contract address
|
|
1222
|
+
*/
|
|
1223
|
+
async function checkTokenAllowance(owner, tokenAddress) {
|
|
1224
|
+
assertSigner(signer);
|
|
1225
|
+
try {
|
|
1226
|
+
const tokenContract = getTokenContract(tokenAddress);
|
|
1227
|
+
if (!tokenContract)
|
|
1228
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1229
|
+
const allowance = await tokenContract.allowance(owner, ADDRESSES.OFFCHAIN_FRACTIONS);
|
|
1230
|
+
return allowance;
|
|
1231
|
+
}
|
|
1232
|
+
catch (error) {
|
|
1233
|
+
throw new Error(parseEthersError(error));
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Check user's token balance
|
|
1238
|
+
* @param owner The wallet address to check balance for
|
|
1239
|
+
* @param tokenAddress The token contract address
|
|
1240
|
+
*/
|
|
1241
|
+
async function checkTokenBalance(owner, tokenAddress) {
|
|
1242
|
+
assertSigner(signer);
|
|
1243
|
+
try {
|
|
1244
|
+
const tokenContract = getTokenContract(tokenAddress);
|
|
1245
|
+
if (!tokenContract)
|
|
1246
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1247
|
+
const balance = await tokenContract.balanceOf(owner);
|
|
1248
|
+
return balance;
|
|
1249
|
+
}
|
|
1250
|
+
catch (error) {
|
|
1251
|
+
throw new Error(parseEthersError(error));
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Approve tokens for the offchain fractions contract
|
|
1256
|
+
* @param tokenAddress The token contract address
|
|
1257
|
+
* @param amount Amount to approve (BigNumber)
|
|
1258
|
+
*/
|
|
1259
|
+
async function approveToken(tokenAddress, amount) {
|
|
1260
|
+
assertSigner(signer);
|
|
1261
|
+
try {
|
|
1262
|
+
const tokenContract = getTokenContract(tokenAddress);
|
|
1263
|
+
if (!tokenContract)
|
|
1264
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1265
|
+
setIsProcessing(true);
|
|
1266
|
+
const approveTx = await tokenContract.approve(ADDRESSES.OFFCHAIN_FRACTIONS, amount);
|
|
1267
|
+
await approveTx.wait();
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
catch (error) {
|
|
1271
|
+
throw new Error(parseEthersError(error));
|
|
1272
|
+
}
|
|
1273
|
+
finally {
|
|
1274
|
+
setIsProcessing(false);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Create a new fractional token sale
|
|
1279
|
+
* @param params Parameters for creating the fraction
|
|
1280
|
+
*/
|
|
1281
|
+
async function createFraction(params) {
|
|
1282
|
+
assertSigner(signer);
|
|
1283
|
+
try {
|
|
1284
|
+
const contract = getOffchainFractionsContract();
|
|
1285
|
+
if (!contract)
|
|
1286
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1287
|
+
setIsProcessing(true);
|
|
1288
|
+
const { id, token, step, totalSteps, expiration, to, useCounterfactualAddress, minSharesToRaise, } = params;
|
|
1289
|
+
// Validate parameters
|
|
1290
|
+
if (!id || !token || !to) {
|
|
1291
|
+
throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
|
|
1292
|
+
}
|
|
1293
|
+
if (step === 0n || totalSteps === 0n) {
|
|
1294
|
+
throw new Error("Step and totalSteps must be greater than zero");
|
|
1295
|
+
}
|
|
1296
|
+
if (minSharesToRaise > totalSteps) {
|
|
1297
|
+
throw new Error("minSharesToRaise cannot be greater than totalSteps");
|
|
1298
|
+
}
|
|
1299
|
+
// Run a static call first to surface any revert reason
|
|
1300
|
+
try {
|
|
1301
|
+
await contract
|
|
1302
|
+
.getFunction("createFraction")
|
|
1303
|
+
.staticCall(id, token, step, totalSteps, expiration, to, useCounterfactualAddress, minSharesToRaise);
|
|
1304
|
+
}
|
|
1305
|
+
catch (staticError) {
|
|
1306
|
+
throw new Error(parseEthersError(staticError));
|
|
1307
|
+
}
|
|
1308
|
+
// Execute the transaction
|
|
1309
|
+
const tx = await contract.getFunction("createFraction")(id, token, step, totalSteps, expiration, to, useCounterfactualAddress, minSharesToRaise);
|
|
1310
|
+
await tx.wait();
|
|
1311
|
+
return tx.hash;
|
|
1312
|
+
}
|
|
1313
|
+
catch (error) {
|
|
1314
|
+
throw new Error(parseEthersError(error));
|
|
1315
|
+
}
|
|
1316
|
+
finally {
|
|
1317
|
+
setIsProcessing(false);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Buy fractions in an existing sale
|
|
1322
|
+
* @param params Parameters for buying fractions
|
|
1323
|
+
*/
|
|
1324
|
+
async function buyFractions(params) {
|
|
1325
|
+
assertSigner(signer);
|
|
1326
|
+
try {
|
|
1327
|
+
const contract = getOffchainFractionsContract();
|
|
1328
|
+
if (!contract)
|
|
1329
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1330
|
+
setIsProcessing(true);
|
|
1331
|
+
const { creator, id, stepsToBuy, minStepsToBuy } = params;
|
|
1332
|
+
// Validate parameters
|
|
1333
|
+
if (!creator || !id) {
|
|
1334
|
+
throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
|
|
1335
|
+
}
|
|
1336
|
+
if (stepsToBuy === 0n) {
|
|
1337
|
+
throw new Error("stepsToBuy must be greater than zero");
|
|
1338
|
+
}
|
|
1339
|
+
// Get fraction data to calculate required amount
|
|
1340
|
+
const fractionData = await getFraction(creator, id);
|
|
1341
|
+
const requiredAmount = stepsToBuy * fractionData.step;
|
|
1342
|
+
const owner = await signer.getAddress();
|
|
1343
|
+
// Check token balance
|
|
1344
|
+
const balance = await checkTokenBalance(owner, fractionData.token);
|
|
1345
|
+
if (balance < requiredAmount) {
|
|
1346
|
+
throw new Error(OffchainFractionsError.INSUFFICIENT_BALANCE);
|
|
1347
|
+
}
|
|
1348
|
+
// Check and approve tokens if necessary
|
|
1349
|
+
const allowance = await checkTokenAllowance(owner, fractionData.token);
|
|
1350
|
+
if (allowance < requiredAmount) {
|
|
1351
|
+
const tokenContract = getTokenContract(fractionData.token);
|
|
1352
|
+
const approveTx = await tokenContract.approve(ADDRESSES.OFFCHAIN_FRACTIONS, requiredAmount);
|
|
1353
|
+
await approveTx.wait();
|
|
1354
|
+
}
|
|
1355
|
+
// Run a static call first to surface any revert reason
|
|
1356
|
+
try {
|
|
1357
|
+
await contract
|
|
1358
|
+
.getFunction("buyFractions")
|
|
1359
|
+
.staticCall(creator, id, stepsToBuy, minStepsToBuy, {
|
|
1360
|
+
from: owner,
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
catch (staticError) {
|
|
1364
|
+
throw new Error(parseEthersError(staticError));
|
|
1365
|
+
}
|
|
1366
|
+
// Execute the transaction
|
|
1367
|
+
const tx = await contract.getFunction("buyFractions")(creator, id, stepsToBuy, minStepsToBuy);
|
|
1368
|
+
await tx.wait();
|
|
1369
|
+
return tx.hash;
|
|
1370
|
+
}
|
|
1371
|
+
catch (error) {
|
|
1372
|
+
throw new Error(parseEthersError(error));
|
|
1373
|
+
}
|
|
1374
|
+
finally {
|
|
1375
|
+
setIsProcessing(false);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Claim refund from an unfilled sale
|
|
1380
|
+
* @param creator The address that created the fraction sale
|
|
1381
|
+
* @param id The unique identifier of the fraction sale
|
|
1382
|
+
*/
|
|
1383
|
+
async function claimRefund(creator, id) {
|
|
1384
|
+
assertSigner(signer);
|
|
1385
|
+
try {
|
|
1386
|
+
const contract = getOffchainFractionsContract();
|
|
1387
|
+
if (!contract)
|
|
1388
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1389
|
+
setIsProcessing(true);
|
|
1390
|
+
// Validate parameters
|
|
1391
|
+
if (!creator || !id) {
|
|
1392
|
+
throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
|
|
1393
|
+
}
|
|
1394
|
+
const owner = await signer.getAddress();
|
|
1395
|
+
// Check if user has steps purchased
|
|
1396
|
+
const userSteps = await getStepsPurchased(owner, creator, id);
|
|
1397
|
+
if (userSteps === 0n) {
|
|
1398
|
+
throw new Error("No steps purchased for this fraction");
|
|
1399
|
+
}
|
|
1400
|
+
// Run a static call first to surface any revert reason
|
|
1401
|
+
try {
|
|
1402
|
+
await contract.getFunction("claimRefund").staticCall(creator, id, {
|
|
1403
|
+
from: owner,
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
catch (staticError) {
|
|
1407
|
+
throw new Error(parseEthersError(staticError));
|
|
1408
|
+
}
|
|
1409
|
+
// Execute the transaction
|
|
1410
|
+
const tx = await contract.getFunction("claimRefund")(creator, id);
|
|
1411
|
+
await tx.wait();
|
|
1412
|
+
return tx.hash;
|
|
1413
|
+
}
|
|
1414
|
+
catch (error) {
|
|
1415
|
+
throw new Error(parseEthersError(error));
|
|
1416
|
+
}
|
|
1417
|
+
finally {
|
|
1418
|
+
setIsProcessing(false);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Close a fraction sale manually (only by owner)
|
|
1423
|
+
* @param id The unique identifier of the fraction sale to close
|
|
1424
|
+
*/
|
|
1425
|
+
async function closeFraction(id) {
|
|
1426
|
+
assertSigner(signer);
|
|
1427
|
+
try {
|
|
1428
|
+
const contract = getOffchainFractionsContract();
|
|
1429
|
+
if (!contract)
|
|
1430
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1431
|
+
setIsProcessing(true);
|
|
1432
|
+
// Validate parameters
|
|
1433
|
+
if (!id) {
|
|
1434
|
+
throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
|
|
1435
|
+
}
|
|
1436
|
+
const owner = await signer.getAddress();
|
|
1437
|
+
// Run a static call first to surface any revert reason
|
|
1438
|
+
try {
|
|
1439
|
+
await contract.getFunction("closeFraction").staticCall(id, {
|
|
1440
|
+
from: owner,
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
535
1443
|
catch (staticError) {
|
|
536
1444
|
throw new Error(parseEthersError(staticError));
|
|
537
1445
|
}
|
|
538
|
-
// Execute the
|
|
539
|
-
|
|
540
|
-
if (!isAuditFees && currency === "USDC") {
|
|
541
|
-
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message);
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
tx = await forwarderContract.getFunction("forward")(tokenAddress, isAuditFees
|
|
545
|
-
? ADDRESSES.AUDIT_FEE_WALLET
|
|
546
|
-
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
547
|
-
}
|
|
1446
|
+
// Execute the transaction
|
|
1447
|
+
const tx = await contract.getFunction("closeFraction")(id);
|
|
548
1448
|
await tx.wait();
|
|
549
1449
|
return tx.hash;
|
|
550
1450
|
}
|
|
551
|
-
catch (
|
|
552
|
-
throw new Error(parseEthersError(
|
|
1451
|
+
catch (error) {
|
|
1452
|
+
throw new Error(parseEthersError(error));
|
|
553
1453
|
}
|
|
554
1454
|
finally {
|
|
555
1455
|
setIsProcessing(false);
|
|
556
1456
|
}
|
|
557
1457
|
}
|
|
558
1458
|
/**
|
|
559
|
-
*
|
|
1459
|
+
* Get fraction data for a specific creator and ID
|
|
1460
|
+
* @param creator The address that created the fraction sale
|
|
1461
|
+
* @param id The unique identifier of the fraction sale
|
|
560
1462
|
*/
|
|
561
|
-
async function
|
|
1463
|
+
async function getFraction(creator, id) {
|
|
562
1464
|
assertSigner(signer);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
1465
|
+
try {
|
|
1466
|
+
const contract = getOffchainFractionsContract();
|
|
1467
|
+
if (!contract)
|
|
1468
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1469
|
+
const result = await contract.getFraction(creator, id);
|
|
1470
|
+
return {
|
|
1471
|
+
token: result.token,
|
|
1472
|
+
expiration: Number(result.expiration),
|
|
1473
|
+
manuallyClosed: result.manuallyClosed,
|
|
1474
|
+
minSharesToRaise: result.minSharesToRaise,
|
|
1475
|
+
useCounterfactualAddress: result.useCounterfactualAddress,
|
|
1476
|
+
claimedFromMinSharesToRaise: result.claimedFromMinSharesToRaise,
|
|
1477
|
+
owner: result.owner,
|
|
1478
|
+
step: result.step,
|
|
1479
|
+
to: result.to,
|
|
1480
|
+
soldSteps: result.soldSteps,
|
|
1481
|
+
totalSteps: result.totalSteps,
|
|
1482
|
+
};
|
|
566
1483
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
userAddress,
|
|
570
|
-
type: TRANSFER_TYPES.PayProtocolFeeAndMintGCTLAndStake,
|
|
571
|
-
currency,
|
|
572
|
-
applicationId,
|
|
573
|
-
regionId,
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Forward tokens for protocol fee payment and GCTL minting with staking
|
|
578
|
-
*/
|
|
579
|
-
async function sponsorProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, currency = "USDC") {
|
|
580
|
-
assertSigner(signer);
|
|
581
|
-
if (currency === "GLW") {
|
|
582
|
-
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
1484
|
+
catch (error) {
|
|
1485
|
+
throw new Error(parseEthersError(error));
|
|
583
1486
|
}
|
|
584
|
-
return forwardTokens({
|
|
585
|
-
amount,
|
|
586
|
-
userAddress,
|
|
587
|
-
type: TRANSFER_TYPES.SponsorProtocolFeeAndMintGCTLAndStake,
|
|
588
|
-
currency,
|
|
589
|
-
applicationId,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Forward tokens for protocol fee payment only
|
|
594
|
-
*/
|
|
595
|
-
async function payProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
|
|
596
|
-
assertSigner(signer);
|
|
597
|
-
return forwardTokens({
|
|
598
|
-
amount,
|
|
599
|
-
userAddress,
|
|
600
|
-
type: TRANSFER_TYPES.PayProtocolFee,
|
|
601
|
-
currency,
|
|
602
|
-
applicationId,
|
|
603
|
-
});
|
|
604
1487
|
}
|
|
605
1488
|
/**
|
|
606
|
-
*
|
|
1489
|
+
* Get the number of steps purchased by a user for a specific fraction
|
|
1490
|
+
* @param user The user address
|
|
1491
|
+
* @param creator The address that created the fraction sale
|
|
1492
|
+
* @param id The unique identifier of the fraction sale
|
|
607
1493
|
*/
|
|
608
|
-
async function
|
|
1494
|
+
async function getStepsPurchased(user, creator, id) {
|
|
609
1495
|
assertSigner(signer);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
1496
|
+
try {
|
|
1497
|
+
const contract = getOffchainFractionsContract();
|
|
1498
|
+
if (!contract)
|
|
1499
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1500
|
+
const result = await contract.stepsPurchased(user, creator, id);
|
|
1501
|
+
return result;
|
|
1502
|
+
}
|
|
1503
|
+
catch (error) {
|
|
1504
|
+
throw new Error(parseEthersError(error));
|
|
1505
|
+
}
|
|
617
1506
|
}
|
|
618
1507
|
/**
|
|
619
|
-
*
|
|
1508
|
+
* Check if a fraction sale is expired
|
|
1509
|
+
* @param creator The address that created the fraction sale
|
|
1510
|
+
* @param id The unique identifier of the fraction sale
|
|
620
1511
|
*/
|
|
621
|
-
async function
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1512
|
+
async function isFractionExpired(creator, id) {
|
|
1513
|
+
try {
|
|
1514
|
+
const fraction = await getFraction(creator, id);
|
|
1515
|
+
return Date.now() / 1000 > fraction.expiration;
|
|
1516
|
+
}
|
|
1517
|
+
catch (error) {
|
|
1518
|
+
throw new Error(parseEthersError(error));
|
|
626
1519
|
}
|
|
627
|
-
return forwardTokens({
|
|
628
|
-
amount,
|
|
629
|
-
userAddress,
|
|
630
|
-
type: TRANSFER_TYPES.MintGCTLAndStake,
|
|
631
|
-
currency,
|
|
632
|
-
regionId,
|
|
633
|
-
});
|
|
634
1520
|
}
|
|
635
1521
|
/**
|
|
636
|
-
*
|
|
1522
|
+
* Check if a fraction sale has reached its minimum shares threshold
|
|
1523
|
+
* @param creator The address that created the fraction sale
|
|
1524
|
+
* @param id The unique identifier of the fraction sale
|
|
637
1525
|
*/
|
|
638
|
-
async function
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1526
|
+
async function hasReachedMinimumShares(creator, id) {
|
|
1527
|
+
try {
|
|
1528
|
+
const fraction = await getFraction(creator, id);
|
|
1529
|
+
return fraction.soldSteps >= fraction.minSharesToRaise;
|
|
1530
|
+
}
|
|
1531
|
+
catch (error) {
|
|
1532
|
+
throw new Error(parseEthersError(error));
|
|
643
1533
|
}
|
|
644
|
-
return forwardTokens({
|
|
645
|
-
amount,
|
|
646
|
-
userAddress,
|
|
647
|
-
type: TRANSFER_TYPES.MintGCTL,
|
|
648
|
-
currency,
|
|
649
|
-
});
|
|
650
1534
|
}
|
|
651
1535
|
/**
|
|
652
|
-
*
|
|
1536
|
+
* Check if a fraction sale is completely filled
|
|
1537
|
+
* @param creator The address that created the fraction sale
|
|
1538
|
+
* @param id The unique identifier of the fraction sale
|
|
653
1539
|
*/
|
|
654
|
-
async function
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
});
|
|
1540
|
+
async function isFractionCompletelyFilled(creator, id) {
|
|
1541
|
+
try {
|
|
1542
|
+
const fraction = await getFraction(creator, id);
|
|
1543
|
+
return fraction.soldSteps >= fraction.totalSteps;
|
|
1544
|
+
}
|
|
1545
|
+
catch (error) {
|
|
1546
|
+
throw new Error(parseEthersError(error));
|
|
1547
|
+
}
|
|
663
1548
|
}
|
|
664
1549
|
/**
|
|
665
|
-
*
|
|
1550
|
+
* Calculate the total amount raised for a fraction
|
|
1551
|
+
* @param creator The address that created the fraction sale
|
|
1552
|
+
* @param id The unique identifier of the fraction sale
|
|
666
1553
|
*/
|
|
667
|
-
async function
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
});
|
|
1554
|
+
async function getTotalAmountRaised(creator, id) {
|
|
1555
|
+
try {
|
|
1556
|
+
const fraction = await getFraction(creator, id);
|
|
1557
|
+
return fraction.soldSteps * fraction.step;
|
|
1558
|
+
}
|
|
1559
|
+
catch (error) {
|
|
1560
|
+
throw new Error(parseEthersError(error));
|
|
1561
|
+
}
|
|
676
1562
|
}
|
|
677
1563
|
/**
|
|
678
|
-
*
|
|
1564
|
+
* Calculate the remaining steps available for purchase
|
|
1565
|
+
* @param creator The address that created the fraction sale
|
|
1566
|
+
* @param id The unique identifier of the fraction sale
|
|
679
1567
|
*/
|
|
680
|
-
async function
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1568
|
+
async function getRemainingSteps(creator, id) {
|
|
1569
|
+
try {
|
|
1570
|
+
const fraction = await getFraction(creator, id);
|
|
1571
|
+
return fraction.totalSteps - fraction.soldSteps;
|
|
1572
|
+
}
|
|
1573
|
+
catch (error) {
|
|
1574
|
+
throw new Error(parseEthersError(error));
|
|
684
1575
|
}
|
|
685
|
-
return forwardTokens({
|
|
686
|
-
amount,
|
|
687
|
-
userAddress,
|
|
688
|
-
type: TRANSFER_TYPES.CommitKickstarter,
|
|
689
|
-
currency,
|
|
690
|
-
kickstarterId,
|
|
691
|
-
});
|
|
692
1576
|
}
|
|
693
1577
|
/**
|
|
694
|
-
* Estimate gas for
|
|
695
|
-
* @param params
|
|
1578
|
+
* Estimate gas for creating a fraction
|
|
1579
|
+
* @param params Parameters for creating the fraction
|
|
696
1580
|
* @param ethPriceInUSD Current ETH price in USD (for cost estimation)
|
|
697
1581
|
*/
|
|
698
|
-
async function
|
|
1582
|
+
async function estimateGasForCreateFraction(params, ethPriceInUSD) {
|
|
699
1583
|
assertSigner(signer);
|
|
700
1584
|
try {
|
|
701
|
-
const
|
|
702
|
-
if (!
|
|
703
|
-
throw new Error(
|
|
704
|
-
const {
|
|
705
|
-
const isAuditFees = params.type === TRANSFER_TYPES.PayAuditFees;
|
|
706
|
-
if (isAuditFees && currency !== "USDC") {
|
|
707
|
-
throw new Error("PayAuditFees only supports USDC");
|
|
708
|
-
}
|
|
709
|
-
// Construct the appropriate message for this forward type
|
|
710
|
-
const message = constructForwardMessage(params);
|
|
711
|
-
// Determine sendToCounterfactualWallet based on currency
|
|
712
|
-
const sendToCounterfactualWallet = currency === "GLW" || currency === "USDG";
|
|
713
|
-
// Get token address
|
|
714
|
-
let tokenAddress;
|
|
715
|
-
switch (currency) {
|
|
716
|
-
case "USDC":
|
|
717
|
-
tokenAddress = ADDRESSES.USDC;
|
|
718
|
-
break;
|
|
719
|
-
case "USDG":
|
|
720
|
-
tokenAddress = ADDRESSES.USDG;
|
|
721
|
-
break;
|
|
722
|
-
case "GLW":
|
|
723
|
-
tokenAddress = ADDRESSES.GLW;
|
|
724
|
-
break;
|
|
725
|
-
default:
|
|
726
|
-
throw new Error(`Unsupported currency for gas estimation: ${currency}`);
|
|
727
|
-
}
|
|
1585
|
+
const contract = getOffchainFractionsContract();
|
|
1586
|
+
if (!contract)
|
|
1587
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1588
|
+
const { id, token, step, totalSteps, expiration, to, useCounterfactualAddress, minSharesToRaise, } = params;
|
|
728
1589
|
const feeData = await signer.provider?.getFeeData();
|
|
729
1590
|
const gasPrice = feeData?.gasPrice ?? feeData?.maxFeePerGas ?? 0n;
|
|
730
1591
|
if (gasPrice === 0n) {
|
|
731
1592
|
throw new Error("Could not fetch gas price to estimate cost.");
|
|
732
1593
|
}
|
|
733
|
-
const estimatedGas =
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
.estimateGas(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message)
|
|
737
|
-
: await forwarderContract
|
|
738
|
-
.getFunction("forward")
|
|
739
|
-
.estimateGas(tokenAddress, isAuditFees
|
|
740
|
-
? ADDRESSES.AUDIT_FEE_WALLET
|
|
741
|
-
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
1594
|
+
const estimatedGas = await contract
|
|
1595
|
+
.getFunction("createFraction")
|
|
1596
|
+
.estimateGas(id, token, step, totalSteps, expiration, to, useCounterfactualAddress, minSharesToRaise);
|
|
742
1597
|
const estimatedCost = estimatedGas * gasPrice;
|
|
743
1598
|
if (ethPriceInUSD) {
|
|
744
1599
|
const estimatedCostInEth = formatEther(estimatedCost);
|
|
@@ -754,57 +1609,61 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
754
1609
|
}
|
|
755
1610
|
}
|
|
756
1611
|
/**
|
|
757
|
-
*
|
|
758
|
-
* @param
|
|
759
|
-
* @param
|
|
1612
|
+
* Estimate gas for buying fractions
|
|
1613
|
+
* @param params Parameters for buying fractions
|
|
1614
|
+
* @param ethPriceInUSD Current ETH price in USD (for cost estimation)
|
|
760
1615
|
*/
|
|
761
|
-
async function
|
|
1616
|
+
async function estimateGasForBuyFractions(params, ethPriceInUSD) {
|
|
762
1617
|
assertSigner(signer);
|
|
763
|
-
if (CHAIN_ID !== 11155111) {
|
|
764
|
-
throw new Error("Minting test USDC is only supported on Sepolia");
|
|
765
|
-
}
|
|
766
1618
|
try {
|
|
767
|
-
const
|
|
768
|
-
if (!
|
|
769
|
-
throw new Error(
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1619
|
+
const contract = getOffchainFractionsContract();
|
|
1620
|
+
if (!contract)
|
|
1621
|
+
throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
|
|
1622
|
+
const { creator, id, stepsToBuy, minStepsToBuy } = params;
|
|
1623
|
+
const feeData = await signer.provider?.getFeeData();
|
|
1624
|
+
const gasPrice = feeData?.gasPrice ?? feeData?.maxFeePerGas ?? 0n;
|
|
1625
|
+
if (gasPrice === 0n) {
|
|
1626
|
+
throw new Error("Could not fetch gas price to estimate cost.");
|
|
1627
|
+
}
|
|
1628
|
+
const estimatedGas = await contract
|
|
1629
|
+
.getFunction("buyFractions")
|
|
1630
|
+
.estimateGas(creator, id, stepsToBuy, minStepsToBuy);
|
|
1631
|
+
const estimatedCost = estimatedGas * gasPrice;
|
|
1632
|
+
if (ethPriceInUSD) {
|
|
1633
|
+
const estimatedCostInEth = formatEther(estimatedCost);
|
|
1634
|
+
const estimatedCostInUSD = (parseFloat(estimatedCostInEth) * ethPriceInUSD).toFixed(2);
|
|
1635
|
+
return estimatedCostInUSD;
|
|
1636
|
+
}
|
|
1637
|
+
else {
|
|
1638
|
+
throw new Error("Could not fetch the ETH price to calculate cost in USD.");
|
|
781
1639
|
}
|
|
782
|
-
throw new Error(errorMessage);
|
|
783
1640
|
}
|
|
784
|
-
|
|
785
|
-
|
|
1641
|
+
catch (error) {
|
|
1642
|
+
throw new Error(parseEthersError(error));
|
|
786
1643
|
}
|
|
787
1644
|
}
|
|
788
1645
|
return {
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
buySolarFarm,
|
|
798
|
-
commitKickstarter,
|
|
799
|
-
payAuditFees,
|
|
1646
|
+
// Core contract functions
|
|
1647
|
+
createFraction,
|
|
1648
|
+
buyFractions,
|
|
1649
|
+
claimRefund,
|
|
1650
|
+
closeFraction,
|
|
1651
|
+
// View functions
|
|
1652
|
+
getFraction,
|
|
1653
|
+
getStepsPurchased,
|
|
800
1654
|
// Token operations
|
|
801
1655
|
approveToken,
|
|
802
1656
|
checkTokenAllowance,
|
|
803
1657
|
checkTokenBalance,
|
|
804
|
-
// Utility
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1658
|
+
// Utility functions
|
|
1659
|
+
isFractionExpired,
|
|
1660
|
+
hasReachedMinimumShares,
|
|
1661
|
+
isFractionCompletelyFilled,
|
|
1662
|
+
getTotalAmountRaised,
|
|
1663
|
+
getRemainingSteps,
|
|
1664
|
+
// Gas estimation
|
|
1665
|
+
estimateGasForCreateFraction,
|
|
1666
|
+
estimateGasForBuyFractions,
|
|
808
1667
|
// State
|
|
809
1668
|
get isProcessing() {
|
|
810
1669
|
return isProcessing;
|
|
@@ -2157,10 +3016,33 @@ function FarmsRouter(baseUrl) {
|
|
|
2157
3016
|
throw new Error(parseApiError(error));
|
|
2158
3017
|
}
|
|
2159
3018
|
};
|
|
3019
|
+
const estimateRewardScore = async (params) => {
|
|
3020
|
+
try {
|
|
3021
|
+
// First try to get the success response
|
|
3022
|
+
const data = await request("/farms/estimate-reward-score", {
|
|
3023
|
+
method: "POST",
|
|
3024
|
+
headers: {
|
|
3025
|
+
"Content-Type": "application/json",
|
|
3026
|
+
},
|
|
3027
|
+
body: JSON.stringify(params),
|
|
3028
|
+
});
|
|
3029
|
+
// Check if it's an error response
|
|
3030
|
+
if ("error" in data) {
|
|
3031
|
+
throw new Error(data.error);
|
|
3032
|
+
}
|
|
3033
|
+
// At this point, TypeScript knows it's EstimateRewardScoreApiResponse
|
|
3034
|
+
// All fields are required in the success response based on the API spec
|
|
3035
|
+
return data;
|
|
3036
|
+
}
|
|
3037
|
+
catch (error) {
|
|
3038
|
+
throw new Error(parseApiError(error));
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
2160
3041
|
return {
|
|
2161
3042
|
fetchSponsoredFarms,
|
|
3043
|
+
estimateRewardScore,
|
|
2162
3044
|
};
|
|
2163
3045
|
}
|
|
2164
3046
|
|
|
2165
|
-
export { ControlRouter as C, DECIMALS_BY_TOKEN as D, FarmsRouter as F, GLOW_WEIGHT_DECIMAL_PRECISION as G, HUB_URL as H, KickstarterRouter as K, MAX_WEIGHT as M,
|
|
2166
|
-
//# sourceMappingURL=farms-router-
|
|
3047
|
+
export { ControlRouter as C, DECIMALS_BY_TOKEN as D, FarmsRouter as F, GLOW_WEIGHT_DECIMAL_PRECISION as G, HUB_URL as H, KickstarterRouter as K, MAX_WEIGHT as M, OffchainFractionsError as O, PAYMENT_CURRENCIES as P, RegionRouter as R, TRANSFER_TYPES as T, USDG_WEIGHT_DECIMAL_PRECISION as U, WalletsRouter as W, useOffchainFractions as a, ForwarderError as b, OFF_CHAIN_PAYMENT_CURRENCIES as c, allRegions as d, usStates as e, countries as f, FORWARDER_ABI as g, getAddresses as h, GCA_URLS as i, regionMetadata as r, useForwarder as u };
|
|
3048
|
+
//# sourceMappingURL=farms-router-Da1P3B8g.js.map
|