@scallop-io/scallop-deepbook-kit 0.1.3 → 0.1.4
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.d.mts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/testnet-config.ts +33 -4
- package/src/toolkit/DeepBookMarginPool.ts +72 -85
- package/src/toolkit/DeepBookMarginToolkit.ts +34 -5
- package/src/toolkit/types.ts +53 -0
- package/src/utils/math.ts +4 -0
package/dist/index.d.mts
CHANGED
|
@@ -51,13 +51,12 @@ declare const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS: readonly ["userSupplyShares
|
|
|
51
51
|
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
52
52
|
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
53
53
|
type InterestConfig = {
|
|
54
|
-
midKink: number;
|
|
55
54
|
highKink: number;
|
|
56
55
|
baseBorrowApr: number;
|
|
57
|
-
|
|
58
|
-
highBorrowApr: number;
|
|
56
|
+
borrowAprOnHighKink: number;
|
|
59
57
|
maxBorrowApr: number;
|
|
60
|
-
|
|
58
|
+
supplyApr: number;
|
|
59
|
+
utilizationRate: number;
|
|
61
60
|
};
|
|
62
61
|
type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, number> & InterestConfig & {
|
|
63
62
|
address: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -51,13 +51,12 @@ declare const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS: readonly ["userSupplyShares
|
|
|
51
51
|
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
52
52
|
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
53
53
|
type InterestConfig = {
|
|
54
|
-
midKink: number;
|
|
55
54
|
highKink: number;
|
|
56
55
|
baseBorrowApr: number;
|
|
57
|
-
|
|
58
|
-
highBorrowApr: number;
|
|
56
|
+
borrowAprOnHighKink: number;
|
|
59
57
|
maxBorrowApr: number;
|
|
60
|
-
|
|
58
|
+
supplyApr: number;
|
|
59
|
+
utilizationRate: number;
|
|
61
60
|
};
|
|
62
61
|
type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, number> & InterestConfig & {
|
|
63
62
|
address: string;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var client=require('@mysten/sui/client'),ed25519=require('@mysten/sui/keypairs/ed25519'),transactions=require('@mysten/sui/transactions'),deepbookV3=require('@mysten/deepbook-v3'),cryptography=require('@mysten/sui/cryptography'),bcs$1=require('@mysten/bcs'),bcs=require('@mysten/sui/bcs'),bignumber_js=require('bignumber.js');var D={MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},f={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI"},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC"}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],B={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var k=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),L=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),x=n=>{if(k(n))return bcs$1.fromHex(n);if(L(n))return bcs$1.fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},_=n=>{if(n.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,cryptography.PRIVATE_KEY_SIZE);if(n.length===cryptography.PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===cryptography.PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var w=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(r){let a=r.fullnodeUrl??client.getFullnodeUrl(r.network);this.suiClient=new client.SuiClient({url:a}),this.keypair=this.#r(r.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r.supplierCapId;let e={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},t={SUI_DBUSDC:{address:f.SUI_DBUSDC.address,baseCoin:f.SUI_DBUSDC.baseCoin,quoteCoin:f.SUI_DBUSDC.quoteCoin}},i={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},o=new deepbookV3.DeepBookConfig({address:this.address,env:r.network,coins:e,pools:t,marginPools:i});this.marginPoolContract=new deepbookV3.MarginPoolContract(o);}#r(r){if(r.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:a}=cryptography.decodeSuiPrivateKey(r);return ed25519.Ed25519Keypair.fromSecretKey(_(a))}return ed25519.Ed25519Keypair.fromSecretKey(_(x(r)))}async#e(){let r=`${D.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#e();if(r)return this.supplierCapId=r,r;let a=await this.createSupplierCap();if(!a)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=a,a}async createSupplierCap(){try{let r=new transactions.Transaction,a=this.marginPoolContract.mintSupplierCap()(r);r.transferObjects([a],this.address);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplierCap"))return t.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let a=new transactions.Transaction;this.marginPoolContract.mintSupplyReferral(r)(a);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplyReferral"))return t.objectId}return null}catch(a){throw new Error(`Failed to create Supply Referral: ${a.message||a}`)}}async supplyToMarginPool(r,a,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction;t.setSender(this.address);let i=t.object(this.supplierCapId);t.add(this.marginPoolContract.supplyToMarginPool(r,i,a,e));let{errors:o}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(o&&o.length>0)throw new Error(`Transaction failed with errors: ${o.map(s=>s.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to supply to margin pool: ${t.message||t}`)}}async withdrawFromMarginPool(r,a){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new transactions.Transaction,t=e.object(this.supplierCapId),o=this.marginPoolContract.withdrawFromMarginPool(r,t,a)(e);e.transferObjects([o],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(l=>l.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(r,a){try{let e=new transactions.Transaction;e.add(this.marginPoolContract.withdrawReferralFees(r,a));let{errors:t}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(t&&t.length>0)throw new Error(`Transaction failed with errors: ${t.map(i=>i.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new transactions.Transaction;a.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let e=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:a}),t=0;if(e&&e.results&&e.results[0]&&e.results[0].returnValues){let p=e.results[0].returnValues[0];if(p&&p[0]){let m=Buffer.from(p[0]).readBigUInt64LE(),C=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;t=Number(m)/C;}}let i=r==="SUI"?c.SUI.type:c.DBUSDC.type,o=await this.suiClient.getBalance({owner:this.address,coinType:i}),s=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(o.totalBalance)/s;return {userSupplyAmount:t,walletBalance:l}}catch(a){throw new Error(`Failed to get balance: ${a.message||a}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var X=new Set(b),J=n=>X.has(n),I=class{constructor(r="testnet",a="",e=new client.SuiClient({url:client.getFullnodeUrl(r)}),t){this.suiClient=e;this.dbConfig=t??new deepbookV3.DeepBookConfig({env:r,address:a,coins:c,pools:f,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#r(r,a,e,t){if(J(a)){let i=this.marginPoolContract[a];if(t==null)throw new Error(`supplierCap is required for '${a}'.`);r.add(i(e,t));}else {let i=this.marginPoolContract[a];r.add(i(e));}}#e(r,a){let e=r.results;return e?a.reduce((t,i,o)=>{let s=e[o]?.returnValues?.[0]?.[0];if(!s)return t;let l=bcs.bcs[B[i]];return t[i]=l.parse(new Uint8Array(s)),t},{}):{}}#t(r,a){let e=this.dbConfig.getCoin(a),t={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,midKink:0,highKink:0,baseBorrowApr:0,midBorrowApr:0,highBorrowApr:0,maxBorrowApr:0,borrowApr:0,...e};if(!e)return t;let i=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[o,s]of Object.entries(r))o==="lastUpdateTimestamp"?t[o]=Number(s):i.has(o)?t[o]=new bignumber_js.BigNumber(s).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():t[o]=new bignumber_js.BigNumber(s).dividedBy(e.scalar).toNumber();return t}#a(r,a){let e=BigInt(a.base_rate),t=BigInt(a.base_slope),i=BigInt(a.excess_slope),o=BigInt(a.optimal_utilization),s=r<0n?0n:r>BigInt(deepbookV3.FLOAT_SCALAR)?BigInt(deepbookV3.FLOAT_SCALAR):BigInt(r);return s<=o?e+t*s/o:e+t+i*(s-o)/(BigInt(deepbookV3.FLOAT_SCALAR)-o)}#o(r,a,e){let t=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),s=BigInt(r.optimal_utilization),l=BigInt(a.max_utilization_rate),p=s,m=l,C=t,E=t+i,T=t+i+o*(l-s)/(1000000000n-s),A=t+i+o,U=this.#a(BigInt(bignumber_js.BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),r),u=M=>bignumber_js.BigNumber(M).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber();return {raw:{midKink:p,highKink:m,baseBorrowApr:C,midBorrowApr:E,highBorrowApr:T,maxBorrowApr:A,borrowApr:U},normalized:{midKink:u(p),highKink:u(m),baseBorrowApr:u(C),midBorrowApr:u(E),highBorrowApr:u(T),maxBorrowApr:u(A),borrowApr:u(U)}}}async#i(r){let{address:a}=this.dbConfig.getMarginPool(r),t=((await this.suiClient.getObject({id:a,options:{showContent:true}})).data?.content).fields,i=t.config.fields,o=i.interest_config.fields,s=i.margin_pool_config.fields,l=t.state.fields,{normalized:p}=this.#o(o,s,l);return p}async getPoolParameters(r,a,e=new transactions.Transaction,t=true){if(P.forEach(p=>this.#r(e,p,r)),a&&b.forEach(p=>this.#r(e,p,r,a)),!t)return e;let i=[...P,...b],o=await this.suiClient.devInspectTransactionBlock({transactionBlock:e,sender:this.dbConfig.address}),s=await this.#i(r);return {...this.#t(this.#e(o,i),r),...s}}};exports.DeepBookMarginPool=I;exports.DeepBookMarginToolkit=w;
|
|
1
|
+
'use strict';var client=require('@mysten/sui/client'),ed25519=require('@mysten/sui/keypairs/ed25519'),transactions=require('@mysten/sui/transactions'),deepbookV3=require('@mysten/deepbook-v3'),cryptography=require('@mysten/sui/cryptography'),bcs$1=require('@mysten/bcs'),bcs=require('@mysten/sui/bcs'),bignumber_js=require('bignumber.js');var g={DEEPBOOK_PACKAGE_ID:"0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982",REGISTRY_ID:"0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1",DEEP_TREASURY_ID:"0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb",MARGIN_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_REGISTRY_ID:"0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},S={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI",initialVersion:658877881,supplyCap:1e6,maxUtilizationRate:.9,referralSpread:.1,minBorrow:.1},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",initialVersion:658877215,supplyCap:1e6,maxUtilizationRate:.95,referralSpread:.1,minBorrow:.1}};var _=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],T={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var K=s=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(s),O=s=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(s),A=s=>{if(K(s))return bcs$1.fromHex(s);if(O(s))return bcs$1.fromBase64(s);throw new Error("The string is not a valid hex or base64 string.")},y=s=>{if(s.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return s.slice(0,cryptography.PRIVATE_KEY_SIZE);if(s.length===cryptography.PRIVATE_KEY_SIZE+1&&s[0]===0)return s.slice(1);if(s.length===cryptography.PRIVATE_KEY_SIZE)return s;throw new Error("invalid secret key")};var P=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(e){let t=e.fullnodeUrl??client.getFullnodeUrl(e.network);this.suiClient=new client.SuiClient({url:t}),this.keypair=this.#e(e.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e.supplierCapId;let r={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},a={SUI_DBUSDC:{address:S.SUI_DBUSDC.address,baseCoin:S.SUI_DBUSDC.baseCoin,quoteCoin:S.SUI_DBUSDC.quoteCoin}},o={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},i=new deepbookV3.DeepBookConfig({address:this.address,env:e.network,coins:r,pools:a,marginPools:o});this.marginPoolContract=new deepbookV3.MarginPoolContract(i);}#e(e){if(e.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:t}=cryptography.decodeSuiPrivateKey(e);return ed25519.Ed25519Keypair.fromSecretKey(y(t))}return ed25519.Ed25519Keypair.fromSecretKey(y(A(e)))}async#r(){let e=`${g.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:e},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let e=await this.#r();if(e)return this.supplierCapId=e,e;let t=await this.createSupplierCap();if(!t)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=t,t}async createSupplierCap(){try{let e=new transactions.Transaction;e.setSender(this.address);let t=e.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supplier_cap`,arguments:[e.object(g.MARGIN_REGISTRY_ID),e.object.clock()]});e.transferObjects([t],e.pure.address(this.address));let r=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(r.errors&&r.errors.length>0)throw new Error(`Transaction failed with errors: ${r.errors.map(a=>a.toString()).join(", ")}`);if(r.objectChanges){for(let a of r.objectChanges)if(a.type==="created"&&a.objectType.includes("SupplierCap"))return a.objectId}return null}catch(e){throw new Error(`Failed to create Supplier Cap: ${e.message||e}`)}}async createSupplyReferral(e){try{let t=new transactions.Transaction;t.setSender(this.address);let r=d[e];t.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[t.sharedObjectRef({objectId:r.address,initialSharedVersion:r.initialVersion,mutable:!0}),t.object(g.MARGIN_REGISTRY_ID),t.object.clock()],typeArguments:[r.coinType]});let a=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(a.errors&&a.errors.length>0)throw new Error(`Transaction failed with errors: ${a.errors.map(o=>o.toString()).join(", ")}`);if(a.objectChanges){for(let o of a.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(t){throw new Error(`Failed to create Supply Referral: ${t.message||t}`)}}async supplyToMarginPool(e,t,r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new transactions.Transaction;a.setSender(this.address);let o=a.object(this.supplierCapId);a.add(this.marginPoolContract.supplyToMarginPool(e,o,t,r));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.map(n=>n.toString()).join(", ")}`);return !0}catch(a){throw new Error(`Failed to supply to margin pool: ${a.message||a}`)}}async withdrawFromMarginPool(e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let r=new transactions.Transaction,a=r.object(this.supplierCapId),i=this.marginPoolContract.withdrawFromMarginPool(e,a,t)(r);r.transferObjects([i],this.address);let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(l=>l.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(e,t){try{let r=new transactions.Transaction;r.add(this.marginPoolContract.withdrawReferralFees(e,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0},requestType:"WaitForLocalExecution"});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(o=>o.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction;t.add(this.marginPoolContract.userSupplyAmount(e,this.supplierCapId));let r=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:t}),a=0;if(r&&r.results&&r.results[0]&&r.results[0].returnValues){let p=r.results[0].returnValues[0];if(p&&p[0]){let f=Buffer.from(p[0]).readBigUInt64LE(),R=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;a=Number(f)/R;}}let o=e==="SUI"?c.SUI.type:c.DBUSDC.type,i=await this.suiClient.getBalance({owner:this.address,coinType:o}),n=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(i.totalBalance)/n;return {userSupplyAmount:a,walletBalance:l}}catch(t){throw new Error(`Failed to get balance: ${t.message||t}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var C=(s,e)=>s*e/BigInt(deepbookV3.FLOAT_SCALAR),u=s=>Number(s)/deepbookV3.FLOAT_SCALAR;var V=new Set(b),H=s=>V.has(s),E=class{constructor(e="testnet",t="",r=new client.SuiClient({url:client.getFullnodeUrl(e)}),a){this.suiClient=r;this.dbConfig=a??new deepbookV3.DeepBookConfig({env:e,address:t,coins:c,pools:S,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#e(e,t,r,a){if(H(t)){let o=this.marginPoolContract[t];if(a==null)throw new Error(`supplierCap is required for '${t}'.`);e.add(o(r,a));}else {let o=this.marginPoolContract[t];e.add(o(r));}}#r(e,t){let r=e.results;return r?t.reduce((a,o,i)=>{let n=r[i]?.returnValues?.[0]?.[0];if(!n)return a;let l=bcs.bcs[T[o]];return a[o]=l.parse(new Uint8Array(n)),a},{}):{}}#a(e,t){let r=this.dbConfig.getCoin(t),a={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)return a;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,n]of Object.entries(e))i==="lastUpdateTimestamp"?a[i]=Number(n):o.has(i)?a[i]=new bignumber_js.BigNumber(n).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():a[i]=new bignumber_js.BigNumber(n).dividedBy(r.scalar).toNumber();return a}#t(e,t){let r=BigInt(t.base_rate),a=BigInt(t.base_slope),o=BigInt(t.excess_slope),i=BigInt(t.optimal_utilization);return e<i?r+C(e,a):r+C(i,a)+C(e-i,o)}#o(e,t,r,a){let o=BigInt(e.optimal_utilization),i=BigInt(t.max_utilization_rate),n=this.#t(o,e),l=this.#t(i,e),p=BigInt(bignumber_js.BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),f=C(C(BigInt(a),p),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(t.protocol_spread));return {raw:{baseBorrowApr:e.base_rate,highKink:o,borrowAprOnHighKink:n,maxBorrowApr:l,supplyApr:f,utilizationRate:p},normalized:{baseBorrowApr:u(BigInt(e.base_rate)),highKink:u(o),borrowAprOnHighKink:u(n),maxBorrowApr:u(l),supplyApr:u(f),utilizationRate:u(p)}}}async#i(e,t){let{address:r}=this.dbConfig.getMarginPool(e),o=((await this.suiClient.getObject({id:r,options:{showContent:true}})).data?.content).fields,i=o.config.fields,n=i.interest_config.fields,l=i.margin_pool_config.fields,p=o.state.fields,{normalized:f}=this.#o(n,l,p,t);return f}async getPoolParameters(e,t,r=new transactions.Transaction,a=true){if(_.forEach(p=>this.#e(r,p,e)),t&&b.forEach(p=>this.#e(r,p,e,t)),!a)return r;let o=[..._,...b],i=await this.suiClient.devInspectTransactionBlock({transactionBlock:r,sender:this.dbConfig.address}),n=this.#a(this.#r(i,o),e),l=await this.#i(e,n.interestRate*deepbookV3.FLOAT_SCALAR);return {...n,...l}}};exports.DeepBookMarginPool=E;exports.DeepBookMarginToolkit=P;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {getFullnodeUrl,SuiClient}from'@mysten/sui/client';import {Ed25519Keypair}from'@mysten/sui/keypairs/ed25519';import {Transaction}from'@mysten/sui/transactions';import {DeepBookConfig,MarginPoolContract,FLOAT_SCALAR}from'@mysten/deepbook-v3';import {SUI_PRIVATE_KEY_PREFIX,decodeSuiPrivateKey,LEGACY_PRIVATE_KEY_SIZE,PRIVATE_KEY_SIZE}from'@mysten/sui/cryptography';import {fromHex,fromBase64}from'@mysten/bcs';import {bcs}from'@mysten/sui/bcs';import {BigNumber}from'bignumber.js';var D={MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},f={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI"},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC"}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],B={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var k=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),L=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),x=n=>{if(k(n))return fromHex(n);if(L(n))return fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},_=n=>{if(n.length===LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,PRIVATE_KEY_SIZE);if(n.length===PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var w=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(r){let a=r.fullnodeUrl??getFullnodeUrl(r.network);this.suiClient=new SuiClient({url:a}),this.keypair=this.#r(r.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r.supplierCapId;let e={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},t={SUI_DBUSDC:{address:f.SUI_DBUSDC.address,baseCoin:f.SUI_DBUSDC.baseCoin,quoteCoin:f.SUI_DBUSDC.quoteCoin}},i={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},o=new DeepBookConfig({address:this.address,env:r.network,coins:e,pools:t,marginPools:i});this.marginPoolContract=new MarginPoolContract(o);}#r(r){if(r.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:a}=decodeSuiPrivateKey(r);return Ed25519Keypair.fromSecretKey(_(a))}return Ed25519Keypair.fromSecretKey(_(x(r)))}async#e(){let r=`${D.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#e();if(r)return this.supplierCapId=r,r;let a=await this.createSupplierCap();if(!a)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=a,a}async createSupplierCap(){try{let r=new Transaction,a=this.marginPoolContract.mintSupplierCap()(r);r.transferObjects([a],this.address);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplierCap"))return t.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let a=new Transaction;this.marginPoolContract.mintSupplyReferral(r)(a);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplyReferral"))return t.objectId}return null}catch(a){throw new Error(`Failed to create Supply Referral: ${a.message||a}`)}}async supplyToMarginPool(r,a,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction;t.setSender(this.address);let i=t.object(this.supplierCapId);t.add(this.marginPoolContract.supplyToMarginPool(r,i,a,e));let{errors:o}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(o&&o.length>0)throw new Error(`Transaction failed with errors: ${o.map(s=>s.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to supply to margin pool: ${t.message||t}`)}}async withdrawFromMarginPool(r,a){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new Transaction,t=e.object(this.supplierCapId),o=this.marginPoolContract.withdrawFromMarginPool(r,t,a)(e);e.transferObjects([o],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(l=>l.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(r,a){try{let e=new Transaction;e.add(this.marginPoolContract.withdrawReferralFees(r,a));let{errors:t}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(t&&t.length>0)throw new Error(`Transaction failed with errors: ${t.map(i=>i.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new Transaction;a.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let e=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:a}),t=0;if(e&&e.results&&e.results[0]&&e.results[0].returnValues){let p=e.results[0].returnValues[0];if(p&&p[0]){let m=Buffer.from(p[0]).readBigUInt64LE(),C=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;t=Number(m)/C;}}let i=r==="SUI"?c.SUI.type:c.DBUSDC.type,o=await this.suiClient.getBalance({owner:this.address,coinType:i}),s=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(o.totalBalance)/s;return {userSupplyAmount:t,walletBalance:l}}catch(a){throw new Error(`Failed to get balance: ${a.message||a}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var X=new Set(b),J=n=>X.has(n),I=class{constructor(r="testnet",a="",e=new SuiClient({url:getFullnodeUrl(r)}),t){this.suiClient=e;this.dbConfig=t??new DeepBookConfig({env:r,address:a,coins:c,pools:f,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#r(r,a,e,t){if(J(a)){let i=this.marginPoolContract[a];if(t==null)throw new Error(`supplierCap is required for '${a}'.`);r.add(i(e,t));}else {let i=this.marginPoolContract[a];r.add(i(e));}}#e(r,a){let e=r.results;return e?a.reduce((t,i,o)=>{let s=e[o]?.returnValues?.[0]?.[0];if(!s)return t;let l=bcs[B[i]];return t[i]=l.parse(new Uint8Array(s)),t},{}):{}}#t(r,a){let e=this.dbConfig.getCoin(a),t={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,midKink:0,highKink:0,baseBorrowApr:0,midBorrowApr:0,highBorrowApr:0,maxBorrowApr:0,borrowApr:0,...e};if(!e)return t;let i=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[o,s]of Object.entries(r))o==="lastUpdateTimestamp"?t[o]=Number(s):i.has(o)?t[o]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():t[o]=new BigNumber(s).dividedBy(e.scalar).toNumber();return t}#a(r,a){let e=BigInt(a.base_rate),t=BigInt(a.base_slope),i=BigInt(a.excess_slope),o=BigInt(a.optimal_utilization),s=r<0n?0n:r>BigInt(FLOAT_SCALAR)?BigInt(FLOAT_SCALAR):BigInt(r);return s<=o?e+t*s/o:e+t+i*(s-o)/(BigInt(FLOAT_SCALAR)-o)}#o(r,a,e){let t=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),s=BigInt(r.optimal_utilization),l=BigInt(a.max_utilization_rate),p=s,m=l,C=t,E=t+i,T=t+i+o*(l-s)/(1000000000n-s),A=t+i+o,U=this.#a(BigInt(BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),r),u=M=>BigNumber(M).dividedBy(FLOAT_SCALAR).toNumber();return {raw:{midKink:p,highKink:m,baseBorrowApr:C,midBorrowApr:E,highBorrowApr:T,maxBorrowApr:A,borrowApr:U},normalized:{midKink:u(p),highKink:u(m),baseBorrowApr:u(C),midBorrowApr:u(E),highBorrowApr:u(T),maxBorrowApr:u(A),borrowApr:u(U)}}}async#i(r){let{address:a}=this.dbConfig.getMarginPool(r),t=((await this.suiClient.getObject({id:a,options:{showContent:true}})).data?.content).fields,i=t.config.fields,o=i.interest_config.fields,s=i.margin_pool_config.fields,l=t.state.fields,{normalized:p}=this.#o(o,s,l);return p}async getPoolParameters(r,a,e=new Transaction,t=true){if(P.forEach(p=>this.#r(e,p,r)),a&&b.forEach(p=>this.#r(e,p,r,a)),!t)return e;let i=[...P,...b],o=await this.suiClient.devInspectTransactionBlock({transactionBlock:e,sender:this.dbConfig.address}),s=await this.#i(r);return {...this.#t(this.#e(o,i),r),...s}}};export{I as DeepBookMarginPool,w as DeepBookMarginToolkit};
|
|
1
|
+
import {getFullnodeUrl,SuiClient}from'@mysten/sui/client';import {Ed25519Keypair}from'@mysten/sui/keypairs/ed25519';import {Transaction}from'@mysten/sui/transactions';import {DeepBookConfig,MarginPoolContract,FLOAT_SCALAR}from'@mysten/deepbook-v3';import {SUI_PRIVATE_KEY_PREFIX,decodeSuiPrivateKey,LEGACY_PRIVATE_KEY_SIZE,PRIVATE_KEY_SIZE}from'@mysten/sui/cryptography';import {fromHex,fromBase64}from'@mysten/bcs';import {bcs}from'@mysten/sui/bcs';import {BigNumber}from'bignumber.js';var g={DEEPBOOK_PACKAGE_ID:"0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982",REGISTRY_ID:"0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1",DEEP_TREASURY_ID:"0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb",MARGIN_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_REGISTRY_ID:"0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},S={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI",initialVersion:658877881,supplyCap:1e6,maxUtilizationRate:.9,referralSpread:.1,minBorrow:.1},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",initialVersion:658877215,supplyCap:1e6,maxUtilizationRate:.95,referralSpread:.1,minBorrow:.1}};var _=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],T={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var K=s=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(s),O=s=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(s),A=s=>{if(K(s))return fromHex(s);if(O(s))return fromBase64(s);throw new Error("The string is not a valid hex or base64 string.")},y=s=>{if(s.length===LEGACY_PRIVATE_KEY_SIZE)return s.slice(0,PRIVATE_KEY_SIZE);if(s.length===PRIVATE_KEY_SIZE+1&&s[0]===0)return s.slice(1);if(s.length===PRIVATE_KEY_SIZE)return s;throw new Error("invalid secret key")};var P=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(e){let t=e.fullnodeUrl??getFullnodeUrl(e.network);this.suiClient=new SuiClient({url:t}),this.keypair=this.#e(e.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e.supplierCapId;let r={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},a={SUI_DBUSDC:{address:S.SUI_DBUSDC.address,baseCoin:S.SUI_DBUSDC.baseCoin,quoteCoin:S.SUI_DBUSDC.quoteCoin}},o={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},i=new DeepBookConfig({address:this.address,env:e.network,coins:r,pools:a,marginPools:o});this.marginPoolContract=new MarginPoolContract(i);}#e(e){if(e.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:t}=decodeSuiPrivateKey(e);return Ed25519Keypair.fromSecretKey(y(t))}return Ed25519Keypair.fromSecretKey(y(A(e)))}async#r(){let e=`${g.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:e},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let e=await this.#r();if(e)return this.supplierCapId=e,e;let t=await this.createSupplierCap();if(!t)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=t,t}async createSupplierCap(){try{let e=new Transaction;e.setSender(this.address);let t=e.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supplier_cap`,arguments:[e.object(g.MARGIN_REGISTRY_ID),e.object.clock()]});e.transferObjects([t],e.pure.address(this.address));let r=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(r.errors&&r.errors.length>0)throw new Error(`Transaction failed with errors: ${r.errors.map(a=>a.toString()).join(", ")}`);if(r.objectChanges){for(let a of r.objectChanges)if(a.type==="created"&&a.objectType.includes("SupplierCap"))return a.objectId}return null}catch(e){throw new Error(`Failed to create Supplier Cap: ${e.message||e}`)}}async createSupplyReferral(e){try{let t=new Transaction;t.setSender(this.address);let r=d[e];t.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[t.sharedObjectRef({objectId:r.address,initialSharedVersion:r.initialVersion,mutable:!0}),t.object(g.MARGIN_REGISTRY_ID),t.object.clock()],typeArguments:[r.coinType]});let a=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(a.errors&&a.errors.length>0)throw new Error(`Transaction failed with errors: ${a.errors.map(o=>o.toString()).join(", ")}`);if(a.objectChanges){for(let o of a.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(t){throw new Error(`Failed to create Supply Referral: ${t.message||t}`)}}async supplyToMarginPool(e,t,r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new Transaction;a.setSender(this.address);let o=a.object(this.supplierCapId);a.add(this.marginPoolContract.supplyToMarginPool(e,o,t,r));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.map(n=>n.toString()).join(", ")}`);return !0}catch(a){throw new Error(`Failed to supply to margin pool: ${a.message||a}`)}}async withdrawFromMarginPool(e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let r=new Transaction,a=r.object(this.supplierCapId),i=this.marginPoolContract.withdrawFromMarginPool(e,a,t)(r);r.transferObjects([i],this.address);let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(l=>l.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(e,t){try{let r=new Transaction;r.add(this.marginPoolContract.withdrawReferralFees(e,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0},requestType:"WaitForLocalExecution"});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(o=>o.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction;t.add(this.marginPoolContract.userSupplyAmount(e,this.supplierCapId));let r=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:t}),a=0;if(r&&r.results&&r.results[0]&&r.results[0].returnValues){let p=r.results[0].returnValues[0];if(p&&p[0]){let f=Buffer.from(p[0]).readBigUInt64LE(),R=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;a=Number(f)/R;}}let o=e==="SUI"?c.SUI.type:c.DBUSDC.type,i=await this.suiClient.getBalance({owner:this.address,coinType:o}),n=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(i.totalBalance)/n;return {userSupplyAmount:a,walletBalance:l}}catch(t){throw new Error(`Failed to get balance: ${t.message||t}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var C=(s,e)=>s*e/BigInt(FLOAT_SCALAR),u=s=>Number(s)/FLOAT_SCALAR;var V=new Set(b),H=s=>V.has(s),E=class{constructor(e="testnet",t="",r=new SuiClient({url:getFullnodeUrl(e)}),a){this.suiClient=r;this.dbConfig=a??new DeepBookConfig({env:e,address:t,coins:c,pools:S,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#e(e,t,r,a){if(H(t)){let o=this.marginPoolContract[t];if(a==null)throw new Error(`supplierCap is required for '${t}'.`);e.add(o(r,a));}else {let o=this.marginPoolContract[t];e.add(o(r));}}#r(e,t){let r=e.results;return r?t.reduce((a,o,i)=>{let n=r[i]?.returnValues?.[0]?.[0];if(!n)return a;let l=bcs[T[o]];return a[o]=l.parse(new Uint8Array(n)),a},{}):{}}#a(e,t){let r=this.dbConfig.getCoin(t),a={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)return a;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,n]of Object.entries(e))i==="lastUpdateTimestamp"?a[i]=Number(n):o.has(i)?a[i]=new BigNumber(n).dividedBy(FLOAT_SCALAR).toNumber():a[i]=new BigNumber(n).dividedBy(r.scalar).toNumber();return a}#t(e,t){let r=BigInt(t.base_rate),a=BigInt(t.base_slope),o=BigInt(t.excess_slope),i=BigInt(t.optimal_utilization);return e<i?r+C(e,a):r+C(i,a)+C(e-i,o)}#o(e,t,r,a){let o=BigInt(e.optimal_utilization),i=BigInt(t.max_utilization_rate),n=this.#t(o,e),l=this.#t(i,e),p=BigInt(BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),f=C(C(BigInt(a),p),BigInt(FLOAT_SCALAR)-BigInt(t.protocol_spread));return {raw:{baseBorrowApr:e.base_rate,highKink:o,borrowAprOnHighKink:n,maxBorrowApr:l,supplyApr:f,utilizationRate:p},normalized:{baseBorrowApr:u(BigInt(e.base_rate)),highKink:u(o),borrowAprOnHighKink:u(n),maxBorrowApr:u(l),supplyApr:u(f),utilizationRate:u(p)}}}async#i(e,t){let{address:r}=this.dbConfig.getMarginPool(e),o=((await this.suiClient.getObject({id:r,options:{showContent:true}})).data?.content).fields,i=o.config.fields,n=i.interest_config.fields,l=i.margin_pool_config.fields,p=o.state.fields,{normalized:f}=this.#o(n,l,p,t);return f}async getPoolParameters(e,t,r=new Transaction,a=true){if(_.forEach(p=>this.#e(r,p,e)),t&&b.forEach(p=>this.#e(r,p,e,t)),!a)return r;let o=[..._,...b],i=await this.suiClient.devInspectTransactionBlock({transactionBlock:r,sender:this.dbConfig.address}),n=this.#a(this.#r(i,o),e),l=await this.#i(e,n.interestRate*FLOAT_SCALAR);return {...n,...l}}};export{E as DeepBookMarginPool,P as DeepBookMarginToolkit};
|
package/package.json
CHANGED
package/src/testnet-config.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DeepBook V3 & Margin Testnet Configuration | DeepBook V3 & Margin 測試網配置
|
|
3
3
|
* Source | 來源: https://github.com/MystenLabs/ts-sdks/tree/main/packages/deepbook-v3
|
|
4
|
-
*
|
|
4
|
+
* Document | 文件: https://docs.google.com/document/d/1UQw2JZ3X3UN4641_WkvqdCehP7jDPgFmgt2KFti6bjs
|
|
5
|
+
* Last updated | 最後更新: 2025-11-20
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
// ============================================================================
|
|
@@ -13,7 +14,7 @@ export const TESTNET_PACKAGES = {
|
|
|
13
14
|
REGISTRY_ID: '0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1',
|
|
14
15
|
DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb',
|
|
15
16
|
MARGIN_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209',
|
|
16
|
-
MARGIN_INITIAL_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209', // for events
|
|
17
|
+
MARGIN_INITIAL_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209', // VERSION 1 - for original events
|
|
17
18
|
MARGIN_REGISTRY_ID: '0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4',
|
|
18
19
|
} as const;
|
|
19
20
|
|
|
@@ -115,14 +116,42 @@ export const TESTNET_POOLS = {
|
|
|
115
116
|
|
|
116
117
|
export const TESTNET_MARGIN_POOLS = {
|
|
117
118
|
SUI: {
|
|
118
|
-
// Actual margin pool address on testnet (
|
|
119
|
+
// Actual margin pool address on testnet (updated 2025-11-20) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-20 更新)
|
|
119
120
|
address: '0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7',
|
|
120
121
|
coinType: '0x2::sui::SUI',
|
|
122
|
+
initialVersion: 658877881,
|
|
123
|
+
supplyCap: 1000000, // 供應上限 | Supply cap
|
|
124
|
+
maxUtilizationRate: 0.9, // 最大使用率 | Max utilization rate
|
|
125
|
+
referralSpread: 0.1, // Referral spread
|
|
126
|
+
minBorrow: 0.1, // 最小借款額 | Min borrow amount
|
|
121
127
|
},
|
|
122
128
|
DBUSDC: {
|
|
123
|
-
// Actual margin pool address on testnet (
|
|
129
|
+
// Actual margin pool address on testnet (updated 2025-11-20) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-20 更新)
|
|
124
130
|
address: '0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69',
|
|
125
131
|
coinType: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC',
|
|
132
|
+
initialVersion: 658877215,
|
|
133
|
+
supplyCap: 1000000, // 供應上限 | Supply cap
|
|
134
|
+
maxUtilizationRate: 0.95, // 最大使用率 | Max utilization rate
|
|
135
|
+
referralSpread: 0.1, // Referral spread
|
|
136
|
+
minBorrow: 0.1, // 最小借款額 | Min borrow amount
|
|
137
|
+
},
|
|
138
|
+
} as const;
|
|
139
|
+
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Risk Parameters Configuration | 風險參數配置
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Risk parameters for trading pairs | 交易對的風險參數
|
|
146
|
+
*/
|
|
147
|
+
export const TESTNET_RISK_PARAMETERS = {
|
|
148
|
+
SUI_DBUSDC: {
|
|
149
|
+
minWithdrawRiskRatio: 2, // 最小提款風險比率 | Min withdraw risk ratio
|
|
150
|
+
minBorrowRiskRatio: 1.25, // 最小借款風險比率 | Min borrow risk ratio
|
|
151
|
+
liquidationRiskRatio: 1.1, // 清算風險比率 | Liquidation risk ratio
|
|
152
|
+
targetLiquidationRiskRatio: 1.25, // 目標清算風險比率 | Target liquidation risk ratio
|
|
153
|
+
userLiquidationReward: 0.03, // 用戶清算獎勵 | User liquidation reward (3%)
|
|
154
|
+
poolLiquidationReward: 0.02, // 池子清算獎勵 | Pool liquidation reward (2%)
|
|
126
155
|
},
|
|
127
156
|
} as const;
|
|
128
157
|
|
|
@@ -10,17 +10,17 @@ import { bcs } from '@mysten/sui/bcs';
|
|
|
10
10
|
import { TESTNET_COINS, TESTNET_MARGIN_POOLS, TESTNET_POOLS } from '../testnet-config';
|
|
11
11
|
import { NetworkType } from './types';
|
|
12
12
|
import { BigNumber } from 'bignumber.js';
|
|
13
|
+
import { mul, normalize } from '../utils/math';
|
|
13
14
|
|
|
14
15
|
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
15
16
|
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
16
17
|
type InterestConfig = {
|
|
17
|
-
midKink: number;
|
|
18
18
|
highKink: number;
|
|
19
19
|
baseBorrowApr: number;
|
|
20
|
-
|
|
21
|
-
highBorrowApr: number;
|
|
20
|
+
borrowAprOnHighKink: number;
|
|
22
21
|
maxBorrowApr: number;
|
|
23
|
-
|
|
22
|
+
supplyApr: number;
|
|
23
|
+
utilizationRate: number;
|
|
24
24
|
};
|
|
25
25
|
export type MarginPoolParams = Record<
|
|
26
26
|
MarginPoolParamKey | MarginPoolWithSupplierCapParamKey,
|
|
@@ -208,13 +208,12 @@ export class DeepBookMarginPool {
|
|
|
208
208
|
userSupplyShares: 0,
|
|
209
209
|
userSupplyAmount: 0,
|
|
210
210
|
decimals: 0,
|
|
211
|
-
midKink: 0,
|
|
212
211
|
highKink: 0,
|
|
213
212
|
baseBorrowApr: 0,
|
|
214
|
-
|
|
215
|
-
highBorrowApr: 0,
|
|
213
|
+
borrowAprOnHighKink: 0,
|
|
216
214
|
maxBorrowApr: 0,
|
|
217
|
-
|
|
215
|
+
supplyApr: 0,
|
|
216
|
+
utilizationRate: 0,
|
|
218
217
|
...coin,
|
|
219
218
|
};
|
|
220
219
|
|
|
@@ -242,94 +241,78 @@ export class DeepBookMarginPool {
|
|
|
242
241
|
return formatted;
|
|
243
242
|
}
|
|
244
243
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Compute the borrow APR based on utilization and interest config.
|
|
246
|
+
*
|
|
247
|
+
* if (U < optimal) {
|
|
248
|
+
* r(U) = base_rate + mul(U, base_slope)
|
|
249
|
+
* } else {
|
|
250
|
+
* r(U) = base_rate + mul(optimal, base_slope) + mul(U - optimal, excess_slope)
|
|
251
|
+
* }
|
|
252
|
+
*/
|
|
253
|
+
#computeBorrowAprAtUtil(
|
|
254
|
+
util: bigint, // 1e9-scaled utilization
|
|
255
|
+
cfg: RawInterestConfig
|
|
256
|
+
): bigint {
|
|
257
|
+
const baseRate = BigInt(cfg.base_rate);
|
|
258
|
+
const baseSlope = BigInt(cfg.base_slope);
|
|
259
|
+
const excessSlope = BigInt(cfg.excess_slope);
|
|
260
|
+
const optimalUtil = BigInt(cfg.optimal_utilization);
|
|
261
|
+
|
|
262
|
+
if (util < optimalUtil) {
|
|
263
|
+
return baseRate + mul(util, baseSlope);
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
|
|
265
|
-
return (
|
|
266
|
-
baseRate +
|
|
267
|
-
baseSlope +
|
|
268
|
-
(excessSlope * (U - optimalUtil)) / (BigInt(FLOAT_SCALAR) - optimalUtil)
|
|
269
|
-
);
|
|
266
|
+
return baseRate + mul(optimalUtil, baseSlope) + mul(util - optimalUtil, excessSlope);
|
|
270
267
|
}
|
|
271
268
|
|
|
272
|
-
#
|
|
269
|
+
#calculateKinksAndRate(
|
|
273
270
|
interestConfig: RawInterestConfig,
|
|
274
271
|
marginPoolConfig: RawMarginPoolConfig,
|
|
275
|
-
state: RawStatePoolConfig
|
|
272
|
+
state: RawStatePoolConfig,
|
|
273
|
+
borrowApr: number
|
|
276
274
|
) {
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
baseRate +
|
|
295
|
-
baseSlope +
|
|
296
|
-
(excessSlope * (maxUtil - optimalUtil)) / (1_000_000_000n - optimalUtil);
|
|
297
|
-
|
|
298
|
-
// maxBorrowApr at U = 1.0 (i.e. 1e9)
|
|
299
|
-
const maxBorrowApr = baseRate + baseSlope + excessSlope;
|
|
300
|
-
const currentBorrowApr = this.#getCurrentBorrowApr(
|
|
301
|
-
BigInt(
|
|
302
|
-
BigNumber(state.total_borrow)
|
|
303
|
-
.dividedBy(state.total_supply)
|
|
304
|
-
.shiftedBy(9)
|
|
305
|
-
.decimalPlaces(0)
|
|
306
|
-
.toString()
|
|
307
|
-
),
|
|
308
|
-
interestConfig
|
|
275
|
+
const highKink = BigInt(interestConfig.optimal_utilization); // v0
|
|
276
|
+
const maxKink = BigInt(marginPoolConfig.max_utilization_rate); // U_max
|
|
277
|
+
|
|
278
|
+
// const midBorrowApr = this.#computeBorrowAprAtUtil(midKink, interestConfig);
|
|
279
|
+
const borrowAprOnHighKink = this.#computeBorrowAprAtUtil(highKink, interestConfig);
|
|
280
|
+
const maxBorrowApr = this.#computeBorrowAprAtUtil(maxKink, interestConfig);
|
|
281
|
+
|
|
282
|
+
const utilizationRate = BigInt(
|
|
283
|
+
BigNumber(state.total_borrow)
|
|
284
|
+
.dividedBy(state.total_supply)
|
|
285
|
+
.shiftedBy(9)
|
|
286
|
+
.decimalPlaces(0)
|
|
287
|
+
.toString()
|
|
288
|
+
);
|
|
289
|
+
const supplyApr = mul(
|
|
290
|
+
mul(BigInt(borrowApr), utilizationRate),
|
|
291
|
+
BigInt(FLOAT_SCALAR) - BigInt(marginPoolConfig.protocol_spread)
|
|
309
292
|
);
|
|
310
|
-
|
|
311
|
-
const normalize = (value: bigint) => BigNumber(value).dividedBy(FLOAT_SCALAR).toNumber();
|
|
312
293
|
|
|
313
294
|
return {
|
|
314
|
-
// raw 1e9-scaled values
|
|
295
|
+
// raw 1e9-scaled values
|
|
315
296
|
raw: {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
midBorrowApr,
|
|
320
|
-
|
|
321
|
-
maxBorrowApr,
|
|
322
|
-
|
|
297
|
+
baseBorrowApr: interestConfig.base_rate,
|
|
298
|
+
// midKink, // utilization at kink1
|
|
299
|
+
highKink, // utilization at kink2
|
|
300
|
+
// midBorrowApr, // APR at midKink
|
|
301
|
+
borrowAprOnHighKink, // APR at highKink
|
|
302
|
+
maxBorrowApr, // APR at U = 1.0
|
|
303
|
+
supplyApr,
|
|
304
|
+
utilizationRate,
|
|
323
305
|
},
|
|
324
|
-
// convenience
|
|
306
|
+
// convenience normalized numbers
|
|
325
307
|
normalized: {
|
|
326
|
-
|
|
308
|
+
baseBorrowApr: normalize(BigInt(interestConfig.base_rate)),
|
|
309
|
+
// midKink: normalize(midKink),
|
|
327
310
|
highKink: normalize(highKink),
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
highBorrowApr: normalize(highBorrowApr),
|
|
311
|
+
// midBorrowApr: normalize(midBorrowApr),
|
|
312
|
+
borrowAprOnHighKink: normalize(borrowAprOnHighKink),
|
|
331
313
|
maxBorrowApr: normalize(maxBorrowApr),
|
|
332
|
-
|
|
314
|
+
supplyApr: normalize(supplyApr),
|
|
315
|
+
utilizationRate: normalize(utilizationRate),
|
|
333
316
|
},
|
|
334
317
|
};
|
|
335
318
|
}
|
|
@@ -339,7 +322,7 @@ export class DeepBookMarginPool {
|
|
|
339
322
|
* @param coinKey - Asset key.
|
|
340
323
|
* @returns Interest configuration data including kinks and APRs.
|
|
341
324
|
*/
|
|
342
|
-
async #getInterestConfig(coinKey: string) {
|
|
325
|
+
async #getInterestConfig(coinKey: string, interestRate: number) {
|
|
343
326
|
const { address } = this.dbConfig.getMarginPool(coinKey);
|
|
344
327
|
const response = await this.suiClient.getObject({
|
|
345
328
|
id: address,
|
|
@@ -353,10 +336,11 @@ export class DeepBookMarginPool {
|
|
|
353
336
|
const interestConfig = config.interest_config.fields as RawInterestConfig;
|
|
354
337
|
const marginPoolConfig = config.margin_pool_config.fields as RawMarginPoolConfig;
|
|
355
338
|
const statePoolConfig = fields.state.fields as RawStatePoolConfig;
|
|
356
|
-
const { normalized } = this.#
|
|
339
|
+
const { normalized } = this.#calculateKinksAndRate(
|
|
357
340
|
interestConfig,
|
|
358
341
|
marginPoolConfig,
|
|
359
|
-
statePoolConfig
|
|
342
|
+
statePoolConfig,
|
|
343
|
+
interestRate
|
|
360
344
|
);
|
|
361
345
|
return normalized;
|
|
362
346
|
}
|
|
@@ -426,11 +410,14 @@ export class DeepBookMarginPool {
|
|
|
426
410
|
sender: this.dbConfig.address,
|
|
427
411
|
});
|
|
428
412
|
|
|
429
|
-
const interestData = await this.#getInterestConfig(coinKey);
|
|
430
413
|
const formattedResult = this.#formatResult(
|
|
431
414
|
this.#parseInspectResultToBcsStructs(inspectResult, allKeys),
|
|
432
415
|
coinKey
|
|
433
416
|
);
|
|
417
|
+
const interestData = await this.#getInterestConfig(
|
|
418
|
+
coinKey,
|
|
419
|
+
formattedResult.interestRate * FLOAT_SCALAR
|
|
420
|
+
);
|
|
434
421
|
|
|
435
422
|
return {
|
|
436
423
|
...formattedResult,
|
|
@@ -179,10 +179,17 @@ export class DeepBookMarginToolkit {
|
|
|
179
179
|
async createSupplierCap(): Promise<string | null> {
|
|
180
180
|
try {
|
|
181
181
|
const tx = new Transaction();
|
|
182
|
+
tx.setSender(this.address);
|
|
183
|
+
|
|
184
|
+
// Direct moveCall to get the returned object reference
|
|
185
|
+
// Based on SDK source: margin_pool::mint_supplier_cap
|
|
186
|
+
const supplierCap = tx.moveCall({
|
|
187
|
+
target: `${TESTNET_PACKAGES.MARGIN_PACKAGE_ID}::margin_pool::mint_supplier_cap`,
|
|
188
|
+
arguments: [tx.object(TESTNET_PACKAGES.MARGIN_REGISTRY_ID), tx.object.clock()],
|
|
189
|
+
});
|
|
182
190
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
tx.transferObjects([cap], this.address);
|
|
191
|
+
// Transfer the created Supplier Cap to the sender
|
|
192
|
+
tx.transferObjects([supplierCap], tx.pure.address(this.address));
|
|
186
193
|
|
|
187
194
|
const result = await this.suiClient.signAndExecuteTransaction({
|
|
188
195
|
signer: this.keypair,
|
|
@@ -191,6 +198,7 @@ export class DeepBookMarginToolkit {
|
|
|
191
198
|
showEffects: true,
|
|
192
199
|
showObjectChanges: true,
|
|
193
200
|
},
|
|
201
|
+
requestType: 'WaitForLocalExecution',
|
|
194
202
|
});
|
|
195
203
|
|
|
196
204
|
if (result.errors && result.errors.length > 0) {
|
|
@@ -222,9 +230,26 @@ export class DeepBookMarginToolkit {
|
|
|
222
230
|
async createSupplyReferral(coin: MarginCoinType): Promise<string | null> {
|
|
223
231
|
try {
|
|
224
232
|
const tx = new Transaction();
|
|
233
|
+
tx.setSender(this.address);
|
|
225
234
|
|
|
226
|
-
//
|
|
227
|
-
|
|
235
|
+
// Get margin pool configuration
|
|
236
|
+
const marginPool = TESTNET_MARGIN_POOLS[coin];
|
|
237
|
+
|
|
238
|
+
// Use the initialVersion from config as the initial_shared_version
|
|
239
|
+
// Margin pools are shared objects on Sui
|
|
240
|
+
tx.moveCall({
|
|
241
|
+
target: `${TESTNET_PACKAGES.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,
|
|
242
|
+
arguments: [
|
|
243
|
+
tx.sharedObjectRef({
|
|
244
|
+
objectId: marginPool.address,
|
|
245
|
+
initialSharedVersion: marginPool.initialVersion,
|
|
246
|
+
mutable: true,
|
|
247
|
+
}),
|
|
248
|
+
tx.object(TESTNET_PACKAGES.MARGIN_REGISTRY_ID),
|
|
249
|
+
tx.object.clock(),
|
|
250
|
+
],
|
|
251
|
+
typeArguments: [marginPool.coinType],
|
|
252
|
+
});
|
|
228
253
|
|
|
229
254
|
const result = await this.suiClient.signAndExecuteTransaction({
|
|
230
255
|
signer: this.keypair,
|
|
@@ -233,6 +258,7 @@ export class DeepBookMarginToolkit {
|
|
|
233
258
|
showEffects: true,
|
|
234
259
|
showObjectChanges: true,
|
|
235
260
|
},
|
|
261
|
+
requestType: 'WaitForLocalExecution',
|
|
236
262
|
});
|
|
237
263
|
|
|
238
264
|
if (result.errors && result.errors.length > 0) {
|
|
@@ -288,6 +314,7 @@ export class DeepBookMarginToolkit {
|
|
|
288
314
|
showEffects: true,
|
|
289
315
|
showObjectChanges: true,
|
|
290
316
|
},
|
|
317
|
+
requestType: 'WaitForLocalExecution',
|
|
291
318
|
});
|
|
292
319
|
|
|
293
320
|
if (errors && errors.length > 0) {
|
|
@@ -336,6 +363,7 @@ export class DeepBookMarginToolkit {
|
|
|
336
363
|
showEffects: true,
|
|
337
364
|
showObjectChanges: true,
|
|
338
365
|
},
|
|
366
|
+
requestType: 'WaitForLocalExecution',
|
|
339
367
|
});
|
|
340
368
|
|
|
341
369
|
if (errors && errors.length > 0) {
|
|
@@ -371,6 +399,7 @@ export class DeepBookMarginToolkit {
|
|
|
371
399
|
showObjectChanges: true,
|
|
372
400
|
showBalanceChanges: true,
|
|
373
401
|
},
|
|
402
|
+
requestType: 'WaitForLocalExecution',
|
|
374
403
|
});
|
|
375
404
|
|
|
376
405
|
if (errors && errors.length > 0) {
|
package/src/toolkit/types.ts
CHANGED
|
@@ -71,3 +71,56 @@ export interface ReferralInfo {
|
|
|
71
71
|
/** Owner address | 擁有者地址 */
|
|
72
72
|
owner: string;
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Margin Pool Parameter Types | Margin Pool 參數類型
|
|
77
|
+
// ============================================================================
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Margin pool configuration parameters | Margin pool 配置參數
|
|
81
|
+
*/
|
|
82
|
+
export interface MarginPoolConfig {
|
|
83
|
+
/** Pool address | 池子地址 */
|
|
84
|
+
address: string;
|
|
85
|
+
|
|
86
|
+
/** Coin type | 幣種類型 */
|
|
87
|
+
coinType: string;
|
|
88
|
+
|
|
89
|
+
/** Initial version | 初始版本 */
|
|
90
|
+
initialVersion: number;
|
|
91
|
+
|
|
92
|
+
/** Supply cap | 供應上限 */
|
|
93
|
+
supplyCap: number;
|
|
94
|
+
|
|
95
|
+
/** Max utilization rate | 最大使用率 */
|
|
96
|
+
maxUtilizationRate: number;
|
|
97
|
+
|
|
98
|
+
/** Referral spread | Referral spread */
|
|
99
|
+
referralSpread: number;
|
|
100
|
+
|
|
101
|
+
/** Min borrow amount | 最小借款額 */
|
|
102
|
+
minBorrow: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Risk parameters for a trading pair | 交易對的風險參數
|
|
107
|
+
*/
|
|
108
|
+
export interface RiskParameters {
|
|
109
|
+
/** Min withdraw risk ratio | 最小提款風險比率 */
|
|
110
|
+
minWithdrawRiskRatio: number;
|
|
111
|
+
|
|
112
|
+
/** Min borrow risk ratio | 最小借款風險比率 */
|
|
113
|
+
minBorrowRiskRatio: number;
|
|
114
|
+
|
|
115
|
+
/** Liquidation risk ratio | 清算風險比率 */
|
|
116
|
+
liquidationRiskRatio: number;
|
|
117
|
+
|
|
118
|
+
/** Target liquidation risk ratio | 目標清算風險比率 */
|
|
119
|
+
targetLiquidationRiskRatio: number;
|
|
120
|
+
|
|
121
|
+
/** User liquidation reward | 用戶清算獎勵 */
|
|
122
|
+
userLiquidationReward: number;
|
|
123
|
+
|
|
124
|
+
/** Pool liquidation reward | 池子清算獎勵 */
|
|
125
|
+
poolLiquidationReward: number;
|
|
126
|
+
}
|