@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.
Files changed (34) hide show
  1. package/dist/cjs/browser.d.ts +1 -0
  2. package/dist/cjs/browser.js +6 -1
  3. package/dist/cjs/browser.js.map +1 -1
  4. package/dist/cjs/constants/addresses.d.ts +1 -1
  5. package/dist/cjs/{farms-router-DCCy-acA.js → farms-router-CDsqKkP6.js} +1093 -210
  6. package/dist/cjs/farms-router-CDsqKkP6.js.map +1 -0
  7. package/dist/cjs/index.d.ts +1 -0
  8. package/dist/cjs/index.js +2 -1
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/lib/abis/offchainFractions.d.ts +473 -0
  11. package/dist/cjs/lib/control-api/farms-router.d.ts +2 -1
  12. package/dist/cjs/lib/hooks/use-offchain-fractions.d.ts +60 -0
  13. package/dist/cjs/lib/types/index.d.ts +64 -0
  14. package/dist/esm/browser.d.ts +1 -0
  15. package/dist/esm/browser.js +2 -2
  16. package/dist/esm/constants/addresses.d.ts +1 -1
  17. package/dist/esm/{farms-router-XDjQOQ-c.js → farms-router-Da1P3B8g.js} +1093 -211
  18. package/dist/esm/farms-router-Da1P3B8g.js.map +1 -0
  19. package/dist/esm/index.d.ts +1 -0
  20. package/dist/esm/index.js +2 -2
  21. package/dist/esm/lib/abis/offchainFractions.d.ts +473 -0
  22. package/dist/esm/lib/control-api/farms-router.d.ts +2 -1
  23. package/dist/esm/lib/hooks/use-offchain-fractions.d.ts +60 -0
  24. package/dist/esm/lib/types/index.d.ts +64 -0
  25. package/package.json +1 -1
  26. package/src/browser.ts +1 -0
  27. package/src/constants/addresses.ts +7 -1
  28. package/src/index.ts +1 -0
  29. package/src/lib/abis/offchainFractions.ts +332 -0
  30. package/src/lib/control-api/farms-router.ts +37 -2
  31. package/src/lib/hooks/use-offchain-fractions.ts +737 -0
  32. package/src/lib/types/index.ts +69 -0
  33. package/dist/cjs/farms-router-DCCy-acA.js.map +0 -1
  34. 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 forward transaction
541
- let tx;
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 (txError) {
554
- throw new Error(parseEthersError(txError));
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
- * Forward tokens for protocol fee payment and GCTL minting with staking
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 payProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, regionId, currency = "USDC") {
1465
+ async function getFraction(creator, id) {
564
1466
  assertSigner(signer);
565
- // GCTL minting only supports USDC and USDG
566
- if (currency === "GLW") {
567
- throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
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
- return forwardTokens({
570
- amount,
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
- * Forward tokens for protocol fee payment only
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 sponsorProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
1496
+ async function getStepsPurchased(user, creator, id) {
611
1497
  assertSigner(signer);
612
- return forwardTokens({
613
- amount,
614
- userAddress,
615
- type: TRANSFER_TYPES.SponsorProtocolFee,
616
- currency,
617
- applicationId,
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
- * Forward USDC to mint GCTL and stake to a region
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 mintGCTLAndStake(amount, userAddress, regionId, currency = "USDC") {
624
- assertSigner(signer);
625
- // GCTL minting only supports USDC and USDG
626
- if (currency === "GLW") {
627
- throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
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
- * Forward USDC to mint GCTL (existing functionality, keeping for compatibility)
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 mintGCTL(amount, userAddress, currency = "USDC") {
641
- assertSigner(signer);
642
- // GCTL minting only supports USDC and USDG
643
- if (currency === "GLW") {
644
- throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
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
- * Forward tokens to pay audit fees (USDC only, calls forward())
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 payAuditFees(amount, userAddress, applicationId) {
657
- assertSigner(signer);
658
- return forwardTokens({
659
- amount,
660
- userAddress,
661
- type: TRANSFER_TYPES.PayAuditFees,
662
- currency: "USDC",
663
- applicationId,
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
- * Forward tokens to buy a solar farm
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 buySolarFarm(amount, userAddress, farmId, currency = "USDC") {
670
- assertSigner(signer);
671
- return forwardTokens({
672
- amount,
673
- userAddress,
674
- type: TRANSFER_TYPES.BuySolarFarm,
675
- currency,
676
- farmId,
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
- * Forward tokens to commit to a Kickstarter (USDC or USDG only)
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 commitKickstarter(amount, userAddress, kickstarterId, currency = "USDC") {
683
- assertSigner(signer);
684
- if (currency === "GLW") {
685
- throw new Error("CommitKickstarter supports only USDC or USDG");
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 forwarding with type-specific handling
697
- * @param params Forward parameters
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 estimateGasForForward(params, ethPriceInUSD) {
1584
+ async function estimateGasForCreateFraction(params, ethPriceInUSD) {
701
1585
  assertSigner(signer);
702
1586
  try {
703
- const forwarderContract = getForwarderContract();
704
- if (!forwarderContract)
705
- throw new Error(exports.ForwarderError.CONTRACT_NOT_AVAILABLE);
706
- const { amount, currency = "USDC" } = params;
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 = !isAuditFees && currency === "USDC"
736
- ? await forwarderContract
737
- .getFunction("swapUSDCAndForwardUSDG")
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
- * Mint test USDC (only works on testnets with mintable USDC contracts)
760
- * @param amount Amount of USDC to mint (BigNumber, 6 decimals)
761
- * @param recipient Address to mint USDC to
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 mintTestUSDC(amount, recipient) {
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 usdcContract = getTokenContract("USDC"); // Use getTokenContract for consistency
770
- if (!usdcContract)
771
- throw new Error(exports.ForwarderError.CONTRACT_NOT_AVAILABLE);
772
- setIsProcessing(true);
773
- // Try to call mint function (common for test tokens)
774
- const tx = await usdcContract.mint(recipient, amount);
775
- await tx.wait();
776
- return tx.hash;
777
- }
778
- catch (error) {
779
- // If mint function doesn't exist or fails, provide helpful error
780
- const errorMessage = parseEthersError(error);
781
- if (errorMessage.includes("mint")) {
782
- throw new Error("This USDC contract doesn't support minting");
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
- finally {
787
- setIsProcessing(false);
1643
+ catch (error) {
1644
+ throw new Error(parseEthersError(error));
788
1645
  }
789
1646
  }
790
1647
  return {
791
- // New methods for different forward types
792
- forwardTokens,
793
- payProtocolFeeAndMintGCTLAndStake,
794
- sponsorProtocolFeeAndMintGCTLAndStake,
795
- sponsorProtocolFee,
796
- payProtocolFee,
797
- mintGCTLAndStake,
798
- mintGCTL,
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 methods
807
- estimateGasForForward,
808
- mintTestUSDC,
809
- constructForwardMessage,
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
- //# sourceMappingURL=farms-router-DCCy-acA.js.map
3070
+ exports.useOffchainFractions = useOffchainFractions;
3071
+ //# sourceMappingURL=farms-router-CDsqKkP6.js.map