@glowlabs-org/utils 0.2.94 → 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-pMZv3N9H.js → farms-router-DCsg5FYX.js} +898 -34
- 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-sm5H9Ax6.js → farms-router-BCnakF0d.js} +898 -35
- 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-forwarder.ts +27 -8
- package/src/lib/hooks/use-offchain-fractions.ts +737 -0
- package/dist/cjs/farms-router-pMZv3N9H.js.map +0 -1
- package/dist/esm/farms-router-sm5H9Ax6.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
|
|
@@ -513,12 +517,14 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
513
517
|
default:
|
|
514
518
|
throw new Error(`Unsupported currency for forwarding: ${currency}`);
|
|
515
519
|
}
|
|
520
|
+
// Determine sendToCounterfactualWallet based on currency
|
|
521
|
+
const sendToCounterfactualWallet = currency === "GLW" || currency === "USDG";
|
|
516
522
|
// Run a static call first to surface any revert reason (ethers v6)
|
|
517
523
|
try {
|
|
518
524
|
if (!isAuditFees && currency === "USDC") {
|
|
519
525
|
await forwarderContract
|
|
520
526
|
.getFunction("swapUSDCAndForwardUSDG")
|
|
521
|
-
.staticCall(amount, ADDRESSES.FOUNDATION_WALLET,
|
|
527
|
+
.staticCall(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message, {
|
|
522
528
|
from: owner,
|
|
523
529
|
});
|
|
524
530
|
}
|
|
@@ -527,27 +533,27 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
527
533
|
.getFunction("forward")
|
|
528
534
|
.staticCall(tokenAddress, isAuditFees
|
|
529
535
|
? ADDRESSES.AUDIT_FEE_WALLET
|
|
530
|
-
: ADDRESSES.FOUNDATION_WALLET, amount,
|
|
536
|
+
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message, { from: owner });
|
|
531
537
|
}
|
|
532
538
|
}
|
|
533
539
|
catch (staticError) {
|
|
534
|
-
throw new Error(parseEthersError(staticError));
|
|
540
|
+
throw new Error(parseEthersError$1(staticError));
|
|
535
541
|
}
|
|
536
542
|
// Execute the forward transaction
|
|
537
543
|
let tx;
|
|
538
544
|
if (!isAuditFees && currency === "USDC") {
|
|
539
|
-
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(amount, ADDRESSES.FOUNDATION_WALLET,
|
|
545
|
+
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message);
|
|
540
546
|
}
|
|
541
547
|
else {
|
|
542
548
|
tx = await forwarderContract.getFunction("forward")(tokenAddress, isAuditFees
|
|
543
549
|
? ADDRESSES.AUDIT_FEE_WALLET
|
|
544
|
-
: ADDRESSES.FOUNDATION_WALLET, amount,
|
|
550
|
+
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
545
551
|
}
|
|
546
552
|
await tx.wait();
|
|
547
553
|
return tx.hash;
|
|
548
554
|
}
|
|
549
555
|
catch (txError) {
|
|
550
|
-
throw new Error(parseEthersError(txError));
|
|
556
|
+
throw new Error(parseEthersError$1(txError));
|
|
551
557
|
}
|
|
552
558
|
finally {
|
|
553
559
|
setIsProcessing(false);
|
|
@@ -557,7 +563,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
557
563
|
* Forward tokens for protocol fee payment and GCTL minting with staking
|
|
558
564
|
*/
|
|
559
565
|
async function payProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, regionId, currency = "USDC") {
|
|
560
|
-
assertSigner(signer);
|
|
566
|
+
assertSigner$1(signer);
|
|
561
567
|
// GCTL minting only supports USDC and USDG
|
|
562
568
|
if (currency === "GLW") {
|
|
563
569
|
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
@@ -575,7 +581,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
575
581
|
* Forward tokens for protocol fee payment and GCTL minting with staking
|
|
576
582
|
*/
|
|
577
583
|
async function sponsorProtocolFeeAndMintGCTLAndStake(amount, userAddress, applicationId, currency = "USDC") {
|
|
578
|
-
assertSigner(signer);
|
|
584
|
+
assertSigner$1(signer);
|
|
579
585
|
if (currency === "GLW") {
|
|
580
586
|
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
581
587
|
}
|
|
@@ -591,7 +597,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
591
597
|
* Forward tokens for protocol fee payment only
|
|
592
598
|
*/
|
|
593
599
|
async function payProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
|
|
594
|
-
assertSigner(signer);
|
|
600
|
+
assertSigner$1(signer);
|
|
595
601
|
return forwardTokens({
|
|
596
602
|
amount,
|
|
597
603
|
userAddress,
|
|
@@ -604,7 +610,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
604
610
|
* Forward tokens for protocol fee payment only
|
|
605
611
|
*/
|
|
606
612
|
async function sponsorProtocolFee(amount, userAddress, applicationId, currency = "USDC") {
|
|
607
|
-
assertSigner(signer);
|
|
613
|
+
assertSigner$1(signer);
|
|
608
614
|
return forwardTokens({
|
|
609
615
|
amount,
|
|
610
616
|
userAddress,
|
|
@@ -617,7 +623,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
617
623
|
* Forward USDC to mint GCTL and stake to a region
|
|
618
624
|
*/
|
|
619
625
|
async function mintGCTLAndStake(amount, userAddress, regionId, currency = "USDC") {
|
|
620
|
-
assertSigner(signer);
|
|
626
|
+
assertSigner$1(signer);
|
|
621
627
|
// GCTL minting only supports USDC and USDG
|
|
622
628
|
if (currency === "GLW") {
|
|
623
629
|
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
@@ -634,7 +640,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
634
640
|
* Forward USDC to mint GCTL (existing functionality, keeping for compatibility)
|
|
635
641
|
*/
|
|
636
642
|
async function mintGCTL(amount, userAddress, currency = "USDC") {
|
|
637
|
-
assertSigner(signer);
|
|
643
|
+
assertSigner$1(signer);
|
|
638
644
|
// GCTL minting only supports USDC and USDG
|
|
639
645
|
if (currency === "GLW") {
|
|
640
646
|
throw new Error("GCTL minting is not supported with GLW payment. Use USDC or USDG.");
|
|
@@ -650,7 +656,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
650
656
|
* Forward tokens to pay audit fees (USDC only, calls forward())
|
|
651
657
|
*/
|
|
652
658
|
async function payAuditFees(amount, userAddress, applicationId) {
|
|
653
|
-
assertSigner(signer);
|
|
659
|
+
assertSigner$1(signer);
|
|
654
660
|
return forwardTokens({
|
|
655
661
|
amount,
|
|
656
662
|
userAddress,
|
|
@@ -663,7 +669,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
663
669
|
* Forward tokens to buy a solar farm
|
|
664
670
|
*/
|
|
665
671
|
async function buySolarFarm(amount, userAddress, farmId, currency = "USDC") {
|
|
666
|
-
assertSigner(signer);
|
|
672
|
+
assertSigner$1(signer);
|
|
667
673
|
return forwardTokens({
|
|
668
674
|
amount,
|
|
669
675
|
userAddress,
|
|
@@ -676,7 +682,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
676
682
|
* Forward tokens to commit to a Kickstarter (USDC or USDG only)
|
|
677
683
|
*/
|
|
678
684
|
async function commitKickstarter(amount, userAddress, kickstarterId, currency = "USDC") {
|
|
679
|
-
assertSigner(signer);
|
|
685
|
+
assertSigner$1(signer);
|
|
680
686
|
if (currency === "GLW") {
|
|
681
687
|
throw new Error("CommitKickstarter supports only USDC or USDG");
|
|
682
688
|
}
|
|
@@ -694,7 +700,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
694
700
|
* @param ethPriceInUSD Current ETH price in USD (for cost estimation)
|
|
695
701
|
*/
|
|
696
702
|
async function estimateGasForForward(params, ethPriceInUSD) {
|
|
697
|
-
assertSigner(signer);
|
|
703
|
+
assertSigner$1(signer);
|
|
698
704
|
try {
|
|
699
705
|
const forwarderContract = getForwarderContract();
|
|
700
706
|
if (!forwarderContract)
|
|
@@ -706,6 +712,8 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
706
712
|
}
|
|
707
713
|
// Construct the appropriate message for this forward type
|
|
708
714
|
const message = constructForwardMessage(params);
|
|
715
|
+
// Determine sendToCounterfactualWallet based on currency
|
|
716
|
+
const sendToCounterfactualWallet = currency === "GLW" || currency === "USDG";
|
|
709
717
|
// Get token address
|
|
710
718
|
let tokenAddress;
|
|
711
719
|
switch (currency) {
|
|
@@ -729,12 +737,12 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
729
737
|
const estimatedGas = !isAuditFees && currency === "USDC"
|
|
730
738
|
? await forwarderContract
|
|
731
739
|
.getFunction("swapUSDCAndForwardUSDG")
|
|
732
|
-
.estimateGas(amount, ADDRESSES.FOUNDATION_WALLET,
|
|
740
|
+
.estimateGas(amount, ADDRESSES.FOUNDATION_WALLET, sendToCounterfactualWallet, message)
|
|
733
741
|
: await forwarderContract
|
|
734
742
|
.getFunction("forward")
|
|
735
743
|
.estimateGas(tokenAddress, isAuditFees
|
|
736
744
|
? ADDRESSES.AUDIT_FEE_WALLET
|
|
737
|
-
: ADDRESSES.FOUNDATION_WALLET, amount,
|
|
745
|
+
: ADDRESSES.FOUNDATION_WALLET, amount, sendToCounterfactualWallet, message);
|
|
738
746
|
const estimatedCost = estimatedGas * gasPrice;
|
|
739
747
|
if (ethPriceInUSD) {
|
|
740
748
|
const estimatedCostInEth = formatEther(estimatedCost);
|
|
@@ -746,7 +754,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
746
754
|
}
|
|
747
755
|
}
|
|
748
756
|
catch (error) {
|
|
749
|
-
throw new Error(parseEthersError(error));
|
|
757
|
+
throw new Error(parseEthersError$1(error));
|
|
750
758
|
}
|
|
751
759
|
}
|
|
752
760
|
/**
|
|
@@ -755,7 +763,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
755
763
|
* @param recipient Address to mint USDC to
|
|
756
764
|
*/
|
|
757
765
|
async function mintTestUSDC(amount, recipient) {
|
|
758
|
-
assertSigner(signer);
|
|
766
|
+
assertSigner$1(signer);
|
|
759
767
|
if (CHAIN_ID !== 11155111) {
|
|
760
768
|
throw new Error("Minting test USDC is only supported on Sepolia");
|
|
761
769
|
}
|
|
@@ -771,7 +779,7 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
771
779
|
}
|
|
772
780
|
catch (error) {
|
|
773
781
|
// If mint function doesn't exist or fails, provide helpful error
|
|
774
|
-
const errorMessage = parseEthersError(error);
|
|
782
|
+
const errorMessage = parseEthersError$1(error);
|
|
775
783
|
if (errorMessage.includes("mint")) {
|
|
776
784
|
throw new Error("This USDC contract doesn't support minting");
|
|
777
785
|
}
|
|
@@ -811,6 +819,861 @@ function useForwarder(signer, CHAIN_ID) {
|
|
|
811
819
|
};
|
|
812
820
|
}
|
|
813
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
|
+
|
|
814
1677
|
// --------------------------------------------------------------------------
|
|
815
1678
|
/**
|
|
816
1679
|
* Extract a useful error message from an unknown error value.
|
|
@@ -2158,5 +3021,5 @@ function FarmsRouter(baseUrl) {
|
|
|
2158
3021
|
};
|
|
2159
3022
|
}
|
|
2160
3023
|
|
|
2161
|
-
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,
|
|
2162
|
-
//# 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
|