Claim Vesting In Smart Contract
In the
create vesting via smart contractthe vesting NFT is sent to themsg.sender, in this case, the user itself holds the vesting NFT and can claim directly on theLiquivest Proxy- this tutorial assumes a smart contract is the owner of a vesting NFT.
If an
EOAis the owner of a vesting NFT then this tutorial does not apply.
Summary
Consider the scenario whereby a DAO, treasury or LP lock is holding a vesting NFT and has to decentralise the splitting and arrangement of funds in a certain way. Perhaps this does one of the following actions.
- Sends funds to developer.
- Splits funds between different amounts of parties.
- LP’s funds.
- Tokens are sent onwards to a contract that can action a call based on a snapshot vote.
- Some arbitrary action with the tokens.
High Level Steps
- Have a smart contract be the owner of a vesting NFT.
- Have a way to claim tokens from Liquivest Proxy.
- Have a way to spend tokens in some allocated way.
Actors
- msg.sender
- User calling or multisig.
- LiquivestClaimer
- The example contract below, showing how to claim a vesting token from a contract and spend the funds.
- LiquivestProxy
- Liquivest proxy used to manage calls to the different vesting types.
- ConcreteVestingNFT
- The specific vesting chosen.
Basic flow
// claim
msg.sender --ILiquivestProxy::batchClaim--> LiquivestClaimer // User calls LiquivestClaimer to call and claim tokens from Liquivest Proxy
// The rest of the sequence is handled by Liquivest
// claim
LiquivestClaimer --IERC5725::claim--> LiquivestProxy // LiquivestClaimer forwards the call to claim tokens from LiquivestProxy
LiquivestProxy --IERC5725::claim--> ConcreteVestingNFT // LiquivestProxy forwards the call to claim tokens from ConcreteVestingNFT
// transfer
ConcreteVestingNFT --IERC20::transferFrom--> LiquivestProxy // ConcreteVestingNFT sends tokens to LiquivestProxy
LiquivestProxy --IERC20::transferFrom--> LiquivestClaimer // LiquivestProxy sends tokens to LiquivestClaimer
// Transaction confirms, tokens are now owned by LiquivestClaimer LiquivestClaimer Example
The LiquivestClaimer contract is an example and are not audited or battle-tested, you should test your intergration thoroughly.
In this example, it is assumed this contract owns a IERC5725 / Liquivest Vesting NFT, this could be implemented in the contract, or be sent manually.
The owner or multisig of the contract is allowed to call claimAll which allows the contract to hold multiple vesting NFTs whereby the claimable amount from the vesting NFT is sent to this contract.
It is up to the user to implement how these tokens are to be used as mentioned above.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface IERC5725 {
function claim(uint256 tokenId) external;
function payoutToken(uint256 tokenId) external view returns (address);
function claimablePayout(uint256 tokenId) external view returns (uint256);
}
interface ILiquivestProxy {
function batchClaim(address[] calldata vestingContracts, uint256[] calldata tokenIds) external;
}
// This contract should not be upgradable to increase trust
contract LiquivestClaimer is Ownable {
// Consider having this contract be owned by a multisig
address public liquivestProxy;
constructor(address _liquivestProxy) Ownable(msg.sender) {
require(_liquivestProxy != address(0), "Invalid proxy address");
liquivestProxy = _liquivestProxy;
}
/// @notice Claims multiple vesting NFTs that this contract owns
/// @param vestingContracts The vesting NFT contract addresses
/// @param tokenIds The token IDs to claim
function claimAll(address[] calldata vestingContracts, uint256[] calldata tokenIds) external onlyOwner {
require(vestingContracts.length == tokenIds.length, "Mismatched array lengths");
ILiquivestProxy(liquivestProxy).batchClaim(vestingContracts, tokenIds);
}
// Consider adding a function to split/spend the recieved vested tokens in a decentralised way
}
