Last active
September 13, 2024 22:58
-
-
Save vm06007/0011819e3c445695a5be324a94d5a4c7 to your computer and use it in GitHub Desktop.
Bitcoin.com vesting contract for VerseToken.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: --BCOM-- | |
pragma solidity =0.8.12; | |
import "./VerseProof.sol"; | |
import "./VerseHelper.sol"; | |
contract VerseClaimer is VerseHelper { | |
bytes32 public immutable merkleRoot; | |
uint256 public immutable createTime; | |
uint256 immutable minimumTimeFrame; | |
struct KeeperInfo { | |
uint256 keeperRate; | |
uint256 keeperFrom; | |
uint256 keeperTill; | |
uint256 keeperBalance; | |
uint256 keeperPayouts; | |
} | |
mapping(address => KeeperInfo) public keeperList; | |
constructor( | |
bytes32 _merkleRoot, | |
uint256 _minimumTimeFrame, | |
address _verseTokenAddress | |
) | |
VerseHelper(_verseTokenAddress) | |
{ | |
require( | |
_minimumTimeFrame > 0, | |
"VerseClaimer: INVALID_TIMEFRAME" | |
); | |
createTime = getNow(); | |
merkleRoot = _merkleRoot; | |
minimumTimeFrame = _minimumTimeFrame; | |
} | |
function enrollRecipient( | |
address _recipient, | |
uint256 _index, | |
uint256 _tokensLocked, | |
uint256 _tokensOpened, | |
uint256 _timeFrame, | |
bytes32[] calldata _merkleProof | |
) | |
external | |
{ | |
_enrollRecipient( | |
_recipient, | |
_index, | |
_tokensLocked, | |
_tokensOpened, | |
_timeFrame, | |
_merkleProof | |
); | |
} | |
function enrollRecipientBulk( | |
address[] calldata _recipient, | |
uint256[] calldata _index, | |
uint256[] calldata _tokensLocked, | |
uint256[] calldata _tokensOpened, | |
uint256[] calldata _timeFrame, | |
bytes32[][] calldata _merkleProof | |
) | |
external | |
{ | |
for (uint256 i = 0; i < _recipient.length; i++) { | |
_enrollRecipient( | |
_recipient[i], | |
_index[i], | |
_tokensLocked[i], | |
_tokensOpened[i], | |
_timeFrame[i], | |
_merkleProof[i] | |
); | |
} | |
} | |
function _enrollRecipient( | |
address _recipient, | |
uint256 _index, | |
uint256 _tokensLocked, | |
uint256 _tokensOpened, | |
uint256 _timeFrame, | |
bytes32[] memory _merkleProof | |
) | |
private | |
{ | |
require( | |
keeperList[_recipient].keeperFrom == 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, | |
_tokensOpened, | |
_tokensLocked, | |
_timeFrame | |
); | |
} | |
function _allocateTokens( | |
address _recipient, | |
uint256 _tokensLocked, | |
uint256 _tokensOpened, | |
uint256 _timeFrame | |
) | |
private | |
{ | |
require( | |
_timeFrame >= minimumTimeFrame, | |
"VerseClaimer: INVALID_TIME_FRAME" | |
); | |
totalRequired = totalRequired | |
+ _tokensOpened | |
+ _tokensLocked; | |
_checkVerseBalance( | |
totalRequired | |
); | |
uint256 time = createTime; | |
keeperList[_recipient].keeperFrom = time; | |
keeperList[_recipient].keeperTill = time | |
+ _timeFrame; | |
keeperList[_recipient].keeperRate = _tokensLocked | |
/ _timeFrame; | |
keeperList[_recipient].keeperBalance = _tokensLocked | |
% _timeFrame | |
+ _tokensOpened; | |
emit recipientEnrolled( | |
_recipient, | |
_timeFrame, | |
_tokensLocked, | |
_tokensOpened | |
); | |
} | |
function enrollAndScrape( | |
uint256 _index, | |
uint256 _tokensLocked, | |
uint256 _tokensOpened, | |
uint256 _timeFrame, | |
bytes32[] calldata _merkleProof | |
) | |
external | |
{ | |
_enrollRecipient( | |
msg.sender, | |
_index, | |
_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 time = getNow(); | |
uint256 timePassed = | |
time < keeperList[_recipient].keeperTill ? | |
time - keeperList[_recipient].keeperFrom : _diff(_recipient); | |
balance = keeperList[_recipient].keeperRate | |
* timePassed | |
+ keeperList[_recipient].keeperBalance | |
- keeperList[_recipient].keeperPayouts; | |
} | |
function lockedBalance( | |
address _recipient | |
) | |
external | |
view | |
returns (uint256 balance) | |
{ | |
uint256 time = getNow(); | |
uint256 timeRemaining = | |
keeperList[_recipient].keeperTill > time ? | |
keeperList[_recipient].keeperTill - time : 0; | |
balance = keeperList[_recipient].keeperRate | |
* timeRemaining; | |
} | |
function _diff( | |
address _recipient | |
) | |
private | |
view | |
returns (uint256 res) | |
{ | |
res = keeperList[_recipient].keeperTill | |
- keeperList[_recipient].keeperFrom; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: --BCOM-- | |
pragma solidity =0.8.12; | |
contract VerseHelper { | |
uint256 public totalRequired; | |
address public immutable verseToken; | |
event recipientEnrolled( | |
address indexed recipient, | |
uint256 timeLock, | |
uint256 timeReward, | |
uint256 instantReward | |
); | |
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 && ( | |
data.length == 0 || 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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: --BCOM-- | |
pragma solidity =0.8.12; | |
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is amazing