@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 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
- midBorrowApr: number;
58
- highBorrowApr: number;
56
+ borrowAprOnHighKink: number;
59
57
  maxBorrowApr: number;
60
- borrowApr: number;
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
- midBorrowApr: number;
58
- highBorrowApr: number;
56
+ borrowAprOnHighKink: number;
59
57
  maxBorrowApr: number;
60
- borrowApr: number;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scallop-io/scallop-deepbook-kit",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A toolkit for integrating Scallop with DeepBook functionality",
5
5
  "keywords": [
6
6
  "scallop",
@@ -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
- * Last updated | 最後更新: 2025-11-11
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 (verified 2025-11-11) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-11 驗證)
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 (verified 2025-11-11) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-11 驗證)
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
- midBorrowApr: number;
21
- highBorrowApr: number;
20
+ borrowAprOnHighKink: number;
22
21
  maxBorrowApr: number;
23
- borrowApr: number;
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
- midBorrowApr: 0,
215
- highBorrowApr: 0,
213
+ borrowAprOnHighKink: 0,
216
214
  maxBorrowApr: 0,
217
- borrowApr: 0,
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
- #getCurrentBorrowApr(utilizationRate: bigint, interestConfig: RawInterestConfig) {
246
- const baseRate = BigInt(interestConfig.base_rate); // B
247
- const baseSlope = BigInt(interestConfig.base_slope); // S
248
- const excessSlope = BigInt(interestConfig.excess_slope); // E
249
- const optimalUtil = BigInt(interestConfig.optimal_utilization); // Uopt
250
-
251
- // clamp U to [0, 1e9]
252
- const U =
253
- utilizationRate < 0n
254
- ? 0n
255
- : utilizationRate > BigInt(FLOAT_SCALAR)
256
- ? BigInt(FLOAT_SCALAR)
257
- : BigInt(utilizationRate);
258
-
259
- if (U <= optimalUtil) {
260
- // r(U) = B + S * U / Uopt
261
- return baseRate + (baseSlope * U) / optimalUtil;
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
- // r(U) = B + S + E * (U - Uopt) / (1 - Uopt)
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
- #calculateKinksAndBorrowApr(
269
+ #calculateKinksAndRate(
273
270
  interestConfig: RawInterestConfig,
274
271
  marginPoolConfig: RawMarginPoolConfig,
275
- state: RawStatePoolConfig
272
+ state: RawStatePoolConfig,
273
+ borrowApr: number
276
274
  ) {
277
- const baseRate = BigInt(interestConfig.base_rate);
278
- const baseSlope = BigInt(interestConfig.base_slope);
279
- const excessSlope = BigInt(interestConfig.excess_slope);
280
- const optimalUtil = BigInt(interestConfig.optimal_utilization);
281
- const maxUtil = BigInt(marginPoolConfig.max_utilization_rate);
282
-
283
- // Kinks (still 1e9-scaled)
284
- const midKink = optimalUtil;
285
- const highKink = maxUtil;
286
-
287
- // APRs in 1e9 scale
288
- const baseBorrowApr = baseRate;
289
- const midBorrowApr = baseRate + baseSlope;
290
-
291
- // highBorrowApr at U = maxUtil
292
- // r(U) = base + slope + excess * (U - optimal) / (1 - optimal)
293
- const highBorrowApr =
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 (BigInt)
295
+ // raw 1e9-scaled values
315
296
  raw: {
316
- midKink,
317
- highKink,
318
- baseBorrowApr,
319
- midBorrowApr,
320
- highBorrowApr,
321
- maxBorrowApr,
322
- borrowApr: currentBorrowApr,
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: normalized numbers (e.g. 0.125 = 12.5%)
306
+ // convenience normalized numbers
325
307
  normalized: {
326
- midKink: normalize(midKink),
308
+ baseBorrowApr: normalize(BigInt(interestConfig.base_rate)),
309
+ // midKink: normalize(midKink),
327
310
  highKink: normalize(highKink),
328
- baseBorrowApr: normalize(baseBorrowApr),
329
- midBorrowApr: normalize(midBorrowApr),
330
- highBorrowApr: normalize(highBorrowApr),
311
+ // midBorrowApr: normalize(midBorrowApr),
312
+ borrowAprOnHighKink: normalize(borrowAprOnHighKink),
331
313
  maxBorrowApr: normalize(maxBorrowApr),
332
- borrowApr: normalize(currentBorrowApr),
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.#calculateKinksAndBorrowApr(
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
- // Use MarginPoolContract to create Supplier Cap | 使用 MarginPoolContract 創建 Supplier Cap
184
- const cap = this.marginPoolContract.mintSupplierCap()(tx);
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
- // Use MarginPoolContract to create supply Referral | 使用 MarginPoolContract 創建供應 Referral
227
- this.marginPoolContract.mintSupplyReferral(coin)(tx);
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) {
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ import { FLOAT_SCALAR } from '@mysten/deepbook-v3';
2
+
3
+ export const mul = (a: bigint, b: bigint): bigint => (a * b) / BigInt(FLOAT_SCALAR);
4
+ export const normalize = (value: bigint): number => Number(value) / FLOAT_SCALAR; // e.g. 0.12 = 12%