diff --git a/__tests__/controller.test.ts b/__tests__/controller.test.ts index 18228f6..fd32f0b 100644 --- a/__tests__/controller.test.ts +++ b/__tests__/controller.test.ts @@ -1831,3 +1831,118 @@ describe("Reserves tests", () => { ); }); }); + +describe("Interactions toggler tests", () => { + let handle: HandleFunction; + let controller: string; + let tags: Record; + + beforeAll(async () => { + handle = await setupProcess(env); + controller = env.Process.Owner; + tags = normalizeTags(env.Process.Tags); + }); + + it("Does not allow the toggle function to be called by anyone other than the controller", async () => { + const otherAddr = generateArweaveAddress(); + const res = await handle(createMessage({ + From: otherAddr, + Owner: otherAddr, + Action: "Toggle-Interactions", + Mint: "Enabled", + Borrow: "Disabled" + })); + + expect(res.Messages).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: otherAddr, + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Error", + value: expect.stringContaining( + "The request could not be handled" + ) + }) + ]) + }) + ]) + ); + }); + + it("Toggles defined functions", async () => { + const res = await handle(createMessage({ + Action: "Toggle-Interactions", + Mint: "Disabled", + Borrow: "Enabled" + })); + + expect(res.Messages).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: controller, + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Updated", + value: "true" + }) + ]) + }) + ]) + ); + + const infoRes = await handle(createMessage({ Action: "Info" })); + + expect(infoRes.Messages).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: controller, + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Enabled-Interactions", + value: expect.toBeJsonEncoded(expect.not.arrayContaining(["Mint"])) + }), + expect.objectContaining({ + name: "Disabled-Interactions", + value: expect.toBeJsonEncoded(expect.arrayContaining(["Mint"])) + }) + ]) + }) + ]) + ); + + const sender = generateArweaveAddress(); + const borrowRes = await handle(createMessage({ + Action: "Credit-Notice", + "X-Action": "Mint", + Owner: tags["Collateral-Id"], + From: tags["Collateral-Id"], + "From-Process": tags["Collateral-Id"], + Quantity: "1", + Recipient: env.Process.Id, + Sender: sender + })); + const queueRes = await handle(createMessage({ + "Queued-User": sender, + "X-Reference": normalizeTags( + getMessageByAction("Add-To-Queue", borrowRes.Messages)?.Tags || [] + )["Reference"] + })); + + expect(queueRes.Messages).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + Target: sender, + Tags: expect.arrayContaining([ + expect.objectContaining({ + name: "Error", + value: expect.stringContaining( + "Minting is currently disabled" + ) + }) + ]) + }) + ]) + ); + }); +}); diff --git a/src/borrow/borrow.lua b/src/borrow/borrow.lua index f52718d..d18e241 100644 --- a/src/borrow/borrow.lua +++ b/src/borrow/borrow.lua @@ -17,6 +17,9 @@ local function borrow(msg, _, oracle) -- get position data local pos = position.globalPosition(account, oracle) + -- check if the interaction is enabled + assert(EnabledInteractions.borrow, "Borrowing is currently disabled") + -- amount of tokens to borrow local rawQuantity = bint(msg.Tags.Quantity) local quantity = precision.toInternalPrecision(rawQuantity) diff --git a/src/borrow/pool.lua b/src/borrow/pool.lua index 1595ec2..24a5fed 100644 --- a/src/borrow/pool.lua +++ b/src/borrow/pool.lua @@ -81,6 +81,16 @@ function mod.setup(msg) -- reserves ReserveFactor = ReserveFactor or tonumber(ao.env.Process.Tags["Reserve-Factor"]) or 0 Reserves = Reserves or "0" + + -- enabled functionalities + EnabledInteractions = EnabledInteractions or { + mint = true, + redeem = true, + borrow = true, + repay = true, + transfer = true, + liquidation = true + } end -- This syncs the global timestamp and block using the current message diff --git a/src/borrow/repay.lua b/src/borrow/repay.lua index 36faf43..7037f49 100644 --- a/src/borrow/repay.lua +++ b/src/borrow/repay.lua @@ -8,6 +8,9 @@ local repay = {} ---@type HandlerFunction function repay.handler(msg) + -- check if the interaction is enabled + assert(EnabledInteractions.repay, "Repaying is currently disabled") + assert( assertions.isTokenQuantity(msg.Tags.Quantity), "Invalid incoming transfer quantity" diff --git a/src/controller/config.lua b/src/controller/config.lua index b02406f..3f104ff 100644 --- a/src/controller/config.lua +++ b/src/controller/config.lua @@ -125,4 +125,23 @@ function mod.update(msg) }) end +-- Handler to enable interactions +---@type HandlerFunction +function mod.toggleInteractions(msg) + local updatedInteractions = {} + + for name, value in pairs(EnabledInteractions) do + local expectedTagName = string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2) + updatedInteractions[name] = value + + if msg.Tags[expectedTagName] ~= nil then + updatedInteractions[name] = msg.Tags[expectedTagName] == "Enabled" + end + end + + EnabledInteractions = updatedInteractions + + msg.reply({ Updated = "true" }) +end + return mod diff --git a/src/liquidations/liquidate.lua b/src/liquidations/liquidate.lua index 4cf13c8..129b5ee 100644 --- a/src/liquidations/liquidate.lua +++ b/src/liquidations/liquidate.lua @@ -11,6 +11,9 @@ local mod = {} -- (similar functionality to repaying) ---@type HandlerFunction function mod.liquidateBorrow(msg) + -- check if the interaction is enabled + assert(EnabledInteractions.liquidation, "Borrow liquidation is currently disabled") + assert( assertions.isTokenQuantity(msg.Tags.Quantity), "Invalid incoming transfer quantity" @@ -150,6 +153,9 @@ end -- (reverse redeem) ---@type HandlerFunction function mod.liquidatePosition(msg) + -- check if the interaction is enabled + assert(EnabledInteractions.liquidation, "Position liquidation is currently disabled") + -- check if the message is coming from a friend process assert( assertions.isFriend(msg.From) or msg.From == ao.id, diff --git a/src/process.lua b/src/process.lua index 33dea04..39257d6 100644 --- a/src/process.lua +++ b/src/process.lua @@ -87,7 +87,7 @@ local function setup_handlers() if msg.Tags.Action ~= "Credit-Notice" then return false -- not a token transfer end - if WrappedAO ~= nil and msg.From == AOToken and msg.Tags.Sender == WrappedAO then + if WrappedAOToken ~= nil and msg.From == AOToken and msg.Tags.Sender == WrappedAOToken then return false -- this oToken process accrues AO and the message is a wAO claim response end if msg.From ~= CollateralID then @@ -180,6 +180,11 @@ local function setup_handlers() { From = Controller, Action = "Update-Config" }, config.update ) + Handlers.add( + "controller-toggle-interactions", + { From = Controller, Action = "Toggle-Interactions" }, + config.toggleInteractions + ) Handlers.advanced({ name = "liquidate-borrow", diff --git a/src/supply/mint.lua b/src/supply/mint.lua index cb845dd..67269ee 100644 --- a/src/supply/mint.lua +++ b/src/supply/mint.lua @@ -8,6 +8,9 @@ local mint = {} ---@type HandlerFunction function mint.handler(msg) + -- check if the interaction is enabled + assert(EnabledInteractions.mint, "Minting is currently disabled") + assert( assertions.isTokenQuantity(msg.Tags.Quantity), "Invalid incoming transfer quantity" diff --git a/src/supply/redeem.lua b/src/supply/redeem.lua index 04be155..c43dfeb 100644 --- a/src/supply/redeem.lua +++ b/src/supply/redeem.lua @@ -6,6 +6,9 @@ local rate = require ".supply.rate" ---@type HandlerWithOracle local function redeem(msg, _, oracle) + -- check if the interaction is enabled + assert(EnabledInteractions.redeem, "Redeeming is currently disabled") + -- the wallet that is burning the tokens local sender = msg.From diff --git a/src/token/token.lua b/src/token/token.lua index 6240bed..23749f4 100644 --- a/src/token/token.lua +++ b/src/token/token.lua @@ -1,6 +1,7 @@ local precision = require ".utils.precision" local bint = require ".utils.bint"(1024) local utils = require ".utils.utils" +local json = require "json" local mod = {} @@ -48,6 +49,17 @@ function mod.info(msg) ) 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 + msg.reply({ Name = Name, Ticker = Ticker, @@ -70,7 +82,9 @@ function mod.info(msg) ["Jump-Rate"] = tostring(JumpRate), ["Kink-Param"] = tostring(KinkParam), ["Cooldown-Period"] = tostring(CooldownPeriod), - Utilization = tostring(utils.bintToFloat(utilization, utilizationDecimals)) + Utilization = tostring(utils.bintToFloat(utilization, utilizationDecimals)), + ["Enabled-Interactions"] = json.encode(enabledInteractions), + ["Disabled-Interactions"] = json.encode(disabledInteractions) }) end diff --git a/src/token/transfer.lua b/src/token/transfer.lua index 9b0f875..b768387 100644 --- a/src/token/transfer.lua +++ b/src/token/transfer.lua @@ -13,6 +13,9 @@ local function transfer(msg, _, oracle) -- get position data local pos = position.globalPosition(sender, oracle) + -- check if the interaction is enabled + assert(EnabledInteractions.transfer, "Transferring is currently disabled") + -- validate target and quantity assert(assertions.isAddress(target), "Invalid address") assert(target ~= sender, "Target cannot be the sender")