@scallop-io/scallop-deepbook-kit 0.2.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1,2 @@
1
- 'use strict';var client=require('@mysten/sui/client'),ed25519=require('@mysten/sui/keypairs/ed25519'),transactions=require('@mysten/sui/transactions'),deepbookV3=require('@mysten/deepbook-v3'),cryptography=require('@mysten/sui/cryptography'),bcs$1=require('@mysten/bcs'),bcs=require('@mysten/sui/bcs'),bignumber_js=require('bignumber.js');var T=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),B=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),A=n=>{if(T(n))return bcs$1.fromHex(n);if(B(n))return bcs$1.fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},P=n=>{if(n.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,cryptography.PRIVATE_KEY_SIZE);if(n.length===cryptography.PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===cryptography.PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var C=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:r,fullnodeUrl:i,supplierCapId:t,privateKey:e,supplierCapPackageId:o,dbConfig:a}){let s=i??client.getFullnodeUrl(r);this.suiClient=new client.SuiClient({url:s}),this.keypair=this.#r(e),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=t,this.dbConfig=a??new deepbookV3.DeepBookConfig({network:r,address:this.address}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#r(r){if(r.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:i}=cryptography.decodeSuiPrivateKey(r);return ed25519.Ed25519Keypair.fromSecretKey(P(i))}return ed25519.Ed25519Keypair.fromSecretKey(P(A(r)))}async#t(){let r=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#t();if(r)return this.supplierCapId=r,r;let i=await this.createSupplierCap();if(!i)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=i,i}async createSupplierCap(){try{let r=new transactions.Transaction;r.setSender(this.address);let i=r.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()]});r.transferObjects([i],r.pure.address(this.address));let t=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(t.errors&&t.errors.length>0)throw new Error(`Transaction failed with errors: ${t.errors.map(e=>e.toString()).join(", ")}`);if(t.objectChanges){for(let e of t.objectChanges)if(e.type==="created"&&e.objectType.includes("SupplierCap"))return e.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let i=new transactions.Transaction;i.setSender(this.address);let t=this.dbConfig.getMarginPool(r);if(!t)throw new Error(`Margin pool configuration not found for coin: ${r}`);i.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[i.object(t.address),i.object(this.dbConfig.MARGIN_REGISTRY_ID),i.object.clock()],typeArguments:[t.type]});let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:i,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(o=>o.toString()).join(", ")}`);if(e.objectChanges){for(let o of e.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(i){throw new Error(`Failed to create Supply Referral: ${i.message||i}`)}}async supplyToMarginPool(r,i,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new transactions.Transaction;e.setSender(this.address);let o=e.object(this.supplierCapId);e.add(this.marginPoolContract.supplyToMarginPool(r,o,i,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(s=>s.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to supply to margin pool: ${e.message||e}`)}}async withdrawFromMarginPool(r,i){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction,e=t.object(this.supplierCapId),a=this.marginPoolContract.withdrawFromMarginPool(r,e,i)(t);t.transferObjects([a],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(p=>p.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw from margin pool: ${t.message||t}`)}}async withdrawReferralFees(r,i){try{let t=new transactions.Transaction;t.add(this.marginPoolContract.withdrawReferralFees(r,i));let{errors:e}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(e&&e.length>0)throw new Error(`Transaction failed with errors: ${e.map(o=>o.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw referral fees: ${t.message||t}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let i=new transactions.Transaction;i.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let t=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:i}),e=0;if(t&&t.results&&t.results[0]&&t.results[0].returnValues){let l=t.results[0].returnValues[0];if(l&&l[0]){let c=Buffer.from(l[0]).readBigUInt64LE(),m=this.dbConfig.getCoin(r).scalar;e=Number(c)/m;}}let o=this.dbConfig.getCoin(r).type,a=await this.suiClient.getBalance({owner:this.address,coinType:o}),s=this.dbConfig.getCoin(r).scalar,p=Number(a.totalBalance)/s;return {userSupplyAmount:e,walletBalance:p}}catch(i){throw new Error(`Failed to get balance: ${i.message||i}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var w=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],h=["userSupplyShares","userSupplyAmount"],_={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=(n,r)=>n*r/BigInt(deepbookV3.FLOAT_SCALAR),u=n=>Number(n)/deepbookV3.FLOAT_SCALAR;var W=new Set(h),D=n=>W.has(n),b=class{marginPoolContract;dbConfig;suiClient;constructor({network:r,address:i="",suiClient:t,dbConfig:e}={}){let o=r??e?.network??"mainnet";if(this.dbConfig=e??new deepbookV3.DeepBookConfig({network:o,address:i}),this.suiClient=t??new client.SuiClient({url:client.getFullnodeUrl(o)}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),r!==void 0&&e!==void 0&&r!==e.network)throw new Error(`Mismatch between provided network (${r}) and dbConfig network (${e.network}).`)}get network(){return this.dbConfig.network}#r(r,i,t,e){if(D(i)){let o=this.marginPoolContract[i];if(e==null)throw new Error(`supplierCap is required for '${i}'.`);r.add(o(t,e));}else {let o=this.marginPoolContract[i];r.add(o(t));}}parseInspectResultToBcsStructs(r,i){let t=r.results;if(!t)throw new Error("No results found in DevInspect output.");return i.reduce((e,o,a)=>{let s=t[a]?.returnValues?.[0]?.[0];if(!s)return e;let p=bcs.bcs[_[o]];return e[o]=p.parse(new Uint8Array(s)),e},{})}formatResult(r,i){let t=this.dbConfig.getCoin(i),e={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:t.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...t};if(!t)return e;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[a,s]of Object.entries(r))a==="lastUpdateTimestamp"?e[a]=Number(s):o.has(a)?e[a]=new bignumber_js.BigNumber(s).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():e[a]=new bignumber_js.BigNumber(s).dividedBy(t.scalar).toNumber();return e}computeBorrowAprAtUtil(r,i){let t=BigInt(i.base_rate),e=BigInt(i.base_slope),o=BigInt(i.excess_slope),a=BigInt(i.optimal_utilization);return r<a?t+d(r,e):t+d(a,e)+d(r-a,o)}calculateKinksAndRate(r,i,t,e){let o=BigInt(r.optimal_utilization),a=BigInt(i.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,r),p=this.computeBorrowAprAtUtil(a,r),l=BigInt(bignumber_js.BigNumber(t.total_borrow).dividedBy(t.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=d(d(e,l),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(i.protocol_spread));return {raw:{baseBorrowApr:r.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:p,supplyApr:c,utilizationRate:l},normalized:{baseBorrowApr:u(BigInt(r.base_rate)),highKink:u(o),borrowAprOnHighKink:u(s),maxBorrowApr:u(p),supplyApr:u(c),utilizationRate:u(l)}}}async#t(r,i){let{address:t}=this.dbConfig.getMarginPool(r),o=((await this.suiClient.getObject({id:t,options:{showContent:true}})).data?.content).fields,a=o.config.fields,s=a.interest_config.fields,p=a.margin_pool_config.fields,l=o.state.fields,{normalized:c}=this.calculateKinksAndRate(s,p,l,i);return c}async getPoolParameters(r,i,t=new transactions.Transaction,e=true){if(w.forEach(m=>this.#r(t,m,r)),i&&h.forEach(m=>this.#r(t,m,r,i)),!e)return t;let o=[...w,...h],a=await this.suiClient.devInspectTransactionBlock({transactionBlock:t,sender:this.dbConfig.address}),s=this.parseInspectResultToBcsStructs(a,o),p=this.formatResult(s,r),l=BigInt(s.interestRate??0),c=await this.#t(r,l);return {...p,...c}}};exports.DeepBookMarginPool=b;exports.DeepBookMarginToolkit=C;
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:e,supplierCapId:r,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=e??h(t);this.suiClient=new grpc.SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r,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:e}=cryptography.decodeSuiPrivateKey(t);return ed25519.Ed25519Keypair.fromSecretKey(w(e))}return ed25519.Ed25519Keypair.fromSecretKey(w(R(t)))}async#t(t,e={}){let r=await t.build({client:this.suiClient}),{signature:i}=await this.keypair.signTransaction(r),o=await this.suiClient.core.executeTransaction({transaction:r,signatures:[i],include:{effects:true,objectTypes:e.includeObjectTypes,balanceChanges:e.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,e){if(!t.effects.changedObjects)return null;for(let r of t.effects.changedObjects){let i=t.objectTypes?.[r.objectId];if(!r.inputDigest&&i?.includes(e))return r.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 e=await this.createSupplierCap();if(!e)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=e,e}async createSupplierCap(){try{let t=new transactions.Transaction;t.setSender(this.address);let e=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([e],t.pure.address(this.address));let r=await this.#t(t,{includeObjectTypes:!0});return this.#i(r,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let e=new transactions.Transaction;e.setSender(this.address);let r=this.dbConfig.getMarginPool(t);if(!r)throw new Error(`Margin pool configuration not found for coin: ${t}`);e.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[e.object(r.address),e.object(this.dbConfig.MARGIN_REGISTRY_ID),e.object.clock()],typeArguments:[r.type]});let i=await this.#t(e,{includeObjectTypes:!0});return this.#i(i,"SupplyReferral")}catch(e){throw new Error(`Failed to create Supply Referral: ${e.message||e}`)}}async supplyToMarginPool(t,e,r){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,e,r)),await this.#t(o),!0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(t,e){try{let r=this.#e(),i=new transactions.Transaction;i.setSender(this.address);let o=i.object(r),s=this.marginPoolContract.withdrawFromMarginPool(t,o,e)(i);return i.transferObjects([s],this.address),await this.#t(i),!0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(t,e){try{let r=new transactions.Transaction;return r.setSender(this.address),r.add(this.marginPoolContract.withdrawReferralFees(t,e)),await this.#t(r,{includeBalanceChanges:!0}),!0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(t){try{let e=this.#e(),r=new transactions.Transaction;r.setSender(this.address),r.add(this.marginPoolContract.userSupplyAmount(t,e));let i=await r.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 d=p[0].returnValues?.[0];if(d?.bcs){let f=Buffer.from(d.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(e){throw new Error(`Failed to get balance: ${e.message||e}`)}}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 m=(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),A=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:e="",suiClient:r,dbConfig:i}={}){let o=t??i?.network??"mainnet";if(this.dbConfig=i??new deepbookV3.DeepBookConfig({network:o,address:e}),this.suiClient=r??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,e,r,i){if(D(e)){let o=this.marginPoolContract[e];if(i==null)throw new Error(`supplierCap is required for '${e}'.`);t.add(o(r,i));}else {let o=this.marginPoolContract[e];t.add(o(r));}}parseInspectResultToBcsStructs(t,e){let r=t.commandResults;if(!r)throw new Error("No results found in simulateTransaction output.");return e.reduce((i,o,n)=>{let s=r[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,e){let r=this.dbConfig.getCoin(e),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:r.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)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(r.scalar).toNumber();return i}computeBorrowAprAtUtil(t,e){let r=BigInt(e.base_rate),i=BigInt(e.base_slope),o=BigInt(e.excess_slope),n=BigInt(e.optimal_utilization);return t<n?r+m(t,i):r+m(n,i)+m(t-n,o)}calculateKinksAndRate(t,e,r,i){let o=BigInt(t.optimal_utilization),n=BigInt(e.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),p=this.computeBorrowAprAtUtil(n,t),l=BigInt(bignumber_js.BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=m(m(i,l),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(e.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,e){let{address:r}=this.dbConfig.getMarginPool(t),o=(await this.suiClient.core.getObject({objectId:r,include:{json:true}})).object?.json,n=o?.config,s=n?.interest_config,p=n?.margin_pool_config,l=o?.state,{normalized:c}=this.calculateKinksAndRate(s,p,l,e);return c}async getPoolParameters(t,e,r=new transactions.Transaction,i=true){if(S.forEach(y=>this.#r(r,y,t)),e&&P.forEach(y=>this.#r(r,y,t,e)),!i)return r;let o=[...S,...P],n=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";r.setSender(n),r.setGasBudget(50000000000n),r.setGasPayment([]);let s=await r.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),d=BigInt(l.interestRate??0),f=await this.#t(t,d);return {...c,...f}}};
2
+ exports.DeepBookMarginPool=A;exports.DeepBookMarginToolkit=b;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { DeepBookConfig, MarginPoolContract, Coin } from '@mysten/deepbook-v3';
2
- import { SuiClient } from '@mysten/sui/client';
2
+ import { SuiClientTypes } from '@mysten/sui/client';
3
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
3
4
  import { Transaction } from '@mysten/sui/transactions';
4
5
 
5
6
  type NetworkType = 'testnet' | 'mainnet';
@@ -66,7 +67,7 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
66
67
  };
67
68
  type DeepBookMarginPoolParams = {
68
69
  address?: string;
69
- suiClient?: SuiClient;
70
+ suiClient?: SuiGrpcClient;
70
71
  network?: NetworkType;
71
72
  dbConfig?: DeepBookConfig;
72
73
  };
@@ -74,7 +75,7 @@ declare class DeepBookMarginPool {
74
75
  #private;
75
76
  marginPoolContract: MarginPoolContract;
76
77
  dbConfig: DeepBookConfig;
77
- suiClient: SuiClient;
78
+ suiClient: SuiGrpcClient;
78
79
  constructor({ network, address, suiClient, dbConfig }?: DeepBookMarginPoolParams);
79
80
  get network(): SuiClientTypes.Network;
80
81
  private parseInspectResultToBcsStructs;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { DeepBookConfig, MarginPoolContract, Coin } from '@mysten/deepbook-v3';
2
- import { SuiClient } from '@mysten/sui/client';
2
+ import { SuiClientTypes } from '@mysten/sui/client';
3
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
3
4
  import { Transaction } from '@mysten/sui/transactions';
4
5
 
5
6
  type NetworkType = 'testnet' | 'mainnet';
@@ -66,7 +67,7 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
66
67
  };
67
68
  type DeepBookMarginPoolParams = {
68
69
  address?: string;
69
- suiClient?: SuiClient;
70
+ suiClient?: SuiGrpcClient;
70
71
  network?: NetworkType;
71
72
  dbConfig?: DeepBookConfig;
72
73
  };
@@ -74,7 +75,7 @@ declare class DeepBookMarginPool {
74
75
  #private;
75
76
  marginPoolContract: MarginPoolContract;
76
77
  dbConfig: DeepBookConfig;
77
- suiClient: SuiClient;
78
+ suiClient: SuiGrpcClient;
78
79
  constructor({ network, address, suiClient, dbConfig }?: DeepBookMarginPoolParams);
79
80
  get network(): SuiClientTypes.Network;
80
81
  private parseInspectResultToBcsStructs;
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
- import {getFullnodeUrl,SuiClient}from'@mysten/sui/client';import {Ed25519Keypair}from'@mysten/sui/keypairs/ed25519';import {Transaction}from'@mysten/sui/transactions';import {DeepBookConfig,MarginPoolContract,FLOAT_SCALAR}from'@mysten/deepbook-v3';import {SUI_PRIVATE_KEY_PREFIX,decodeSuiPrivateKey,LEGACY_PRIVATE_KEY_SIZE,PRIVATE_KEY_SIZE}from'@mysten/sui/cryptography';import {fromHex,fromBase64}from'@mysten/bcs';import {bcs}from'@mysten/sui/bcs';import {BigNumber}from'bignumber.js';var T=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),B=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),A=n=>{if(T(n))return fromHex(n);if(B(n))return fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},P=n=>{if(n.length===LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,PRIVATE_KEY_SIZE);if(n.length===PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var C=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:r,fullnodeUrl:i,supplierCapId:t,privateKey:e,supplierCapPackageId:o,dbConfig:a}){let s=i??getFullnodeUrl(r);this.suiClient=new SuiClient({url:s}),this.keypair=this.#r(e),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=t,this.dbConfig=a??new DeepBookConfig({network:r,address:this.address}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),this.supplierCapPackageId=o??this.dbConfig.MARGIN_PACKAGE_ID;}#r(r){if(r.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:i}=decodeSuiPrivateKey(r);return Ed25519Keypair.fromSecretKey(P(i))}return Ed25519Keypair.fromSecretKey(P(A(r)))}async#t(){let r=`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#t();if(r)return this.supplierCapId=r,r;let i=await this.createSupplierCap();if(!i)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=i,i}async createSupplierCap(){try{let r=new Transaction;r.setSender(this.address);let i=r.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()]});r.transferObjects([i],r.pure.address(this.address));let t=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(t.errors&&t.errors.length>0)throw new Error(`Transaction failed with errors: ${t.errors.map(e=>e.toString()).join(", ")}`);if(t.objectChanges){for(let e of t.objectChanges)if(e.type==="created"&&e.objectType.includes("SupplierCap"))return e.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let i=new Transaction;i.setSender(this.address);let t=this.dbConfig.getMarginPool(r);if(!t)throw new Error(`Margin pool configuration not found for coin: ${r}`);i.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[i.object(t.address),i.object(this.dbConfig.MARGIN_REGISTRY_ID),i.object.clock()],typeArguments:[t.type]});let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:i,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(o=>o.toString()).join(", ")}`);if(e.objectChanges){for(let o of e.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(i){throw new Error(`Failed to create Supply Referral: ${i.message||i}`)}}async supplyToMarginPool(r,i,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new Transaction;e.setSender(this.address);let o=e.object(this.supplierCapId);e.add(this.marginPoolContract.supplyToMarginPool(r,o,i,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(s=>s.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to supply to margin pool: ${e.message||e}`)}}async withdrawFromMarginPool(r,i){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction,e=t.object(this.supplierCapId),a=this.marginPoolContract.withdrawFromMarginPool(r,e,i)(t);t.transferObjects([a],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(p=>p.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw from margin pool: ${t.message||t}`)}}async withdrawReferralFees(r,i){try{let t=new Transaction;t.add(this.marginPoolContract.withdrawReferralFees(r,i));let{errors:e}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(e&&e.length>0)throw new Error(`Transaction failed with errors: ${e.map(o=>o.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw referral fees: ${t.message||t}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let i=new Transaction;i.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let t=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:i}),e=0;if(t&&t.results&&t.results[0]&&t.results[0].returnValues){let l=t.results[0].returnValues[0];if(l&&l[0]){let c=Buffer.from(l[0]).readBigUInt64LE(),m=this.dbConfig.getCoin(r).scalar;e=Number(c)/m;}}let o=this.dbConfig.getCoin(r).type,a=await this.suiClient.getBalance({owner:this.address,coinType:o}),s=this.dbConfig.getCoin(r).scalar,p=Number(a.totalBalance)/s;return {userSupplyAmount:e,walletBalance:p}}catch(i){throw new Error(`Failed to get balance: ${i.message||i}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var w=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],h=["userSupplyShares","userSupplyAmount"],_={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=(n,r)=>n*r/BigInt(FLOAT_SCALAR),u=n=>Number(n)/FLOAT_SCALAR;var W=new Set(h),D=n=>W.has(n),b=class{marginPoolContract;dbConfig;suiClient;constructor({network:r,address:i="",suiClient:t,dbConfig:e}={}){let o=r??e?.network??"mainnet";if(this.dbConfig=e??new DeepBookConfig({network:o,address:i}),this.suiClient=t??new SuiClient({url:getFullnodeUrl(o)}),this.marginPoolContract=new MarginPoolContract(this.dbConfig),r!==void 0&&e!==void 0&&r!==e.network)throw new Error(`Mismatch between provided network (${r}) and dbConfig network (${e.network}).`)}get network(){return this.dbConfig.network}#r(r,i,t,e){if(D(i)){let o=this.marginPoolContract[i];if(e==null)throw new Error(`supplierCap is required for '${i}'.`);r.add(o(t,e));}else {let o=this.marginPoolContract[i];r.add(o(t));}}parseInspectResultToBcsStructs(r,i){let t=r.results;if(!t)throw new Error("No results found in DevInspect output.");return i.reduce((e,o,a)=>{let s=t[a]?.returnValues?.[0]?.[0];if(!s)return e;let p=bcs[_[o]];return e[o]=p.parse(new Uint8Array(s)),e},{})}formatResult(r,i){let t=this.dbConfig.getCoin(i),e={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:t.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...t};if(!t)return e;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[a,s]of Object.entries(r))a==="lastUpdateTimestamp"?e[a]=Number(s):o.has(a)?e[a]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():e[a]=new BigNumber(s).dividedBy(t.scalar).toNumber();return e}computeBorrowAprAtUtil(r,i){let t=BigInt(i.base_rate),e=BigInt(i.base_slope),o=BigInt(i.excess_slope),a=BigInt(i.optimal_utilization);return r<a?t+d(r,e):t+d(a,e)+d(r-a,o)}calculateKinksAndRate(r,i,t,e){let o=BigInt(r.optimal_utilization),a=BigInt(i.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,r),p=this.computeBorrowAprAtUtil(a,r),l=BigInt(BigNumber(t.total_borrow).dividedBy(t.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=d(d(e,l),BigInt(FLOAT_SCALAR)-BigInt(i.protocol_spread));return {raw:{baseBorrowApr:r.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:p,supplyApr:c,utilizationRate:l},normalized:{baseBorrowApr:u(BigInt(r.base_rate)),highKink:u(o),borrowAprOnHighKink:u(s),maxBorrowApr:u(p),supplyApr:u(c),utilizationRate:u(l)}}}async#t(r,i){let{address:t}=this.dbConfig.getMarginPool(r),o=((await this.suiClient.getObject({id:t,options:{showContent:true}})).data?.content).fields,a=o.config.fields,s=a.interest_config.fields,p=a.margin_pool_config.fields,l=o.state.fields,{normalized:c}=this.calculateKinksAndRate(s,p,l,i);return c}async getPoolParameters(r,i,t=new Transaction,e=true){if(w.forEach(m=>this.#r(t,m,r)),i&&h.forEach(m=>this.#r(t,m,r,i)),!e)return t;let o=[...w,...h],a=await this.suiClient.devInspectTransactionBlock({transactionBlock:t,sender:this.dbConfig.address}),s=this.parseInspectResultToBcsStructs(a,o),p=this.formatResult(s,r),l=BigInt(s.interestRate??0),c=await this.#t(r,l);return {...p,...c}}};export{b as DeepBookMarginPool,C 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';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:e,supplierCapId:r,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=e??h(t);this.suiClient=new SuiGrpcClient({baseUrl:s,network:t}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r,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:e}=decodeSuiPrivateKey(t);return Ed25519Keypair.fromSecretKey(w(e))}return Ed25519Keypair.fromSecretKey(w(R(t)))}async#t(t,e={}){let r=await t.build({client:this.suiClient}),{signature:i}=await this.keypair.signTransaction(r),o=await this.suiClient.core.executeTransaction({transaction:r,signatures:[i],include:{effects:true,objectTypes:e.includeObjectTypes,balanceChanges:e.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,e){if(!t.effects.changedObjects)return null;for(let r of t.effects.changedObjects){let i=t.objectTypes?.[r.objectId];if(!r.inputDigest&&i?.includes(e))return r.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 e=await this.createSupplierCap();if(!e)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=e,e}async createSupplierCap(){try{let t=new Transaction;t.setSender(this.address);let e=t.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[t.object(this.dbConfig.MARGIN_REGISTRY_ID),t.object.clock()]});t.transferObjects([e],t.pure.address(this.address));let r=await this.#t(t,{includeObjectTypes:!0});return this.#i(r,"SupplierCap")}catch(t){throw new Error(`Failed to create Supplier Cap: ${t.message||t}`)}}async createSupplyReferral(t){try{let e=new Transaction;e.setSender(this.address);let r=this.dbConfig.getMarginPool(t);if(!r)throw new Error(`Margin pool configuration not found for coin: ${t}`);e.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[e.object(r.address),e.object(this.dbConfig.MARGIN_REGISTRY_ID),e.object.clock()],typeArguments:[r.type]});let i=await this.#t(e,{includeObjectTypes:!0});return this.#i(i,"SupplyReferral")}catch(e){throw new Error(`Failed to create Supply Referral: ${e.message||e}`)}}async supplyToMarginPool(t,e,r){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,e,r)),await this.#t(o),!0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(t,e){try{let r=this.#e(),i=new Transaction;i.setSender(this.address);let o=i.object(r),s=this.marginPoolContract.withdrawFromMarginPool(t,o,e)(i);return i.transferObjects([s],this.address),await this.#t(i),!0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(t,e){try{let r=new Transaction;return r.setSender(this.address),r.add(this.marginPoolContract.withdrawReferralFees(t,e)),await this.#t(r,{includeBalanceChanges:!0}),!0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(t){try{let e=this.#e(),r=new Transaction;r.setSender(this.address),r.add(this.marginPoolContract.userSupplyAmount(t,e));let i=await r.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 d=p[0].returnValues?.[0];if(d?.bcs){let f=Buffer.from(d.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(e){throw new Error(`Failed to get balance: ${e.message||e}`)}}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 m=(a,t)=>a*t/BigInt(FLOAT_SCALAR),u=a=>Number(a)/FLOAT_SCALAR;var Y=new Set(P),D=a=>Y.has(a),A=class{marginPoolContract;dbConfig;suiClient;constructor({network:t,address:e="",suiClient:r,dbConfig:i}={}){let o=t??i?.network??"mainnet";if(this.dbConfig=i??new DeepBookConfig({network:o,address:e}),this.suiClient=r??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,e,r,i){if(D(e)){let o=this.marginPoolContract[e];if(i==null)throw new Error(`supplierCap is required for '${e}'.`);t.add(o(r,i));}else {let o=this.marginPoolContract[e];t.add(o(r));}}parseInspectResultToBcsStructs(t,e){let r=t.commandResults;if(!r)throw new Error("No results found in simulateTransaction output.");return e.reduce((i,o,n)=>{let s=r[n]?.returnValues?.[0]?.bcs;if(!s)return i;let p=bcs[I[o]];return i[o]=p.parse(new Uint8Array(s)),i},{})}formatResult(t,e){let r=this.dbConfig.getCoin(e),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:r.scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)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(r.scalar).toNumber();return i}computeBorrowAprAtUtil(t,e){let r=BigInt(e.base_rate),i=BigInt(e.base_slope),o=BigInt(e.excess_slope),n=BigInt(e.optimal_utilization);return t<n?r+m(t,i):r+m(n,i)+m(t-n,o)}calculateKinksAndRate(t,e,r,i){let o=BigInt(t.optimal_utilization),n=BigInt(e.max_utilization_rate),s=this.computeBorrowAprAtUtil(o,t),p=this.computeBorrowAprAtUtil(n,t),l=BigInt(BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),c=m(m(i,l),BigInt(FLOAT_SCALAR)-BigInt(e.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,e){let{address:r}=this.dbConfig.getMarginPool(t),o=(await this.suiClient.core.getObject({objectId:r,include:{json:true}})).object?.json,n=o?.config,s=n?.interest_config,p=n?.margin_pool_config,l=o?.state,{normalized:c}=this.calculateKinksAndRate(s,p,l,e);return c}async getPoolParameters(t,e,r=new Transaction,i=true){if(S.forEach(y=>this.#r(r,y,t)),e&&P.forEach(y=>this.#r(r,y,t,e)),!i)return r;let o=[...S,...P],n=this.dbConfig.address||"0x0000000000000000000000000000000000000000000000000000000000000000";r.setSender(n),r.setGasBudget(50000000000n),r.setGasPayment([]);let s=await r.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),d=BigInt(l.interestRate??0),f=await this.#t(t,d);return {...c,...f}}};
2
+ export{A as DeepBookMarginPool,b as DeepBookMarginToolkit};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@scallop-io/scallop-deepbook-kit",
4
- "version": "0.2.1",
4
+ "version": "1.0.1",
5
5
  "description": "A toolkit for integrating Scallop with DeepBook functionality",
6
6
  "keywords": [
7
7
  "scallop",
@@ -54,9 +54,9 @@
54
54
  "vitest": "^4.0.18"
55
55
  },
56
56
  "dependencies": {
57
- "@mysten/bcs": "^1.9.2",
57
+ "@mysten/bcs": "^2.0.0",
58
58
  "@mysten/deepbook-v3": "^1.0.9",
59
- "@mysten/sui": "^1.45.2",
59
+ "@mysten/sui": "^2.0.0",
60
60
  "bignumber.js": "^9.3.1",
61
61
  "dotenv": "^17.2.3"
62
62
  },
@@ -74,7 +74,7 @@
74
74
  }
75
75
  },
76
76
  "engines": {
77
- "node": ">=18.0.0",
77
+ "node": ">=22.0.0",
78
78
  "pnpm": ">=9.0.0"
79
79
  },
80
80
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  import { DeepBookConfig } from '@mysten/deepbook-v3';
2
- import { DeepBookMarginPool } from '../toolkit';
2
+ import { DeepBookMarginPool } from '../toolkit/index.js';
3
3
 
4
4
  const MARGIN_POOLS = {
5
5
  SUI: {
@@ -22,11 +22,11 @@ const MARGIN_POOLS = {
22
22
 
23
23
  const main = async () => {
24
24
  try {
25
- const env = 'mainnet';
25
+ const network = 'mainnet';
26
26
  const dbMarginPool = new DeepBookMarginPool({
27
- env,
27
+ network,
28
28
  dbConfig: new DeepBookConfig({
29
- env,
29
+ network,
30
30
  address: '',
31
31
  marginPools: MARGIN_POOLS,
32
32
  }),
@@ -11,9 +11,9 @@
11
11
  * 5. Balance change tracking | 餘額變化追蹤
12
12
  */
13
13
 
14
- import { DeepBookMarginToolkit } from '../toolkit/index';
15
- import { getConfig, getPrivateKey } from '../config';
16
- import { setEnvVar, getEnvVar, displayEnvVars } from '../utils/env-manager';
14
+ import { DeepBookMarginToolkit } from '../toolkit/index.js';
15
+ import { getConfig, getPrivateKey } from '../config.js';
16
+ import { setEnvVar, getEnvVar, displayEnvVars } from '../utils/env-manager.js';
17
17
 
18
18
  // Interface definition | 介面定義
19
19
  interface BalanceSnapshot {
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export * from './toolkit';
1
+ export * from './toolkit/index.js';
@@ -1,6 +1,7 @@
1
1
  import { Coin, DeepBookConfig, FLOAT_SCALAR, MarginPoolContract } from '@mysten/deepbook-v3';
2
2
  import { bcs } from '@mysten/sui/bcs';
3
- import { DevInspectResults, getFullnodeUrl, SuiClient } from '@mysten/sui/client';
3
+ import { type SuiClientTypes } from '@mysten/sui/client';
4
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
4
5
  import { Transaction } from '@mysten/sui/transactions';
5
6
  import { BigNumber } from 'bignumber.js';
6
7
  import {
@@ -9,9 +10,10 @@ import {
9
10
  MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS,
10
11
  MarginPoolParamKey,
11
12
  MarginPoolWithSupplierCapParamKey,
12
- } from '../margin-pool-config';
13
- import { mul, normalize } from '../utils/math';
14
- import { NetworkType } from './types';
13
+ } from '../margin-pool-config.js';
14
+ import { mul, normalize } from '../utils/math.js';
15
+ import { getGrpcFullnodeUrl } from '../utils/network.js';
16
+ import { NetworkType } from './types.js';
15
17
 
16
18
  type InterestConfig = {
17
19
  highKink: number;
@@ -61,7 +63,7 @@ const isWithSupplierCapKey = (
61
63
 
62
64
  type DeepBookMarginPoolParams = {
63
65
  address?: string;
64
- suiClient?: SuiClient;
66
+ suiClient?: SuiGrpcClient;
65
67
  network?: NetworkType;
66
68
  dbConfig?: DeepBookConfig;
67
69
  };
@@ -77,18 +79,20 @@ type DeepBookMarginPoolParams = {
77
79
  export class DeepBookMarginPool {
78
80
  marginPoolContract: MarginPoolContract;
79
81
  dbConfig: DeepBookConfig;
80
- suiClient: SuiClient;
82
+ suiClient: SuiGrpcClient;
81
83
 
82
84
  /**
83
85
  * @param dbConfig - DeepBook configuration instance.
84
- * @param suiClient - Optional SuiClient; defaults to fullnode client based on config network.
86
+ * @param suiClient - Optional SuiGrpcClient; defaults to fullnode client based on config network.
85
87
  */
86
88
  constructor({ network, address = '', suiClient, dbConfig }: DeepBookMarginPoolParams = {}) {
87
89
  // If dbConfig is provided and network is not, derive network from dbConfig
88
- const resolvedNetwork = network ?? dbConfig?.network ?? 'mainnet';
90
+ const resolvedNetwork = (network ?? dbConfig?.network ?? 'mainnet') as NetworkType;
89
91
 
90
92
  this.dbConfig = dbConfig ?? new DeepBookConfig({ network: resolvedNetwork, address });
91
- this.suiClient = suiClient ?? new SuiClient({ url: getFullnodeUrl(resolvedNetwork) });
93
+ this.suiClient =
94
+ suiClient ??
95
+ new SuiGrpcClient({ baseUrl: getGrpcFullnodeUrl(resolvedNetwork), network: resolvedNetwork });
92
96
 
93
97
  // Initialize smart contract wrapper
94
98
  this.marginPoolContract = new MarginPoolContract(this.dbConfig);
@@ -159,16 +163,16 @@ export class DeepBookMarginPool {
159
163
  * Used to extract actual values returned from contract functions.
160
164
  */
161
165
  private parseInspectResultToBcsStructs(
162
- inspectResults: DevInspectResults,
166
+ inspectResults: SuiClientTypes.SimulateTransactionResult<{ commandResults: true }>,
163
167
  keys: (MarginPoolParamKey | MarginPoolWithSupplierCapParamKey)[]
164
168
  ): Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, string> {
165
- const results = inspectResults.results;
166
- if (!results) throw new Error('No results found in DevInspect output.');
169
+ const results = inspectResults.commandResults;
170
+ if (!results) throw new Error('No results found in simulateTransaction output.');
167
171
 
168
172
  return keys.reduce(
169
173
  (acc, key, idx) => {
170
174
  // Raw bytes returned from devInspect
171
- const bytes = results[idx]?.returnValues?.[0]?.[0];
175
+ const bytes = results[idx]?.returnValues?.[0]?.bcs;
172
176
  if (!bytes) return acc;
173
177
 
174
178
  // Decode bytes according to struct map
@@ -311,18 +315,18 @@ export class DeepBookMarginPool {
311
315
  */
312
316
  async #getInterestConfig(coinKey: string, borrowAprScaled: bigint) {
313
317
  const { address } = this.dbConfig.getMarginPool(coinKey);
314
- const response = await this.suiClient.getObject({
315
- id: address,
316
- options: {
317
- showContent: true,
318
+ const response = await this.suiClient.core.getObject({
319
+ objectId: address,
320
+ include: {
321
+ json: true,
318
322
  },
319
323
  });
320
-
321
- const fields = (response.data?.content as any).fields;
322
- const config = fields.config.fields;
323
- const interestConfig = config.interest_config.fields as RawInterestConfig;
324
- const marginPoolConfig = config.margin_pool_config.fields as RawMarginPoolConfig;
325
- const statePoolConfig = fields.state.fields as RawStatePoolConfig;
324
+ // gRPC returns Move object fields directly in json (no .fields wrapper)
325
+ const json = response.object?.json as any;
326
+ const config = json?.config;
327
+ const interestConfig = config?.interest_config as RawInterestConfig;
328
+ const marginPoolConfig = config?.margin_pool_config as RawMarginPoolConfig;
329
+ const statePoolConfig = json?.state as RawStatePoolConfig;
326
330
 
327
331
  const { normalized } = this.calculateKinksAndRate(
328
332
  interestConfig,
@@ -394,9 +398,18 @@ export class DeepBookMarginPool {
394
398
  ...MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS,
395
399
  ];
396
400
 
397
- const inspectResult = await this.suiClient.devInspectTransactionBlock({
398
- transactionBlock: tx,
399
- sender: this.dbConfig.address,
401
+ // Use a fallback address for simulation if not provided (simulation doesn't require a real sender)
402
+ const sender =
403
+ this.dbConfig.address || '0x0000000000000000000000000000000000000000000000000000000000000000';
404
+ tx.setSender(sender);
405
+ tx.setGasBudget(50_000_000_000n);
406
+ tx.setGasPayment([]);
407
+ const txBytes = await tx.build({ client: this.suiClient });
408
+ const inspectResult = await this.suiClient.core.simulateTransaction({
409
+ transaction: txBytes,
410
+ include: {
411
+ commandResults: true,
412
+ },
400
413
  });
401
414
 
402
415
  const parsed = this.parseInspectResultToBcsStructs(inspectResult, allKeys);
@@ -31,19 +31,25 @@
31
31
  * ```
32
32
  */
33
33
 
34
- import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
34
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
35
35
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
36
36
  import { Transaction } from '@mysten/sui/transactions';
37
37
  import { MarginPoolContract, DeepBookConfig } from '@mysten/deepbook-v3';
38
- import { ToolkitConfig, MarginCoinType, MarginBalance } from './types';
38
+ import { ToolkitConfig, MarginCoinType, MarginBalance } from './types.js';
39
39
  import { decodeSuiPrivateKey, SUI_PRIVATE_KEY_PREFIX } from '@mysten/sui/cryptography';
40
- import { hexOrBase64ToUint8Array, normalizePrivateKey } from '../utils/private-key';
40
+ import { hexOrBase64ToUint8Array, normalizePrivateKey } from '../utils/private-key.js';
41
+ import { getGrpcFullnodeUrl } from '../utils/network.js';
42
+
43
+ interface ExecuteOptions {
44
+ includeObjectTypes?: boolean;
45
+ includeBalanceChanges?: boolean;
46
+ }
41
47
 
42
48
  /**
43
49
  * Main DeepBook Margin Toolkit class | DeepBook Margin Toolkit 主類別
44
50
  */
45
51
  export class DeepBookMarginToolkit {
46
- private suiClient: SuiClient;
52
+ private suiClient: SuiGrpcClient;
47
53
  private keypair: Ed25519Keypair;
48
54
  private address: string;
49
55
  private marginPoolContract: MarginPoolContract;
@@ -59,18 +65,14 @@ export class DeepBookMarginToolkit {
59
65
  supplierCapPackageId,
60
66
  dbConfig,
61
67
  }: ToolkitConfig) {
62
- // Initialize SuiClient | 初始化 SuiClient
63
- const rpcUrl = fullnodeUrl ?? getFullnodeUrl(network);
64
- this.suiClient = new SuiClient({ url: rpcUrl });
68
+ const baseUrl = fullnodeUrl ?? getGrpcFullnodeUrl(network);
69
+ this.suiClient = new SuiGrpcClient({ baseUrl, network });
65
70
 
66
- // Initialize keypair | 初始化密鑰對
67
71
  this.keypair = this.#parseSecretKey(privateKey);
68
72
  this.address = this.keypair.getPublicKey().toSuiAddress();
69
73
 
70
- // Store Supplier Cap ID if provided | 儲存 Supplier Cap ID(如果提供)
71
74
  this.supplierCapId = supplierCapId;
72
75
 
73
- // Create DeepBookConfig | 創建 DeepBookConfig
74
76
  this.dbConfig =
75
77
  dbConfig ??
76
78
  new DeepBookConfig({
@@ -78,7 +80,6 @@ export class DeepBookMarginToolkit {
78
80
  address: this.address,
79
81
  });
80
82
 
81
- // Initialize MarginPoolContract | 初始化 MarginPoolContract
82
83
  this.marginPoolContract = new MarginPoolContract(this.dbConfig);
83
84
 
84
85
  this.supplierCapPackageId = supplierCapPackageId ?? this.dbConfig.MARGIN_PACKAGE_ID;
@@ -93,20 +94,67 @@ export class DeepBookMarginToolkit {
93
94
  return Ed25519Keypair.fromSecretKey(normalizePrivateKey(hexOrBase64ToUint8Array(secretKey)));
94
95
  }
95
96
 
97
+ /**
98
+ * Build, sign, execute a transaction, and validate success.
99
+ */
100
+ async #signAndExecute(tx: Transaction, options: ExecuteOptions = {}) {
101
+ const txBytes = await tx.build({ client: this.suiClient });
102
+ const { signature } = await this.keypair.signTransaction(txBytes);
103
+
104
+ const result = await this.suiClient.core.executeTransaction({
105
+ transaction: txBytes,
106
+ signatures: [signature],
107
+ include: {
108
+ effects: true,
109
+ objectTypes: options.includeObjectTypes,
110
+ balanceChanges: options.includeBalanceChanges,
111
+ },
112
+ });
113
+
114
+ const txResult = result.$kind === 'Transaction' ? result.Transaction : result.FailedTransaction;
115
+ if (!txResult?.effects.status.success) {
116
+ throw new Error(`Transaction failed: ${txResult?.effects.status.error || 'Unknown error'}`);
117
+ }
118
+
119
+ return txResult;
120
+ }
121
+
122
+ /**
123
+ * Find a newly created object by type name substring from transaction results.
124
+ * Returns the objectId of the first match, or null if none found.
125
+ */
126
+ #findCreatedObject(
127
+ txResult: { effects: { changedObjects?: any[] }; objectTypes?: Record<string, string> },
128
+ typeSubstring: string
129
+ ): string | null {
130
+ if (!txResult.effects.changedObjects) return null;
131
+
132
+ for (const change of txResult.effects.changedObjects) {
133
+ const objectType = txResult.objectTypes?.[change.objectId];
134
+ if (!change.inputDigest && objectType?.includes(typeSubstring)) {
135
+ return change.objectId;
136
+ }
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ #requireSupplierCap(): string {
143
+ if (!this.supplierCapId) {
144
+ throw new Error('Supplier Cap not initialized. Call initialize() first.');
145
+ }
146
+ return this.supplierCapId;
147
+ }
148
+
96
149
  // @TODO: Handle more than 1 supplier cap in future
97
150
  async #getExistingSupplierCapId() {
98
151
  const type = `${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;
99
- const resp = await this.suiClient.getOwnedObjects({
152
+ const resp = await this.suiClient.core.listOwnedObjects({
100
153
  owner: this.address,
101
- filter: {
102
- StructType: type,
103
- },
104
- options: {
105
- showType: true,
106
- },
154
+ type,
107
155
  });
108
156
 
109
- return resp.data?.[0]?.data?.objectId;
157
+ return resp.objects?.[0]?.objectId;
110
158
  }
111
159
 
112
160
  /**
@@ -156,31 +204,8 @@ export class DeepBookMarginToolkit {
156
204
  // Transfer the created Supplier Cap to the sender
157
205
  tx.transferObjects([supplierCap], tx.pure.address(this.address));
158
206
 
159
- const result = await this.suiClient.signAndExecuteTransaction({
160
- signer: this.keypair,
161
- transaction: tx,
162
- options: {
163
- showEffects: true,
164
- showObjectChanges: true,
165
- },
166
- });
167
-
168
- if (result.errors && result.errors.length > 0) {
169
- throw new Error(
170
- `Transaction failed with errors: ${result.errors.map((e) => e.toString()).join(', ')}`
171
- );
172
- }
173
-
174
- // Find created Supplier Cap from objectChanges | 從 objectChanges 中找到創建的 Supplier Cap
175
- if (result.objectChanges) {
176
- for (const change of result.objectChanges) {
177
- if (change.type === 'created' && change.objectType.includes('SupplierCap')) {
178
- return change.objectId;
179
- }
180
- }
181
- }
182
-
183
- return null;
207
+ const txResult = await this.#signAndExecute(tx, { includeObjectTypes: true });
208
+ return this.#findCreatedObject(txResult, 'SupplierCap');
184
209
  } catch (error: any) {
185
210
  throw new Error(`Failed to create Supplier Cap: ${error.message || error}`);
186
211
  }
@@ -214,31 +239,8 @@ export class DeepBookMarginToolkit {
214
239
  typeArguments: [marginPool.type],
215
240
  });
216
241
 
217
- const result = await this.suiClient.signAndExecuteTransaction({
218
- signer: this.keypair,
219
- transaction: tx,
220
- options: {
221
- showEffects: true,
222
- showObjectChanges: true,
223
- },
224
- });
225
-
226
- if (result.errors && result.errors.length > 0) {
227
- throw new Error(
228
- `Transaction failed with errors: ${result.errors.map((e) => e.toString()).join(', ')}`
229
- );
230
- }
231
-
232
- // Find created Referral from objectChanges | 從 objectChanges 中找到創建的 Referral
233
- if (result.objectChanges) {
234
- for (const change of result.objectChanges) {
235
- if (change.type === 'created' && change.objectType.includes('SupplyReferral')) {
236
- return change.objectId;
237
- }
238
- }
239
- }
240
-
241
- return null;
242
+ const txResult = await this.#signAndExecute(tx, { includeObjectTypes: true });
243
+ return this.#findCreatedObject(txResult, 'SupplyReferral');
242
244
  } catch (error: any) {
243
245
  throw new Error(`Failed to create Supply Referral: ${error.message || error}`);
244
246
  }
@@ -257,32 +259,15 @@ export class DeepBookMarginToolkit {
257
259
  referralId?: string
258
260
  ): Promise<boolean> {
259
261
  try {
260
- if (!this.supplierCapId) {
261
- throw new Error('Supplier Cap not initialized. Call initialize() first.');
262
- }
262
+ const capId = this.#requireSupplierCap();
263
263
 
264
264
  const tx = new Transaction();
265
265
  tx.setSender(this.address);
266
266
 
267
- const supplierCap = tx.object(this.supplierCapId);
268
-
269
- // SDK automatically handles unit conversion | SDK 自動處理單位轉換
267
+ const supplierCap = tx.object(capId);
270
268
  tx.add(this.marginPoolContract.supplyToMarginPool(coin, supplierCap, amount, referralId));
271
269
 
272
- const { errors } = await this.suiClient.signAndExecuteTransaction({
273
- signer: this.keypair,
274
- transaction: tx,
275
- options: {
276
- showEffects: true,
277
- showObjectChanges: true,
278
- },
279
- });
280
-
281
- if (errors && errors.length > 0) {
282
- throw new Error(
283
- `Transaction failed with errors: ${errors.map((e) => e.toString()).join(', ')}`
284
- );
285
- }
270
+ await this.#signAndExecute(tx);
286
271
  return true;
287
272
  } catch (error: any) {
288
273
  throw new Error(`Failed to supply to margin pool: ${error.message || error}`);
@@ -297,15 +282,12 @@ export class DeepBookMarginToolkit {
297
282
  */
298
283
  async withdrawFromMarginPool(coin: MarginCoinType, amount?: number): Promise<boolean> {
299
284
  try {
300
- if (!this.supplierCapId) {
301
- throw new Error('Supplier Cap not initialized. Call initialize() first.');
302
- }
285
+ const capId = this.#requireSupplierCap();
303
286
 
304
287
  const tx = new Transaction();
288
+ tx.setSender(this.address);
305
289
 
306
- const supplierCap = tx.object(this.supplierCapId);
307
-
308
- // Call withdrawFromMarginPool | 調用 withdrawFromMarginPool
290
+ const supplierCap = tx.object(capId);
309
291
  const withdrawFunc = this.marginPoolContract.withdrawFromMarginPool(
310
292
  coin,
311
293
  supplierCap,
@@ -313,25 +295,9 @@ export class DeepBookMarginToolkit {
313
295
  );
314
296
 
315
297
  const withdrawnCoin = withdrawFunc(tx);
316
-
317
- // Transfer withdrawn coin to sender | 將提取的 coin 轉給發送者
318
298
  tx.transferObjects([withdrawnCoin], this.address);
319
299
 
320
- const { errors } = await this.suiClient.signAndExecuteTransaction({
321
- signer: this.keypair,
322
- transaction: tx,
323
- options: {
324
- showEffects: true,
325
- showObjectChanges: true,
326
- },
327
- });
328
-
329
- if (errors && errors.length > 0) {
330
- throw new Error(
331
- `Transaction failed with errors: ${errors.map((e) => e.toString()).join(', ')}`
332
- );
333
- }
334
-
300
+ await this.#signAndExecute(tx);
335
301
  return true;
336
302
  } catch (error: any) {
337
303
  throw new Error(`Failed to withdraw from margin pool: ${error.message || error}`);
@@ -347,25 +313,11 @@ export class DeepBookMarginToolkit {
347
313
  async withdrawReferralFees(coin: MarginCoinType, referralId: string): Promise<boolean> {
348
314
  try {
349
315
  const tx = new Transaction();
316
+ tx.setSender(this.address);
350
317
 
351
- // Add withdrawReferralFees call | 添加 withdrawReferralFees 調用
352
318
  tx.add(this.marginPoolContract.withdrawReferralFees(coin, referralId));
353
319
 
354
- const { errors } = await this.suiClient.signAndExecuteTransaction({
355
- signer: this.keypair,
356
- transaction: tx,
357
- options: {
358
- showEffects: true,
359
- showObjectChanges: true,
360
- showBalanceChanges: true,
361
- },
362
- });
363
-
364
- if (errors && errors.length > 0) {
365
- throw new Error(
366
- `Transaction failed with errors: ${errors.map((e) => e.toString()).join(', ')}`
367
- );
368
- }
320
+ await this.#signAndExecute(tx, { includeBalanceChanges: true });
369
321
  return true;
370
322
  } catch (error: any) {
371
323
  throw new Error(`Failed to withdraw referral fees: ${error.message || error}`);
@@ -379,40 +331,38 @@ export class DeepBookMarginToolkit {
379
331
  */
380
332
  async getBalance(coin: MarginCoinType): Promise<MarginBalance> {
381
333
  try {
382
- if (!this.supplierCapId) {
383
- throw new Error('Supplier Cap not initialized. Call initialize() first.');
384
- }
334
+ const capId = this.#requireSupplierCap();
385
335
 
386
- // Query user supply amount in margin pool | 查詢用戶在 margin pool 中的供應量
387
336
  const tx = new Transaction();
388
- tx.add(this.marginPoolContract.userSupplyAmount(coin, this.supplierCapId));
337
+ tx.setSender(this.address);
338
+ tx.add(this.marginPoolContract.userSupplyAmount(coin, capId));
389
339
 
390
- const result = await this.suiClient.devInspectTransactionBlock({
391
- sender: this.address,
392
- transactionBlock: tx,
340
+ const txBytes = await tx.build({ client: this.suiClient });
341
+ const result = await this.suiClient.core.simulateTransaction({
342
+ transaction: txBytes,
343
+ include: {
344
+ commandResults: true,
345
+ },
393
346
  });
394
347
 
348
+ const coinInfo = this.dbConfig.getCoin(coin);
395
349
  let userSupplyAmount = 0;
396
350
 
397
- if (result && result.results && result.results[0] && result.results[0].returnValues) {
398
- const supplyData = result.results[0].returnValues[0];
399
- if (supplyData && supplyData[0]) {
400
- const rawAmount = Buffer.from(supplyData[0]).readBigUInt64LE();
401
- // Convert from smallest unit to human-readable | 從最小單位轉換為人類可讀
402
- const scalar = this.dbConfig.getCoin(coin).scalar;
403
- userSupplyAmount = Number(rawAmount) / scalar;
351
+ const commandResults = result.commandResults;
352
+ if (commandResults && commandResults[0]) {
353
+ const returnValue = commandResults[0].returnValues?.[0];
354
+ if (returnValue?.bcs) {
355
+ const rawAmount = Buffer.from(returnValue.bcs).readBigUInt64LE();
356
+ userSupplyAmount = Number(rawAmount) / coinInfo.scalar;
404
357
  }
405
358
  }
406
359
 
407
- // Query wallet balance | 查詢錢包餘額
408
- const coinType = this.dbConfig.getCoin(coin).type;
409
- const balance = await this.suiClient.getBalance({
360
+ const balance = await this.suiClient.core.getBalance({
410
361
  owner: this.address,
411
- coinType,
362
+ coinType: coinInfo.type,
412
363
  });
413
364
 
414
- const scalar = this.dbConfig.getCoin(coin).scalar;
415
- const walletBalance = Number(balance.totalBalance) / scalar;
365
+ const walletBalance = Number(balance.balance) / coinInfo.scalar;
416
366
 
417
367
  return {
418
368
  userSupplyAmount,
@@ -15,8 +15,8 @@
15
15
  * ```
16
16
  */
17
17
 
18
- export { DeepBookMarginToolkit } from './DeepBookMarginToolkit';
19
- export { DeepBookMarginPool, type MarginPoolParams } from './DeepBookMarginPool';
18
+ export { DeepBookMarginToolkit } from './DeepBookMarginToolkit.js';
19
+ export { DeepBookMarginPool, type MarginPoolParams } from './DeepBookMarginPool.js';
20
20
 
21
21
  export type {
22
22
  NetworkType,
@@ -25,4 +25,4 @@ export type {
25
25
  TransactionResult,
26
26
  MarginBalance,
27
27
  ReferralInfo,
28
- } from './types';
28
+ } from './types.js';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Network utility functions
3
+ */
4
+
5
+ import { NetworkType } from '../toolkit/types.js';
6
+
7
+ /**
8
+ * Get gRPC fullnode URL for a given network
9
+ * @param network Network type (mainnet, testnet)
10
+ * @returns gRPC fullnode URL
11
+ */
12
+ export function getGrpcFullnodeUrl(network: NetworkType): string {
13
+ switch (network) {
14
+ case 'mainnet':
15
+ return 'https://fullnode.mainnet.sui.io:443';
16
+ case 'testnet':
17
+ return 'https://fullnode.testnet.sui.io:443';
18
+ default:
19
+ throw new Error(`Unknown network: ${network}`);
20
+ }
21
+ }