交易与交换
本节介绍如何在 DeepBook 协议上构建和执行交易。
1. 创建池
在 DeepBook 中创建池时,我们需要指定 BaseAsset、QuoteAsset、tick_size 和 lot_size。我们还需要支付一个交易费用(以 SUI 代币计)来创建池。
在 Move 合约中调用 create_pool
的函数签名
/// Parameters expected by this func
///
/// 0. `[registry]` Object ID refers to the pool registry
/// 1. `[tick_size]` Minimal Price Change Accuracy of this pool
/// 2. `[lot_size]` Minimal lot Change Accuracy of this pool
/// 3. `[coin]` Object ID of the sui coin, to pay fee for create pool (100 MIST sui charged)
/// Returns the AccountCap
public fun create_pool<BaseAsset, QuoteAsset, SUI>(
registry: &mut Registry,
tick_size: u64,
lot_size: u64,
coin: Coin<SUI>,
ctx: &mut TxContext,
)
调用 create_pool
的 Typescript SDK
/**
* @description: Create pool for trading pair
* @param tickSize: Minimal Price Change Accuracy of this pool
* @param lotSize: Minimal Lot Change Accuracy of this pool
* @param token1: Full coin type(recommend) or coin name of the base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::usdt::USDT" for example
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId and registryId should be set in configs.json, if it is not set, the transaction will fail.
*/
public createPool(
tickSize: number,
lotSize: number,
token1: string,
token2: string,
overrides: Overrides = new Overrides(),
): TransactionBlock {
const coinType1 = convertToTokenType(token1, this.records);
const coinType2 = convertToTokenType(token2, this.records);
// we have to split sui as a param into the code
const [coin] = overrides.txb.splitCoins(overrides.txb.gas, [overrides.txb.pure(200000000)]);
overrides.txb.moveCall({
typeArguments: [coinType1, coinType2, '0x2::sui::SUI'],
target: `${this.configs.swapPackageId}::clob::create_pool`,
arguments: [
overrides.txb.object(String(this.configs.registryId)),
overrides.txb.pure(tickSize),
overrides.txb.pure(lotSize),
coin,
],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
2. 创建订单
DeepBook 中有两种类型的订单,限价订单
和 市价订单
。
对于限价订单,用户在与特定池的首次交互时需要创建一个托管账户并将资产存入池中。
2.1 限价订单
2.1.1 限价订单类型
有四种限价订单上可以设置的限制类型:
NO_RESTRICTION
:0
尽可能在当前事务中以吃单方式填充尽量多的数量,将剩余部分注入为挂单。
IMMEDIATE_OR_CANCEL
:1
尽可能在当前事务中以吃单方式填充尽量多的数量,取消剩余的订单。
FILL_OR_KILL
:2
仅在整个订单大小可以在当前事务中以吃单方式填充时填充。否则,中止整个事务。
POST_OR_ABORT
:3
仅在整个订单大小可以在当前事务中以挂单方式发布到订单簿时继续。否则,中止整个事务。
2.1.2 托管账户
在下限价订单之前,交易员需要拥有一个已存入资产的托管账户。交易员可以通过调用 create_account
来生成一个托管账户,并将获得一个授权其访问托管账户的 AccountCap 对象。请注意,AccountCap 对象将是托管账户的密钥,如果将此对象转移给其他人,新所有者将可以访问托管账户中的所有资金和订单。
在 Move 合约中调用 create_account
的函数签名
/// Parameters expected by this func
///
/// 0. `[ctx]` Information about the current transaction.
/// Returns the AccountCap
public fun create_account(ctx: &mut TxContext): AccountCap {
mint_account_cap(ctx)
}
调用 create_account
的 Typescript SDK
/**
* @description: Create and Transfer custodian account to user
* @param currentAddress: current user address
* @param needsTransfer: if needs transfer the cap to user, default true, if false, which means we need to use the cap in the next transaction.
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
async createAccount(
currentAddress: string,
needsTransfer: boolean = true,
overrides: Overrides = new Overrides(),
): Promise<TransactionBlock | TransactionArgument> {
let [cap] = overrides.txb.moveCall({
typeArguments: [],
target: `${this.configs.swapPackageId}::clob::create_account`,
arguments: [],
});
if (!needsTransfer) {
return cap;
} else {
overrides.txb.transferObjects([cap], overrides.txb.pure(currentAddress));
overrides.txb.setSenderIfNotSet(currentAddress);
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
}
2.1.3 存入资产
交易员可以将基础/报价资产存入其托管账户,以便稍后用于下限价订单。我们分别提供存入基础资产和报价资产的两个函数。
在 Move 合约中存入基础资产的函数签名
/// Parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool containing the trading pair
/// 1. `[coin]` Object ID of the base asset coin
/// 2. `[account_cap]` Object ID of the account_cap authorizing the
/// accessilility to the escrow account
/// Returns none
public fun deposit_base<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
coin: Coin<BaseAsset>,
account_cap: &AccountCap
)
调用 deposit_base
的 Typescript SDK
/**
* @description: Deposit base asset into custodian account
* @param accountCap: Object id of Account Capacity under user address, created after invoking createAccount
* @param token1: Full coin type(recommend) or coin name of the base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::usdt::USDT" for example
* @param coin: Object id of coin to deposit
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public depositBase(
token1: string,
token2: string,
coin: string | TransactionArgument,
accountCap: string,
overrides: Overrides = new Overrides(),
): TransactionBlock {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [tokenType1, tokenType2],
target: `${this.configs.swapPackageId}::clob::deposit_base`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
typeof coin == 'string' ? overrides.txb.object(coin) : coin,
overrides.txb.object(String(accountCap)),
],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
在合约中调用 deposit_quote
的函数签名
/// Parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool containing the trading pair
/// 1. `[coin]` Object ID of the coin
/// 2. `[account_cap]` Object ID of the account_cap authorizing the
/// accessilility to the escrow account
/// Returns none
public fun deposit_quote<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
coin: Coin<QuoteAsset>,
account_cap: &AccountCap
)
调用 deposit_quote
的 Typescript SDK
/**
* @description: Deposit quote asset into custodian account
* @param accountCap: Object id of Account Capacity under user address, created after invoking createAccount
* @param token1: Full coin type(recommend) or coin name of the base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::usdt::USDT" for example
* @param coin: Object id of coin to deposit
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public depositQuote(
token1: string,
token2: string,
coin: string | TransactionArgument,
accountCap: string | TransactionArgument,
overrides: Overrides = new Overrides(),
): TransactionBlock {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [tokenType1, tokenType2],
target: `${this.configs.swapPackageId}::clob::deposit_quote`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
typeof coin == 'string' ? overrides.txb.object(coin) : coin,
typeof accountCap == 'string' ? overrides.txb.object(String(accountCap)) : accountCap,
],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
2.1.4 下限价订单
确保你拥有足够的基础/报价资产以进行交易的托管账户。现在,你可以使用以下函数在 DeepBook 上下限价订单:
在 Move 合约中下限价订单的函数签名
/// Parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool of the trading pair
/// 1. `[price]` Price of the placed limit order to sell or buy
/// 2. `[quantity]` Quantity of the base asset want to sell or buy
/// 3. `[is_bid]` Flag indicate sell base asset out or buy base asset in
/// 4. `[expire_timestamp]` Expire timestamp in ms in absolute value inclusive., if exceeded, order turn invalid
/// 5. `[restriction]` Object ID of the pool containing the trading pair
/// 6. `[clock]` Object ID of global system clock
/// 7. `[account_cap]` Object ID of the account_cap authorizing the
/// accessilility to the escrow account
/// 8. `[ctx]` Information about the current transaction.
/// Returns (base quantity filled, quote quantity filled, whether a maker order is being placed, order id of the maker order)
public fun place_limit_order<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
price: u64,
quantity: u64,
is_bid: bool,
expire_timestamp: u64,
restriction: u8,
clock: &Clock,
account_cap: &AccountCap,
ctx: &mut TxContext
): (u64, u64, bool, u64)
调用 place_limit_order
的 Typescript SDK
/**
* @description: place a limit order
* @param token1: Full coin type(recommend) of base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param price: price of the limit order
* @param quantity: quantity of the limit order in BASE ASSET.
* @param isBid: true for buying base with quote, false for selling base for quote
* @param expireTimestamp: expire timestamp of the limit order
* @param restriction restrictions on limit orders, explain in doc for more details
* @param accountCap: account cap for the limit order
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public placeLimitOrder(
token1: string,
token2: string,
price: number,
quantity: number,
isBid: boolean,
expireTimestamp: number,
restriction: number,
accountCap: string | TransactionArgument,
overrides: Overrides = new Overrides(),
): TransactionBlock {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [tokenType1, tokenType2],
target: `${this.configs.swapPackageId}::clob::place_limit_order`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
overrides.txb.pure(Math.floor(price * F)),
overrides.txb.pure(quantity),
overrides.txb.pure(isBid),
overrides.txb.pure(Math.floor(expireTimestamp)),
overrides.txb.pure(restriction),
overrides.txb.object(normalizeSuiObjectId(this.configs.clock)),
typeof accountCap == "string" ? overrides.txb.object(accountCap) : accountCap,
],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
2.2 下市价订单
下市价订单不需要托管账户。如果订单只能部分成交,则取消剩余的订单。
在 Move 合约中下市价订单的函数签名:
/// Parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool of the trading pair
/// 1. `[quantity]` Quantity of the base asset usr want to buy or sell
/// 2. `[is_bid]` Flag indicates buy or sell, true for buy and false for sell
/// 3. `[base_coin]` Object id of base coin, can be zero balance
/// 4. `[quote_coin]` Object id of the quote coin, can be zero balance
/// 5. `[clock]` Object ID refers to global system clock
/// 6. `[ctx]` Information about the transaction currently being executed.
/// Returns the base asset coin and quote asset coin after transaction
public fun place_market_order<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
quantity: u64,
is_bid: bool,
base_coin: Coin<BaseAsset>,
quote_coin: Coin<QuoteAsset>,
clock: &Clock,
ctx: &mut TxContext,
): (Coin<BaseAsset>, Coin<QuoteAsset>)
调用 place_market_order
的 Typescript SDK
/**
* @description: place a market order
* @param tokenObjectIn: Object id of the token to buy or sell
* @param tokenOut: Full coin type(recommend) or coin name of the asset one want to get "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param amountIn: amount of token to buy or sell
* @param currentAddress: user address
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public async placeMarketOrder(
tokenObjectIn: string,
tokenOut: string,
amountIn: number,
currentAddress: string,
overrides: Overrides = new Overrides(),
): Promise<TransactionBlock> {
// in this case, we assume that the tokenIn--tokenOut always exists.
const tokenTypeOut = convertToTokenType(tokenOut, this.records);
const tokenIn = overrides.txb.object(tokenObjectIn);
const tokenInfo = await this.provider.getObject({
id: tokenObjectIn,
options: {
showType: true,
},
});
const tokenTypeIn = tokenInfo.data.type.split('<')[1].split('>')[0];
let base_coin_ret, quote_coin_ret, amount;
const poolInfo: PoolInfo = getPoolInfoByRecords(tokenTypeIn, tokenTypeOut, this.records);
let _isBid, _tokenIn, _tokenOut, _amount;
if (!poolInfo.needChange) {
_isBid = false;
_tokenIn = tokenIn;
_tokenOut = this.mint(tokenTypeOut, 0, {txb: overrides.txb});
_amount = overrides.txb.object(String(amountIn));
} else {
_isBid = true;
// _tokenIn = this.mint(txb, nextPath, 0)
_tokenOut = tokenIn;
_amount = overrides.txb.object(String(amountIn));
}
if (_isBid) {
// here swap_exact_quote_for_base
[base_coin_ret, quote_coin_ret, amount] = overrides.txb.moveCall({
typeArguments: [
poolInfo.needChange ? tokenTypeOut : tokenTypeIn,
poolInfo.needChange ? tokenTypeIn : tokenTypeOut,
],
target: `${this.configs.swapPackageId}::clob::swap_exact_quote_for_base`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
_amount,
overrides.txb.object(normalizeSuiObjectId(this.configs.clock)),
_tokenOut,
],
});
} else {
// here swap_exact_base_for_quote
[base_coin_ret, quote_coin_ret, amount] = overrides.txb.moveCall({
typeArguments: [
poolInfo.needChange ? tokenTypeOut : tokenTypeIn,
poolInfo.needChange ? tokenTypeIn : tokenTypeOut,
],
target: `${this.configs.swapPackageId}::clob::swap_exact_base_for_quote`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
_amount,
_tokenIn,
_tokenOut,
overrides.txb.object(normalizeSuiObjectId(this.configs.clock)),
],
});
}
overrides.txb.transferObjects([base_coin_ret], overrides.txb.pure(currentAddress));
overrides.txb.transferObjects([quote_coin_ret], overrides.txb.pure(currentAddress));
overrides.txb.setSenderIfNotSet(currentAddress);
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
3. 取消订单
3.1 取消单个订单
取消放置在 CLOB 上的限价订单。如果 order_id 无效或订单不是由交易发送者提交的,则中止。
在 Move 合约中取消单个订单的函数签名。
/// parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool of the trading pair
/// 1. `[order_id]` Order ID of the placed limit order
/// 2. `[account_cap]` Object ID of the account_cap authorizing the
/// accessilility to the escrow account
/// Returns none
public fun cancel_order<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
order_id: u64,
account_cap: &AccountCap
)
调用 cancel_order
的 Typescript SDK
/**
* @description: cancel single order
* @param token1: Full coin type(recommend) of base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param orderId: Order id to cancel corresponding order
* @param accountCap: Object id of account capacity under usr address, created after invoking createAccount
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public async cancelOrder(
token1: string,
token2: string,
orderId: bigint,
accountCap: string,
overrides: Overrides = new Overrides(),
): Promise<TransactionBlock> {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo: PoolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [tokenType1, tokenType2],
target: `${this.configs.swapPackageId}::clob::cancel_order`,
arguments: [
overrides.txb.object(poolInfo.clob),
overrides.txb.pure(String(orderId)),
overrides.txb.object(accountCap),
],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
3.2 取消所有订单
取消某个账户容量下的所有限价订单
在 Move 合约中取消所有订单的函数签名。
/// parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool of the trading pair
/// 1. `[account_cap]` Object ID of the account_cap authorizing the
/// accessilility to the escrow account
/// Returns none
public fun cancel_all_orders<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
account_cap: &AccountCap
)
取消所有订单的 Typescript SDK
/**
* @description: Cancel all limit orders under a certain account capacity
* @param token1: Full coin type(recommend) of base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::usdt::USDT" for example
* @param accountCap: Object id of account capacity under usr address, created after invoking createAccount
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public async cancelAllOrders(
token1: string,
token2: string,
accountCap: string,
overrides: Overrides = new Overrides(),
): Promise<TransactionBlock> {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo: PoolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [token1, token2],
target: `${this.configs.swapPackageId}::clob::cancel_all_orders`,
arguments: [overrides.txb.object(poolInfo.clob), overrides.txb.object(accountCap)],
});
overrides.txb.setGasBudget(overrides.gasBudget);
return overrides.txb;
}
3.3 批量取消订单
取消多个限价订单以节省燃料成本。如果任何订单 id 无效或不是由发送方提交的,则中止。请注意,如果调用者具有相同价格级别的多个订单,并且具有相同价格的订单在向量中分组,则此函数甚至可以进一步降低燃料成本。
例如,如果我们有以下的 order_id 到价格映射,{0: 100., 1: 200., 2: 100., 3: 200.}。将 order_id 分组如 [0, 2, 1, 3] 将使其成为最节省燃料的方式。
批量取消订单的函数签名。
/// parameters expected by this func
///
/// 0. `[pool]` Object ID refers to the pool containing the trading pair
/// 1. `[order_ids]` List of object ID of the pool containing the trading pair
/// 2. `[account_cap]` Object ID of the account_cap authorize the
/// accessilility to the escrow account
/// Returns none
public fun batch_cancel_order<BaseAsset, QuoteAsset>(
pool: &mut Pool<BaseAsset, QuoteAsset>,
order_ids: vector<u64>,
account_cap: &AccountCap
)
取消所有订单的 Typescript SDK
/**
* @description: batch cancel order
* @param token1: Full coin type(recommend) of base asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::wbtc::WBTC" for example
* @param token2: Full coin type(recommend) of quote asset "0x3d0d0ce17dcd3b40c2d839d96ce66871ffb40e1154a8dd99af72292b3d10d7fc::usdt::USDT" for example
* @param orderIds: Order ids to cancel corresponding orders
* @param accountCap: Object id of account capacity under usr address, created after invoking createAccount
* @param overrides: overriders for blockchain params. Like gas budget, transaction block and the provider, default we will set to the max gas budget, and create a new transaction block
* @notice: the packageId should be set in configs.json, if it is not set, the transaction will fail.
*/
public async batchCancelOrder(
token1: string,
token2: string,
orderIds: Uint8Array,
accountCap: string,
overrides: Overrides = new Overrides(),
): Promise<TransactionBlock> {
const tokenType1 = convertToTokenType(token1, this.records);
const tokenType2 = convertToTokenType(token2, this.records);
const poolInfo: PoolInfo = getPoolInfoByRecords(tokenType1, tokenType2, this.records);
overrides.txb.moveCall({
typeArguments: [tokenType1, tokenType2],
target: `${this.configs.swapPackageId}::clob::batch_cancel_order`,
arguments: [
overrides.txb.object(String(poolInfo.clob)),
overrides.txb.pure(String(orderIds)),
overrides.txb.object(accountCap),
],
});
overrides.txb.setGasBudget(defaultGasBudget);
return overrides.txb;
}