@scallop-io/scallop-deepbook-kit 0.1.3 → 0.1.5-rc.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.d.mts +18 -9
- package/dist/index.d.ts +18 -9
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
- package/src/margin-pool-config.ts +43 -0
- package/src/toolkit/DeepBookMarginPool.ts +101 -115
- package/src/toolkit/DeepBookMarginToolkit.ts +56 -68
- package/src/toolkit/types.ts +60 -0
- package/src/utils/math.ts +4 -0
- package/src/testnet-config.ts +0 -229
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _mysten_deepbook_v3 from '@mysten/deepbook-v3';
|
|
2
|
-
import {
|
|
2
|
+
import { DeepBookConfig, MarginPoolContract } from '@mysten/deepbook-v3';
|
|
3
3
|
import { SuiClient } from '@mysten/sui/client';
|
|
4
4
|
import { Transaction } from '@mysten/sui/transactions';
|
|
5
5
|
|
|
@@ -10,6 +10,8 @@ interface ToolkitConfig {
|
|
|
10
10
|
privateKey: string;
|
|
11
11
|
supplierCapId?: string;
|
|
12
12
|
fullnodeUrl?: string;
|
|
13
|
+
supplierCapPackageId?: string;
|
|
14
|
+
dbConfig?: DeepBookConfig;
|
|
13
15
|
}
|
|
14
16
|
interface TransactionResult {
|
|
15
17
|
digest: string;
|
|
@@ -33,7 +35,9 @@ declare class DeepBookMarginToolkit {
|
|
|
33
35
|
private address;
|
|
34
36
|
private marginPoolContract;
|
|
35
37
|
private supplierCapId?;
|
|
36
|
-
|
|
38
|
+
private dbConfig;
|
|
39
|
+
private supplierCapPackageId;
|
|
40
|
+
constructor({ network, fullnodeUrl, supplierCapId, privateKey, supplierCapPackageId, dbConfig, }: ToolkitConfig);
|
|
37
41
|
initialize(): Promise<string>;
|
|
38
42
|
createSupplierCap(): Promise<string | null>;
|
|
39
43
|
createSupplyReferral(coin: MarginCoinType): Promise<string | null>;
|
|
@@ -47,17 +51,16 @@ declare class DeepBookMarginToolkit {
|
|
|
47
51
|
|
|
48
52
|
declare const MARGIN_POOL_PARAM_KEYS: readonly ["supplyCap", "maxUtilizationRate", "protocolSpread", "minBorrow", "interestRate", "totalSupply", "supplyShares", "totalBorrow", "borrowShares", "lastUpdateTimestamp"];
|
|
49
53
|
declare const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS: readonly ["userSupplyShares", "userSupplyAmount"];
|
|
50
|
-
|
|
51
54
|
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
52
55
|
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
56
|
+
|
|
53
57
|
type InterestConfig = {
|
|
54
|
-
midKink: number;
|
|
55
58
|
highKink: number;
|
|
56
59
|
baseBorrowApr: number;
|
|
57
|
-
|
|
58
|
-
highBorrowApr: number;
|
|
60
|
+
borrowAprOnHighKink: number;
|
|
59
61
|
maxBorrowApr: number;
|
|
60
|
-
|
|
62
|
+
supplyApr: number;
|
|
63
|
+
utilizationRate: number;
|
|
61
64
|
};
|
|
62
65
|
type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, number> & InterestConfig & {
|
|
63
66
|
address: string;
|
|
@@ -65,12 +68,18 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
|
|
|
65
68
|
scalar: number;
|
|
66
69
|
decimals: number;
|
|
67
70
|
};
|
|
71
|
+
type DeepBookMarginPoolParams = {
|
|
72
|
+
address?: string;
|
|
73
|
+
suiClient?: SuiClient;
|
|
74
|
+
env?: NetworkType;
|
|
75
|
+
dbConfig?: DeepBookConfig;
|
|
76
|
+
};
|
|
68
77
|
declare class DeepBookMarginPool {
|
|
69
78
|
#private;
|
|
70
|
-
readonly suiClient: SuiClient;
|
|
71
79
|
marginPoolContract: MarginPoolContract;
|
|
72
80
|
dbConfig: DeepBookConfig;
|
|
73
|
-
|
|
81
|
+
suiClient: SuiClient;
|
|
82
|
+
constructor({ env, address, suiClient, dbConfig, }?: DeepBookMarginPoolParams);
|
|
74
83
|
get env(): _mysten_deepbook_v3.Environment;
|
|
75
84
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
76
85
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _mysten_deepbook_v3 from '@mysten/deepbook-v3';
|
|
2
|
-
import {
|
|
2
|
+
import { DeepBookConfig, MarginPoolContract } from '@mysten/deepbook-v3';
|
|
3
3
|
import { SuiClient } from '@mysten/sui/client';
|
|
4
4
|
import { Transaction } from '@mysten/sui/transactions';
|
|
5
5
|
|
|
@@ -10,6 +10,8 @@ interface ToolkitConfig {
|
|
|
10
10
|
privateKey: string;
|
|
11
11
|
supplierCapId?: string;
|
|
12
12
|
fullnodeUrl?: string;
|
|
13
|
+
supplierCapPackageId?: string;
|
|
14
|
+
dbConfig?: DeepBookConfig;
|
|
13
15
|
}
|
|
14
16
|
interface TransactionResult {
|
|
15
17
|
digest: string;
|
|
@@ -33,7 +35,9 @@ declare class DeepBookMarginToolkit {
|
|
|
33
35
|
private address;
|
|
34
36
|
private marginPoolContract;
|
|
35
37
|
private supplierCapId?;
|
|
36
|
-
|
|
38
|
+
private dbConfig;
|
|
39
|
+
private supplierCapPackageId;
|
|
40
|
+
constructor({ network, fullnodeUrl, supplierCapId, privateKey, supplierCapPackageId, dbConfig, }: ToolkitConfig);
|
|
37
41
|
initialize(): Promise<string>;
|
|
38
42
|
createSupplierCap(): Promise<string | null>;
|
|
39
43
|
createSupplyReferral(coin: MarginCoinType): Promise<string | null>;
|
|
@@ -47,17 +51,16 @@ declare class DeepBookMarginToolkit {
|
|
|
47
51
|
|
|
48
52
|
declare const MARGIN_POOL_PARAM_KEYS: readonly ["supplyCap", "maxUtilizationRate", "protocolSpread", "minBorrow", "interestRate", "totalSupply", "supplyShares", "totalBorrow", "borrowShares", "lastUpdateTimestamp"];
|
|
49
53
|
declare const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS: readonly ["userSupplyShares", "userSupplyAmount"];
|
|
50
|
-
|
|
51
54
|
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
52
55
|
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
56
|
+
|
|
53
57
|
type InterestConfig = {
|
|
54
|
-
midKink: number;
|
|
55
58
|
highKink: number;
|
|
56
59
|
baseBorrowApr: number;
|
|
57
|
-
|
|
58
|
-
highBorrowApr: number;
|
|
60
|
+
borrowAprOnHighKink: number;
|
|
59
61
|
maxBorrowApr: number;
|
|
60
|
-
|
|
62
|
+
supplyApr: number;
|
|
63
|
+
utilizationRate: number;
|
|
61
64
|
};
|
|
62
65
|
type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapParamKey, number> & InterestConfig & {
|
|
63
66
|
address: string;
|
|
@@ -65,12 +68,18 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
|
|
|
65
68
|
scalar: number;
|
|
66
69
|
decimals: number;
|
|
67
70
|
};
|
|
71
|
+
type DeepBookMarginPoolParams = {
|
|
72
|
+
address?: string;
|
|
73
|
+
suiClient?: SuiClient;
|
|
74
|
+
env?: NetworkType;
|
|
75
|
+
dbConfig?: DeepBookConfig;
|
|
76
|
+
};
|
|
68
77
|
declare class DeepBookMarginPool {
|
|
69
78
|
#private;
|
|
70
|
-
readonly suiClient: SuiClient;
|
|
71
79
|
marginPoolContract: MarginPoolContract;
|
|
72
80
|
dbConfig: DeepBookConfig;
|
|
73
|
-
|
|
81
|
+
suiClient: SuiClient;
|
|
82
|
+
constructor({ env, address, suiClient, dbConfig, }?: DeepBookMarginPoolParams);
|
|
74
83
|
get env(): _mysten_deepbook_v3.Environment;
|
|
75
84
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
76
85
|
getPoolParameters(coinKey: string, supplierCapId: string | undefined, tx: Transaction, inspect: true): Promise<MarginPoolParams>;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var client=require('@mysten/sui/client'),ed25519=require('@mysten/sui/keypairs/ed25519'),transactions=require('@mysten/sui/transactions'),deepbookV3=require('@mysten/deepbook-v3'),cryptography=require('@mysten/sui/cryptography'),bcs$1=require('@mysten/bcs'),bcs=require('@mysten/sui/bcs'),bignumber_js=require('bignumber.js');var D={MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},f={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI"},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC"}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],B={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var k=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),L=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),x=n=>{if(k(n))return bcs$1.fromHex(n);if(L(n))return bcs$1.fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},_=n=>{if(n.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,cryptography.PRIVATE_KEY_SIZE);if(n.length===cryptography.PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===cryptography.PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var w=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(r){let a=r.fullnodeUrl??client.getFullnodeUrl(r.network);this.suiClient=new client.SuiClient({url:a}),this.keypair=this.#r(r.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r.supplierCapId;let e={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},t={SUI_DBUSDC:{address:f.SUI_DBUSDC.address,baseCoin:f.SUI_DBUSDC.baseCoin,quoteCoin:f.SUI_DBUSDC.quoteCoin}},i={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},o=new deepbookV3.DeepBookConfig({address:this.address,env:r.network,coins:e,pools:t,marginPools:i});this.marginPoolContract=new deepbookV3.MarginPoolContract(o);}#r(r){if(r.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:a}=cryptography.decodeSuiPrivateKey(r);return ed25519.Ed25519Keypair.fromSecretKey(_(a))}return ed25519.Ed25519Keypair.fromSecretKey(_(x(r)))}async#e(){let r=`${D.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#e();if(r)return this.supplierCapId=r,r;let a=await this.createSupplierCap();if(!a)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=a,a}async createSupplierCap(){try{let r=new transactions.Transaction,a=this.marginPoolContract.mintSupplierCap()(r);r.transferObjects([a],this.address);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplierCap"))return t.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let a=new transactions.Transaction;this.marginPoolContract.mintSupplyReferral(r)(a);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplyReferral"))return t.objectId}return null}catch(a){throw new Error(`Failed to create Supply Referral: ${a.message||a}`)}}async supplyToMarginPool(r,a,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction;t.setSender(this.address);let i=t.object(this.supplierCapId);t.add(this.marginPoolContract.supplyToMarginPool(r,i,a,e));let{errors:o}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(o&&o.length>0)throw new Error(`Transaction failed with errors: ${o.map(s=>s.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to supply to margin pool: ${t.message||t}`)}}async withdrawFromMarginPool(r,a){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new transactions.Transaction,t=e.object(this.supplierCapId),o=this.marginPoolContract.withdrawFromMarginPool(r,t,a)(e);e.transferObjects([o],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(l=>l.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(r,a){try{let e=new transactions.Transaction;e.add(this.marginPoolContract.withdrawReferralFees(r,a));let{errors:t}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(t&&t.length>0)throw new Error(`Transaction failed with errors: ${t.map(i=>i.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new transactions.Transaction;a.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let e=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:a}),t=0;if(e&&e.results&&e.results[0]&&e.results[0].returnValues){let p=e.results[0].returnValues[0];if(p&&p[0]){let m=Buffer.from(p[0]).readBigUInt64LE(),C=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;t=Number(m)/C;}}let i=r==="SUI"?c.SUI.type:c.DBUSDC.type,o=await this.suiClient.getBalance({owner:this.address,coinType:i}),s=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(o.totalBalance)/s;return {userSupplyAmount:t,walletBalance:l}}catch(a){throw new Error(`Failed to get balance: ${a.message||a}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var X=new Set(b),J=n=>X.has(n),I=class{constructor(r="testnet",a="",e=new client.SuiClient({url:client.getFullnodeUrl(r)}),t){this.suiClient=e;this.dbConfig=t??new deepbookV3.DeepBookConfig({env:r,address:a,coins:c,pools:f,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#r(r,a,e,t){if(J(a)){let i=this.marginPoolContract[a];if(t==null)throw new Error(`supplierCap is required for '${a}'.`);r.add(i(e,t));}else {let i=this.marginPoolContract[a];r.add(i(e));}}#e(r,a){let e=r.results;return e?a.reduce((t,i,o)=>{let s=e[o]?.returnValues?.[0]?.[0];if(!s)return t;let l=bcs.bcs[B[i]];return t[i]=l.parse(new Uint8Array(s)),t},{}):{}}#t(r,a){let e=this.dbConfig.getCoin(a),t={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,midKink:0,highKink:0,baseBorrowApr:0,midBorrowApr:0,highBorrowApr:0,maxBorrowApr:0,borrowApr:0,...e};if(!e)return t;let i=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[o,s]of Object.entries(r))o==="lastUpdateTimestamp"?t[o]=Number(s):i.has(o)?t[o]=new bignumber_js.BigNumber(s).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():t[o]=new bignumber_js.BigNumber(s).dividedBy(e.scalar).toNumber();return t}#a(r,a){let e=BigInt(a.base_rate),t=BigInt(a.base_slope),i=BigInt(a.excess_slope),o=BigInt(a.optimal_utilization),s=r<0n?0n:r>BigInt(deepbookV3.FLOAT_SCALAR)?BigInt(deepbookV3.FLOAT_SCALAR):BigInt(r);return s<=o?e+t*s/o:e+t+i*(s-o)/(BigInt(deepbookV3.FLOAT_SCALAR)-o)}#o(r,a,e){let t=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),s=BigInt(r.optimal_utilization),l=BigInt(a.max_utilization_rate),p=s,m=l,C=t,E=t+i,T=t+i+o*(l-s)/(1000000000n-s),A=t+i+o,U=this.#a(BigInt(bignumber_js.BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),r),u=M=>bignumber_js.BigNumber(M).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber();return {raw:{midKink:p,highKink:m,baseBorrowApr:C,midBorrowApr:E,highBorrowApr:T,maxBorrowApr:A,borrowApr:U},normalized:{midKink:u(p),highKink:u(m),baseBorrowApr:u(C),midBorrowApr:u(E),highBorrowApr:u(T),maxBorrowApr:u(A),borrowApr:u(U)}}}async#i(r){let{address:a}=this.dbConfig.getMarginPool(r),t=((await this.suiClient.getObject({id:a,options:{showContent:true}})).data?.content).fields,i=t.config.fields,o=i.interest_config.fields,s=i.margin_pool_config.fields,l=t.state.fields,{normalized:p}=this.#o(o,s,l);return p}async getPoolParameters(r,a,e=new transactions.Transaction,t=true){if(P.forEach(p=>this.#r(e,p,r)),a&&b.forEach(p=>this.#r(e,p,r,a)),!t)return e;let i=[...P,...b],o=await this.suiClient.devInspectTransactionBlock({transactionBlock:e,sender:this.dbConfig.address}),s=await this.#i(r);return {...this.#t(this.#e(o,i),r),...s}}};exports.DeepBookMarginPool=I;exports.DeepBookMarginToolkit=w;
|
|
1
|
+
'use strict';var client=require('@mysten/sui/client'),ed25519=require('@mysten/sui/keypairs/ed25519'),transactions=require('@mysten/sui/transactions'),deepbookV3=require('@mysten/deepbook-v3'),cryptography=require('@mysten/sui/cryptography'),bcs$1=require('@mysten/bcs'),bcs=require('@mysten/sui/bcs'),bignumber_js=require('bignumber.js');var K=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),B=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),S=a=>{if(K(a))return bcs$1.fromHex(a);if(B(a))return bcs$1.fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},f=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")};var C=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:r,fullnodeUrl:e,supplierCapId:t,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=e??client.getFullnodeUrl(r);this.suiClient=new client.SuiClient({url:s}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=t,this.dbConfig=n??new deepbookV3.DeepBookConfig({env: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:e}=cryptography.decodeSuiPrivateKey(r);return ed25519.Ed25519Keypair.fromSecretKey(f(e))}return ed25519.Ed25519Keypair.fromSecretKey(f(S(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 e=await this.createSupplierCap();if(!e)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=e,e}async createSupplierCap(){try{let r=new transactions.Transaction;r.setSender(this.address);let e=r.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()]});r.transferObjects([e],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(i=>i.toString()).join(", ")}`);if(t.objectChanges){for(let i of t.objectChanges)if(i.type==="created"&&i.objectType.includes("SupplierCap"))return i.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let e=new transactions.Transaction;e.setSender(this.address);let t=this.dbConfig.getMarginPool(r);if(!t)throw new Error(`Margin pool configuration not found for coin: ${r}`);e.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[e.object(t.address),e.object(this.dbConfig.MARGIN_REGISTRY_ID),e.object.clock()],typeArguments:[t.type]});let i=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(i.errors&&i.errors.length>0)throw new Error(`Transaction failed with errors: ${i.errors.map(o=>o.toString()).join(", ")}`);if(i.objectChanges){for(let o of i.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(e){throw new Error(`Failed to create Supply Referral: ${e.message||e}`)}}async supplyToMarginPool(r,e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let i=new transactions.Transaction;i.setSender(this.address);let o=i.object(this.supplierCapId);i.add(this.marginPoolContract.supplyToMarginPool(r,o,e,t));let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:i,options:{showEffects:!0,showObjectChanges:!0}});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(s=>s.toString()).join(", ")}`);return !0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(r,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction,i=t.object(this.supplierCapId),n=this.marginPoolContract.withdrawFromMarginPool(r,i,e)(t);t.transferObjects([n],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(l=>l.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw from margin pool: ${t.message||t}`)}}async withdrawReferralFees(r,e){try{let t=new transactions.Transaction;t.add(this.marginPoolContract.withdrawReferralFees(r,e));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.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 e=new transactions.Transaction;e.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let t=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:e}),i=0;if(t&&t.results&&t.results[0]&&t.results[0].returnValues){let p=t.results[0].returnValues[0];if(p&&p[0]){let g=Buffer.from(p[0]).readBigUInt64LE(),R=this.dbConfig.getCoin(r).scalar;i=Number(g)/R;}}let o=this.dbConfig.getCoin(r).type,n=await this.suiClient.getBalance({owner:this.address,coinType:o}),s=this.dbConfig.getCoin(r).scalar,l=Number(n.totalBalance)/s;return {userSupplyAmount:i,walletBalance:l}}catch(e){throw new Error(`Failed to get balance: ${e.message||e}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],m=["userSupplyShares","userSupplyAmount"],A={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var d=(a,r)=>a*r/BigInt(deepbookV3.FLOAT_SCALAR),c=a=>Number(a)/deepbookV3.FLOAT_SCALAR;var W=new Set(m),Y=a=>W.has(a),b=class{marginPoolContract;dbConfig;suiClient;constructor({env:r="mainnet",address:e="",suiClient:t=new client.SuiClient({url:client.getFullnodeUrl(r)}),dbConfig:i=new deepbookV3.DeepBookConfig({env:r,address:e})}={}){if(this.dbConfig=i,this.suiClient=t,this.marginPoolContract=new deepbookV3.MarginPoolContract(this.dbConfig),r!==this.env)throw new Error(`Mismatch between provided env (${r}) and dbConfig env (${this.env}).`)}get env(){return this.dbConfig.env}#r(r,e,t,i){if(Y(e)){let o=this.marginPoolContract[e];if(i==null)throw new Error(`supplierCap is required for '${e}'.`);r.add(o(t,i));}else {let o=this.marginPoolContract[e];r.add(o(t));}}#t(r,e){let t=r.results;return t?e.reduce((i,o,n)=>{let s=t[n]?.returnValues?.[0]?.[0];if(!s)return i;let l=bcs.bcs[A[o]];return i[o]=l.parse(new Uint8Array(s)),i},{}):{}}#i(r,e){let t=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:this.dbConfig.getCoin(e).scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...t};if(!t)return i;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[n,s]of Object.entries(r))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(t.scalar).toNumber();return i}#e(r,e){let t=BigInt(e.base_rate),i=BigInt(e.base_slope),o=BigInt(e.excess_slope),n=BigInt(e.optimal_utilization);return r<n?t+d(r,i):t+d(n,i)+d(r-n,o)}#o(r,e,t,i){let o=BigInt(r.optimal_utilization),n=BigInt(e.max_utilization_rate),s=this.#e(o,r),l=this.#e(n,r),p=BigInt(bignumber_js.BigNumber(t.total_borrow).dividedBy(t.total_supply).shiftedBy(9).decimalPlaces(0).toString()),g=d(d(BigInt(i),p),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(e.protocol_spread));return {raw:{baseBorrowApr:r.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:l,supplyApr:g,utilizationRate:p},normalized:{baseBorrowApr:c(BigInt(r.base_rate)),highKink:c(o),borrowAprOnHighKink:c(s),maxBorrowApr:c(l),supplyApr:c(g),utilizationRate:c(p)}}}async#n(r,e){let{address:t}=this.dbConfig.getMarginPool(r),o=((await this.suiClient.getObject({id:t,options:{showContent:true}})).data?.content).fields,n=o.config.fields,s=n.interest_config.fields,l=n.margin_pool_config.fields,p=o.state.fields,{normalized:g}=this.#o(s,l,p,e);return g}async getPoolParameters(r,e,t=new transactions.Transaction,i=true){if(P.forEach(p=>this.#r(t,p,r)),e&&m.forEach(p=>this.#r(t,p,r,e)),!i)return t;let o=[...P,...m],n=await this.suiClient.devInspectTransactionBlock({transactionBlock:t,sender:this.dbConfig.address}),s=this.#i(this.#t(n,o),r),l=await this.#n(r,s.interestRate*deepbookV3.FLOAT_SCALAR);return {...s,...l}}};exports.DeepBookMarginPool=b;exports.DeepBookMarginToolkit=C;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {getFullnodeUrl,SuiClient}from'@mysten/sui/client';import {Ed25519Keypair}from'@mysten/sui/keypairs/ed25519';import {Transaction}from'@mysten/sui/transactions';import {DeepBookConfig,MarginPoolContract,FLOAT_SCALAR}from'@mysten/deepbook-v3';import {SUI_PRIVATE_KEY_PREFIX,decodeSuiPrivateKey,LEGACY_PRIVATE_KEY_SIZE,PRIVATE_KEY_SIZE}from'@mysten/sui/cryptography';import {fromHex,fromBase64}from'@mysten/bcs';import {bcs}from'@mysten/sui/bcs';import {BigNumber}from'bignumber.js';var D={MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209"},c={DEEP:{address:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8",type:"0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP",scalar:1e6,decimals:6},SUI:{address:"0x0000000000000000000000000000000000000000000000000000000000000002",type:"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",scalar:1e9,decimals:9},DBUSDC:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",scalar:1e6,decimals:6},DBUSDT:{address:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7",type:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT",scalar:1e6,decimals:6},WAL:{address:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76",type:"0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL",scalar:1e9,decimals:9}},f={DEEP_SUI:{address:"0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f",baseCoin:"DEEP",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6},SUI_DBUSDC:{address:"0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5",baseCoin:"SUI",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DEEP_DBUSDC:{address:"0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622",baseCoin:"DEEP",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},DBUSDT_DBUSDC:{address:"0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca",baseCoin:"DBUSDT",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_DBUSDC:{address:"0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb",baseCoin:"WAL",quoteCoin:"DBUSDC",lotSize:1e6,tickSize:1e3},WAL_SUI:{address:"0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a",baseCoin:"WAL",quoteCoin:"SUI",lotSize:1e6,tickSize:1e6}},d={SUI:{address:"0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7",coinType:"0x2::sui::SUI"},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC"}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],B={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var k=n=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(n),L=n=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(n),x=n=>{if(k(n))return fromHex(n);if(L(n))return fromBase64(n);throw new Error("The string is not a valid hex or base64 string.")},_=n=>{if(n.length===LEGACY_PRIVATE_KEY_SIZE)return n.slice(0,PRIVATE_KEY_SIZE);if(n.length===PRIVATE_KEY_SIZE+1&&n[0]===0)return n.slice(1);if(n.length===PRIVATE_KEY_SIZE)return n;throw new Error("invalid secret key")};var w=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(r){let a=r.fullnodeUrl??getFullnodeUrl(r.network);this.suiClient=new SuiClient({url:a}),this.keypair=this.#r(r.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=r.supplierCapId;let e={DEEP:{address:c.DEEP.address,type:c.DEEP.type,scalar:c.DEEP.scalar},SUI:{address:c.SUI.address,type:c.SUI.type,scalar:c.SUI.scalar},DBUSDC:{address:c.DBUSDC.address,type:c.DBUSDC.type,scalar:c.DBUSDC.scalar}},t={SUI_DBUSDC:{address:f.SUI_DBUSDC.address,baseCoin:f.SUI_DBUSDC.baseCoin,quoteCoin:f.SUI_DBUSDC.quoteCoin}},i={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},o=new DeepBookConfig({address:this.address,env:r.network,coins:e,pools:t,marginPools:i});this.marginPoolContract=new MarginPoolContract(o);}#r(r){if(r.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:a}=decodeSuiPrivateKey(r);return Ed25519Keypair.fromSecretKey(_(a))}return Ed25519Keypair.fromSecretKey(_(x(r)))}async#e(){let r=`${D.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:r},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let r=await this.#e();if(r)return this.supplierCapId=r,r;let a=await this.createSupplierCap();if(!a)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=a,a}async createSupplierCap(){try{let r=new Transaction,a=this.marginPoolContract.mintSupplierCap()(r);r.transferObjects([a],this.address);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplierCap"))return t.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let a=new Transaction;this.marginPoolContract.mintSupplyReferral(r)(a);let e=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0}});if(e.errors&&e.errors.length>0)throw new Error(`Transaction failed with errors: ${e.errors.map(t=>t.toString()).join(", ")}`);if(e.objectChanges){for(let t of e.objectChanges)if(t.type==="created"&&t.objectType.includes("SupplyReferral"))return t.objectId}return null}catch(a){throw new Error(`Failed to create Supply Referral: ${a.message||a}`)}}async supplyToMarginPool(r,a,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction;t.setSender(this.address);let i=t.object(this.supplierCapId);t.add(this.marginPoolContract.supplyToMarginPool(r,i,a,e));let{errors:o}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0}});if(o&&o.length>0)throw new Error(`Transaction failed with errors: ${o.map(s=>s.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to supply to margin pool: ${t.message||t}`)}}async withdrawFromMarginPool(r,a){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let e=new Transaction,t=e.object(this.supplierCapId),o=this.marginPoolContract.withdrawFromMarginPool(r,t,a)(e);e.transferObjects([o],this.address);let{errors:s}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(s&&s.length>0)throw new Error(`Transaction failed with errors: ${s.map(l=>l.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw from margin pool: ${e.message||e}`)}}async withdrawReferralFees(r,a){try{let e=new Transaction;e.add(this.marginPoolContract.withdrawReferralFees(r,a));let{errors:t}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(t&&t.length>0)throw new Error(`Transaction failed with errors: ${t.map(i=>i.toString()).join(", ")}`);return !0}catch(e){throw new Error(`Failed to withdraw referral fees: ${e.message||e}`)}}async getBalance(r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new Transaction;a.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let e=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:a}),t=0;if(e&&e.results&&e.results[0]&&e.results[0].returnValues){let p=e.results[0].returnValues[0];if(p&&p[0]){let m=Buffer.from(p[0]).readBigUInt64LE(),C=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;t=Number(m)/C;}}let i=r==="SUI"?c.SUI.type:c.DBUSDC.type,o=await this.suiClient.getBalance({owner:this.address,coinType:i}),s=r==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(o.totalBalance)/s;return {userSupplyAmount:t,walletBalance:l}}catch(a){throw new Error(`Failed to get balance: ${a.message||a}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var X=new Set(b),J=n=>X.has(n),I=class{constructor(r="testnet",a="",e=new SuiClient({url:getFullnodeUrl(r)}),t){this.suiClient=e;this.dbConfig=t??new DeepBookConfig({env:r,address:a,coins:c,pools:f,marginPools:{SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}}}),this.marginPoolContract=new MarginPoolContract(this.dbConfig);}marginPoolContract;dbConfig;get env(){return this.dbConfig.env}#r(r,a,e,t){if(J(a)){let i=this.marginPoolContract[a];if(t==null)throw new Error(`supplierCap is required for '${a}'.`);r.add(i(e,t));}else {let i=this.marginPoolContract[a];r.add(i(e));}}#e(r,a){let e=r.results;return e?a.reduce((t,i,o)=>{let s=e[o]?.returnValues?.[0]?.[0];if(!s)return t;let l=bcs[B[i]];return t[i]=l.parse(new Uint8Array(s)),t},{}):{}}#t(r,a){let e=this.dbConfig.getCoin(a),t={supplyCap:0,maxUtilizationRate:0,protocolSpread:0,minBorrow:0,interestRate:0,totalSupply:0,supplyShares:0,totalBorrow:0,borrowShares:0,lastUpdateTimestamp:0,userSupplyShares:0,userSupplyAmount:0,decimals:0,midKink:0,highKink:0,baseBorrowApr:0,midBorrowApr:0,highBorrowApr:0,maxBorrowApr:0,borrowApr:0,...e};if(!e)return t;let i=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[o,s]of Object.entries(r))o==="lastUpdateTimestamp"?t[o]=Number(s):i.has(o)?t[o]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():t[o]=new BigNumber(s).dividedBy(e.scalar).toNumber();return t}#a(r,a){let e=BigInt(a.base_rate),t=BigInt(a.base_slope),i=BigInt(a.excess_slope),o=BigInt(a.optimal_utilization),s=r<0n?0n:r>BigInt(FLOAT_SCALAR)?BigInt(FLOAT_SCALAR):BigInt(r);return s<=o?e+t*s/o:e+t+i*(s-o)/(BigInt(FLOAT_SCALAR)-o)}#o(r,a,e){let t=BigInt(r.base_rate),i=BigInt(r.base_slope),o=BigInt(r.excess_slope),s=BigInt(r.optimal_utilization),l=BigInt(a.max_utilization_rate),p=s,m=l,C=t,E=t+i,T=t+i+o*(l-s)/(1000000000n-s),A=t+i+o,U=this.#a(BigInt(BigNumber(e.total_borrow).dividedBy(e.total_supply).shiftedBy(9).decimalPlaces(0).toString()),r),u=M=>BigNumber(M).dividedBy(FLOAT_SCALAR).toNumber();return {raw:{midKink:p,highKink:m,baseBorrowApr:C,midBorrowApr:E,highBorrowApr:T,maxBorrowApr:A,borrowApr:U},normalized:{midKink:u(p),highKink:u(m),baseBorrowApr:u(C),midBorrowApr:u(E),highBorrowApr:u(T),maxBorrowApr:u(A),borrowApr:u(U)}}}async#i(r){let{address:a}=this.dbConfig.getMarginPool(r),t=((await this.suiClient.getObject({id:a,options:{showContent:true}})).data?.content).fields,i=t.config.fields,o=i.interest_config.fields,s=i.margin_pool_config.fields,l=t.state.fields,{normalized:p}=this.#o(o,s,l);return p}async getPoolParameters(r,a,e=new Transaction,t=true){if(P.forEach(p=>this.#r(e,p,r)),a&&b.forEach(p=>this.#r(e,p,r,a)),!t)return e;let i=[...P,...b],o=await this.suiClient.devInspectTransactionBlock({transactionBlock:e,sender:this.dbConfig.address}),s=await this.#i(r);return {...this.#t(this.#e(o,i),r),...s}}};export{I as DeepBookMarginPool,w as DeepBookMarginToolkit};
|
|
1
|
+
import {getFullnodeUrl,SuiClient}from'@mysten/sui/client';import {Ed25519Keypair}from'@mysten/sui/keypairs/ed25519';import {Transaction}from'@mysten/sui/transactions';import {DeepBookConfig,MarginPoolContract,FLOAT_SCALAR}from'@mysten/deepbook-v3';import {SUI_PRIVATE_KEY_PREFIX,decodeSuiPrivateKey,LEGACY_PRIVATE_KEY_SIZE,PRIVATE_KEY_SIZE}from'@mysten/sui/cryptography';import {fromHex,fromBase64}from'@mysten/bcs';import {bcs}from'@mysten/sui/bcs';import {BigNumber}from'bignumber.js';var K=a=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(a),B=a=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(a),S=a=>{if(K(a))return fromHex(a);if(B(a))return fromBase64(a);throw new Error("The string is not a valid hex or base64 string.")},f=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")};var C=class{suiClient;keypair;address;marginPoolContract;supplierCapId;dbConfig;supplierCapPackageId;constructor({network:r,fullnodeUrl:e,supplierCapId:t,privateKey:i,supplierCapPackageId:o,dbConfig:n}){let s=e??getFullnodeUrl(r);this.suiClient=new SuiClient({url:s}),this.keypair=this.#r(i),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=t,this.dbConfig=n??new DeepBookConfig({env: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:e}=decodeSuiPrivateKey(r);return Ed25519Keypair.fromSecretKey(f(e))}return Ed25519Keypair.fromSecretKey(f(S(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 e=await this.createSupplierCap();if(!e)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=e,e}async createSupplierCap(){try{let r=new Transaction;r.setSender(this.address);let e=r.moveCall({target:`${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,arguments:[r.object(this.dbConfig.MARGIN_REGISTRY_ID),r.object.clock()]});r.transferObjects([e],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(i=>i.toString()).join(", ")}`);if(t.objectChanges){for(let i of t.objectChanges)if(i.type==="created"&&i.objectType.includes("SupplierCap"))return i.objectId}return null}catch(r){throw new Error(`Failed to create Supplier Cap: ${r.message||r}`)}}async createSupplyReferral(r){try{let e=new Transaction;e.setSender(this.address);let t=this.dbConfig.getMarginPool(r);if(!t)throw new Error(`Margin pool configuration not found for coin: ${r}`);e.moveCall({target:`${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[e.object(t.address),e.object(this.dbConfig.MARGIN_REGISTRY_ID),e.object.clock()],typeArguments:[t.type]});let i=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0}});if(i.errors&&i.errors.length>0)throw new Error(`Transaction failed with errors: ${i.errors.map(o=>o.toString()).join(", ")}`);if(i.objectChanges){for(let o of i.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(e){throw new Error(`Failed to create Supply Referral: ${e.message||e}`)}}async supplyToMarginPool(r,e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let i=new Transaction;i.setSender(this.address);let o=i.object(this.supplierCapId);i.add(this.marginPoolContract.supplyToMarginPool(r,o,e,t));let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:i,options:{showEffects:!0,showObjectChanges:!0}});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(s=>s.toString()).join(", ")}`);return !0}catch(i){throw new Error(`Failed to supply to margin pool: ${i.message||i}`)}}async withdrawFromMarginPool(r,e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction,i=t.object(this.supplierCapId),n=this.marginPoolContract.withdrawFromMarginPool(r,i,e)(t);t.transferObjects([n],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(l=>l.toString()).join(", ")}`);return !0}catch(t){throw new Error(`Failed to withdraw from margin pool: ${t.message||t}`)}}async withdrawReferralFees(r,e){try{let t=new Transaction;t.add(this.marginPoolContract.withdrawReferralFees(r,e));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0}});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.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 e=new Transaction;e.add(this.marginPoolContract.userSupplyAmount(r,this.supplierCapId));let t=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:e}),i=0;if(t&&t.results&&t.results[0]&&t.results[0].returnValues){let p=t.results[0].returnValues[0];if(p&&p[0]){let g=Buffer.from(p[0]).readBigUInt64LE(),R=this.dbConfig.getCoin(r).scalar;i=Number(g)/R;}}let o=this.dbConfig.getCoin(r).type,n=await this.suiClient.getBalance({owner:this.address,coinType:o}),s=this.dbConfig.getCoin(r).scalar,l=Number(n.totalBalance)/s;return {userSupplyAmount:i,walletBalance:l}}catch(e){throw new Error(`Failed to get balance: ${e.message||e}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var P=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],m=["userSupplyShares","userSupplyAmount"],A={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var d=(a,r)=>a*r/BigInt(FLOAT_SCALAR),c=a=>Number(a)/FLOAT_SCALAR;var W=new Set(m),Y=a=>W.has(a),b=class{marginPoolContract;dbConfig;suiClient;constructor({env:r="mainnet",address:e="",suiClient:t=new SuiClient({url:getFullnodeUrl(r)}),dbConfig:i=new DeepBookConfig({env:r,address:e})}={}){if(this.dbConfig=i,this.suiClient=t,this.marginPoolContract=new MarginPoolContract(this.dbConfig),r!==this.env)throw new Error(`Mismatch between provided env (${r}) and dbConfig env (${this.env}).`)}get env(){return this.dbConfig.env}#r(r,e,t,i){if(Y(e)){let o=this.marginPoolContract[e];if(i==null)throw new Error(`supplierCap is required for '${e}'.`);r.add(o(t,i));}else {let o=this.marginPoolContract[e];r.add(o(t));}}#t(r,e){let t=r.results;return t?e.reduce((i,o,n)=>{let s=t[n]?.returnValues?.[0]?.[0];if(!s)return i;let l=bcs[A[o]];return i[o]=l.parse(new Uint8Array(s)),i},{}):{}}#i(r,e){let t=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:this.dbConfig.getCoin(e).scalar.toString().length-1,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...t};if(!t)return i;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[n,s]of Object.entries(r))n==="lastUpdateTimestamp"?i[n]=Number(s):o.has(n)?i[n]=new BigNumber(s).dividedBy(FLOAT_SCALAR).toNumber():i[n]=new BigNumber(s).dividedBy(t.scalar).toNumber();return i}#e(r,e){let t=BigInt(e.base_rate),i=BigInt(e.base_slope),o=BigInt(e.excess_slope),n=BigInt(e.optimal_utilization);return r<n?t+d(r,i):t+d(n,i)+d(r-n,o)}#o(r,e,t,i){let o=BigInt(r.optimal_utilization),n=BigInt(e.max_utilization_rate),s=this.#e(o,r),l=this.#e(n,r),p=BigInt(BigNumber(t.total_borrow).dividedBy(t.total_supply).shiftedBy(9).decimalPlaces(0).toString()),g=d(d(BigInt(i),p),BigInt(FLOAT_SCALAR)-BigInt(e.protocol_spread));return {raw:{baseBorrowApr:r.base_rate,highKink:o,borrowAprOnHighKink:s,maxBorrowApr:l,supplyApr:g,utilizationRate:p},normalized:{baseBorrowApr:c(BigInt(r.base_rate)),highKink:c(o),borrowAprOnHighKink:c(s),maxBorrowApr:c(l),supplyApr:c(g),utilizationRate:c(p)}}}async#n(r,e){let{address:t}=this.dbConfig.getMarginPool(r),o=((await this.suiClient.getObject({id:t,options:{showContent:true}})).data?.content).fields,n=o.config.fields,s=n.interest_config.fields,l=n.margin_pool_config.fields,p=o.state.fields,{normalized:g}=this.#o(s,l,p,e);return g}async getPoolParameters(r,e,t=new Transaction,i=true){if(P.forEach(p=>this.#r(t,p,r)),e&&m.forEach(p=>this.#r(t,p,r,e)),!i)return t;let o=[...P,...m],n=await this.suiClient.devInspectTransactionBlock({transactionBlock:t,sender:this.dbConfig.address}),s=this.#i(this.#t(n,o),r),l=await this.#n(r,s.interestRate*FLOAT_SCALAR);return {...s,...l}}};export{b as DeepBookMarginPool,C as DeepBookMarginToolkit};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scallop-io/scallop-deepbook-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5-rc.1",
|
|
4
4
|
"description": "A toolkit for integrating Scallop with DeepBook functionality",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"scallop",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@mysten/bcs": "^1.9.2",
|
|
57
|
-
"@mysten/deepbook-v3": "^0.
|
|
58
|
-
"@mysten/sui": "^1.45.
|
|
57
|
+
"@mysten/deepbook-v3": "^0.27.0",
|
|
58
|
+
"@mysten/sui": "^1.45.2",
|
|
59
59
|
"bignumber.js": "^9.3.1",
|
|
60
60
|
"dotenv": "^17.2.3"
|
|
61
61
|
},
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Margin Pool Contract Parameter Keys | Margin Pool 合約參數鍵
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
export const MARGIN_POOL_PARAM_KEYS = [
|
|
6
|
+
'supplyCap',
|
|
7
|
+
'maxUtilizationRate',
|
|
8
|
+
'protocolSpread',
|
|
9
|
+
'minBorrow',
|
|
10
|
+
'interestRate',
|
|
11
|
+
'totalSupply',
|
|
12
|
+
'supplyShares',
|
|
13
|
+
'totalBorrow',
|
|
14
|
+
'borrowShares',
|
|
15
|
+
'lastUpdateTimestamp',
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
export const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS = [
|
|
19
|
+
'userSupplyShares',
|
|
20
|
+
'userSupplyAmount',
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export const MARGIN_POOL_PARAM_KEY_STRUCT_MAP = {
|
|
24
|
+
supplyCap: 'U64',
|
|
25
|
+
maxUtilizationRate: 'U64',
|
|
26
|
+
protocolSpread: 'U64',
|
|
27
|
+
minBorrow: 'U64',
|
|
28
|
+
interestRate: 'U64',
|
|
29
|
+
totalSupply: 'U64',
|
|
30
|
+
supplyShares: 'U64',
|
|
31
|
+
totalBorrow: 'U64',
|
|
32
|
+
borrowShares: 'U64',
|
|
33
|
+
lastUpdateTimestamp: 'U64',
|
|
34
|
+
userSupplyShares: 'U64',
|
|
35
|
+
userSupplyAmount: 'U64',
|
|
36
|
+
} as Record<
|
|
37
|
+
(typeof MARGIN_POOL_PARAM_KEYS)[number] | (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number],
|
|
38
|
+
'U64'
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
export type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
42
|
+
export type MarginPoolWithSupplierCapParamKey =
|
|
43
|
+
(typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import { DeepBookConfig, FLOAT_SCALAR, MarginPoolContract } from '@mysten/deepbook-v3';
|
|
2
|
+
import { bcs } from '@mysten/sui/bcs';
|
|
2
3
|
import { DevInspectResults, getFullnodeUrl, SuiClient } from '@mysten/sui/client';
|
|
3
4
|
import { Transaction } from '@mysten/sui/transactions';
|
|
5
|
+
import { BigNumber } from 'bignumber.js';
|
|
4
6
|
import {
|
|
5
7
|
MARGIN_POOL_PARAM_KEY_STRUCT_MAP,
|
|
6
8
|
MARGIN_POOL_PARAM_KEYS,
|
|
7
9
|
MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
MarginPoolParamKey,
|
|
11
|
+
MarginPoolWithSupplierCapParamKey,
|
|
12
|
+
} from '../margin-pool-config';
|
|
13
|
+
import { mul, normalize } from '../utils/math';
|
|
11
14
|
import { NetworkType } from './types';
|
|
12
|
-
import { BigNumber } from 'bignumber.js';
|
|
13
15
|
|
|
14
|
-
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
15
|
-
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
16
16
|
type InterestConfig = {
|
|
17
|
-
midKink: number;
|
|
18
17
|
highKink: number;
|
|
19
18
|
baseBorrowApr: number;
|
|
20
|
-
|
|
21
|
-
highBorrowApr: number;
|
|
19
|
+
borrowAprOnHighKink: number;
|
|
22
20
|
maxBorrowApr: number;
|
|
23
|
-
|
|
21
|
+
supplyApr: number;
|
|
22
|
+
utilizationRate: number;
|
|
24
23
|
};
|
|
25
24
|
export type MarginPoolParams = Record<
|
|
26
25
|
MarginPoolParamKey | MarginPoolWithSupplierCapParamKey,
|
|
@@ -64,6 +63,13 @@ const isWithSupplierCapKey = (
|
|
|
64
63
|
return _WITH_CAP_KEYS.has(key as string);
|
|
65
64
|
};
|
|
66
65
|
|
|
66
|
+
type DeepBookMarginPoolParams = {
|
|
67
|
+
address?: string;
|
|
68
|
+
suiClient?: SuiClient;
|
|
69
|
+
env?: NetworkType;
|
|
70
|
+
dbConfig?: DeepBookConfig;
|
|
71
|
+
};
|
|
72
|
+
|
|
67
73
|
/**
|
|
68
74
|
* DeepBookMarginPool
|
|
69
75
|
* -------------------
|
|
@@ -75,37 +81,32 @@ const isWithSupplierCapKey = (
|
|
|
75
81
|
export class DeepBookMarginPool {
|
|
76
82
|
marginPoolContract: MarginPoolContract;
|
|
77
83
|
dbConfig: DeepBookConfig;
|
|
84
|
+
suiClient: SuiClient;
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
87
|
* @param dbConfig - DeepBook configuration instance.
|
|
81
88
|
* @param suiClient - Optional SuiClient; defaults to fullnode client based on config env.
|
|
82
89
|
*/
|
|
83
|
-
constructor(
|
|
84
|
-
env
|
|
90
|
+
constructor({
|
|
91
|
+
env = 'mainnet',
|
|
85
92
|
address = '',
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
SUI: {
|
|
98
|
-
address: TESTNET_MARGIN_POOLS.SUI.address,
|
|
99
|
-
type: TESTNET_MARGIN_POOLS.SUI.coinType,
|
|
100
|
-
},
|
|
101
|
-
DBUSDC: {
|
|
102
|
-
address: TESTNET_MARGIN_POOLS.DBUSDC.address,
|
|
103
|
-
type: TESTNET_MARGIN_POOLS.DBUSDC.coinType,
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
});
|
|
93
|
+
suiClient = new SuiClient({
|
|
94
|
+
url: getFullnodeUrl(env),
|
|
95
|
+
}),
|
|
96
|
+
dbConfig = new DeepBookConfig({
|
|
97
|
+
env,
|
|
98
|
+
address,
|
|
99
|
+
}),
|
|
100
|
+
}: DeepBookMarginPoolParams = {}) {
|
|
101
|
+
this.dbConfig = dbConfig;
|
|
102
|
+
this.suiClient = suiClient;
|
|
103
|
+
|
|
107
104
|
// Initialize smart contract wrapper
|
|
108
105
|
this.marginPoolContract = new MarginPoolContract(this.dbConfig);
|
|
106
|
+
|
|
107
|
+
if (env !== this.env) {
|
|
108
|
+
throw new Error(`Mismatch between provided env (${env}) and dbConfig env (${this.env}).`);
|
|
109
|
+
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
get env() {
|
|
@@ -207,14 +208,13 @@ export class DeepBookMarginPool {
|
|
|
207
208
|
lastUpdateTimestamp: 0,
|
|
208
209
|
userSupplyShares: 0,
|
|
209
210
|
userSupplyAmount: 0,
|
|
210
|
-
decimals:
|
|
211
|
-
midKink: 0,
|
|
211
|
+
decimals: this.dbConfig.getCoin(coinKey).scalar.toString().length - 1,
|
|
212
212
|
highKink: 0,
|
|
213
213
|
baseBorrowApr: 0,
|
|
214
|
-
|
|
215
|
-
highBorrowApr: 0,
|
|
214
|
+
borrowAprOnHighKink: 0,
|
|
216
215
|
maxBorrowApr: 0,
|
|
217
|
-
|
|
216
|
+
supplyApr: 0,
|
|
217
|
+
utilizationRate: 0,
|
|
218
218
|
...coin,
|
|
219
219
|
};
|
|
220
220
|
|
|
@@ -242,94 +242,76 @@ export class DeepBookMarginPool {
|
|
|
242
242
|
return formatted;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Compute the borrow APR based on utilization and interest config.
|
|
247
|
+
*
|
|
248
|
+
* if (U < optimal) {
|
|
249
|
+
* r(U) = base_rate + mul(U, base_slope)
|
|
250
|
+
* } else {
|
|
251
|
+
* r(U) = base_rate + mul(optimal, base_slope) + mul(U - optimal, excess_slope)
|
|
252
|
+
* }
|
|
253
|
+
*/
|
|
254
|
+
#computeBorrowAprAtUtil(
|
|
255
|
+
util: bigint, // 1e9-scaled utilization
|
|
256
|
+
cfg: RawInterestConfig
|
|
257
|
+
): bigint {
|
|
258
|
+
const baseRate = BigInt(cfg.base_rate);
|
|
259
|
+
const baseSlope = BigInt(cfg.base_slope);
|
|
260
|
+
const excessSlope = BigInt(cfg.excess_slope);
|
|
261
|
+
const optimalUtil = BigInt(cfg.optimal_utilization);
|
|
262
|
+
|
|
263
|
+
if (util < optimalUtil) {
|
|
264
|
+
return baseRate + mul(util, baseSlope);
|
|
262
265
|
}
|
|
263
266
|
|
|
264
|
-
|
|
265
|
-
return (
|
|
266
|
-
baseRate +
|
|
267
|
-
baseSlope +
|
|
268
|
-
(excessSlope * (U - optimalUtil)) / (BigInt(FLOAT_SCALAR) - optimalUtil)
|
|
269
|
-
);
|
|
267
|
+
return baseRate + mul(optimalUtil, baseSlope) + mul(util - optimalUtil, excessSlope);
|
|
270
268
|
}
|
|
271
269
|
|
|
272
|
-
#
|
|
270
|
+
#calculateKinksAndRate(
|
|
273
271
|
interestConfig: RawInterestConfig,
|
|
274
272
|
marginPoolConfig: RawMarginPoolConfig,
|
|
275
|
-
state: RawStatePoolConfig
|
|
273
|
+
state: RawStatePoolConfig,
|
|
274
|
+
borrowApr: number
|
|
276
275
|
) {
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
baseRate +
|
|
295
|
-
baseSlope +
|
|
296
|
-
(excessSlope * (maxUtil - optimalUtil)) / (1_000_000_000n - optimalUtil);
|
|
297
|
-
|
|
298
|
-
// maxBorrowApr at U = 1.0 (i.e. 1e9)
|
|
299
|
-
const maxBorrowApr = baseRate + baseSlope + excessSlope;
|
|
300
|
-
const currentBorrowApr = this.#getCurrentBorrowApr(
|
|
301
|
-
BigInt(
|
|
302
|
-
BigNumber(state.total_borrow)
|
|
303
|
-
.dividedBy(state.total_supply)
|
|
304
|
-
.shiftedBy(9)
|
|
305
|
-
.decimalPlaces(0)
|
|
306
|
-
.toString()
|
|
307
|
-
),
|
|
308
|
-
interestConfig
|
|
276
|
+
const highKink = BigInt(interestConfig.optimal_utilization); // v0
|
|
277
|
+
const maxKink = BigInt(marginPoolConfig.max_utilization_rate); // U_max
|
|
278
|
+
|
|
279
|
+
// const midBorrowApr = this.#computeBorrowAprAtUtil(midKink, interestConfig);
|
|
280
|
+
const borrowAprOnHighKink = this.#computeBorrowAprAtUtil(highKink, interestConfig);
|
|
281
|
+
const maxBorrowApr = this.#computeBorrowAprAtUtil(maxKink, interestConfig);
|
|
282
|
+
|
|
283
|
+
const utilizationRate = BigInt(
|
|
284
|
+
BigNumber(state.total_borrow)
|
|
285
|
+
.dividedBy(state.total_supply)
|
|
286
|
+
.shiftedBy(9)
|
|
287
|
+
.decimalPlaces(0)
|
|
288
|
+
.toString()
|
|
289
|
+
);
|
|
290
|
+
const supplyApr = mul(
|
|
291
|
+
mul(BigInt(borrowApr), utilizationRate),
|
|
292
|
+
BigInt(FLOAT_SCALAR) - BigInt(marginPoolConfig.protocol_spread)
|
|
309
293
|
);
|
|
310
|
-
|
|
311
|
-
const normalize = (value: bigint) => BigNumber(value).dividedBy(FLOAT_SCALAR).toNumber();
|
|
312
294
|
|
|
313
295
|
return {
|
|
314
|
-
// raw 1e9-scaled values
|
|
296
|
+
// raw 1e9-scaled values
|
|
315
297
|
raw: {
|
|
316
|
-
|
|
317
|
-
highKink,
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
borrowApr: currentBorrowApr,
|
|
298
|
+
baseBorrowApr: interestConfig.base_rate,
|
|
299
|
+
highKink, // utilization at kink2
|
|
300
|
+
borrowAprOnHighKink, // APR at highKink
|
|
301
|
+
maxBorrowApr, // APR at U = 1.0
|
|
302
|
+
supplyApr,
|
|
303
|
+
utilizationRate,
|
|
323
304
|
},
|
|
324
|
-
// convenience
|
|
305
|
+
// convenience normalized numbers
|
|
325
306
|
normalized: {
|
|
326
|
-
|
|
307
|
+
baseBorrowApr: normalize(BigInt(interestConfig.base_rate)),
|
|
308
|
+
// midKink: normalize(midKink),
|
|
327
309
|
highKink: normalize(highKink),
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
highBorrowApr: normalize(highBorrowApr),
|
|
310
|
+
// midBorrowApr: normalize(midBorrowApr),
|
|
311
|
+
borrowAprOnHighKink: normalize(borrowAprOnHighKink),
|
|
331
312
|
maxBorrowApr: normalize(maxBorrowApr),
|
|
332
|
-
|
|
313
|
+
supplyApr: normalize(supplyApr),
|
|
314
|
+
utilizationRate: normalize(utilizationRate),
|
|
333
315
|
},
|
|
334
316
|
};
|
|
335
317
|
}
|
|
@@ -339,7 +321,7 @@ export class DeepBookMarginPool {
|
|
|
339
321
|
* @param coinKey - Asset key.
|
|
340
322
|
* @returns Interest configuration data including kinks and APRs.
|
|
341
323
|
*/
|
|
342
|
-
async #getInterestConfig(coinKey: string) {
|
|
324
|
+
async #getInterestConfig(coinKey: string, interestRate: number) {
|
|
343
325
|
const { address } = this.dbConfig.getMarginPool(coinKey);
|
|
344
326
|
const response = await this.suiClient.getObject({
|
|
345
327
|
id: address,
|
|
@@ -353,10 +335,11 @@ export class DeepBookMarginPool {
|
|
|
353
335
|
const interestConfig = config.interest_config.fields as RawInterestConfig;
|
|
354
336
|
const marginPoolConfig = config.margin_pool_config.fields as RawMarginPoolConfig;
|
|
355
337
|
const statePoolConfig = fields.state.fields as RawStatePoolConfig;
|
|
356
|
-
const { normalized } = this.#
|
|
338
|
+
const { normalized } = this.#calculateKinksAndRate(
|
|
357
339
|
interestConfig,
|
|
358
340
|
marginPoolConfig,
|
|
359
|
-
statePoolConfig
|
|
341
|
+
statePoolConfig,
|
|
342
|
+
interestRate
|
|
360
343
|
);
|
|
361
344
|
return normalized;
|
|
362
345
|
}
|
|
@@ -426,11 +409,14 @@ export class DeepBookMarginPool {
|
|
|
426
409
|
sender: this.dbConfig.address,
|
|
427
410
|
});
|
|
428
411
|
|
|
429
|
-
const interestData = await this.#getInterestConfig(coinKey);
|
|
430
412
|
const formattedResult = this.#formatResult(
|
|
431
413
|
this.#parseInspectResultToBcsStructs(inspectResult, allKeys),
|
|
432
414
|
coinKey
|
|
433
415
|
);
|
|
416
|
+
const interestData = await this.#getInterestConfig(
|
|
417
|
+
coinKey,
|
|
418
|
+
formattedResult.interestRate * FLOAT_SCALAR
|
|
419
|
+
);
|
|
434
420
|
|
|
435
421
|
return {
|
|
436
422
|
...formattedResult,
|
|
@@ -35,12 +35,6 @@ import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
|
|
|
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 {
|
|
39
|
-
TESTNET_COINS,
|
|
40
|
-
TESTNET_POOLS,
|
|
41
|
-
TESTNET_MARGIN_POOLS,
|
|
42
|
-
TESTNET_PACKAGES,
|
|
43
|
-
} from '../testnet-config';
|
|
44
38
|
import { ToolkitConfig, MarginCoinType, MarginBalance } from './types';
|
|
45
39
|
import { decodeSuiPrivateKey, SUI_PRIVATE_KEY_PREFIX } from '@mysten/sui/cryptography';
|
|
46
40
|
import { hexOrBase64ToUint8Array, normalizePrivateKey } from '../utils/private-key';
|
|
@@ -54,70 +48,40 @@ export class DeepBookMarginToolkit {
|
|
|
54
48
|
private address: string;
|
|
55
49
|
private marginPoolContract: MarginPoolContract;
|
|
56
50
|
private supplierCapId?: string;
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
private dbConfig: DeepBookConfig;
|
|
52
|
+
private supplierCapPackageId: string;
|
|
53
|
+
|
|
54
|
+
constructor({
|
|
55
|
+
network,
|
|
56
|
+
fullnodeUrl,
|
|
57
|
+
supplierCapId,
|
|
58
|
+
privateKey,
|
|
59
|
+
supplierCapPackageId,
|
|
60
|
+
dbConfig,
|
|
61
|
+
}: ToolkitConfig) {
|
|
59
62
|
// Initialize SuiClient | 初始化 SuiClient
|
|
60
|
-
const rpcUrl =
|
|
63
|
+
const rpcUrl = fullnodeUrl ?? getFullnodeUrl(network);
|
|
61
64
|
this.suiClient = new SuiClient({ url: rpcUrl });
|
|
62
65
|
|
|
63
66
|
// Initialize keypair | 初始化密鑰對
|
|
64
|
-
this.keypair = this.#parseSecretKey(
|
|
67
|
+
this.keypair = this.#parseSecretKey(privateKey);
|
|
65
68
|
this.address = this.keypair.getPublicKey().toSuiAddress();
|
|
66
69
|
|
|
67
70
|
// Store Supplier Cap ID if provided | 儲存 Supplier Cap ID(如果提供)
|
|
68
|
-
this.supplierCapId =
|
|
69
|
-
|
|
70
|
-
// Prepare coins configuration | 準備 coins 配置
|
|
71
|
-
const coins = {
|
|
72
|
-
DEEP: {
|
|
73
|
-
address: TESTNET_COINS.DEEP.address,
|
|
74
|
-
type: TESTNET_COINS.DEEP.type,
|
|
75
|
-
scalar: TESTNET_COINS.DEEP.scalar,
|
|
76
|
-
},
|
|
77
|
-
SUI: {
|
|
78
|
-
address: TESTNET_COINS.SUI.address,
|
|
79
|
-
type: TESTNET_COINS.SUI.type,
|
|
80
|
-
scalar: TESTNET_COINS.SUI.scalar,
|
|
81
|
-
},
|
|
82
|
-
DBUSDC: {
|
|
83
|
-
address: TESTNET_COINS.DBUSDC.address,
|
|
84
|
-
type: TESTNET_COINS.DBUSDC.type,
|
|
85
|
-
scalar: TESTNET_COINS.DBUSDC.scalar,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// Prepare pools configuration | 準備 pools 配置
|
|
90
|
-
const pools = {
|
|
91
|
-
SUI_DBUSDC: {
|
|
92
|
-
address: TESTNET_POOLS.SUI_DBUSDC.address,
|
|
93
|
-
baseCoin: TESTNET_POOLS.SUI_DBUSDC.baseCoin,
|
|
94
|
-
quoteCoin: TESTNET_POOLS.SUI_DBUSDC.quoteCoin,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Prepare margin pools configuration | 準備 margin pools 配置
|
|
99
|
-
const marginPools = {
|
|
100
|
-
SUI: {
|
|
101
|
-
address: TESTNET_MARGIN_POOLS.SUI.address,
|
|
102
|
-
type: TESTNET_MARGIN_POOLS.SUI.coinType,
|
|
103
|
-
},
|
|
104
|
-
DBUSDC: {
|
|
105
|
-
address: TESTNET_MARGIN_POOLS.DBUSDC.address,
|
|
106
|
-
type: TESTNET_MARGIN_POOLS.DBUSDC.coinType,
|
|
107
|
-
},
|
|
108
|
-
};
|
|
71
|
+
this.supplierCapId = supplierCapId;
|
|
109
72
|
|
|
110
73
|
// Create DeepBookConfig | 創建 DeepBookConfig
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
74
|
+
this.dbConfig =
|
|
75
|
+
dbConfig ??
|
|
76
|
+
new DeepBookConfig({
|
|
77
|
+
env: network,
|
|
78
|
+
address: this.address,
|
|
79
|
+
});
|
|
118
80
|
|
|
119
81
|
// Initialize MarginPoolContract | 初始化 MarginPoolContract
|
|
120
|
-
this.marginPoolContract = new MarginPoolContract(
|
|
82
|
+
this.marginPoolContract = new MarginPoolContract(this.dbConfig);
|
|
83
|
+
|
|
84
|
+
this.supplierCapPackageId = supplierCapPackageId ?? this.dbConfig.MARGIN_PACKAGE_ID;
|
|
121
85
|
}
|
|
122
86
|
|
|
123
87
|
#parseSecretKey(secretKey: string): Ed25519Keypair {
|
|
@@ -129,8 +93,9 @@ export class DeepBookMarginToolkit {
|
|
|
129
93
|
return Ed25519Keypair.fromSecretKey(normalizePrivateKey(hexOrBase64ToUint8Array(secretKey)));
|
|
130
94
|
}
|
|
131
95
|
|
|
96
|
+
// @TODO: Handle more than 1 supplier cap in future
|
|
132
97
|
async #getExistingSupplierCapId() {
|
|
133
|
-
const type = `${
|
|
98
|
+
const type = `${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::SupplierCap`;
|
|
134
99
|
const resp = await this.suiClient.getOwnedObjects({
|
|
135
100
|
owner: this.address,
|
|
136
101
|
filter: {
|
|
@@ -179,10 +144,17 @@ export class DeepBookMarginToolkit {
|
|
|
179
144
|
async createSupplierCap(): Promise<string | null> {
|
|
180
145
|
try {
|
|
181
146
|
const tx = new Transaction();
|
|
147
|
+
tx.setSender(this.address);
|
|
148
|
+
|
|
149
|
+
// Direct moveCall to get the returned object reference
|
|
150
|
+
// Based on SDK source: margin_pool::mint_supplier_cap
|
|
151
|
+
const supplierCap = tx.moveCall({
|
|
152
|
+
target: `${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,
|
|
153
|
+
arguments: [tx.object(this.dbConfig.MARGIN_REGISTRY_ID), tx.object.clock()],
|
|
154
|
+
});
|
|
182
155
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
tx.transferObjects([cap], this.address);
|
|
156
|
+
// Transfer the created Supplier Cap to the sender
|
|
157
|
+
tx.transferObjects([supplierCap], tx.pure.address(this.address));
|
|
186
158
|
|
|
187
159
|
const result = await this.suiClient.signAndExecuteTransaction({
|
|
188
160
|
signer: this.keypair,
|
|
@@ -222,9 +194,25 @@ export class DeepBookMarginToolkit {
|
|
|
222
194
|
async createSupplyReferral(coin: MarginCoinType): Promise<string | null> {
|
|
223
195
|
try {
|
|
224
196
|
const tx = new Transaction();
|
|
197
|
+
tx.setSender(this.address);
|
|
198
|
+
|
|
199
|
+
// Get margin pool configuration
|
|
200
|
+
const marginPool = this.dbConfig.getMarginPool(coin);
|
|
201
|
+
if (!marginPool) {
|
|
202
|
+
throw new Error(`Margin pool configuration not found for coin: ${coin}`);
|
|
203
|
+
}
|
|
225
204
|
|
|
226
|
-
// Use
|
|
227
|
-
|
|
205
|
+
// Use the initialVersion from config as the initial_shared_version
|
|
206
|
+
// Margin pools are shared objects on Sui
|
|
207
|
+
tx.moveCall({
|
|
208
|
+
target: `${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,
|
|
209
|
+
arguments: [
|
|
210
|
+
tx.object(marginPool.address),
|
|
211
|
+
tx.object(this.dbConfig.MARGIN_REGISTRY_ID),
|
|
212
|
+
tx.object.clock(),
|
|
213
|
+
],
|
|
214
|
+
typeArguments: [marginPool.type],
|
|
215
|
+
});
|
|
228
216
|
|
|
229
217
|
const result = await this.suiClient.signAndExecuteTransaction({
|
|
230
218
|
signer: this.keypair,
|
|
@@ -411,19 +399,19 @@ export class DeepBookMarginToolkit {
|
|
|
411
399
|
if (supplyData && supplyData[0]) {
|
|
412
400
|
const rawAmount = Buffer.from(supplyData[0]).readBigUInt64LE();
|
|
413
401
|
// Convert from smallest unit to human-readable | 從最小單位轉換為人類可讀
|
|
414
|
-
const scalar =
|
|
402
|
+
const scalar = this.dbConfig.getCoin(coin).scalar;
|
|
415
403
|
userSupplyAmount = Number(rawAmount) / scalar;
|
|
416
404
|
}
|
|
417
405
|
}
|
|
418
406
|
|
|
419
407
|
// Query wallet balance | 查詢錢包餘額
|
|
420
|
-
const coinType =
|
|
408
|
+
const coinType = this.dbConfig.getCoin(coin).type;
|
|
421
409
|
const balance = await this.suiClient.getBalance({
|
|
422
410
|
owner: this.address,
|
|
423
411
|
coinType,
|
|
424
412
|
});
|
|
425
413
|
|
|
426
|
-
const scalar =
|
|
414
|
+
const scalar = this.dbConfig.getCoin(coin).scalar;
|
|
427
415
|
const walletBalance = Number(balance.totalBalance) / scalar;
|
|
428
416
|
|
|
429
417
|
return {
|
package/src/toolkit/types.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Type definitions for DeepBook Margin Toolkit | DeepBook Margin Toolkit 類型定義
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { DeepBookConfig } from '@mysten/deepbook-v3';
|
|
6
|
+
|
|
5
7
|
// ============================================================================
|
|
6
8
|
// Network & Configuration Types | 網路與配置類型
|
|
7
9
|
// ============================================================================
|
|
@@ -31,6 +33,11 @@ export interface ToolkitConfig {
|
|
|
31
33
|
|
|
32
34
|
/** Optional: custom fullnode URL | 可選:自訂 fullnode URL */
|
|
33
35
|
fullnodeUrl?: string;
|
|
36
|
+
|
|
37
|
+
/** Optional: Supplier Cap Object type ID | 可選:Supplier Cap 物件類型 ID */
|
|
38
|
+
supplierCapPackageId?: string;
|
|
39
|
+
|
|
40
|
+
dbConfig?: DeepBookConfig;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -71,3 +78,56 @@ export interface ReferralInfo {
|
|
|
71
78
|
/** Owner address | 擁有者地址 */
|
|
72
79
|
owner: string;
|
|
73
80
|
}
|
|
81
|
+
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// Margin Pool Parameter Types | Margin Pool 參數類型
|
|
84
|
+
// ============================================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Margin pool configuration parameters | Margin pool 配置參數
|
|
88
|
+
*/
|
|
89
|
+
export interface MarginPoolConfig {
|
|
90
|
+
/** Pool address | 池子地址 */
|
|
91
|
+
address: string;
|
|
92
|
+
|
|
93
|
+
/** Coin type | 幣種類型 */
|
|
94
|
+
coinType: string;
|
|
95
|
+
|
|
96
|
+
/** Initial version | 初始版本 */
|
|
97
|
+
initialVersion: number;
|
|
98
|
+
|
|
99
|
+
/** Supply cap | 供應上限 */
|
|
100
|
+
supplyCap: number;
|
|
101
|
+
|
|
102
|
+
/** Max utilization rate | 最大使用率 */
|
|
103
|
+
maxUtilizationRate: number;
|
|
104
|
+
|
|
105
|
+
/** Referral spread | Referral spread */
|
|
106
|
+
referralSpread: number;
|
|
107
|
+
|
|
108
|
+
/** Min borrow amount | 最小借款額 */
|
|
109
|
+
minBorrow: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Risk parameters for a trading pair | 交易對的風險參數
|
|
114
|
+
*/
|
|
115
|
+
export interface RiskParameters {
|
|
116
|
+
/** Min withdraw risk ratio | 最小提款風險比率 */
|
|
117
|
+
minWithdrawRiskRatio: number;
|
|
118
|
+
|
|
119
|
+
/** Min borrow risk ratio | 最小借款風險比率 */
|
|
120
|
+
minBorrowRiskRatio: number;
|
|
121
|
+
|
|
122
|
+
/** Liquidation risk ratio | 清算風險比率 */
|
|
123
|
+
liquidationRiskRatio: number;
|
|
124
|
+
|
|
125
|
+
/** Target liquidation risk ratio | 目標清算風險比率 */
|
|
126
|
+
targetLiquidationRiskRatio: number;
|
|
127
|
+
|
|
128
|
+
/** User liquidation reward | 用戶清算獎勵 */
|
|
129
|
+
userLiquidationReward: number;
|
|
130
|
+
|
|
131
|
+
/** Pool liquidation reward | 池子清算獎勵 */
|
|
132
|
+
poolLiquidationReward: number;
|
|
133
|
+
}
|
package/src/testnet-config.ts
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DeepBook V3 & Margin Testnet Configuration | DeepBook V3 & Margin 測試網配置
|
|
3
|
-
* Source | 來源: https://github.com/MystenLabs/ts-sdks/tree/main/packages/deepbook-v3
|
|
4
|
-
* Last updated | 最後更新: 2025-11-11
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ============================================================================
|
|
8
|
-
// Package IDs
|
|
9
|
-
// ============================================================================
|
|
10
|
-
|
|
11
|
-
export const TESTNET_PACKAGES = {
|
|
12
|
-
DEEPBOOK_PACKAGE_ID: '0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982',
|
|
13
|
-
REGISTRY_ID: '0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1',
|
|
14
|
-
DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb',
|
|
15
|
-
MARGIN_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209',
|
|
16
|
-
MARGIN_INITIAL_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209', // for events
|
|
17
|
-
MARGIN_REGISTRY_ID: '0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4',
|
|
18
|
-
} as const;
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Coin Configuration | 代幣配置
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
export const TESTNET_COINS = {
|
|
25
|
-
DEEP: {
|
|
26
|
-
address: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8',
|
|
27
|
-
type: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
|
|
28
|
-
scalar: 1_000_000,
|
|
29
|
-
decimals: 6,
|
|
30
|
-
},
|
|
31
|
-
SUI: {
|
|
32
|
-
address: '0x0000000000000000000000000000000000000000000000000000000000000002',
|
|
33
|
-
type: '0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI',
|
|
34
|
-
scalar: 1_000_000_000,
|
|
35
|
-
decimals: 9,
|
|
36
|
-
},
|
|
37
|
-
DBUSDC: {
|
|
38
|
-
address: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7',
|
|
39
|
-
type: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC',
|
|
40
|
-
scalar: 1_000_000,
|
|
41
|
-
decimals: 6,
|
|
42
|
-
},
|
|
43
|
-
DBUSDT: {
|
|
44
|
-
address: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7',
|
|
45
|
-
type: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT',
|
|
46
|
-
scalar: 1_000_000,
|
|
47
|
-
decimals: 6,
|
|
48
|
-
},
|
|
49
|
-
WAL: {
|
|
50
|
-
address: '0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76',
|
|
51
|
-
type: '0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL',
|
|
52
|
-
scalar: 1_000_000_000,
|
|
53
|
-
decimals: 9,
|
|
54
|
-
},
|
|
55
|
-
} as const;
|
|
56
|
-
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// Trading Pool Configuration | 交易池配置
|
|
59
|
-
// ============================================================================
|
|
60
|
-
|
|
61
|
-
export const TESTNET_POOLS = {
|
|
62
|
-
DEEP_SUI: {
|
|
63
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
64
|
-
address: '0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f',
|
|
65
|
-
baseCoin: 'DEEP',
|
|
66
|
-
quoteCoin: 'SUI',
|
|
67
|
-
lotSize: 1_000_000, // 1 DEEP (6 decimals) | 1 DEEP(6 位小數)
|
|
68
|
-
tickSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
69
|
-
},
|
|
70
|
-
SUI_DBUSDC: {
|
|
71
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
72
|
-
address: '0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5',
|
|
73
|
-
baseCoin: 'SUI',
|
|
74
|
-
quoteCoin: 'DBUSDC',
|
|
75
|
-
lotSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
76
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
77
|
-
},
|
|
78
|
-
DEEP_DBUSDC: {
|
|
79
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
80
|
-
address: '0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622',
|
|
81
|
-
baseCoin: 'DEEP',
|
|
82
|
-
quoteCoin: 'DBUSDC',
|
|
83
|
-
lotSize: 1_000_000, // 1 DEEP (6 decimals) | 1 DEEP(6 位小數)
|
|
84
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
85
|
-
},
|
|
86
|
-
DBUSDT_DBUSDC: {
|
|
87
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
88
|
-
address: '0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca',
|
|
89
|
-
baseCoin: 'DBUSDT',
|
|
90
|
-
quoteCoin: 'DBUSDC',
|
|
91
|
-
lotSize: 1_000_000, // 1 DBUSDT (6 decimals) | 1 DBUSDT(6 位小數)
|
|
92
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
93
|
-
},
|
|
94
|
-
WAL_DBUSDC: {
|
|
95
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
96
|
-
address: '0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb',
|
|
97
|
-
baseCoin: 'WAL',
|
|
98
|
-
quoteCoin: 'DBUSDC',
|
|
99
|
-
lotSize: 1_000_000, // 0.001 WAL (9 decimals) | 0.001 WAL(9 位小數)
|
|
100
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
101
|
-
},
|
|
102
|
-
WAL_SUI: {
|
|
103
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
104
|
-
address: '0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a',
|
|
105
|
-
baseCoin: 'WAL',
|
|
106
|
-
quoteCoin: 'SUI',
|
|
107
|
-
lotSize: 1_000_000, // 0.001 WAL (9 decimals) | 0.001 WAL(9 位小數)
|
|
108
|
-
tickSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
109
|
-
},
|
|
110
|
-
} as const;
|
|
111
|
-
|
|
112
|
-
// ============================================================================
|
|
113
|
-
// Margin Pool Configuration | Margin 池配置
|
|
114
|
-
// ============================================================================
|
|
115
|
-
|
|
116
|
-
export const TESTNET_MARGIN_POOLS = {
|
|
117
|
-
SUI: {
|
|
118
|
-
// Actual margin pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-11 驗證)
|
|
119
|
-
address: '0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7',
|
|
120
|
-
coinType: '0x2::sui::SUI',
|
|
121
|
-
},
|
|
122
|
-
DBUSDC: {
|
|
123
|
-
// Actual margin pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-11 驗證)
|
|
124
|
-
address: '0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69',
|
|
125
|
-
coinType: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC',
|
|
126
|
-
},
|
|
127
|
-
} as const;
|
|
128
|
-
|
|
129
|
-
// ============================================================================
|
|
130
|
-
// Pyth Oracle Configuration | Pyth Oracle 配置
|
|
131
|
-
// ============================================================================
|
|
132
|
-
|
|
133
|
-
export const TESTNET_PYTH = {
|
|
134
|
-
pythStateId: '0x243759059f4c3111179da5878c12f68d612c21a8d54d85edc86164bb18be1c7c',
|
|
135
|
-
wormholeStateId: '0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790',
|
|
136
|
-
} as const;
|
|
137
|
-
|
|
138
|
-
// ============================================================================
|
|
139
|
-
// Pool Keys (SDK shorthand) | Pool Keys(SDK 使用的簡寫)
|
|
140
|
-
// ============================================================================
|
|
141
|
-
|
|
142
|
-
export const TESTNET_POOL_KEYS = [
|
|
143
|
-
'DEEP_SUI',
|
|
144
|
-
'SUI_DBUSDC',
|
|
145
|
-
'DEEP_DBUSDC',
|
|
146
|
-
'DBUSDT_DBUSDC',
|
|
147
|
-
'WAL_DBUSDC',
|
|
148
|
-
'WAL_SUI',
|
|
149
|
-
] as const;
|
|
150
|
-
|
|
151
|
-
export type TestnetPoolKey = (typeof TESTNET_POOL_KEYS)[number];
|
|
152
|
-
|
|
153
|
-
// ============================================================================
|
|
154
|
-
// Margin Pool Contract Parameter Keys | Margin Pool 合約參數鍵
|
|
155
|
-
// ============================================================================
|
|
156
|
-
|
|
157
|
-
export const MARGIN_POOL_PARAM_KEYS = [
|
|
158
|
-
'supplyCap',
|
|
159
|
-
'maxUtilizationRate',
|
|
160
|
-
'protocolSpread',
|
|
161
|
-
'minBorrow',
|
|
162
|
-
'interestRate',
|
|
163
|
-
'totalSupply',
|
|
164
|
-
'supplyShares',
|
|
165
|
-
'totalBorrow',
|
|
166
|
-
'borrowShares',
|
|
167
|
-
'lastUpdateTimestamp',
|
|
168
|
-
] as const;
|
|
169
|
-
|
|
170
|
-
export const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS = [
|
|
171
|
-
'userSupplyShares',
|
|
172
|
-
'userSupplyAmount',
|
|
173
|
-
] as const;
|
|
174
|
-
|
|
175
|
-
export const MARGIN_POOL_PARAM_KEY_STRUCT_MAP = {
|
|
176
|
-
supplyCap: 'U64',
|
|
177
|
-
maxUtilizationRate: 'U64',
|
|
178
|
-
protocolSpread: 'U64',
|
|
179
|
-
minBorrow: 'U64',
|
|
180
|
-
interestRate: 'U64',
|
|
181
|
-
totalSupply: 'U64',
|
|
182
|
-
supplyShares: 'U64',
|
|
183
|
-
totalBorrow: 'U64',
|
|
184
|
-
borrowShares: 'U64',
|
|
185
|
-
lastUpdateTimestamp: 'U64',
|
|
186
|
-
userSupplyShares: 'U64',
|
|
187
|
-
userSupplyAmount: 'U64',
|
|
188
|
-
} as Record<
|
|
189
|
-
(typeof MARGIN_POOL_PARAM_KEYS)[number] | (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number],
|
|
190
|
-
'U64'
|
|
191
|
-
>;
|
|
192
|
-
|
|
193
|
-
// ============================================================================
|
|
194
|
-
// Utility Functions | 輔助函數
|
|
195
|
-
// ============================================================================
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Get full pool configuration | 取得 Pool 的完整配置
|
|
199
|
-
*/
|
|
200
|
-
export function getPoolConfig(poolKey: TestnetPoolKey) {
|
|
201
|
-
return TESTNET_POOLS[poolKey];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Get full coin configuration | 取得代幣的完整配置
|
|
206
|
-
*/
|
|
207
|
-
export function getCoinConfig(coinSymbol: keyof typeof TESTNET_COINS) {
|
|
208
|
-
return TESTNET_COINS[coinSymbol];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Format coin amount (convert from smallest unit to human-readable) | 格式化代幣數量(從最小單位轉換為人類可讀)
|
|
213
|
-
*/
|
|
214
|
-
export function formatCoinAmount(
|
|
215
|
-
amount: number | bigint,
|
|
216
|
-
coinSymbol: keyof typeof TESTNET_COINS
|
|
217
|
-
): string {
|
|
218
|
-
const config = TESTNET_COINS[coinSymbol];
|
|
219
|
-
const value = Number(amount) / config.scalar;
|
|
220
|
-
return value.toFixed(config.decimals);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Parse coin amount (convert from human-readable to smallest unit) | 解析代幣數量(從人類可讀轉換為最小單位)
|
|
225
|
-
*/
|
|
226
|
-
export function parseCoinAmount(amount: number, coinSymbol: keyof typeof TESTNET_COINS): number {
|
|
227
|
-
const config = TESTNET_COINS[coinSymbol];
|
|
228
|
-
return Math.floor(amount * config.scalar);
|
|
229
|
-
}
|