From 2900bed8fd29ff978c5ce0be0b127c068c8f7c8f Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 3 Mar 2025 10:49:56 +0100 Subject: [PATCH] vault: rename Fund -> FundId --- contracts/Vault.sol | 82 ++++++++++++----------- contracts/vault/VaultBase.sol | 122 ++++++++++++++++++---------------- 2 files changed, 107 insertions(+), 97 deletions(-) diff --git a/contracts/Vault.sol b/contracts/Vault.sol index 89f9831..a1b3ef6 100644 --- a/contracts/Vault.sol +++ b/contracts/Vault.sol @@ -62,64 +62,67 @@ contract Vault is VaultBase, Pausable, Ownable { /// Extracts the address of the account holder and the discriminator from the /// account id. function decodeAccountId( - AccountId account + AccountId id ) public pure returns (address holder, bytes12 discriminator) { - return Accounts.decodeId(account); + return Accounts.decodeId(id); } /// The amount of tokens that are currently in an account. /// This includes available and designated tokens. Available tokens can be /// transfered to other accounts, but designated tokens cannot. - function getBalance(Fund fund, AccountId id) public view returns (uint128) { + function getBalance( + FundId fundId, + AccountId accountId + ) public view returns (uint128) { Controller controller = Controller.wrap(msg.sender); - Balance memory balance = _getBalance(controller, fund, id); + Balance memory balance = _getBalance(controller, fundId, accountId); return balance.available + balance.designated; } /// The amount of tokens that are currently designated in an account /// These tokens can no longer be transfered to other accounts. function getDesignatedBalance( - Fund fund, - AccountId id + FundId fundId, + AccountId accountId ) public view returns (uint128) { Controller controller = Controller.wrap(msg.sender); - Balance memory balance = _getBalance(controller, fund, id); + Balance memory balance = _getBalance(controller, fundId, accountId); return balance.designated; } /// Returns the status of the lock on the fund. Most operations on the vault /// can only be done by the controller when the funds are locked. Withdrawal /// can only be done when the funds are unlocked. - function getLockStatus(Fund fund) public view returns (LockStatus) { + function getLockStatus(FundId fundId) public view returns (LockStatus) { Controller controller = Controller.wrap(msg.sender); - return _getLockStatus(controller, fund); + return _getLockStatus(controller, fundId); } /// Returns the expiry time of the lock on the fund. A locked fund unlocks /// automatically at this timestamp. - function getLockExpiry(Fund fund) public view returns (Timestamp) { + function getLockExpiry(FundId fundId) public view returns (Timestamp) { Controller controller = Controller.wrap(msg.sender); - return _getLockExpiry(controller, fund); + return _getLockExpiry(controller, fundId); } /// 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, + FundId fundId, Timestamp expiry, Timestamp maximum ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _lock(controller, fund, expiry, maximum); + _lock(controller, fundId, expiry, maximum); } /// Delays unlocking of a locked fund. The new expiry should be later than /// 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 whenNotPaused { + function extendLock(FundId fundId, Timestamp expiry) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _extendLock(controller, fund, expiry); + _extendLock(controller, fundId, expiry); } /// Deposits an amount of tokens into the vault, and adds them to the balance @@ -127,12 +130,12 @@ contract Vault is VaultBase, Pausable, Ownable { /// contract. /// Only allowed when the fund is locked. function deposit( - Fund fund, - AccountId id, + FundId fundId, + AccountId accountId, uint128 amount ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _deposit(controller, fund, id, amount); + _deposit(controller, fundId, accountId, amount); } /// Takes an amount of tokens from the account balance and designates them @@ -140,24 +143,24 @@ contract Vault is VaultBase, Pausable, Ownable { /// transfered to other accounts. /// Only allowed when the fund is locked. function designate( - Fund fund, - AccountId id, + FundId fundId, + AccountId accountId, uint128 amount ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _designate(controller, fund, id, amount); + _designate(controller, fundId, accountId, amount); } /// Transfers an amount of tokens from one account to the other. /// Only allowed when the fund is locked. function transfer( - Fund fund, + FundId fundId, AccountId from, AccountId to, uint128 amount ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _transfer(controller, fund, from, to, amount); + _transfer(controller, fundId, from, to, amount); } /// Transfers tokens from one account the other over time. @@ -168,40 +171,43 @@ contract Vault is VaultBase, Pausable, Ownable { /// Only allowed when the balance is sufficient to sustain the flow until the /// fund unlocks, even if the lock expiry time is extended to its maximum. function flow( - Fund fund, + FundId fundId, AccountId from, AccountId to, TokensPerSecond rate ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _flow(controller, fund, from, to, rate); + _flow(controller, fundId, from, to, rate); } /// Burns an amount of designated tokens from the account. /// Only allowed when the fund is locked. function burnDesignated( - Fund fund, - AccountId account, + FundId fundId, + AccountId accountId, uint128 amount ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _burnDesignated(controller, fund, account, amount); + _burnDesignated(controller, fundId, accountId, amount); } /// Burns all tokens from the account. /// Only allowed when the fund is locked. /// Only allowed when no funds are flowing into or out of the account. - function burnAccount(Fund fund, AccountId account) public whenNotPaused { + function burnAccount( + FundId fundId, + AccountId accountId + ) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _burnAccount(controller, fund, account); + _burnAccount(controller, fundId, accountId); } /// Freezes a fund. Stops all tokens flows and disallows any operations on the /// fund until it unlocks. /// Only allowed when the fund is locked. - function freezeFund(Fund fund) public whenNotPaused { + function freezeFund(FundId fundId) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _freezeFund(controller, fund); + _freezeFund(controller, fundId); } /// Transfers all ERC20 tokens in the account out of the vault to the account @@ -210,9 +216,9 @@ contract Vault is VaultBase, Pausable, Ownable { /// ⚠️ The account holder 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, AccountId account) public whenNotPaused { + function withdraw(FundId fund, AccountId accountId) public whenNotPaused { Controller controller = Controller.wrap(msg.sender); - _withdraw(controller, fund, account); + _withdraw(controller, fund, accountId); } /// Allows an account holder to withdraw its tokens from a fund directly, @@ -221,12 +227,12 @@ contract Vault is VaultBase, Pausable, Ownable { /// Only allowed when the fund is unlocked. function withdrawByRecipient( Controller controller, - Fund fund, - AccountId account + FundId fund, + AccountId accountId ) public { - (address holder, ) = Accounts.decodeId(account); + (address holder, ) = Accounts.decodeId(accountId); require(msg.sender == holder, VaultOnlyAccountHolder()); - _withdraw(controller, fund, account); + _withdraw(controller, fund, accountId); } function pause() public onlyOwner { diff --git a/contracts/vault/VaultBase.sol b/contracts/vault/VaultBase.sol index 843ddff..b3cfd25 100644 --- a/contracts/vault/VaultBase.sol +++ b/contracts/vault/VaultBase.sol @@ -17,14 +17,14 @@ import "./Locks.sol"; /// The lock invariant ensures that there is a maximum time that a fund can be /// locked: /// -/// (∀ controller ∈ Controller, fund ∈ Fund: +/// (∀ controller ∈ Controller, fund ∈ FundId: /// lock.expiry <= lock.maximum /// where lock = _locks[controller][fund]) /// /// The account invariant ensures that the outgoing token flow can be sustained /// for the maximum time that a fund can be locked: /// -/// (∀ controller ∈ Controller, fund ∈ Fund, account ∈ AccountId: +/// (∀ controller ∈ Controller, fund ∈ FundId, account ∈ AccountId: /// flow.outgoing * (lock.maximum - flow.updated) <= balance.available /// where lock = _locks[controller][fund]) /// and flow = _accounts[controller][fund][account].flow @@ -32,7 +32,7 @@ import "./Locks.sol"; /// /// The flow invariant ensures that incoming and outgoing flow rates match: /// -/// (∀ controller ∈ Controller, fund ∈ Fund: +/// (∀ controller ∈ Controller, fund ∈ FundId: /// (∑ account ∈ AccountId: accounts[account].flow.incoming) = /// (∑ account ∈ AccountId: accounts[account].flow.outgoing) /// where accounts = _accounts[controller][fund]) @@ -47,12 +47,12 @@ abstract contract VaultBase { /// Represents a smart contract that can redistribute and burn tokens in funds type Controller is address; /// Unique identifier for a fund, chosen by the controller - type Fund is bytes32; + type FundId is bytes32; /// Each fund has its own time lock - mapping(Controller => mapping(Fund => Lock)) private _locks; + mapping(Controller => mapping(FundId => Lock)) private _locks; /// Each account holder has its own set of accounts in a fund - mapping(Controller => mapping(Fund => mapping(AccountId => Account))) + mapping(Controller => mapping(FundId => mapping(AccountId => Account))) private _accounts; constructor(IERC20 token) { @@ -61,32 +61,32 @@ abstract contract VaultBase { function _getLockStatus( Controller controller, - Fund fund + FundId fundId ) internal view returns (LockStatus) { - return _locks[controller][fund].status(); + return _locks[controller][fundId].status(); } function _getLockExpiry( Controller controller, - Fund fund + FundId fundId ) internal view returns (Timestamp) { - return _locks[controller][fund].expiry; + return _locks[controller][fundId].expiry; } function _getBalance( Controller controller, - Fund fund, - AccountId id + FundId fundId, + AccountId accountId ) internal view returns (Balance memory) { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; LockStatus lockStatus = lock.status(); if (lockStatus == LockStatus.Locked) { - Account memory account = _accounts[controller][fund][id]; + Account memory account = _accounts[controller][fundId][accountId]; account.update(Timestamps.currentTime()); return account.balance; } if (lockStatus == LockStatus.Unlocked || lockStatus == LockStatus.Frozen) { - Account memory account = _accounts[controller][fund][id]; + Account memory account = _accounts[controller][fundId][accountId]; account.update(lock.flowEnd()); return account.balance; } @@ -95,41 +95,41 @@ abstract contract VaultBase { function _lock( Controller controller, - Fund fund, + FundId fundId, Timestamp expiry, Timestamp maximum ) internal { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.NoLock, VaultFundAlreadyLocked()); lock.expiry = expiry; lock.maximum = maximum; _checkLockInvariant(lock); - _locks[controller][fund] = lock; + _locks[controller][fundId] = lock; } function _extendLock( Controller controller, - Fund fund, + FundId fundId, Timestamp expiry ) internal { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); require(lock.expiry <= expiry, VaultInvalidExpiry()); lock.expiry = expiry; _checkLockInvariant(lock); - _locks[controller][fund] = lock; + _locks[controller][fundId] = lock; } function _deposit( Controller controller, - Fund fund, - AccountId id, + FundId fundId, + AccountId accountId, uint128 amount ) internal { - Lock storage lock = _locks[controller][fund]; + Lock storage lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account storage account = _accounts[controller][fund][id]; + Account storage account = _accounts[controller][fundId][accountId]; account.balance.available += amount; lock.value += amount; @@ -143,74 +143,74 @@ abstract contract VaultBase { function _designate( Controller controller, - Fund fund, - AccountId id, + FundId fundId, + AccountId accountId, uint128 amount ) internal { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account memory account = _accounts[controller][fund][id]; + Account memory account = _accounts[controller][fundId][accountId]; require(amount <= account.balance.available, VaultInsufficientBalance()); account.balance.available -= amount; account.balance.designated += amount; _checkAccountInvariant(account, lock); - _accounts[controller][fund][id] = account; + _accounts[controller][fundId][accountId] = account; } function _transfer( Controller controller, - Fund fund, + FundId fundId, AccountId from, AccountId to, uint128 amount ) internal { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account memory sender = _accounts[controller][fund][from]; + Account memory sender = _accounts[controller][fundId][from]; require(amount <= sender.balance.available, VaultInsufficientBalance()); sender.balance.available -= amount; _checkAccountInvariant(sender, lock); - _accounts[controller][fund][from] = sender; + _accounts[controller][fundId][from] = sender; - _accounts[controller][fund][to].balance.available += amount; + _accounts[controller][fundId][to].balance.available += amount; } function _flow( Controller controller, - Fund fund, + FundId fundId, AccountId from, AccountId to, TokensPerSecond rate ) internal { - Lock memory lock = _locks[controller][fund]; + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account memory sender = _accounts[controller][fund][from]; + Account memory sender = _accounts[controller][fundId][from]; sender.flowOut(rate); _checkAccountInvariant(sender, lock); - _accounts[controller][fund][from] = sender; + _accounts[controller][fundId][from] = sender; - Account memory receiver = _accounts[controller][fund][to]; + Account memory receiver = _accounts[controller][fundId][to]; receiver.flowIn(rate); - _accounts[controller][fund][to] = receiver; + _accounts[controller][fundId][to] = receiver; } function _burnDesignated( Controller controller, - Fund fund, - AccountId id, + FundId fundId, + AccountId accountId, uint128 amount ) internal { - Lock storage lock = _locks[controller][fund]; + Lock storage lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account storage account = _accounts[controller][fund][id]; + Account storage account = _accounts[controller][fundId][accountId]; require(account.balance.designated >= amount, VaultInsufficientBalance()); account.balance.designated -= amount; @@ -222,49 +222,53 @@ abstract contract VaultBase { function _burnAccount( Controller controller, - Fund fund, - AccountId id + FundId fundId, + AccountId accountId ) internal { - Lock storage lock = _locks[controller][fund]; + Lock storage lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); - Account memory account = _accounts[controller][fund][id]; + Account memory account = _accounts[controller][fundId][accountId]; require(account.flow.incoming == account.flow.outgoing, VaultFlowNotZero()); uint128 amount = account.balance.available + account.balance.designated; lock.value -= amount; - delete _accounts[controller][fund][id]; + delete _accounts[controller][fundId][accountId]; _token.safeTransfer(address(0xdead), amount); } - function _freezeFund(Controller controller, Fund fund) internal { - Lock storage lock = _locks[controller][fund]; + function _freezeFund(Controller controller, FundId fundId) internal { + Lock storage lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Locked, VaultFundNotLocked()); lock.frozenAt = Timestamps.currentTime(); } - function _withdraw(Controller controller, Fund fund, AccountId id) internal { - Lock memory lock = _locks[controller][fund]; + function _withdraw( + Controller controller, + FundId fundId, + AccountId accountId + ) internal { + Lock memory lock = _locks[controller][fundId]; require(lock.status() == LockStatus.Unlocked, VaultFundNotUnlocked()); - Account memory account = _accounts[controller][fund][id]; + Account memory account = _accounts[controller][fundId][accountId]; account.update(lock.flowEnd()); uint128 amount = account.balance.available + account.balance.designated; lock.value -= amount; if (lock.value == 0) { - delete _locks[controller][fund]; + delete _locks[controller][fundId]; } else { - _locks[controller][fund] = lock; + _locks[controller][fundId] = lock; } - delete _accounts[controller][fund][id]; + delete _accounts[controller][fundId][accountId]; - (address owner, ) = Accounts.decodeId(id); + (address owner, ) = Accounts.decodeId(accountId); _token.safeTransfer(owner, amount); }