Finance
Smart contract finance utilities and implementations
This directory includes primitives for on-chain confidential financial systems:
VestingWalletConfidential
: Handles the vesting of confidential tokens for a given beneficiary. Custody of multiple tokens can be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting schedule.VestingWalletCliffConfidential
: Variant ofVestingWalletConfidential
which adds a cliff period to the vesting schedule.
For convenience, this directory also includes:
VestingWalletConfidentialFactory
: A factory which enables batch funding of vesting wallets.
Contracts
VestingWalletConfidential
VestingWalletCliffConfidential
VestingWalletConfidentialFactory
import "@openzeppelin/contracts/finance/ERC7821WithExecutor.sol";
Extension of ERC7821
that adds an ERC7821WithExecutor.executor
address that is able to execute arbitrary calls via ERC7821.execute
.
Functions
Errors
executor() → address
public
#Trusted address that is able to execute arbitrary calls from the vesting wallet via ERC7821.execute
.
_ERC7821WithExecutor_init(address executor)
internal
#_erc7821AuthorizedExecutor(address caller, bytes32 mode, bytes executionData) → bool
internal
#Access control mechanism for the execute
function.
By default, only the contract itself is allowed to execute.
Override this function to implement custom access control, for example to allow the ERC-4337 entrypoint to execute.
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
import "@openzeppelin/contracts/finance/VestingWalletCliffConfidential.sol";
An extension of VestingWalletConfidential
that adds a cliff to the vesting schedule. The cliff is cliffSeconds
long and
starts at the vesting start timestamp (see VestingWalletConfidential
).
Functions
- cliff()
- __VestingWalletCliffConfidential_init(beneficiary, startTimestamp, durationSeconds, cliffSeconds)
- __VestingWalletCliffConfidential_init_unchained(cliffSeconds)
- _vestingSchedule(totalAllocation, timestamp)
VestingWalletConfidential
- start()
- duration()
- end()
- released(token)
- releasable(token)
- release(token)
- vestedAmount(token, timestamp)
- __VestingWalletConfidential_init(beneficiary, startTimestamp, durationSeconds)
- __VestingWalletConfidential_init_unchained(startTimestamp, durationSeconds)
ReentrancyGuardTransient
OwnableUpgradeable
- __Ownable_init(initialOwner)
- __Ownable_init_unchained(initialOwner)
- owner()
- _checkOwner()
- renounceOwnership()
- transferOwnership(newOwner)
- _transferOwnership(newOwner)
ContextUpgradeable
Initializable
Events
Errors
cliff() → uint64
public
#The timestamp at which the cliff ends.
__VestingWalletCliffConfidential_init(address beneficiary, uint48 startTimestamp, uint48 durationSeconds, uint48 cliffSeconds)
internal
#Set the duration of the cliff, in seconds. The cliff starts at the vesting
start timestamp (see VestingWalletConfidential.start
) and ends cliffSeconds
later.
__VestingWalletCliffConfidential_init_unchained(uint48 cliffSeconds)
internal
#_vestingSchedule(euint128 totalAllocation, uint48 timestamp) → euint128
internal
#This function returns the amount vested, as a function of time, for
an asset given its total historical allocation. Returns 0 if the VestingWalletCliffConfidential.cliff
timestamp is not met.
The cliff not only makes the schedule return 0, but it also ignores every possible side
effect from calling the inherited implementation (i.e. super._vestingSchedule
). Carefully consider
this caveat if the overridden implementation of this function has any (e.g. writing to memory or reverting).
VestingWalletCliffConfidentialInvalidCliffDuration(uint64 cliffSeconds, uint64 durationSeconds)
error
#The specified cliff duration is larger than the vesting duration.
import "@openzeppelin/contracts/finance/VestingWalletConfidential.sol";
A vesting wallet is an ownable contract that can receive ERC7984 tokens, and release these assets to the wallet owner, also referred to as "beneficiary", according to a vesting schedule.
Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning. Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) be immediately releasable.
By setting the duration to 0, one can configure this contract to behave like an asset timelock that holds tokens for a beneficiary until a specified time.
NOTE: Since the wallet is Ownable
, and ownership can be transferred, it is possible to sell unvested tokens.
NOTE: When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
Confidential vesting wallet contracts can be deployed (as clones) using the VestingWalletConfidentialFactory
.
Functions
- start()
- duration()
- end()
- released(token)
- releasable(token)
- release(token)
- vestedAmount(token, timestamp)
- __VestingWalletConfidential_init(beneficiary, startTimestamp, durationSeconds)
- __VestingWalletConfidential_init_unchained(startTimestamp, durationSeconds)
- _vestingSchedule(totalAllocation, timestamp)
ReentrancyGuardTransient
OwnableUpgradeable
- __Ownable_init(initialOwner)
- __Ownable_init_unchained(initialOwner)
- owner()
- _checkOwner()
- renounceOwnership()
- transferOwnership(newOwner)
- _transferOwnership(newOwner)
ContextUpgradeable
Initializable
Events
Errors
start() → uint64
public
#Timestamp at which the vesting starts.
duration() → uint64
public
#Duration of the vesting in seconds.
end() → uint64
public
#Timestamp at which the vesting ends.
released(address token) → euint128
public
#Amount of token already released
releasable(address token) → euint64
public
#Getter for the amount of releasable token
tokens. token
should be the address of an
IERC7984
contract.
release(address token)
public
#Release the tokens that have already vested.
Emits a VestingWalletConfidential.VestingWalletConfidentialTokenReleased
event.
vestedAmount(address token, uint48 timestamp) → euint128
public
#Calculates the amount of tokens that have been vested at the given timestamp. Default implementation is a linear vesting curve.
__VestingWalletConfidential_init(address beneficiary, uint48 startTimestamp, uint48 durationSeconds)
internal
#Initializes the vesting wallet for a given beneficiary
with a start time of startTimestamp
and an end time of startTimestamp + durationSeconds
.
__VestingWalletConfidential_init_unchained(uint48 startTimestamp, uint48 durationSeconds)
internal
#_vestingSchedule(euint128 totalAllocation, uint48 timestamp) → euint128
internal
#This returns the amount vested, as a function of time, for an asset given its total historical allocation.
VestingWalletConfidentialTokenReleased(address indexed token, euint64 amount)
event
#Emitted when releasable vested tokens are released.
import "@openzeppelin/contracts/finance/VestingWalletConfidentialFactory.sol";
A factory which enables batch funding of vesting wallets.
The VestingWalletConfidentialFactory._deployVestingWalletImplementation
, VestingWalletConfidentialFactory._initializeVestingWallet
, and VestingWalletConfidentialFactory._validateVestingWalletInitArgs
functions remain unimplemented to allow for custom implementations of the vesting wallet to be used.
Functions
- constructor()
- batchFundVestingWalletConfidential(token, vestingPlans, inputProof)
- createVestingWalletConfidential(initArgs)
- predictVestingWalletConfidential(initArgs)
- _validateVestingWalletInitArgs(initArgs)
- _initializeVestingWallet(vestingWalletAddress, initArgs)
- _deployVestingWalletImplementation()
- _getCreate2VestingWalletConfidentialSalt(initArgs)
Events
constructor()
internal
#batchFundVestingWalletConfidential(address token, struct VestingWalletConfidentialFactory.VestingPlan[] vestingPlans, bytes inputProof)
public
#Batches the funding of multiple confidential vesting wallets.
Funds are sent to deterministic wallet addresses. Wallets can be created either before or after this operation.
Emits a VestingWalletConfidentialFactory.VestingWalletConfidentialFunded
event for each funded vesting plan.
createVestingWalletConfidential(bytes initArgs) → address
public
#Creates a confidential vesting wallet.
Emits a VestingWalletConfidentialFactory.VestingWalletConfidentialCreated
.
predictVestingWalletConfidential(bytes initArgs) → address
public
#Predicts the deterministic address for a confidential vesting wallet.
_validateVestingWalletInitArgs(bytes initArgs)
internal
#Virtual function that must be implemented to validate the initArgs bytes.
_initializeVestingWallet(address vestingWalletAddress, bytes initArgs)
internal
#Virtual function that must be implemented to initialize the vesting wallet at vestingWalletAddress
.
_deployVestingWalletImplementation() → address
internal
#Internal function that is called once to deploy the vesting wallet implementation.
Vesting wallet clones will be initialized by calls to the VestingWalletConfidentialFactory._initializeVestingWallet
function.
_getCreate2VestingWalletConfidentialSalt(bytes initArgs) → bytes32
internal
#Gets create2 salt for a confidential vesting wallet.
VestingWalletConfidentialFunded(address indexed vestingWalletConfidential, address indexed token, euint64 transferredAmount, bytes initArgs)
event
#Emitted for each vesting wallet funded within a batch.
VestingWalletConfidentialCreated(address indexed vestingWalletConfidential, bytes initArgs)
event
#Emitted when a vesting wallet is deployed.