编译器
0.8.17+commit.8df45f5f
文件 1 的 4:MerkleProof.sol
pragma solidity =0.8.17;
library MerkleProof {
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
)
internal
pure
returns (bool)
{
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
computedHash = computedHash <= proofElement
? keccak256(abi.encodePacked(computedHash, proofElement))
: keccak256(abi.encodePacked(proofElement, computedHash));
}
return computedHash == root;
}
}
文件 2 的 4:VerseClaimer.sol
pragma solidity =0.8.17;
import "./MerkleProof.sol";
import "./VerseHelper.sol";
contract VerseClaimer is VerseHelper {
bytes32 public immutable merkleRoot;
uint256 public immutable createTime;
uint256 immutable minimumTimeFrame;
struct KeeperInfo {
uint256 keeperRate;
uint256 keeperTill;
uint256 keeperInstant;
uint256 keeperPayouts;
}
mapping(address => KeeperInfo) public keeperList;
constructor(
bytes32 _merkleRoot,
uint256 _minimumTimeFrame,
address _verseTokenAddress
)
VerseHelper(_verseTokenAddress)
{
require(
_minimumTimeFrame > 0,
"VerseClaimer: INVALID_TIMEFRAME"
);
require(
_merkleRoot > 0,
"VerseClaimer: INVALID_MERKLE_ROOT"
);
createTime = getNow();
merkleRoot = _merkleRoot;
minimumTimeFrame = _minimumTimeFrame;
}
function enrollRecipient(
uint256 _index,
address _recipient,
uint256 _tokensLocked,
uint256 _tokensOpened,
uint256 _timeFrame,
bytes32[] calldata _merkleProof
)
external
{
_enrollRecipient(
_index,
_recipient,
_tokensLocked,
_tokensOpened,
_timeFrame,
_merkleProof
);
}
function enrollRecipientBulk(
uint256 _index,
address[] calldata _recipient,
uint256[] calldata _tokensLocked,
uint256[] calldata _tokensOpened,
uint256[] calldata _timeFrame,
bytes32[][] calldata _merkleProof
)
external
{
require(
_recipient.length < 10,
"VerseClaimer: TOO_MANY"
);
for (uint256 i = 0; i < _recipient.length; i++) {
_enrollRecipient(
_index + i,
_recipient[i],
_tokensLocked[i],
_tokensOpened[i],
_timeFrame[i],
_merkleProof[i]
);
}
}
function _enrollRecipient(
uint256 _index,
address _recipient,
uint256 _tokensLocked,
uint256 _tokensOpened,
uint256 _timeFrame,
bytes32[] memory _merkleProof
)
private
{
require(
keeperList[_recipient].keeperTill == 0,
"VerseClaimer: RECIPIENT_ALREADY_ENROLLED"
);
bytes32 node = keccak256(
abi.encodePacked(
_index,
_recipient,
_tokensLocked,
_tokensOpened,
_timeFrame
)
);
require(
MerkleProof.verify(
_merkleProof,
merkleRoot,
node
),
"VerseClaimer: INVALID_PROOF"
);
_allocateTokens(
_recipient,
_tokensLocked,
_tokensOpened,
_timeFrame
);
}
function _allocateTokens(
address _recipient,
uint256 _tokensLocked,
uint256 _tokensOpened,
uint256 _timeFrame
)
private
{
require(
_timeFrame >= minimumTimeFrame,
"VerseClaimer: INVALID_TIME_FRAME"
);
totalRequired = totalRequired
+ _tokensOpened
+ _tokensLocked;
keeperList[_recipient].keeperTill = createTime
+ _timeFrame;
keeperList[_recipient].keeperRate = _tokensLocked
/ _timeFrame;
keeperList[_recipient].keeperInstant = _tokensLocked
% _timeFrame
+ _tokensOpened;
_checkVerseBalance(
totalRequired
);
emit recipientEnrolled(
_recipient,
_timeFrame,
_tokensLocked,
_tokensOpened
);
}
function enrollAndScrape(
uint256 _index,
uint256 _tokensLocked,
uint256 _tokensOpened,
uint256 _timeFrame,
bytes32[] calldata _merkleProof
)
external
{
_enrollRecipient(
_index,
msg.sender,
_tokensLocked,
_tokensOpened,
_timeFrame,
_merkleProof
);
_scrapeTokens(
msg.sender
);
}
function scrapeMyTokens()
external
{
_scrapeTokens(
msg.sender
);
}
function _scrapeTokens(
address _recipient
)
private
{
uint256 scrapeAmount = availableBalance(
_recipient
);
keeperList[_recipient].keeperPayouts += scrapeAmount;
_safeVerseScrape(
_recipient,
scrapeAmount
);
emit tokensScraped(
_recipient,
scrapeAmount,
getNow()
);
}
function availableBalance(
address _recipient
)
public
view
returns (uint256 balance)
{
uint256 timeNow = getNow();
uint256 timeMax = keeperList[_recipient].keeperTill;
if (timeMax == 0) return 0;
uint256 timePassed = timeNow > timeMax
? timeMax - createTime
: timeNow - createTime;
balance = keeperList[_recipient].keeperRate
* timePassed
+ keeperList[_recipient].keeperInstant
- keeperList[_recipient].keeperPayouts;
}
function lockedBalance(
address _recipient
)
external
view
returns (uint256 balance)
{
uint256 timeNow = getNow();
uint256 timeRemaining =
keeperList[_recipient].keeperTill > timeNow ?
keeperList[_recipient].keeperTill - timeNow : 0;
balance = keeperList[_recipient].keeperRate
* timeRemaining;
}
}
文件 3 的 4:VerseHelper.sol
pragma solidity =0.8.17;
contract VerseHelper {
uint256 public totalRequired;
address public immutable verseToken;
event recipientEnrolled(
address indexed recipient,
uint256 timeFrame,
uint256 tokensLocked,
uint256 tokensOpened
);
event tokensScraped(
address indexed scraper,
uint256 scrapedAmount,
uint256 timestamp
);
constructor(
address _verseTokenAddress
) {
if (_verseTokenAddress == address(0x0)) {
revert("VerseHelper: INVALID_VERSE_TOKEN");
}
verseToken = _verseTokenAddress;
}
bytes4 private constant TRANSFER = bytes4(
keccak256(
bytes(
"transfer(address,uint256)"
)
)
);
bytes4 private constant BALANCEOF = bytes4(
keccak256(
bytes(
"balanceOf(address)"
)
)
);
function _safeVerseScrape(
address _to,
uint256 _scrapeAmount
)
internal
{
totalRequired -= _scrapeAmount;
(bool success, bytes memory data) = verseToken.call(
abi.encodeWithSelector(
TRANSFER,
_to,
_scrapeAmount
)
);
require(
success && (
abi.decode(
data, (bool)
)
),
"VerseHelper: TRANSFER_FAILED"
);
}
function _checkVerseBalance(
uint256 _required
)
internal
{
(bool success, bytes memory data) = verseToken.call(
abi.encodeWithSelector(
BALANCEOF,
address(this)
)
);
require(
success && abi.decode(
data, (uint256)
) >= _required,
"VerseHelper: BALANCE_CHECK_FAILED"
);
}
function getNow()
public
view
returns (uint256 time)
{
time = block.timestamp;
}
}
文件 4 的 4:VerseToken.sol
pragma solidity =0.8.17;
import "./VerseClaimer.sol";
contract VerseToken {
string public constant name = "Verse";
string public constant symbol = "VERSE";
uint8 public constant decimals = 18;
VerseClaimer public immutable claimer;
address constant ZERO_ADDRESS = address(0);
uint256 constant UINT256_MAX = type(uint256).max;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(address => uint256) public nonces;
bytes32 public immutable DOMAIN_SEPARATOR;
bytes32 public constant PERMIT_TYPEHASH = keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
constructor(
uint256 _initialSupply,
uint256 _minimumTimeFrame,
bytes32 _merkleRoot
) {
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
claimer = new VerseClaimer(
_merkleRoot,
_minimumTimeFrame,
address(this)
);
_mint(
address(claimer),
_initialSupply
);
}
function _mint(
address _to,
uint256 _value
)
internal
{
totalSupply =
totalSupply + _value;
unchecked {
balanceOf[_to] =
balanceOf[_to] + _value;
}
emit Transfer(
ZERO_ADDRESS,
_to,
_value
);
}
function burn(
uint256 _value
)
external
{
_burn(
msg.sender,
_value
);
}
function _burn(
address _from,
uint256 _value
)
internal
{
unchecked {
totalSupply =
totalSupply - _value;
}
balanceOf[_from] =
balanceOf[_from] - _value;
emit Transfer(
_from,
ZERO_ADDRESS,
_value
);
}
function _approve(
address _owner,
address _spender,
uint256 _value
)
private
{
allowance[_owner][_spender] = _value;
emit Approval(
_owner,
_spender,
_value
);
}
function _transfer(
address _from,
address _to,
uint256 _value
)
private
{
balanceOf[_from] =
balanceOf[_from] - _value;
unchecked {
balanceOf[_to] =
balanceOf[_to] + _value;
}
emit Transfer(
_from,
_to,
_value
);
}
function approve(
address _spender,
uint256 _value
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
_value
);
return true;
}
function increaseAllowance(
address _spender,
uint256 _addedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
allowance[msg.sender][_spender] + _addedValue
);
return true;
}
function decreaseAllowance(
address _spender,
uint256 _subtractedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
allowance[msg.sender][_spender] - _subtractedValue
);
return true;
}
function transfer(
address _to,
uint256 _value
)
external
returns (bool)
{
_transfer(
msg.sender,
_to,
_value
);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _value
)
external
returns (bool)
{
if (allowance[_from][msg.sender] != UINT256_MAX) {
allowance[_from][msg.sender] -= _value;
}
_transfer(
_from,
_to,
_value
);
return true;
}
function permit(
address _owner,
address _spender,
uint256 _value,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
)
external
{
require(
_deadline >= block.timestamp,
"VerseToken: PERMIT_CALL_EXPIRED"
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
PERMIT_TYPEHASH,
_owner,
_spender,
_value,
nonces[_owner]++,
_deadline
)
)
)
);
if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("VerseToken: INVALID_SIGNATURE");
}
address recoveredAddress = ecrecover(
digest,
_v,
_r,
_s
);
require(
recoveredAddress != ZERO_ADDRESS &&
recoveredAddress == _owner,
"VerseToken: INVALID_SIGNATURE"
);
_approve(
_owner,
_spender,
_value
);
}
}
{
"compilationTarget": {
"VerseToken.sol": "VerseToken"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"uint256","name":"_initialSupply","type":"uint256"},{"internalType":"uint256","name":"_minimumTimeFrame","type":"uint256"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimer","outputs":[{"internalType":"contract VerseClaimer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"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":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]