Handlers.add('ffp.addPool', 'FFP.AddPool',
function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can add pool')
assert(type(msg.X) == 'string', 'X is required')
assert(type(msg.Y) == 'string', 'Y is required')
assert(msg.X < msg.Y, 'X must be less than Y')
assert(type(msg.Algo) == 'string', 'Algo is required')
assert(type(msg.Data) == 'string', 'Params is required')
local poolId = getPoolId(msg.X, msg.Y)
if FFP.Pools[poolId] then
msg.reply({Error = 'Pool already exists'})
return
end
local P = FFP.AlgoToPool[msg.Algo]
if not P then
msg.reply({Error = 'Unsupported amm algo'})
return
end
local pool = P.new(msg.X, msg.Y, json.decode(msg.Data))
if not pool then
msg.reply({Error = 'Invalid pool params'})
return
end
FFP.Pools[poolId] = pool
msg.reply({Action = 'PoolAdded-Notice', Pool = poolId})
Send({Target = FFP.Publisher, Action = 'PoolAdded-Notice', Creator = ao.id, Data = json.encode(pool)})
end
)
Handlers.add('ffp.updateLiquidity', 'FFP.UpdateLiquidity',
function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can update liquidity')
assert(type(msg.Y) == 'string', 'Y is required')
assert(msg.X < msg.Y, 'X must be less than Y')
assert(type(msg.Data) == 'string', 'Params is required')
local pool = getPool(msg.X, msg.Y)
if not pool then
msg.reply({Error = 'Pool not found'})
return
end
local ok = pool:updateLiquidity(json.decode(msg.Data))
if not ok then
msg.reply({Error = 'Invalid updateLiquidity params'})
return
end
msg.reply({Action = 'LiquidityUpdated-Notice', Data = json.encode(pool)})
end
)
Handlers.add('ffp.makeOrder', 'FFP.MakeOrder',
function(msg)
if FFP.Executing then
msg.reply({Error = 'err_executing'})
return
end
local ok, err = validateOrderMsg(msg)
if not ok then
msg.reply({Error = err})
return
end
local pool = getPool(msg.TokenIn, msg.TokenOut)
local tokenOut, amountOut = pool:getAmountOut(msg.TokenIn, msg.AmountIn)
if not amountOut or amountOut == '0' then
msg.reply({Error = 'err_no_amount_out'})
return
end
if msg.AmountOut then
local ok, err = validateAmountOut(pool, msg.TokenIn, msg.AmountIn, msg.TokenOut, msg.AmountOut)
if not ok then
msg.reply({Error = err})
return
end
amountOut = msg.AmountOut
end
-- 90 seconds
local expireDate = msg.Timestamp + FFP.TimeoutPeriod
local res = Send({
Target = FFP.Settle,
Action = 'CreateNote',
AssetID = msg.TokenOut,
Amount = tostring(amountOut),
HolderAssetID = msg.TokenIn,
HolderAmount = msg.AmountIn,
IssueDate = tostring(msg.Timestamp),
ExpireDate = tostring(expireDate),
Version = FFP.SettleVersion
}).receive()
local noteID = res.NoteID
local note = json.decode(res.Data)
note.MakeTx = msg.Id
FFP.Notes[noteID] = note
FFP.MakeTxToNoteID[msg.Id] = noteID
-- remove expired notes in Notes
for noteID, note in pairs(FFP.Notes) do
if note.Status == 'Open' and note.ExpireDate and note.ExpireDate < msg.Timestamp then
FFP.Notes[noteID] = nil
FFP.MakeTxToNoteID[note.MakeTx] = nil
end
end
msg.reply({
Action = 'OrderMade-Notice',
NoteID = noteID,
Data = json.encode(note)
})
end
)
Handlers.add('ffp.execute', 'Execute',
function(msg)
assert(msg.From == FFP.Settle, 'Only settle can start exectue')
assert(type(msg.NoteID) == 'string', 'NoteID is required')
assert(type(msg.SettleID) == 'string', 'SettleID is required')
if FFP.Executing then
msg.reply({Action= 'Reject', Error = 'err_executing', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
local note = FFP.Notes[msg.NoteID]
if not note then
msg.reply({Action= 'Reject', Error = 'err_not_found', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.Status ~= 'Open' then
msg.reply({Action= 'Reject', Error = 'err_not_open', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.Issuer ~= ao.id then
msg.reply({Action = 'Reject', Error = 'err_invalid_issuer', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.ExpireDate and note.ExpireDate < msg.Timestamp then
FFP.Notes[note.NoteID] = nil
FFP.MakeTxToNoteID[note.MakeTx] = nil
msg.reply({Action= 'Reject', Error = 'err_expired', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
local pool = getPool(note.HolderAssetID, note.AssetID)
local ok, err = validateAmountOut(pool, note.HolderAssetID, note.HolderAmount, note.AssetID, note.Amount)
if not ok then
msg.reply({Action= 'Reject', Error = err, SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
msg.reply({Action = 'ExecuteStarted', SettleID = msg.SettleID, NoteID = msg.NoteID})
FFP.Executing = true
note.Status = 'Executing'
Send({Target = note.AssetID, Action = 'Transfer', Quantity = note.Amount, Recipient = FFP.Settle,
['X-FFP-SettleID'] = msg.SettleID,
['X-FFP-NoteID'] = msg.NoteID,
['X-FFP-For'] = 'Execute'
})
end
)
Handlers.add('ffp.done',
function(msg) return (msg.Action == 'Credit-Notice') and (msg['X-FFP-For'] == 'Settled' or msg['X-FFP-For'] == 'Refund') end,
function(msg)
assert(msg.Sender == FFP.Settle, 'Only settle can send settled or refund notice')
local noteID = msg['X-FFP-NoteID']
local note = FFP.Notes[noteID]
if not note then
print('no note found when settled: ' .. noteID)
return
end
local pool = getPool(note.HolderAssetID, note.AssetID)
if msg['X-FFP-For'] == 'Settled' then
pool:updateAfterSwap(note.HolderAssetID, note.HolderAmount, note.AssetID, note.Amount)
end
FFP.Notes[noteID].Status = msg['X-FFP-For']
FFP.Executing = false
FFP.Notes[noteID] = json.decode(Send({Target = FFP.Settle, Action = 'GetNote', NoteID = noteID}).receive().Data)
end
)
Handlers.add('ffp.pools', 'FFP.GetPools',
function(msg)
local pools = {}
for poolId, pool in pairs(FFP.Pools) do
pools[poolId] = pool:info()
end
msg.reply({Data = json.encode(pools)})
end
)
Handlers.add('ffp.getNote', 'FFP.GetNote',
function(msg)
assert(type(msg.MakeTx) == 'string', 'MakeTx is required')
local note = FFP.Notes[FFP.MakeTxToNoteID[msg.MakeTx]] or ''
msg.reply({NoteID=note.NoteID, Data = json.encode(note)})
end
)
Handlers.add('ffp.getAmountOut', 'FFP.GetAmountOut',
function(msg)
if FFP.Executing then
msg.reply({Error = 'err_executing'})
return
end
local ok, err = validateOrderMsg(msg)
if not ok then
msg.reply({Error = err})
return
end
local pool = getPool(msg.TokenIn, msg.TokenOut)
local tokenOut, amountOut = pool:getAmountOut(msg.TokenIn, msg.AmountIn)
if not amountOut or amountOut == '0' then
msg.reply({Error = 'err_no_amount_out'})
return
end
msg.reply({
TokenIn = msg.TokenIn,
AmountIn = msg.AmountIn,
TokenOut = tokenOut,
AmountOut = tostring(amountOut)
})
end
)
local json = require('json')
local bint = require('.bint')(512)
local utils = require('.utils')
local poolUniswapV2 = require('.algo.uniswapv2')
local poolBigOrder = require('.algo.bigorder')
FFP = FFP or {}
-- config
FFP.Settle = FFP.Settle or 'rKpOUxssKxgfXQOpaCq22npHno6oRw66L3kZeoo_Ndk'
FFP.Publisher = FFP.Publisher or '8VzqSX_0rdAr99P3QIYd0bRG-XySTQUNFlfJf20zNEo'
FFP.SettleVersion = FFP.SettleVersion or '0.31'
FFP.MaxNotesToSettle = FFP.MaxNotesToSettle or 2
FFP.TimeoutPeriod = FFP.TimeoutPeriod or 90000
-- database or state
FFP.Pools = FFP.Pools or {}
FFP.Notes = FFP.Notes or {}
FFP.MakeTxToNoteID = FFP.MakeTxToNoteID or {}
FFP.Executing = FFP.Executing or false
-- amm pool functions
FFP.AlgoToPool = FFP.AlgoToPool or {
UniswapV2 = poolUniswapV2,
BigOrder = poolBigOrder
}
local function getPoolId(X, Y)
if X > Y then
return Y .. ':' .. X
end
return X .. ':' .. Y
end
local function getPool(X, Y)
return FFP.Pools[getPoolId(X, Y)]
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 bint.__lt(0, qty) then
return false, 'err_negative_amount'
end
return true, nil
end
-- tokenIn, tokenOut, amountIn, amountOut(optional)
local function validateOrderMsg(msg)
local Pool = getPool(msg.TokenIn, msg.TokenOut)
if not Pool then
return false, 'err_pool_not_found'
end
local ok, err = validateAmount(msg.AmountIn)
if not ok then
return false, 'err_invalid_amount_in'
end
if msg.AmountOut then
local ok, err = validateAmount(msg.AmountOut)
if not ok then
return false, 'err_invalid_amount_out'
end
end
return true, nil
end
local function validateAmountOut(pool, tokenIn, amountIn, tokenOut, expectedAmountOut)
local tokenOut_, amountOut = pool:getAmountOut(tokenIn, amountIn)
if not amountOut or amountOut == '0' then
return false, 'err_no_amount_out'
end
if tokenOut_ ~= tokenOut then
return false, 'err_invalid_token_out'
end
if expectedAmountOut then
if utils.lt(amountOut, expectedAmountOut) then
return false, 'err_amount_out_too_small'
end
end
return true, nil
end
Handlers.add('ffp.withdraw', 'FFP.Withdraw',
function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can withdraw')
assert(type(msg.Token) == 'string', 'Token is required')
assert(type(msg.Amount) == 'string', 'Amount is required')
assert(bint.__lt(0, bint(msg.Amount)), 'Amount must be greater than 0')
Send({
Target = msg.Token,
Action = 'Transfer',
Quantity = msg.Amount,
Recipient = Owner
})
end
)
Handlers.add('ffp.pools', 'FFP.GetPools',
function(msg)
local pools = {}
for poolId, pool in pairs(FFP.Pools) do
pools[poolId] = pool:info()
end
msg.reply({Data = json.encode(pools)})
end
)
Handlers.add('ffp.addPool', 'FFP.AddPool',
function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can add pool')
assert(type(msg.X) == 'string', 'X is required')
assert(type(msg.Y) == 'string', 'Y is required')
assert(msg.X < msg.Y, 'X must be less than Y')
assert(type(msg.Algo) == 'string', 'Algo is required')
assert(type(msg.Data) == 'string', 'Params is required')
local poolId = getPoolId(msg.X, msg.Y)
if FFP.Pools[poolId] then
msg.reply({Error = 'Pool already exists'})
return
end
local P = FFP.AlgoToPool[msg.Algo]
if not P then
msg.reply({Error = 'Unsupported amm algo'})
return
end
local pool = P.new(msg.X, msg.Y, json.decode(msg.Data))
if not pool then
msg.reply({Error = 'Invalid pool params'})
return
end
FFP.Pools[poolId] = pool
msg.reply({Action = 'PoolAdded-Notice', Pool = poolId})
Send({Target = FFP.Publisher, Action = 'PoolAdded-Notice', Creator = ao.id, Data = json.encode(pool)})
end
)
Handlers.add('ffp.updateLiquidity', 'FFP.UpdateLiquidity',
function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can update liquidity')
assert(type(msg.Y) == 'string', 'Y is required')
assert(msg.X < msg.Y, 'X must be less than Y')
assert(type(msg.Data) == 'string', 'Params is required')
local pool = getPool(msg.X, msg.Y)
if not pool then
msg.reply({Error = 'Pool not found'})
return
end
local ok = pool:updateLiquidity(json.decode(msg.Data))
if not ok then
msg.reply({Error = 'Invalid updateLiquidity params'})
return
end
msg.reply({Action = 'LiquidityUpdated-Notice', Data = json.encode(pool)})
end
)
Handlers.add('ffp.getAmountOut', 'FFP.GetAmountOut',
function(msg)
if FFP.Executing then
msg.reply({Error = 'err_executing'})
return
end
local ok, err = validateOrderMsg(msg)
if not ok then
msg.reply({Error = err})
return
end
local pool = getPool(msg.TokenIn, msg.TokenOut)
local tokenOut, amountOut = pool:getAmountOut(msg.TokenIn, msg.AmountIn)
if not amountOut or amountOut == '0' then
msg.reply({Error = 'err_no_amount_out'})
return
end
msg.reply({
TokenIn = msg.TokenIn,
AmountIn = msg.AmountIn,
TokenOut = tokenOut,
AmountOut = tostring(amountOut)
})
end
)
Handlers.add('ffp.makeOrder', 'FFP.MakeOrder',
function(msg)
if FFP.Executing then
msg.reply({Error = 'err_executing'})
return
end
local ok, err = validateOrderMsg(msg)
if not ok then
msg.reply({Error = err})
return
end
local pool = getPool(msg.TokenIn, msg.TokenOut)
local tokenOut, amountOut = pool:getAmountOut(msg.TokenIn, msg.AmountIn)
if not amountOut or amountOut == '0' then
msg.reply({Error = 'err_no_amount_out'})
return
end
if msg.AmountOut then
local ok, err = validateAmountOut(pool, msg.TokenIn, msg.AmountIn, msg.TokenOut, msg.AmountOut)
if not ok then
msg.reply({Error = err})
return
end
amountOut = msg.AmountOut
end
-- 90 seconds
local expireDate = msg.Timestamp + FFP.TimeoutPeriod
local res = Send({
Target = FFP.Settle,
Action = 'CreateNote',
AssetID = msg.TokenOut,
Amount = tostring(amountOut),
HolderAssetID = msg.TokenIn,
HolderAmount = msg.AmountIn,
IssueDate = tostring(msg.Timestamp),
ExpireDate = tostring(expireDate),
Version = FFP.SettleVersion
}).receive()
local noteID = res.NoteID
local note = json.decode(res.Data)
note.MakeTx = msg.Id
FFP.Notes[noteID] = note
FFP.MakeTxToNoteID[msg.Id] = noteID
-- remove expired notes in Notes
for noteID, note in pairs(FFP.Notes) do
if note.Status == 'Open' and note.ExpireDate and note.ExpireDate < msg.Timestamp then
FFP.Notes[noteID] = nil
FFP.MakeTxToNoteID[note.MakeTx] = nil
end
end
msg.reply({
Action = 'OrderMade-Notice',
NoteID = noteID,
Data = json.encode(note)
})
end
)
Handlers.add('ffp.getNote', 'FFP.GetNote',
function(msg)
assert(type(msg.MakeTx) == 'string', 'MakeTx is required')
local note = FFP.Notes[FFP.MakeTxToNoteID[msg.MakeTx]] or ''
msg.reply({NoteID=note.NoteID, Data = json.encode(note)})
end
)
Handlers.add('ffp.execute', 'Execute',
function(msg)
assert(msg.From == FFP.Settle, 'Only settle can start exectue')
assert(type(msg.NoteID) == 'string', 'NoteID is required')
assert(type(msg.SettleID) == 'string', 'SettleID is required')
if FFP.Executing then
msg.reply({Action= 'Reject', Error = 'err_executing', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
local note = FFP.Notes[msg.NoteID]
if not note then
msg.reply({Action= 'Reject', Error = 'err_not_found', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.Status ~= 'Open' then
msg.reply({Action= 'Reject', Error = 'err_not_open', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.Issuer ~= ao.id then
msg.reply({Action = 'Reject', Error = 'err_invalid_issuer', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
if note.ExpireDate and note.ExpireDate < msg.Timestamp then
FFP.Notes[note.NoteID] = nil
FFP.MakeTxToNoteID[note.MakeTx] = nil
msg.reply({Action= 'Reject', Error = 'err_expired', SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
local pool = getPool(note.HolderAssetID, note.AssetID)
local ok, err = validateAmountOut(pool, note.HolderAssetID, note.HolderAmount, note.AssetID, note.Amount)
if not ok then
msg.reply({Action= 'Reject', Error = err, SettleID = msg.SettleID, NoteID = msg.NoteID})
return
end
msg.reply({Action = 'ExecuteStarted', SettleID = msg.SettleID, NoteID = msg.NoteID})
FFP.Executing = true
note.Status = 'Executing'
Send({Target = note.AssetID, Action = 'Transfer', Quantity = note.Amount, Recipient = FFP.Settle,
['X-FFP-SettleID'] = msg.SettleID,
['X-FFP-NoteID'] = msg.NoteID,
['X-FFP-For'] = 'Execute'
})
end
)
Handlers.add('ffp.done',
function(msg) return (msg.Action == 'Credit-Notice') and (msg['X-FFP-For'] == 'Settled' or msg['X-FFP-For'] == 'Refund') end,
function(msg)
assert(msg.Sender == FFP.Settle, 'Only settle can send settled or refund notice')
local noteID = msg['X-FFP-NoteID']
local note = FFP.Notes[noteID]
if not note then
print('no note found when settled: ' .. noteID)
return
end
local pool = getPool(note.HolderAssetID, note.AssetID)
if msg['X-FFP-For'] == 'Settled' then
pool:updateAfterSwap(note.HolderAssetID, note.HolderAmount, note.AssetID, note.Amount)
end
FFP.Notes[noteID].Status = msg['X-FFP-For']
FFP.Executing = false
FFP.Notes[noteID] = json.decode(Send({Target = FFP.Settle, Action = 'GetNote', NoteID = noteID}).receive().Data)
end
)
local json = require('json')
local bint = require('.bint')(512)
local mod = {
add = function(a, b)
return tostring(bint(a) + bint(b))
end,
subtract = function(a, b)
return tostring(bint(a) - bint(b))
end,
toBalanceValue = function(a)
return tostring(bint(a))
end,
toNumber = function(a)
return tonumber(a)
end,
lt = function (a, b)
return bint.__lt(bint(a), bint(b))
end
}
mod.getNoteIDs = function(data)
if string.find(data, "null") then
return nil
end
local noteIDs, err = json.decode(data)
if err then
return nil
end
if type(noteIDs) == "string" then
return {noteIDs}
end
if type(noteIDs) == "table" then
for i = 1, #noteIDs do
if noteIDs[i] == nil or type(noteIDs[i]) ~= "string" then
return nil
end
end
return noteIDs
end
return nil
end
mod.SettlerIO = function (notes)
local settlerIn = {}
local settlerOut = {}
local bc = {}
for _, note in ipairs(notes) do
if not bc[note.AssetID] then bc[note.AssetID] = bint(0) end
if not bc[note.HolderAssetID] then bc[note.HolderAssetID] = bint(0) end
bc[note.AssetID] = mod.add(bc[note.AssetID], note.Amount)
bc[note.HolderAssetID] = mod.subtract(bc[note.HolderAssetID], note.HolderAmount)
end
for k, v in pairs(bc) do
if v == '0' then bc[k] = nil end
end
for k, v in pairs(bc) do
if mod.lt(v, '0') then
settlerOut[k] = v
else
settlerIn[k] = v
end
end
return settlerIn, settlerOut
end
return mod
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
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 getAmountOut(amountIn, reserveIn, reserveOut, fee)
local amountInWithFee = utils.mul(amountIn, utils.subtract(10000, fee))
local numerator = utils.mul(amountInWithFee, reserveOut)
local denominator = utils.add(utils.mul(10000, reserveIn), amountInWithFee)
return utils.div(numerator, denominator)
end
local function validateInitParams(params)
if not params then
return false
end
return validateAmount(params.fee) and validateAmount(params.px) and validateAmount(params.py)
end
local function validateUpdateLiquidityParams(params)
if not params then
return false
end
return validateAmount(params.px) and validateAmount(params.py)
end
function Pool.new(x, y, params)
if not validateInitParams(params) then
return nil
end
local self = setmetatable({}, Pool)
self.algo = 'UniswapV2'
self.x = x
self.y = y
self.fee = params.fee
self.px = params.px
self.py = params.py
return self
end
function Pool:balances()
local balance = {
[self.x] = self.px,
[self.y] = self.py
}
return balance
end
function Pool:info()
local info = {
x = self.x,
y = self.y,
fee = self.fee,
algo = self.algo,
px = self.px,
py = self.py,
balances = self:balances()
}
return info
end
function Pool:updateLiquidity(params)
if not validateUpdateLiquidityParams(params) then
return false
end
self.px = params.px
self.py = params.py
return true
end
function Pool:updateAfterSwap(tokenIn, amountIn, tokenOut, amountOut)
if tokenIn == self.x then
self.px = utils.add(self.px, amountIn)
self.py = utils.subtract(self.py, amountOut)
else
self.py = utils.add(self.py, amountIn)
self.px = utils.subtract(self.px, amountOut)
end
end
function Pool:getAmountOut(tokenIn, amountIn)
local tokenOut = self.y
local reserveIn = bint(self.px)
local reserveOut = bint(self.py)
if tokenIn == self.y then
reserveIn, reserveOut = reserveOut, reserveIn
tokenOut = self.x
end
local amountOut = getAmountOut(bint(amountIn), reserveIn, reserveOut, bint(self.fee))
return tokenOut, tostring(amountOut)
end
return Pool