@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 +2 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/package.json +4 -4
- package/src/examples/margin-pool-demo.ts +4 -4
- package/src/examples/toolkit-demo.ts +3 -3
- package/src/index.ts +1 -1
- package/src/toolkit/DeepBookMarginPool.ts +39 -26
- package/src/toolkit/DeepBookMarginToolkit.ts +99 -149
- package/src/toolkit/index.ts +3 -3
- package/src/utils/network.ts +21 -0
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 {
|
|
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?:
|
|
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:
|
|
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 {
|
|
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?:
|
|
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:
|
|
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.
|
|
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": "^
|
|
57
|
+
"@mysten/bcs": "^2.0.0",
|
|
58
58
|
"@mysten/deepbook-v3": "^1.0.9",
|
|
59
|
-
"@mysten/sui": "^
|
|
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": ">=
|
|
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
|
|
25
|
+
const network = 'mainnet';
|
|
26
26
|
const dbMarginPool = new DeepBookMarginPool({
|
|
27
|
-
|
|
27
|
+
network,
|
|
28
28
|
dbConfig: new DeepBookConfig({
|
|
29
|
-
|
|
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 {
|
|
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 {
|
|
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?:
|
|
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:
|
|
82
|
+
suiClient: SuiGrpcClient;
|
|
81
83
|
|
|
82
84
|
/**
|
|
83
85
|
* @param dbConfig - DeepBook configuration instance.
|
|
84
|
-
* @param suiClient - Optional
|
|
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 =
|
|
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:
|
|
166
|
+
inspectResults: SuiClientTypes.SimulateTransactionResult<{ commandResults: true }>,
|
|
163
167
|
keys: (MarginPoolParamKey | MarginPoolWithSupplierCapParamKey)[]
|
|
164
168
|
): Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, string> {
|
|
165
|
-
const results = inspectResults.
|
|
166
|
-
if (!results) throw new Error('No results found in
|
|
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]?.
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
const response = await this.suiClient.core.getObject({
|
|
319
|
+
objectId: address,
|
|
320
|
+
include: {
|
|
321
|
+
json: true,
|
|
318
322
|
},
|
|
319
323
|
});
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
const config =
|
|
323
|
-
const interestConfig = config
|
|
324
|
-
const marginPoolConfig = config
|
|
325
|
-
const statePoolConfig =
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
63
|
-
|
|
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.
|
|
152
|
+
const resp = await this.suiClient.core.listOwnedObjects({
|
|
100
153
|
owner: this.address,
|
|
101
|
-
|
|
102
|
-
StructType: type,
|
|
103
|
-
},
|
|
104
|
-
options: {
|
|
105
|
-
showType: true,
|
|
106
|
-
},
|
|
154
|
+
type,
|
|
107
155
|
});
|
|
108
156
|
|
|
109
|
-
return resp.
|
|
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
|
|
160
|
-
|
|
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
|
|
218
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
337
|
+
tx.setSender(this.address);
|
|
338
|
+
tx.add(this.marginPoolContract.userSupplyAmount(coin, capId));
|
|
389
339
|
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
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
|
|
415
|
-
const walletBalance = Number(balance.totalBalance) / scalar;
|
|
365
|
+
const walletBalance = Number(balance.balance) / coinInfo.scalar;
|
|
416
366
|
|
|
417
367
|
return {
|
|
418
368
|
userSupplyAmount,
|
package/src/toolkit/index.ts
CHANGED
|
@@ -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
|
+
}
|