文件 1 的 6:Address.sol
pragma solidity ^0.7.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 6:IERC20.sol
pragma solidity ^0.7.0;
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 6:ILockManager.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
interface ILockManager {
struct LockedStake {
uint256 amount;
uint256 votingPower;
}
function getAmountStaked(address staker, address stakedToken) external view returns (uint256);
function getStake(address staker, address stakedToken) external view returns (LockedStake memory);
function calculateVotingPower(address token, uint256 amount) external view returns (uint256);
function grantVotingPower(address receiver, address token, uint256 tokenAmount) external returns (uint256 votingPowerGranted);
function removeVotingPower(address receiver, address token, uint256 tokenAmount) external returns (uint256 votingPowerRemoved);
}
文件 4 的 6:SafeERC20.sol
pragma solidity ^0.7.0;
import "../interfaces/IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 5 的 6:SafeMath.sol
pragma solidity ^0.7.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction underflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
文件 6 的 6:Vault.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "./interfaces/IERC20.sol";
import "./interfaces/ILockManager.sol";
import "./lib/SafeMath.sol";
import "./lib/SafeERC20.sol";
contract Vault {
using SafeMath for uint256;
using SafeERC20 for IERC20;
ILockManager public lockManager;
struct Lock {
address token;
address receiver;
uint48 startTime;
uint16 vestingDurationInDays;
uint16 cliffDurationInDays;
uint256 amount;
uint256 amountClaimed;
uint256 votingPower;
}
struct LockBalance {
uint256 id;
uint256 claimableAmount;
Lock lock;
}
struct TokenBalance {
uint256 totalAmount;
uint256 claimableAmount;
uint256 claimedAmount;
uint256 votingPower;
}
uint256 constant internal SECONDS_PER_DAY = 86400;
mapping (uint256 => Lock) public tokenLocks;
mapping (address => uint256[]) public lockIds;
uint256 public numLocks;
event LockCreated(address indexed token, address indexed locker, address indexed receiver, uint256 lockId, uint256 amount, uint48 startTime, uint16 durationInDays, uint16 cliffInDays, uint256 votingPower);
event UnlockedTokensClaimed(address indexed receiver, address indexed token, uint256 indexed lockId, uint256 amountClaimed, uint256 votingPowerRemoved);
event LockExtended(uint256 indexed lockId, uint16 indexed oldDuration, uint16 indexed newDuration, uint16 oldCliff, uint16 newCliff, uint48 startTime);
constructor(address _lockManager) {
lockManager = ILockManager(_lockManager);
}
function lockTokens(
address token,
address locker,
address receiver,
uint48 startTime,
uint256 amount,
uint16 vestingDurationInDays,
uint16 cliffDurationInDays,
bool grantVotingPower
)
external
{
require(vestingDurationInDays > 0, "Vault::lockTokens: vesting duration must be > 0");
require(vestingDurationInDays <= 25*365, "Vault::lockTokens: vesting duration more than 25 years");
require(vestingDurationInDays >= cliffDurationInDays, "Vault::lockTokens: vesting duration < cliff");
require(amount > 0, "Vault::lockTokens: amount not > 0");
_lockTokens(token, locker, receiver, startTime, amount, vestingDurationInDays, cliffDurationInDays, grantVotingPower);
}
function lockTokensWithPermit(
address token,
address locker,
address receiver,
uint48 startTime,
uint256 amount,
uint16 vestingDurationInDays,
uint16 cliffDurationInDays,
bool grantVotingPower,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
)
external
{
require(vestingDurationInDays > 0, "Vault::lockTokensWithPermit: vesting duration must be > 0");
require(vestingDurationInDays <= 25*365, "Vault::lockTokensWithPermit: vesting duration more than 25 years");
require(vestingDurationInDays >= cliffDurationInDays, "Vault::lockTokensWithPermit: duration < cliff");
require(amount > 0, "Vault::lockTokensWithPermit: amount not > 0");
IERC20(token).permit(locker, address(this), amount, deadline, v, r, s);
_lockTokens(token, locker, receiver, startTime, amount, vestingDurationInDays, cliffDurationInDays, grantVotingPower);
}
function allActiveLockIds() external view returns(uint256[] memory){
uint256 activeCount;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
uint256[] memory result = new uint256[](activeCount);
uint256 j;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
result[j] = i;
j++;
}
}
return result;
}
function allActiveLocks() external view returns(Lock[] memory){
uint256 activeCount;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
Lock[] memory result = new Lock[](activeCount);
uint256 j;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
result[j] = lock;
j++;
}
}
return result;
}
function allActiveLockBalances() external view returns(LockBalance[] memory){
uint256 activeCount;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
LockBalance[] memory result = new LockBalance[](activeCount);
uint256 j;
for (uint256 i; i < numLocks; i++) {
Lock memory lock = tokenLocks[i];
if(lock.amount != lock.amountClaimed) {
result[j] = lockBalance(i);
j++;
}
}
return result;
}
function activeLockIds(address receiver) external view returns(uint256[] memory){
uint256 activeCount;
uint256[] memory receiverLockIds = lockIds[receiver];
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
uint256[] memory result = new uint256[](activeCount);
uint256 j;
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
result[j] = receiverLockIds[i];
j++;
}
}
return result;
}
function allLocks(address receiver) external view returns(Lock[] memory){
uint256[] memory allLockIds = lockIds[receiver];
Lock[] memory result = new Lock[](allLockIds.length);
for (uint256 i; i < allLockIds.length; i++) {
result[i] = tokenLocks[allLockIds[i]];
}
return result;
}
function activeLocks(address receiver) external view returns(Lock[] memory){
uint256 activeCount;
uint256[] memory receiverLockIds = lockIds[receiver];
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
Lock[] memory result = new Lock[](activeCount);
uint256 j;
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
result[j] = tokenLocks[receiverLockIds[i]];
j++;
}
}
return result;
}
function activeLockBalances(address receiver) external view returns(LockBalance[] memory){
uint256 activeCount;
uint256[] memory receiverLockIds = lockIds[receiver];
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
activeCount++;
}
}
LockBalance[] memory result = new LockBalance[](activeCount);
uint256 j;
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory lock = tokenLocks[receiverLockIds[i]];
if(lock.amount != lock.amountClaimed) {
result[j] = lockBalance(receiverLockIds[i]);
j++;
}
}
return result;
}
function totalTokenBalance(address token) external view returns(TokenBalance memory balance){
for (uint256 i; i < numLocks; i++) {
Lock memory tokenLock = tokenLocks[i];
if(tokenLock.token == token && tokenLock.amount != tokenLock.amountClaimed){
balance.totalAmount = balance.totalAmount.add(tokenLock.amount);
balance.votingPower = balance.votingPower.add(tokenLock.votingPower);
if(block.timestamp > tokenLock.startTime) {
balance.claimedAmount = balance.claimedAmount.add(tokenLock.amountClaimed);
uint256 elapsedTime = block.timestamp.sub(tokenLock.startTime);
uint256 elapsedDays = elapsedTime.div(SECONDS_PER_DAY);
if (
elapsedDays >= tokenLock.cliffDurationInDays
) {
if (elapsedDays >= tokenLock.vestingDurationInDays) {
balance.claimableAmount = balance.claimableAmount.add(tokenLock.amount).sub(tokenLock.amountClaimed);
} else {
uint256 vestingDurationInSecs = uint256(tokenLock.vestingDurationInDays).mul(SECONDS_PER_DAY);
uint256 vestingAmountPerSec = tokenLock.amount.div(vestingDurationInSecs);
uint256 amountVested = vestingAmountPerSec.mul(elapsedTime);
balance.claimableAmount = balance.claimableAmount.add(amountVested.sub(tokenLock.amountClaimed));
}
}
}
}
}
}
function tokenBalance(address token, address receiver) external view returns(TokenBalance memory balance){
uint256[] memory receiverLockIds = lockIds[receiver];
for (uint256 i; i < receiverLockIds.length; i++) {
Lock memory receiverLock = tokenLocks[receiverLockIds[i]];
if(receiverLock.token == token && receiverLock.amount != receiverLock.amountClaimed){
balance.totalAmount = balance.totalAmount.add(receiverLock.amount);
balance.votingPower = balance.votingPower.add(receiverLock.votingPower);
if(block.timestamp > receiverLock.startTime) {
balance.claimedAmount = balance.claimedAmount.add(receiverLock.amountClaimed);
uint256 elapsedTime = block.timestamp.sub(receiverLock.startTime);
uint256 elapsedDays = elapsedTime.div(SECONDS_PER_DAY);
if (
elapsedDays >= receiverLock.cliffDurationInDays
) {
if (elapsedDays >= receiverLock.vestingDurationInDays) {
balance.claimableAmount = balance.claimableAmount.add(receiverLock.amount).sub(receiverLock.amountClaimed);
} else {
uint256 vestingDurationInSecs = uint256(receiverLock.vestingDurationInDays).mul(SECONDS_PER_DAY);
uint256 vestingAmountPerSec = receiverLock.amount.div(vestingDurationInSecs);
uint256 amountVested = vestingAmountPerSec.mul(elapsedTime);
balance.claimableAmount = balance.claimableAmount.add(amountVested.sub(receiverLock.amountClaimed));
}
}
}
}
}
}
function lockBalance(uint256 lockId) public view returns (LockBalance memory balance) {
balance.id = lockId;
balance.claimableAmount = claimableBalance(lockId);
balance.lock = tokenLocks[lockId];
}
function claimableBalance(uint256 lockId) public view returns (uint256) {
Lock storage lock = tokenLocks[lockId];
if (block.timestamp < lock.startTime) {
return 0;
}
uint256 elapsedTime = block.timestamp.sub(lock.startTime);
uint256 elapsedDays = elapsedTime.div(SECONDS_PER_DAY);
if (elapsedDays < lock.cliffDurationInDays) {
return 0;
}
if (elapsedDays >= lock.vestingDurationInDays) {
return lock.amount.sub(lock.amountClaimed);
} else {
uint256 vestingDurationInSecs = uint256(lock.vestingDurationInDays).mul(SECONDS_PER_DAY);
uint256 vestingAmountPerSec = lock.amount.div(vestingDurationInSecs);
uint256 amountVested = vestingAmountPerSec.mul(elapsedTime);
return amountVested.sub(lock.amountClaimed);
}
}
function claimAllUnlockedTokens(uint256[] memory locks) external {
for (uint i = 0; i < locks.length; i++) {
uint256 claimableAmount = claimableBalance(locks[i]);
require(claimableAmount > 0, "Vault::claimAllUnlockedTokens: claimableAmount is 0");
_claimTokens(locks[i], claimableAmount);
}
}
function claimUnlockedTokenAmounts(uint256[] memory locks, uint256[] memory amounts) external {
require(locks.length == amounts.length, "Vault::claimUnlockedTokenAmounts: arrays must be same length");
for (uint i = 0; i < locks.length; i++) {
uint256 claimableAmount = claimableBalance(locks[i]);
require(claimableAmount >= amounts[i], "Vault::claimUnlockedTokenAmounts: claimableAmount < amount");
_claimTokens(locks[i], amounts[i]);
}
}
function extendLock(uint256 lockId, uint16 vestingDaysToAdd, uint16 cliffDaysToAdd) external {
Lock storage lock = tokenLocks[lockId];
require(msg.sender == lock.receiver, "Vault::extendLock: msg.sender must be receiver");
uint16 oldVestingDuration = lock.vestingDurationInDays;
uint16 newVestingDuration = _add16(oldVestingDuration, vestingDaysToAdd, "Vault::extendLock: vesting max days exceeded");
uint16 oldCliffDuration = lock.cliffDurationInDays;
uint16 newCliffDuration = _add16(oldCliffDuration, cliffDaysToAdd, "Vault::extendLock: cliff max days exceeded");
require(newCliffDuration <= 10*365, "Vault::extendLock: cliff more than 10 years");
require(newVestingDuration <= 25*365, "Vault::extendLock: vesting duration more than 25 years");
require(newVestingDuration >= newCliffDuration, "Vault::extendLock: duration < cliff");
lock.vestingDurationInDays = newVestingDuration;
emit LockExtended(lockId, oldVestingDuration, newVestingDuration, oldCliffDuration, newCliffDuration, lock.startTime);
}
function _lockTokens(
address token,
address locker,
address receiver,
uint48 startTime,
uint256 amount,
uint16 vestingDurationInDays,
uint16 cliffDurationInDays,
bool grantVotingPower
) internal {
IERC20(token).safeTransferFrom(locker, address(this), amount);
uint48 lockStartTime = startTime == 0 ? uint48(block.timestamp) : startTime;
uint256 votingPowerGranted;
if(grantVotingPower) {
votingPowerGranted = lockManager.grantVotingPower(receiver, token, amount);
}
Lock memory lock = Lock({
token: token,
receiver: receiver,
startTime: lockStartTime,
vestingDurationInDays: vestingDurationInDays,
cliffDurationInDays: cliffDurationInDays,
amount: amount,
amountClaimed: 0,
votingPower: votingPowerGranted
});
tokenLocks[numLocks] = lock;
lockIds[receiver].push(numLocks);
emit LockCreated(token, locker, receiver, numLocks, amount, lockStartTime, vestingDurationInDays, cliffDurationInDays, votingPowerGranted);
numLocks++;
}
function _claimTokens(uint256 lockId, uint256 claimAmount) internal {
Lock storage lock = tokenLocks[lockId];
uint256 votingPowerRemoved;
if (lock.votingPower > 0) {
votingPowerRemoved = lockManager.removeVotingPower(lock.receiver, lock.token, claimAmount);
lock.votingPower = lock.votingPower.sub(votingPowerRemoved);
}
lock.amountClaimed = lock.amountClaimed.add(claimAmount);
IERC20(lock.token).safeTransfer(lock.receiver, claimAmount);
emit UnlockedTokensClaimed(lock.receiver, lock.token, lockId, claimAmount, votingPowerRemoved);
}
function _add16(uint16 a, uint16 b, string memory errorMessage) internal pure returns (uint16) {
uint16 c = a + b;
require(c >= a, errorMessage);
return c;
}
}
{
"compilationTarget": {
"contracts/Vault.sol": "Vault"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_lockManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"locker","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"lockId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"startTime","type":"uint48"},{"indexed":false,"internalType":"uint16","name":"durationInDays","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"cliffInDays","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"votingPower","type":"uint256"}],"name":"LockCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"lockId","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"oldDuration","type":"uint16"},{"indexed":true,"internalType":"uint16","name":"newDuration","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"oldCliff","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"newCliff","type":"uint16"},{"indexed":false,"internalType":"uint48","name":"startTime","type":"uint48"}],"name":"LockExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"lockId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingPowerRemoved","type":"uint256"}],"name":"UnlockedTokensClaimed","type":"event"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"activeLockBalances","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock","name":"lock","type":"tuple"}],"internalType":"struct Vault.LockBalance[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"activeLockIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"activeLocks","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allActiveLockBalances","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock","name":"lock","type":"tuple"}],"internalType":"struct Vault.LockBalance[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allActiveLockIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allActiveLocks","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"allLocks","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"locks","type":"uint256[]"}],"name":"claimAllUnlockedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"locks","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"claimUnlockedTokenAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockId","type":"uint256"}],"name":"claimableBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockId","type":"uint256"},{"internalType":"uint16","name":"vestingDaysToAdd","type":"uint16"},{"internalType":"uint16","name":"cliffDaysToAdd","type":"uint16"}],"name":"extendLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockId","type":"uint256"}],"name":"lockBalance","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.Lock","name":"lock","type":"tuple"}],"internalType":"struct Vault.LockBalance","name":"balance","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"lockIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockManager","outputs":[{"internalType":"contract ILockManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"locker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"bool","name":"grantVotingPower","type":"bool"}],"name":"lockTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"locker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"bool","name":"grantVotingPower","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"lockTokensWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"numLocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"tokenBalance","outputs":[{"components":[{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.TokenBalance","name":"balance","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenLocks","outputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint48","name":"startTime","type":"uint48"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"cliffDurationInDays","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountClaimed","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"totalTokenBalance","outputs":[{"components":[{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"internalType":"struct Vault.TokenBalance","name":"balance","type":"tuple"}],"stateMutability":"view","type":"function"}]