自定义做市算法

AMM 蓝图的模块化设计允许开发者通过实现核心的 Pool 实例方法来定义和扩展自己的做市算法。这种设计定义了一个通用的接口,使不同的算法可以无缝接入到 AMM 系统中。

接口

AMM 自定义算法必须实现的接口:

  • Pool.new: 初始化 Pool 实例。

  • Pool:balances: 返回池中资产余额。

  • Pool:Info: 返回池的元信息。

  • Pool:updateLiquidity: 更新池的流动性。

  • Pool:updateAfterSwap: 更新 Swap 之后池中输入或输出资产的余额。

  • Pool:getAmountOut: 计算给定输入资产数量对应的输出资产数量。

开发者需要确保实现以上接口,满足 AMM 蓝图的调用约定。

以下通过解析 BigOrder 做市算法的实现,帮助开发者更直观地理解如何开发自定义的做市算法。

BigOrder 做市算法解析

BigOrder 是一个简单的 AMM 做市算法实现,它基于指定的输入资产和输出资产的固定比率来进行兑换。

该算法使用场景如下:

例如,某用户希望以 20 USDC 的价格卖出 10,000 个 AR。如果直接在 DEX 交易所进行 Swap,可能因流动性不足导致滑点较大。为避免这种情况,该用户可以部署一个基于 BigOrder 算法的 AMM Agent,并设置 tokenIn 为 10,000 AR,tokenOut 为 200,000 USDC。这样,Agent 将以设定的兑换比率接入 FFP 进行撮合交易,相当于为 FFP 提供了一笔 10,000 AR 的流动性。当 FFP 中出现购买 AR 且符合价格匹配的挂单时,该 Agent 就能自动进行吃单成交。Agent 会持续的在 FFP 中进行吃单,知道 10,000 AR 被兑换完成为止。

代码解析如下:

1. Pool.new

code
function Pool.new(x, y, params)
    if not validateInitParams(params) then
        return nil
    end
    local self = setmetatable({}, Pool)
    self.algo = 'BigOrder'
    self.x = x
    self.y = y
    self.tokenIn = params.tokenIn
    self.tokenOut = params.tokenOut
    self.amountOut = params.amountOut
    self.amountIn = params.amountIn
    self.balanceTokenIn = '0'
    self.balanceTokenOut = self.amountOut
    return self
end

用于初始化一个新的 BigOrder 池实例。

接收初始化参数如下:

  • tokenIn 和 tokenOut 是输入和输出资产类型。

  • amountIn 和 amountOut 用于指定兑换比率。

  • balancesTokenIn 和 balancesTokenOut 用于记录资产余额。

2. Pool:balances

code
function Pool:balances()
    local balance = {
        [self.tokenOut] = self.balanceTokenOut,
        [self.tokenIn] = self.balanceTokenIn
    }
    return balance
end

用于返回池子中当前两种资产的余额。

3. Pool:Info

code
function Pool:info()
    local info = {
        x = self.x,
        y = self.y,
        algo = self.algo,
        tokenIn = self.tokenIn,
        tokenOut = self.tokenOut,
        amountOut = self.amountOut,
        amountIn = self.amountIn,
        balances = self:balances()
    }
    return info
end

返回池的元信息。

4. Pool:updateLiquidity

function Pool:updateLiquidity(params)
    return false
end

该算法不需要更新流动性池信息。

5. Pool:updateAfterSwap

function Pool:updateAfterSwap(tokenIn, amountIn, tokenOut, amountOut)
   self.balanceTokenIn = utils.add(self.balanceTokenIn, amountIn)
   self.balanceTokenOut = utils.subtract(self.balanceTokenOut, amountOut)
end

在完成一笔交易后更新池内的资产余额。

6: Pool:getAmountOut

function Pool:getAmountOut(tokenIn, amountIn)
    if tokenIn == self.tokenIn then
        local amountOut = utils.div(utils.mul(amountIn, self.amountOut), self.amountIn)
        if utils.lt(amountOut, self.balanceTokenOut) then
            return self.tokenOut, tostring(amountOut)
        end
    end

    return nil, nil
end

根据输入资产类型和数量,计算可兑换的输出资产数量。

基于固定比率的公式:

amountOut=amountIn×amountOutamountIn \text{amountOut} = \frac{\text{amountIn} \times \text{amountOut}}{\text{amountIn}}

完整代码

bigorder.lua
local bint = require('.bint')(1024)

local Pool = {}
Pool.__index = Pool 

local utils = {
    add = function (a,b) 
      return tostring(bint(a) + bint(b))
    end,
    subtract = function (a,b)
      return tostring(bint(a) - bint(b))
    end,
    mul = function(a, b)
        return tostring(bint.__mul(bint(a), bint(b)))
    end,
    div = function(a, b)
        return tostring(bint.udiv(bint(a), bint(b)))
    end,
    lt = function (a, b)
        return bint.__lt(bint(a), bint(b))
    end
}

local function validateAmount(amount)
    if not amount then
        return false, 'err_no_amount'
    end
    local ok, qty = pcall(bint, amount)
    if not ok then
        return false, 'err_invalid_amount'
    end
    if not utils.lt(0, qty) then
        return false, 'err_negative_amount'
    end
    return true, nil
end

local function validateInitParams(params)
    if not params then
        return false
    end
    return params.tokenOut and params.tokenIn and validateAmount(params.amountOut) and validateAmount(params.amountIn)
end

function Pool.new(x, y, params)
    if not validateInitParams(params) then
        return nil
    end
    local self = setmetatable({}, Pool)
    self.algo = 'BigOrder'
    self.x = x
    self.y = y
    self.tokenIn = params.tokenIn
    self.tokenOut = params.tokenOut
    self.amountOut = params.amountOut
    self.amountIn = params.amountIn
    self.balanceTokenIn = '0'
    self.balanceTokenOut = self.amountOut
    return self
end

function Pool:balances()
    local balance = {
        [self.tokenOut] = self.balanceTokenOut,
        [self.tokenIn] = self.balanceTokenIn
    }
    return balance
end

function Pool:info()
    local info = {
        x = self.x,
        y = self.y,
        algo = self.algo,
        tokenIn = self.tokenIn,
        tokenOut = self.tokenOut,
        amountOut = self.amountOut,
        amountIn = self.amountIn,
        balances = self:balances()
    }
    return info
end

function Pool:updateLiquidity(params)
    return false
end

function Pool:updateAfterSwap(tokenIn, amountIn, tokenOut, amountOut)
   self.balanceTokenIn = utils.add(self.balanceTokenIn, amountIn)
   self.balanceTokenOut = utils.subtract(self.balanceTokenOut, amountOut)
end

function Pool:getAmountOut(tokenIn, amountIn)
    if tokenIn == self.tokenIn then
        local amountOut = utils.div(utils.mul(amountIn, self.amountOut), self.amountIn)
        if utils.lt(amountOut, self.balanceTokenOut) then
            return self.tokenOut, tostring(amountOut)
        end
    end

    return nil, nil
end

return Pool

总结

BigOrder 是一种简单易用的做市算法,其实现逻辑简单清晰,所以非常适合用来例举 AMM 蓝图的自定义实现做市算法。开发者可以参考 BigOrder 实现更多的做市算法,比如 Uniswap V2 V3 做市曲线算法。

Last updated

Was this helpful?