Skip to content

Instantly share code, notes, and snippets.

@vm06007
Last active September 13, 2024 22:58
Show Gist options
  • Save vm06007/0011819e3c445695a5be324a94d5a4c7 to your computer and use it in GitHub Desktop.
Save vm06007/0011819e3c445695a5be324a94d5a4c7 to your computer and use it in GitHub Desktop.
Bitcoin.com vesting contract for VerseToken.sol
// 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;
}
}
// 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;
}
}
// 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;
}
}
@jashanpratapsingh
Copy link

This is amazing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment