// 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: UNLICENSED
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract DynamicVestingV1 {
mapping(address => uint256) public startTimes;
mapping(address => uint256[]) public releaseAmounts;
}
contract VestingBase is Ownable {
address duelToken;
constructor(address _duelToken) Ownable(msg.sender) {
duelToken = _duelToken;
}
function setDuelToken(address newContract) external onlyOwner {
duelToken = newContract;
}
function allocateDUEL(address, uint256) external virtual {}
function allocateDUEL(address, uint256, uint256) external virtual {}
function claimDUEL() external virtual {}
}
/// @title Core static vesting contract allowing for cliff and variable percentage releases
/// @author Haider
/// @notice Contract uses pre-approved DUEL amount to transferFrom() the owner()
/// @notice Deploy 3 versions: Private Sale, Team & Advisors, Operations & Marketing
contract StaticVesting is VestingBase {
uint256[] public releaseTimestamps; // Release UNIX timestamps
uint16[] public releasePercentages; // Release percentage [0,0,0,100,100,100,...]
mapping(address => uint256) public vestedAllocations; // wallet => total allocated
mapping(address => uint256) public vestedClaims; // wallet => total claimed
constructor(
address _duelToken,
uint256[] memory _releaseTimestamps,
uint16[] memory _releasePercentages
) VestingBase(_duelToken) {
require(
releaseTimestamps.length == releasePercentages.length,
"PARAMETERS_MISMATCH"
);
releaseTimestamps = _releaseTimestamps;
releasePercentages = _releasePercentages;
}
function updateReleaseParameters(
uint256[] memory _releaseTimestamps,
uint16[] memory _releasePercentages
) external onlyOwner {
require(
releaseTimestamps.length == releasePercentages.length,
"PARAMETERS_MISMATCH"
);
releaseTimestamps = _releaseTimestamps;
releasePercentages = _releasePercentages;
}
function batchAllocateDUEL(
address[] memory wallets,
uint256[] memory allocations
) external onlyOwner {
for (uint16 i = 0; i < wallets.length; i++) {
vestedAllocations[wallets[i]] = allocations[i];
}
}
function batchUpdateAllocations(
address[] memory wallets,
uint256[] memory allocations,
uint256[] memory claims
) external onlyOwner {
for (uint16 i = 0; i < wallets.length; i++) {
vestedAllocations[wallets[i]] = allocations[i];
vestedClaims[wallets[i]] = claims[i];
}
}
function allocateDUEL(
address wallet,
uint256 amount
) external override onlyOwner {
require(vestedAllocations[wallet] == 0, "ALREADY_ALLOCATED");
vestedAllocations[wallet] = amount;
}
function claimDUEL() external override {
require(
vestedAllocations[_msgSender()] > 0 &&
vestedAllocations[_msgSender()] > vestedClaims[_msgSender()],
"NO_CLAIMS"
);
uint16 cumulativePercentage = 0; // Cumulative percentage released so far
for (uint8 i = 0; i < releaseTimestamps.length; i++) {
if (releaseTimestamps[i] > block.timestamp) {
break;
}
cumulativePercentage += releasePercentages[i];
}
uint256 availableClaim = (vestedAllocations[_msgSender()] *
cumulativePercentage) / 10000;
require(availableClaim > vestedClaims[_msgSender()], "ALREADY_CLAIMED");
uint256 unclaimedTokens = availableClaim - vestedClaims[_msgSender()];
vestedClaims[_msgSender()] += unclaimedTokens;
IERC20(duelToken).transfer(_msgSender(), unclaimedTokens);
}
function withdrawDUEL(uint256 amount) external onlyOwner {
if (amount == 0) {
amount = IERC20(duelToken).balanceOf(address(this));
}
IERC20(duelToken).transfer(owner(), amount);
}
}
/// @title Core dynamic vesting contract to be used upon RAINxDUEL conversion
/// @author Haider
/// @notice Contract uses pre-approved DUEL amount to transferFrom() the owner()
/// @dev Does not allow for two separate schedules for same wallet
contract DynamicVesting is VestingBase {
DynamicVestingV1 public dynamicVestingV1;
mapping(address => uint256) public startTimes; // wallet => claim start time
mapping(address => uint256[]) public releaseAmounts; // wallet => [amount30d, amount90d]
constructor(address _duelToken) VestingBase(_duelToken) {}
function setDynamicVestingV1(
DynamicVestingV1 newContract
) external onlyOwner {
dynamicVestingV1 = newContract;
}
function batchV1Transition(address[] memory wallets) external onlyOwner {
for (uint16 i = 0; i < wallets.length; i++) {
if (dynamicVestingV1.startTimes(wallets[i]) > 0) {
startTimes[wallets[i]] = dynamicVestingV1.startTimes(
wallets[i]
);
releaseAmounts[wallets[i]].push(
dynamicVestingV1.releaseAmounts(wallets[i], 0)
);
releaseAmounts[wallets[i]].push(
dynamicVestingV1.releaseAmounts(wallets[i], 1)
);
}
}
}
function allocateDUEL(
address wallet,
uint256 amount30d,
uint256 amount90d
) external override {
require(
_msgSender() == duelToken || _msgSender() == owner(),
"Access forbidden"
);
require(startTimes[wallet] == 0, "Vesting already in progress");
startTimes[wallet] = block.timestamp;
releaseAmounts[wallet] = [amount30d, amount90d];
}
function claimDUEL() external override {
require(startTimes[_msgSender()] >= 0, "Nothing vested");
uint256 availableAmount = 0;
if (
block.timestamp >= startTimes[_msgSender()] + 30 days &&
releaseAmounts[_msgSender()][0] > 0
) {
availableAmount += releaseAmounts[_msgSender()][0];
releaseAmounts[_msgSender()][0] = 0;
}
if (
block.timestamp >= startTimes[_msgSender()] + 90 days &&
releaseAmounts[_msgSender()][1] > 0
) {
availableAmount += releaseAmounts[_msgSender()][1];
releaseAmounts[_msgSender()][1] = 0;
}
require(availableAmount > 0, "Nothing to claim");
IERC20(duelToken).transferFrom(owner(), _msgSender(), availableAmount);
}
}
interface IWinbackStaking {
function stakeWinback(uint256 amount, uint32 periodDays) external;
}
contract DynamicVestingMin is Ownable {
address duelToken;
address vestingV1;
address winbackStaking;
bytes32 public merkleRoot;
mapping(address => bool[]) public claimed;
event Claimed(address indexed wallet, uint256 amount, bool was30d);
constructor(address _duelToken, address _vestingV1) Ownable(msg.sender) {
duelToken = _duelToken;
vestingV1 = _vestingV1;
}
function setDuelToken(address _newToken) external onlyOwner {
duelToken = _newToken;
}
function setVestingV1(address _newV1) external onlyOwner {
vestingV1 = _newV1;
}
function setWinbackStaking(address _winbackStaking) external onlyOwner {
winbackStaking = _winbackStaking;
}
function setMerkleRoot(bytes32 _newRoot) external onlyOwner {
merkleRoot = _newRoot;
}
function _getClaimable(
uint8 chainId,
uint256 unlock30d,
uint256 unlock90d,
bytes32[] calldata merkleProof
) internal returns (uint256 claimable) {
bytes32 leaf = keccak256(
abi.encodePacked(_msgSender(), chainId, unlock30d, unlock90d)
);
require(
MerkleProof.verify(merkleProof, merkleRoot, leaf),
"INCORRECT_PROOF"
);
require(chainId == block.chainid, "INCORRECT_CHAIN");
uint256 startTime = DynamicVestingV1(vestingV1).startTimes(
_msgSender()
);
if (
block.timestamp > startTime + 30 days &&
claimed[_msgSender()].length == 0
) {
claimable += unlock30d;
claimed[_msgSender()].push(true);
emit Claimed(_msgSender(), unlock30d, true);
}
if (
block.timestamp > startTime + 90 days &&
claimed[_msgSender()].length <= 1
) {
claimable += unlock90d;
claimed[_msgSender()].push(true);
emit Claimed(_msgSender(), unlock90d, false);
}
require(claimable > 0, "NOTHING_CLAIMABLE");
}
function claimDUEL(
uint8 chainId,
uint256 unlock30d,
uint256 unlock90d,
bytes32[] calldata merkleProof
) external {
uint256 claimable = _getClaimable(
chainId,
unlock30d,
unlock90d,
merkleProof
);
IERC20(duelToken).transfer(_msgSender(), claimable);
}
function winbackStake(
uint8 chainId,
uint256 unlock30d,
uint256 unlock90d,
bytes32[] calldata merkleProof,
uint32 stakeDuration
) external {
uint256 claimable = _getClaimable(
chainId,
unlock30d,
unlock90d,
merkleProof
);
IERC20(duelToken).transfer(winbackStaking, claimable);
IWinbackStaking(winbackStaking).stakeWinback(claimable, stakeDuration);
}
function withdrawDUEL(uint256 amount) external onlyOwner {
uint256 balance = IERC20(duelToken).balanceOf(address(this));
IERC20(duelToken).transfer(owner(), amount == 0 ? balance : amount);
}
}
contract SingleBeneficiaryLockup is VestingBase {
string public LOCKUP_TYPE;
address public BENEFICIARY;
uint256[] releaseTimestamps;
uint16[] releasePercentages;
uint256 vestedAllocation;
uint256 vestedClaim;
constructor(
address _duelToken,
string memory name
) VestingBase(_duelToken) {
LOCKUP_TYPE = name;
}
function updateReleaseParameters(
uint256[] memory _releaseTimestamps,
uint16[] memory _releasePercentages
) external onlyOwner {
require(
releaseTimestamps.length == releasePercentages.length,
"Parameters length mismatch"
);
releaseTimestamps = _releaseTimestamps;
releasePercentages = _releasePercentages;
}
function allocateDUEL(
address beneficiary,
uint256 amount
) external override onlyOwner {
IERC20(duelToken).transferFrom(owner(), address(this), amount);
BENEFICIARY = beneficiary;
vestedAllocation = amount;
vestedClaim = 0;
}
function reallocateDUEL(
address newBeneficiary,
uint256 amountOut,
uint256 amountIn
) external onlyOwner {
IERC20(duelToken).transfer(owner(), amountOut);
IERC20(duelToken).transferFrom(owner(), address(this), amountIn);
BENEFICIARY = newBeneficiary;
}
function claimDUEL() external override {
require(_msgSender() == BENEFICIARY, "ACCESS_FORBIDDEN");
require(
vestedAllocation > 0 && vestedAllocation > vestedClaim,
"Nothing to claim"
);
uint16 cumulativePercentage = 0;
for (uint8 i = 0; i < releaseTimestamps.length; i++) {
if (releaseTimestamps[i] > block.timestamp) {
break;
}
cumulativePercentage += releasePercentages[i];
}
uint256 availableClaim = (vestedAllocation * cumulativePercentage) /
100000;
require(availableClaim > vestedClaim, "Period already claimed");
uint256 unclaimedTokens = availableClaim - vestedClaim;
vestedClaim += unclaimedTokens;
IERC20(duelToken).transfer(_msgSender(), unclaimedTokens);
}
}
// 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);
}
}
{
"compilationTarget": {
"contracts/DUELVesting.sol": "DynamicVestingMin"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"details": {
"constantOptimizer": true,
"cse": true,
"deduplicate": true,
"inliner": true,
"jumpdestRemover": true,
"orderLiterals": true,
"peephole": true,
"yul": false
},
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_duelToken","type":"address"},{"internalType":"address","name":"_vestingV1","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"was30d","type":"bool"}],"name":"Claimed","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"},{"inputs":[{"internalType":"uint8","name":"chainId","type":"uint8"},{"internalType":"uint256","name":"unlock30d","type":"uint256"},{"internalType":"uint256","name":"unlock90d","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"claimDUEL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newToken","type":"address"}],"name":"setDuelToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_newRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newV1","type":"address"}],"name":"setVestingV1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_winbackStaking","type":"address"}],"name":"setWinbackStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"chainId","type":"uint8"},{"internalType":"uint256","name":"unlock30d","type":"uint256"},{"internalType":"uint256","name":"unlock90d","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint32","name":"stakeDuration","type":"uint32"}],"name":"winbackStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawDUEL","outputs":[],"stateMutability":"nonpayable","type":"function"}]