vault: designate tokens for a single recipient

This commit is contained in:
Mark Spanbroek 2025-01-15 11:43:18 +01:00
parent 7018f9ef22
commit e1f914726b
2 changed files with 137 additions and 19 deletions

View File

@ -15,6 +15,8 @@ contract Vault {
mapping(Controller => mapping(Context => mapping(Recipient => uint256)))
private _available;
mapping(Controller => mapping(Context => mapping(Recipient => uint256)))
private _designated;
constructor(IERC20 token) {
_token = token;
@ -25,7 +27,17 @@ contract Vault {
Recipient recipient
) public view returns (uint256) {
Controller controller = Controller.wrap(msg.sender);
return _available[controller][context][recipient];
return
_available[controller][context][recipient] +
_designated[controller][context][recipient];
}
function designated(
Context context,
Recipient recipient
) public view returns (uint256) {
Controller controller = Controller.wrap(msg.sender);
return _designated[controller][context][recipient];
}
function deposit(Context context, address from, uint256 amount) public {
@ -35,17 +47,21 @@ contract Vault {
_token.safeTransferFrom(from, address(this), amount);
}
function withdraw(Context context, Recipient recipient) public {
function _delete(Context context, Recipient recipient) private {
Controller controller = Controller.wrap(msg.sender);
uint256 amount = _available[controller][context][recipient];
delete _available[controller][context][recipient];
delete _designated[controller][context][recipient];
}
function withdraw(Context context, Recipient recipient) public {
uint256 amount = balance(context, recipient);
_delete(context, recipient);
_token.safeTransfer(Recipient.unwrap(recipient), amount);
}
function burn(Context context, Recipient recipient) public {
Controller controller = Controller.wrap(msg.sender);
uint256 amount = _available[controller][context][recipient];
delete _available[controller][context][recipient];
uint256 amount = balance(context, recipient);
_delete(context, recipient);
_token.safeTransfer(address(0xdead), amount);
}
@ -64,5 +80,19 @@ contract Vault {
_available[controller][context][to] += amount;
}
function designate(
Context context,
Recipient recipient,
uint256 amount
) public {
Controller controller = Controller.wrap(msg.sender);
require(
amount <= _available[controller][context][recipient],
InsufficientBalance()
);
_available[controller][context][recipient] -= amount;
_designated[controller][context][recipient] += amount;
}
error InsufficientBalance();
}

View File

@ -38,7 +38,7 @@ describe("Vault", function () {
await expect(depositing).to.be.revertedWith("insufficient allowance")
})
it("multiple deposits add to the balance", async function () {
it("adds multiple deposits to the balance", async function () {
await token.connect(account).approve(vault.address, amount)
await vault.deposit(context, account.address, amount / 2)
await vault.deposit(context, account.address, amount / 2)
@ -132,48 +132,136 @@ describe("Vault", function () {
const context = randomBytes(32)
const amount = 42
let other
let account2
let account3
beforeEach(async function () {
;[, , other] = await ethers.getSigners()
;[, , account2, account3] = await ethers.getSigners()
await token.connect(account).approve(vault.address, amount)
await vault.deposit(context, account.address, amount)
})
it("can transfer tokens from one recipient to the other", async function () {
await vault.transfer(context, account.address, other.address, amount)
await vault.transfer(context, account.address, account2.address, amount)
expect(await vault.balance(context, account.address)).to.equal(0)
expect(await vault.balance(context, other.address)).to.equal(amount)
expect(await vault.balance(context, account2.address)).to.equal(amount)
})
it("can transfer part of a balance", async function () {
await vault.transfer(context, account.address, other.address, 10)
await vault.transfer(context, account.address, account2.address, 10)
expect(await vault.balance(context, account.address)).to.equal(
amount - 10
)
expect(await vault.balance(context, other.address)).to.equal(10)
expect(await vault.balance(context, account2.address)).to.equal(10)
})
it("does not transfer more than the balance", async function () {
await expect(
vault.transfer(context, account.address, other.address, amount + 1)
vault.transfer(context, account.address, account2.address, amount + 1)
).to.be.revertedWith("InsufficientBalance")
})
it("can withdraw funds that were transfered in", async function () {
await vault.transfer(context, account.address, other.address, amount)
const before = await token.balanceOf(other.address)
await vault.withdraw(context, other.address)
const after = await token.balanceOf(other.address)
await vault.transfer(context, account.address, account2.address, amount)
const before = await token.balanceOf(account2.address)
await vault.withdraw(context, account2.address)
const after = await token.balanceOf(account2.address)
expect(after - before).to.equal(amount)
})
it("cannot withdraw funds that were transfered out", async function () {
await vault.transfer(context, account.address, other.address, amount)
await vault.transfer(context, account.address, account2.address, amount)
const before = await token.balanceOf(account.address)
await vault.withdraw(context, account.address)
const after = await token.balanceOf(account.address)
expect(after).to.equal(before)
})
it("can transfer out funds that were transfered in", async function () {
await vault.transfer(context, account.address, account2.address, amount)
await vault.transfer(context, account2.address, account3.address, amount)
expect(await vault.balance(context, account2.address)).to.equal(0)
expect(await vault.balance(context, account3.address)).to.equal(amount)
})
})
describe("designating", async function () {
const context = randomBytes(32)
const amount = 42
let account2
beforeEach(async function () {
;[, , account2] = await ethers.getSigners()
await token.connect(account).approve(vault.address, amount)
await vault.deposit(context, account.address, amount)
})
it("can designate tokens for a single recipient", async function () {
await vault.designate(context, account.address, amount)
expect(await vault.designated(context, account.address)).to.equal(amount)
})
it("can designate part of the balance", async function () {
await vault.designate(context, account.address, 10)
expect(await vault.designated(context, account.address)).to.equal(10)
})
it("adds up designated tokens", async function () {
await vault.designate(context, account.address, 10)
await vault.designate(context, account.address, 10)
expect(await vault.designated(context, account.address)).to.equal(20)
})
it("cannot designate more than the undesignated balance", async function () {
await vault.designate(context, account.address, amount)
await expect(
vault.designate(context, account.address, 1)
).to.be.revertedWith("InsufficientBalance")
})
it("does not change the balance", async function () {
await vault.designate(context, account.address, 10)
expect(await vault.balance(context, account.address)).to.equal(amount)
})
it("does not allow designated tokens to be transfered", async function () {
await vault.designate(context, account.address, 1)
await expect(
vault.transfer(context, account.address, account2.address, amount)
).to.be.revertedWith("InsufficientBalance")
})
it("allows designated tokens to be withdrawn", async function () {
await vault.designate(context, account.address, 10)
const before = await token.balanceOf(account.address)
await vault.withdraw(context, account.address)
const after = await token.balanceOf(account.address)
expect(after - before).to.equal(amount)
})
it("does not withdraw designated tokens more than once", async function () {
await vault.designate(context, account.address, 10)
await vault.withdraw(context, account.address)
const before = await token.balanceOf(account.address)
await vault.withdraw(context, account.address)
const after = await token.balanceOf(account.address)
expect(after).to.equal(before)
})
it("allows designated tokens to be burned", async function () {
await vault.designate(context, account.address, 10)
await vault.burn(context, account.address)
expect(await vault.balance(context, account.address)).to.equal(0)
})
it("moves burned designated tokens to address 0xdead", async function () {
const dead = "0x000000000000000000000000000000000000dead"
await vault.designate(context, account.address, 10)
const before = await token.balanceOf(dead)
await vault.burn(context, account.address)
const after = await token.balanceOf(dead)
expect(after - before).to.equal(amount)
})
})
})