Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions __tests__/supply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
53 changes: 51 additions & 2 deletions src/borrow/borrow.lua
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -80,11 +85,55 @@ 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",
["Borrowed-Quantity"] = tostring(rawQuantity)
})
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
39 changes: 33 additions & 6 deletions src/borrow/interest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
51 changes: 28 additions & 23 deletions src/borrow/position.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -165,12 +191,9 @@ end
function mod.handlers.allPositions(msg)
---@type table<string, { Collateralization: string, Capacity: string, Borrow-Balance: string, Liquidation-Limit: string }>
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),
Expand All @@ -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,
Expand Down
29 changes: 29 additions & 0 deletions src/borrow/repay.lua
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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
Expand All @@ -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({
Expand Down Expand Up @@ -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

Expand Down
42 changes: 42 additions & 0 deletions src/controller/config.lua
Original file line number Diff line number Diff line change
@@ -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 = {}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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

Expand Down
Loading