@scallop-io/scallop-deepbook-kit 0.1.4 → 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 +15 -5
- package/dist/index.d.ts +15 -5
- 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 +32 -33
- package/src/toolkit/DeepBookMarginToolkit.ts +38 -79
- package/src/toolkit/types.ts +7 -0
- package/src/testnet-config.ts +0 -258
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,9 +51,9 @@ 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
58
|
highKink: number;
|
|
55
59
|
baseBorrowApr: number;
|
|
@@ -64,12 +68,18 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
|
|
|
64
68
|
scalar: number;
|
|
65
69
|
decimals: number;
|
|
66
70
|
};
|
|
71
|
+
type DeepBookMarginPoolParams = {
|
|
72
|
+
address?: string;
|
|
73
|
+
suiClient?: SuiClient;
|
|
74
|
+
env?: NetworkType;
|
|
75
|
+
dbConfig?: DeepBookConfig;
|
|
76
|
+
};
|
|
67
77
|
declare class DeepBookMarginPool {
|
|
68
78
|
#private;
|
|
69
|
-
readonly suiClient: SuiClient;
|
|
70
79
|
marginPoolContract: MarginPoolContract;
|
|
71
80
|
dbConfig: DeepBookConfig;
|
|
72
|
-
|
|
81
|
+
suiClient: SuiClient;
|
|
82
|
+
constructor({ env, address, suiClient, dbConfig, }?: DeepBookMarginPoolParams);
|
|
73
83
|
get env(): _mysten_deepbook_v3.Environment;
|
|
74
84
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
75
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,9 +51,9 @@ 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
58
|
highKink: number;
|
|
55
59
|
baseBorrowApr: number;
|
|
@@ -64,12 +68,18 @@ type MarginPoolParams = Record<MarginPoolParamKey | MarginPoolWithSupplierCapPar
|
|
|
64
68
|
scalar: number;
|
|
65
69
|
decimals: number;
|
|
66
70
|
};
|
|
71
|
+
type DeepBookMarginPoolParams = {
|
|
72
|
+
address?: string;
|
|
73
|
+
suiClient?: SuiClient;
|
|
74
|
+
env?: NetworkType;
|
|
75
|
+
dbConfig?: DeepBookConfig;
|
|
76
|
+
};
|
|
67
77
|
declare class DeepBookMarginPool {
|
|
68
78
|
#private;
|
|
69
|
-
readonly suiClient: SuiClient;
|
|
70
79
|
marginPoolContract: MarginPoolContract;
|
|
71
80
|
dbConfig: DeepBookConfig;
|
|
72
|
-
|
|
81
|
+
suiClient: SuiClient;
|
|
82
|
+
constructor({ env, address, suiClient, dbConfig, }?: DeepBookMarginPoolParams);
|
|
73
83
|
get env(): _mysten_deepbook_v3.Environment;
|
|
74
84
|
getPoolParameters(coinKey: string, supplierCapId?: string, tx?: Transaction): Promise<MarginPoolParams>;
|
|
75
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 g={DEEPBOOK_PACKAGE_ID:"0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982",REGISTRY_ID:"0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1",DEEP_TREASURY_ID:"0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb",MARGIN_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_REGISTRY_ID:"0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4"},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}},S={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",initialVersion:658877881,supplyCap:1e6,maxUtilizationRate:.9,referralSpread:.1,minBorrow:.1},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",initialVersion:658877215,supplyCap:1e6,maxUtilizationRate:.95,referralSpread:.1,minBorrow:.1}};var _=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],T={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var K=s=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(s),O=s=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(s),A=s=>{if(K(s))return bcs$1.fromHex(s);if(O(s))return bcs$1.fromBase64(s);throw new Error("The string is not a valid hex or base64 string.")},y=s=>{if(s.length===cryptography.LEGACY_PRIVATE_KEY_SIZE)return s.slice(0,cryptography.PRIVATE_KEY_SIZE);if(s.length===cryptography.PRIVATE_KEY_SIZE+1&&s[0]===0)return s.slice(1);if(s.length===cryptography.PRIVATE_KEY_SIZE)return s;throw new Error("invalid secret key")};var P=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(e){let t=e.fullnodeUrl??client.getFullnodeUrl(e.network);this.suiClient=new client.SuiClient({url:t}),this.keypair=this.#e(e.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e.supplierCapId;let r={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}},a={SUI_DBUSDC:{address:S.SUI_DBUSDC.address,baseCoin:S.SUI_DBUSDC.baseCoin,quoteCoin:S.SUI_DBUSDC.quoteCoin}},o={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},i=new deepbookV3.DeepBookConfig({address:this.address,env:e.network,coins:r,pools:a,marginPools:o});this.marginPoolContract=new deepbookV3.MarginPoolContract(i);}#e(e){if(e.startsWith(cryptography.SUI_PRIVATE_KEY_PREFIX)){let{secretKey:t}=cryptography.decodeSuiPrivateKey(e);return ed25519.Ed25519Keypair.fromSecretKey(y(t))}return ed25519.Ed25519Keypair.fromSecretKey(y(A(e)))}async#r(){let e=`${g.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:e},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let e=await this.#r();if(e)return this.supplierCapId=e,e;let t=await this.createSupplierCap();if(!t)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=t,t}async createSupplierCap(){try{let e=new transactions.Transaction;e.setSender(this.address);let t=e.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supplier_cap`,arguments:[e.object(g.MARGIN_REGISTRY_ID),e.object.clock()]});e.transferObjects([t],e.pure.address(this.address));let r=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(r.errors&&r.errors.length>0)throw new Error(`Transaction failed with errors: ${r.errors.map(a=>a.toString()).join(", ")}`);if(r.objectChanges){for(let a of r.objectChanges)if(a.type==="created"&&a.objectType.includes("SupplierCap"))return a.objectId}return null}catch(e){throw new Error(`Failed to create Supplier Cap: ${e.message||e}`)}}async createSupplyReferral(e){try{let t=new transactions.Transaction;t.setSender(this.address);let r=d[e];t.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[t.sharedObjectRef({objectId:r.address,initialSharedVersion:r.initialVersion,mutable:!0}),t.object(g.MARGIN_REGISTRY_ID),t.object.clock()],typeArguments:[r.coinType]});let a=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(a.errors&&a.errors.length>0)throw new Error(`Transaction failed with errors: ${a.errors.map(o=>o.toString()).join(", ")}`);if(a.objectChanges){for(let o of a.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(t){throw new Error(`Failed to create Supply Referral: ${t.message||t}`)}}async supplyToMarginPool(e,t,r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new transactions.Transaction;a.setSender(this.address);let o=a.object(this.supplierCapId);a.add(this.marginPoolContract.supplyToMarginPool(e,o,t,r));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.map(n=>n.toString()).join(", ")}`);return !0}catch(a){throw new Error(`Failed to supply to margin pool: ${a.message||a}`)}}async withdrawFromMarginPool(e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let r=new transactions.Transaction,a=r.object(this.supplierCapId),i=this.marginPoolContract.withdrawFromMarginPool(e,a,t)(r);r.transferObjects([i],this.address);let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(l=>l.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(e,t){try{let r=new transactions.Transaction;r.add(this.marginPoolContract.withdrawReferralFees(e,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0},requestType:"WaitForLocalExecution"});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(o=>o.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new transactions.Transaction;t.add(this.marginPoolContract.userSupplyAmount(e,this.supplierCapId));let r=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:t}),a=0;if(r&&r.results&&r.results[0]&&r.results[0].returnValues){let p=r.results[0].returnValues[0];if(p&&p[0]){let f=Buffer.from(p[0]).readBigUInt64LE(),R=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;a=Number(f)/R;}}let o=e==="SUI"?c.SUI.type:c.DBUSDC.type,i=await this.suiClient.getBalance({owner:this.address,coinType:o}),n=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(i.totalBalance)/n;return {userSupplyAmount:a,walletBalance:l}}catch(t){throw new Error(`Failed to get balance: ${t.message||t}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var C=(s,e)=>s*e/BigInt(deepbookV3.FLOAT_SCALAR),u=s=>Number(s)/deepbookV3.FLOAT_SCALAR;var V=new Set(b),H=s=>V.has(s),E=class{constructor(e="testnet",t="",r=new client.SuiClient({url:client.getFullnodeUrl(e)}),a){this.suiClient=r;this.dbConfig=a??new deepbookV3.DeepBookConfig({env:e,address:t,coins:c,pools:S,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}#e(e,t,r,a){if(H(t)){let o=this.marginPoolContract[t];if(a==null)throw new Error(`supplierCap is required for '${t}'.`);e.add(o(r,a));}else {let o=this.marginPoolContract[t];e.add(o(r));}}#r(e,t){let r=e.results;return r?t.reduce((a,o,i)=>{let n=r[i]?.returnValues?.[0]?.[0];if(!n)return a;let l=bcs.bcs[T[o]];return a[o]=l.parse(new Uint8Array(n)),a},{}):{}}#a(e,t){let r=this.dbConfig.getCoin(t),a={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,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)return a;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,n]of Object.entries(e))i==="lastUpdateTimestamp"?a[i]=Number(n):o.has(i)?a[i]=new bignumber_js.BigNumber(n).dividedBy(deepbookV3.FLOAT_SCALAR).toNumber():a[i]=new bignumber_js.BigNumber(n).dividedBy(r.scalar).toNumber();return a}#t(e,t){let r=BigInt(t.base_rate),a=BigInt(t.base_slope),o=BigInt(t.excess_slope),i=BigInt(t.optimal_utilization);return e<i?r+C(e,a):r+C(i,a)+C(e-i,o)}#o(e,t,r,a){let o=BigInt(e.optimal_utilization),i=BigInt(t.max_utilization_rate),n=this.#t(o,e),l=this.#t(i,e),p=BigInt(bignumber_js.BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),f=C(C(BigInt(a),p),BigInt(deepbookV3.FLOAT_SCALAR)-BigInt(t.protocol_spread));return {raw:{baseBorrowApr:e.base_rate,highKink:o,borrowAprOnHighKink:n,maxBorrowApr:l,supplyApr:f,utilizationRate:p},normalized:{baseBorrowApr:u(BigInt(e.base_rate)),highKink:u(o),borrowAprOnHighKink:u(n),maxBorrowApr:u(l),supplyApr:u(f),utilizationRate:u(p)}}}async#i(e,t){let{address:r}=this.dbConfig.getMarginPool(e),o=((await this.suiClient.getObject({id:r,options:{showContent:true}})).data?.content).fields,i=o.config.fields,n=i.interest_config.fields,l=i.margin_pool_config.fields,p=o.state.fields,{normalized:f}=this.#o(n,l,p,t);return f}async getPoolParameters(e,t,r=new transactions.Transaction,a=true){if(_.forEach(p=>this.#e(r,p,e)),t&&b.forEach(p=>this.#e(r,p,e,t)),!a)return r;let o=[..._,...b],i=await this.suiClient.devInspectTransactionBlock({transactionBlock:r,sender:this.dbConfig.address}),n=this.#a(this.#r(i,o),e),l=await this.#i(e,n.interestRate*deepbookV3.FLOAT_SCALAR);return {...n,...l}}};exports.DeepBookMarginPool=E;exports.DeepBookMarginToolkit=P;
|
|
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 g={DEEPBOOK_PACKAGE_ID:"0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982",REGISTRY_ID:"0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1",DEEP_TREASURY_ID:"0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb",MARGIN_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_INITIAL_PACKAGE_ID:"0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209",MARGIN_REGISTRY_ID:"0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4"},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}},S={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",initialVersion:658877881,supplyCap:1e6,maxUtilizationRate:.9,referralSpread:.1,minBorrow:.1},DBUSDC:{address:"0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69",coinType:"0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC",initialVersion:658877215,supplyCap:1e6,maxUtilizationRate:.95,referralSpread:.1,minBorrow:.1}};var _=["supplyCap","maxUtilizationRate","protocolSpread","minBorrow","interestRate","totalSupply","supplyShares","totalBorrow","borrowShares","lastUpdateTimestamp"],b=["userSupplyShares","userSupplyAmount"],T={supplyCap:"U64",maxUtilizationRate:"U64",protocolSpread:"U64",minBorrow:"U64",interestRate:"U64",totalSupply:"U64",supplyShares:"U64",totalBorrow:"U64",borrowShares:"U64",lastUpdateTimestamp:"U64",userSupplyShares:"U64",userSupplyAmount:"U64"};var K=s=>/^0x[0-9a-fA-F]+$|^[0-9a-fA-F]+$/.test(s),O=s=>/^[a-zA-Z0-9+/]+={0,2}$/g.test(s),A=s=>{if(K(s))return fromHex(s);if(O(s))return fromBase64(s);throw new Error("The string is not a valid hex or base64 string.")},y=s=>{if(s.length===LEGACY_PRIVATE_KEY_SIZE)return s.slice(0,PRIVATE_KEY_SIZE);if(s.length===PRIVATE_KEY_SIZE+1&&s[0]===0)return s.slice(1);if(s.length===PRIVATE_KEY_SIZE)return s;throw new Error("invalid secret key")};var P=class{suiClient;keypair;address;marginPoolContract;supplierCapId;constructor(e){let t=e.fullnodeUrl??getFullnodeUrl(e.network);this.suiClient=new SuiClient({url:t}),this.keypair=this.#e(e.privateKey),this.address=this.keypair.getPublicKey().toSuiAddress(),this.supplierCapId=e.supplierCapId;let r={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}},a={SUI_DBUSDC:{address:S.SUI_DBUSDC.address,baseCoin:S.SUI_DBUSDC.baseCoin,quoteCoin:S.SUI_DBUSDC.quoteCoin}},o={SUI:{address:d.SUI.address,type:d.SUI.coinType},DBUSDC:{address:d.DBUSDC.address,type:d.DBUSDC.coinType}},i=new DeepBookConfig({address:this.address,env:e.network,coins:r,pools:a,marginPools:o});this.marginPoolContract=new MarginPoolContract(i);}#e(e){if(e.startsWith(SUI_PRIVATE_KEY_PREFIX)){let{secretKey:t}=decodeSuiPrivateKey(e);return Ed25519Keypair.fromSecretKey(y(t))}return Ed25519Keypair.fromSecretKey(y(A(e)))}async#r(){let e=`${g.MARGIN_INITIAL_PACKAGE_ID}::margin_pool::SupplierCap`;return (await this.suiClient.getOwnedObjects({owner:this.address,filter:{StructType:e},options:{showType:true}})).data?.[0]?.data?.objectId}async initialize(){if(this.supplierCapId)return this.supplierCapId;let e=await this.#r();if(e)return this.supplierCapId=e,e;let t=await this.createSupplierCap();if(!t)throw new Error("Failed to create Supplier Cap");return this.supplierCapId=t,t}async createSupplierCap(){try{let e=new Transaction;e.setSender(this.address);let t=e.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supplier_cap`,arguments:[e.object(g.MARGIN_REGISTRY_ID),e.object.clock()]});e.transferObjects([t],e.pure.address(this.address));let r=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:e,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(r.errors&&r.errors.length>0)throw new Error(`Transaction failed with errors: ${r.errors.map(a=>a.toString()).join(", ")}`);if(r.objectChanges){for(let a of r.objectChanges)if(a.type==="created"&&a.objectType.includes("SupplierCap"))return a.objectId}return null}catch(e){throw new Error(`Failed to create Supplier Cap: ${e.message||e}`)}}async createSupplyReferral(e){try{let t=new Transaction;t.setSender(this.address);let r=d[e];t.moveCall({target:`${g.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,arguments:[t.sharedObjectRef({objectId:r.address,initialSharedVersion:r.initialVersion,mutable:!0}),t.object(g.MARGIN_REGISTRY_ID),t.object.clock()],typeArguments:[r.coinType]});let a=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:t,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(a.errors&&a.errors.length>0)throw new Error(`Transaction failed with errors: ${a.errors.map(o=>o.toString()).join(", ")}`);if(a.objectChanges){for(let o of a.objectChanges)if(o.type==="created"&&o.objectType.includes("SupplyReferral"))return o.objectId}return null}catch(t){throw new Error(`Failed to create Supply Referral: ${t.message||t}`)}}async supplyToMarginPool(e,t,r){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let a=new Transaction;a.setSender(this.address);let o=a.object(this.supplierCapId);a.add(this.marginPoolContract.supplyToMarginPool(e,o,t,r));let{errors:i}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:a,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(i&&i.length>0)throw new Error(`Transaction failed with errors: ${i.map(n=>n.toString()).join(", ")}`);return !0}catch(a){throw new Error(`Failed to supply to margin pool: ${a.message||a}`)}}async withdrawFromMarginPool(e,t){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let r=new Transaction,a=r.object(this.supplierCapId),i=this.marginPoolContract.withdrawFromMarginPool(e,a,t)(r);r.transferObjects([i],this.address);let{errors:n}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0},requestType:"WaitForLocalExecution"});if(n&&n.length>0)throw new Error(`Transaction failed with errors: ${n.map(l=>l.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw from margin pool: ${r.message||r}`)}}async withdrawReferralFees(e,t){try{let r=new Transaction;r.add(this.marginPoolContract.withdrawReferralFees(e,t));let{errors:a}=await this.suiClient.signAndExecuteTransaction({signer:this.keypair,transaction:r,options:{showEffects:!0,showObjectChanges:!0,showBalanceChanges:!0},requestType:"WaitForLocalExecution"});if(a&&a.length>0)throw new Error(`Transaction failed with errors: ${a.map(o=>o.toString()).join(", ")}`);return !0}catch(r){throw new Error(`Failed to withdraw referral fees: ${r.message||r}`)}}async getBalance(e){try{if(!this.supplierCapId)throw new Error("Supplier Cap not initialized. Call initialize() first.");let t=new Transaction;t.add(this.marginPoolContract.userSupplyAmount(e,this.supplierCapId));let r=await this.suiClient.devInspectTransactionBlock({sender:this.address,transactionBlock:t}),a=0;if(r&&r.results&&r.results[0]&&r.results[0].returnValues){let p=r.results[0].returnValues[0];if(p&&p[0]){let f=Buffer.from(p[0]).readBigUInt64LE(),R=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar;a=Number(f)/R;}}let o=e==="SUI"?c.SUI.type:c.DBUSDC.type,i=await this.suiClient.getBalance({owner:this.address,coinType:o}),n=e==="SUI"?c.SUI.scalar:c.DBUSDC.scalar,l=Number(i.totalBalance)/n;return {userSupplyAmount:a,walletBalance:l}}catch(t){throw new Error(`Failed to get balance: ${t.message||t}`)}}getSupplierCapId(){return this.supplierCapId}getAddress(){return this.address}};var C=(s,e)=>s*e/BigInt(FLOAT_SCALAR),u=s=>Number(s)/FLOAT_SCALAR;var V=new Set(b),H=s=>V.has(s),E=class{constructor(e="testnet",t="",r=new SuiClient({url:getFullnodeUrl(e)}),a){this.suiClient=r;this.dbConfig=a??new DeepBookConfig({env:e,address:t,coins:c,pools:S,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}#e(e,t,r,a){if(H(t)){let o=this.marginPoolContract[t];if(a==null)throw new Error(`supplierCap is required for '${t}'.`);e.add(o(r,a));}else {let o=this.marginPoolContract[t];e.add(o(r));}}#r(e,t){let r=e.results;return r?t.reduce((a,o,i)=>{let n=r[i]?.returnValues?.[0]?.[0];if(!n)return a;let l=bcs[T[o]];return a[o]=l.parse(new Uint8Array(n)),a},{}):{}}#a(e,t){let r=this.dbConfig.getCoin(t),a={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,highKink:0,baseBorrowApr:0,borrowAprOnHighKink:0,maxBorrowApr:0,supplyApr:0,utilizationRate:0,...r};if(!r)return a;let o=new Set(["interestRate","maxUtilizationRate","protocolSpread"]);for(let[i,n]of Object.entries(e))i==="lastUpdateTimestamp"?a[i]=Number(n):o.has(i)?a[i]=new BigNumber(n).dividedBy(FLOAT_SCALAR).toNumber():a[i]=new BigNumber(n).dividedBy(r.scalar).toNumber();return a}#t(e,t){let r=BigInt(t.base_rate),a=BigInt(t.base_slope),o=BigInt(t.excess_slope),i=BigInt(t.optimal_utilization);return e<i?r+C(e,a):r+C(i,a)+C(e-i,o)}#o(e,t,r,a){let o=BigInt(e.optimal_utilization),i=BigInt(t.max_utilization_rate),n=this.#t(o,e),l=this.#t(i,e),p=BigInt(BigNumber(r.total_borrow).dividedBy(r.total_supply).shiftedBy(9).decimalPlaces(0).toString()),f=C(C(BigInt(a),p),BigInt(FLOAT_SCALAR)-BigInt(t.protocol_spread));return {raw:{baseBorrowApr:e.base_rate,highKink:o,borrowAprOnHighKink:n,maxBorrowApr:l,supplyApr:f,utilizationRate:p},normalized:{baseBorrowApr:u(BigInt(e.base_rate)),highKink:u(o),borrowAprOnHighKink:u(n),maxBorrowApr:u(l),supplyApr:u(f),utilizationRate:u(p)}}}async#i(e,t){let{address:r}=this.dbConfig.getMarginPool(e),o=((await this.suiClient.getObject({id:r,options:{showContent:true}})).data?.content).fields,i=o.config.fields,n=i.interest_config.fields,l=i.margin_pool_config.fields,p=o.state.fields,{normalized:f}=this.#o(n,l,p,t);return f}async getPoolParameters(e,t,r=new Transaction,a=true){if(_.forEach(p=>this.#e(r,p,e)),t&&b.forEach(p=>this.#e(r,p,e,t)),!a)return r;let o=[..._,...b],i=await this.suiClient.devInspectTransactionBlock({transactionBlock:r,sender:this.dbConfig.address}),n=this.#a(this.#r(i,o),e),l=await this.#i(e,n.interestRate*FLOAT_SCALAR);return {...n,...l}}};export{E as DeepBookMarginPool,P 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,19 +1,18 @@
|
|
|
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
|
-
|
|
11
|
-
import { NetworkType } from './types';
|
|
12
|
-
import { BigNumber } from 'bignumber.js';
|
|
10
|
+
MarginPoolParamKey,
|
|
11
|
+
MarginPoolWithSupplierCapParamKey,
|
|
12
|
+
} from '../margin-pool-config';
|
|
13
13
|
import { mul, normalize } from '../utils/math';
|
|
14
|
+
import { NetworkType } from './types';
|
|
14
15
|
|
|
15
|
-
type MarginPoolParamKey = (typeof MARGIN_POOL_PARAM_KEYS)[number];
|
|
16
|
-
type MarginPoolWithSupplierCapParamKey = (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number];
|
|
17
16
|
type InterestConfig = {
|
|
18
17
|
highKink: number;
|
|
19
18
|
baseBorrowApr: number;
|
|
@@ -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,7 +208,7 @@ export class DeepBookMarginPool {
|
|
|
207
208
|
lastUpdateTimestamp: 0,
|
|
208
209
|
userSupplyShares: 0,
|
|
209
210
|
userSupplyAmount: 0,
|
|
210
|
-
decimals:
|
|
211
|
+
decimals: this.dbConfig.getCoin(coinKey).scalar.toString().length - 1,
|
|
211
212
|
highKink: 0,
|
|
212
213
|
baseBorrowApr: 0,
|
|
213
214
|
borrowAprOnHighKink: 0,
|
|
@@ -295,9 +296,7 @@ export class DeepBookMarginPool {
|
|
|
295
296
|
// raw 1e9-scaled values
|
|
296
297
|
raw: {
|
|
297
298
|
baseBorrowApr: interestConfig.base_rate,
|
|
298
|
-
// midKink, // utilization at kink1
|
|
299
299
|
highKink, // utilization at kink2
|
|
300
|
-
// midBorrowApr, // APR at midKink
|
|
301
300
|
borrowAprOnHighKink, // APR at highKink
|
|
302
301
|
maxBorrowApr, // APR at U = 1.0
|
|
303
302
|
supplyApr,
|
|
@@ -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: {
|
|
@@ -184,8 +149,8 @@ export class DeepBookMarginToolkit {
|
|
|
184
149
|
// Direct moveCall to get the returned object reference
|
|
185
150
|
// Based on SDK source: margin_pool::mint_supplier_cap
|
|
186
151
|
const supplierCap = tx.moveCall({
|
|
187
|
-
target: `${
|
|
188
|
-
arguments: [tx.object(
|
|
152
|
+
target: `${this.supplierCapPackageId}::margin_pool::mint_supplier_cap`,
|
|
153
|
+
arguments: [tx.object(this.dbConfig.MARGIN_REGISTRY_ID), tx.object.clock()],
|
|
189
154
|
});
|
|
190
155
|
|
|
191
156
|
// Transfer the created Supplier Cap to the sender
|
|
@@ -198,7 +163,6 @@ export class DeepBookMarginToolkit {
|
|
|
198
163
|
showEffects: true,
|
|
199
164
|
showObjectChanges: true,
|
|
200
165
|
},
|
|
201
|
-
requestType: 'WaitForLocalExecution',
|
|
202
166
|
});
|
|
203
167
|
|
|
204
168
|
if (result.errors && result.errors.length > 0) {
|
|
@@ -233,22 +197,21 @@ export class DeepBookMarginToolkit {
|
|
|
233
197
|
tx.setSender(this.address);
|
|
234
198
|
|
|
235
199
|
// Get margin pool configuration
|
|
236
|
-
const marginPool =
|
|
200
|
+
const marginPool = this.dbConfig.getMarginPool(coin);
|
|
201
|
+
if (!marginPool) {
|
|
202
|
+
throw new Error(`Margin pool configuration not found for coin: ${coin}`);
|
|
203
|
+
}
|
|
237
204
|
|
|
238
205
|
// Use the initialVersion from config as the initial_shared_version
|
|
239
206
|
// Margin pools are shared objects on Sui
|
|
240
207
|
tx.moveCall({
|
|
241
|
-
target: `${
|
|
208
|
+
target: `${this.dbConfig.MARGIN_PACKAGE_ID}::margin_pool::mint_supply_referral`,
|
|
242
209
|
arguments: [
|
|
243
|
-
tx.
|
|
244
|
-
|
|
245
|
-
initialSharedVersion: marginPool.initialVersion,
|
|
246
|
-
mutable: true,
|
|
247
|
-
}),
|
|
248
|
-
tx.object(TESTNET_PACKAGES.MARGIN_REGISTRY_ID),
|
|
210
|
+
tx.object(marginPool.address),
|
|
211
|
+
tx.object(this.dbConfig.MARGIN_REGISTRY_ID),
|
|
249
212
|
tx.object.clock(),
|
|
250
213
|
],
|
|
251
|
-
typeArguments: [marginPool.
|
|
214
|
+
typeArguments: [marginPool.type],
|
|
252
215
|
});
|
|
253
216
|
|
|
254
217
|
const result = await this.suiClient.signAndExecuteTransaction({
|
|
@@ -258,7 +221,6 @@ export class DeepBookMarginToolkit {
|
|
|
258
221
|
showEffects: true,
|
|
259
222
|
showObjectChanges: true,
|
|
260
223
|
},
|
|
261
|
-
requestType: 'WaitForLocalExecution',
|
|
262
224
|
});
|
|
263
225
|
|
|
264
226
|
if (result.errors && result.errors.length > 0) {
|
|
@@ -314,7 +276,6 @@ export class DeepBookMarginToolkit {
|
|
|
314
276
|
showEffects: true,
|
|
315
277
|
showObjectChanges: true,
|
|
316
278
|
},
|
|
317
|
-
requestType: 'WaitForLocalExecution',
|
|
318
279
|
});
|
|
319
280
|
|
|
320
281
|
if (errors && errors.length > 0) {
|
|
@@ -363,7 +324,6 @@ export class DeepBookMarginToolkit {
|
|
|
363
324
|
showEffects: true,
|
|
364
325
|
showObjectChanges: true,
|
|
365
326
|
},
|
|
366
|
-
requestType: 'WaitForLocalExecution',
|
|
367
327
|
});
|
|
368
328
|
|
|
369
329
|
if (errors && errors.length > 0) {
|
|
@@ -399,7 +359,6 @@ export class DeepBookMarginToolkit {
|
|
|
399
359
|
showObjectChanges: true,
|
|
400
360
|
showBalanceChanges: true,
|
|
401
361
|
},
|
|
402
|
-
requestType: 'WaitForLocalExecution',
|
|
403
362
|
});
|
|
404
363
|
|
|
405
364
|
if (errors && errors.length > 0) {
|
|
@@ -440,19 +399,19 @@ export class DeepBookMarginToolkit {
|
|
|
440
399
|
if (supplyData && supplyData[0]) {
|
|
441
400
|
const rawAmount = Buffer.from(supplyData[0]).readBigUInt64LE();
|
|
442
401
|
// Convert from smallest unit to human-readable | 從最小單位轉換為人類可讀
|
|
443
|
-
const scalar =
|
|
402
|
+
const scalar = this.dbConfig.getCoin(coin).scalar;
|
|
444
403
|
userSupplyAmount = Number(rawAmount) / scalar;
|
|
445
404
|
}
|
|
446
405
|
}
|
|
447
406
|
|
|
448
407
|
// Query wallet balance | 查詢錢包餘額
|
|
449
|
-
const coinType =
|
|
408
|
+
const coinType = this.dbConfig.getCoin(coin).type;
|
|
450
409
|
const balance = await this.suiClient.getBalance({
|
|
451
410
|
owner: this.address,
|
|
452
411
|
coinType,
|
|
453
412
|
});
|
|
454
413
|
|
|
455
|
-
const scalar =
|
|
414
|
+
const scalar = this.dbConfig.getCoin(coin).scalar;
|
|
456
415
|
const walletBalance = Number(balance.totalBalance) / scalar;
|
|
457
416
|
|
|
458
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
|
/**
|
package/src/testnet-config.ts
DELETED
|
@@ -1,258 +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
|
-
* Document | 文件: https://docs.google.com/document/d/1UQw2JZ3X3UN4641_WkvqdCehP7jDPgFmgt2KFti6bjs
|
|
5
|
-
* Last updated | 最後更新: 2025-11-20
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// Package IDs
|
|
10
|
-
// ============================================================================
|
|
11
|
-
|
|
12
|
-
export const TESTNET_PACKAGES = {
|
|
13
|
-
DEEPBOOK_PACKAGE_ID: '0xfb28c4cbc6865bd1c897d26aecbe1f8792d1509a20ffec692c800660cbec6982',
|
|
14
|
-
REGISTRY_ID: '0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1',
|
|
15
|
-
DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb',
|
|
16
|
-
MARGIN_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209',
|
|
17
|
-
MARGIN_INITIAL_PACKAGE_ID: '0x7f2d8f15343f210e813595a8798d6197d152061d0a35be938372f4b1cd66f209', // VERSION 1 - for original events
|
|
18
|
-
MARGIN_REGISTRY_ID: '0x31b9086767e9b5925cb29414ea623a7705b5600d9594d88c17c7a011cb499ab4',
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
// ============================================================================
|
|
22
|
-
// Coin Configuration | 代幣配置
|
|
23
|
-
// ============================================================================
|
|
24
|
-
|
|
25
|
-
export const TESTNET_COINS = {
|
|
26
|
-
DEEP: {
|
|
27
|
-
address: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8',
|
|
28
|
-
type: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
|
|
29
|
-
scalar: 1_000_000,
|
|
30
|
-
decimals: 6,
|
|
31
|
-
},
|
|
32
|
-
SUI: {
|
|
33
|
-
address: '0x0000000000000000000000000000000000000000000000000000000000000002',
|
|
34
|
-
type: '0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI',
|
|
35
|
-
scalar: 1_000_000_000,
|
|
36
|
-
decimals: 9,
|
|
37
|
-
},
|
|
38
|
-
DBUSDC: {
|
|
39
|
-
address: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7',
|
|
40
|
-
type: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC',
|
|
41
|
-
scalar: 1_000_000,
|
|
42
|
-
decimals: 6,
|
|
43
|
-
},
|
|
44
|
-
DBUSDT: {
|
|
45
|
-
address: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7',
|
|
46
|
-
type: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT',
|
|
47
|
-
scalar: 1_000_000,
|
|
48
|
-
decimals: 6,
|
|
49
|
-
},
|
|
50
|
-
WAL: {
|
|
51
|
-
address: '0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76',
|
|
52
|
-
type: '0x9ef7676a9f81937a52ae4b2af8d511a28a0b080477c0c2db40b0ab8882240d76::wal::WAL',
|
|
53
|
-
scalar: 1_000_000_000,
|
|
54
|
-
decimals: 9,
|
|
55
|
-
},
|
|
56
|
-
} as const;
|
|
57
|
-
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// Trading Pool Configuration | 交易池配置
|
|
60
|
-
// ============================================================================
|
|
61
|
-
|
|
62
|
-
export const TESTNET_POOLS = {
|
|
63
|
-
DEEP_SUI: {
|
|
64
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
65
|
-
address: '0x48c95963e9eac37a316b7ae04a0deb761bcdcc2b67912374d6036e7f0e9bae9f',
|
|
66
|
-
baseCoin: 'DEEP',
|
|
67
|
-
quoteCoin: 'SUI',
|
|
68
|
-
lotSize: 1_000_000, // 1 DEEP (6 decimals) | 1 DEEP(6 位小數)
|
|
69
|
-
tickSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
70
|
-
},
|
|
71
|
-
SUI_DBUSDC: {
|
|
72
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
73
|
-
address: '0x1c19362ca52b8ffd7a33cee805a67d40f31e6ba303753fd3a4cfdfacea7163a5',
|
|
74
|
-
baseCoin: 'SUI',
|
|
75
|
-
quoteCoin: 'DBUSDC',
|
|
76
|
-
lotSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
77
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
78
|
-
},
|
|
79
|
-
DEEP_DBUSDC: {
|
|
80
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
81
|
-
address: '0xe86b991f8632217505fd859445f9803967ac84a9d4a1219065bf191fcb74b622',
|
|
82
|
-
baseCoin: 'DEEP',
|
|
83
|
-
quoteCoin: 'DBUSDC',
|
|
84
|
-
lotSize: 1_000_000, // 1 DEEP (6 decimals) | 1 DEEP(6 位小數)
|
|
85
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
86
|
-
},
|
|
87
|
-
DBUSDT_DBUSDC: {
|
|
88
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
89
|
-
address: '0x83970bb02e3636efdff8c141ab06af5e3c9a22e2f74d7f02a9c3430d0d10c1ca',
|
|
90
|
-
baseCoin: 'DBUSDT',
|
|
91
|
-
quoteCoin: 'DBUSDC',
|
|
92
|
-
lotSize: 1_000_000, // 1 DBUSDT (6 decimals) | 1 DBUSDT(6 位小數)
|
|
93
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
94
|
-
},
|
|
95
|
-
WAL_DBUSDC: {
|
|
96
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
97
|
-
address: '0xeb524b6aea0ec4b494878582e0b78924208339d360b62aec4a8ecd4031520dbb',
|
|
98
|
-
baseCoin: 'WAL',
|
|
99
|
-
quoteCoin: 'DBUSDC',
|
|
100
|
-
lotSize: 1_000_000, // 0.001 WAL (9 decimals) | 0.001 WAL(9 位小數)
|
|
101
|
-
tickSize: 1_000, // 0.001 DBUSDC (6 decimals) | 0.001 DBUSDC(6 位小數)
|
|
102
|
-
},
|
|
103
|
-
WAL_SUI: {
|
|
104
|
-
// Actual pool address on testnet (verified 2025-11-11) | 實際存在於 testnet 上的 Pool 地址(2025-11-11 驗證)
|
|
105
|
-
address: '0x8c1c1b186c4fddab1ebd53e0895a36c1d1b3b9a77cd34e607bef49a38af0150a',
|
|
106
|
-
baseCoin: 'WAL',
|
|
107
|
-
quoteCoin: 'SUI',
|
|
108
|
-
lotSize: 1_000_000, // 0.001 WAL (9 decimals) | 0.001 WAL(9 位小數)
|
|
109
|
-
tickSize: 1_000_000, // 0.001 SUI (9 decimals) | 0.001 SUI(9 位小數)
|
|
110
|
-
},
|
|
111
|
-
} as const;
|
|
112
|
-
|
|
113
|
-
// ============================================================================
|
|
114
|
-
// Margin Pool Configuration | Margin 池配置
|
|
115
|
-
// ============================================================================
|
|
116
|
-
|
|
117
|
-
export const TESTNET_MARGIN_POOLS = {
|
|
118
|
-
SUI: {
|
|
119
|
-
// Actual margin pool address on testnet (updated 2025-11-20) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-20 更新)
|
|
120
|
-
address: '0xe620d6a5390e57e88baff18af89383130d4210eb496a024edcd62f270a655af7',
|
|
121
|
-
coinType: '0x2::sui::SUI',
|
|
122
|
-
initialVersion: 658877881,
|
|
123
|
-
supplyCap: 1000000, // 供應上限 | Supply cap
|
|
124
|
-
maxUtilizationRate: 0.9, // 最大使用率 | Max utilization rate
|
|
125
|
-
referralSpread: 0.1, // Referral spread
|
|
126
|
-
minBorrow: 0.1, // 最小借款額 | Min borrow amount
|
|
127
|
-
},
|
|
128
|
-
DBUSDC: {
|
|
129
|
-
// Actual margin pool address on testnet (updated 2025-11-20) | 實際存在於 testnet 上的 Margin Pool 地址(2025-11-20 更新)
|
|
130
|
-
address: '0xfd0dc290a120ad6c534507614d4dc0b2e78baab649c35bfacbaec2ce18140b69',
|
|
131
|
-
coinType: '0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC',
|
|
132
|
-
initialVersion: 658877215,
|
|
133
|
-
supplyCap: 1000000, // 供應上限 | Supply cap
|
|
134
|
-
maxUtilizationRate: 0.95, // 最大使用率 | Max utilization rate
|
|
135
|
-
referralSpread: 0.1, // Referral spread
|
|
136
|
-
minBorrow: 0.1, // 最小借款額 | Min borrow amount
|
|
137
|
-
},
|
|
138
|
-
} as const;
|
|
139
|
-
|
|
140
|
-
// ============================================================================
|
|
141
|
-
// Risk Parameters Configuration | 風險參數配置
|
|
142
|
-
// ============================================================================
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Risk parameters for trading pairs | 交易對的風險參數
|
|
146
|
-
*/
|
|
147
|
-
export const TESTNET_RISK_PARAMETERS = {
|
|
148
|
-
SUI_DBUSDC: {
|
|
149
|
-
minWithdrawRiskRatio: 2, // 最小提款風險比率 | Min withdraw risk ratio
|
|
150
|
-
minBorrowRiskRatio: 1.25, // 最小借款風險比率 | Min borrow risk ratio
|
|
151
|
-
liquidationRiskRatio: 1.1, // 清算風險比率 | Liquidation risk ratio
|
|
152
|
-
targetLiquidationRiskRatio: 1.25, // 目標清算風險比率 | Target liquidation risk ratio
|
|
153
|
-
userLiquidationReward: 0.03, // 用戶清算獎勵 | User liquidation reward (3%)
|
|
154
|
-
poolLiquidationReward: 0.02, // 池子清算獎勵 | Pool liquidation reward (2%)
|
|
155
|
-
},
|
|
156
|
-
} as const;
|
|
157
|
-
|
|
158
|
-
// ============================================================================
|
|
159
|
-
// Pyth Oracle Configuration | Pyth Oracle 配置
|
|
160
|
-
// ============================================================================
|
|
161
|
-
|
|
162
|
-
export const TESTNET_PYTH = {
|
|
163
|
-
pythStateId: '0x243759059f4c3111179da5878c12f68d612c21a8d54d85edc86164bb18be1c7c',
|
|
164
|
-
wormholeStateId: '0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790',
|
|
165
|
-
} as const;
|
|
166
|
-
|
|
167
|
-
// ============================================================================
|
|
168
|
-
// Pool Keys (SDK shorthand) | Pool Keys(SDK 使用的簡寫)
|
|
169
|
-
// ============================================================================
|
|
170
|
-
|
|
171
|
-
export const TESTNET_POOL_KEYS = [
|
|
172
|
-
'DEEP_SUI',
|
|
173
|
-
'SUI_DBUSDC',
|
|
174
|
-
'DEEP_DBUSDC',
|
|
175
|
-
'DBUSDT_DBUSDC',
|
|
176
|
-
'WAL_DBUSDC',
|
|
177
|
-
'WAL_SUI',
|
|
178
|
-
] as const;
|
|
179
|
-
|
|
180
|
-
export type TestnetPoolKey = (typeof TESTNET_POOL_KEYS)[number];
|
|
181
|
-
|
|
182
|
-
// ============================================================================
|
|
183
|
-
// Margin Pool Contract Parameter Keys | Margin Pool 合約參數鍵
|
|
184
|
-
// ============================================================================
|
|
185
|
-
|
|
186
|
-
export const MARGIN_POOL_PARAM_KEYS = [
|
|
187
|
-
'supplyCap',
|
|
188
|
-
'maxUtilizationRate',
|
|
189
|
-
'protocolSpread',
|
|
190
|
-
'minBorrow',
|
|
191
|
-
'interestRate',
|
|
192
|
-
'totalSupply',
|
|
193
|
-
'supplyShares',
|
|
194
|
-
'totalBorrow',
|
|
195
|
-
'borrowShares',
|
|
196
|
-
'lastUpdateTimestamp',
|
|
197
|
-
] as const;
|
|
198
|
-
|
|
199
|
-
export const MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS = [
|
|
200
|
-
'userSupplyShares',
|
|
201
|
-
'userSupplyAmount',
|
|
202
|
-
] as const;
|
|
203
|
-
|
|
204
|
-
export const MARGIN_POOL_PARAM_KEY_STRUCT_MAP = {
|
|
205
|
-
supplyCap: 'U64',
|
|
206
|
-
maxUtilizationRate: 'U64',
|
|
207
|
-
protocolSpread: 'U64',
|
|
208
|
-
minBorrow: 'U64',
|
|
209
|
-
interestRate: 'U64',
|
|
210
|
-
totalSupply: 'U64',
|
|
211
|
-
supplyShares: 'U64',
|
|
212
|
-
totalBorrow: 'U64',
|
|
213
|
-
borrowShares: 'U64',
|
|
214
|
-
lastUpdateTimestamp: 'U64',
|
|
215
|
-
userSupplyShares: 'U64',
|
|
216
|
-
userSupplyAmount: 'U64',
|
|
217
|
-
} as Record<
|
|
218
|
-
(typeof MARGIN_POOL_PARAM_KEYS)[number] | (typeof MARGIN_POOL_W_SUPPLIER_CAP_PARAM_KEYS)[number],
|
|
219
|
-
'U64'
|
|
220
|
-
>;
|
|
221
|
-
|
|
222
|
-
// ============================================================================
|
|
223
|
-
// Utility Functions | 輔助函數
|
|
224
|
-
// ============================================================================
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Get full pool configuration | 取得 Pool 的完整配置
|
|
228
|
-
*/
|
|
229
|
-
export function getPoolConfig(poolKey: TestnetPoolKey) {
|
|
230
|
-
return TESTNET_POOLS[poolKey];
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Get full coin configuration | 取得代幣的完整配置
|
|
235
|
-
*/
|
|
236
|
-
export function getCoinConfig(coinSymbol: keyof typeof TESTNET_COINS) {
|
|
237
|
-
return TESTNET_COINS[coinSymbol];
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Format coin amount (convert from smallest unit to human-readable) | 格式化代幣數量(從最小單位轉換為人類可讀)
|
|
242
|
-
*/
|
|
243
|
-
export function formatCoinAmount(
|
|
244
|
-
amount: number | bigint,
|
|
245
|
-
coinSymbol: keyof typeof TESTNET_COINS
|
|
246
|
-
): string {
|
|
247
|
-
const config = TESTNET_COINS[coinSymbol];
|
|
248
|
-
const value = Number(amount) / config.scalar;
|
|
249
|
-
return value.toFixed(config.decimals);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Parse coin amount (convert from human-readable to smallest unit) | 解析代幣數量(從人類可讀轉換為最小單位)
|
|
254
|
-
*/
|
|
255
|
-
export function parseCoinAmount(amount: number, coinSymbol: keyof typeof TESTNET_COINS): number {
|
|
256
|
-
const config = TESTNET_COINS[coinSymbol];
|
|
257
|
-
return Math.floor(amount * config.scalar);
|
|
258
|
-
}
|