diff --git a/examples/refund/allow-claim-boosted.ts b/examples/refund/allow-claim-boosted.ts new file mode 100644 index 0000000..2070a40 --- /dev/null +++ b/examples/refund/allow-claim-boosted.ts @@ -0,0 +1,22 @@ +import { RefundManagerSingleton } from "../../src"; +import { provider, signAndExecuteTransaction, suiProviderUrl, user } from "../common"; +import { delegateeKeypair, delegateeUser } from "../dca/common"; + +// yarn ts-node examples/refund/allow-claim-boosted.ts +(async () => { + const txData = RefundManagerSingleton.getAllowBoostedClaim({ + publisherObjectId: RefundManagerSingleton.REFUND_POOL_PUBLISHER_OBJECT_ID, + affectedAddress: "", + newAddress: "", + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + }); + + const res = await provider.devInspectTransactionBlock({ + sender: delegateeUser, + transactionBlock: txData.tx, + }); + + // const res = await signAndExecuteTransaction(txData.tx, delegateeKeypair); + + console.debug("res: ", res); +})(); diff --git a/examples/refund/claim-boosted-refund.ts b/examples/refund/claim-boosted-refund.ts new file mode 100644 index 0000000..9df74db --- /dev/null +++ b/examples/refund/claim-boosted-refund.ts @@ -0,0 +1,30 @@ +import { RefundManagerSingleton } from "../../src"; +import { keypair, provider, signAndExecuteTransaction, suiProviderUrl, user } from "../common"; + +// yarn ts-node examples/refund/claim-boosted-refund.ts +(async () => { + const refundManager = RefundManagerSingleton.getInstance(suiProviderUrl); + + const amount = await refundManager.getClaimAmount({ + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + affectedAddress: user, + }); + console.debug("amount: ", amount); + + const boostedClaimCap = await refundManager.getBoostedClaimCap({ ownerAddress: user }); + console.debug("boostedClaimCap: ", boostedClaimCap); + + const txData = RefundManagerSingleton.getClaimRefundBoostedTransaction({ + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + boostedClaimCap: boostedClaimCap, + }); + + // const res = await provider.devInspectTransactionBlock({ + // sender: user, + // transactionBlock: txData.tx, + // }); + + const res = await signAndExecuteTransaction(txData.tx, keypair); + + console.debug("res: ", res); +})(); diff --git a/examples/refund/claim-normal-refund.ts b/examples/refund/claim-normal-refund.ts new file mode 100644 index 0000000..7ac3c24 --- /dev/null +++ b/examples/refund/claim-normal-refund.ts @@ -0,0 +1,27 @@ +import { RefundManagerSingleton } from "../../src"; +import { keypair, provider, signAndExecuteTransaction, suiProviderUrl, user } from "../common"; + +// yarn ts-node examples/refund/claim-normal-refund.ts +(async () => { + const refundManager = RefundManagerSingleton.getInstance(suiProviderUrl); + + const amount = await refundManager.getClaimAmount({ + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + affectedAddress: user, + }); + + console.debug("amount: ", amount); + + const txData = RefundManagerSingleton.getClaimRefundTransaction({ + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + }); + + // const res = await provider.devInspectTransactionBlock({ + // sender: user, + // transactionBlock: txData.tx, + // }); + + const res = await signAndExecuteTransaction(txData.tx, keypair); + + console.debug("res: ", res); +})(); diff --git a/examples/refund/start-claim-phase.ts b/examples/refund/start-claim-phase.ts new file mode 100644 index 0000000..1772cbf --- /dev/null +++ b/examples/refund/start-claim-phase.ts @@ -0,0 +1,21 @@ +import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui.js/utils"; +import { RefundManagerSingleton } from "../../src"; +import { RefundPoolPhase } from "../../src/managers/refund/utils"; +import { keypair, provider, signAndExecuteTransaction, suiProviderUrl, user } from "../common"; + +// yarn ts-node examples/refund/start-claim-phase.ts +(async () => { + const fundingPhaseTxAndRes = RefundManagerSingleton.startClaimPhase({ + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + }); + + const res = await provider.devInspectTransactionBlock({ + sender: "0x935fd79d69b98dc87cd43d4112e621fe92967c7f33fa232ddf4f6351bb1b9a19", + transactionBlock: fundingPhaseTxAndRes.tx, + }); + + // const res = await signAndExecuteTransaction(fundingPhaseTxAndRes.tx, keypair); + + console.debug("result: ", fundingPhaseTxAndRes); + console.debug("res: ", res); +})(); diff --git a/examples/refund/start-funding-phase.ts b/examples/refund/start-funding-phase.ts new file mode 100644 index 0000000..c934fd6 --- /dev/null +++ b/examples/refund/start-funding-phase.ts @@ -0,0 +1,24 @@ +import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui.js/utils"; +import { RefundManagerSingleton } from "../../src"; +import { RefundPoolPhase } from "../../src/managers/refund/utils"; +import { keypair, provider, signAndExecuteTransaction, suiProviderUrl, user } from "../common"; + +// yarn ts-node examples/refund/start-funding-phase.ts +(async () => { + const fundingPhaseTxAndRes = RefundManagerSingleton.startFundingPhase({ + publisherObjectId: RefundManagerSingleton.REFUND_POOL_PUBLISHER_OBJECT_ID, + poolObjectId: RefundManagerSingleton.REFUND_POOL_OBJECT_ID, + timeoutMilliseconds: 600000, // 10 minutes + clock: SUI_CLOCK_OBJECT_ID, + }); + + const res = await provider.devInspectTransactionBlock({ + sender: "0x935fd79d69b98dc87cd43d4112e621fe92967c7f33fa232ddf4f6351bb1b9a19", + transactionBlock: fundingPhaseTxAndRes.tx, + }); + + // const res = await signAndExecuteTransaction(fundingPhaseTxAndRes.tx, keypair); + + console.debug("result: ", fundingPhaseTxAndRes); + console.debug("res: ", res); +})(); diff --git a/src/managers/refund/RefundManager.ts b/src/managers/refund/RefundManager.ts index 25ad49c..600e6bc 100644 --- a/src/managers/refund/RefundManager.ts +++ b/src/managers/refund/RefundManager.ts @@ -5,10 +5,11 @@ import { TransactionBlock } from "@mysten/sui.js/transactions"; import { verifyPersonalMessage } from "@mysten/sui.js/verify"; import { ObjectArg } from "../../transactions/types"; import { obj } from "../../transactions/utils"; -import { hexStringToByteArray } from "./utils"; +import { DecodedAmount, hexStringToByteArray } from "./utils"; import BigNumber from "bignumber.js"; import { SUI_DENOMINATOR } from "../.."; import { getAllOwnedObjects } from "../../providers/utils/getAllOwnedObjects"; +import { bcs } from "@mysten/sui.js/bcs"; /** * @class RefundManagerSingleton @@ -22,6 +23,10 @@ export class RefundManagerSingleton { public static REFUND_POOL_OBJECT_ID = "0xf8f7e8e3c4a4c08e5a334c45ed0b3c669b3b86098e7fc1ff9cfe062105c1f74e"; public static REFUND_POOL_PUBLISHER_OBJECT_ID = "0x492ef3058c292d1d343545001c65ff42baef932a4fb06be79968137ec381a4fc"; public static REFUND_BOOSTED_CLAIM_CAP_STRUCT_TYPE_NAME = "BoostedClaimCap"; + public static REFUND_MODULE_NAME = "refund"; + public static REFUND_BOOSTED_MODULE_NAME = "booster"; + // eslint-disable-next-line max-len + public static BOOSTER_OBJECT_TYPE = `${RefundManagerSingleton.REFUND_PACKAGE_ADDRESS}::${RefundManagerSingleton.REFUND_BOOSTED_MODULE_NAME}::${this.REFUND_BOOSTED_CLAIM_CAP_STRUCT_TYPE_NAME}`; public static REFUND_GAS_BUGET = 50_000_000; @@ -107,6 +112,56 @@ export class RefundManagerSingleton { return { tx, txRes }; } + public static startFundingPhase({ + publisherObjectId, + poolObjectId, + timeoutMilliseconds, + clock, + + transaction, + }: { + publisherObjectId: ObjectArg; + poolObjectId: ObjectArg; + timeoutMilliseconds: number; + clock: ObjectArg; + + transaction?: TransactionBlock; + }) { + const tx = transaction ?? new TransactionBlock(); + + const txRes = tx.moveCall({ + target: `${RefundManagerSingleton.REFUND_PACKAGE_ADDRESS}::refund::start_funding_phase`, + typeArguments: [], + arguments: [obj(tx, publisherObjectId), obj(tx, poolObjectId), tx.pure(timeoutMilliseconds), obj(tx, clock)], + }); + + tx.setGasBudget(RefundManagerSingleton.REFUND_GAS_BUGET); + + return { tx, txRes }; + } + + public static startClaimPhase({ + poolObjectId, + + transaction, + }: { + poolObjectId: ObjectArg; + + transaction?: TransactionBlock; + }) { + const tx = transaction ?? new TransactionBlock(); + + const txRes = tx.moveCall({ + target: `${RefundManagerSingleton.REFUND_PACKAGE_ADDRESS}::refund::start_claim_phase`, + typeArguments: [], + arguments: [obj(tx, poolObjectId)], + }); + + tx.setGasBudget(RefundManagerSingleton.REFUND_GAS_BUGET); + + return { tx, txRes }; + } + public async getCurrentRefundPhase({ poolObjectId, transaction, @@ -213,9 +268,22 @@ export class RefundManagerSingleton { throw new Error("Return values are undefined"); } - const amount = returnValues[0][0][0]; + const rawAmountBytes = returnValues[0][0]; + const decoded: DecodedAmount = bcs.de("Option", new Uint8Array(rawAmountBytes)); + let amount: string; - return { mist: amount, sui: new BigNumber(amount).div(SUI_DENOMINATOR).toString() }; + if ("Some" in decoded && decoded.Some) { + amount = decoded.Some; + } else if ("None" in decoded && decoded.None === true) { + amount = "0"; // Use "0" if decoded.None is true + } else { + throw new Error("Decoded amount has an invalid shape"); + } + + const amountInMist = amount.toString(); + const amountInSui = new BigNumber(amount).div(SUI_DENOMINATOR).toString(); + + return { mist: amountInMist, sui: amountInSui }; } public async getClaimAmountBoosted({ @@ -250,9 +318,22 @@ export class RefundManagerSingleton { throw new Error("Return values are undefined"); } - const amount = returnValues[0][0][0]; + const rawAmountBytes = returnValues[0][0]; + const decoded: DecodedAmount = bcs.de("Option", new Uint8Array(rawAmountBytes)); + let amount: string; + + if ("Some" in decoded && decoded.Some) { + amount = decoded.Some; + } else if ("None" in decoded && decoded.None === true) { + amount = "0"; // Use "0" if decoded.None is true + } else { + throw new Error("Decoded amount has an invalid shape"); + } + + const amountInMist = amount.toString(); + const amountInSui = new BigNumber(amount).div(SUI_DENOMINATOR).toString(); - return { mist: amount, sui: new BigNumber(amount).div(SUI_DENOMINATOR).toString() }; + return { mist: amountInMist, sui: amountInSui }; } public async getClaimAmount({ poolObjectId, affectedAddress }: { poolObjectId: string; affectedAddress: string }) { @@ -271,7 +352,9 @@ export class RefundManagerSingleton { options: { owner: ownerAddress, // TODO: Check for correctness - filter: { StructType: RefundManagerSingleton.REFUND_BOOSTED_CLAIM_CAP_STRUCT_TYPE_NAME }, + // Because this might not work in case of upgraded package id, so as a solution, + // we need to use another filter, which would allow to fetch `BoostedClaimCap` for multiple package addresses + filter: { StructType: RefundManagerSingleton.BOOSTER_OBJECT_TYPE }, }, }); @@ -312,7 +395,7 @@ export class RefundManagerSingleton { return { tx, txRes }; } - public static getClaimRefundBoosted({ + public static getClaimRefundBoostedTransaction({ boostedClaimCap, poolObjectId, diff --git a/src/managers/refund/utils.ts b/src/managers/refund/utils.ts index 3fac51a..84939d6 100644 --- a/src/managers/refund/utils.ts +++ b/src/managers/refund/utils.ts @@ -14,3 +14,12 @@ export function hexStringToByteArray(hexString: string): Uint8Array { } return byteArray; } + +export type DecodedAmount = { Some?: string } | { None?: boolean }; + +export enum RefundPoolPhase { + AddressAddition = 1, // Phase for adding addresses + Funding = 2, // Phase for funding + Claim = 3, // Phase for claiming refunds + Reclaim = 4, // Phase for reclaiming refunds +}