@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 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=_;exports.DeepBookMarginToolkit=b;
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
- export { DeepBookMarginPool, DeepBookMarginToolkit, type MarginBalance, type MarginCoinType, type MarginPoolParams, type NetworkType, type ReferralInfo, type ToolkitConfig, type TransactionResult };
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
- export { DeepBookMarginPool, DeepBookMarginToolkit, type MarginBalance, type MarginCoinType, type MarginPoolParams, type NetworkType, type ReferralInfo, type ToolkitConfig, type TransactionResult };
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{_ as DeepBookMarginPool,b as DeepBookMarginToolkit};
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.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.0.9",
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 coinKey = 'USDC';
35
- const suiMarginPoolParams = await dbMarginPool.getPoolParameters(coinKey);
36
- console.log(`${coinKey} Margin Pool Parameters:`, suiMarginPoolParams);
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
@@ -1 +1,2 @@
1
1
  export * from './toolkit/index.js';
2
+ export * from './queries/index.js';
@@ -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 = BigInt(
279
- BigNumber(state.total_borrow)
280
- .dividedBy(state.total_supply)
281
- .shiftedBy(9)
282
- .decimalPlaces(0)
283
- .toString()
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
- const { address } = this.dbConfig.getMarginPool(coinKey);
318
- const response = await this.suiClient.core.getObject({
319
- objectId: address,
320
- include: {
321
- json: true,
322
- },
323
- });
324
-
325
- const fields = (response.object?.json as any)?.fields;
326
- const config = fields.config.fields;
327
- const interestConfig = config.interest_config.fields as RawInterestConfig;
328
- const marginPoolConfig = config.margin_pool_config.fields as RawMarginPoolConfig;
329
- const statePoolConfig = fields.state.fields as RawStatePoolConfig;
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
  }