mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-06 23:33:12 +00:00
vault: burn entire fund
This commit is contained in:
parent
682b65b24a
commit
bbdd614579
@ -79,6 +79,11 @@ contract Vault is VaultBase {
|
|||||||
_burn(controller, fund, recipient);
|
_burn(controller, fund, recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function burnAll(Fund fund) public {
|
||||||
|
Controller controller = Controller.wrap(msg.sender);
|
||||||
|
_burnAll(controller, fund);
|
||||||
|
}
|
||||||
|
|
||||||
function withdraw(Fund fund, Recipient recipient) public {
|
function withdraw(Fund fund, Recipient recipient) public {
|
||||||
Controller controller = Controller.wrap(msg.sender);
|
Controller controller = Controller.wrap(msg.sender);
|
||||||
_withdraw(controller, fund, recipient);
|
_withdraw(controller, fund, recipient);
|
||||||
|
|||||||
@ -7,10 +7,27 @@ struct Lock {
|
|||||||
Timestamp expiry;
|
Timestamp expiry;
|
||||||
Timestamp maximum;
|
Timestamp maximum;
|
||||||
uint128 value;
|
uint128 value;
|
||||||
|
bool burned;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LockStatus {
|
||||||
|
NoLock,
|
||||||
|
Locked,
|
||||||
|
Unlocked,
|
||||||
|
Burned
|
||||||
}
|
}
|
||||||
|
|
||||||
library Locks {
|
library Locks {
|
||||||
function isLocked(Lock memory lock) internal view returns (bool) {
|
function status(Lock memory lock) internal view returns (LockStatus) {
|
||||||
return Timestamps.currentTime() < lock.expiry;
|
if (lock.burned) {
|
||||||
|
return LockStatus.Burned;
|
||||||
|
}
|
||||||
|
if (Timestamps.currentTime() < lock.expiry) {
|
||||||
|
return LockStatus.Locked;
|
||||||
|
}
|
||||||
|
if (lock.maximum == Timestamp.wrap(0)) {
|
||||||
|
return LockStatus.NoLock;
|
||||||
|
}
|
||||||
|
return LockStatus.Unlocked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,14 +40,19 @@ abstract contract VaultBase {
|
|||||||
Fund fund,
|
Fund fund,
|
||||||
Recipient recipient
|
Recipient recipient
|
||||||
) internal view returns (Balance memory) {
|
) internal view returns (Balance memory) {
|
||||||
Account memory account = _accounts[controller][fund][recipient];
|
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
if (lock.isLocked()) {
|
LockStatus lockStatus = lock.status();
|
||||||
|
if (lockStatus == LockStatus.Locked) {
|
||||||
|
Account memory account = _accounts[controller][fund][recipient];
|
||||||
account.update(Timestamps.currentTime());
|
account.update(Timestamps.currentTime());
|
||||||
} else {
|
return account.balance;
|
||||||
account.update(lock.expiry);
|
|
||||||
}
|
}
|
||||||
return account.balance;
|
if (lockStatus == LockStatus.Unlocked) {
|
||||||
|
Account memory account = _accounts[controller][fund][recipient];
|
||||||
|
account.update(lock.expiry);
|
||||||
|
return account.balance;
|
||||||
|
}
|
||||||
|
return Balance({available: 0, designated: 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _lock(
|
function _lock(
|
||||||
@ -70,7 +75,7 @@ abstract contract VaultBase {
|
|||||||
Timestamp expiry
|
Timestamp expiry
|
||||||
) internal {
|
) internal {
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
require(lock.expiry <= expiry, InvalidExpiry());
|
require(lock.expiry <= expiry, InvalidExpiry());
|
||||||
lock.expiry = expiry;
|
lock.expiry = expiry;
|
||||||
_checkLockInvariant(lock);
|
_checkLockInvariant(lock);
|
||||||
@ -84,7 +89,7 @@ abstract contract VaultBase {
|
|||||||
uint128 amount
|
uint128 amount
|
||||||
) internal {
|
) internal {
|
||||||
Lock storage lock = _locks[controller][fund];
|
Lock storage lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
Account storage account = _accounts[controller][fund][recipient];
|
Account storage account = _accounts[controller][fund][recipient];
|
||||||
|
|
||||||
@ -105,7 +110,7 @@ abstract contract VaultBase {
|
|||||||
uint128 amount
|
uint128 amount
|
||||||
) internal {
|
) internal {
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
Account memory account = _accounts[controller][fund][recipient];
|
Account memory account = _accounts[controller][fund][recipient];
|
||||||
require(amount <= account.balance.available, InsufficientBalance());
|
require(amount <= account.balance.available, InsufficientBalance());
|
||||||
@ -125,7 +130,7 @@ abstract contract VaultBase {
|
|||||||
uint128 amount
|
uint128 amount
|
||||||
) internal {
|
) internal {
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
Account memory sender = _accounts[controller][fund][from];
|
Account memory sender = _accounts[controller][fund][from];
|
||||||
require(amount <= sender.balance.available, InsufficientBalance());
|
require(amount <= sender.balance.available, InsufficientBalance());
|
||||||
@ -146,7 +151,7 @@ abstract contract VaultBase {
|
|||||||
TokensPerSecond rate
|
TokensPerSecond rate
|
||||||
) internal {
|
) internal {
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
Account memory sender = _accounts[controller][fund][from];
|
Account memory sender = _accounts[controller][fund][from];
|
||||||
sender.flowOut(rate);
|
sender.flowOut(rate);
|
||||||
@ -164,7 +169,7 @@ abstract contract VaultBase {
|
|||||||
Recipient recipient
|
Recipient recipient
|
||||||
) internal {
|
) internal {
|
||||||
Lock storage lock = _locks[controller][fund];
|
Lock storage lock = _locks[controller][fund];
|
||||||
require(lock.isLocked(), LockRequired());
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
Account memory account = _accounts[controller][fund][recipient];
|
Account memory account = _accounts[controller][fund][recipient];
|
||||||
require(account.flow.incoming == account.flow.outgoing, FlowMustBeZero());
|
require(account.flow.incoming == account.flow.outgoing, FlowMustBeZero());
|
||||||
@ -177,13 +182,23 @@ abstract contract VaultBase {
|
|||||||
_token.safeTransfer(address(0xdead), amount);
|
_token.safeTransfer(address(0xdead), amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _burnAll(
|
||||||
|
Controller controller,
|
||||||
|
Fund fund
|
||||||
|
) internal {
|
||||||
|
Lock storage lock = _locks[controller][fund];
|
||||||
|
require(lock.status() == LockStatus.Locked, LockRequired());
|
||||||
|
|
||||||
|
lock.burned = true;
|
||||||
|
}
|
||||||
|
|
||||||
function _withdraw(
|
function _withdraw(
|
||||||
Controller controller,
|
Controller controller,
|
||||||
Fund fund,
|
Fund fund,
|
||||||
Recipient recipient
|
Recipient recipient
|
||||||
) internal {
|
) internal {
|
||||||
Lock memory lock = _locks[controller][fund];
|
Lock memory lock = _locks[controller][fund];
|
||||||
require(!lock.isLocked(), Locked());
|
require(lock.status() == LockStatus.Unlocked, Locked());
|
||||||
|
|
||||||
Account memory account = _accounts[controller][fund][recipient];
|
Account memory account = _accounts[controller][fund][recipient];
|
||||||
account.update(lock.expiry);
|
account.update(lock.expiry);
|
||||||
|
|||||||
@ -56,8 +56,8 @@ describe("Vault", function () {
|
|||||||
await expect(locking).to.be.revertedWith("ExpiryPastMaximum")
|
await expect(locking).to.be.revertedWith("ExpiryPastMaximum")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("unlocked fund", function () {
|
describe("fund is not locked", function () {
|
||||||
testUnlockedFund()
|
testFundThatIsNotLocked()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -501,6 +501,15 @@ describe("Vault", function () {
|
|||||||
await vault.flow(fund, account2.address, account.address, 5)
|
await vault.flow(fund, account2.address, account.address, 5)
|
||||||
await expect(vault.burn(fund, account.address)).not.to.be.reverted
|
await expect(vault.burn(fund, account.address)).not.to.be.reverted
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("can burn an entire fund", async function () {
|
||||||
|
await vault.transfer(fund, account.address, account2.address, 10)
|
||||||
|
await vault.transfer(fund, account.address, account3.address, 10)
|
||||||
|
await vault.burnAll(fund)
|
||||||
|
await expect(await vault.getBalance(fund, account.address)).to.equal(0)
|
||||||
|
await expect(await vault.getBalance(fund, account2.address)).to.equal(0)
|
||||||
|
await expect(await vault.getBalance(fund, account3.address)).to.equal(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("withdrawing", function () {
|
describe("withdrawing", function () {
|
||||||
@ -630,6 +639,8 @@ describe("Vault", function () {
|
|||||||
setAutomine(true)
|
setAutomine(true)
|
||||||
await token.connect(controller).approve(vault.address, amount)
|
await token.connect(controller).approve(vault.address, amount)
|
||||||
await vault.deposit(fund, account.address, amount)
|
await vault.deposit(fund, account.address, amount)
|
||||||
|
await token.connect(controller).approve(vault.address, amount)
|
||||||
|
await vault.deposit(fund, account2.address, amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("allows controller to withdraw for a recipient", async function () {
|
it("allows controller to withdraw for a recipient", async function () {
|
||||||
@ -676,16 +687,16 @@ describe("Vault", function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("can withdraw funds that were transfered in", async function () {
|
it("can withdraw funds that were transfered in", async function () {
|
||||||
await vault.transfer(fund, account.address, account2.address, amount)
|
await vault.transfer(fund, account.address, account3.address, amount)
|
||||||
await expire()
|
await expire()
|
||||||
const before = await token.balanceOf(account2.address)
|
const before = await token.balanceOf(account3.address)
|
||||||
await vault.withdraw(fund, account2.address)
|
await vault.withdraw(fund, account3.address)
|
||||||
const after = await token.balanceOf(account2.address)
|
const after = await token.balanceOf(account3.address)
|
||||||
expect(after - before).to.equal(amount)
|
expect(after - before).to.equal(amount)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("cannot withdraw funds that were transfered out", async function () {
|
it("cannot withdraw funds that were transfered out", async function () {
|
||||||
await vault.transfer(fund, account.address, account2.address, amount)
|
await vault.transfer(fund, account.address, account3.address, amount)
|
||||||
await expire()
|
await expire()
|
||||||
const before = await token.balanceOf(account.address)
|
const before = await token.balanceOf(account.address)
|
||||||
await vault.withdraw(fund, account.address)
|
await vault.withdraw(fund, account.address)
|
||||||
@ -712,17 +723,55 @@ describe("Vault", function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("unlocked fund", function () {
|
describe("fund is not locked", function () {
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
setAutomine(true)
|
setAutomine(true)
|
||||||
await expire()
|
await expire()
|
||||||
})
|
})
|
||||||
|
|
||||||
testUnlockedFund()
|
testFundThatIsNotLocked()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function testUnlockedFund() {
|
describe("when a fund is burned", function () {
|
||||||
|
const amount = 1000
|
||||||
|
|
||||||
|
let expiry
|
||||||
|
|
||||||
|
beforeEach(async function () {
|
||||||
|
expiry = (await currentTime()) + 100
|
||||||
|
await token.connect(controller).approve(vault.address, amount)
|
||||||
|
await vault.lock(fund, expiry, expiry)
|
||||||
|
await vault.deposit(fund, account.address, amount)
|
||||||
|
await vault.burnAll(fund)
|
||||||
|
})
|
||||||
|
|
||||||
|
testBurnedFund()
|
||||||
|
|
||||||
|
describe("when the lock expires", function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
await advanceTimeTo(expiry)
|
||||||
|
})
|
||||||
|
|
||||||
|
testBurnedFund()
|
||||||
|
})
|
||||||
|
|
||||||
|
function testBurnedFund() {
|
||||||
|
it("cannot set lock", async function () {
|
||||||
|
const locking = vault.lock(fund, expiry, maximum)
|
||||||
|
await expect(locking).to.be.revertedWith("AlreadyLocked")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("cannot withdraw", async function () {
|
||||||
|
const withdrawing = vault.withdraw(fund, account.address)
|
||||||
|
await expect(withdrawing).to.be.revertedWith("Locked")
|
||||||
|
})
|
||||||
|
|
||||||
|
testFundThatIsNotLocked()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function testFundThatIsNotLocked() {
|
||||||
it("does not allow extending of lock", async function () {
|
it("does not allow extending of lock", async function () {
|
||||||
await expect(
|
await expect(
|
||||||
vault.extendLock(fund, (await currentTime()) + 1)
|
vault.extendLock(fund, (await currentTime()) + 1)
|
||||||
@ -755,10 +804,14 @@ describe("Vault", function () {
|
|||||||
).to.be.revertedWith("LockRequired")
|
).to.be.revertedWith("LockRequired")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not allow burning of tokens", async function () {
|
it("does not allow burning of accounts", async function () {
|
||||||
await expect(vault.burn(fund, account.address)).to.be.revertedWith(
|
await expect(vault.burn(fund, account.address)).to.be.revertedWith(
|
||||||
"LockRequired"
|
"LockRequired"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("does not allow burning an entire fund", async function () {
|
||||||
|
await expect(vault.burnAll(fund)).to.be.revertedWith("LockRequired")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user