vault: rename Fund -> FundId

This commit is contained in:
Mark Spanbroek 2025-03-03 10:49:56 +01:00
parent 62aea75295
commit 2900bed8fd
2 changed files with 107 additions and 97 deletions

View File

@ -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 {

View File

@ -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);
}