From c7f03b94dbf77c214cc209d9e666342eef77a2c4 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 12:07:29 +0200 Subject: [PATCH 01/16] feat: patching setup --- src/borrow/interest.lua | 11 +++-- src/borrow/position.lua | 51 +++++++++++--------- src/process.lua | 2 + src/token/patching.lua | 102 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 src/token/patching.lua diff --git a/src/borrow/interest.lua b/src/borrow/interest.lua index 84e000c..7746a8b 100644 --- a/src/borrow/interest.lua +++ b/src/borrow/interest.lua @@ -77,9 +77,8 @@ function mod.interestRate(msg) }) end ----@type HandlerFunction -function mod.supplyRate(msg) - ---@type Bint, number +-- Calculates the current supply rate +function mod.calculateSupplyRate() local borrowRate, rateMul = mod.calculateBorrowRate() local borrowRateFloat = utils.bintToFloat( borrowRate, @@ -100,6 +99,12 @@ function mod.supplyRate(msg) ) - 1 end + return supplyRate +end + +---@type HandlerFunction +function mod.supplyRate(msg) + local supplyRate = mod.calculateSupplyRate() msg.reply({ ["Supply-Rate"] = tostring(supplyRate) }) end diff --git a/src/borrow/position.lua b/src/borrow/position.lua index 21074ff..695da00 100644 --- a/src/borrow/position.lua +++ b/src/borrow/position.lua @@ -129,6 +129,32 @@ function mod.globalPosition(address, oracle) return res end +-- Get all positions on this oToken +---@return Position[] +function mod.allPositions() + ---@type Position[] + local positions = {} + + -- go through all users who have collateral deposited + -- and add their position + for address, _ in pairs(Balances) do + positions[address] = mod.position(address) + end + + -- go through all users who have "Loans" + -- because it is possible that their collateralization + -- is not in this instance of the process + for address, _ in pairs(Loans) do + -- do not handle positions that have + -- already been added above + if not positions[address] then + positions[address] = mod.position(address) + end + end + + return positions +end + -- Local position action handler ---@type HandlerFunction function mod.handlers.localPosition(msg) @@ -165,12 +191,9 @@ end function mod.handlers.allPositions(msg) ---@type table local positions = {} + local calculatedPositions = mod.allPositions() - -- go through all users who have collateral deposited - -- and add their position - for address, _ in pairs(Balances) do - local position = mod.position(address) - + for address, position in pairs(calculatedPositions) do positions[address] = { Collateralization = tostring(position.collateralization), Capacity = tostring(position.capacity), @@ -179,24 +202,6 @@ function mod.handlers.allPositions(msg) } end - -- go through all users who have "Loans" - -- because it is possible that their collateralization - -- is not in this instance of the process - for address, _ in pairs(Loans) do - -- do not handle positions that have - -- already been added above - if not positions[address] then - local position = mod.position(address) - - positions[address] = { - Collateralization = tostring(position.collateralization), - Capacity = tostring(position.capacity), - ["Borrow-Balance"] = tostring(position.borrowBalance), - ["Liquidation-Limit"] = tostring(position.liquidationLimit) - } - end - end - -- reply with the serialized result msg.reply({ ["Collateral-Ticker"] = CollateralTicker, diff --git a/src/process.lua b/src/process.lua index b147767..ca230a5 100644 --- a/src/process.lua +++ b/src/process.lua @@ -18,6 +18,7 @@ local reserves = require ".controller.reserves" local balance = require ".token.balance" local token = require ".token.token" local transfer = require ".token.transfer" +local patching = require ".token.patching" local pool = require ".borrow.pool" local position = require ".borrow.position" @@ -53,6 +54,7 @@ local function setup_handlers() oracle.setup(msg, env) cooldown.setup(msg, env) delegation.setup(msg, env) + patching.setup(msg, env) end ) diff --git a/src/token/patching.lua b/src/token/patching.lua new file mode 100644 index 0000000..347d5c0 --- /dev/null +++ b/src/token/patching.lua @@ -0,0 +1,102 @@ +local precision = require ".utils.precision" +local interest = require ".borrow.interest" +local bint = require ".utils.bint"(1024) +local utils = require ".utils.utils" + +local mod = {} + +---@type HandlerFunction +function mod.setup() + -- parse as bint + local totalLent = bint(TotalBorrows) + local cash = bint(Cash) + local reserves = bint(Reserves) + + -- calculate utilization + local totalPooled = totalLent + cash - reserves + local utilizationDecimals = 5 + local utilization = bint.zero() + + if not bint.eq(totalPooled, 0) then + utilization = bint.udiv( + totalLent * bint(100) * bint.ipow(10, utilizationDecimals), + totalPooled + ) + end + + -- enabled and disabled interactions + local enabledInteractions = {} + local disabledInteractions = {} + + for name, enabled in pairs(EnabledInteractions) do + table.insert( + enabled and enabledInteractions or disabledInteractions, + string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2) + ) + end + + -- clone balances + local balances = { device = "trie@1.0" } + + for address, balance in pairs(Balances) do + balances[address] = balance + end + + -- current interest rate + local interestRate, rateMul = interest.calculateBorrowRate() + local borrowRate = utils.bintToFloat( + interestRate, + select(2, string.gsub(tostring(rateMul), "0", "")) + ) + + -- map positions + local positions = {} + local calculatedPositions = mod.allPositions() + + for address, position in pairs(calculatedPositions) do + positions[address] = { + collateralization = tostring(position.collateralization), + capacity = tostring(position.capacity), + borrowBalance = tostring(position.borrowBalance), + liquidationLimit = tostring(position.liquidationLimit) + } + end + + ao.send({ + device = "patch@1.0", + ["token-info"] = { + name = Name, + ticker = Ticker, + logo = Logo, + denomination = tostring(Denomination), + supply = TotalSupply, + collateralId = CollateralID, + collateralFactor = tostring(CollateralFactor), + collateralDenomination = tostring(CollateralDenomination), + liquidationThreshold = tostring(LiquidationThreshold), + valueLimit = precision.formatInternalAsNative(ValueLimit, "rounddown"), + oracle = Oracle, + oracleDelayTolerance = tostring(MaxOracleDelay), + totalBorrows = tostring(precision.toNativePrecision(totalLent, "roundup")), + cash = tostring(precision.toNativePrecision(cash, "rounddown")), + reserveFactor = tostring(ReserveFactor), + totalReserves = tostring(precision.toNativePrecision(reserves, "roundup")), + initRate = tostring(InitRate), + baseRate = tostring(BaseRate), + jumpRate = tostring(JumpRate), + kinkParam = tostring(KinkParam), + cooldownPeriod = tostring(CooldownPeriod), + utilization = tostring(utils.bintToFloat(utilization, utilizationDecimals)), + enabledInteractions = enabledInteractions, + disabledInteractions = disabledInteractions + }, + balances = balances, + friends = Friends, + cooldowns = Cooldowns, + borrowRate = borrowRate, + supplyRate = interest.calculateSupplyRate(), + positions = positions + }) +end + +return mod From d2b9da7448b219d6e9a163ea7b1f72e34ee61d06 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 12:20:14 +0200 Subject: [PATCH 02/16] feat: additional patch setup --- src/token/patching.lua | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/token/patching.lua b/src/token/patching.lua index 347d5c0..85ab6a3 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -35,12 +35,10 @@ function mod.setup() ) end - -- clone balances - local balances = { device = "trie@1.0" } - - for address, balance in pairs(Balances) do - balances[address] = balance - end + -- clone loans, interest indices, balances + local loans = mod.mapAsTrie(Loans) + local interestIndices = mod.mapAsTrie(InterestIndices) + local balances = mod.mapAsTrie(Balances) -- current interest rate local interestRate, rateMul = interest.calculateBorrowRate() @@ -50,7 +48,7 @@ function mod.setup() ) -- map positions - local positions = {} + local positions = { device = "trie@1.0" } local calculatedPositions = mod.allPositions() for address, position in pairs(calculatedPositions) do @@ -95,8 +93,27 @@ function mod.setup() cooldowns = Cooldowns, borrowRate = borrowRate, supplyRate = interest.calculateSupplyRate(), - positions = positions + positions = positions, + loans = loans, + borrowIndex = BorrowIndex, + lastBorrowIndexUpdate = LastBorrowIndexUpdate, + reservesRemainder = ReservesRemainder, + interestIndices = interestIndices }) end +-- Convert a map to a trie structure when patching +---@generic T : unknown +---@param map table The map to convert +---@return table +function mod.mapAsTrie(map) + local res = { device = "trie@1.0" } + + for key, value in pairs(map) do + res[key] = value + end + + return res +end + return mod From c27dd0153cc68655d652d979c37a928d69913b0b Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 12:40:34 +0200 Subject: [PATCH 03/16] feat: patch order results --- src/borrow/borrow.lua | 26 ++++++++++++++++++++++++-- src/borrow/repay.lua | 7 +++++++ src/process.lua | 6 ++++-- src/supply/mint.lua | 8 ++++++++ src/supply/redeem.lua | 24 ++++++++++++++++++++++-- src/token/patching.lua | 19 ++++++++++++++++++- 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/borrow/borrow.lua b/src/borrow/borrow.lua index d18e241..8e0ef34 100644 --- a/src/borrow/borrow.lua +++ b/src/borrow/borrow.lua @@ -1,10 +1,14 @@ local assertions = require ".utils.assertions" local precision = require ".utils.precision" local position = require ".borrow.position" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) +local utils = require ".utils.utils" + +local mod = {} ---@type HandlerWithOracle -local function borrow(msg, _, oracle) +function mod.borrow(msg, _, oracle) -- the wallet that will borrow the tokens local account = msg.From @@ -80,6 +84,9 @@ local function borrow(msg, _, oracle) Recipient = account }) + -- patch order result + patching.patchOrder(msg.Id, true) + -- send confirmation msg.reply({ Action = "Borrow-Confirmation", @@ -87,4 +94,19 @@ local function borrow(msg, _, oracle) }) end -return borrow +---@param msg Message +---@param _ Message +---@param err unknown +function mod.error(msg, _, err) + local prettyError, rawError = utils.prettyError(err) + + patching.patchOrder(msg.Id, false, prettyError) + ao.send({ + Target = msg.From, + Action = "Borrow-Error", + Error = prettyError, + ["Raw-Error"] = rawError + }) +end + +return mod diff --git a/src/borrow/repay.lua b/src/borrow/repay.lua index 7037f49..02a28ee 100644 --- a/src/borrow/repay.lua +++ b/src/borrow/repay.lua @@ -1,6 +1,7 @@ local assertions = require ".utils.assertions" local precision = require ".utils.precision" local interest = require ".borrow.interest" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) local utils = require ".utils.utils" @@ -48,6 +49,7 @@ function repay.handler(msg) ["Repaid-Quantity"] = tostring(actualRepaidQty), ["Refund-Quantity"] = tostring(refundQty) }) + patching.patchOrder(msg.Tags["Pushed-For"] or msg.Id, true) -- if this was paid on behalf of someone else -- we will also notify them @@ -74,6 +76,11 @@ function repay.error(msg, _, err) Quantity = msg.Tags.Quantity, Recipient = sender }) + patching.patchOrder( + msg.Tags["Pushed-For"] or msg.Id, + false, + prettyError + ) if assertions.isAddress(sender) then ao.send({ diff --git a/src/process.lua b/src/process.lua index ca230a5..8fb09b8 100644 --- a/src/process.lua +++ b/src/process.lua @@ -244,7 +244,8 @@ local function setup_handlers() Handlers.advanced(queue.useQueue({ name = "borrow-loan-borrow", pattern = { Action = "Borrow" }, - handle = oracle.withOracle(borrow) + handle = oracle.withOracle(borrow.borrow), + errorHandler = borrow.error })) Handlers.advanced(queue.useQueue({ name = "borrow-repay", @@ -306,7 +307,8 @@ local function setup_handlers() Handlers.advanced(queue.useQueue({ name = "supply-redeem", pattern = { Action = "Redeem" }, - handle = oracle.withOracle(redeem) + handle = oracle.withOracle(redeem.redeem), + errorHandler = redeem.error })) Handlers.add( diff --git a/src/supply/mint.lua b/src/supply/mint.lua index a27c7a4..a701d4a 100644 --- a/src/supply/mint.lua +++ b/src/supply/mint.lua @@ -2,6 +2,7 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" local interest = require ".borrow.interest" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) local utils = require ".utils.utils" @@ -57,6 +58,8 @@ function mint.handler(msg) Cash = tostring(availableTokens + quantity) TotalSupply = tostring(totalSupply + mintQty) + patching.patchOrder(msg.Tags["Pushed-For"] or msg.Id, true) + -- TODO: maybe we could msg.reply, but the target of that would be -- the token process that sent the "Credit-Notice" -- we need to discuss with the ao team if the reply is actually forwarded @@ -79,6 +82,11 @@ end function mint.error(msg, _, err) local prettyError, rawError = utils.prettyError(err) + patching.patchOrder( + msg.Tags["Pushed-For"] or msg.Id, + false, + prettyError + ) ao.send({ Target = CollateralID, Action = "Transfer", diff --git a/src/supply/redeem.lua b/src/supply/redeem.lua index 0661965..477ab92 100644 --- a/src/supply/redeem.lua +++ b/src/supply/redeem.lua @@ -2,11 +2,15 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" local position = require ".borrow.position" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) +local utils = require ".utils.utils" local rate = require ".supply.rate" +local mod = {} + ---@type HandlerWithOracle -local function redeem(msg, _, oracle) +function mod.redeem(msg, _, oracle) -- check if the interaction is enabled assert(EnabledInteractions.redeem, "Redeeming is currently disabled") @@ -93,6 +97,7 @@ local function redeem(msg, _, oracle) Quantity = tostring(rewardQty), Recipient = sender }) + patching.patchOrder(msg.Id, true) msg.reply({ Action = "Redeem-Confirmation", @@ -101,4 +106,19 @@ local function redeem(msg, _, oracle) }) end -return redeem +---@param msg Message +---@param _ Message +---@param err unknown +function mod.error(msg, _, err) + local prettyError, rawError = utils.prettyError(err) + + patching.patchOrder(msg.Id, false, prettyError) + ao.send({ + Target = msg.From, + Action = "Redeem-Error", + Error = prettyError, + ["Raw-Error"] = rawError + }) +end + +return mod diff --git a/src/token/patching.lua b/src/token/patching.lua index 85ab6a3..f5ab9aa 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -98,7 +98,24 @@ function mod.setup() borrowIndex = BorrowIndex, lastBorrowIndexUpdate = LastBorrowIndexUpdate, reservesRemainder = ReservesRemainder, - interestIndices = interestIndices + interestIndices = interestIndices, + orders = { device = "trie@1.0" } + }) +end + +-- Patch the result of a borrow/mint/redeem/repay +---@param id string The ID of the message that initiated the order (pushed for or message id) +---@param success boolean The result of the order +---@param message string? Optional result message +function mod.patchOrder(id, success, message) + ao.send({ + device = "patch@1.0", + orders = { + [id] = { + success = success, + message = message + } + } }) end From 18fd25c2f3834eeed56c12a697167bf2f6c56bb2 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 13:22:58 +0200 Subject: [PATCH 04/16] feat: restructure patching --- src/borrow/borrow.lua | 9 ++++++ src/token/patching.lua | 70 +++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/borrow/borrow.lua b/src/borrow/borrow.lua index 8e0ef34..644e5e2 100644 --- a/src/borrow/borrow.lua +++ b/src/borrow/borrow.lua @@ -86,6 +86,15 @@ function mod.borrow(msg, _, oracle) -- patch order result patching.patchOrder(msg.Id, true) + ao.send({ + device = "patch@1.0", + loans = { [account] = Loans[account] }, + ["token-info"] = { + cash = tostring(precision.toNativePrecision(bint(Cash), "rounddown")), + totalBorrows = tostring(precision.toNativePrecision(bint(TotalBorrows), "roundup")) + }, + interestIndices = { [account] = InterestIndices[account] } + }) -- send confirmation msg.reply({ diff --git a/src/token/patching.lua b/src/token/patching.lua index f5ab9aa..cf1d6ce 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -67,38 +67,58 @@ function mod.setup() ticker = Ticker, logo = Logo, denomination = tostring(Denomination), - supply = TotalSupply, - collateralId = CollateralID, - collateralFactor = tostring(CollateralFactor), - collateralDenomination = tostring(CollateralDenomination), - liquidationThreshold = tostring(LiquidationThreshold), - valueLimit = precision.formatInternalAsNative(ValueLimit, "rounddown"), - oracle = Oracle, - oracleDelayTolerance = tostring(MaxOracleDelay), - totalBorrows = tostring(precision.toNativePrecision(totalLent, "roundup")), + supply = TotalSupply + }, + environment = { + collateral = { + id = CollateralID, + denomination = CollateralDenomination + }, + ["risk-parameters"] = { + ["collateral-factor"] = CollateralFactor, + ["liquidation-threshold"] = LiquidationThreshold, + ["reserve-factor"] = ReserveFactor + }, + limits = { + ["value-limit"] = precision.formatInternalAsNative(ValueLimit, "rounddown"), + ["cooldown-period"] = CooldownPeriod, + ["enabled-interactions"] = enabledInteractions, + ["disabled-interactions"] = disabledInteractions + }, + ["oracle-parameters"] = { + oracle = Oracle, + ["oracle-delay-tolerance"] = MaxOracleDelay + }, + ["interest-model"] = { + rates = { + init = InitRate, + base = BaseRate, + jump = JumpRate + }, + ["kink-param"] = KinkParam + }, + friends = Friends + }, + ["pool-state"] = { + ["total-borrows"] = tostring(precision.toNativePrecision(totalLent, "roundup")), cash = tostring(precision.toNativePrecision(cash, "rounddown")), - reserveFactor = tostring(ReserveFactor), - totalReserves = tostring(precision.toNativePrecision(reserves, "roundup")), - initRate = tostring(InitRate), - baseRate = tostring(BaseRate), - jumpRate = tostring(JumpRate), - kinkParam = tostring(KinkParam), - cooldownPeriod = tostring(CooldownPeriod), + ["total-reserves"] = tostring(precision.toNativePrecision(reserves, "roundup")), utilization = tostring(utils.bintToFloat(utilization, utilizationDecimals)), - enabledInteractions = enabledInteractions, - disabledInteractions = disabledInteractions + rates = { + borrow = borrowRate, + supply = interest.calculateSupplyRate() + }, + ["interest-accrual"] = { + ["borrow-index"] = BorrowIndex, + ["last-updated"] = LastBorrowIndexUpdate, + ["reserves-remainder"] = ReservesRemainder + } }, balances = balances, - friends = Friends, cooldowns = Cooldowns, - borrowRate = borrowRate, - supplyRate = interest.calculateSupplyRate(), positions = positions, loans = loans, - borrowIndex = BorrowIndex, - lastBorrowIndexUpdate = LastBorrowIndexUpdate, - reservesRemainder = ReservesRemainder, - interestIndices = interestIndices, + ["interest-indices"] = interestIndices, orders = { device = "trie@1.0" } }) end From d5989ae1ac65a6f323aafec7c079c6bc715a8d68 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 14:27:18 +0200 Subject: [PATCH 05/16] feat: separate some patching logic --- src/token/patching.lua | 88 ++++++++++++++++++++++++++---------------- src/utils/utils.lua | 23 +++++++---- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/token/patching.lua b/src/token/patching.lua index cf1d6ce..391724f 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -3,37 +3,17 @@ local interest = require ".borrow.interest" local bint = require ".utils.bint"(1024) local utils = require ".utils.utils" -local mod = {} +local mod = { + utils = {} +} ---@type HandlerFunction function mod.setup() - -- parse as bint - local totalLent = bint(TotalBorrows) - local cash = bint(Cash) - local reserves = bint(Reserves) - -- calculate utilization - local totalPooled = totalLent + cash - reserves - local utilizationDecimals = 5 - local utilization = bint.zero() - - if not bint.eq(totalPooled, 0) then - utilization = bint.udiv( - totalLent * bint(100) * bint.ipow(10, utilizationDecimals), - totalPooled - ) - end + local utilization, utilizationDecimals = mod.utils.utilization() -- enabled and disabled interactions - local enabledInteractions = {} - local disabledInteractions = {} - - for name, enabled in pairs(EnabledInteractions) do - table.insert( - enabled and enabledInteractions or disabledInteractions, - string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2) - ) - end + local enabledInteractions, disabledInteractions = mod.utils.interactionsState() -- clone loans, interest indices, balances local loans = mod.mapAsTrie(Loans) @@ -41,11 +21,7 @@ function mod.setup() local balances = mod.mapAsTrie(Balances) -- current interest rate - local interestRate, rateMul = interest.calculateBorrowRate() - local borrowRate = utils.bintToFloat( - interestRate, - select(2, string.gsub(tostring(rateMul), "0", "")) - ) + local borrowRate = mod.utils.borrowRate() -- map positions local positions = { device = "trie@1.0" } @@ -100,10 +76,10 @@ function mod.setup() friends = Friends }, ["pool-state"] = { - ["total-borrows"] = tostring(precision.toNativePrecision(totalLent, "roundup")), - cash = tostring(precision.toNativePrecision(cash, "rounddown")), - ["total-reserves"] = tostring(precision.toNativePrecision(reserves, "roundup")), - utilization = tostring(utils.bintToFloat(utilization, utilizationDecimals)), + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-reserves"] = precision.formatInternalAsNative(Reserves, "roundup"), + utilization = utils.bintToFloatString(utilization, utilizationDecimals), rates = { borrow = borrowRate, supply = interest.calculateSupplyRate() @@ -153,4 +129,48 @@ function mod.mapAsTrie(map) return res end +function mod.utils.utilization() + -- parse as bint + local totalLent = bint(TotalBorrows) + local cash = bint(Cash) + local reserves = bint(Reserves) + + -- calculate utilization + local totalPooled = totalLent + cash - reserves + local utilizationDecimals = 5 + local utilization = bint.zero() + + if not bint.eq(totalPooled, 0) then + utilization = bint.udiv( + totalLent * bint(100) * bint.ipow(10, utilizationDecimals), + totalPooled + ) + end + + return utilization, utilizationDecimals +end + +function mod.utils.borrowRate() + local interestRate, rateMul = interest.calculateBorrowRate() + + return utils.bintToFloatString( + interestRate, + select(2, string.gsub(tostring(rateMul), "0", "")) + ) +end + +function mod.utils.interactionsState() + local enabled = {} + local disabled = {} + + for name, isEnabled in pairs(EnabledInteractions) do + table.insert( + isEnabled and enabled or disabled, + string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2) + ) + end + + return enabled, disabled +end + return mod diff --git a/src/utils/utils.lua b/src/utils/utils.lua index c7ce829..6db032f 100644 --- a/src/utils/utils.lua +++ b/src/utils/utils.lua @@ -27,7 +27,7 @@ function utils.matchesPattern(pattern, value, msg) return false end end - + -- if the patternMatchSpec is a string, check it for special symbols (less `-` alone) -- and exact string match mode if (type(pattern) == 'string') then @@ -346,7 +346,7 @@ function utils.floatBintRepresentation(raw, floatMul) return repr, floatMul end --- Create a pretty error message from any Lua error +-- Create a pretty error message from any Lua error -- (returns the pretty error and the raw stringified error) ---@param err unknown Original error message function utils.prettyError(err) @@ -361,25 +361,34 @@ function utils.floatToString(val) return string.format("%.17f", val):gsub("0+$", ""):gsub("%.$", "") end --- Convert a biginteger with a denomination to a float +-- Convert a biginteger with a denomination to a float string ---@param val Bint Bigint value ---@param denomination number Denomination -function utils.bintToFloat(val, denomination) +function utils.bintToFloatString(val, denomination) local stringVal = tostring(val) local len = #stringVal - if stringVal == "0" then return 0.0 end + if stringVal == "0" then return "0.0" end -- if denomination is greater than or equal to the string length, prepend "0." if denomination >= len then - return tonumber("0." .. string.rep("0", denomination - len) .. stringVal) + return "0." .. string.rep("0", denomination - len) .. stringVal end -- insert decimal point at the correct position from the back local integer_part = string.sub(stringVal, 1, len - denomination) local fractional_part = string.sub(stringVal, len - denomination + 1) - return tonumber(integer_part .. "." .. fractional_part) + return integer_part .. "." .. fractional_part +end + +-- Convert a biginteger with a denomination to a float +---@param val Bint Bigint value +---@param denomination number Denomination +function utils.bintToFloat(val, denomination) + return tonumber( + utils.bintToFloatString(val, denomination) + ) end -- Perform unsigned integer division, but rounding upwards From 741ee8a07b6a255ade38389df7ae28f11daab8ae Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 14:39:50 +0200 Subject: [PATCH 06/16] feat: borrow patching --- src/borrow/borrow.lua | 30 ++++++++++++++++++++++++------ src/token/patching.lua | 30 +++++++++++++++--------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/borrow/borrow.lua b/src/borrow/borrow.lua index 644e5e2..b3be414 100644 --- a/src/borrow/borrow.lua +++ b/src/borrow/borrow.lua @@ -1,5 +1,6 @@ local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local interest = require ".borrow.interest" local position = require ".borrow.position" local patching = require ".token.patching" local bint = require ".utils.bint"(1024) @@ -85,15 +86,32 @@ function mod.borrow(msg, _, oracle) }) -- patch order result - patching.patchOrder(msg.Id, true) + patching.patchOrder( + msg.Id, + true, + "Successfully borrowed " .. + utils.bintToFloatString(rawQuantity, CollateralDenomination) .. + " " .. + CollateralTicker + ) ao.send({ device = "patch@1.0", - loans = { [account] = Loans[account] }, - ["token-info"] = { - cash = tostring(precision.toNativePrecision(bint(Cash), "rounddown")), - totalBorrows = tostring(precision.toNativePrecision(bint(TotalBorrows), "roundup")) + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + utilization = patching.utils.utilization(), + rates = { + borrow = patching.utils.borrowRate(), + supply = interest.calculateSupplyRate() + } }, - interestIndices = { [account] = InterestIndices[account] } + positions = { + [account] = patching.utils.stringValPosition( + position.position(account) + ) + }, + loans = { [account] = Loans[account] }, + ["interest-indices"] = { [account] = InterestIndices[account] } }) -- send confirmation diff --git a/src/token/patching.lua b/src/token/patching.lua index 391724f..a1d07e9 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -9,9 +9,6 @@ local mod = { ---@type HandlerFunction function mod.setup() - -- calculate utilization - local utilization, utilizationDecimals = mod.utils.utilization() - -- enabled and disabled interactions local enabledInteractions, disabledInteractions = mod.utils.interactionsState() @@ -20,20 +17,12 @@ function mod.setup() local interestIndices = mod.mapAsTrie(InterestIndices) local balances = mod.mapAsTrie(Balances) - -- current interest rate - local borrowRate = mod.utils.borrowRate() - -- map positions local positions = { device = "trie@1.0" } local calculatedPositions = mod.allPositions() for address, position in pairs(calculatedPositions) do - positions[address] = { - collateralization = tostring(position.collateralization), - capacity = tostring(position.capacity), - borrowBalance = tostring(position.borrowBalance), - liquidationLimit = tostring(position.liquidationLimit) - } + positions[address] = mod.utils.stringValPosition(position) end ao.send({ @@ -79,9 +68,9 @@ function mod.setup() ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), cash = precision.formatInternalAsNative(Cash, "rounddown"), ["total-reserves"] = precision.formatInternalAsNative(Reserves, "roundup"), - utilization = utils.bintToFloatString(utilization, utilizationDecimals), + utilization = mod.utils.utilization(), rates = { - borrow = borrowRate, + borrow = mod.utils.borrowRate(), supply = interest.calculateSupplyRate() }, ["interest-accrual"] = { @@ -147,7 +136,7 @@ function mod.utils.utilization() ) end - return utilization, utilizationDecimals + return utils.bintToFloatString(utilization, utilizationDecimals) end function mod.utils.borrowRate() @@ -173,4 +162,15 @@ function mod.utils.interactionsState() return enabled, disabled end +-- Make position values strings from Bint +---@param position Position The position to transform +function mod.utils.stringValPosition(position) + return { + collateralization = tostring(position.collateralization), + capacity = tostring(position.capacity), + borrowBalance = tostring(position.borrowBalance), + liquidationLimit = tostring(position.liquidationLimit) + } +end + return mod From d09f1d1b4122b5d0b7ae7957161b60b1e1babbb0 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 14:49:58 +0200 Subject: [PATCH 07/16] feat: interest patching --- src/borrow/interest.lua | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/borrow/interest.lua b/src/borrow/interest.lua index 7746a8b..f5a2521 100644 --- a/src/borrow/interest.lua +++ b/src/borrow/interest.lua @@ -145,9 +145,6 @@ function mod.accrueInterest(msg) hundred ) - -- update the reserves remainder value - ReservesRemainder = tostring(remainder) - -- update global borrow index local borrowIndexUpdate = bint.udiv( borrowIndex * interestFactor, @@ -168,6 +165,23 @@ function mod.accrueInterest(msg) TotalBorrows = tostring(totalBorrows + interestAccrued) Reserves = tostring(reserves + reservesUpdate) BorrowIndex = tostring(borrowIndex + borrowIndexUpdate) + + -- update the reserves remainder value + ReservesRemainder = tostring(remainder) + + -- patch the updated globals + ao.send({ + device = "patch@1.0", + ["pool-state"] = { + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + ["total-reserves"] = precision.formatInternalAsNative(Reserves, "roundup"), + ["interest-accrual"] = { + ["borrow-index"] = BorrowIndex, + ["last-updated"] = LastBorrowIndexUpdate, + ["reserves-remainder"] = ReservesRemainder + } + } + }) end -- Accrues interest for a specific user and returns the updated borrow balance @@ -197,6 +211,14 @@ function mod.accrueInterestForUser(address) Loans[address] = tostring(borrowBalance) InterestIndices[address] = tostring(interestIndex) + -- patch updated global values + ao.send({ + device = "patch@1.0", + loans = { [address] = Loans[address] }, + ["interest-indices"] = { [address] = InterestIndices[address] }, + -- !!do not update positions to avoid an infinite recursive loop!! + }) + return borrowBalance end From eecab64d5b71d0d5c2d4d863448fa88653843d4c Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:01:04 +0200 Subject: [PATCH 08/16] feat: patch repay --- src/borrow/repay.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/borrow/repay.lua b/src/borrow/repay.lua index 02a28ee..f367754 100644 --- a/src/borrow/repay.lua +++ b/src/borrow/repay.lua @@ -1,5 +1,6 @@ local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local position = require ".borrow.position" local interest = require ".borrow.interest" local patching = require ".token.patching" local bint = require ".utils.bint"(1024) @@ -157,6 +158,27 @@ function repay.repayToPool(target, quantity) Cash = tostring(bint(Cash) + precision.toInternalPrecision(actualRepaidQty)) TotalBorrows = tostring(bint(TotalBorrows) - precision.toInternalPrecision(actualRepaidQty)) + -- patch the updated globals + ao.send({ + device = "patch@1.0", + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + utilization = patching.utils.utilization(), + rates = { + borrow = patching.utils.borrowRate(), + supply = interest.calculateSupplyRate() + } + }, + positions = { + [target] = patching.utils.stringValPosition( + position.position(target) + ) + }, + loans = { [target] = Loans[target] }, + ["interest-indices"] = { [target] = InterestIndices[target] } + }) + return refundQty, actualRepaidQty end From 45f67c817847ca27b437812331481821512e654d Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:13:53 +0200 Subject: [PATCH 09/16] feat: patch config --- src/controller/config.lua | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/controller/config.lua b/src/controller/config.lua index 3f104ff..1903fb0 100644 --- a/src/controller/config.lua +++ b/src/controller/config.lua @@ -1,5 +1,6 @@ local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) local mod = {} @@ -109,6 +110,34 @@ function mod.update(msg) if newWrappedAOToken then WrappedAOToken = newWrappedAOToken end if newAOToken then AOToken = newAOToken end + -- patch + ao.send({ + device = "patch@1.0", + environment = { + ["risk-parameters"] = { + ["collateral-factor"] = CollateralFactor, + ["liquidation-threshold"] = LiquidationThreshold, + ["reserve-factor"] = ReserveFactor + }, + limits = { + ["value-limit"] = precision.formatInternalAsNative(ValueLimit, "rounddown"), + ["cooldown-period"] = CooldownPeriod + }, + ["oracle-parameters"] = { + oracle = Oracle, + ["oracle-delay-tolerance"] = MaxOracleDelay + }, + ["interest-model"] = { + rates = { + init = InitRate, + base = BaseRate, + jump = JumpRate + }, + ["kink-param"] = KinkParam + } + } + }) + msg.reply({ Oracle = Oracle, ["Collateral-Factor"] = tostring(CollateralFactor), @@ -141,6 +170,19 @@ function mod.toggleInteractions(msg) EnabledInteractions = updatedInteractions + -- patch + local enabledInteractions, disabledInteractions = patching.utils.interactionsState() + + ao.send({ + device = "patch@1.0", + environment = { + limits = { + ["enabled-interactions"] = enabledInteractions, + ["disabled-interactions"] = disabledInteractions + } + } + }) + msg.reply({ Updated = "true" }) end From f50c23698e62da30b91b79e587d02d8bd7a4a129 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:18:26 +0200 Subject: [PATCH 10/16] feat: patch friends & cooldown --- src/controller/cooldown.lua | 8 +++++++- src/controller/friend.lua | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/controller/cooldown.lua b/src/controller/cooldown.lua index 855c6b5..dd80ba6 100644 --- a/src/controller/cooldown.lua +++ b/src/controller/cooldown.lua @@ -32,7 +32,7 @@ function mod.sync(msg) end end --- Cooldown middleware/gate that rejects +-- Cooldown middleware/gate that rejects -- interactions if the user is on cooldown ---@type HandlerFunction function mod.gate(msg) @@ -47,6 +47,12 @@ function mod.gate(msg) -- add user cooldown Cooldowns[sender] = msg["Block-Height"] + CooldownPeriod + + -- patch + ao.send({ + device = "patch@1.0", + cooldowns = { [sender] = Cooldowns[sender] } + }) end -- Refunds the user and sends the cooldown error diff --git a/src/controller/friend.lua b/src/controller/friend.lua index 1145fde..4e751ca 100644 --- a/src/controller/friend.lua +++ b/src/controller/friend.lua @@ -51,6 +51,12 @@ function friend.add(msg) denomination = friendDenomination }) + -- patch + ao.send({ + device = "patch@1.0", + environment = { friends = Friends } + }) + -- notify the sender msg.reply({ Action = "Friend-Added", @@ -82,6 +88,12 @@ function friend.remove(msg) -- check if any were removed assert(#removed > 0, "Friend " .. target .. " not yet added") + -- patch + ao.send({ + device = "patch@1.0", + environment = { friends = Friends } + }) + -- notify the sender msg.reply({ Action = "Friend-Removed", From 20e871be5fb5a6cdf5ca8880fb2752dcd4165c5e Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:21:26 +0200 Subject: [PATCH 11/16] feat: patch reserves --- src/controller/reserves.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/controller/reserves.lua b/src/controller/reserves.lua index 68f7fac..664f96c 100644 --- a/src/controller/reserves.lua +++ b/src/controller/reserves.lua @@ -65,6 +65,15 @@ function mod.deploy(msg) Reserves = tostring(reserves - quantity) Cash = tostring(bint(Cash) + quantity) + -- patch + ao.send({ + device = "patch@1.0", + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-reserves"] = precision.formatInternalAsNative(Reserves, "roundup") + } + }) + -- reply msg.reply({ ["Total-Reserves"] = precision.formatInternalAsNative(Reserves, "roundup"), From 6372cfef465271fa0b9a3f32fe3a00724c54e730 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:28:19 +0200 Subject: [PATCH 12/16] feat: patch liquidation --- src/liquidations/liquidate.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/liquidations/liquidate.lua b/src/liquidations/liquidate.lua index 80939c7..abb2057 100644 --- a/src/liquidations/liquidate.lua +++ b/src/liquidations/liquidate.lua @@ -1,7 +1,9 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local position = require ".borrow.position" local interest = require ".borrow.interest" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) local repay = require ".borrow.repay" local utils = require ".utils.utils" @@ -240,6 +242,26 @@ function mod.liquidatePosition(msg) Recipient = liquidator }) + -- patch + ao.send({ + device = "patch@1.0", + ["token-info"] = { supply = TotalSupply }, + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + utilization = mod.utils.utilization(), + rates = { + borrow = mod.utils.borrowRate(), + supply = interest.calculateSupplyRate() + } + }, + balances = { [target] = Balances[target] }, + positions = { + [target] = patching.utils.stringValPosition( + position.position(target) + ) + } + }) + -- reply to the controller msg.reply({ Action = "Liquidate-Position-Confirmation", From 0fd5fd4e1706d04b4eee0a0232617843bab3aa04 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:43:21 +0200 Subject: [PATCH 13/16] feat: patch minting --- src/supply/mint.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/supply/mint.lua b/src/supply/mint.lua index a701d4a..2483343 100644 --- a/src/supply/mint.lua +++ b/src/supply/mint.lua @@ -1,6 +1,7 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local position = require ".borrow.position" local interest = require ".borrow.interest" local patching = require ".token.patching" local bint = require ".utils.bint"(1024) @@ -58,7 +59,26 @@ function mint.handler(msg) Cash = tostring(availableTokens + quantity) TotalSupply = tostring(totalSupply + mintQty) + -- patch patching.patchOrder(msg.Tags["Pushed-For"] or msg.Id, true) + ao.send({ + device = "patch@1.0", + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + utilization = patching.utils.utilization(), + rates = { + borrow = patching.utils.borrowRate(), + supply = interest.calculateSupplyRate() + } + }, + balances = { [sender] = Balances[sender] }, + positions = { + [sender] = patching.utils.stringValPosition( + position.position(sender) + ) + } + }) -- TODO: maybe we could msg.reply, but the target of that would be -- the token process that sent the "Credit-Notice" From 1bab7a22c023d973efc510412d138d9807d5b302 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 15:47:30 +0200 Subject: [PATCH 14/16] fix: patching errors --- src/liquidations/liquidate.lua | 4 ++-- src/token/patching.lua | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/liquidations/liquidate.lua b/src/liquidations/liquidate.lua index abb2057..11434fb 100644 --- a/src/liquidations/liquidate.lua +++ b/src/liquidations/liquidate.lua @@ -248,9 +248,9 @@ function mod.liquidatePosition(msg) ["token-info"] = { supply = TotalSupply }, ["pool-state"] = { cash = precision.formatInternalAsNative(Cash, "rounddown"), - utilization = mod.utils.utilization(), + utilization = patching.utils.utilization(), rates = { - borrow = mod.utils.borrowRate(), + borrow = patching.utils.borrowRate(), supply = interest.calculateSupplyRate() } }, diff --git a/src/token/patching.lua b/src/token/patching.lua index a1d07e9..146b9b0 100644 --- a/src/token/patching.lua +++ b/src/token/patching.lua @@ -1,4 +1,5 @@ local precision = require ".utils.precision" +local position = require ".borrow.position" local interest = require ".borrow.interest" local bint = require ".utils.bint"(1024) local utils = require ".utils.utils" @@ -19,10 +20,10 @@ function mod.setup() -- map positions local positions = { device = "trie@1.0" } - local calculatedPositions = mod.allPositions() + local calculatedPositions = position.allPositions() - for address, position in pairs(calculatedPositions) do - positions[address] = mod.utils.stringValPosition(position) + for address, pos in pairs(calculatedPositions) do + positions[address] = mod.utils.stringValPosition(pos) end ao.send({ @@ -163,13 +164,13 @@ function mod.utils.interactionsState() end -- Make position values strings from Bint ----@param position Position The position to transform -function mod.utils.stringValPosition(position) +---@param pos Position The position to transform +function mod.utils.stringValPosition(pos) return { - collateralization = tostring(position.collateralization), - capacity = tostring(position.capacity), - borrowBalance = tostring(position.borrowBalance), - liquidationLimit = tostring(position.liquidationLimit) + collateralization = tostring(pos.collateralization), + capacity = tostring(pos.capacity), + borrowBalance = tostring(pos.borrowBalance), + liquidationLimit = tostring(pos.liquidationLimit) } end From 1bf591d1cd2c05098b6281114d106160a7b339f8 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 16:53:30 +0200 Subject: [PATCH 15/16] feat: patch transfer --- src/supply/redeem.lua | 19 +++++++++++++++++++ src/token/transfer.lua | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/supply/redeem.lua b/src/supply/redeem.lua index 477ab92..47da439 100644 --- a/src/supply/redeem.lua +++ b/src/supply/redeem.lua @@ -1,6 +1,7 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" +local interest = require ".borrow.interest" local position = require ".borrow.position" local patching = require ".token.patching" local bint = require ".utils.bint"(1024) @@ -98,6 +99,24 @@ function mod.redeem(msg, _, oracle) Recipient = sender }) patching.patchOrder(msg.Id, true) + ao.send({ + device = "patch@1.0", + ["pool-state"] = { + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-borrows"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + utilization = patching.utils.utilization(), + rates = { + borrow = patching.utils.borrowRate(), + supply = interest.calculateSupplyRate() + } + }, + balances = { [sender] = Balances[sender] }, + positions = { + [sender] = patching.utils.stringValPosition( + position.position(sender) + ) + } + }) msg.reply({ Action = "Redeem-Confirmation", diff --git a/src/token/transfer.lua b/src/token/transfer.lua index a5a00e8..9d882ac 100644 --- a/src/token/transfer.lua +++ b/src/token/transfer.lua @@ -2,6 +2,7 @@ local delegation = require ".supply.delegation" local assertions = require ".utils.assertions" local precision = require ".utils.precision" local position = require ".borrow.position" +local patching = require ".token.patching" local bint = require ".utils.bint"(1024) local rate = require ".supply.rate" @@ -61,6 +62,23 @@ local function transfer(msg, _, oracle) Balances[target] = tostring(bint(Balances[target] or 0) + quantity) Balances[sender] = tostring(walletBalance - quantity) + -- patch transfer + ao.send({ + device = "patch@1.0", + balances = { + [sender] = Balances[sender], + [target] = Balances[target] + }, + positions = { + [sender] = patching.utils.stringValPosition( + position.position(sender) + ), + [target] = patching.utils.stringValPosition( + position.position(target) + ) + } + }) + -- send notices about the transfer if not msg.Tags.Cast then local debitNotice = { From 86791fbe30e80b3450fc858ba20fa75dc830ce71 Mon Sep 17 00:00:00 2001 From: Marton Lederer Date: Thu, 9 Oct 2025 16:59:34 +0200 Subject: [PATCH 16/16] fix: tests --- __tests__/supply.test.ts | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/__tests__/supply.test.ts b/__tests__/supply.test.ts index 46cece0..3259cc0 100644 --- a/__tests__/supply.test.ts +++ b/__tests__/supply.test.ts @@ -74,17 +74,29 @@ describe("Minting and providing", () => { }); it("Does not throw unhandled error for debit notices", async () => { - const res = await handle(createMessage({ + const msg = createMessage({ Action: "Debit-Notice", Owner: tags["Collateral-Id"], From: tags["Collateral-Id"], "From-Process": tags["Collateral-Id"], Quantity: "15", Recipient: generateArweaveAddress() - })); + }); + const res = await handle(msg); - expect(res.Messages).toEqual([]); - expect(res.Messages).toHaveLength(0); + expect(res.Messages).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: msg.From, + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Action", + value: "Debit-Notice-Error" + }) + ]) + }) + ]) + ); }); it("Does not handle invalid token quantities", async () => { @@ -901,7 +913,19 @@ describe("AO delegation", () => { const handle = await setupProcess(env); const res = await handle(createMessage({ Action: "Delegate" })); - expect(res.Messages).toHaveLength(0); + expect(res.Messages).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: tags["Wrapped-AO-Token"], + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Action", + value: "Claim" + }) + ]) + }) + ]) + ); }); it("Does not delegate any tokens if the claim failed", async () => {