Contract Name:
veArcDistributor
Contract Source Code:
File 1 of 1 : veArcDistributor
// Sources flattened with hardhat v2.19.5 https://hardhat.org
// SPDX-License-Identifier: GPL-3.0-or-later AND MIT
// File @openzeppelin/contracts/token/ERC20/[email protected]
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// File @openzeppelin/contracts/utils/math/[email protected]
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
// File contracts/interfaces/IVotingEscrow.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity 0.8.22;
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope; // # -dweight / dt
uint256 ts;
uint256 blk; // block
}
function user_point_epoch(uint tokenId) external view returns (uint);
function epoch() external view returns (uint);
function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
function point_history(uint loc) external view returns (Point memory);
function checkpoint() external;
function deposit_for(uint tokenId, uint value) external;
function token() external view returns (address);
function user_point_history__ts(uint tokenId, uint idx) external view returns (uint);
function locked__end(uint _tokenId) external view returns (uint);
function locked__amount(uint _tokenId) external view returns (uint);
function approve(address spender, uint tokenId) external;
function balanceOfNFT(uint) external view returns (uint);
function isApprovedOrOwner(address, uint) external view returns (bool);
function ownerOf(uint) external view returns (address);
function transferFrom(address, address, uint) external;
function totalSupply() external view returns (uint);
function supply() external view returns (uint);
function create_lock_for(uint, uint, address) external returns (uint);
function lockVote(uint tokenId) external;
function isVoteExpired(uint tokenId) external view returns (bool);
function voteExpiry(uint _tokenId) external view returns (uint);
function attach(uint tokenId) external;
function detach(uint tokenId) external;
function voting(uint tokenId) external;
function abstain(uint tokenId) external;
function voted(uint tokenId) external view returns (bool);
function withdraw(uint tokenId) external;
function create_lock(uint value, uint duration) external returns (uint);
function setVoter(address voter) external;
function balanceOf(address owner) external view returns (uint);
function safeTransferFrom(address from, address to, uint tokenId) external;
function burn(uint _tokenId) external;
function setAdmin(address _admin) external;
function setArtProxy(address _proxy) external;
}
// File contracts/veArcDistributor.sol
// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.22;
/*
@title Curve Fee Distribution modified for ve(3,3) emissions
@author Curve Finance, andrecronje
@license MIT
*/
contract veArcDistributor {
event CheckpointToken(
uint time,
uint tokens
);
event Claimed(
uint tokenId,
uint amount,
uint claim_epoch,
uint max_epoch
);
uint constant WEEK = 7 * 86400;
uint public start_time;
uint public time_cursor;
mapping(uint => uint) public time_cursor_of;
mapping(uint => uint) public user_epoch_of;
uint public last_token_time;
uint[1000000000000000] public tokens_per_week;
address public voting_escrow;
address public token;
uint public token_last_balance;
uint[1000000000000000] public ve_supply;
address public depositor;
constructor(address _voting_escrow) {
uint _t = block.timestamp / WEEK * WEEK;
start_time = _t;
last_token_time = _t;
time_cursor = _t;
address _token = IVotingEscrow(_voting_escrow).token();
token = _token;
voting_escrow = _voting_escrow;
depositor = msg.sender;
IERC20(_token).approve(_voting_escrow, type(uint).max);
}
function timestamp() external view returns (uint) {
return block.timestamp / WEEK * WEEK;
}
function _checkpoint_token() internal {
uint token_balance = IERC20(token).balanceOf(address(this));
uint to_distribute = token_balance - token_last_balance;
token_last_balance = token_balance;
uint t = last_token_time;
uint since_last = block.timestamp - t;
last_token_time = block.timestamp;
uint this_week = t / WEEK * WEEK;
uint next_week = 0;
for (uint i = 0; i < 20; i++) {
next_week = this_week + WEEK;
if (block.timestamp < next_week) {
if (since_last == 0 && block.timestamp == t) {
tokens_per_week[this_week] += to_distribute;
} else {
tokens_per_week[this_week] += to_distribute * (block.timestamp - t) / since_last;
}
break;
} else {
if (since_last == 0 && next_week == t) {
tokens_per_week[this_week] += to_distribute;
} else {
tokens_per_week[this_week] += to_distribute * (next_week - t) / since_last;
}
}
t = next_week;
this_week = next_week;
}
emit CheckpointToken(block.timestamp, to_distribute);
}
function checkpoint_token() external {
assert(msg.sender == depositor);
_checkpoint_token();
}
function _find_timestamp_epoch(address ve, uint _timestamp) internal view returns (uint) {
uint _min = 0;
uint _max = IVotingEscrow(ve).epoch();
for (uint i = 0; i < 128; i++) {
if (_min >= _max) break;
uint _mid = (_min + _max + 2) / 2;
IVotingEscrow.Point memory pt = IVotingEscrow(ve).point_history(_mid);
if (pt.ts <= _timestamp) {
_min = _mid;
} else {
_max = _mid - 1;
}
}
return _min;
}
function _find_timestamp_user_epoch(address ve, uint tokenId, uint _timestamp, uint max_user_epoch) internal view returns (uint) {
uint _min = 0;
uint _max = max_user_epoch;
for (uint i = 0; i < 128; i++) {
if (_min >= _max) break;
uint _mid = (_min + _max + 2) / 2;
IVotingEscrow.Point memory pt = IVotingEscrow(ve).user_point_history(tokenId, _mid);
if (pt.ts <= _timestamp) {
_min = _mid;
} else {
_max = _mid -1;
}
}
return _min;
}
function ve_for_at(uint _tokenId, uint _timestamp) external view returns (uint) {
address ve = voting_escrow;
uint max_user_epoch = IVotingEscrow(ve).user_point_epoch(_tokenId);
uint epoch = _find_timestamp_user_epoch(ve, _tokenId, _timestamp, max_user_epoch);
IVotingEscrow.Point memory pt = IVotingEscrow(ve).user_point_history(_tokenId, epoch);
return Math.max(uint(int256(pt.bias - pt.slope * (int128(int256(_timestamp - pt.ts))))), 0);
}
function _checkpoint_total_supply() internal {
address ve = voting_escrow;
uint t = time_cursor;
uint rounded_timestamp = block.timestamp / WEEK * WEEK;
IVotingEscrow(ve).checkpoint();
for (uint i = 0; i < 20; i++) {
if (t > rounded_timestamp) {
break;
} else {
uint epoch = _find_timestamp_epoch(ve, t);
IVotingEscrow.Point memory pt = IVotingEscrow(ve).point_history(epoch);
int128 dt = 0;
if (t > pt.ts) {
dt = int128(int256(t - pt.ts));
}
ve_supply[t] = Math.max(uint(int256(pt.bias - pt.slope * dt)), 0);
}
t += WEEK;
}
time_cursor = t;
}
function checkpoint_total_supply() external {
_checkpoint_total_supply();
}
function _claim(uint _tokenId, address ve, uint _last_token_time) internal returns (uint) {
uint user_epoch = 0;
uint to_distribute = 0;
uint max_user_epoch = IVotingEscrow(ve).user_point_epoch(_tokenId);
uint _start_time = start_time;
if (max_user_epoch == 0) return 0;
uint week_cursor = time_cursor_of[_tokenId];
if (week_cursor == 0) {
user_epoch = _find_timestamp_user_epoch(ve, _tokenId, _start_time, max_user_epoch);
} else {
user_epoch = user_epoch_of[_tokenId];
}
if (user_epoch == 0) user_epoch = 1;
IVotingEscrow.Point memory user_point = IVotingEscrow(ve).user_point_history(_tokenId, user_epoch);
if (week_cursor == 0) week_cursor = (user_point.ts + WEEK - 1) / WEEK * WEEK;
if (week_cursor >= last_token_time) return 0;
if (week_cursor < _start_time) week_cursor = _start_time;
IVotingEscrow.Point memory old_user_point;
for (uint i = 0; i < 50; i++) {
if (week_cursor >= _last_token_time) break;
if (week_cursor >= user_point.ts && user_epoch <= max_user_epoch) {
user_epoch += 1;
old_user_point = user_point;
if (user_epoch > max_user_epoch) {
user_point = IVotingEscrow.Point(0,0,0,0);
} else {
user_point = IVotingEscrow(ve).user_point_history(_tokenId, user_epoch);
}
} else {
int128 dt = int128(int256(week_cursor - old_user_point.ts));
uint balance_of = Math.max(uint(int256(old_user_point.bias - dt * old_user_point.slope)), 0);
if (balance_of == 0 && user_epoch > max_user_epoch) break;
if (balance_of > 0) {
to_distribute += balance_of * tokens_per_week[week_cursor] / ve_supply[week_cursor];
}
week_cursor += WEEK;
}
}
user_epoch = Math.min(max_user_epoch, user_epoch - 1);
user_epoch_of[_tokenId] = user_epoch;
time_cursor_of[_tokenId] = week_cursor;
emit Claimed(_tokenId, to_distribute, user_epoch, max_user_epoch);
return to_distribute;
}
function _claimable(uint _tokenId, address ve, uint _last_token_time) internal view returns (uint) {
uint user_epoch = 0;
uint to_distribute = 0;
uint max_user_epoch = IVotingEscrow(ve).user_point_epoch(_tokenId);
uint _start_time = start_time;
if (max_user_epoch == 0) return 0;
uint week_cursor = time_cursor_of[_tokenId];
if (week_cursor == 0) {
user_epoch = _find_timestamp_user_epoch(ve, _tokenId, _start_time, max_user_epoch);
} else {
user_epoch = user_epoch_of[_tokenId];
}
if (user_epoch == 0) user_epoch = 1;
IVotingEscrow.Point memory user_point = IVotingEscrow(ve).user_point_history(_tokenId, user_epoch);
if (week_cursor == 0) week_cursor = (user_point.ts + WEEK - 1) / WEEK * WEEK;
if (week_cursor >= last_token_time) return 0;
if (week_cursor < _start_time) week_cursor = _start_time;
IVotingEscrow.Point memory old_user_point;
for (uint i = 0; i < 50; i++) {
if (week_cursor >= _last_token_time) break;
if (week_cursor >= user_point.ts && user_epoch <= max_user_epoch) {
user_epoch += 1;
old_user_point = user_point;
if (user_epoch > max_user_epoch) {
user_point = IVotingEscrow.Point(0,0,0,0);
} else {
user_point = IVotingEscrow(ve).user_point_history(_tokenId, user_epoch);
}
} else {
int128 dt = int128(int256(week_cursor - old_user_point.ts));
uint balance_of = Math.max(uint(int256(old_user_point.bias - dt * old_user_point.slope)), 0);
if (balance_of == 0 && user_epoch > max_user_epoch) break;
if (balance_of > 0) {
to_distribute += balance_of * tokens_per_week[week_cursor] / ve_supply[week_cursor];
}
week_cursor += WEEK;
}
}
return to_distribute;
}
function claimable(uint _tokenId) external view returns (uint) {
uint _last_token_time = last_token_time / WEEK * WEEK;
return _claimable(_tokenId, voting_escrow, _last_token_time);
}
function claim(uint _tokenId) external returns (uint) {
if (block.timestamp >= time_cursor) _checkpoint_total_supply();
uint _last_token_time = last_token_time;
_last_token_time = _last_token_time / WEEK * WEEK;
uint amount = _claim(_tokenId, voting_escrow, _last_token_time);
if (amount != 0) {
IVotingEscrow(voting_escrow).deposit_for(_tokenId, amount);
token_last_balance -= amount;
}
return amount;
}
function claim_many(uint[] memory _tokenIds) external returns (bool) {
if (block.timestamp >= time_cursor) _checkpoint_total_supply();
uint _last_token_time = last_token_time;
_last_token_time = _last_token_time / WEEK * WEEK;
address _voting_escrow = voting_escrow;
uint total = 0;
for (uint i = 0; i < _tokenIds.length; i++) {
uint _tokenId = _tokenIds[i];
if (_tokenId == 0) break;
uint amount = _claim(_tokenId, _voting_escrow, _last_token_time);
if (amount != 0) {
IVotingEscrow(_voting_escrow).deposit_for(_tokenId, amount);
total += amount;
}
}
if (total != 0) {
token_last_balance -= total;
}
return true;
}
// Once off event on contract initialize
function setDepositor(address _depositor) external {
require(msg.sender == depositor);
depositor = _depositor;
}
}