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
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v2

- name: Install ao
run: curl -L https://install_ao.g8way.io | bash
run: curl -L https://install_ao.arweave.net | bash

- name: Build module
run: |
Expand Down
290 changes: 290 additions & 0 deletions __tests__/supply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,293 @@ describe("Price and underlying asset value, supplies after initial provide", ()

it.todo("Price input quantity is 1 by default when there is no quantity provided");
});

describe("AO delegation", () => {
let handle: HandleFunction;
let tags: Record<string, string>;

beforeEach(async () => {
const envWithWAO = {
Process: {
...env.Process,
Tags: [
...env.Process.Tags,
{ name: "AO-Token", value: generateArweaveAddress() },
{ name: "Wrapped-AO-Token", value: generateArweaveAddress() },
]
}
};

handle = await setupProcess(envWithWAO);
tags = normalizeTags(envWithWAO.Process.Tags);
});

it("Does not run delegate if there is no wAO token process defined", async () => {
const handle = await setupProcess(env);
const res = await handle(createMessage({ Action: "Delegate" }));

expect(res.Messages).toHaveLength(0);
});

it("Does not delegate any tokens if the claim failed", async () => {
const delegateRes = await handle(createMessage({ Action: "Delegate" }));

expect(delegateRes.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Target: tags["Wrapped-AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Claim"
})
])
})
])
);

const claimRes = await handle(createMessage({
Owner: tags["Wrapped-AO-Token"],
From: tags["Wrapped-AO-Token"],
Action: "Claim-Error",
Error: "No balance",
"X-Reference": normalizeTags(
getMessageByAction("Claim", delegateRes.Messages)?.Tags || []
)["Reference"]
}));

expect(claimRes.Messages).toHaveLength(0);
});

it("Does not distribute an invalid quantity", async () => {
const id = generateArweaveAddress();
const delegateRes = await handle(createMessage({
Id: id,
Action: "Delegate"
}));

expect(delegateRes.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Target: tags["Wrapped-AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Claim"
})
])
})
])
);

const claimRes = await handle(createMessage({
Owner: tags["AO-Token"],
From: tags["AO-Token"],
Action: "Credit-Notice",
Quantity: "invalid",
Sender: tags["Wrapped-AO-Token"],
["Pushed-For"]: id
}));

expect(claimRes.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Credit-Notice-Error"
}),
expect.objectContaining({
name: "Error",
value: expect.stringContaining(
"Invalid claimed quantity"
)
})
])
})
])
);
});

it("Distributes the claimed quantity evenly", async () => {
// prepare by adding holders
const balances = [
{ addr: generateArweaveAddress(), qty: "2" },
{ addr: generateArweaveAddress(), qty: "1" }
];
await handle(createMessage({
Action: "Update",
Data: `Balances = { ["${balances[0].addr}"] = "${balances[0].qty}", ["${balances[1].addr}"] = "${balances[1].qty}" }
TotalSupply = "3"`
}));

// distribute
const id = generateArweaveAddress();
const delegateRes = await handle(createMessage({
Id: id,
Action: "Delegate"
}));

expect(delegateRes.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Target: tags["Wrapped-AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Claim"
})
])
})
])
);

const claimRes = await handle(createMessage({
Owner: tags["AO-Token"],
From: tags["AO-Token"],
Action: "Credit-Notice",
Quantity: "6",
Sender: tags["Wrapped-AO-Token"],
["Pushed-For"]: id
}));

expect(claimRes.Messages).toEqual(
expect.arrayContaining(balances.map((balance) => (
expect.objectContaining({
Target: tags["AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Transfer"
}),
expect.objectContaining({
name: "Recipient",
value: balance.addr
}),
expect.objectContaining({
name: "Quantity",
value: (parseInt(balance.qty) * 2).toString()
}),
])
})
)))
);
});

it("Distributes the claimed quantity with a remainder", async () => {
// prepare by adding holders
const balances = [
{ addr: generateArweaveAddress(), qty: "2" },
{ addr: generateArweaveAddress(), qty: "1" }
];
await handle(createMessage({
Action: "Update",
Data: `Balances = { ["${balances[0].addr}"] = "${balances[0].qty}", ["${balances[1].addr}"] = "${balances[1].qty}" }
TotalSupply = "3"`
}));

// distribute
const id = generateArweaveAddress();
const delegateRes = await handle(createMessage({
Id: id,
Action: "Delegate"
}));

expect(delegateRes.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Target: tags["Wrapped-AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Claim"
})
])
})
])
);

const claimRes = await handle(createMessage({
Owner: tags["AO-Token"],
From: tags["AO-Token"],
Action: "Credit-Notice",
Quantity: "7",
Sender: tags["Wrapped-AO-Token"],
["Pushed-For"]: id
}));

expect(claimRes.Messages).toEqual(
expect.arrayContaining(balances.map((balance) => (
expect.objectContaining({
Target: tags["AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Transfer"
}),
expect.objectContaining({
name: "Recipient",
value: balance.addr
}),
expect.objectContaining({
name: "Quantity",
value: (parseInt(balance.qty) * 2).toString()
}),
])
})
)))
);

// now check redistribution of the remainder
const id2 = generateArweaveAddress();
const delegateRes2 = await handle(createMessage({
Id: id2,
Action: "Delegate"
}));

expect(delegateRes2.Messages).toEqual(
expect.arrayContaining([
expect.objectContaining({
Target: tags["Wrapped-AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Claim"
})
])
})
])
);

const claimRes2 = await handle(createMessage({
Owner: tags["AO-Token"],
From: tags["AO-Token"],
Action: "Credit-Notice",
Quantity: "2",
Sender: tags["Wrapped-AO-Token"],
["Pushed-For"]: id2
}));

expect(claimRes2.Messages).toEqual(
expect.arrayContaining(balances.map((balance) => (
expect.objectContaining({
Target: tags["AO-Token"],
Tags: expect.arrayContaining([
expect.objectContaining({
name: "Action",
value: "Transfer"
}),
expect.objectContaining({
name: "Recipient",
value: balance.addr
}),
expect.objectContaining({
name: "Quantity",
value: balance.qty
}),
])
})
)))
);
});
});
3 changes: 3 additions & 0 deletions src/borrow/pool.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ function mod.setup(msg)
"Invalid collateral id"
)

-- AO token process
AOToken = AOToken or ao.env.Process.Tags["AO-Token"] or "0syT13r0s0tgPmIed95bJnuSqaD29HQNN8D3ElLSrsc"

-- token that can be lent/borrowed
CollateralID = CollateralID or ao.env.Process.Tags["Collateral-Id"]

Expand Down
16 changes: 15 additions & 1 deletion src/controller/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ function mod.update(msg)
local newJumpRate = tonumber(msg.Tags["Jump-Rate"])
local newInitRate = tonumber(msg.Tags["Init-Rate"])
local newCooldownPeriod = tonumber(msg.Tags["Cooldown-Period"])
local newWrappedAOToken = msg.Tags["Wrapped-AO-Token"]
local newAOToken = msg.Tags["AO-Token"]

-- validate new config values, update
assert(
Expand Down Expand Up @@ -62,6 +64,14 @@ function mod.update(msg)
not newCooldownPeriod or assertions.isValidInteger(newCooldownPeriod),
"Invalid cooldown period"
)
assert(
not newWrappedAOToken or assertions.isAddress(newWrappedAOToken),
"Invalid Wrapped AO token process ID"
)
assert(
not newAOToken or assertions.isAddress(newAOToken),
"Invalid AO token process ID"
)

if newValueLimit then
assert(
Expand Down Expand Up @@ -96,6 +106,8 @@ function mod.update(msg)
if newJumpRate then JumpRate = newJumpRate end
if newInitRate then InitRate = newInitRate end
if newCooldownPeriod then CooldownPeriod = newCooldownPeriod end
if newWrappedAOToken then WrappedAOToken = newWrappedAOToken end
if newAOToken then AOToken = newAOToken end

msg.reply({
Oracle = Oracle,
Expand All @@ -107,7 +119,9 @@ function mod.update(msg)
["Kink-Param"] = tostring(KinkParam),
["Base-Rate"] = tostring(BaseRate),
["Jump-Rate"] = tostring(JumpRate),
["Init-Rate"] = tostring(InitRate)
["Init-Rate"] = tostring(InitRate),
["AO-Token"] = tostring(AOToken),
["Wrapped-AO-Token"] = tostring(WrappedAOToken)
})
end

Expand Down
Loading