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 () => { diff --git a/src/borrow/borrow.lua b/src/borrow/borrow.lua index d18e241..b3be414 100644 --- a/src/borrow/borrow.lua +++ b/src/borrow/borrow.lua @@ -1,10 +1,15 @@ 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) +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 +85,35 @@ local function borrow(msg, _, oracle) Recipient = account }) + -- patch order result + patching.patchOrder( + msg.Id, + true, + "Successfully borrowed " .. + utils.bintToFloatString(rawQuantity, CollateralDenomination) .. + " " .. + CollateralTicker + ) + 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 = { + [account] = patching.utils.stringValPosition( + position.position(account) + ) + }, + loans = { [account] = Loans[account] }, + ["interest-indices"] = { [account] = InterestIndices[account] } + }) + -- send confirmation msg.reply({ Action = "Borrow-Confirmation", @@ -87,4 +121,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/interest.lua b/src/borrow/interest.lua index 84e000c..f5a2521 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 @@ -140,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, @@ -163,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 @@ -192,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 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/borrow/repay.lua b/src/borrow/repay.lua index 7037f49..f367754 100644 --- a/src/borrow/repay.lua +++ b/src/borrow/repay.lua @@ -1,6 +1,8 @@ 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 utils = require ".utils.utils" @@ -48,6 +50,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 +77,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({ @@ -150,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 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 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", 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"), diff --git a/src/liquidations/liquidate.lua b/src/liquidations/liquidate.lua index 80939c7..11434fb 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 = patching.utils.utilization(), + rates = { + borrow = patching.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", diff --git a/src/process.lua b/src/process.lua index b147767..8fb09b8 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 ) @@ -242,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", @@ -304,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..2483343 100644 --- a/src/supply/mint.lua +++ b/src/supply/mint.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 utils = require ".utils.utils" @@ -57,6 +59,27 @@ 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" -- we need to discuss with the ao team if the reply is actually forwarded @@ -79,6 +102,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..47da439 100644 --- a/src/supply/redeem.lua +++ b/src/supply/redeem.lua @@ -1,12 +1,17 @@ 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) +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 +98,25 @@ local function redeem(msg, _, oracle) Quantity = tostring(rewardQty), 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", @@ -101,4 +125,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 new file mode 100644 index 0000000..146b9b0 --- /dev/null +++ b/src/token/patching.lua @@ -0,0 +1,177 @@ +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" + +local mod = { + utils = {} +} + +---@type HandlerFunction +function mod.setup() + -- enabled and disabled interactions + local enabledInteractions, disabledInteractions = mod.utils.interactionsState() + + -- clone loans, interest indices, balances + local loans = mod.mapAsTrie(Loans) + local interestIndices = mod.mapAsTrie(InterestIndices) + local balances = mod.mapAsTrie(Balances) + + -- map positions + local positions = { device = "trie@1.0" } + local calculatedPositions = position.allPositions() + + for address, pos in pairs(calculatedPositions) do + positions[address] = mod.utils.stringValPosition(pos) + end + + ao.send({ + device = "patch@1.0", + ["token-info"] = { + name = Name, + ticker = Ticker, + logo = Logo, + denomination = tostring(Denomination), + 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"] = precision.formatInternalAsNative(TotalBorrows, "roundup"), + cash = precision.formatInternalAsNative(Cash, "rounddown"), + ["total-reserves"] = precision.formatInternalAsNative(Reserves, "roundup"), + utilization = mod.utils.utilization(), + rates = { + borrow = mod.utils.borrowRate(), + supply = interest.calculateSupplyRate() + }, + ["interest-accrual"] = { + ["borrow-index"] = BorrowIndex, + ["last-updated"] = LastBorrowIndexUpdate, + ["reserves-remainder"] = ReservesRemainder + } + }, + balances = balances, + cooldowns = Cooldowns, + positions = positions, + loans = loans, + ["interest-indices"] = 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 + +-- 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 + +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 utils.bintToFloatString(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 + +-- Make position values strings from Bint +---@param pos Position The position to transform +function mod.utils.stringValPosition(pos) + return { + collateralization = tostring(pos.collateralization), + capacity = tostring(pos.capacity), + borrowBalance = tostring(pos.borrowBalance), + liquidationLimit = tostring(pos.liquidationLimit) + } +end + +return mod 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 = { 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