228 lines
5.9 KiB
Solidity
Raw Normal View History

2025-01-22 11:32:22 +01:00
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./Accounts.sol";
2025-01-22 11:32:22 +01:00
import "./Timestamps.sol";
import "./TokensPerSecond.sol";
import "./Locks.sol";
2025-01-22 11:32:22 +01:00
using SafeERC20 for IERC20;
using Timestamps for Timestamp;
using Accounts for Account;
using Locks for Lock;
2025-01-22 11:32:22 +01:00
abstract contract VaultBase {
IERC20 internal immutable _token;
type Controller is address;
2025-02-04 09:53:31 +01:00
type Fund is bytes32;
2025-01-22 11:32:22 +01:00
type Recipient is address;
2025-02-04 09:53:31 +01:00
mapping(Controller => mapping(Fund => Lock)) private _locks;
2025-02-04 13:27:27 +01:00
mapping(Controller => mapping(Fund => mapping(Recipient => Account)))
private _accounts;
2025-01-22 11:32:22 +01:00
constructor(IERC20 token) {
_token = token;
}
2025-02-04 15:06:10 +01:00
function _getLock(
Controller controller,
Fund fund
) internal view returns (Lock memory) {
return _locks[controller][fund];
}
2025-02-04 13:27:27 +01:00
function _getAccount(
2025-01-22 11:32:22 +01:00
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
2025-01-22 11:32:22 +01:00
Recipient recipient
2025-02-04 13:27:27 +01:00
) internal view returns (Account memory) {
2025-02-04 15:04:19 +01:00
Account memory account = _accounts[controller][fund][recipient];
Lock memory lock = _locks[controller][fund];
Timestamp timestamp = Timestamps.earliest(
Timestamps.currentTime(),
lock.expiry
);
return account.at(timestamp);
}
function _lock(
2025-01-22 11:32:22 +01:00
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Timestamp expiry,
Timestamp maximum
2025-01-22 11:32:22 +01:00
) internal {
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(lock.maximum == Timestamp.wrap(0), AlreadyLocked());
lock.expiry = expiry;
lock.maximum = maximum;
_checkLockInvariant(lock);
2025-02-04 09:53:31 +01:00
_locks[controller][fund] = lock;
2025-01-22 11:32:22 +01:00
}
function _extendLock(
2025-01-22 11:32:22 +01:00
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Timestamp expiry
) internal {
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
require(lock.expiry <= expiry, InvalidExpiry());
lock.expiry = expiry;
_checkLockInvariant(lock);
2025-02-04 09:53:31 +01:00
_locks[controller][fund] = lock;
2025-01-22 11:32:22 +01:00
}
function _deposit(
2025-01-22 11:32:22 +01:00
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
address from,
uint128 amount
2025-01-22 11:32:22 +01:00
) internal {
Lock storage lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
Recipient recipient = Recipient.wrap(from);
Account storage account = _accounts[controller][fund][recipient];
2025-02-04 13:27:27 +01:00
account.available += amount;
lock.value += amount;
_token.safeTransferFrom(from, address(this), amount);
2025-01-22 11:32:22 +01:00
}
function _designate(
2025-01-22 11:32:22 +01:00
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Recipient recipient,
uint128 amount
2025-01-22 11:32:22 +01:00
) internal {
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
2025-02-04 13:27:27 +01:00
Account memory account = _accounts[controller][fund][recipient];
require(amount <= account.available, InsufficientBalance());
2025-02-04 13:27:27 +01:00
account.available -= amount;
account.designated += amount;
_checkAccountInvariant(account, lock);
2025-01-28 16:08:11 +01:00
2025-02-04 13:27:27 +01:00
_accounts[controller][fund][recipient] = account;
2025-01-22 11:32:22 +01:00
}
function _transfer(
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
2025-01-22 11:32:22 +01:00
Recipient from,
Recipient to,
uint128 amount
2025-01-22 11:32:22 +01:00
) internal {
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
2025-02-04 13:27:27 +01:00
Account memory senderAccount = _getAccount(controller, fund, from);
Account memory receiverAccount = _getAccount(controller, fund, to);
require(amount <= senderAccount.available, InsufficientBalance());
2025-02-04 13:27:27 +01:00
senderAccount.available -= amount;
receiverAccount.available += amount;
_checkAccountInvariant(senderAccount, lock);
2025-02-04 13:27:27 +01:00
_accounts[controller][fund][from] = senderAccount;
_accounts[controller][fund][to] = receiverAccount;
2025-01-22 11:32:22 +01:00
}
function _flow(
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Recipient from,
Recipient to,
TokensPerSecond rate
) internal {
require(rate >= TokensPerSecond.wrap(0), NegativeFlow());
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
2025-02-04 13:27:27 +01:00
Account memory senderAccount = _getAccount(controller, fund, from);
Account memory receiverAccount = _getAccount(controller, fund, to);
2025-01-28 14:58:14 +01:00
senderAccount.flow = senderAccount.flow - rate;
receiverAccount.flow = receiverAccount.flow + rate;
_checkAccountInvariant(senderAccount, lock);
2025-02-04 13:27:27 +01:00
_accounts[controller][fund][from] = senderAccount;
_accounts[controller][fund][to] = receiverAccount;
}
function _burn(
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Recipient recipient
) internal {
2025-02-04 15:15:51 +01:00
Lock storage lock = _locks[controller][fund];
require(lock.isLocked(), LockRequired());
2025-02-04 13:27:27 +01:00
Account memory account = _getAccount(controller, fund, recipient);
require(account.flow == TokensPerSecond.wrap(0), CannotBurnFlowingTokens());
uint128 amount = account.available + account.designated;
2025-02-04 15:15:51 +01:00
lock.value -= amount;
delete _accounts[controller][fund][recipient];
_token.safeTransfer(address(0xdead), amount);
}
function _withdraw(
Controller controller,
2025-02-04 09:53:31 +01:00
Fund fund,
Recipient recipient
) internal {
2025-02-04 09:53:31 +01:00
Lock memory lock = _locks[controller][fund];
require(!lock.isLocked(), Locked());
2025-02-04 13:27:27 +01:00
Account memory account = _getAccount(controller, fund, recipient);
uint128 amount = account.available + account.designated;
lock.value -= amount;
if (lock.value == 0) {
2025-02-04 09:53:31 +01:00
delete _locks[controller][fund];
} else {
2025-02-04 09:53:31 +01:00
_locks[controller][fund] = lock;
}
delete _accounts[controller][fund][recipient];
2025-02-04 09:53:31 +01:00
_token.safeTransfer(Recipient.unwrap(recipient), amount);
}
function _checkLockInvariant(Lock memory lock) private pure {
require(lock.expiry <= lock.maximum, ExpiryPastMaximum());
}
function _checkAccountInvariant(
2025-02-04 13:27:27 +01:00
Account memory account,
Lock memory lock
2025-01-28 14:58:14 +01:00
) private pure {
require(account.isValidAt(lock.maximum), InsufficientBalance());
2025-01-28 14:58:14 +01:00
}
2025-01-22 11:32:22 +01:00
error InsufficientBalance();
error Locked();
error AlreadyLocked();
error ExpiryPastMaximum();
error InvalidExpiry();
2025-01-23 10:29:53 +01:00
error LockRequired();
error NegativeFlow();
2025-01-28 16:08:11 +01:00
error CannotBurnFlowingTokens();
2025-01-22 11:32:22 +01:00
}