// SPDX-License-Identifier: ISC
pragma solidity 0.8.25;
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./IClaimable.sol";
/**
* @notice Contract that supports one-time issuance of a configurable amount of tokens to a configurable number of
* addresses over a configurable vesting schedule. Instead of tracking token balances at construction-/initialization
* time, a merkle root is posted to this contract, allowing accounts to prove their balances if they so choose.
*
* Note: only the associated token contract is able to call `proveInitialBalance(...)`, so this contract is very tightly-
* coupled to tokens that resemble the Anvil token, that is to say governance tokens that permit accounts with balances
* in this Claim contract to be delegated and used in voting.
*
* @custom:security-contact security@af.xyz
*/
contract Claim is IClaimable, Ownable2Step {
/***************
* ERROR TYPES *
***************/
error Unauthorized();
error AlreadyInitialized();
error ClaimAmountTooBig(uint256 _requested, uint256 _availableForClaim);
error InvalidProof();
error NoClaimableTokens();
error OwnerRescueTooSoon();
error RescueDestinationHasInitialBalance();
error VestingPeriodNotStarted();
error InvalidInitialization();
/***********
* STRUCTS *
***********/
// NB: Total issuance is 100_000_000_000e18, so uint128 is plenty
struct Balance {
uint128 initial;
uint128 claimed;
}
/**********
* EVENTS *
**********/
event TokensClaimed(address indexed byAccount, uint256 amount);
event InitialBalanceProven(address indexed account, uint256 initialBalance);
event FundsRescued(address indexed to, uint256 amount);
/******************
* CONTRACT STATE *
******************/
/// The merkle root of initial user balances that will be subject to this contract's vesting / claim functionality.
/// NB: leaves in this tree are of the format `abi.encode(address _address, uint256 _balance)`.
bytes32 public balanceRoot;
/// The timestamp at which vesting begins. No tokens will be claimable prior to this date.
uint32 public vestingStartTimestamp;
/// The number of seconds after `vestingStartTimestamp` at which point all tokens will be vested.
uint32 public vestingPeriodSeconds;
/// The timestamp after which unproven balances may be withdrawn by the owner. See documentation for ownerRescueTokens(...).
uint32 public ownerRescueTimestamp;
/// The total amount that has been proven but not claimed. This reduces the amount eligible for rescue after `ownerRescueTimestamp`.
uint128 public totalProvenUnclaimed;
/// The token for which this contract manages claims
IERC20 public token;
/// account address => Balance (see struct above). Note: this struct is 0 until `proveInitialBalance()` is called.
mapping(address => Balance) public provenBalances;
/****************
* PUBLIC VIEWS *
****************/
/**
* @notice Returns the proven unclaimed balance for the provided account.
* @param _forAccount The account for which the proven unclaimed balance will be returned.
* @return The proven unclaimed balance.
*/
function getProvenUnclaimedBalance(address _forAccount) public view returns (uint256) {
Balance storage provenBalanceStorage = provenBalances[_forAccount];
return uint256(provenBalanceStorage.initial - provenBalanceStorage.claimed);
}
/*****************************
* STATE-MODIFYING FUNCTIONS *
*****************************/
/// Note: this contract is mostly useless until it is initialized via `initialize(...)` below.
constructor() Ownable(msg.sender) {}
/**
* @notice Initializes this claim contract, indicating the token that may be claimed, the root of the balances
* merkle tree and vesting parameters.
*
* @dev Leaves of the merkle tree for which the root is provided are of the format `abi.encode(address, uint256)`,
* where the address is the address of the account and the uint256 is the initial balance of that account.
*
* Note: It is assumed, not enforced, that this contract will receive the amount of _token that constitutes the sum
* of all leaves in the merkle tree for which _balanceRoot is the root. On-chain observers will have no way of
* knowing that, but off-chain proofs can be made publicly available to provide this assurance.
* @param _token The ERC-20 token on which this contract operates.
* @param _balanceRoot The merkle root of the balances.
* @param _vestingStartDelaySeconds The number of seconds after which claim vesting will start.
* @param _vestingPeriodSeconds The period over which token vesting will complete.
* @param _ownerRescueDelaySeconds The number of seconds after initialization when the owner may withdraw all unproven tokens.
*/
function initialize(
IERC20 _token,
bytes32 _balanceRoot,
uint256 _vestingStartDelaySeconds,
uint256 _vestingPeriodSeconds,
uint256 _ownerRescueDelaySeconds
) external onlyOwner {
if (address(token) != address(0)) revert AlreadyInitialized();
if (address(_token) == address(0)) revert InvalidInitialization();
if (_balanceRoot == bytes32(0)) revert InvalidInitialization();
if (_vestingPeriodSeconds == 0) revert InvalidInitialization();
token = _token;
balanceRoot = _balanceRoot;
vestingStartTimestamp = uint32(block.timestamp + _vestingStartDelaySeconds);
vestingPeriodSeconds = uint32(_vestingPeriodSeconds);
ownerRescueTimestamp = uint32(block.timestamp + _ownerRescueDelaySeconds);
}
/**
* @notice Claims the provided amount to the sender, assuming that address has a sufficient proven claimable amount.
* @param _amount The amount to claim.this
*/
function claim(uint256 _amount) external {
uint256 vestingStart = vestingStartTimestamp;
if (block.timestamp <= vestingStart) revert VestingPeriodNotStarted();
uint256 vestedSeconds = block.timestamp - vestingStartTimestamp;
uint256 periodSeconds = vestingPeriodSeconds;
Balance storage provenBalanceStorage = provenBalances[msg.sender];
uint256 vested = vestedSeconds >= periodSeconds
? uint256(provenBalanceStorage.initial)
: (vestedSeconds * uint256(provenBalanceStorage.initial)) / periodSeconds;
uint256 claimed = uint256(provenBalanceStorage.claimed);
uint256 claimableBalance = vested - claimed;
if (claimableBalance == 0) revert NoClaimableTokens();
if (_amount > claimableBalance) revert ClaimAmountTooBig(_amount, claimableBalance);
if (_amount == 0) {
_amount = claimableBalance;
}
provenBalanceStorage.claimed = uint128(claimed + _amount);
totalProvenUnclaimed -= uint128(_amount);
// NB: Return value not checked because this was developed for Anvil, and that reverts on failure.
// If repurposing this contract, update to suit your needs.
token.transfer(msg.sender, _amount);
emit TokensClaimed(msg.sender, _amount);
}
/**
* @notice Allows tokens locked in this contract to be rescued by the owner after a sufficiently long period of time
* allowing intended owners to prove their balances. The idea is that if the intended owner hasn't taken action,
* they have lost access or do not care to claim.
* @dev This disables all future proofs and rescues.
* @param _destination The address to which tokens will be transferred.
*/
function ownerRescueTokens(address _destination) external onlyOwner {
if (block.timestamp < ownerRescueTimestamp) revert OwnerRescueTooSoon();
if (getProvenUnclaimedBalance(_destination) > 0) revert RescueDestinationHasInitialBalance();
delete balanceRoot;
uint256 amount = token.balanceOf(address(this)) - totalProvenUnclaimed;
if (amount == 0) return;
// NB: Return value not checked because this was developed for Anvil, and that reverts on failure.
// If repurposing this contract, update to suit your needs.
token.transfer(_destination, amount);
emit FundsRescued(_destination, amount);
}
/**
* @inheritdoc IClaimable
*/
function proveInitialBalance(
address _address,
uint256 _initialBalance,
bytes32[] calldata _proof
) external returns (uint256) {
if (msg.sender != address(token)) revert Unauthorized();
Balance storage provenBalanceStorage = provenBalances[_address];
if (provenBalanceStorage.initial != 0) return 0;
_verifyInitialBalanceOrRevert(_address, _initialBalance, _proof);
totalProvenUnclaimed += uint128(_initialBalance);
provenBalanceStorage.initial = uint128(_initialBalance);
emit InitialBalanceProven(_address, _initialBalance);
return _initialBalance;
}
/**
* Note: this is overridden to disable it.
*
* @inheritdoc Ownable
*/
function renounceOwnership() public override onlyOwner {
// Disallow accidental and intentional ownership renunciation.
revert();
}
/********************************
* PRIVATE / INTERNAL FUNCTIONS *
********************************/
/**
* @notice Verifies the provided address has the provided initial balance according to the provided merkle proof,
* reverting if it does not.
* @param _address The address in question.
* @param _initialBalance The initial balance being proven.
* @param _proof The merkle proof that the address has the balance.
*/
function _verifyInitialBalanceOrRevert(
address _address,
uint256 _initialBalance,
bytes32[] calldata _proof
) private view {
if (
!MerkleProof.verifyCalldata(
_proof,
balanceRoot,
keccak256(abi.encodePacked(keccak256(abi.encode(_address, _initialBalance))))
)
) {
revert InvalidProof();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: ISC
pragma solidity 0.8.25;
interface IClaimable {
/**
* @notice Proves that the provided address has the provided initial balance, enabling claim and voting.
* @dev The merkle proof is for a merkle tree for which leaves take the form
* `abi.encode(address _address, uint256 _balance)`.
*
* This function will revert if the provided merkle proof is not valid UNLESS a balance for the account had previously
* been proven via a successful invocation of this function, in which case this is a no-op that always returns 0.
* @param _address The address of the account for which the initial balance is being proven.
* @param _initialBalance The initial balance of the address, as proven by the provided merkle proof.
* @param _proof The merkle proof that proves the initial balance for the address.
* @return The amount that has been proven that was not previously proven (will be 0 after initial call for an address).
*/
function proveInitialBalance(
address _address,
uint256 _initialBalance,
bytes32[] calldata _proof
) external returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
{
"compilationTarget": {
"contracts/governance/Claim.sol": "Claim"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requested","type":"uint256"},{"internalType":"uint256","name":"_availableForClaim","type":"uint256"}],"name":"ClaimAmountTooBig","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"NoClaimableTokens","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerRescueTooSoon","type":"error"},{"inputs":[],"name":"RescueDestinationHasInitialBalance","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VestingPeriodNotStarted","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundsRescued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"initialBalance","type":"uint256"}],"name":"InitialBalanceProven","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"byAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"balanceRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_forAccount","type":"address"}],"name":"getProvenUnclaimedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"bytes32","name":"_balanceRoot","type":"bytes32"},{"internalType":"uint256","name":"_vestingStartDelaySeconds","type":"uint256"},{"internalType":"uint256","name":"_vestingPeriodSeconds","type":"uint256"},{"internalType":"uint256","name":"_ownerRescueDelaySeconds","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerRescueTimestamp","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_destination","type":"address"}],"name":"ownerRescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint256","name":"_initialBalance","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"proveInitialBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"provenBalances","outputs":[{"internalType":"uint128","name":"initial","type":"uint128"},{"internalType":"uint128","name":"claimed","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalProvenUnclaimed","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingPeriodSeconds","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingStartTimestamp","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]