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