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