mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-08 08:13:08 +00:00
vault: pausing and unpausing
This commit is contained in:
parent
15e7ae855d
commit
4c46f2d1a0
@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/utils/Pausable.sol";
|
||||
import "./vault/VaultBase.sol";
|
||||
|
||||
/// A vault provides a means for smart contracts to control allocation of ERC20
|
||||
@ -43,9 +45,8 @@ import "./vault/VaultBase.sol";
|
||||
/// - burning tokens in a fund ensures that these tokens can no longer be
|
||||
/// extracted by an attacker
|
||||
///
|
||||
contract Vault is VaultBase {
|
||||
|
||||
constructor(IERC20 token) VaultBase(token) {}
|
||||
contract Vault is VaultBase, Pausable, Ownable {
|
||||
constructor(IERC20 token) VaultBase(token) Ownable(msg.sender) {}
|
||||
|
||||
/// The amount of tokens that are currently assigned to a recipient in a fund.
|
||||
/// This includes available and designated tokens. Available tokens can be
|
||||
@ -87,7 +88,11 @@ contract Vault is VaultBase {
|
||||
|
||||
/// Locks the fund until the expiry timestamp. The lock expiry can be extended
|
||||
/// later, but no more than the maximum timestamp.
|
||||
function lock(Fund fund, Timestamp expiry, Timestamp maximum) public {
|
||||
function lock(
|
||||
Fund fund,
|
||||
Timestamp expiry,
|
||||
Timestamp maximum
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_lock(controller, fund, expiry, maximum);
|
||||
}
|
||||
@ -96,7 +101,7 @@ contract Vault is VaultBase {
|
||||
/// the existing expiry, but no later than the maximum timestamp that was
|
||||
/// provided when locking the fund.
|
||||
/// Only allowed when the lock has not unlocked yet.
|
||||
function extendLock(Fund fund, Timestamp expiry) public {
|
||||
function extendLock(Fund fund, Timestamp expiry) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_extendLock(controller, fund, expiry);
|
||||
}
|
||||
@ -105,7 +110,11 @@ contract Vault is VaultBase {
|
||||
/// of the recipient. ERC20 tokens are transfered from the caller to the vault
|
||||
/// contract.
|
||||
/// Only allowed when the fund is locked.
|
||||
function deposit(Fund fund, Recipient recipient, uint128 amount) public {
|
||||
function deposit(
|
||||
Fund fund,
|
||||
Recipient recipient,
|
||||
uint128 amount
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_deposit(controller, fund, recipient, amount);
|
||||
}
|
||||
@ -118,7 +127,7 @@ contract Vault is VaultBase {
|
||||
Fund fund,
|
||||
Recipient recipient,
|
||||
uint128 amount
|
||||
) public {
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_designate(controller, fund, recipient, amount);
|
||||
}
|
||||
@ -131,7 +140,7 @@ contract Vault is VaultBase {
|
||||
Recipient from,
|
||||
Recipient to,
|
||||
uint128 amount
|
||||
) public {
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_transfer(controller, fund, from, to, amount);
|
||||
}
|
||||
@ -148,14 +157,18 @@ contract Vault is VaultBase {
|
||||
Recipient from,
|
||||
Recipient to,
|
||||
TokensPerSecond rate
|
||||
) public {
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_flow(controller, fund, from, to, rate);
|
||||
}
|
||||
|
||||
/// Burns an amount of designated tokens from the account of the recipient.
|
||||
/// Only allowed when the fund is locked.
|
||||
function burnDesignated(Fund fund, Recipient recipient, uint128 amount) public {
|
||||
function burnDesignated(
|
||||
Fund fund,
|
||||
Recipient recipient,
|
||||
uint128 amount
|
||||
) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_burnDesignated(controller, fund, recipient, amount);
|
||||
}
|
||||
@ -163,14 +176,14 @@ contract Vault is VaultBase {
|
||||
/// Burns all tokens from the account of the recipient.
|
||||
/// Only allowed when the fund is locked.
|
||||
/// Only allowed when no funds are flowing into or out of the account.
|
||||
function burnAccount(Fund fund, Recipient recipient) public {
|
||||
function burnAccount(Fund fund, Recipient recipient) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_burnAccount(controller, fund, recipient);
|
||||
}
|
||||
|
||||
/// Burns all tokens from all accounts in a fund.
|
||||
/// Only allowed when the fund is locked.
|
||||
function burnFund(Fund fund) public {
|
||||
function burnFund(Fund fund) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_burnFund(controller, fund);
|
||||
}
|
||||
@ -181,7 +194,7 @@ contract Vault is VaultBase {
|
||||
/// ⚠️ The recipient can also withdraw itself, so when designing a smart
|
||||
/// contract that controls funds in the vault, don't assume that only this
|
||||
/// smart contract can initiate a withdrawal ⚠️
|
||||
function withdraw(Fund fund, Recipient recipient) public {
|
||||
function withdraw(Fund fund, Recipient recipient) public whenNotPaused {
|
||||
Controller controller = Controller.wrap(msg.sender);
|
||||
_withdraw(controller, fund, recipient);
|
||||
}
|
||||
@ -193,4 +206,12 @@ contract Vault is VaultBase {
|
||||
Recipient recipient = Recipient.wrap(msg.sender);
|
||||
_withdraw(controller, fund, recipient);
|
||||
}
|
||||
|
||||
function pause() public onlyOwner {
|
||||
_pause();
|
||||
}
|
||||
|
||||
function unpause() public onlyOwner {
|
||||
_unpause();
|
||||
}
|
||||
}
|
||||
|
||||
@ -894,4 +894,133 @@ describe("Vault", function () {
|
||||
await expect(vault.burnFund(fund)).to.be.revertedWith("FundNotLocked")
|
||||
})
|
||||
}
|
||||
|
||||
describe("pausing", function () {
|
||||
let owner
|
||||
let owner2
|
||||
let other
|
||||
|
||||
beforeEach(async function () {
|
||||
;[owner, owner2, other] = await ethers.getSigners()
|
||||
})
|
||||
|
||||
it("allows the vault to be paused by the owner", async function () {
|
||||
await expect(vault.connect(owner).pause()).not.to.be.reverted
|
||||
})
|
||||
|
||||
it("allows the vault to be unpaused by the owner", async function () {
|
||||
await vault.connect(owner).pause()
|
||||
await expect(vault.connect(owner).unpause()).not.to.be.reverted
|
||||
})
|
||||
|
||||
it("does not allow pause to be called by others", async function () {
|
||||
await expect(vault.connect(other).pause()).to.be.revertedWith(
|
||||
"UnauthorizedAccount"
|
||||
)
|
||||
})
|
||||
|
||||
it("does not allow unpause to be called by others", async function () {
|
||||
await vault.connect(owner).pause()
|
||||
await expect(vault.connect(other).unpause()).to.be.revertedWith(
|
||||
"UnauthorizedAccount"
|
||||
)
|
||||
})
|
||||
|
||||
it("allows the ownership to change", async function () {
|
||||
await vault.connect(owner).pause()
|
||||
await vault.connect(owner).transferOwnership(owner2.address)
|
||||
await expect(vault.connect(owner2).unpause()).not.to.be.reverted
|
||||
})
|
||||
|
||||
it("allows the ownership to be renounced", async function () {
|
||||
await vault.connect(owner).renounceOwnership()
|
||||
await expect(vault.connect(owner).pause()).to.be.revertedWith(
|
||||
"UnauthorizedAccount"
|
||||
)
|
||||
})
|
||||
|
||||
describe("when the vault is paused", function () {
|
||||
let expiry
|
||||
let maximum
|
||||
|
||||
beforeEach(async function () {
|
||||
expiry = (await currentTime()) + 80
|
||||
maximum = (await currentTime()) + 100
|
||||
await vault.lock(fund, expiry, maximum)
|
||||
await token.approve(vault.address, 1000)
|
||||
await vault.deposit(fund, account.address, 1000)
|
||||
await vault.designate(fund, account.address, 100)
|
||||
await vault.connect(owner).pause()
|
||||
})
|
||||
|
||||
it("only allows a recipient to withdraw itself", async function () {
|
||||
await advanceTimeTo(expiry)
|
||||
await expect(
|
||||
vault.connect(account).withdrawByRecipient(controller.address, fund)
|
||||
).not.to.be.reverted
|
||||
})
|
||||
|
||||
it("does not allow funds to be locked", async function () {
|
||||
const fund = randomBytes(32)
|
||||
const expiry = (await currentTime()) + 100
|
||||
await expect(vault.lock(fund, expiry, expiry)).to.be.revertedWith(
|
||||
"EnforcedPause"
|
||||
)
|
||||
})
|
||||
|
||||
it("does not allow extending of lock", async function () {
|
||||
await expect(vault.extendLock(fund, maximum)).to.be.revertedWith(
|
||||
"EnforcedPause"
|
||||
)
|
||||
})
|
||||
|
||||
it("does not allow depositing of tokens", async function () {
|
||||
await token.approve(vault.address, 100)
|
||||
await expect(
|
||||
vault.deposit(fund, account.address, 100)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow designating tokens", async function () {
|
||||
await expect(
|
||||
vault.designate(fund, account.address, 10)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow transfer of tokens", async function () {
|
||||
await expect(
|
||||
vault.transfer(fund, account.address, account2.address, 10)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow new token flows to start", async function () {
|
||||
await expect(
|
||||
vault.flow(fund, account.address, account2.address, 1)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow burning of designated tokens", async function () {
|
||||
await expect(
|
||||
vault.burnDesignated(fund, account.address, 10)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow burning of accounts", async function () {
|
||||
await expect(
|
||||
vault.burnAccount(fund, account.address)
|
||||
).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow burning an entire fund", async function () {
|
||||
await expect(vault.burnFund(fund)).to.be.revertedWith("EnforcedPause")
|
||||
})
|
||||
|
||||
it("does not allow a controller to withdraw for a recipient", async function () {
|
||||
await advanceTimeTo(expiry)
|
||||
await expect(vault.withdraw(fund, account.address)).to.be.revertedWith(
|
||||
"EnforcedPause"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user