Basic 蓝图
Last updated
Was this helpful?
Last updated
Was this helpful?
Basic 蓝图是一个预先设计好的模板,可帮助你快速构建一个基础版本的 Agent,并根据需求进行定制。
: 支持 Agent 所有者从 Agent 中提取指定资产。
: 支持从 FFP 系统中获取指定票据(Notes),验证有效性并提交结算。
: 监听 FFP 结算中心的结算通知并更新票据和结算状态。
以下是 basic.lua 的代码结构:
local json = require('json')
local bint = require('.bint')(512)
local utils = require('.utils')
-- Agent FFP 相关配置,可自定义配置
FFP = FFP or {}
FFP.Settle = FFP.Settle or 'rKpOUxssKxgfXQOpaCq22npHno6oRw66L3kZeoo_Ndk' -- 指定 FFP 结算中心
FFP.Version = FFP.Version or '0.31' -- FFP 版本号
FFP.MaxNotesToSettle = FFP.MaxNotesToSettle or 2 -- 最大票据结算数量
-- 数据存储
FFP.Notes = FFP.Notes or {} -- 存储票据
FFP.Settled = FFP.Settled or {} -- 存储已结算票据
-- 提现操作
Handlers.add('ffp.withdraw', 'FFP.Withdraw', function(msg)
-- 提现逻辑
end)
-- 接单结算
Handlers.add('ffp.takeOrder', 'FFP.TakeOrder', function(msg)
-- 接单结算逻辑
end)
-- 结算完成通知操作
Handlers.add('ffp.done', function(msg)
-- 结算完成通知
end, function(msg)
-- 结算状态更新
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)
该功能允许 Agent 的所有者从 Agent 中提取指定资产。
典型场景:
Agent 所有者提取收益。
定期清空 Agent 中的剩余资产。
Handlers.add('ffp.takeOrder', 'FFP.TakeOrder', function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can take order')
local noteIDs = utils.getNoteIDs(msg.Data)
if noteIDs == nil then
msg.reply({
Error = 'err_invalid_note_ids',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
if #noteIDs > FFP.MaxNotesToSettle then
msg.reply({
Error = 'err_too_many_orders',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local notes = {}
for _, noteID in ipairs(noteIDs) do
local data = Send({
Target = FFP.Settle,
Action = 'GetNote',
NoteID = noteID
}).receive().Data
if data == '' then
msg.reply({
Error = 'err_not_found',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local note = json.decode(data)
if note.Status ~= 'Open' then
msg.reply({
Error = 'err_not_open',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
if note.Issuer == ao.id then
msg.reply({
Error = 'err_cannot_take_self_order',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
if note.ExpireDate and note.ExpireDate < msg.Timestamp then
msg.reply({
Error = 'err_expired',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
table.insert(notes, note)
end
local si, so = utils.SettlerIO(notes)
-- todo: make sure we have enough balance
-- start a settle session
local res = Send({
Target = FFP.Settle,
Action = 'StartSettle',
Version = FFP.SettleVersion,
Data = json.encode(noteIDs)
}).receive()
if res.Error then
msg.reply({
Error = res.Error,
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local settleID = res.SettleID
FFP.Settled[settleID] = {
SettleID = settleID,
NoteIDs = noteIDs,
Status = 'Pending'
}
if next(so) == nil then
print('TakeOrder: Settler no need to transfer to settle process')
Send({
Target = FFP.Settle,
Action = 'Settle',
Version = FFP.SettleVersion,
SettleID = settleID
})
msg.reply({
Action = 'TakeOrder-Settle-Sent-Notice',
SettleID = settleID
})
return
end
for k, v in pairs(so) do
local amount = utils.subtract('0', v)
Send({
Target = k,
Action = 'Transfer',
Quantity = amount,
Recipient = FFP.Settle,
['X-FFP-SettleID'] = settleID,
['X-FFP-For'] = 'Settle'
})
end
msg.reply({
Action = 'TakeOrder-Settle-Sent-Notice',
SettleID = settleID
})
end)
该功能允许 Agent 从 FFP 协议中获取指定的票据集(Notes),验证其有效性,并提交到 FFP 协议中生成结算订单进行结算。
关键代码逻辑:
验证票据有效性(例如状态、来源、过期时间等)。
调用 FFP 协议中的 StartSettle 接口生成结算订单。
执行结算订单逻辑(比如资金划转)。
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 settleID = msg['X-FFP-SettleID']
if settleID and FFP.Settled[settleID] then
if msg['X-FFP-For'] == 'Settled' then
FFP.Settled[settleID].Status = 'Settled'
else
FFP.Settled[settleID].Status = 'Rejected'
end
FFP.Settled[settleID].SettledDate = msg['X-FFP-SettledDate']
for _, noteID in ipairs(FFP.Settled[settleID].NoteIDs) do
local data = Send({
Target = FFP.Settle,
Action = 'GetNote',
NoteID = noteID
}).receive().Data
FFP.Notes[noteID] = json.decode(data)
end
else
print('SettleID not found: ' .. settleID)
end
end)
该功能监听 FFP 协议发送的结算完成通知,并更新票据和结算订单状态。
主要作用是保证 Agent 保存的票据和结算订单状态和 FFP 协议的状态保持一致。
local json = require('json')
local bint = require('.bint')(512)
local utils = require('.utils')
FFP = FFP or {}
-- config
FFP.Settle = FFP.Settle or 'rKpOUxssKxgfXQOpaCq22npHno6oRw66L3kZeoo_Ndk'
FFP.SettleVersion = FFP.SettleVersion or '0.31'
FFP.MaxNotesToSettle = FFP.MaxNotesToSettle or 2
-- database
FFP.Notes = FFP.Notes or {}
FFP.Settled = FFP.Settled or {}
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.takeOrder', 'FFP.TakeOrder', function(msg)
assert(msg.From == Owner or msg.From == ao.id, 'Only owner can take order')
local noteIDs = utils.getNoteIDs(msg.Data)
if noteIDs == nil then
msg.reply({
Error = 'err_invalid_note_ids',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
if #noteIDs > FFP.MaxNotesToSettle then
msg.reply({
Error = 'err_too_many_orders',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local notes = {}
for _, noteID in ipairs(noteIDs) do
local data = Send({
Target = FFP.Settle,
Action = 'GetNote',
NoteID = noteID
}).receive().Data
if data == '' then
msg.reply({
Error = 'err_not_found',
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local note = json.decode(data)
if note.Status ~= 'Open' then
msg.reply({
Error = 'err_not_open',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
if note.Issuer == ao.id then
msg.reply({
Error = 'err_cannot_take_self_order',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
if note.ExpireDate and note.ExpireDate < msg.Timestamp then
msg.reply({
Error = 'err_expired',
['X-FFP-TakeOrderID'] = msg.Id,
Data = noteID
})
return
end
table.insert(notes, note)
end
local si, so = utils.SettlerIO(notes)
-- todo: make sure we have enough balance
-- start a settle session
local res = Send({
Target = FFP.Settle,
Action = 'StartSettle',
Version = FFP.SettleVersion,
Data = json.encode(noteIDs)
}).receive()
if res.Error then
msg.reply({
Error = res.Error,
['X-FFP-TakeOrderID'] = msg.Id
})
return
end
local settleID = res.SettleID
FFP.Settled[settleID] = {
SettleID = settleID,
NoteIDs = noteIDs,
Status = 'Pending'
}
if next(so) == nil then
print('TakeOrder: Settler no need to transfer to settle process')
Send({
Target = FFP.Settle,
Action = 'Settle',
Version = FFP.SettleVersion,
SettleID = settleID
})
msg.reply({
Action = 'TakeOrder-Settle-Sent-Notice',
SettleID = settleID
})
return
end
for k, v in pairs(so) do
local amount = utils.subtract('0', v)
Send({
Target = k,
Action = 'Transfer',
Quantity = amount,
Recipient = FFP.Settle,
['X-FFP-SettleID'] = settleID,
['X-FFP-For'] = 'Settle'
})
end
msg.reply({
Action = 'TakeOrder-Settle-Sent-Notice',
SettleID = settleID
})
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 settleID = msg['X-FFP-SettleID']
if settleID and FFP.Settled[settleID] then
if msg['X-FFP-For'] == 'Settled' then
FFP.Settled[settleID].Status = 'Settled'
else
FFP.Settled[settleID].Status = 'Rejected'
end
FFP.Settled[settleID].SettledDate = msg['X-FFP-SettledDate']
for _, noteID in ipairs(FFP.Settled[settleID].NoteIDs) do
local data = Send({
Target = FFP.Settle,
Action = 'GetNote',
NoteID = noteID
}).receive().Data
FFP.Notes[noteID] = json.decode(data)
end
else
print('SettleID not found: ' .. settleID)
end
end)
Handlers.add('ffp.getOrders', 'FFP.GetOrders', function(msg)
msg.reply({
Action = 'GetOrders-Notice',
Data = json.encode(FFP.Notes)
})
end)
Handlers.add('ffp.getSettled', 'FFP.GetSettled', function(msg)
msg.reply({
Action = 'GetSettled-Notice',
Data = json.encode(FFP.Settled)
})
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
将 basic.lua 和 utils.lua 代码复制到开发环境。
通常情况下,您无需修改 basic.lua 中已经实现的核心功能代码。只需根据业务需求,在此基础上添加额外的功能逻辑。
Basic 蓝图是构建 FFP Agent 的标准模板,所有 FFP Agent 均以此为基础,根据具体业务需求扩展更多功能。通过定制化的 Agent,开发者可以高效融入 FusionFi 协议生态,参与各类金融活动。
例如,我们可以构建一个 FFP 中的 。