@minswap/noodles-sdk 0.0.38-next.0 → 0.0.38-next.1
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/index.cjs +3317 -1
- package/dist/index.js +3152 -1
- package/dist/pay.cjs +905 -1
- package/dist/pay.js +829 -1
- package/package.json +1 -1
package/dist/pay.js
CHANGED
|
@@ -1 +1,829 @@
|
|
|
1
|
-
import e from"@minswap/tiny-invariant";import{Transaction as t}from"@mysten/sui/transactions";import{normalizeStructTag as i}from"@mysten/sui/utils";import{SuiClient as n,getFullnodeUrl as s}from"@mysten/sui/client";async function a(e,t,i,n){let{effects:{gasUsed:s,status:a}}=await e.devInspectTransactionBlock({sender:i,transactionBlock:t});if("success"!==a.status)throw Error(`Transaction failed during dev inspect: ${a.error??""}`);let r=BigInt(s.computationCost)+BigInt(s.storageCost)-BigInt(s.storageRebate),u=BigInt(1e4*Number(n));return r*(BigInt(1e4)+u)/BigInt(1e4)}let r="0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",u=async(e,t,i,n)=>{let s,a=[],r=!0;for(;r;)try{let i=await e.getCoins({owner:t,coinType:n,cursor:s});a=[...a,...i.data],r=i.hasNextPage,s=i.nextCursor}catch(e){console.error("Error fetching data:",e),r=!1}let u=((e,t,i)=>{let n;if(e?.length){do{n=!1;for(let s=0;s<e.length-1;s++){let a=BigInt(e[s][t]),r=BigInt(e[s+1][t]);if("desc"===i?a<r:a>r){let t=e[s];e[s]=e[s+1],e[s+1]=t,n=!0}}}while(n);return e}})(a.map(e=>({...e,balance:e.balance})),"balance","desc")??[],o="0",l=[],d=[];for(let e of u)if(o=(BigInt(e.balance)+BigInt(o)).toString(10),l.push(e.coinObjectId),d.push(e),BigInt(o)>=BigInt(i))break;return{objectIds:l,balance:o,objectCoins:d}};async function o(e,i){let{inheritTx:n,sender:s,feeAmount:a,suiInputAmount:o}=e,l=n||new t;l.setGasBudget(a);let{objectCoins:d}=await u(i,s,(BigInt(o||"0")+a).toString(),r);return l.setGasPayment(d.map(e=>({objectId:e.coinObjectId,version:e.version,digest:e.digest}))),l}let l="0x25fa0eb553dd69fa6a8028f73a38f1df7f289fa88a58ed3d7dc5a5360155bb19",d="0x8b1a8ac6e34688cd8f8f8e5fefa9d5fb9e1ff74d2f132f8e208ae5ac9cc530bb",c="0x13d101f44a0710a5475bec5f492a87b80e03d92d1cf540f26096f65850b23d26";class g{sdk;constructor(e){this.sdk=e}async getConfig(){let t=await this.sdk.suiClient.getObject({id:d,options:{showContent:!0}});e(t.data?.content,"Config object not found or has no content"),e(t.data?.content.dataType==="moveObject","Config object is not a Move object");let i=t.data.content.fields,n=i.id.id,s=i.fee_receiver,a=i.tiers.fields.contents,r={};for(let e of a){let t=e.fields.key,i=e.fields.value.fields.duration,n=e.fields.value.fields.amount,s=e.fields.value.fields.point,a=e.fields.value.fields.original_amount;r[t]={duration:BigInt(i),amount:BigInt(n),point:BigInt(s),originalAmount:BigInt(a)}}return{id:n,receiverAddress:s,tiers:r}}async buildPayBoostingTx({walletAddress:n,tierId:s,boostingCoinType:r,currentTx:u}){let c=await this.getConfig();e(s in c.tiers,"Invalid tier ID");let g=c.tiers[s].amount,p=u||new t,f=p.splitCoins(p.gas,[p.pure.u64(g)]);p.moveCall({target:`${l}::boosting::pay`,typeArguments:[i(r)],arguments:[p.object(d),p.pure.u64(s),f]}),p.setSender(n);let b=await a(this.sdk.suiClient,p,n,0);return o({inheritTx:p,sender:n,feeAmount:b},this.sdk.suiClient)}async buildAddTierTx({adminAddress:e,tierId:i,duration:n,point:s,originalAmount:r,amount:u,currentTx:g}){let p=g||new t;p.moveCall({target:`${l}::config::add_tier`,typeArguments:[],arguments:[p.object(d),p.object(c),p.pure.u64(i),p.pure.u64(s),p.pure.u64(n),p.pure.u64(r),p.pure.u64(u)]}),p.setSender(e);let f=await a(this.sdk.suiClient,p,e,0);return o({inheritTx:p,sender:e,feeAmount:f},this.sdk.suiClient)}async buildUpdateTierTx({adminAddress:e,tierId:i,duration:n,amount:s,originalAmount:r,point:u,currentTx:g}){let p=g||new t;p.moveCall({target:`${l}::config::update_tier`,typeArguments:[],arguments:[p.object(d),p.object(c),p.pure.u64(i),p.pure.u64(n),p.pure.u64(s),p.pure.u64(u),p.pure.u64(r)]}),p.setSender(e);let f=await a(this.sdk.suiClient,p,e,0);return o({inheritTx:p,sender:e,feeAmount:f},this.sdk.suiClient)}async buildRemoveTierTx({adminAddress:e,tierId:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${l}::config::remove_tier`,typeArguments:[],arguments:[s.object(d),s.object(c),s.pure.u64(i)]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}async buildUpdateFeeReceiver({adminAddress:e,newReceiverAddress:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${l}::config::update_fee_receiver`,typeArguments:[],arguments:[s.object(d),s.object(c),s.pure.address(i)]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}async buildPayBoostingWithAdminTx({walletAddress:e,tierId:n,customAmount:s,boostingCoinType:r,currentTx:u}){let g=u||new t,p=g.splitCoins(g.gas,[g.pure.u64(s)]);g.moveCall({target:`${l}::boosting::pay_with_admin`,typeArguments:[i(r)],arguments:[g.object(c),g.object(d),g.pure.u64(n),g.pure.u64(s),p]}),g.setSender(e);let f=await a(this.sdk.suiClient,g,e,0);return o({inheritTx:g,sender:e,feeAmount:f},this.sdk.suiClient)}}async function p(e,n){let{account:s,amount:a,splits:o,coinType:l,inheritTx:d,inspecTransaction:c,isSponsored:g=!1}=e,p=d??new t,{objectIds:f}=await u(n,s,a,l),b=f[0];if(i(l)===i(r)&&!g){let e;return c?(f.length>1&&p.mergeCoins(p.object(b),f.slice(1).map(e=>p.object(e))),e=p.splitCoins(p.object(b),o)):e=p.splitCoins(p.gas,o),{tx:p,coinData:e}}f.length>1&&p.mergeCoins(p.object(b),f.slice(1).map(e=>p.object(e)));let m=p.splitCoins(p.object(b),o);return{tx:p,coinData:m}}let f="0x5dd4286ec234c117bb9577d4eaed1885b70d73ab1d3dab2794d0ab9a2c06c4a1",b="0x095b941beff22213fe8ccb223e4d1c5b4ede4dcd7f82ca4bb18c9a45fd3d6e20",m="0xdc27fe8187131b7fc095fccd213b79f72312793152a2483d1f67199c390e256d",C="0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";class h{sdk;constructor(e){this.sdk=e}async getConfig(){let t=await this.sdk.suiClient.getObject({id:b,options:{showContent:!0}});e(t.data,"Config object not found"),e(t.data.content,"Config object has no content"),e(t.data?.content.dataType==="moveObject","Config object is not a Move object");let i=t.data.content.fields,n=i.id.id,s=i.fee_receiver,a=i.paused,r=i.accepted_payment_type,u=i.plans.fields.contents,o={};for(let e of u){let t=e.fields.key,i=e.fields.value.fields.id,n=e.fields.value.fields.price,s=e.fields.value.fields.month_interval,a=e.fields.value.fields.ptype;o[t]={id:BigInt(i),price:BigInt(n),monthInterval:s,planType:a}}return{id:n,plans:o,feeReceiverWallet:s,paused:a,acceptedPaymentType:r}}async buildSubscribeCyclePlanTx({walletAddress:t,planId:i,reference:n,paymentCoinType:s=C,currentTx:r}){let u=await this.getConfig();e(!u.paused,"Contract is paused"),e(i in u.plans,"Invalid plan ID");let l=u.plans[i].price,{tx:d,coinData:c}=await p({account:t,amount:l.toString(),splits:[l.toString()],coinType:s,inheritTx:r},this.sdk.suiClient);d.moveCall({target:`${f}::subscription::subscribe_cycle_plan`,typeArguments:[s],arguments:[d.object(b),d.pure.u64(i),d.pure.string(n),c]}),d.setSender(t);let g=await a(this.sdk.suiClient,d,t,0);return o({inheritTx:d,sender:t,feeAmount:g},this.sdk.suiClient)}async buildBuyMoreCUPlanTx({walletAddress:t,planId:i,reference:n,paidAmount:s,paymentCoinType:r=C,currentTx:u}){let l=await this.getConfig();e(!l.paused,"Contract is paused"),e(i in l.plans,"Invalid plan ID");let{tx:d,coinData:c}=await p({account:t,amount:s.toString(),splits:[s.toString()],coinType:r,inheritTx:u},this.sdk.suiClient);d.moveCall({target:`${f}::subscription::buy_more_cu`,typeArguments:[r],arguments:[d.object(b),d.pure.u64(i),d.pure.string(n),d.pure.u64(s),c]}),d.setSender(t);let g=await a(this.sdk.suiClient,d,t,0);return o({inheritTx:d,sender:t,feeAmount:g},this.sdk.suiClient)}async buildSubscribePlanWithAdminTx({walletAddress:t,planId:i,reference:n,customAmount:s,paymentCoinType:r=C,currentTx:u}){e(!(await this.getConfig()).paused,"Contract is paused");let{tx:l,coinData:d}=await p({account:t,amount:s.toString(),splits:[s.toString()],coinType:r,inheritTx:u},this.sdk.suiClient);l.moveCall({target:`${f}::subscription::subscribe_plan_with_admin`,typeArguments:[r],arguments:[l.object(m),l.object(b),l.pure.u64(i),l.pure.string(n),l.pure.u64(s),d]}),l.setSender(t);let c=await a(this.sdk.suiClient,l,t,0);return o({inheritTx:l,sender:t,feeAmount:c},this.sdk.suiClient)}async buildAddPlanTx({adminAddress:e,planId:i,planType:n,monthInterval:s,price:r,currentTx:u}){let l=u||new t;l.moveCall({target:`${f}::config::add_plan`,typeArguments:[],arguments:[l.object(b),l.object(m),l.pure.u64(i),l.pure.u8(n),l.pure.u8(s),l.pure.u64(r)]}),l.setSender(e);let d=await a(this.sdk.suiClient,l,e,0);return o({inheritTx:l,sender:e,feeAmount:d},this.sdk.suiClient)}async buildUpdatePlanTx({adminAddress:e,planId:i,planType:n,monthInterval:s,price:r,currentTx:u}){let l=u||new t;l.moveCall({target:`${f}::config::update_plan`,typeArguments:[],arguments:[l.object(b),l.object(m),l.pure.u64(BigInt(i)),l.pure.u8(n),l.pure.u8(s),l.pure.u64(r)]}),l.setSender(e);let d=await a(this.sdk.suiClient,l,e,0);return o({inheritTx:l,sender:e,feeAmount:d},this.sdk.suiClient)}async buildRemovePlanTx({adminAddress:e,planId:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${f}::config::remove_plan`,typeArguments:[],arguments:[s.object(b),s.object(m),s.pure.u64(BigInt(i))]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}async buildUpdateFeeReceiverWalletTx({adminAddress:e,newFeeReceiverWallet:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${f}::config::update_fee_receiver`,typeArguments:[],arguments:[s.object(b),s.object(m),s.pure.address(i)]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}async buildSetPausedTx({adminAddress:e,paused:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${f}::config::set_paused`,typeArguments:[],arguments:[s.object(b),s.object(m),s.pure.bool(i)]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}async buildSetAcceptedPaymentTypeTx({adminAddress:e,paymentCoinType:i,currentTx:n}){let s=n||new t;s.moveCall({target:`${f}::config::set_accepted_payment_type`,typeArguments:[i],arguments:[s.object(b),s.object(m)]}),s.setSender(e);let r=await a(this.sdk.suiClient,s,e,0);return o({inheritTx:s,sender:e,feeAmount:r},this.sdk.suiClient)}}class y{_suiClient;boostingModule;subscriptionModule;constructor(){this._suiClient=new n({url:s("mainnet")}),this.subscriptionModule=new h(this),this.boostingModule=new g(this)}get suiClient(){return this._suiClient}}export{c as ADMIN_CAP_ID,l as BOOSTING_PACKAGE_ID,g as BoostingModule,d as CONFIG_ID,y as NoodlesPaySdk,C as PAYMENT_COIN_TYPE,m as SUBSCRIPTION_ADMIN_CAP_ID,b as SUBSCRIPTION_CONFIG_ID,f as SUBSCRIPTION_PACKAGE_ID,h as SubscriptionModule};
|
|
1
|
+
import tiny_invariant from "@minswap/tiny-invariant";
|
|
2
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
3
|
+
import { normalizeStructTag } from "@mysten/sui/utils";
|
|
4
|
+
import { SuiClient, getFullnodeUrl } from "@mysten/sui/client";
|
|
5
|
+
|
|
6
|
+
;// CONCATENATED MODULE: external "@minswap/tiny-invariant"
|
|
7
|
+
|
|
8
|
+
;// CONCATENATED MODULE: external "@mysten/sui/transactions"
|
|
9
|
+
|
|
10
|
+
;// CONCATENATED MODULE: external "@mysten/sui/utils"
|
|
11
|
+
|
|
12
|
+
;// CONCATENATED MODULE: ./src/libs/math/fee.ts
|
|
13
|
+
const BASE_BPS = 10000;
|
|
14
|
+
function getSplitCoinsAfterFee(amount, fee) {
|
|
15
|
+
const amountNum = BigInt(amount);
|
|
16
|
+
const feeNum = BigInt(Number(fee) * BASE_BPS);
|
|
17
|
+
if (feeNum === BigInt(0)) {
|
|
18
|
+
return [
|
|
19
|
+
amountNum.toString()
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
const feeAmount = amountNum * feeNum / BigInt(BASE_BPS);
|
|
23
|
+
const remainingAmount = amountNum - feeAmount;
|
|
24
|
+
return [
|
|
25
|
+
remainingAmount.toString(),
|
|
26
|
+
feeAmount.toString()
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
function getAmountAfterFee(amount, fee) {
|
|
30
|
+
if (!fee || Number(fee) <= 0) {
|
|
31
|
+
return BigInt(amount);
|
|
32
|
+
}
|
|
33
|
+
const amountNum = BigInt(amount);
|
|
34
|
+
const feeNum = BigInt(Number(fee) * BASE_BPS);
|
|
35
|
+
if (feeNum === BigInt(0)) {
|
|
36
|
+
return amountNum;
|
|
37
|
+
}
|
|
38
|
+
const feeAmount = amountNum * feeNum / BigInt(BASE_BPS);
|
|
39
|
+
return amountNum - feeAmount;
|
|
40
|
+
}
|
|
41
|
+
async function getNeededGasFee(suiClient, tx, sender, bufferPercent) {
|
|
42
|
+
const { effects: { gasUsed, status } } = await suiClient.devInspectTransactionBlock({
|
|
43
|
+
sender,
|
|
44
|
+
transactionBlock: tx
|
|
45
|
+
});
|
|
46
|
+
if (status.status !== "success") {
|
|
47
|
+
throw new Error(`Transaction failed during dev inspect: ${status.error ?? ""}`);
|
|
48
|
+
}
|
|
49
|
+
const fee = BigInt(gasUsed.computationCost) + BigInt(gasUsed.storageCost) - BigInt(gasUsed.storageRebate);
|
|
50
|
+
const feeNum = BigInt(Number(bufferPercent) * BASE_BPS);
|
|
51
|
+
return fee * (BigInt(BASE_BPS) + feeNum) / BigInt(BASE_BPS);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
;// CONCATENATED MODULE: ./src/constant/sui.ts
|
|
55
|
+
const SUI_TYPE = "0x2::sui::SUI";
|
|
56
|
+
const SUI_FULL_TYPE = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI";
|
|
57
|
+
const USDC_TOKEN_TYPE = "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN";
|
|
58
|
+
const NATIVE_USDC_TOKEN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
|
|
59
|
+
const SUI_METADATA_OBJECT_ID = "0x9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3";
|
|
60
|
+
const CLOCK_OBJECT_ID = "0x0000000000000000000000000000000000000000000000000000000000000006";
|
|
61
|
+
function isSuiCoinType(coinType) {
|
|
62
|
+
return coinType === SUI_TYPE || coinType === SUI_FULL_TYPE;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
;// CONCATENATED MODULE: ./src/libs/getCoinOjectIdsByAmount.ts
|
|
66
|
+
const orderByKey = (array, key, sortBy)=>{
|
|
67
|
+
if (!array?.length) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let swapped;
|
|
71
|
+
do {
|
|
72
|
+
swapped = false;
|
|
73
|
+
for(let i = 0; i < array.length - 1; i++){
|
|
74
|
+
const a = BigInt(array[i][key]);
|
|
75
|
+
const b = BigInt(array[i + 1][key]);
|
|
76
|
+
if (sortBy === "desc" ? a < b : a > b) {
|
|
77
|
+
const temp = array[i];
|
|
78
|
+
array[i] = array[i + 1];
|
|
79
|
+
array[i + 1] = temp;
|
|
80
|
+
swapped = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}while (swapped);
|
|
84
|
+
return array;
|
|
85
|
+
};
|
|
86
|
+
const getCoinObjectIdsByAmount = async (suiClient, address, amount, coinType)=>{
|
|
87
|
+
let coinBalances = [];
|
|
88
|
+
let hasNextPage = true;
|
|
89
|
+
let nextCursor;
|
|
90
|
+
while(hasNextPage){
|
|
91
|
+
try {
|
|
92
|
+
const coins = await suiClient.getCoins({
|
|
93
|
+
owner: address,
|
|
94
|
+
coinType,
|
|
95
|
+
cursor: nextCursor
|
|
96
|
+
});
|
|
97
|
+
coinBalances = [
|
|
98
|
+
...coinBalances,
|
|
99
|
+
...coins.data
|
|
100
|
+
];
|
|
101
|
+
hasNextPage = coins.hasNextPage;
|
|
102
|
+
nextCursor = coins.nextCursor;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error("Error fetching data:", error);
|
|
105
|
+
hasNextPage = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// sort coin balance before get object id
|
|
109
|
+
const coinObj = orderByKey(coinBalances.map((item)=>{
|
|
110
|
+
return {
|
|
111
|
+
...item,
|
|
112
|
+
balance: item.balance
|
|
113
|
+
};
|
|
114
|
+
}), "balance", "desc") ?? [];
|
|
115
|
+
let balance = "0";
|
|
116
|
+
const objectIds = [];
|
|
117
|
+
const objectCoins = [];
|
|
118
|
+
for (const coin of coinObj){
|
|
119
|
+
balance = (BigInt(coin.balance) + BigInt(balance)).toString(10);
|
|
120
|
+
objectIds.push(coin.coinObjectId);
|
|
121
|
+
objectCoins.push(coin);
|
|
122
|
+
if (BigInt(balance) >= BigInt(amount)) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
objectIds,
|
|
128
|
+
balance,
|
|
129
|
+
objectCoins
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
;// CONCATENATED MODULE: ./src/libs/addGasFee.ts
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async function addGasFee(params, suiClient) {
|
|
138
|
+
const { inheritTx, sender, feeAmount, suiInputAmount } = params;
|
|
139
|
+
const tx = inheritTx || new Transaction();
|
|
140
|
+
tx.setGasBudget(feeAmount);
|
|
141
|
+
const { objectCoins } = await getCoinObjectIdsByAmount(suiClient, sender, (BigInt(suiInputAmount || "0") + feeAmount).toString(), SUI_FULL_TYPE);
|
|
142
|
+
tx.setGasPayment(objectCoins.map((c)=>({
|
|
143
|
+
objectId: c.coinObjectId,
|
|
144
|
+
version: c.version,
|
|
145
|
+
digest: c.digest
|
|
146
|
+
})));
|
|
147
|
+
return tx;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
;// CONCATENATED MODULE: ./src/modules/boosting-module.ts
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/** Sui package ID for the boosting protocol */ const BOOSTING_PACKAGE_ID = "0x25fa0eb553dd69fa6a8028f73a38f1df7f289fa88a58ed3d7dc5a5360155bb19";
|
|
156
|
+
/** Configuration object ID for boosting settings */ const CONFIG_ID = "0x8b1a8ac6e34688cd8f8f8e5fefa9d5fb9e1ff74d2f132f8e208ae5ac9cc530bb";
|
|
157
|
+
/** Admin capability object ID for privileged operations */ const ADMIN_CAP_ID = "0x13d101f44a0710a5475bec5f492a87b80e03d92d1cf540f26096f65850b23d26";
|
|
158
|
+
/**
|
|
159
|
+
* Module for managing boosting functionality in the Noodles SDK.
|
|
160
|
+
*
|
|
161
|
+
* This module provides methods for:
|
|
162
|
+
* - Paying boosting fees for different tiers
|
|
163
|
+
* - Managing boosting tiers (admin functions)
|
|
164
|
+
* - Retrieving boosting configuration
|
|
165
|
+
* - Updating fee receiver settings
|
|
166
|
+
*/ class BoostingModule {
|
|
167
|
+
/** Reference to the main SDK instance */ sdk;
|
|
168
|
+
/**
|
|
169
|
+
* Creates a new BoostingModule instance.
|
|
170
|
+
*
|
|
171
|
+
* @param sdk - The main Noodles Pay SDK instance
|
|
172
|
+
*/ constructor(sdk){
|
|
173
|
+
this.sdk = sdk;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Retrieves the current boosting configuration from the blockchain.
|
|
177
|
+
*
|
|
178
|
+
* @returns Promise resolving to the current boosting configuration
|
|
179
|
+
* @throws Error if config object is not found or invalid
|
|
180
|
+
*/ async getConfig() {
|
|
181
|
+
const configObject = await this.sdk.suiClient.getObject({
|
|
182
|
+
id: CONFIG_ID,
|
|
183
|
+
options: {
|
|
184
|
+
showContent: true
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
tiny_invariant(configObject.data?.content, "Config object not found or has no content");
|
|
188
|
+
tiny_invariant(configObject.data?.content.dataType === "moveObject", "Config object is not a Move object");
|
|
189
|
+
const objectFields = configObject.data.content.fields;
|
|
190
|
+
const id = objectFields.id.id;
|
|
191
|
+
const feeReceiver = objectFields.fee_receiver;
|
|
192
|
+
const tiers = objectFields.tiers.fields.contents;
|
|
193
|
+
// Parse tier information from blockchain data
|
|
194
|
+
const tierMap = {};
|
|
195
|
+
for (const tier of tiers){
|
|
196
|
+
const key = tier.fields.key;
|
|
197
|
+
const duration = tier.fields.value.fields.duration;
|
|
198
|
+
const amount = tier.fields.value.fields.amount;
|
|
199
|
+
const point = tier.fields.value.fields.point;
|
|
200
|
+
const originalAmount = tier.fields.value.fields.original_amount;
|
|
201
|
+
tierMap[key] = {
|
|
202
|
+
duration: BigInt(duration),
|
|
203
|
+
amount: BigInt(amount),
|
|
204
|
+
point: BigInt(point),
|
|
205
|
+
originalAmount: BigInt(originalAmount)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
id,
|
|
210
|
+
receiverAddress: feeReceiver,
|
|
211
|
+
tiers: tierMap
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Builds a transaction for paying boosting fees for a specific tier.
|
|
216
|
+
* The payment amount is determined by the tier configuration.
|
|
217
|
+
*
|
|
218
|
+
* @param request - Payment request parameters
|
|
219
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
220
|
+
* @throws Error if tier ID is invalid
|
|
221
|
+
*/ async buildPayBoostingTx({ walletAddress, tierId, boostingCoinType, currentTx }) {
|
|
222
|
+
const configObj = await this.getConfig();
|
|
223
|
+
tiny_invariant(tierId in configObj.tiers, "Invalid tier ID");
|
|
224
|
+
const tier = configObj.tiers[tierId];
|
|
225
|
+
const paymentAmount = tier.amount;
|
|
226
|
+
const tx = currentTx || new Transaction();
|
|
227
|
+
const paymentCoin = tx.splitCoins(tx.gas, [
|
|
228
|
+
tx.pure.u64(paymentAmount)
|
|
229
|
+
]);
|
|
230
|
+
tx.moveCall({
|
|
231
|
+
target: `${BOOSTING_PACKAGE_ID}::boosting::pay`,
|
|
232
|
+
typeArguments: [
|
|
233
|
+
normalizeStructTag(boostingCoinType)
|
|
234
|
+
],
|
|
235
|
+
arguments: [
|
|
236
|
+
tx.object(CONFIG_ID),
|
|
237
|
+
tx.pure.u64(tierId),
|
|
238
|
+
paymentCoin
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
tx.setSender(walletAddress);
|
|
242
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, walletAddress, 0);
|
|
243
|
+
return addGasFee({
|
|
244
|
+
inheritTx: tx,
|
|
245
|
+
sender: walletAddress,
|
|
246
|
+
feeAmount: gasAmount
|
|
247
|
+
}, this.sdk.suiClient);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Builds a transaction for adding a new boosting tier.
|
|
251
|
+
*
|
|
252
|
+
* **Requires admin capability.**
|
|
253
|
+
*
|
|
254
|
+
* @param request - Add tier request parameters
|
|
255
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
256
|
+
*/ async buildAddTierTx({ adminAddress, tierId, duration, point, originalAmount, amount, currentTx }) {
|
|
257
|
+
const tx = currentTx || new Transaction();
|
|
258
|
+
tx.moveCall({
|
|
259
|
+
target: `${BOOSTING_PACKAGE_ID}::config::add_tier`,
|
|
260
|
+
typeArguments: [],
|
|
261
|
+
arguments: [
|
|
262
|
+
tx.object(CONFIG_ID),
|
|
263
|
+
tx.object(ADMIN_CAP_ID),
|
|
264
|
+
tx.pure.u64(tierId),
|
|
265
|
+
tx.pure.u64(point),
|
|
266
|
+
tx.pure.u64(duration),
|
|
267
|
+
tx.pure.u64(originalAmount),
|
|
268
|
+
tx.pure.u64(amount)
|
|
269
|
+
]
|
|
270
|
+
});
|
|
271
|
+
tx.setSender(adminAddress);
|
|
272
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
273
|
+
return addGasFee({
|
|
274
|
+
inheritTx: tx,
|
|
275
|
+
sender: adminAddress,
|
|
276
|
+
feeAmount: gasAmount
|
|
277
|
+
}, this.sdk.suiClient);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Builds a transaction for updating an existing boosting tier.
|
|
281
|
+
*
|
|
282
|
+
* **Requires admin capability.**
|
|
283
|
+
*
|
|
284
|
+
* @param request - Update tier request parameters
|
|
285
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
286
|
+
*/ async buildUpdateTierTx({ adminAddress, tierId, duration, amount, originalAmount, point, currentTx }) {
|
|
287
|
+
const tx = currentTx || new Transaction();
|
|
288
|
+
tx.moveCall({
|
|
289
|
+
target: `${BOOSTING_PACKAGE_ID}::config::update_tier`,
|
|
290
|
+
typeArguments: [],
|
|
291
|
+
arguments: [
|
|
292
|
+
tx.object(CONFIG_ID),
|
|
293
|
+
tx.object(ADMIN_CAP_ID),
|
|
294
|
+
tx.pure.u64(tierId),
|
|
295
|
+
tx.pure.u64(duration),
|
|
296
|
+
tx.pure.u64(amount),
|
|
297
|
+
tx.pure.u64(point),
|
|
298
|
+
tx.pure.u64(originalAmount)
|
|
299
|
+
]
|
|
300
|
+
});
|
|
301
|
+
tx.setSender(adminAddress);
|
|
302
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
303
|
+
return addGasFee({
|
|
304
|
+
inheritTx: tx,
|
|
305
|
+
sender: adminAddress,
|
|
306
|
+
feeAmount: gasAmount
|
|
307
|
+
}, this.sdk.suiClient);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Builds a transaction for removing a boosting tier.
|
|
311
|
+
*
|
|
312
|
+
* **Requires admin capability.**
|
|
313
|
+
*
|
|
314
|
+
* @param request - Remove tier request parameters
|
|
315
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
316
|
+
*/ async buildRemoveTierTx({ adminAddress, tierId, currentTx }) {
|
|
317
|
+
const tx = currentTx || new Transaction();
|
|
318
|
+
tx.moveCall({
|
|
319
|
+
target: `${BOOSTING_PACKAGE_ID}::config::remove_tier`,
|
|
320
|
+
typeArguments: [],
|
|
321
|
+
arguments: [
|
|
322
|
+
tx.object(CONFIG_ID),
|
|
323
|
+
tx.object(ADMIN_CAP_ID),
|
|
324
|
+
tx.pure.u64(tierId)
|
|
325
|
+
]
|
|
326
|
+
});
|
|
327
|
+
tx.setSender(adminAddress);
|
|
328
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
329
|
+
return addGasFee({
|
|
330
|
+
inheritTx: tx,
|
|
331
|
+
sender: adminAddress,
|
|
332
|
+
feeAmount: gasAmount
|
|
333
|
+
}, this.sdk.suiClient);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Builds a transaction for updating the fee receiver address.
|
|
337
|
+
*
|
|
338
|
+
* **Requires admin capability.**
|
|
339
|
+
*
|
|
340
|
+
* @param request - Update fee receiver request parameters
|
|
341
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
342
|
+
*/ async buildUpdateFeeReceiver({ adminAddress, newReceiverAddress, currentTx }) {
|
|
343
|
+
const tx = currentTx || new Transaction();
|
|
344
|
+
tx.moveCall({
|
|
345
|
+
target: `${BOOSTING_PACKAGE_ID}::config::update_fee_receiver`,
|
|
346
|
+
typeArguments: [],
|
|
347
|
+
arguments: [
|
|
348
|
+
tx.object(CONFIG_ID),
|
|
349
|
+
tx.object(ADMIN_CAP_ID),
|
|
350
|
+
tx.pure.address(newReceiverAddress)
|
|
351
|
+
]
|
|
352
|
+
});
|
|
353
|
+
tx.setSender(adminAddress);
|
|
354
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
355
|
+
return addGasFee({
|
|
356
|
+
inheritTx: tx,
|
|
357
|
+
sender: adminAddress,
|
|
358
|
+
feeAmount: gasAmount
|
|
359
|
+
}, this.sdk.suiClient);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Builds a transaction for paying boosting fees with admin privileges.
|
|
363
|
+
* Allows specifying a custom payment amount instead of using the tier's default amount.
|
|
364
|
+
*
|
|
365
|
+
* **Requires admin capability.**
|
|
366
|
+
*
|
|
367
|
+
* @param request - Admin payment request parameters
|
|
368
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
369
|
+
*/ async buildPayBoostingWithAdminTx({ walletAddress, tierId, customAmount, boostingCoinType, currentTx }) {
|
|
370
|
+
const tx = currentTx || new Transaction();
|
|
371
|
+
const paymentCoin = tx.splitCoins(tx.gas, [
|
|
372
|
+
tx.pure.u64(customAmount)
|
|
373
|
+
]);
|
|
374
|
+
tx.moveCall({
|
|
375
|
+
target: `${BOOSTING_PACKAGE_ID}::boosting::pay_with_admin`,
|
|
376
|
+
typeArguments: [
|
|
377
|
+
normalizeStructTag(boostingCoinType)
|
|
378
|
+
],
|
|
379
|
+
arguments: [
|
|
380
|
+
tx.object(ADMIN_CAP_ID),
|
|
381
|
+
tx.object(CONFIG_ID),
|
|
382
|
+
tx.pure.u64(tierId),
|
|
383
|
+
tx.pure.u64(customAmount),
|
|
384
|
+
paymentCoin
|
|
385
|
+
]
|
|
386
|
+
});
|
|
387
|
+
tx.setSender(walletAddress);
|
|
388
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, walletAddress, 0);
|
|
389
|
+
return addGasFee({
|
|
390
|
+
inheritTx: tx,
|
|
391
|
+
sender: walletAddress,
|
|
392
|
+
feeAmount: gasAmount
|
|
393
|
+
}, this.sdk.suiClient);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
;// CONCATENATED MODULE: ./src/libs/getSplitCoinForTx.ts
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
async function getSplitCoinForTx(params, suiClient) {
|
|
403
|
+
const { account, amount, splits, coinType, inheritTx, inspecTransaction, isSponsored = false } = params;
|
|
404
|
+
const tx = inheritTx ?? new Transaction();
|
|
405
|
+
const { objectIds } = await getCoinObjectIdsByAmount(suiClient, account, amount, coinType);
|
|
406
|
+
const coinObjectId = objectIds[0];
|
|
407
|
+
if (normalizeStructTag(coinType) === normalizeStructTag(SUI_FULL_TYPE) && !isSponsored) {
|
|
408
|
+
let coin;
|
|
409
|
+
if (inspecTransaction) {
|
|
410
|
+
if (objectIds.length > 1) {
|
|
411
|
+
tx.mergeCoins(tx.object(coinObjectId), objectIds.slice(1).map((item)=>tx.object(item)));
|
|
412
|
+
}
|
|
413
|
+
coin = tx.splitCoins(tx.object(coinObjectId), splits);
|
|
414
|
+
} else {
|
|
415
|
+
coin = tx.splitCoins(tx.gas, splits);
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
tx,
|
|
419
|
+
coinData: coin
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (objectIds.length > 1) {
|
|
423
|
+
tx.mergeCoins(tx.object(coinObjectId), objectIds.slice(1).map((item)=>tx.object(item)));
|
|
424
|
+
}
|
|
425
|
+
// split correct amount to swap
|
|
426
|
+
const coinData = tx.splitCoins(tx.object(coinObjectId), splits);
|
|
427
|
+
return {
|
|
428
|
+
tx,
|
|
429
|
+
coinData
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
;// CONCATENATED MODULE: ./src/modules/subscription-module.ts
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
/** Sui package ID for the subscription protocol */ const SUBSCRIPTION_PACKAGE_ID = "0x5dd4286ec234c117bb9577d4eaed1885b70d73ab1d3dab2794d0ab9a2c06c4a1";
|
|
438
|
+
/** Configuration object ID for subscription settings */ const SUBSCRIPTION_CONFIG_ID = "0x095b941beff22213fe8ccb223e4d1c5b4ede4dcd7f82ca4bb18c9a45fd3d6e20";
|
|
439
|
+
/** Admin capability object ID for privileged operations */ const SUBSCRIPTION_ADMIN_CAP_ID = "0xdc27fe8187131b7fc095fccd213b79f72312793152a2483d1f67199c390e256d";
|
|
440
|
+
/** Payment coin type on SUI */ const PAYMENT_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
|
|
441
|
+
/**
|
|
442
|
+
* Module for managing subscription functionality in the Noodles SDK.
|
|
443
|
+
*
|
|
444
|
+
* This module provides methods for:
|
|
445
|
+
* - Subscribing to plans
|
|
446
|
+
* - Upgrading plans (immediate or next billing cycle)
|
|
447
|
+
* - Managing subscription plans (admin functions)
|
|
448
|
+
* - Retrieving subscription configuration
|
|
449
|
+
* - Updating fee receiver wallet and pause settings
|
|
450
|
+
*/ class SubscriptionModule {
|
|
451
|
+
/** Reference to the main SDK instance */ sdk;
|
|
452
|
+
/**
|
|
453
|
+
* Creates a new SubscriptionModule instance.
|
|
454
|
+
*
|
|
455
|
+
* @param sdk - The main Noodles Pay SDK instance
|
|
456
|
+
*/ constructor(sdk){
|
|
457
|
+
this.sdk = sdk;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Retrieves the current subscription configuration from the blockchain.
|
|
461
|
+
*
|
|
462
|
+
* @returns Promise resolving to the current subscription configuration
|
|
463
|
+
* @throws Error if config object is not found or invalid
|
|
464
|
+
*/ async getConfig() {
|
|
465
|
+
const configObject = await this.sdk.suiClient.getObject({
|
|
466
|
+
id: SUBSCRIPTION_CONFIG_ID,
|
|
467
|
+
options: {
|
|
468
|
+
showContent: true
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
tiny_invariant(configObject.data, "Config object not found");
|
|
472
|
+
tiny_invariant(configObject.data.content, "Config object has no content");
|
|
473
|
+
tiny_invariant(configObject.data?.content.dataType === "moveObject", "Config object is not a Move object");
|
|
474
|
+
const objectFields = configObject.data.content.fields;
|
|
475
|
+
const id = objectFields.id.id;
|
|
476
|
+
const feeReceiverWallet = objectFields.fee_receiver;
|
|
477
|
+
const paused = objectFields.paused;
|
|
478
|
+
const acceptedPaymentType = objectFields.accepted_payment_type;
|
|
479
|
+
const plans = objectFields.plans.fields.contents;
|
|
480
|
+
// Parse plan information from blockchain data
|
|
481
|
+
const planMap = {};
|
|
482
|
+
for (const plan of plans){
|
|
483
|
+
const key = plan.fields.key;
|
|
484
|
+
const planId = plan.fields.value.fields.id;
|
|
485
|
+
const price = plan.fields.value.fields.price;
|
|
486
|
+
const monthInterval = plan.fields.value.fields.month_interval;
|
|
487
|
+
const planType = plan.fields.value.fields.ptype;
|
|
488
|
+
planMap[key] = {
|
|
489
|
+
id: BigInt(planId),
|
|
490
|
+
price: BigInt(price),
|
|
491
|
+
monthInterval: monthInterval,
|
|
492
|
+
planType: planType
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
id,
|
|
497
|
+
plans: planMap,
|
|
498
|
+
feeReceiverWallet: feeReceiverWallet,
|
|
499
|
+
paused,
|
|
500
|
+
acceptedPaymentType
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Builds a transaction for subscribing to a plan.
|
|
505
|
+
* The payment amount is determined by the plan configuration.
|
|
506
|
+
*
|
|
507
|
+
* @param request - Subscription request parameters
|
|
508
|
+
* @reference order_id generated from your backend to identify the purchase
|
|
509
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
510
|
+
* @throws Error if plan ID is invalid or contract is paused
|
|
511
|
+
*/ async buildSubscribeCyclePlanTx({ walletAddress, planId, reference, paymentCoinType = PAYMENT_COIN_TYPE, currentTx }) {
|
|
512
|
+
const configObj = await this.getConfig();
|
|
513
|
+
tiny_invariant(!configObj.paused, "Contract is paused");
|
|
514
|
+
tiny_invariant(planId in configObj.plans, "Invalid plan ID");
|
|
515
|
+
const plan = configObj.plans[planId];
|
|
516
|
+
const paymentAmount = plan.price;
|
|
517
|
+
// Get USDC coins from user's wallet
|
|
518
|
+
const { tx: txWithCoin, coinData } = await getSplitCoinForTx({
|
|
519
|
+
account: walletAddress,
|
|
520
|
+
amount: paymentAmount.toString(),
|
|
521
|
+
splits: [
|
|
522
|
+
paymentAmount.toString()
|
|
523
|
+
],
|
|
524
|
+
coinType: paymentCoinType,
|
|
525
|
+
inheritTx: currentTx
|
|
526
|
+
}, this.sdk.suiClient);
|
|
527
|
+
txWithCoin.moveCall({
|
|
528
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::subscription::subscribe_cycle_plan`,
|
|
529
|
+
typeArguments: [
|
|
530
|
+
paymentCoinType
|
|
531
|
+
],
|
|
532
|
+
arguments: [
|
|
533
|
+
txWithCoin.object(SUBSCRIPTION_CONFIG_ID),
|
|
534
|
+
txWithCoin.pure.u64(planId),
|
|
535
|
+
txWithCoin.pure.string(reference),
|
|
536
|
+
coinData
|
|
537
|
+
]
|
|
538
|
+
});
|
|
539
|
+
txWithCoin.setSender(walletAddress);
|
|
540
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, txWithCoin, walletAddress, 0);
|
|
541
|
+
return addGasFee({
|
|
542
|
+
inheritTx: txWithCoin,
|
|
543
|
+
sender: walletAddress,
|
|
544
|
+
feeAmount: gasAmount
|
|
545
|
+
}, this.sdk.suiClient);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Builds a transaction for buying additional compute units (CU) for CUAddOn plans.
|
|
549
|
+
*
|
|
550
|
+
* @param request - Buy more CU request parameters
|
|
551
|
+
* @reference order_id generated from your backend to identify the purchase
|
|
552
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
553
|
+
* @throws Error if plan ID is invalid or contract is paused
|
|
554
|
+
*/ async buildBuyMoreCUPlanTx({ walletAddress, planId, reference, paidAmount, paymentCoinType = PAYMENT_COIN_TYPE, currentTx }) {
|
|
555
|
+
const configObj = await this.getConfig();
|
|
556
|
+
tiny_invariant(!configObj.paused, "Contract is paused");
|
|
557
|
+
tiny_invariant(planId in configObj.plans, "Invalid plan ID");
|
|
558
|
+
// Get USDC coins from user's wallet
|
|
559
|
+
const { tx: txWithCoin, coinData } = await getSplitCoinForTx({
|
|
560
|
+
account: walletAddress,
|
|
561
|
+
amount: paidAmount.toString(),
|
|
562
|
+
splits: [
|
|
563
|
+
paidAmount.toString()
|
|
564
|
+
],
|
|
565
|
+
coinType: paymentCoinType,
|
|
566
|
+
inheritTx: currentTx
|
|
567
|
+
}, this.sdk.suiClient);
|
|
568
|
+
txWithCoin.moveCall({
|
|
569
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::subscription::buy_more_cu`,
|
|
570
|
+
typeArguments: [
|
|
571
|
+
paymentCoinType
|
|
572
|
+
],
|
|
573
|
+
arguments: [
|
|
574
|
+
txWithCoin.object(SUBSCRIPTION_CONFIG_ID),
|
|
575
|
+
txWithCoin.pure.u64(planId),
|
|
576
|
+
txWithCoin.pure.string(reference),
|
|
577
|
+
txWithCoin.pure.u64(paidAmount),
|
|
578
|
+
coinData
|
|
579
|
+
]
|
|
580
|
+
});
|
|
581
|
+
txWithCoin.setSender(walletAddress);
|
|
582
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, txWithCoin, walletAddress, 0);
|
|
583
|
+
return addGasFee({
|
|
584
|
+
inheritTx: txWithCoin,
|
|
585
|
+
sender: walletAddress,
|
|
586
|
+
feeAmount: gasAmount
|
|
587
|
+
}, this.sdk.suiClient);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Builds a transaction for subscribing with admin privileges.
|
|
591
|
+
* Allows specifying a custom payment amount instead of using the plan's default price.
|
|
592
|
+
* Useful for promotions, discounts, or special cases.
|
|
593
|
+
*
|
|
594
|
+
* **Requires admin capability.**
|
|
595
|
+
*
|
|
596
|
+
* @param request - Admin subscription request parameters
|
|
597
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
598
|
+
* @throws Error if contract is paused
|
|
599
|
+
*/ async buildSubscribePlanWithAdminTx({ walletAddress, planId, reference, customAmount, paymentCoinType = PAYMENT_COIN_TYPE, currentTx }) {
|
|
600
|
+
const configObj = await this.getConfig();
|
|
601
|
+
tiny_invariant(!configObj.paused, "Contract is paused");
|
|
602
|
+
// Get USDC coins from user's wallet
|
|
603
|
+
const { tx: txWithCoin, coinData } = await getSplitCoinForTx({
|
|
604
|
+
account: walletAddress,
|
|
605
|
+
amount: customAmount.toString(),
|
|
606
|
+
splits: [
|
|
607
|
+
customAmount.toString()
|
|
608
|
+
],
|
|
609
|
+
coinType: paymentCoinType,
|
|
610
|
+
inheritTx: currentTx
|
|
611
|
+
}, this.sdk.suiClient);
|
|
612
|
+
txWithCoin.moveCall({
|
|
613
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::subscription::subscribe_plan_with_admin`,
|
|
614
|
+
typeArguments: [
|
|
615
|
+
paymentCoinType
|
|
616
|
+
],
|
|
617
|
+
arguments: [
|
|
618
|
+
txWithCoin.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
619
|
+
txWithCoin.object(SUBSCRIPTION_CONFIG_ID),
|
|
620
|
+
txWithCoin.pure.u64(planId),
|
|
621
|
+
txWithCoin.pure.string(reference),
|
|
622
|
+
txWithCoin.pure.u64(customAmount),
|
|
623
|
+
coinData
|
|
624
|
+
]
|
|
625
|
+
});
|
|
626
|
+
txWithCoin.setSender(walletAddress);
|
|
627
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, txWithCoin, walletAddress, 0);
|
|
628
|
+
return addGasFee({
|
|
629
|
+
inheritTx: txWithCoin,
|
|
630
|
+
sender: walletAddress,
|
|
631
|
+
feeAmount: gasAmount
|
|
632
|
+
}, this.sdk.suiClient);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Builds a transaction for adding a new subscription plan.
|
|
636
|
+
*
|
|
637
|
+
* **Requires admin capability.**
|
|
638
|
+
*
|
|
639
|
+
* @param request - Add plan request parameters
|
|
640
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
641
|
+
*/ async buildAddPlanTx({ adminAddress, planId, planType, monthInterval, price, currentTx }) {
|
|
642
|
+
const tx = currentTx || new Transaction();
|
|
643
|
+
tx.moveCall({
|
|
644
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::add_plan`,
|
|
645
|
+
typeArguments: [],
|
|
646
|
+
arguments: [
|
|
647
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
648
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
649
|
+
tx.pure.u64(planId),
|
|
650
|
+
tx.pure.u8(planType),
|
|
651
|
+
tx.pure.u8(monthInterval),
|
|
652
|
+
tx.pure.u64(price)
|
|
653
|
+
]
|
|
654
|
+
});
|
|
655
|
+
tx.setSender(adminAddress);
|
|
656
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
657
|
+
return addGasFee({
|
|
658
|
+
inheritTx: tx,
|
|
659
|
+
sender: adminAddress,
|
|
660
|
+
feeAmount: gasAmount
|
|
661
|
+
}, this.sdk.suiClient);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Builds a transaction for updating an existing subscription plan.
|
|
665
|
+
*
|
|
666
|
+
* **Requires admin capability.**
|
|
667
|
+
*
|
|
668
|
+
* @param request - Update plan request parameters
|
|
669
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
670
|
+
*/ async buildUpdatePlanTx({ adminAddress, planId, planType, monthInterval, price, currentTx }) {
|
|
671
|
+
const tx = currentTx || new Transaction();
|
|
672
|
+
tx.moveCall({
|
|
673
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::update_plan`,
|
|
674
|
+
typeArguments: [],
|
|
675
|
+
arguments: [
|
|
676
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
677
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
678
|
+
tx.pure.u64(BigInt(planId)),
|
|
679
|
+
tx.pure.u8(planType),
|
|
680
|
+
tx.pure.u8(monthInterval),
|
|
681
|
+
tx.pure.u64(price)
|
|
682
|
+
]
|
|
683
|
+
});
|
|
684
|
+
tx.setSender(adminAddress);
|
|
685
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
686
|
+
return addGasFee({
|
|
687
|
+
inheritTx: tx,
|
|
688
|
+
sender: adminAddress,
|
|
689
|
+
feeAmount: gasAmount
|
|
690
|
+
}, this.sdk.suiClient);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Builds a transaction for removing a subscription plan.
|
|
694
|
+
*
|
|
695
|
+
* **Requires admin capability.**
|
|
696
|
+
*
|
|
697
|
+
* @param request - Remove plan request parameters
|
|
698
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
699
|
+
*/ async buildRemovePlanTx({ adminAddress, planId, currentTx }) {
|
|
700
|
+
const tx = currentTx || new Transaction();
|
|
701
|
+
tx.moveCall({
|
|
702
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::remove_plan`,
|
|
703
|
+
typeArguments: [],
|
|
704
|
+
arguments: [
|
|
705
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
706
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
707
|
+
tx.pure.u64(BigInt(planId))
|
|
708
|
+
]
|
|
709
|
+
});
|
|
710
|
+
tx.setSender(adminAddress);
|
|
711
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
712
|
+
return addGasFee({
|
|
713
|
+
inheritTx: tx,
|
|
714
|
+
sender: adminAddress,
|
|
715
|
+
feeAmount: gasAmount
|
|
716
|
+
}, this.sdk.suiClient);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Builds a transaction for updating the admin wallet address.
|
|
720
|
+
*
|
|
721
|
+
* **Requires admin capability.**
|
|
722
|
+
*
|
|
723
|
+
* @param request - Update admin wallet request parameters
|
|
724
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
725
|
+
*/ async buildUpdateFeeReceiverWalletTx({ adminAddress, newFeeReceiverWallet, currentTx }) {
|
|
726
|
+
const tx = currentTx || new Transaction();
|
|
727
|
+
tx.moveCall({
|
|
728
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::update_fee_receiver`,
|
|
729
|
+
typeArguments: [],
|
|
730
|
+
arguments: [
|
|
731
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
732
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
733
|
+
tx.pure.address(newFeeReceiverWallet)
|
|
734
|
+
]
|
|
735
|
+
});
|
|
736
|
+
tx.setSender(adminAddress);
|
|
737
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
738
|
+
return addGasFee({
|
|
739
|
+
inheritTx: tx,
|
|
740
|
+
sender: adminAddress,
|
|
741
|
+
feeAmount: gasAmount
|
|
742
|
+
}, this.sdk.suiClient);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Builds a transaction for pausing or unpausing the contract.
|
|
746
|
+
*
|
|
747
|
+
* **Requires admin capability.**
|
|
748
|
+
*
|
|
749
|
+
* @param request - Set paused request parameters
|
|
750
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
751
|
+
*/ async buildSetPausedTx({ adminAddress, paused, currentTx }) {
|
|
752
|
+
const tx = currentTx || new Transaction();
|
|
753
|
+
tx.moveCall({
|
|
754
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::set_paused`,
|
|
755
|
+
typeArguments: [],
|
|
756
|
+
arguments: [
|
|
757
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
758
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID),
|
|
759
|
+
tx.pure.bool(paused)
|
|
760
|
+
]
|
|
761
|
+
});
|
|
762
|
+
tx.setSender(adminAddress);
|
|
763
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
764
|
+
return addGasFee({
|
|
765
|
+
inheritTx: tx,
|
|
766
|
+
sender: adminAddress,
|
|
767
|
+
feeAmount: gasAmount
|
|
768
|
+
}, this.sdk.suiClient);
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Builds a transaction to set the accepted payment coin type.
|
|
772
|
+
* Note: Only one payment type can be accepted at a time. Setting a new type replaces the previous one.
|
|
773
|
+
*
|
|
774
|
+
* **Requires admin capability.**
|
|
775
|
+
*
|
|
776
|
+
* @param request - Set accepted payment type request parameters
|
|
777
|
+
* @returns Promise resolving to a transaction ready for execution
|
|
778
|
+
*/ async buildSetAcceptedPaymentTypeTx({ adminAddress, paymentCoinType, currentTx }) {
|
|
779
|
+
const tx = currentTx || new Transaction();
|
|
780
|
+
tx.moveCall({
|
|
781
|
+
target: `${SUBSCRIPTION_PACKAGE_ID}::config::set_accepted_payment_type`,
|
|
782
|
+
typeArguments: [
|
|
783
|
+
paymentCoinType
|
|
784
|
+
],
|
|
785
|
+
arguments: [
|
|
786
|
+
tx.object(SUBSCRIPTION_CONFIG_ID),
|
|
787
|
+
tx.object(SUBSCRIPTION_ADMIN_CAP_ID)
|
|
788
|
+
]
|
|
789
|
+
});
|
|
790
|
+
tx.setSender(adminAddress);
|
|
791
|
+
const gasAmount = await getNeededGasFee(this.sdk.suiClient, tx, adminAddress, 0);
|
|
792
|
+
return addGasFee({
|
|
793
|
+
inheritTx: tx,
|
|
794
|
+
sender: adminAddress,
|
|
795
|
+
feeAmount: gasAmount
|
|
796
|
+
}, this.sdk.suiClient);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
;// CONCATENATED MODULE: ./src/modules/pay-modules.ts
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
;// CONCATENATED MODULE: external "@mysten/sui/client"
|
|
805
|
+
|
|
806
|
+
;// CONCATENATED MODULE: ./src/pay-sdk.ts
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
class NoodlesPaySdk {
|
|
810
|
+
_suiClient;
|
|
811
|
+
boostingModule;
|
|
812
|
+
subscriptionModule;
|
|
813
|
+
constructor(){
|
|
814
|
+
this._suiClient = new SuiClient({
|
|
815
|
+
url: getFullnodeUrl("mainnet")
|
|
816
|
+
});
|
|
817
|
+
this.subscriptionModule = new SubscriptionModule(this);
|
|
818
|
+
this.boostingModule = new BoostingModule(this);
|
|
819
|
+
}
|
|
820
|
+
get suiClient() {
|
|
821
|
+
return this._suiClient;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
;// CONCATENATED MODULE: ./src/exports/pay.ts
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
export { ADMIN_CAP_ID, BOOSTING_PACKAGE_ID, BoostingModule, CONFIG_ID, NoodlesPaySdk, PAYMENT_COIN_TYPE, SUBSCRIPTION_ADMIN_CAP_ID, SUBSCRIPTION_CONFIG_ID, SUBSCRIPTION_PACKAGE_ID, SubscriptionModule };
|