@scallop-io/scallop-deepbook-kit 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/src/examples/margin-pool-demo.ts +12 -3
- package/src/index.ts +1 -0
- package/src/queries/getOnChainMarginPools.ts +86 -0
- package/src/queries/index.ts +1 -0
- package/src/toolkit/DeepBookMarginPool.ts +154 -22
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var grpc=require('@mysten/sui/grpc'),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 U=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),O=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),R=a=>{if(U(a))return bcs$1.fromHex(a);if(O(a))return bcs$1.fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},w=a=>{if(a.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return a.slice(0,cryptography.PRIVATE_KEY_SIZE);if(a.length===cryptography.PRIVATE_KEY_SIZE+1&&a[0]===0)return a.slice(1);if(a.length===cryptography.PRIVATE_KEY_SIZE)return a;throw new Error("invalid secret key")};function h(a){switch(a){case "mainnet":return "https://fullnode.mainnet.sui.io:443";case "testnet":return "https://fullnode.testnet.sui.io:443";default:throw new Error(`Unknown network: ${a}`)}}var b=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:t,fullnodeUrl:r,supplierCapId:e,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=r??h(t);this.suiClient=new grpc.SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e,this.dbConfig=n??new deepbookV3.DeepBookConfig({network:t,address:this.address}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#r(t){if(t.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:r}=cryptography.decodeSuiPrivateKey(t);return ed25519.Ed25519Keypair.fromSecretKey(w(r))}return ed25519.Ed25519Keypair.fromSecretKey(w(R(t)))}async#t(t,r={}){let e=await t.build({client:this.suiClient}),{signature:i}=await this.keypair.signTransaction(e),o=await this.suiClient.core.executeTransaction({transaction:e,signatures:[i],include:{effects:true,objectTypes:r.includeObjectTypes,balanceChanges:r.includeBalanceChanges}}),n=o.$kind==="Transaction"?o.Transaction:o.FailedTransaction;if(!n?.effects.status.success)throw new Error(`Transaction failed: ${n?.effects.status.error||"Unknown error"}`);return n}#i(t,r){if(!t.effects.changedObjects)return null;for(let e of t.effects.changedObjects){let i=t.objectTypes?.[e.objectId];if(!e.inputDigest&&i?.includes(r))return e.objectId}return null}#e(){if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");return this.supplierCapId}async#o(){let t=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.core.listOwnedObjects({owner:this.address,type:t})).objects?.[0]?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let t=await this.#o();if(t)return this.supplierCapId=t,t;let r=await this.createSupplierCap();if(!r)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=r,r}async createSupplierCap(){try{let t=new transactions.Transaction;t.setSender(this.address);let r=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([r],t.pure.address(this.address));let e=await this.#t(t,{includeObjectTypes:!0});return this.#i(e,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let r=new transactions.Transaction;r.setSender(this.address);let e=this.dbConfig.getMarginPool(t);if(!e)throw new Error(`Margin pool configuration not found for coin: ${t}`);r.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[r.object(e.address),r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()],typeArguments:[e.type]});let i=await this.#t(r,{includeObjectTypes:!0});return this.#i(i,"SupplyReferral")}catch(r){throw new Error(`Failed to create Supply Referral: ${r.message||r}`)}}async supplyToMarginPool(t,r,e){try{let i=this.#e(),o=new transactions.Transaction;o.setSender(this.address);let n=o.object(i);return o.add(this.marginPoolContract.supplyToMarginPool(t,n,r,e)),await this.#t(o),!0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(t,r){try{let e=this.#e(),i=new transactions.Transaction;i.setSender(this.address);let o=i.object(e),s=this.marginPoolContract.withdrawFromMarginPool(t,o,r)(i);return i.transferObjects([s],this.address),await this.#t(i),!0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(t,r){try{let e=new transactions.Transaction;return e.setSender(this.address),e.add(this.marginPoolContract.withdrawReferralFees(t,r)),await this.#t(e,{includeBalanceChanges:!0}),!0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(t){try{let r=this.#e(),e=new transactions.Transaction;e.setSender(this.address),e.add(this.marginPoolContract.userSupplyAmount(t,r));let i=await e.build({client:this.suiClient}),o=await this.suiClient.core.simulateTransaction({transaction:i,include:{commandResults:!0}}),n=this.dbConfig.getCoin(t),s=0,p=o.commandResults;if(p&&p[0]){let m=p[0].returnValues?.[0];if(m?.bcs){let f=Buffer.from(m.bcs).readBigUInt64LE();s=Number(f)/n.scalar;}}let l=await this.suiClient.core.getBalance({owner:this.address,coinType:n.type}),c=Number(l.balance)/n.scalar;return {userSupplyAmount:s,walletBalance:c}}catch(r){throw new Error(`Failed to get balance: ${r.message||r}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var S=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],P=["userSupplyShares","userSupplyAmount"],I={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 d=(a,t)=>a*t/BigInt(deepbookV3.FLOAT_SCALAR),u=a=>Number(a)/deepbookV3.FLOAT_SCALAR;var Y=new Set(P),D=a=>Y.has(a),_=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:r="",suiClient:e,dbConfig:i}={}){let o=t??i?.network??"mainnet";if(this.dbConfig=i??new deepbookV3.DeepBookConfig({network:o,address:r}),this.suiClient=e??new grpc.SuiGrpcClient({baseUrl:h(o),network:o}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),t!==void 0&&i!==void 0&&t!==i.network)throw new Error(`Mismatch between provided network (${t}) and dbConfig network (${i.network}).`)}get network(){return this.dbConfig.network}#r(t,r,e,i){if(D(r)){let o=this.marginPoolContract[r];if(i==null)throw new Error(`supplierCap is required for '${r}'.`);t.add(o(e,i));}else {let o=this.marginPoolContract[r];t.add(o(e));}}parseInspectResultToBcsStructs(t,r){let e=t.commandResults;if(!e)throw new Error("No results found in simulateTransaction output.");return r.reduce((i,o,n)=>{let s=e[n]?.returnValues?.[0]?.bcs;if(!s)return i;let p=bcs.bcs[I[o]];return i[o]=p.parse(new Uint8Array(s)),i},{})}formatResult(t,r){let e=this.dbConfig.getCoin(r),i={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:e.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...e};if(!e)return i;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[n,s]of Object.entries(t))n==="lastUpdateTimestamp"?i[n]=Number(s):o.has(n)?i[n]=new bignumber_js.BigNumber(s).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():i[n]=new bignumber_js.BigNumber(s).dividedBy(e.scalar).toNumber();return i}computeBorrowAprAtUtil(t,r){let e=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),n=BigInt(r.optimal_utilization);return t<n?e+d(t,i):e+d(n,i)+d(t-n,o)}calculateKinksAndRate(t,r,e,i){let o=BigInt(t.optimal_utilization),n=BigInt(r.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),p=this.computeBorrowAprAtUtil(n,t),l=BigInt(bignumber_js.BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=d(d(i,l),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(r.protocol_spread));return {raw:{baseBorrowApr:t.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:p,supplyApr:c,utilizationRate:l},normalized:{baseBorrowApr:u(BigInt(t.base_rate)),highKink:u(o),borrowAprOnHighKink:u(s),maxBorrowApr:u(p),supplyApr:u(c),utilizationRate:u(l)}}}async#t(t,r){let{address:e}=this.dbConfig.getMarginPool(t),o=(await this.suiClient.core.getObject({objectId:e,include:{json:true}})).object?.json?.fields,n=o.config.fields,s=n.interest_config.fields,p=n.margin_pool_config.fields,l=o.state.fields,{normalized:c}=this.calculateKinksAndRate(s,p,l,r);return c}async getPoolParameters(t,r,e=new transactions.Transaction,i=true){if(S.forEach(y=>this.#r(e,y,t)),r&&P.forEach(y=>this.#r(e,y,t,r)),!i)return e;let o=[...S,...P],n=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(n);let s=await e.build({client:this.suiClient}),p=await this.suiClient.core.simulateTransaction({transaction:s,include:{commandResults:true}}),l=this.parseInspectResultToBcsStructs(p,o),c=this.formatResult(l,t),m=BigInt(l.interestRate??0),f=await this.#t(t,m);return {...c,...f}}};
|
|
2
|
-
exports.DeepBookMarginPool=
|
|
1
|
+
'use strict';var grpc=require('@mysten/sui/grpc'),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'),utils=require('@mysten/sui/utils');var J=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),X=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),O=a=>{if(J(a))return bcs$1.fromHex(a);if(X(a))return bcs$1.fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},M=a=>{if(a.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return a.slice(0,cryptography.PRIVATE_KEY_SIZE);if(a.length===cryptography.PRIVATE_KEY_SIZE+1&&a[0]===0)return a.slice(1);if(a.length===cryptography.PRIVATE_KEY_SIZE)return a;throw new Error("invalid secret key")};function P(a){switch(a){case "mainnet":return "https://fullnode.mainnet.sui.io:443";case "testnet":return "https://fullnode.testnet.sui.io:443";default:throw new Error(`Unknown network: ${a}`)}}var I=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:t,fullnodeUrl:r,supplierCapId:e,privateKey:n,supplierCapPackageId:o,dbConfig:i}){let s=r??P(t);this.suiClient=new grpc.SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#e(n),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e,this.dbConfig=i??new deepbookV3.DeepBookConfig({network:t,address:this.address}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#e(t){if(t.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:r}=cryptography.decodeSuiPrivateKey(t);return ed25519.Ed25519Keypair.fromSecretKey(M(r))}return ed25519.Ed25519Keypair.fromSecretKey(M(O(t)))}async#t(t,r={}){let e=await t.build({client:this.suiClient}),{signature:n}=await this.keypair.signTransaction(e),o=await this.suiClient.core.executeTransaction({transaction:e,signatures:[n],include:{effects:true,objectTypes:r.includeObjectTypes,balanceChanges:r.includeBalanceChanges}}),i=o.$kind==="Transaction"?o.Transaction:o.FailedTransaction;if(!i?.effects.status.success)throw new Error(`Transaction failed: ${i?.effects.status.error||"Unknown error"}`);return i}#n(t,r){if(!t.effects.changedObjects)return null;for(let e of t.effects.changedObjects){let n=t.objectTypes?.[e.objectId];if(!e.inputDigest&&n?.includes(r))return e.objectId}return null}#r(){if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");return this.supplierCapId}async#o(){let t=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.core.listOwnedObjects({owner:this.address,type:t})).objects?.[0]?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let t=await this.#o();if(t)return this.supplierCapId=t,t;let r=await this.createSupplierCap();if(!r)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=r,r}async createSupplierCap(){try{let t=new transactions.Transaction;t.setSender(this.address);let r=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([r],t.pure.address(this.address));let e=await this.#t(t,{includeObjectTypes:!0});return this.#n(e,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let r=new transactions.Transaction;r.setSender(this.address);let e=this.dbConfig.getMarginPool(t);if(!e)throw new Error(`Margin pool configuration not found for coin: ${t}`);r.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[r.object(e.address),r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()],typeArguments:[e.type]});let n=await this.#t(r,{includeObjectTypes:!0});return this.#n(n,"SupplyReferral")}catch(r){throw new Error(`Failed to create Supply Referral: ${r.message||r}`)}}async supplyToMarginPool(t,r,e){try{let n=this.#r(),o=new transactions.Transaction;o.setSender(this.address);let i=o.object(n);return o.add(this.marginPoolContract.supplyToMarginPool(t,i,r,e)),await this.#t(o),!0}catch(n){throw new Error(`Failed to supply to margin pool: ${n.message||n}`)}}async withdrawFromMarginPool(t,r){try{let e=this.#r(),n=new transactions.Transaction;n.setSender(this.address);let o=n.object(e),s=this.marginPoolContract.withdrawFromMarginPool(t,o,r)(n);return n.transferObjects([s],this.address),await this.#t(n),!0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(t,r){try{let e=new transactions.Transaction;return e.setSender(this.address),e.add(this.marginPoolContract.withdrawReferralFees(t,r)),await this.#t(e,{includeBalanceChanges:!0}),!0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(t){try{let r=this.#r(),e=new transactions.Transaction;e.setSender(this.address),e.add(this.marginPoolContract.userSupplyAmount(t,r));let n=await e.build({client:this.suiClient}),o=await this.suiClient.core.simulateTransaction({transaction:n,include:{commandResults:!0}}),i=this.dbConfig.getCoin(t),s=0,l=o.commandResults;if(l&&l[0]){let g=l[0].returnValues?.[0];if(g?.bcs){let C=Buffer.from(g.bcs).readBigUInt64LE();s=Number(C)/i.scalar;}}let p=await this.suiClient.core.getBalance({owner:this.address,coinType:i.type}),u=Number(p.balance)/i.scalar;return {userSupplyAmount:s,walletBalance:u}}catch(r){throw new Error(`Failed to get balance: ${r.message||r}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var b=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],h=["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 y=(a,t)=>a*t/BigInt(deepbookV3.FLOAT_SCALAR),m=a=>Number(a)/deepbookV3.FLOAT_SCALAR;var st=new Set(h),lt=a=>st.has(a),K=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:r="",suiClient:e,dbConfig:n}={}){let o=t??n?.network??"mainnet";if(this.dbConfig=n??new deepbookV3.DeepBookConfig({network:o,address:r}),this.suiClient=e??new grpc.SuiGrpcClient({baseUrl:P(o),network:o}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),t!==void 0&&n!==void 0&&t!==n.network)throw new Error(`Mismatch between provided network (${t}) and dbConfig network (${n.network}).`)}get network(){return this.dbConfig.network}#e(t,r,e,n){if(lt(r)){let o=this.marginPoolContract[r];if(n==null)throw new Error(`supplierCap is required for '${r}'.`);t.add(o(e,n));}else {let o=this.marginPoolContract[r];t.add(o(e));}}parseInspectResultToBcsStructs(t,r){let e=t.commandResults;if(!e)throw new Error("No results found in simulateTransaction output.");return r.reduce((n,o,i)=>{let s=e[i]?.returnValues?.[0]?.bcs;if(!s)return n;let l=bcs.bcs[T[o]];return n[o]=l.parse(new Uint8Array(s)),n},{})}formatResult(t,r){let e=this.dbConfig.getCoin(r),n={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:e.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...e};if(!e)return n;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,s]of Object.entries(t))i==="lastUpdateTimestamp"?n[i]=Number(s):o.has(i)?n[i]=new bignumber_js.BigNumber(s).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():n[i]=new bignumber_js.BigNumber(s).dividedBy(e.scalar).toNumber();return n}computeBorrowAprAtUtil(t,r){let e=BigInt(r.base_rate),n=BigInt(r.base_slope),o=BigInt(r.excess_slope),i=BigInt(r.optimal_utilization);return t<i?e+y(t,n):e+y(i,n)+y(t-i,o)}calculateKinksAndRate(t,r,e,n){let o=BigInt(t.optimal_utilization),i=BigInt(r.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),l=this.computeBorrowAprAtUtil(i,t),p=bignumber_js.BigNumber(e.total_supply).isZero()?0n:BigInt(bignumber_js.BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),u=y(y(n,p),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(r.protocol_spread));return {raw:{baseBorrowApr:t.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:l,supplyApr:u,utilizationRate:p},normalized:{baseBorrowApr:m(BigInt(t.base_rate)),highKink:m(o),borrowAprOnHighKink:m(s),maxBorrowApr:m(l),supplyApr:m(u),utilizationRate:m(p)}}}async#t(t,r,e){let n=e;if(!n){let{address:u}=this.dbConfig.getMarginPool(t);n=(await this.suiClient.core.getObject({objectId:u,include:{json:true}})).object?.json;}let o=n?.config,i=o?.interest_config,s=o?.margin_pool_config,l=n?.state,{normalized:p}=this.calculateKinksAndRate(i,s,l,r);return p}async getPoolParameters(t,r,e=new transactions.Transaction,n=true){if(b.forEach(w=>this.#e(e,w,t)),r&&h.forEach(w=>this.#e(e,w,t,r)),!n)return e;let o=[...b,...h],i=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(i),e.setGasBudget(50000000000n),e.setGasPayment([]);let s=await e.build({client:this.suiClient}),l=await this.suiClient.core.simulateTransaction({transaction:s,include:{commandResults:true}}),p=this.parseInspectResultToBcsStructs(l,o),u=this.formatResult(p,t),g=BigInt(p.interestRate??0),C=await this.#t(t,g);return {...u,...C}}async getPoolsParameters(t,r,e=new transactions.Transaction,n=true){for(let c of t)b.forEach(d=>this.#e(e,d,c)),r&&h.forEach(d=>this.#e(e,d,c,r));if(!n)return e;let o=[...b,...r?h:[]],i=o.length,s=t.map(c=>this.dbConfig.getMarginPool(c).address),l=[];for(let c=0;c<s.length;c+=50)l.push(s.slice(c,c+50));let p=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(p),e.setGasBudget(50000000000n),e.setGasPayment([]);let u=await e.build({client:this.suiClient}),[g,...C]=await Promise.all([this.suiClient.core.simulateTransaction({transaction:u,include:{commandResults:true}}),...l.map(c=>this.suiClient.core.getObjects({objectIds:c,include:{json:true}}))]),w=C.flatMap(c=>c.objects),B=g.commandResults;if(!B)throw new Error("No results found in simulateTransaction output.");return Promise.all(t.map(async(c,d)=>{let z=d*i,E=o.reduce((A,x,Y)=>{let j=B[z+Y]?.returnValues?.[0]?.bcs;if(!j)return A;let H=bcs.bcs[T[x]];return A[x]=H.parse(new Uint8Array(j)),A},{}),L=this.formatResult(E,c),W=BigInt(E.interestRate??0),_=w[d];if(!_||"code"in _)throw new Error(`Failed to fetch interest config for ${c}`);let $=_.json,D=await this.#t(c,W,$);return {...L,...D}}))}};var ut="0x7f7351ef7e5089dfddf17f55abe028d719c45ca91d2c23e45a441ba65897f804",F=50,gt=async({suiClient:a=new grpc.SuiGrpcClient({baseUrl:P("mainnet"),network:"mainnet"}),tableId:t=ut}={})=>{let r=[],e=[],n=null,o=true;for(;o;){let{dynamicFields:i,cursor:s,hasNextPage:l}=await a.listDynamicFields({parentId:t,cursor:n,limit:50});if(e.push(...i.map(p=>p.fieldId)),i.length===0)break;n=s,o=l;}for(let i=0;i<e.length;i+=F){let s=e.slice(i,i+F),{objects:l}=await a.getObjects({objectIds:s,include:{json:true}});for(let p of l){if(p instanceof Error)continue;let u=p.json;!u?.value||!u.name?.name||r.push({address:u.value,type:`0x${u.name.name}`});}}return r.reduce((i,s)=>{let{name:l}=utils.parseStructTag(s.type);return i[l.replace(/_/g,"")]=s,i},{})};
|
|
2
|
+
exports.DeepBookMarginPool=K;exports.DeepBookMarginToolkit=I;exports.getOnChainMarginPools=gt;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeepBookConfig, MarginPoolContract, Coin } from '@mysten/deepbook-v3';
|
|
1
|
+
import { DeepBookConfig, MarginPoolContract, Coin, MarginPool } from '@mysten/deepbook-v3';
|
|
2
2
|
import { SuiClientTypes } from '@mysten/sui/client';
|
|
3
3
|
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
4
4
|
import { Transaction } from '@mysten/sui/transactions';
|
|
@@ -85,6 +85,14 @@ declare class DeepBookMarginPool {
|
|
|
85
85
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
86
86
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams>;
|
|
87
87
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: false): Promise<Transaction>;
|
|
88
|
+
getPoolsParameters(coinKeys: string[], supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams[]>;
|
|
89
|
+
getPoolsParameters(coinKeys: string[], supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams[]>;
|
|
90
|
+
getPoolsParameters(coinKeys: string[], supplierCapId: string | undefined, tx: Transaction, inspect: false): Promise<Transaction>;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
declare const getOnChainMarginPools: ({ suiClient, tableId, }?: {
|
|
94
|
+
suiClient?: Pick<SuiGrpcClient, "listDynamicFields" | "getObjects">;
|
|
95
|
+
tableId?: string;
|
|
96
|
+
}) => Promise<Record<string, MarginPool>>;
|
|
97
|
+
|
|
98
|
+
export { DeepBookMarginPool, DeepBookMarginToolkit, type MarginBalance, type MarginCoinType, type MarginPoolParams, type NetworkType, type ReferralInfo, type ToolkitConfig, type TransactionResult, getOnChainMarginPools };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeepBookConfig, MarginPoolContract, Coin } from '@mysten/deepbook-v3';
|
|
1
|
+
import { DeepBookConfig, MarginPoolContract, Coin, MarginPool } from '@mysten/deepbook-v3';
|
|
2
2
|
import { SuiClientTypes } from '@mysten/sui/client';
|
|
3
3
|
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
4
4
|
import { Transaction } from '@mysten/sui/transactions';
|
|
@@ -85,6 +85,14 @@ declare class DeepBookMarginPool {
|
|
|
85
85
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
86
86
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams>;
|
|
87
87
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: false): Promise<Transaction>;
|
|
88
|
+
getPoolsParameters(coinKeys: string[], supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams[]>;
|
|
89
|
+
getPoolsParameters(coinKeys: string[], supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams[]>;
|
|
90
|
+
getPoolsParameters(coinKeys: string[], supplierCapId: string | undefined, tx: Transaction, inspect: false): Promise<Transaction>;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
declare const getOnChainMarginPools: ({ suiClient, tableId, }?: {
|
|
94
|
+
suiClient?: Pick<SuiGrpcClient, "listDynamicFields" | "getObjects">;
|
|
95
|
+
tableId?: string;
|
|
96
|
+
}) => Promise<Record<string, MarginPool>>;
|
|
97
|
+
|
|
98
|
+
export { DeepBookMarginPool, DeepBookMarginToolkit, type MarginBalance, type MarginCoinType, type MarginPoolParams, type NetworkType, type ReferralInfo, type ToolkitConfig, type TransactionResult, getOnChainMarginPools };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {SuiGrpcClient}from'@mysten/sui/grpc';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 U=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),O=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),R=a=>{if(U(a))return fromHex(a);if(O(a))return fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},w=a=>{if(a.length===LEGACY_PRIVATE_KEY_SIZE)return a.slice(0,PRIVATE_KEY_SIZE);if(a.length===PRIVATE_KEY_SIZE+1&&a[0]===0)return a.slice(1);if(a.length===PRIVATE_KEY_SIZE)return a;throw new Error("invalid secret key")};function h(a){switch(a){case "mainnet":return "https://fullnode.mainnet.sui.io:443";case "testnet":return "https://fullnode.testnet.sui.io:443";default:throw new Error(`Unknown network: ${a}`)}}var b=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:t,fullnodeUrl:r,supplierCapId:e,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=r??h(t);this.suiClient=new SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e,this.dbConfig=n??new DeepBookConfig({network:t,address:this.address}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#r(t){if(t.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:r}=decodeSuiPrivateKey(t);return Ed25519Keypair.fromSecretKey(w(r))}return Ed25519Keypair.fromSecretKey(w(R(t)))}async#t(t,r={}){let e=await t.build({client:this.suiClient}),{signature:i}=await this.keypair.signTransaction(e),o=await this.suiClient.core.executeTransaction({transaction:e,signatures:[i],include:{effects:true,objectTypes:r.includeObjectTypes,balanceChanges:r.includeBalanceChanges}}),n=o.$kind==="Transaction"?o.Transaction:o.FailedTransaction;if(!n?.effects.status.success)throw new Error(`Transaction failed: ${n?.effects.status.error||"Unknown error"}`);return n}#i(t,r){if(!t.effects.changedObjects)return null;for(let e of t.effects.changedObjects){let i=t.objectTypes?.[e.objectId];if(!e.inputDigest&&i?.includes(r))return e.objectId}return null}#e(){if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");return this.supplierCapId}async#o(){let t=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.core.listOwnedObjects({owner:this.address,type:t})).objects?.[0]?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let t=await this.#o();if(t)return this.supplierCapId=t,t;let r=await this.createSupplierCap();if(!r)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=r,r}async createSupplierCap(){try{let t=new Transaction;t.setSender(this.address);let r=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([r],t.pure.address(this.address));let e=await this.#t(t,{includeObjectTypes:!0});return this.#i(e,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let r=new Transaction;r.setSender(this.address);let e=this.dbConfig.getMarginPool(t);if(!e)throw new Error(`Margin pool configuration not found for coin: ${t}`);r.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[r.object(e.address),r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()],typeArguments:[e.type]});let i=await this.#t(r,{includeObjectTypes:!0});return this.#i(i,"SupplyReferral")}catch(r){throw new Error(`Failed to create Supply Referral: ${r.message||r}`)}}async supplyToMarginPool(t,r,e){try{let i=this.#e(),o=new Transaction;o.setSender(this.address);let n=o.object(i);return o.add(this.marginPoolContract.supplyToMarginPool(t,n,r,e)),await this.#t(o),!0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(t,r){try{let e=this.#e(),i=new Transaction;i.setSender(this.address);let o=i.object(e),s=this.marginPoolContract.withdrawFromMarginPool(t,o,r)(i);return i.transferObjects([s],this.address),await this.#t(i),!0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(t,r){try{let e=new Transaction;return e.setSender(this.address),e.add(this.marginPoolContract.withdrawReferralFees(t,r)),await this.#t(e,{includeBalanceChanges:!0}),!0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(t){try{let r=this.#e(),e=new Transaction;e.setSender(this.address),e.add(this.marginPoolContract.userSupplyAmount(t,r));let i=await e.build({client:this.suiClient}),o=await this.suiClient.core.simulateTransaction({transaction:i,include:{commandResults:!0}}),n=this.dbConfig.getCoin(t),s=0,p=o.commandResults;if(p&&p[0]){let m=p[0].returnValues?.[0];if(m?.bcs){let f=Buffer.from(m.bcs).readBigUInt64LE();s=Number(f)/n.scalar;}}let l=await this.suiClient.core.getBalance({owner:this.address,coinType:n.type}),c=Number(l.balance)/n.scalar;return {userSupplyAmount:s,walletBalance:c}}catch(r){throw new Error(`Failed to get balance: ${r.message||r}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var S=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],P=["userSupplyShares","userSupplyAmount"],I={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 d=(a,t)=>a*t/BigInt(FLOAT_SCALAR),u=a=>Number(a)/FLOAT_SCALAR;var Y=new Set(P),D=a=>Y.has(a),_=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:r="",suiClient:e,dbConfig:i}={}){let o=t??i?.network??"mainnet";if(this.dbConfig=i??new DeepBookConfig({network:o,address:r}),this.suiClient=e??new SuiGrpcClient({baseUrl:h(o),network:o}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),t!==void 0&&i!==void 0&&t!==i.network)throw new Error(`Mismatch between provided network (${t}) and dbConfig network (${i.network}).`)}get network(){return this.dbConfig.network}#r(t,r,e,i){if(D(r)){let o=this.marginPoolContract[r];if(i==null)throw new Error(`supplierCap is required for '${r}'.`);t.add(o(e,i));}else {let o=this.marginPoolContract[r];t.add(o(e));}}parseInspectResultToBcsStructs(t,r){let e=t.commandResults;if(!e)throw new Error("No results found in simulateTransaction output.");return r.reduce((i,o,n)=>{let s=e[n]?.returnValues?.[0]?.bcs;if(!s)return i;let p=bcs[I[o]];return i[o]=p.parse(new Uint8Array(s)),i},{})}formatResult(t,r){let e=this.dbConfig.getCoin(r),i={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:e.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...e};if(!e)return i;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[n,s]of Object.entries(t))n==="lastUpdateTimestamp"?i[n]=Number(s):o.has(n)?i[n]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():i[n]=new BigNumber(s).dividedBy(e.scalar).toNumber();return i}computeBorrowAprAtUtil(t,r){let e=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),n=BigInt(r.optimal_utilization);return t<n?e+d(t,i):e+d(n,i)+d(t-n,o)}calculateKinksAndRate(t,r,e,i){let o=BigInt(t.optimal_utilization),n=BigInt(r.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),p=this.computeBorrowAprAtUtil(n,t),l=BigInt(BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=d(d(i,l),BigInt(FLOAT_SCALAR)-BigInt(r.protocol_spread));return {raw:{baseBorrowApr:t.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:p,supplyApr:c,utilizationRate:l},normalized:{baseBorrowApr:u(BigInt(t.base_rate)),highKink:u(o),borrowAprOnHighKink:u(s),maxBorrowApr:u(p),supplyApr:u(c),utilizationRate:u(l)}}}async#t(t,r){let{address:e}=this.dbConfig.getMarginPool(t),o=(await this.suiClient.core.getObject({objectId:e,include:{json:true}})).object?.json?.fields,n=o.config.fields,s=n.interest_config.fields,p=n.margin_pool_config.fields,l=o.state.fields,{normalized:c}=this.calculateKinksAndRate(s,p,l,r);return c}async getPoolParameters(t,r,e=new Transaction,i=true){if(S.forEach(y=>this.#r(e,y,t)),r&&P.forEach(y=>this.#r(e,y,t,r)),!i)return e;let o=[...S,...P],n=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(n);let s=await e.build({client:this.suiClient}),p=await this.suiClient.core.simulateTransaction({transaction:s,include:{commandResults:true}}),l=this.parseInspectResultToBcsStructs(p,o),c=this.formatResult(l,t),m=BigInt(l.interestRate??0),f=await this.#t(t,m);return {...c,...f}}};
|
|
2
|
-
export{
|
|
1
|
+
import {SuiGrpcClient}from'@mysten/sui/grpc';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';import {parseStructTag}from'@mysten/sui/utils';var J=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),X=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),O=a=>{if(J(a))return fromHex(a);if(X(a))return fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},M=a=>{if(a.length===LEGACY_PRIVATE_KEY_SIZE)return a.slice(0,PRIVATE_KEY_SIZE);if(a.length===PRIVATE_KEY_SIZE+1&&a[0]===0)return a.slice(1);if(a.length===PRIVATE_KEY_SIZE)return a;throw new Error("invalid secret key")};function P(a){switch(a){case "mainnet":return "https://fullnode.mainnet.sui.io:443";case "testnet":return "https://fullnode.testnet.sui.io:443";default:throw new Error(`Unknown network: ${a}`)}}var I=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:t,fullnodeUrl:r,supplierCapId:e,privateKey:n,supplierCapPackageId:o,dbConfig:i}){let s=r??P(t);this.suiClient=new SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#e(n),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e,this.dbConfig=i??new DeepBookConfig({network:t,address:this.address}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#e(t){if(t.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:r}=decodeSuiPrivateKey(t);return Ed25519Keypair.fromSecretKey(M(r))}return Ed25519Keypair.fromSecretKey(M(O(t)))}async#t(t,r={}){let e=await t.build({client:this.suiClient}),{signature:n}=await this.keypair.signTransaction(e),o=await this.suiClient.core.executeTransaction({transaction:e,signatures:[n],include:{effects:true,objectTypes:r.includeObjectTypes,balanceChanges:r.includeBalanceChanges}}),i=o.$kind==="Transaction"?o.Transaction:o.FailedTransaction;if(!i?.effects.status.success)throw new Error(`Transaction failed: ${i?.effects.status.error||"Unknown error"}`);return i}#n(t,r){if(!t.effects.changedObjects)return null;for(let e of t.effects.changedObjects){let n=t.objectTypes?.[e.objectId];if(!e.inputDigest&&n?.includes(r))return e.objectId}return null}#r(){if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");return this.supplierCapId}async#o(){let t=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.core.listOwnedObjects({owner:this.address,type:t})).objects?.[0]?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let t=await this.#o();if(t)return this.supplierCapId=t,t;let r=await this.createSupplierCap();if(!r)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=r,r}async createSupplierCap(){try{let t=new Transaction;t.setSender(this.address);let r=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([r],t.pure.address(this.address));let e=await this.#t(t,{includeObjectTypes:!0});return this.#n(e,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let r=new Transaction;r.setSender(this.address);let e=this.dbConfig.getMarginPool(t);if(!e)throw new Error(`Margin pool configuration not found for coin: ${t}`);r.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[r.object(e.address),r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()],typeArguments:[e.type]});let n=await this.#t(r,{includeObjectTypes:!0});return this.#n(n,"SupplyReferral")}catch(r){throw new Error(`Failed to create Supply Referral: ${r.message||r}`)}}async supplyToMarginPool(t,r,e){try{let n=this.#r(),o=new Transaction;o.setSender(this.address);let i=o.object(n);return o.add(this.marginPoolContract.supplyToMarginPool(t,i,r,e)),await this.#t(o),!0}catch(n){throw new Error(`Failed to supply to margin pool: ${n.message||n}`)}}async withdrawFromMarginPool(t,r){try{let e=this.#r(),n=new Transaction;n.setSender(this.address);let o=n.object(e),s=this.marginPoolContract.withdrawFromMarginPool(t,o,r)(n);return n.transferObjects([s],this.address),await this.#t(n),!0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(t,r){try{let e=new Transaction;return e.setSender(this.address),e.add(this.marginPoolContract.withdrawReferralFees(t,r)),await this.#t(e,{includeBalanceChanges:!0}),!0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(t){try{let r=this.#r(),e=new Transaction;e.setSender(this.address),e.add(this.marginPoolContract.userSupplyAmount(t,r));let n=await e.build({client:this.suiClient}),o=await this.suiClient.core.simulateTransaction({transaction:n,include:{commandResults:!0}}),i=this.dbConfig.getCoin(t),s=0,l=o.commandResults;if(l&&l[0]){let g=l[0].returnValues?.[0];if(g?.bcs){let C=Buffer.from(g.bcs).readBigUInt64LE();s=Number(C)/i.scalar;}}let p=await this.suiClient.core.getBalance({owner:this.address,coinType:i.type}),u=Number(p.balance)/i.scalar;return {userSupplyAmount:s,walletBalance:u}}catch(r){throw new Error(`Failed to get balance: ${r.message||r}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var b=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],h=["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 y=(a,t)=>a*t/BigInt(FLOAT_SCALAR),m=a=>Number(a)/FLOAT_SCALAR;var st=new Set(h),lt=a=>st.has(a),K=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:r="",suiClient:e,dbConfig:n}={}){let o=t??n?.network??"mainnet";if(this.dbConfig=n??new DeepBookConfig({network:o,address:r}),this.suiClient=e??new SuiGrpcClient({baseUrl:P(o),network:o}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),t!==void 0&&n!==void 0&&t!==n.network)throw new Error(`Mismatch between provided network (${t}) and dbConfig network (${n.network}).`)}get network(){return this.dbConfig.network}#e(t,r,e,n){if(lt(r)){let o=this.marginPoolContract[r];if(n==null)throw new Error(`supplierCap is required for '${r}'.`);t.add(o(e,n));}else {let o=this.marginPoolContract[r];t.add(o(e));}}parseInspectResultToBcsStructs(t,r){let e=t.commandResults;if(!e)throw new Error("No results found in simulateTransaction output.");return r.reduce((n,o,i)=>{let s=e[i]?.returnValues?.[0]?.bcs;if(!s)return n;let l=bcs[T[o]];return n[o]=l.parse(new Uint8Array(s)),n},{})}formatResult(t,r){let e=this.dbConfig.getCoin(r),n={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:e.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...e};if(!e)return n;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,s]of Object.entries(t))i==="lastUpdateTimestamp"?n[i]=Number(s):o.has(i)?n[i]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():n[i]=new BigNumber(s).dividedBy(e.scalar).toNumber();return n}computeBorrowAprAtUtil(t,r){let e=BigInt(r.base_rate),n=BigInt(r.base_slope),o=BigInt(r.excess_slope),i=BigInt(r.optimal_utilization);return t<i?e+y(t,n):e+y(i,n)+y(t-i,o)}calculateKinksAndRate(t,r,e,n){let o=BigInt(t.optimal_utilization),i=BigInt(r.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),l=this.computeBorrowAprAtUtil(i,t),p=BigNumber(e.total_supply).isZero()?0n:BigInt(BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),u=y(y(n,p),BigInt(FLOAT_SCALAR)-BigInt(r.protocol_spread));return {raw:{baseBorrowApr:t.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:l,supplyApr:u,utilizationRate:p},normalized:{baseBorrowApr:m(BigInt(t.base_rate)),highKink:m(o),borrowAprOnHighKink:m(s),maxBorrowApr:m(l),supplyApr:m(u),utilizationRate:m(p)}}}async#t(t,r,e){let n=e;if(!n){let{address:u}=this.dbConfig.getMarginPool(t);n=(await this.suiClient.core.getObject({objectId:u,include:{json:true}})).object?.json;}let o=n?.config,i=o?.interest_config,s=o?.margin_pool_config,l=n?.state,{normalized:p}=this.calculateKinksAndRate(i,s,l,r);return p}async getPoolParameters(t,r,e=new Transaction,n=true){if(b.forEach(w=>this.#e(e,w,t)),r&&h.forEach(w=>this.#e(e,w,t,r)),!n)return e;let o=[...b,...h],i=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(i),e.setGasBudget(50000000000n),e.setGasPayment([]);let s=await e.build({client:this.suiClient}),l=await this.suiClient.core.simulateTransaction({transaction:s,include:{commandResults:true}}),p=this.parseInspectResultToBcsStructs(l,o),u=this.formatResult(p,t),g=BigInt(p.interestRate??0),C=await this.#t(t,g);return {...u,...C}}async getPoolsParameters(t,r,e=new Transaction,n=true){for(let c of t)b.forEach(d=>this.#e(e,d,c)),r&&h.forEach(d=>this.#e(e,d,c,r));if(!n)return e;let o=[...b,...r?h:[]],i=o.length,s=t.map(c=>this.dbConfig.getMarginPool(c).address),l=[];for(let c=0;c<s.length;c+=50)l.push(s.slice(c,c+50));let p=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";e.setSender(p),e.setGasBudget(50000000000n),e.setGasPayment([]);let u=await e.build({client:this.suiClient}),[g,...C]=await Promise.all([this.suiClient.core.simulateTransaction({transaction:u,include:{commandResults:true}}),...l.map(c=>this.suiClient.core.getObjects({objectIds:c,include:{json:true}}))]),w=C.flatMap(c=>c.objects),B=g.commandResults;if(!B)throw new Error("No results found in simulateTransaction output.");return Promise.all(t.map(async(c,d)=>{let z=d*i,E=o.reduce((A,x,Y)=>{let j=B[z+Y]?.returnValues?.[0]?.bcs;if(!j)return A;let H=bcs[T[x]];return A[x]=H.parse(new Uint8Array(j)),A},{}),L=this.formatResult(E,c),W=BigInt(E.interestRate??0),_=w[d];if(!_||"code"in _)throw new Error(`Failed to fetch interest config for ${c}`);let $=_.json,D=await this.#t(c,W,$);return {...L,...D}}))}};var ut="0x7f7351ef7e5089dfddf17f55abe028d719c45ca91d2c23e45a441ba65897f804",F=50,gt=async({suiClient:a=new SuiGrpcClient({baseUrl:P("mainnet"),network:"mainnet"}),tableId:t=ut}={})=>{let r=[],e=[],n=null,o=true;for(;o;){let{dynamicFields:i,cursor:s,hasNextPage:l}=await a.listDynamicFields({parentId:t,cursor:n,limit:50});if(e.push(...i.map(p=>p.fieldId)),i.length===0)break;n=s,o=l;}for(let i=0;i<e.length;i+=F){let s=e.slice(i,i+F),{objects:l}=await a.getObjects({objectIds:s,include:{json:true}});for(let p of l){if(p instanceof Error)continue;let u=p.json;!u?.value||!u.name?.name||r.push({address:u.value,type:`0x${u.name.name}`});}}return r.reduce((i,s)=>{let{name:l}=parseStructTag(s.type);return i[l.replace(/_/g,"")]=s,i},{})};
|
|
2
|
+
export{K as DeepBookMarginPool,I as DeepBookMarginToolkit,gt as getOnChainMarginPools};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@scallop-io/scallop-deepbook-kit",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "A toolkit for integrating Scallop with DeepBook functionality",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"scallop",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@mysten/bcs": "^2.0.0",
|
|
58
|
-
"@mysten/deepbook-v3": "^1.
|
|
58
|
+
"@mysten/deepbook-v3": "^1.1.5",
|
|
59
59
|
"@mysten/sui": "^2.0.0",
|
|
60
60
|
"bignumber.js": "^9.3.1",
|
|
61
61
|
"dotenv": "^17.2.3"
|
|
@@ -18,6 +18,14 @@ const MARGIN_POOLS = {
|
|
|
18
18
|
address: '0x38decd3dbb62bd4723144349bf57bc403b393aee86a51596846a824a1e0c2c01',
|
|
19
19
|
type: '0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL',
|
|
20
20
|
},
|
|
21
|
+
XBTC: {
|
|
22
|
+
address: '0x14dfbf54400e0b97e892349310d392bef6d187c2b6709d9b246b8f41c9a13de4',
|
|
23
|
+
type: '0x876a4b7bce8aeaef60464c11f4026903e9afacab79b9b142686158aa86560b50::xbtc::XBTC',
|
|
24
|
+
},
|
|
25
|
+
SUIUSDE: {
|
|
26
|
+
address: '0xbb990ca04a7743e6c0a25a7fb16f60fc6f6d8bf213624ff03a63f1bb04c3a12f',
|
|
27
|
+
type: '0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE',
|
|
28
|
+
},
|
|
21
29
|
};
|
|
22
30
|
|
|
23
31
|
const main = async () => {
|
|
@@ -31,9 +39,10 @@ const main = async () => {
|
|
|
31
39
|
marginPools: MARGIN_POOLS,
|
|
32
40
|
}),
|
|
33
41
|
});
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
console.log(
|
|
42
|
+
const coinKeys = ['USDC', 'XBTC'];
|
|
43
|
+
const suiMarginPoolsParams = await dbMarginPool.getPoolsParameters(coinKeys);
|
|
44
|
+
console.log('Margin Pools Parameters:');
|
|
45
|
+
console.log(JSON.stringify(suiMarginPoolsParams, null, 2));
|
|
37
46
|
} catch (error) {
|
|
38
47
|
console.error('An error occurred:', error);
|
|
39
48
|
} finally {
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { MarginPool } from '@mysten/deepbook-v3';
|
|
2
|
+
import { SuiGrpcClient } from '@mysten/sui/grpc';
|
|
3
|
+
import { parseStructTag } from '@mysten/sui/utils';
|
|
4
|
+
import { getGrpcFullnodeUrl } from '../utils/network.js';
|
|
5
|
+
|
|
6
|
+
const MARGIN_POOLS_TABLE_ID = '0x7f7351ef7e5089dfddf17f55abe028d719c45ca91d2c23e45a441ba65897f804';
|
|
7
|
+
const OBJECT_BATCH_SIZE = 50;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Fetch all margin pool addresses and their types from the on-chain dynamic fields table.
|
|
11
|
+
* @returns Record<coinKey, { address: string; type: string }>
|
|
12
|
+
*/
|
|
13
|
+
export const getOnChainMarginPools = async ({
|
|
14
|
+
suiClient = new SuiGrpcClient({
|
|
15
|
+
baseUrl: getGrpcFullnodeUrl('mainnet'),
|
|
16
|
+
network: 'mainnet',
|
|
17
|
+
}),
|
|
18
|
+
tableId = MARGIN_POOLS_TABLE_ID,
|
|
19
|
+
}: {
|
|
20
|
+
suiClient?: Pick<SuiGrpcClient, 'listDynamicFields' | 'getObjects'>;
|
|
21
|
+
tableId?: string;
|
|
22
|
+
} = {}) => {
|
|
23
|
+
const marginPools: Array<{ address: string; type: string }> = [];
|
|
24
|
+
const ids: string[] = [];
|
|
25
|
+
|
|
26
|
+
let cursor: string | null = null;
|
|
27
|
+
let nextPage = true;
|
|
28
|
+
|
|
29
|
+
while (nextPage) {
|
|
30
|
+
const {
|
|
31
|
+
dynamicFields,
|
|
32
|
+
cursor: nextCursor,
|
|
33
|
+
hasNextPage,
|
|
34
|
+
} = await suiClient.listDynamicFields({
|
|
35
|
+
parentId: tableId,
|
|
36
|
+
cursor,
|
|
37
|
+
limit: 50,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
ids.push(...dynamicFields.map((item) => item.fieldId));
|
|
41
|
+
|
|
42
|
+
if (dynamicFields.length === 0) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
cursor = nextCursor;
|
|
47
|
+
nextPage = hasNextPage;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < ids.length; i += OBJECT_BATCH_SIZE) {
|
|
51
|
+
const objectIds = ids.slice(i, i + OBJECT_BATCH_SIZE);
|
|
52
|
+
const { objects } = await suiClient.getObjects({
|
|
53
|
+
objectIds,
|
|
54
|
+
include: { json: true },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
for (const obj of objects) {
|
|
58
|
+
if (obj instanceof Error) continue;
|
|
59
|
+
|
|
60
|
+
const json = obj.json as
|
|
61
|
+
| {
|
|
62
|
+
value?: string;
|
|
63
|
+
name?: {
|
|
64
|
+
name?: string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
| undefined;
|
|
68
|
+
|
|
69
|
+
if (!json?.value || !json.name?.name) continue;
|
|
70
|
+
|
|
71
|
+
marginPools.push({
|
|
72
|
+
address: json.value,
|
|
73
|
+
type: `0x${json.name.name}`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return marginPools.reduce(
|
|
79
|
+
(acc, pool) => {
|
|
80
|
+
const { name } = parseStructTag(pool.type);
|
|
81
|
+
acc[name.replace(/_/g, '')] = pool;
|
|
82
|
+
return acc;
|
|
83
|
+
},
|
|
84
|
+
{} as Record<string, MarginPool>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getOnChainMarginPools } from './getOnChainMarginPools.js';
|
|
@@ -275,13 +275,15 @@ export class DeepBookMarginPool {
|
|
|
275
275
|
const borrowAprOnHighKink = this.computeBorrowAprAtUtil(highKink, interestConfig);
|
|
276
276
|
const maxBorrowApr = this.computeBorrowAprAtUtil(maxKink, interestConfig);
|
|
277
277
|
|
|
278
|
-
const utilizationRate =
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
278
|
+
const utilizationRate = BigNumber(state.total_supply).isZero()
|
|
279
|
+
? 0n
|
|
280
|
+
: BigInt(
|
|
281
|
+
BigNumber(state.total_borrow)
|
|
282
|
+
.dividedBy(state.total_supply)
|
|
283
|
+
.shiftedBy(9)
|
|
284
|
+
.decimalPlaces(0)
|
|
285
|
+
.toString()
|
|
286
|
+
);
|
|
285
287
|
|
|
286
288
|
const supplyApr = mul(
|
|
287
289
|
mul(borrowAprScaled, utilizationRate),
|
|
@@ -313,20 +315,23 @@ export class DeepBookMarginPool {
|
|
|
313
315
|
* @param coinKey - Asset key.
|
|
314
316
|
* @param borrowAprScaled - interestRate in FLOAT_SCALAR scale (bigint).
|
|
315
317
|
*/
|
|
316
|
-
async #getInterestConfig(coinKey: string, borrowAprScaled: bigint) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const
|
|
318
|
+
async #getInterestConfig(coinKey: string, borrowAprScaled: bigint, prefetchedJson?: any) {
|
|
319
|
+
let json = prefetchedJson;
|
|
320
|
+
if (!json) {
|
|
321
|
+
const { address } = this.dbConfig.getMarginPool(coinKey);
|
|
322
|
+
const response = await this.suiClient.core.getObject({
|
|
323
|
+
objectId: address,
|
|
324
|
+
include: {
|
|
325
|
+
json: true,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
// gRPC returns Move object fields directly in json (no .fields wrapper)
|
|
329
|
+
json = response.object?.json as any;
|
|
330
|
+
}
|
|
331
|
+
const config = json?.config;
|
|
332
|
+
const interestConfig = config?.interest_config as RawInterestConfig;
|
|
333
|
+
const marginPoolConfig = config?.margin_pool_config as RawMarginPoolConfig;
|
|
334
|
+
const statePoolConfig = json?.state as RawStatePoolConfig;
|
|
330
335
|
|
|
331
336
|
const { normalized } = this.calculateKinksAndRate(
|
|
332
337
|
interestConfig,
|
|
@@ -372,7 +377,6 @@ export class DeepBookMarginPool {
|
|
|
372
377
|
inspect: false
|
|
373
378
|
): Promise<Transaction>;
|
|
374
379
|
|
|
375
|
-
// 👇 implementation signature (must be last)
|
|
376
380
|
async getPoolParameters(
|
|
377
381
|
coinKey: string,
|
|
378
382
|
supplierCapId?: string,
|
|
@@ -402,6 +406,8 @@ export class DeepBookMarginPool {
|
|
|
402
406
|
const sender =
|
|
403
407
|
this.dbConfig.address || '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
404
408
|
tx.setSender(sender);
|
|
409
|
+
tx.setGasBudget(50_000_000_000n);
|
|
410
|
+
tx.setGasPayment([]);
|
|
405
411
|
const txBytes = await tx.build({ client: this.suiClient });
|
|
406
412
|
const inspectResult = await this.suiClient.core.simulateTransaction({
|
|
407
413
|
transaction: txBytes,
|
|
@@ -420,4 +426,130 @@ export class DeepBookMarginPool {
|
|
|
420
426
|
...interestData,
|
|
421
427
|
};
|
|
422
428
|
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Batch version of getPoolParameters for reduced RPC calls.
|
|
432
|
+
* Adds all param calls to a single transaction and performs one simulateTransaction.
|
|
433
|
+
*
|
|
434
|
+
* @param coinKeys - Asset keys.
|
|
435
|
+
* @param supplierCapId - Supplier cap object ID.
|
|
436
|
+
* @param tx - Optional transaction to append to.
|
|
437
|
+
* @param inspect - Whether to perform simulateTransaction and return decoded results.
|
|
438
|
+
*
|
|
439
|
+
* @returns Parsed parameters or transaction object ready for execution.
|
|
440
|
+
*/
|
|
441
|
+
async getPoolsParameters(
|
|
442
|
+
coinKeys: string[],
|
|
443
|
+
supplierCapId?: string,
|
|
444
|
+
tx?: Transaction
|
|
445
|
+
): Promise<MarginPoolParams[]>;
|
|
446
|
+
|
|
447
|
+
async getPoolsParameters(
|
|
448
|
+
coinKeys: string[],
|
|
449
|
+
supplierCapId: string | undefined,
|
|
450
|
+
tx: Transaction,
|
|
451
|
+
inspect: true
|
|
452
|
+
): Promise<MarginPoolParams[]>;
|
|
453
|
+
|
|
454
|
+
async getPoolsParameters(
|
|
455
|
+
coinKeys: string[],
|
|
456
|
+
supplierCapId: string | undefined,
|
|
457
|
+
tx: Transaction,
|
|
458
|
+
inspect: false
|
|
459
|
+
): Promise<Transaction>;
|
|
460
|
+
|
|
461
|
+
async getPoolsParameters(
|
|
462
|
+
coinKeys: string[],
|
|
463
|
+
supplierCapId?: string,
|
|
464
|
+
tx: Transaction = new Transaction(),
|
|
465
|
+
inspect: boolean = true
|
|
466
|
+
): Promise<MarginPoolParams[] | Transaction> {
|
|
467
|
+
// Add all param calls for every coin into a single transaction
|
|
468
|
+
for (const coinKey of coinKeys) {
|
|
469
|
+
MARGIN_POOL_PARAM_KEYS.forEach((paramKey) => this.#addParamCall(tx, paramKey, coinKey));
|
|
470
|
+
|
|
471
|
+
if (supplierCapId) {
|
|
472
|
+
MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS.forEach((paramKey) =>
|
|
473
|
+
this.#addParamCall(tx, paramKey, coinKey, supplierCapId)
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (!inspect) return tx;
|
|
479
|
+
|
|
480
|
+
// Determine how many result slots each coin occupies
|
|
481
|
+
const keysPerCoin: (MarginPoolParamKey | MarginPoolWithSupplierCapParamKey)[] = [
|
|
482
|
+
...MARGIN_POOL_PARAM_KEYS,
|
|
483
|
+
...(supplierCapId ? MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS : []),
|
|
484
|
+
];
|
|
485
|
+
const slotCount = keysPerCoin.length;
|
|
486
|
+
|
|
487
|
+
// Single simulateTransaction + batched getObjects
|
|
488
|
+
const objectIds = coinKeys.map((coinKey) => this.dbConfig.getMarginPool(coinKey).address);
|
|
489
|
+
const objectIdChunks: string[][] = [];
|
|
490
|
+
for (let i = 0; i < objectIds.length; i += 50) {
|
|
491
|
+
objectIdChunks.push(objectIds.slice(i, i + 50));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const sender =
|
|
495
|
+
this.dbConfig.address || '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
496
|
+
tx.setSender(sender);
|
|
497
|
+
tx.setGasBudget(50_000_000_000n);
|
|
498
|
+
tx.setGasPayment([]);
|
|
499
|
+
const txBytes = await tx.build({ client: this.suiClient });
|
|
500
|
+
|
|
501
|
+
const [inspectResult, ...objectResponses] = await Promise.all([
|
|
502
|
+
this.suiClient.core.simulateTransaction({
|
|
503
|
+
transaction: txBytes,
|
|
504
|
+
include: {
|
|
505
|
+
commandResults: true,
|
|
506
|
+
},
|
|
507
|
+
}),
|
|
508
|
+
...objectIdChunks.map((chunk) =>
|
|
509
|
+
this.suiClient.core.getObjects({
|
|
510
|
+
objectIds: chunk,
|
|
511
|
+
include: { json: true },
|
|
512
|
+
})
|
|
513
|
+
),
|
|
514
|
+
]);
|
|
515
|
+
const objects = objectResponses.flatMap((response) => response.objects);
|
|
516
|
+
|
|
517
|
+
const allResults = inspectResult.commandResults;
|
|
518
|
+
if (!allResults) throw new Error('No results found in simulateTransaction output.');
|
|
519
|
+
|
|
520
|
+
// Split results per coin and format
|
|
521
|
+
return Promise.all(
|
|
522
|
+
coinKeys.map(async (coinKey, coinIdx) => {
|
|
523
|
+
const offset = coinIdx * slotCount;
|
|
524
|
+
|
|
525
|
+
// Parse the slice of results belonging to this coin
|
|
526
|
+
const parsed = keysPerCoin.reduce(
|
|
527
|
+
(acc, key, keyIdx) => {
|
|
528
|
+
const bytes = allResults[offset + keyIdx]?.returnValues?.[0]?.bcs;
|
|
529
|
+
if (!bytes) return acc;
|
|
530
|
+
const bcsType = bcs[MARGIN_POOL_PARAM_KEY_STRUCT_MAP[key]];
|
|
531
|
+
acc[key] = bcsType.parse(new Uint8Array(bytes));
|
|
532
|
+
return acc;
|
|
533
|
+
},
|
|
534
|
+
{} as Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, string>
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
const formattedResult = this.formatResult(parsed, coinKey);
|
|
538
|
+
const borrowAprScaled = BigInt(parsed.interestRate ?? 0);
|
|
539
|
+
|
|
540
|
+
const objectResult = objects[coinIdx];
|
|
541
|
+
if (!objectResult || 'code' in objectResult) {
|
|
542
|
+
throw new Error(`Failed to fetch interest config for ${coinKey}`);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const json = (objectResult as any).json;
|
|
546
|
+
const interestData = await this.#getInterestConfig(coinKey, borrowAprScaled, json);
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
...formattedResult,
|
|
550
|
+
...interestData,
|
|
551
|
+
};
|
|
552
|
+
})
|
|
553
|
+
);
|
|
554
|
+
}
|
|
423
555
|
}
|