ERC20
This contract is an ERC20 token.
Name
HEX
Symbol
HEX
Decimals
8
Total Supply
571,716,973,536 HEX
About link description
HEX (HEX) is a cryptocurrency and operates on the Ethereum platform. HEX has a current supply of 633,542,658,973 with 173,411,074,413.06497 in circulation. The last known price of HEX is 0.17450672 USD and is up 8.61 over the last 24 hours. It is currently trading on 39 active market(s) with $143,598,330.96 traded over the last 24 hours. More information can be found at https://hex.com/.
Stats
Public Functions
31
Event Types
11
Code Size
105,703 bytes
Events (11) keyboard_arrow_up
Constants (67) keyboard_arrow_up
MERKLE_TREE_ROOT Constant
bytes32 help
0x4e831acb4223b66de3b3d2e54a2edeefb0de3d7916e2886a4b134d9764d41bec
State Variables (9) keyboard_arrow_up
xfLobbyMembers Variable
mapping(uint256 => mapping(address => XfLobbyQueueStore)) help
Internal Variable
Functions
totalSupply keyboard_arrow_up
balanceOf keyboard_arrow_up
transfer keyboard_arrow_up
Requirements help
Source Code
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
allowance keyboard_arrow_up
approve keyboard_arrow_up
Requirements help
Source Code
function approve(address spender, uint256 amount) public returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
transferFrom keyboard_arrow_up
Requirements help
Source Code
function transferFrom(
address sender,
address recipient,
uint256 amount
) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(
sender,
_msgSender(),
_allowances[sender][_msgSender()].sub(
amount,
"ERC20: transfer amount exceeds allowance"
)
);
return true;
}
increaseAllowance keyboard_arrow_up
Requirements help
Source Code
function increaseAllowance(address spender, uint256 addedValue)
public
returns (bool)
{
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].add(addedValue)
);
return true;
}
decreaseAllowance keyboard_arrow_up
Requirements help
Source Code
function decreaseAllowance(address spender, uint256 subtractedValue)
public
returns (bool)
{
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(
subtractedValue,
"ERC20: decreased allowance below zero"
)
);
return true;
}
dailyDataUpdate keyboard_arrow_up
Requirements help
Source Code
function dailyDataUpdate(uint256 beforeDay) external {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
/* Skip pre-claim period */
require(g._currentDay > CLAIM_PHASE_START_DAY, "HEX: Too early");
if (beforeDay != 0) {
require(
beforeDay <= g._currentDay,
"HEX: beforeDay cannot be in the future"
);
_dailyDataUpdate(g, beforeDay, false);
} else {
/* Default to updating before current day */
_dailyDataUpdate(g, g._currentDay, false);
}
_globalsSync(g, gSnapshot);
}
dailyDataRange keyboard_arrow_up
Requirements help
Source Code
function dailyDataRange(uint256 beginDay, uint256 endDay)
external
view
returns (uint256[] memory list)
{
require(
beginDay < endDay && endDay <= globals.dailyDataCount,
"HEX: range invalid"
);
list = new uint256[](endDay - beginDay);
uint256 src = beginDay;
uint256 dst = 0;
uint256 v;
do {
v =
uint256(dailyData[src].dayUnclaimedSatoshisTotal) <<
(HEART_UINT_SIZE * 2);
v |= uint256(dailyData[src].dayStakeSharesTotal) << HEART_UINT_SIZE;
v |= uint256(dailyData[src].dayPayoutTotal);
list[dst++] = v;
} while (++src < endDay);
return list;
}
globalInfo keyboard_arrow_up
Parameters help
This function has no parameters.
Source Code
function globalInfo() external view returns (uint256[13] memory) {
uint256 _claimedBtcAddrCount;
uint256 _claimedSatoshisTotal;
uint256 _unclaimedSatoshisTotal;
(
_claimedBtcAddrCount,
_claimedSatoshisTotal,
_unclaimedSatoshisTotal
) = _claimStatsDecode(globals.claimStats);
return [
// 1
globals.lockedHeartsTotal,
globals.nextStakeSharesTotal,
globals.shareRate,
globals.stakePenaltyTotal,
// 2
globals.dailyDataCount,
globals.stakeSharesTotal,
globals.latestStakeId,
_unclaimedSatoshisTotal,
_claimedSatoshisTotal,
_claimedBtcAddrCount,
//
block.timestamp,
totalSupply(),
xfLobby[_currentDay()]
];
}
allocatedSupply keyboard_arrow_up
currentDay keyboard_arrow_up
stakeStart keyboard_arrow_up
Requirements help
Source Code
function stakeStart(uint256 newStakedHearts, uint256 newStakedDays) external {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
/* Enforce the minimum stake time */
require(
newStakedDays >= MIN_STAKE_DAYS,
"HEX: newStakedDays lower than minimum"
);
/* Check if log data needs to be updated */
_dailyDataUpdateAuto(g);
_stakeStart(g, newStakedHearts, newStakedDays, false);
/* Remove staked Hearts from balance of staker */
_burn(msg.sender, newStakedHearts);
_globalsSync(g, gSnapshot);
}
stakeGoodAccounting keyboard_arrow_up
Requirements help
Source Code
function stakeGoodAccounting(
address stakerAddr,
uint256 stakeIndex,
uint40 stakeIdParam
) external {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
/* require() is more informative than the default assert() */
require(stakeLists[stakerAddr].length != 0, "HEX: Empty stake list");
require(
stakeIndex < stakeLists[stakerAddr].length,
"HEX: stakeIndex invalid"
);
StakeStore storage stRef = stakeLists[stakerAddr][stakeIndex];
/* Get stake copy */
StakeCache memory st;
_stakeLoad(stRef, stakeIdParam, st);
/* Stake must have served full term */
require(
g._currentDay >= st._lockedDay + st._stakedDays,
"HEX: Stake not fully served"
);
/* Stake must still be locked */
require(st._unlockedDay == 0, "HEX: Stake already unlocked");
/* Check if log data needs to be updated */
_dailyDataUpdateAuto(g);
/* Unlock the completed stake */
_stakeUnlock(g, st);
/* stakeReturn value is unused here */
(
,
uint256 payout,
uint256 penalty,
uint256 cappedPenalty
) = _stakePerformance(g, st, st._stakedDays);
_emitStakeGoodAccounting(
stakerAddr,
stakeIdParam,
st._stakedHearts,
st._stakeShares,
payout,
penalty
);
if (cappedPenalty != 0) {
_splitPenaltyProceeds(g, cappedPenalty);
}
/* st._unlockedDay has changed */
_stakeUpdate(stRef, st);
_globalsSync(g, gSnapshot);
}
stakeEnd keyboard_arrow_up
Requirements help
Source Code
function stakeEnd(uint256 stakeIndex, uint40 stakeIdParam) external {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
StakeStore[] storage stakeListRef = stakeLists[msg.sender];
/* require() is more informative than the default assert() */
require(stakeListRef.length != 0, "HEX: Empty stake list");
require(stakeIndex < stakeListRef.length, "HEX: stakeIndex invalid");
/* Get stake copy */
StakeCache memory st;
_stakeLoad(stakeListRef[stakeIndex], stakeIdParam, st);
/* Check if log data needs to be updated */
_dailyDataUpdateAuto(g);
uint256 servedDays = 0;
bool prevUnlocked = (st._unlockedDay != 0);
uint256 stakeReturn;
uint256 payout = 0;
uint256 penalty = 0;
uint256 cappedPenalty = 0;
if (g._currentDay >= st._lockedDay) {
if (prevUnlocked) {
/* Previously unlocked in stakeGoodAccounting(), so must have served full term */
servedDays = st._stakedDays;
} else {
_stakeUnlock(g, st);
servedDays = g._currentDay - st._lockedDay;
if (servedDays > st._stakedDays) {
servedDays = st._stakedDays;
} else {
/* Deny early-unstake before an auto-stake minimum has been served */
if (servedDays < MIN_AUTO_STAKE_DAYS) {
require(!st._isAutoStake, "HEX: Auto-stake still locked");
}
}
}
(stakeReturn, payout, penalty, cappedPenalty) = _stakePerformance(
g,
st,
servedDays
);
} else {
/* Deny early-unstake before an auto-stake minimum has been served */
require(!st._isAutoStake, "HEX: Auto-stake still locked");
/* Stake hasn't been added to the total yet, so no penalties or rewards apply */
g._nextStakeSharesTotal -= st._stakeShares;
stakeReturn = st._stakedHearts;
}
_emitStakeEnd(
stakeIdParam,
st._stakedHearts,
st._stakeShares,
payout,
penalty,
servedDays,
prevUnlocked
);
if (cappedPenalty != 0 && !prevUnlocked) {
/* Split penalty proceeds only if not previously unlocked by stakeGoodAccounting() */
_splitPenaltyProceeds(g, cappedPenalty);
}
/* Pay the stake return, if any, to the staker */
if (stakeReturn != 0) {
_mint(msg.sender, stakeReturn);
/* Update the share rate if necessary */
_shareRateUpdate(g, st, stakeReturn);
}
g._lockedHeartsTotal -= st._stakedHearts;
_stakeRemove(stakeListRef, stakeIndex);
_globalsSync(g, gSnapshot);
}
stakeCount keyboard_arrow_up
btcAddressIsClaimable keyboard_arrow_up
Requirements help
UNKNOWN VALUE
must be equal to
0
Source Code
function btcAddressIsClaimable(
bytes20 btcAddr,
uint256 rawSatoshis,
bytes32[] calldata proof
) external view returns (bool) {
uint256 day = _currentDay();
require(day >= CLAIM_PHASE_START_DAY, "HEX: Claim phase has not yet started");
require(day < CLAIM_PHASE_END_DAY, "HEX: Claim phase has ended");
/* Don't need to check Merkle proof if UTXO BTC address has already been claimed */
if (btcAddressClaims[btcAddr]) {
return false;
}
/* Verify the Merkle tree proof */
return _btcAddressIsValid(btcAddr, rawSatoshis, proof);
}
btcAddressIsValid keyboard_arrow_up
Requirements help
UNKNOWN VALUE
must be equal to
0
Source Code
function btcAddressIsValid(
bytes20 btcAddr,
uint256 rawSatoshis,
bytes32[] calldata proof
) external pure returns (bool) {
return _btcAddressIsValid(btcAddr, rawSatoshis, proof);
}
merkleProofIsValid keyboard_arrow_up
claimMessageMatchesSignature keyboard_arrow_up
Parameters help
Source Code
function claimMessageMatchesSignature(
address claimToAddr,
bytes32 claimParamHash,
bytes32 pubKeyX,
bytes32 pubKeyY,
uint8 claimFlags,
uint8 v,
bytes32 r,
bytes32 s
) public pure returns (bool) {
require(v >= 27 && v <= 30, "HEX: v invalid");
/*
ecrecover() returns an Eth address rather than a public key, so
we must do the same to compare.
*/
address pubKeyEthAddr = pubKeyToEthAddress(pubKeyX, pubKeyY);
/* Create and hash the claim message text */
bytes32 messageHash = _hash256(
_claimMessageCreate(claimToAddr, claimParamHash, claimFlags)
);
/* Verify the public key */
return ecrecover(messageHash, v, r, s) == pubKeyEthAddr;
}
pubKeyToEthAddress keyboard_arrow_up
Source Code
function pubKeyToEthAddress(bytes32 pubKeyX, bytes32 pubKeyY)
public
pure
returns (address)
{
return
address(uint160(uint256(keccak256(abi.encodePacked(pubKeyX, pubKeyY)))));
}
pubKeyToBtcAddress keyboard_arrow_up
Source Code
function pubKeyToBtcAddress(
bytes32 pubKeyX,
bytes32 pubKeyY,
uint8 claimFlags
) public pure returns (bytes20) {
/*
Helpful references:
- https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
- https://github.com/cryptocoinjs/ecurve/blob/master/lib/point.js
*/
uint8 startingByte;
bytes memory pubKey;
bool compressed = (claimFlags & CLAIM_FLAG_BTC_ADDR_COMPRESSED) != 0;
bool nested = (claimFlags & CLAIM_FLAG_BTC_ADDR_P2WPKH_IN_P2SH) != 0;
bool bech32 = (claimFlags & CLAIM_FLAG_BTC_ADDR_BECH32) != 0;
if (compressed) {
/* Compressed public key format */
require(!(nested && bech32), "HEX: claimFlags invalid");
startingByte = (pubKeyY[31] & 0x01) == 0 ? 0x02 : 0x03;
pubKey = abi.encodePacked(startingByte, pubKeyX);
} else {
/* Uncompressed public key format */
require(!nested && !bech32, "HEX: claimFlags invalid");
startingByte = 0x04;
pubKey = abi.encodePacked(startingByte, pubKeyX, pubKeyY);
}
bytes20 pubKeyHash = _hash160(pubKey);
if (nested) {
return _hash160(abi.encodePacked(hex"0014", pubKeyHash));
}
return pubKeyHash;
}
btcAddressClaim keyboard_arrow_up
Parameters help
Requirements help
null
Source Code
function btcAddressClaim(
uint256 rawSatoshis,
bytes32[] calldata proof,
address claimToAddr,
bytes32 pubKeyX,
bytes32 pubKeyY,
uint8 claimFlags,
uint8 v,
bytes32 r,
bytes32 s,
uint256 autoStakeDays,
address referrerAddr
) external returns (uint256) {
/* Sanity check */
require(
rawSatoshis <= MAX_BTC_ADDR_BALANCE_SATOSHIS,
"HEX: CHK: rawSatoshis"
);
/* Enforce the minimum stake time for the auto-stake from this claim */
require(
autoStakeDays >= MIN_AUTO_STAKE_DAYS,
"HEX: autoStakeDays lower than minimum"
);
/* Ensure signature matches the claim message containing the Eth address and claimParamHash */
{
bytes32 claimParamHash = 0;
if (claimToAddr != msg.sender) {
/* Claimer did not send this, so claim params must be signed */
claimParamHash = keccak256(
abi.encodePacked(MERKLE_TREE_ROOT, autoStakeDays, referrerAddr)
);
}
require(
claimMessageMatchesSignature(
claimToAddr,
claimParamHash,
pubKeyX,
pubKeyY,
claimFlags,
v,
r,
s
),
"HEX: Signature mismatch"
);
}
/* Derive BTC address from public key */
bytes20 btcAddr = pubKeyToBtcAddress(pubKeyX, pubKeyY, claimFlags);
/* Ensure BTC address has not yet been claimed */
require(
!btcAddressClaims[btcAddr],
"HEX: BTC address balance already claimed"
);
/* Ensure BTC address is part of the Merkle tree */
require(
_btcAddressIsValid(btcAddr, rawSatoshis, proof),
"HEX: BTC address or balance unknown"
);
/* Mark BTC address as claimed */
btcAddressClaims[btcAddr] = true;
return
_satoshisClaimSync(
rawSatoshis,
claimToAddr,
btcAddr,
claimFlags,
autoStakeDays,
referrerAddr
);
}
xfLobbyEnter keyboard_arrow_up
Requirements help
Source Code
function xfLobbyEnter(address referrerAddr) external payable {
uint256 enterDay = _currentDay();
require(enterDay < CLAIM_PHASE_END_DAY, "HEX: Lobbies have ended");
uint256 rawAmount = msg.value;
require(rawAmount != 0, "HEX: Amount required");
XfLobbyQueueStore storage qRef = xfLobbyMembers[enterDay][msg.sender];
uint256 entryIndex = qRef.tailIndex++;
qRef.entries[entryIndex] = XfLobbyEntryStore(uint96(rawAmount), referrerAddr);
xfLobby[enterDay] += rawAmount;
_emitXfLobbyEnter(enterDay, entryIndex, rawAmount, referrerAddr);
}
xfLobbyExit keyboard_arrow_up
Requirements help
Source Code
function xfLobbyExit(uint256 enterDay, uint256 count) external {
require(enterDay < _currentDay(), "HEX: Round is not complete");
XfLobbyQueueStore storage qRef = xfLobbyMembers[enterDay][msg.sender];
uint256 headIndex = qRef.headIndex;
uint256 endIndex;
if (count != 0) {
require(count <= qRef.tailIndex - headIndex, "HEX: count invalid");
endIndex = headIndex + count;
} else {
endIndex = qRef.tailIndex;
require(headIndex < endIndex, "HEX: count invalid");
}
uint256 waasLobby = _waasLobby(enterDay);
uint256 _xfLobby = xfLobby[enterDay];
uint256 totalXfAmount = 0;
uint256 originBonusHearts = 0;
do {
uint256 rawAmount = qRef.entries[headIndex].rawAmount;
address referrerAddr = qRef.entries[headIndex].referrerAddr;
delete qRef.entries[headIndex];
uint256 xfAmount = (waasLobby * rawAmount) / _xfLobby;
if (referrerAddr == address(0)) {
/* No referrer */
_emitXfLobbyExit(enterDay, headIndex, xfAmount, referrerAddr);
} else {
/* Referral bonus of 10% of xfAmount to member */
uint256 referralBonusHearts = xfAmount / 10;
xfAmount += referralBonusHearts;
/* Then a cumulative referrer bonus of 20% to referrer */
uint256 referrerBonusHearts = xfAmount / 5;
if (referrerAddr == msg.sender) {
/* Self-referred */
xfAmount += referrerBonusHearts;
_emitXfLobbyExit(enterDay, headIndex, xfAmount, referrerAddr);
} else {
/* Referred by different address */
_emitXfLobbyExit(enterDay, headIndex, xfAmount, referrerAddr);
_mint(referrerAddr, referrerBonusHearts);
}
originBonusHearts += referralBonusHearts + referrerBonusHearts;
}
totalXfAmount += xfAmount;
} while (++headIndex < endIndex);
qRef.headIndex = uint40(headIndex);
if (originBonusHearts != 0) {
_mint(ORIGIN_ADDR, originBonusHearts);
}
if (totalXfAmount != 0) {
_mint(msg.sender, totalXfAmount);
}
}
xfLobbyFlush keyboard_arrow_up
xfLobbyRange keyboard_arrow_up
Requirements help
Source Code
function xfLobbyRange(uint256 beginDay, uint256 endDay)
external
view
returns (uint256[] memory list)
{
require(
beginDay < endDay &&
endDay <= CLAIM_PHASE_END_DAY &&
endDay <= _currentDay(),
"HEX: invalid range"
);
list = new uint256[](endDay - beginDay);
uint256 src = beginDay;
uint256 dst = 0;
do {
list[dst++] = uint256(xfLobby[src++]);
} while (src < endDay);
return list;
}
xfLobbyEntry keyboard_arrow_up
Requirements help
Source Code
function xfLobbyEntry(address memberAddr, uint256 entryId)
external
view
returns (uint256 rawAmount, address referrerAddr)
{
uint256 enterDay = entryId >> XF_LOBBY_ENTRY_INDEX_SIZE;
uint256 entryIndex = entryId & XF_LOBBY_ENTRY_INDEX_MASK;
XfLobbyEntryStore storage entry = xfLobbyMembers[enterDay][memberAddr]
.entries[entryIndex];
require(entry.rawAmount != 0, "HEX: Param invalid");
return (entry.rawAmount, entry.referrerAddr);
}
xfLobbyPendingDays keyboard_arrow_up
Source Code
function xfLobbyPendingDays(address memberAddr)
external
view
returns (uint256[XF_LOBBY_DAY_WORDS] memory words)
{
uint256 day = _currentDay() + 1;
if (day > CLAIM_PHASE_END_DAY) {
day = CLAIM_PHASE_END_DAY;
}
while (day-- != 0) {
if (
xfLobbyMembers[day][memberAddr].tailIndex >
xfLobbyMembers[day][memberAddr].headIndex
) {
words[day >> 8] |= 1 << (day & 255);
}
}
return words;
}
Internal Functions
Internal functions are parts of the contract that can't be used directly, but instead are used by the public functions listed above.
internal TransformableToken._waasLobby keyboard_arrow_up
Source Code
function _waasLobby(uint256 enterDay) private returns (uint256 waasLobby) {
if (enterDay >= CLAIM_PHASE_START_DAY) {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
_dailyDataUpdateAuto(g);
uint256 unclaimed = dailyData[enterDay].dayUnclaimedSatoshisTotal;
waasLobby = (unclaimed * HEARTS_PER_SATOSHI) / CLAIM_PHASE_DAYS;
_globalsSync(g, gSnapshot);
} else {
waasLobby = WAAS_LOBBY_SEED_HEARTS;
}
return waasLobby;
}
internal TransformableToken._emitXfLobbyEnter keyboard_arrow_up
Parameters help
Source Code
function _emitXfLobbyEnter(
uint256 enterDay,
uint256 entryIndex,
uint256 rawAmount,
address referrerAddr
) private {
emit XfLobbyEnter( // (auto-generated event)
uint256(uint40(block.timestamp)) | (uint256(uint96(rawAmount)) << 40),
msg.sender,
(enterDay << XF_LOBBY_ENTRY_INDEX_SIZE) | entryIndex,
referrerAddr
);
}
internal TransformableToken._emitXfLobbyExit keyboard_arrow_up
Parameters help
Source Code
function _emitXfLobbyExit(
uint256 enterDay,
uint256 entryIndex,
uint256 xfAmount,
address referrerAddr
) private {
emit XfLobbyExit( // (auto-generated event)
uint256(uint40(block.timestamp)) | (uint256(uint72(xfAmount)) << 40),
msg.sender,
(enterDay << XF_LOBBY_ENTRY_INDEX_SIZE) | entryIndex,
referrerAddr
);
}
internal UTXORedeemableToken._satoshisClaimSync keyboard_arrow_up
Parameters help
Source Code
function _satoshisClaimSync(
uint256 rawSatoshis,
address claimToAddr,
bytes20 btcAddr,
uint8 claimFlags,
uint256 autoStakeDays,
address referrerAddr
) private returns (uint256 totalClaimedHearts) {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
totalClaimedHearts = _satoshisClaim(
g,
rawSatoshis,
claimToAddr,
btcAddr,
claimFlags,
autoStakeDays,
referrerAddr
);
_globalsSync(g, gSnapshot);
return totalClaimedHearts;
}
internal UTXORedeemableToken._satoshisClaim keyboard_arrow_up
Parameters help
Requirements help
Source Code
function _satoshisClaim(
GlobalsCache memory g,
uint256 rawSatoshis,
address claimToAddr,
bytes20 btcAddr,
uint8 claimFlags,
uint256 autoStakeDays,
address referrerAddr
) private returns (uint256 totalClaimedHearts) {
/* Allowed only during the claim phase */
require(
g._currentDay >= CLAIM_PHASE_START_DAY,
"HEX: Claim phase has not yet started"
);
require(g._currentDay < CLAIM_PHASE_END_DAY, "HEX: Claim phase has ended");
/* Check if log data needs to be updated */
_dailyDataUpdateAuto(g);
/* Sanity check */
require(
g._claimedBtcAddrCount < CLAIMABLE_BTC_ADDR_COUNT,
"HEX: CHK: _claimedBtcAddrCount"
);
(
uint256 adjSatoshis,
uint256 claimedHearts,
uint256 claimBonusHearts
) = _calcClaimValues(g, rawSatoshis);
/* Increment claim count to track viral rewards */
g._claimedBtcAddrCount++;
totalClaimedHearts = _remitBonuses(
claimToAddr,
btcAddr,
claimFlags,
rawSatoshis,
adjSatoshis,
claimedHearts,
claimBonusHearts,
referrerAddr
);
/* Auto-stake a percentage of the successful claim */
uint256 autoStakeHearts = (totalClaimedHearts * AUTO_STAKE_CLAIM_PERCENT) /
100;
_stakeStart(g, autoStakeHearts, autoStakeDays, true);
/* Mint remaining claimed Hearts to claim address */
_mint(claimToAddr, totalClaimedHearts - autoStakeHearts);
return totalClaimedHearts;
}
internal UTXORedeemableToken._remitBonuses keyboard_arrow_up
Parameters help
Requirements help
Source Code
function _remitBonuses(
address claimToAddr,
bytes20 btcAddr,
uint8 claimFlags,
uint256 rawSatoshis,
uint256 adjSatoshis,
uint256 claimedHearts,
uint256 claimBonusHearts,
address referrerAddr
) private returns (uint256 totalClaimedHearts) {
totalClaimedHearts = claimedHearts + claimBonusHearts;
uint256 originBonusHearts = claimBonusHearts;
if (referrerAddr == address(0)) {
/* No referrer */
_emitClaim(
claimToAddr,
btcAddr,
claimFlags,
rawSatoshis,
adjSatoshis,
totalClaimedHearts,
referrerAddr
);
} else {
/* Referral bonus of 10% of total claimed Hearts to claimer */
uint256 referralBonusHearts = totalClaimedHearts / 10;
totalClaimedHearts += referralBonusHearts;
/* Then a cumulative referrer bonus of 20% to referrer */
uint256 referrerBonusHearts = totalClaimedHearts / 5;
originBonusHearts += referralBonusHearts + referrerBonusHearts;
if (referrerAddr == claimToAddr) {
/* Self-referred */
totalClaimedHearts += referrerBonusHearts;
_emitClaim(
claimToAddr,
btcAddr,
claimFlags,
rawSatoshis,
adjSatoshis,
totalClaimedHearts,
referrerAddr
);
} else {
/* Referred by different address */
_emitClaim(
claimToAddr,
btcAddr,
claimFlags,
rawSatoshis,
adjSatoshis,
totalClaimedHearts,
referrerAddr
);
_mint(referrerAddr, referrerBonusHearts);
}
}
_mint(ORIGIN_ADDR, originBonusHearts);
return totalClaimedHearts;
}
internal UTXORedeemableToken._emitClaim keyboard_arrow_up
Parameters help
Source Code
function _emitClaim(
address claimToAddr,
bytes20 btcAddr,
uint8 claimFlags,
uint256 rawSatoshis,
uint256 adjSatoshis,
uint256 claimedHearts,
address referrerAddr
) private {
emit Claim( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint56(rawSatoshis)) << 40) |
(uint256(uint56(adjSatoshis)) << 96) |
(uint256(claimFlags) << 152) |
(uint256(uint72(claimedHearts)) << 160),
uint256(uint160(msg.sender)),
btcAddr,
claimToAddr,
referrerAddr
);
if (claimToAddr == msg.sender) {
return;
}
emit ClaimAssist( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint160(btcAddr)) << 40) |
(uint256(uint56(rawSatoshis)) << 200),
uint256(uint56(adjSatoshis)) |
(uint256(uint160(claimToAddr)) << 56) |
(uint256(claimFlags) << 216),
uint256(uint72(claimedHearts)) | (uint256(uint160(referrerAddr)) << 72),
msg.sender
);
}
internal UTXORedeemableToken._calcClaimValues keyboard_arrow_up
Requirements help
Source Code
function _calcClaimValues(GlobalsCache memory g, uint256 rawSatoshis)
private
pure
returns (
uint256 adjSatoshis,
uint256 claimedHearts,
uint256 claimBonusHearts
)
{
/* Apply Silly Whale reduction */
adjSatoshis = _adjustSillyWhale(rawSatoshis);
require(
g._claimedSatoshisTotal + adjSatoshis <= CLAIMABLE_SATOSHIS_TOTAL,
"HEX: CHK: _claimedSatoshisTotal"
);
g._claimedSatoshisTotal += adjSatoshis;
uint256 daysRemaining = CLAIM_PHASE_END_DAY - g._currentDay;
/* Apply late-claim reduction */
adjSatoshis = _adjustLateClaim(adjSatoshis, daysRemaining);
g._unclaimedSatoshisTotal -= adjSatoshis;
/* Convert to Hearts and calculate speed bonus */
claimedHearts = adjSatoshis * HEARTS_PER_SATOSHI;
claimBonusHearts = _calcSpeedBonus(claimedHearts, daysRemaining);
return (adjSatoshis, claimedHearts, claimBonusHearts);
}
internal UTXORedeemableToken._adjustSillyWhale keyboard_arrow_up
Source Code
function _adjustSillyWhale(uint256 rawSatoshis) private pure returns (uint256) {
if (rawSatoshis < 1000e8) {
/* For < 1,000 BTC: no penalty */
return rawSatoshis;
}
if (rawSatoshis >= 10000e8) {
/* For >= 10,000 BTC: penalty is 75%, leaving 25% */
return rawSatoshis / 4;
}
/*
For 1,000 <= BTC < 10,000: penalty scales linearly from 50% to 75%
penaltyPercent = (btc - 1000) / (10000 - 1000) * (75 - 50) + 50
= (btc - 1000) / 9000 * 25 + 50
= (btc - 1000) / 360 + 50
appliedPercent = 100 - penaltyPercent
= 100 - ((btc - 1000) / 360 + 50)
= 100 - (btc - 1000) / 360 - 50
= 50 - (btc - 1000) / 360
= (18000 - (btc - 1000)) / 360
= (18000 - btc + 1000) / 360
= (19000 - btc) / 360
adjustedBtc = btc * appliedPercent / 100
= btc * ((19000 - btc) / 360) / 100
= btc * (19000 - btc) / 36000
adjustedSat = 1e8 * adjustedBtc
= 1e8 * (btc * (19000 - btc) / 36000)
= 1e8 * ((sat / 1e8) * (19000 - (sat / 1e8)) / 36000)
= 1e8 * (sat / 1e8) * (19000 - (sat / 1e8)) / 36000
= (sat / 1e8) * 1e8 * (19000 - (sat / 1e8)) / 36000
= (sat / 1e8) * (19000e8 - sat) / 36000
= sat * (19000e8 - sat) / 36000e8
*/
return (rawSatoshis * (19000e8 - rawSatoshis)) / 36000e8;
}
internal UTXORedeemableToken._adjustLateClaim keyboard_arrow_up
Source Code
function _adjustLateClaim(uint256 adjSatoshis, uint256 daysRemaining)
private
pure
returns (uint256)
{
/*
Only valid from CLAIM_PHASE_DAYS to 1, and only used during that time.
adjustedSat = sat * (daysRemaining / CLAIM_PHASE_DAYS) * 100%
= sat * daysRemaining / CLAIM_PHASE_DAYS
*/
return (adjSatoshis * daysRemaining) / CLAIM_PHASE_DAYS;
}
internal UTXORedeemableToken._calcSpeedBonus keyboard_arrow_up
Source Code
function _calcSpeedBonus(uint256 claimedHearts, uint256 daysRemaining)
private
pure
returns (uint256)
{
/*
Only valid from CLAIM_PHASE_DAYS to 1, and only used during that time.
Speed bonus is 20% ... 0% inclusive.
bonusHearts = claimedHearts * ((daysRemaining - 1) / (CLAIM_PHASE_DAYS - 1)) * 20%
= claimedHearts * ((daysRemaining - 1) / (CLAIM_PHASE_DAYS - 1)) * 20/100
= claimedHearts * ((daysRemaining - 1) / (CLAIM_PHASE_DAYS - 1)) / 5
= claimedHearts * (daysRemaining - 1) / ((CLAIM_PHASE_DAYS - 1) * 5)
*/
return (claimedHearts * (daysRemaining - 1)) / ((CLAIM_PHASE_DAYS - 1) * 5);
}
internal UTXOClaimValidation._btcAddressIsValid keyboard_arrow_up
Requirements help
UNKNOWN VALUE
must be equal to
0
Source Code
function _btcAddressIsValid(
bytes20 btcAddr,
uint256 rawSatoshis,
bytes32[] memory proof
) internal pure returns (bool) {
/*
Ensure the proof does not attempt to treat a Merkle leaf as if it were an
internal Merkle tree node. A leaf will always have the zero-fill. An
internal node will never have the zero-fill, as guaranteed by HEX's Merkle
tree construction.
The first element, proof[0], will always be a leaf because it is the pair
of the leaf being validated. The rest of the elements, proof[1..length-1],
must be internal nodes.
The number of leaves (CLAIMABLE_BTC_ADDR_COUNT) is even, as guaranteed by
HEX's Merkle tree construction, which eliminates the only edge-case where
this validation would not apply.
*/
require(
(uint256(proof[0]) & MERKLE_LEAF_FILL_MASK) == 0,
"HEX: proof invalid"
);
for (uint256 i = 1; i < proof.length; i++) {
require(
(uint256(proof[i]) & MERKLE_LEAF_FILL_MASK) != 0,
"HEX: proof invalid"
);
}
/*
Calculate the 32 byte Merkle leaf associated with this BTC address and balance
160 bits: BTC address
52 bits: Zero-fill
45 bits: Satoshis (limited by MAX_BTC_ADDR_BALANCE_SATOSHIS)
*/
bytes32 merkleLeaf = bytes32(btcAddr) | bytes32(rawSatoshis);
/* Verify the Merkle tree proof */
return _merkleProofIsValid(merkleLeaf, proof);
}
internal UTXOClaimValidation._merkleProofIsValid keyboard_arrow_up
Source Code
function _merkleProofIsValid(bytes32 merkleLeaf, bytes32[] memory proof)
private
pure
returns (bool)
{
return MerkleProof.verify(proof, MERKLE_TREE_ROOT, merkleLeaf);
}
internal UTXOClaimValidation._claimMessageCreate keyboard_arrow_up
Parameters help
Source Code
function _claimMessageCreate(
address claimToAddr,
bytes32 claimParamHash,
uint8 claimFlags
) private pure returns (bytes memory) {
bytes memory prefixStr = (claimFlags & CLAIM_FLAG_MSG_PREFIX_OLD) != 0
? OLD_CLAIM_PREFIX_STR
: STD_CLAIM_PREFIX_STR;
bool includeAddrChecksum = (claimFlags & CLAIM_FLAG_ETH_ADDR_LOWERCASE) == 0;
bytes memory addrStr = _addressStringCreate(claimToAddr, includeAddrChecksum);
if (claimParamHash == 0) {
return
abi.encodePacked(
BITCOIN_SIG_PREFIX_LEN,
BITCOIN_SIG_PREFIX_STR,
uint8(prefixStr.length) + ETH_ADDRESS_HEX_LEN,
prefixStr,
addrStr
);
}
bytes memory claimParamHashStr = new bytes(CLAIM_PARAM_HASH_HEX_LEN);
_hexStringFromData(
claimParamHashStr,
claimParamHash,
CLAIM_PARAM_HASH_BYTE_LEN
);
return
abi.encodePacked(
BITCOIN_SIG_PREFIX_LEN,
BITCOIN_SIG_PREFIX_STR,
uint8(prefixStr.length) +
ETH_ADDRESS_HEX_LEN +
1 +
CLAIM_PARAM_HASH_HEX_LEN,
prefixStr,
addrStr,
"_",
claimParamHashStr
);
}
internal UTXOClaimValidation._addressStringCreate keyboard_arrow_up
Source Code
function _addressStringCreate(address addr, bool includeAddrChecksum)
private
pure
returns (bytes memory addrStr)
{
addrStr = new bytes(ETH_ADDRESS_HEX_LEN);
_hexStringFromData(addrStr, bytes32(bytes20(addr)), ETH_ADDRESS_BYTE_LEN);
if (includeAddrChecksum) {
bytes32 addrStrHash = keccak256(addrStr);
uint256 offset = 0;
for (uint256 i = 0; i < ETH_ADDRESS_BYTE_LEN; i++) {
uint8 b = uint8(addrStrHash[i]);
_addressStringChecksumChar(addrStr, offset++, b >> 4);
_addressStringChecksumChar(addrStr, offset++, b & 0x0f);
}
}
return addrStr;
}
internal UTXOClaimValidation._addressStringChecksumChar keyboard_arrow_up
Source Code
function _addressStringChecksumChar(
bytes memory addrStr,
uint256 offset,
uint8 hashNybble
) private pure {
bytes1 ch = addrStr[offset];
if (ch >= "a" && hashNybble >= 8) {
addrStr[offset] = ch ^ 0x20;
}
}
internal UTXOClaimValidation._hexStringFromData keyboard_arrow_up
Source Code
function _hexStringFromData(
bytes memory hexStr,
bytes32 data,
uint256 dataLen
) private pure {
uint256 offset = 0;
for (uint256 i = 0; i < dataLen; i++) {
uint8 b = uint8(data[i]);
hexStr[offset++] = HEX_DIGITS[b >> 4];
hexStr[offset++] = HEX_DIGITS[b & 0x0f];
}
}
internal UTXOClaimValidation._hash256 keyboard_arrow_up
internal UTXOClaimValidation._hash160 keyboard_arrow_up
internal StakeableToken._stakeStart keyboard_arrow_up
Parameters help
Requirements help
Source Code
function _stakeStart(
GlobalsCache memory g,
uint256 newStakedHearts,
uint256 newStakedDays,
bool newAutoStake
) internal {
/* Enforce the maximum stake time */
require(
newStakedDays <= MAX_STAKE_DAYS,
"HEX: newStakedDays higher than maximum"
);
uint256 bonusHearts = _stakeStartBonusHearts(newStakedHearts, newStakedDays);
uint256 newStakeShares = ((newStakedHearts + bonusHearts) *
SHARE_RATE_SCALE) / g._shareRate;
/* Ensure newStakedHearts is enough for at least one stake share */
require(
newStakeShares != 0,
"HEX: newStakedHearts must be at least minimum shareRate"
);
/*
The stakeStart timestamp will always be part-way through the current
day, so it needs to be rounded-up to the next day to ensure all
stakes align with the same fixed calendar days. The current day is
already rounded-down, so rounded-up is current day + 1.
*/
uint256 newLockedDay = g._currentDay < CLAIM_PHASE_START_DAY
? CLAIM_PHASE_START_DAY + 1
: g._currentDay + 1;
/* Create Stake */
uint40 newStakeId = ++g._latestStakeId;
_stakeAdd(
stakeLists[msg.sender],
newStakeId,
newStakedHearts,
newStakeShares,
newLockedDay,
newStakedDays,
newAutoStake
);
_emitStakeStart(
newStakeId,
newStakedHearts,
newStakeShares,
newStakedDays,
newAutoStake
);
/* Stake is added to total in the next round, not the current round */
g._nextStakeSharesTotal += newStakeShares;
/* Track total staked Hearts for inflation calculations */
g._lockedHeartsTotal += newStakedHearts;
}
internal StakeableToken._calcPayoutRewards keyboard_arrow_up
Parameters help
Source Code
function _calcPayoutRewards(
GlobalsCache memory g,
uint256 stakeSharesParam,
uint256 beginDay,
uint256 endDay
) private view returns (uint256 payout) {
for (uint256 day = beginDay; day < endDay; day++) {
payout +=
(dailyData[day].dayPayoutTotal * stakeSharesParam) /
dailyData[day].dayStakeSharesTotal;
}
/* Less expensive to re-read storage than to have the condition inside the loop */
if (beginDay <= BIG_PAY_DAY && endDay > BIG_PAY_DAY) {
uint256 bigPaySlice = (g._unclaimedSatoshisTotal *
HEARTS_PER_SATOSHI *
stakeSharesParam) / dailyData[BIG_PAY_DAY].dayStakeSharesTotal;
payout += bigPaySlice + _calcAdoptionBonus(g, bigPaySlice);
}
return payout;
}
internal StakeableToken._stakeStartBonusHearts keyboard_arrow_up
Source Code
function _stakeStartBonusHearts(uint256 newStakedHearts, uint256 newStakedDays)
private
pure
returns (uint256 bonusHearts)
{
/*
LONGER PAYS BETTER:
If longer than 1 day stake is committed to, each extra day
gives bonus shares of approximately 0.0548%, which is approximately 20%
extra per year of increased stake length committed to, but capped to a
maximum of 200% extra.
extraDays = stakedDays - 1
longerBonus% = (extraDays / 364) * 20%
= (extraDays / 364) / 5
= extraDays / 1820
= extraDays / LPB
extraDays = longerBonus% * 1820
extraDaysMax = longerBonusMax% * 1820
= 200% * 1820
= 3640
= LPB_MAX_DAYS
BIGGER PAYS BETTER:
Bonus percentage scaled 0% to 10% for the first 150M HEX of stake.
biggerBonus% = (cappedHearts / BPB_MAX_HEARTS) * 10%
= (cappedHearts / BPB_MAX_HEARTS) / 10
= cappedHearts / (BPB_MAX_HEARTS * 10)
= cappedHearts / BPB
COMBINED:
combinedBonus% = longerBonus% + biggerBonus%
cappedExtraDays cappedHearts
= --------------- + ------------
LPB BPB
cappedExtraDays * BPB cappedHearts * LPB
= --------------------- + ------------------
LPB * BPB LPB * BPB
cappedExtraDays * BPB + cappedHearts * LPB
= --------------------------------------------
LPB * BPB
bonusHearts = hearts * combinedBonus%
= hearts * (cappedExtraDays * BPB + cappedHearts * LPB) / (LPB * BPB)
*/
uint256 cappedExtraDays = 0;
/* Must be more than 1 day for Longer-Pays-Better */
if (newStakedDays > 1) {
cappedExtraDays = newStakedDays <= LPB_MAX_DAYS
? newStakedDays - 1
: LPB_MAX_DAYS;
}
uint256 cappedStakedHearts = newStakedHearts <= BPB_MAX_HEARTS
? newStakedHearts
: BPB_MAX_HEARTS;
bonusHearts = cappedExtraDays * BPB + cappedStakedHearts * LPB;
bonusHearts = (newStakedHearts * bonusHearts) / (LPB * BPB);
return bonusHearts;
}
internal StakeableToken._stakeUnlock keyboard_arrow_up
internal StakeableToken._stakePerformance keyboard_arrow_up
Source Code
function _stakePerformance(
GlobalsCache memory g,
StakeCache memory st,
uint256 servedDays
)
private
view
returns (
uint256 stakeReturn,
uint256 payout,
uint256 penalty,
uint256 cappedPenalty
)
{
if (servedDays < st._stakedDays) {
(payout, penalty) = _calcPayoutAndEarlyPenalty(
g,
st._lockedDay,
st._stakedDays,
servedDays,
st._stakeShares
);
stakeReturn = st._stakedHearts + payout;
} else {
// servedDays must == stakedDays here
payout = _calcPayoutRewards(
g,
st._stakeShares,
st._lockedDay,
st._lockedDay + servedDays
);
stakeReturn = st._stakedHearts + payout;
penalty = _calcLatePenalty(
st._lockedDay,
st._stakedDays,
st._unlockedDay,
stakeReturn
);
}
if (penalty != 0) {
if (penalty > stakeReturn) {
/* Cannot have a negative stake return */
cappedPenalty = stakeReturn;
stakeReturn = 0;
} else {
/* Remove penalty from the stake return */
cappedPenalty = penalty;
stakeReturn -= cappedPenalty;
}
}
return (stakeReturn, payout, penalty, cappedPenalty);
}
internal StakeableToken._calcPayoutAndEarlyPenalty keyboard_arrow_up
Parameters help
Source Code
function _calcPayoutAndEarlyPenalty(
GlobalsCache memory g,
uint256 lockedDayParam,
uint256 stakedDaysParam,
uint256 servedDays,
uint256 stakeSharesParam
) private view returns (uint256 payout, uint256 penalty) {
uint256 servedEndDay = lockedDayParam + servedDays;
/* 50% of stakedDays (rounded up) with a minimum applied */
uint256 penaltyDays = (stakedDaysParam + 1) / 2;
if (penaltyDays < EARLY_PENALTY_MIN_DAYS) {
penaltyDays = EARLY_PENALTY_MIN_DAYS;
}
if (servedDays == 0) {
/* Fill penalty days with the estimated average payout */
uint256 expected = _estimatePayoutRewardsDay(
g,
stakeSharesParam,
lockedDayParam
);
penalty = expected * penaltyDays;
return (payout, penalty); // Actual payout was 0
}
if (penaltyDays < servedDays) {
/*
Simplified explanation of intervals where end-day is non-inclusive:
penalty: [lockedDay ... penaltyEndDay)
delta: [penaltyEndDay ... servedEndDay)
payout: [lockedDay ....................... servedEndDay)
*/
uint256 penaltyEndDay = lockedDayParam + penaltyDays;
penalty = _calcPayoutRewards(
g,
stakeSharesParam,
lockedDayParam,
penaltyEndDay
);
uint256 delta = _calcPayoutRewards(
g,
stakeSharesParam,
penaltyEndDay,
servedEndDay
);
payout = penalty + delta;
return (payout, penalty);
}
/* penaltyDays >= servedDays */
payout = _calcPayoutRewards(
g,
stakeSharesParam,
lockedDayParam,
servedEndDay
);
if (penaltyDays == servedDays) {
penalty = payout;
} else {
/*
(penaltyDays > servedDays) means not enough days served, so fill the
penalty days with the average payout from only the days that were served.
*/
penalty = (payout * penaltyDays) / servedDays;
}
return (payout, penalty);
}
internal StakeableToken._calcLatePenalty keyboard_arrow_up
Parameters help
Source Code
function _calcLatePenalty(
uint256 lockedDayParam,
uint256 stakedDaysParam,
uint256 unlockedDayParam,
uint256 rawStakeReturn
) private pure returns (uint256) {
/* Allow grace time before penalties accrue */
uint256 maxUnlockedDay = lockedDayParam +
stakedDaysParam +
LATE_PENALTY_GRACE_DAYS;
if (unlockedDayParam <= maxUnlockedDay) {
return 0;
}
/* Calculate penalty as a percentage of stake return based on time */
return
(rawStakeReturn * (unlockedDayParam - maxUnlockedDay)) /
LATE_PENALTY_SCALE_DAYS;
}
internal StakeableToken._splitPenaltyProceeds keyboard_arrow_up
Source Code
function _splitPenaltyProceeds(GlobalsCache memory g, uint256 penalty) private {
/* Split a penalty 50:50 between Origin and stakePenaltyTotal */
uint256 splitPenalty = penalty / 2;
if (splitPenalty != 0) {
_mint(ORIGIN_ADDR, splitPenalty);
}
/* Use the other half of the penalty to account for an odd-numbered penalty */
splitPenalty = penalty - splitPenalty;
g._stakePenaltyTotal += splitPenalty;
}
internal StakeableToken._shareRateUpdate keyboard_arrow_up
Source Code
function _shareRateUpdate(
GlobalsCache memory g,
StakeCache memory st,
uint256 stakeReturn
) private {
if (stakeReturn > st._stakedHearts) {
/*
Calculate the new shareRate that would yield the same number of shares if
the user re-staked this stakeReturn, factoring in any bonuses they would
receive in stakeStart().
*/
uint256 bonusHearts = _stakeStartBonusHearts(stakeReturn, st._stakedDays);
uint256 newShareRate = ((stakeReturn + bonusHearts) * SHARE_RATE_SCALE) /
st._stakeShares;
if (newShareRate > SHARE_RATE_MAX) {
/*
Realistically this can't happen, but there are contrived theoretical
scenarios that can lead to extreme values of newShareRate, so it is
capped to prevent them anyway.
*/
newShareRate = SHARE_RATE_MAX;
}
if (newShareRate > g._shareRate) {
g._shareRate = newShareRate;
_emitShareRateChange(newShareRate, st._stakeId);
}
}
}
internal StakeableToken._emitStakeStart keyboard_arrow_up
Parameters help
Source Code
function _emitStakeStart(
uint40 stakeId,
uint256 stakedHearts,
uint256 stakeShares,
uint256 stakedDays,
bool isAutoStake
) private {
emit StakeStart( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint72(stakedHearts)) << 40) |
(uint256(uint72(stakeShares)) << 112) |
(uint256(uint16(stakedDays)) << 184) |
(isAutoStake ? (1 << 200) : 0),
msg.sender,
stakeId
);
}
internal StakeableToken._emitStakeGoodAccounting keyboard_arrow_up
Parameters help
Source Code
function _emitStakeGoodAccounting(
address stakerAddr,
uint40 stakeId,
uint256 stakedHearts,
uint256 stakeShares,
uint256 payout,
uint256 penalty
) private {
emit StakeGoodAccounting( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint72(stakedHearts)) << 40) |
(uint256(uint72(stakeShares)) << 112) |
(uint256(uint72(payout)) << 184),
uint256(uint72(penalty)),
stakerAddr,
stakeId,
msg.sender
);
}
internal StakeableToken._emitStakeEnd keyboard_arrow_up
Parameters help
Source Code
function _emitStakeEnd(
uint40 stakeId,
uint256 stakedHearts,
uint256 stakeShares,
uint256 payout,
uint256 penalty,
uint256 servedDays,
bool prevUnlocked
) private {
emit StakeEnd( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint72(stakedHearts)) << 40) |
(uint256(uint72(stakeShares)) << 112) |
(uint256(uint72(payout)) << 184),
uint256(uint72(penalty)) |
(uint256(uint16(servedDays)) << 72) |
(prevUnlocked ? (1 << 88) : 0),
msg.sender,
stakeId
);
}
internal StakeableToken._emitShareRateChange keyboard_arrow_up
Source Code
function _emitShareRateChange(uint256 shareRate, uint40 stakeId) private {
emit ShareRateChange( // (auto-generated event)
uint256(uint40(block.timestamp)) | (uint256(uint40(shareRate)) << 40),
stakeId
);
}
internal GlobalsAndUtility._currentDay keyboard_arrow_up
internal GlobalsAndUtility._dailyDataUpdateAuto keyboard_arrow_up
internal GlobalsAndUtility._globalsLoad keyboard_arrow_up
Source Code
function _globalsLoad(GlobalsCache memory g, GlobalsCache memory gSnapshot)
internal
view
{
// 1
g._lockedHeartsTotal = globals.lockedHeartsTotal;
g._nextStakeSharesTotal = globals.nextStakeSharesTotal;
g._shareRate = globals.shareRate;
g._stakePenaltyTotal = globals.stakePenaltyTotal;
// 2
g._dailyDataCount = globals.dailyDataCount;
g._stakeSharesTotal = globals.stakeSharesTotal;
g._latestStakeId = globals.latestStakeId;
(
g._claimedBtcAddrCount,
g._claimedSatoshisTotal,
g._unclaimedSatoshisTotal
) = _claimStatsDecode(globals.claimStats);
//
g._currentDay = _currentDay();
_globalsCacheSnapshot(g, gSnapshot);
}
internal GlobalsAndUtility._globalsCacheSnapshot keyboard_arrow_up
Source Code
function _globalsCacheSnapshot(
GlobalsCache memory g,
GlobalsCache memory gSnapshot
) internal pure {
// 1
gSnapshot._lockedHeartsTotal = g._lockedHeartsTotal;
gSnapshot._nextStakeSharesTotal = g._nextStakeSharesTotal;
gSnapshot._shareRate = g._shareRate;
gSnapshot._stakePenaltyTotal = g._stakePenaltyTotal;
// 2
gSnapshot._dailyDataCount = g._dailyDataCount;
gSnapshot._stakeSharesTotal = g._stakeSharesTotal;
gSnapshot._latestStakeId = g._latestStakeId;
gSnapshot._unclaimedSatoshisTotal = g._unclaimedSatoshisTotal;
gSnapshot._claimedSatoshisTotal = g._claimedSatoshisTotal;
gSnapshot._claimedBtcAddrCount = g._claimedBtcAddrCount;
}
internal GlobalsAndUtility._globalsSync keyboard_arrow_up
Source Code
function _globalsSync(GlobalsCache memory g, GlobalsCache memory gSnapshot)
internal
{
if (
g._lockedHeartsTotal != gSnapshot._lockedHeartsTotal ||
g._nextStakeSharesTotal != gSnapshot._nextStakeSharesTotal ||
g._shareRate != gSnapshot._shareRate ||
g._stakePenaltyTotal != gSnapshot._stakePenaltyTotal
) {
// 1
globals.lockedHeartsTotal = uint72(g._lockedHeartsTotal);
globals.nextStakeSharesTotal = uint72(g._nextStakeSharesTotal);
globals.shareRate = uint40(g._shareRate);
globals.stakePenaltyTotal = uint72(g._stakePenaltyTotal);
}
if (
g._dailyDataCount != gSnapshot._dailyDataCount ||
g._stakeSharesTotal != gSnapshot._stakeSharesTotal ||
g._latestStakeId != gSnapshot._latestStakeId ||
g._unclaimedSatoshisTotal != gSnapshot._unclaimedSatoshisTotal ||
g._claimedSatoshisTotal != gSnapshot._claimedSatoshisTotal ||
g._claimedBtcAddrCount != gSnapshot._claimedBtcAddrCount
) {
// 2
globals.dailyDataCount = uint16(g._dailyDataCount);
globals.stakeSharesTotal = uint72(g._stakeSharesTotal);
globals.latestStakeId = g._latestStakeId;
globals.claimStats = _claimStatsEncode(
g._claimedBtcAddrCount,
g._claimedSatoshisTotal,
g._unclaimedSatoshisTotal
);
}
}
internal GlobalsAndUtility._stakeLoad keyboard_arrow_up
Requirements help
Source Code
function _stakeLoad(
StakeStore storage stRef,
uint40 stakeIdParam,
StakeCache memory st
) internal view {
/* Ensure caller's stakeIndex is still current */
require(stakeIdParam == stRef.stakeId, "HEX: stakeIdParam not in stake");
st._stakeId = stRef.stakeId;
st._stakedHearts = stRef.stakedHearts;
st._stakeShares = stRef.stakeShares;
st._lockedDay = stRef.lockedDay;
st._stakedDays = stRef.stakedDays;
st._unlockedDay = stRef.unlockedDay;
st._isAutoStake = stRef.isAutoStake;
}
internal GlobalsAndUtility._stakeUpdate keyboard_arrow_up
Source Code
function _stakeUpdate(StakeStore storage stRef, StakeCache memory st) internal {
stRef.stakeId = st._stakeId;
stRef.stakedHearts = uint72(st._stakedHearts);
stRef.stakeShares = uint72(st._stakeShares);
stRef.lockedDay = uint16(st._lockedDay);
stRef.stakedDays = uint16(st._stakedDays);
stRef.unlockedDay = uint16(st._unlockedDay);
stRef.isAutoStake = st._isAutoStake;
}
internal GlobalsAndUtility._stakeAdd keyboard_arrow_up
Parameters help
Source Code
function _stakeAdd(
StakeStore[] storage stakeListRef,
uint40 newStakeId,
uint256 newStakedHearts,
uint256 newStakeShares,
uint256 newLockedDay,
uint256 newStakedDays,
bool newAutoStake
) internal {
stakeListRef.push(
StakeStore(
newStakeId,
uint72(newStakedHearts),
uint72(newStakeShares),
uint16(newLockedDay),
uint16(newStakedDays),
uint16(0), // unlockedDay
newAutoStake
)
);
}
internal GlobalsAndUtility._stakeRemove keyboard_arrow_up
Source Code
function _stakeRemove(StakeStore[] storage stakeListRef, uint256 stakeIndex)
internal
{
uint256 lastIndex = stakeListRef.length - 1;
/* Skip the copy if element to be removed is already the last element */
if (stakeIndex != lastIndex) {
/* Copy last element to the requested element's "hole" */
stakeListRef[stakeIndex] = stakeListRef[lastIndex];
}
/*
Reduce the array length now that the array is contiguous.
Surprisingly, 'pop()' uses less gas than 'stakeListRef.length = lastIndex'
*/
stakeListRef.pop();
}
internal GlobalsAndUtility._claimStatsEncode keyboard_arrow_up
Parameters help
Source Code
function _claimStatsEncode(
uint256 _claimedBtcAddrCount,
uint256 _claimedSatoshisTotal,
uint256 _unclaimedSatoshisTotal
) internal pure returns (uint128) {
uint256 v = _claimedBtcAddrCount << (SATOSHI_UINT_SIZE * 2);
v |= _claimedSatoshisTotal << SATOSHI_UINT_SIZE;
v |= _unclaimedSatoshisTotal;
return uint128(v);
}
internal GlobalsAndUtility._claimStatsDecode keyboard_arrow_up
Source Code
function _claimStatsDecode(uint128 v)
internal
pure
returns (
uint256 _claimedBtcAddrCount,
uint256 _claimedSatoshisTotal,
uint256 _unclaimedSatoshisTotal
)
{
_claimedBtcAddrCount = v >> (SATOSHI_UINT_SIZE * 2);
_claimedSatoshisTotal = (v >> SATOSHI_UINT_SIZE) & SATOSHI_UINT_MASK;
_unclaimedSatoshisTotal = v & SATOSHI_UINT_MASK;
return (_claimedBtcAddrCount, _claimedSatoshisTotal, _unclaimedSatoshisTotal);
}
internal GlobalsAndUtility._estimatePayoutRewardsDay keyboard_arrow_up
Source Code
function _estimatePayoutRewardsDay(
GlobalsCache memory g,
uint256 stakeSharesParam,
uint256 day
) internal view returns (uint256 payout) {
/* Prevent updating state for this estimation */
GlobalsCache memory gTmp;
_globalsCacheSnapshot(g, gTmp);
DailyRoundState memory rs;
rs._allocSupplyCached = totalSupply() + g._lockedHeartsTotal;
_dailyRoundCalc(gTmp, rs, day);
/* Stake is no longer locked so it must be added to total as if it were */
gTmp._stakeSharesTotal += stakeSharesParam;
payout = (rs._payoutTotal * stakeSharesParam) / gTmp._stakeSharesTotal;
if (day == BIG_PAY_DAY) {
uint256 bigPaySlice = (gTmp._unclaimedSatoshisTotal *
HEARTS_PER_SATOSHI *
stakeSharesParam) / gTmp._stakeSharesTotal;
payout += bigPaySlice + _calcAdoptionBonus(gTmp, bigPaySlice);
}
return payout;
}
internal GlobalsAndUtility._calcAdoptionBonus keyboard_arrow_up
Source Code
function _calcAdoptionBonus(GlobalsCache memory g, uint256 payout)
internal
pure
returns (uint256)
{
/*
VIRAL REWARDS: Add adoption percentage bonus to payout
viral = payout * (claimedBtcAddrCount / CLAIMABLE_BTC_ADDR_COUNT)
*/
uint256 viral = (payout * g._claimedBtcAddrCount) / CLAIMABLE_BTC_ADDR_COUNT;
/*
CRIT MASS REWARDS: Add adoption percentage bonus to payout
crit = payout * (claimedSatoshisTotal / CLAIMABLE_SATOSHIS_TOTAL)
*/
uint256 crit = (payout * g._claimedSatoshisTotal) / CLAIMABLE_SATOSHIS_TOTAL;
return viral + crit;
}
internal GlobalsAndUtility._dailyRoundCalc keyboard_arrow_up
Source Code
function _dailyRoundCalc(
GlobalsCache memory g,
DailyRoundState memory rs,
uint256 day
) private pure {
/*
Calculate payout round
Inflation of 3.69% inflation per 364 days (approx 1 year)
dailyInterestRate = exp(log(1 + 3.69%) / 364) - 1
= exp(log(1 + 0.0369) / 364) - 1
= exp(log(1.0369) / 364) - 1
= 0.000099553011616349 (approx)
payout = allocSupply * dailyInterestRate
= allocSupply / (1 / dailyInterestRate)
= allocSupply / (1 / 0.000099553011616349)
= allocSupply / 10044.899534066692 (approx)
= allocSupply * 10000 / 100448995 (* 10000/10000 for int precision)
*/
rs._payoutTotal = (rs._allocSupplyCached * 10000) / 100448995;
if (day < CLAIM_PHASE_END_DAY) {
uint256 bigPaySlice = (g._unclaimedSatoshisTotal * HEARTS_PER_SATOSHI) /
CLAIM_PHASE_DAYS;
uint256 originBonus = bigPaySlice +
_calcAdoptionBonus(g, rs._payoutTotal + bigPaySlice);
rs._mintOriginBatch += originBonus;
rs._allocSupplyCached += originBonus;
rs._payoutTotal += _calcAdoptionBonus(g, rs._payoutTotal);
}
if (g._stakePenaltyTotal != 0) {
rs._payoutTotal += g._stakePenaltyTotal;
g._stakePenaltyTotal = 0;
}
}
internal GlobalsAndUtility._dailyRoundCalcAndStore keyboard_arrow_up
Source Code
function _dailyRoundCalcAndStore(
GlobalsCache memory g,
DailyRoundState memory rs,
uint256 day
) private {
_dailyRoundCalc(g, rs, day);
dailyData[day].dayPayoutTotal = uint72(rs._payoutTotal);
dailyData[day].dayStakeSharesTotal = uint72(g._stakeSharesTotal);
dailyData[day].dayUnclaimedSatoshisTotal = uint56(g._unclaimedSatoshisTotal);
}
internal GlobalsAndUtility._dailyDataUpdate keyboard_arrow_up
Source Code
function _dailyDataUpdate(
GlobalsCache memory g,
uint256 beforeDay,
bool isAutoUpdate
) private {
if (g._dailyDataCount >= beforeDay) {
/* Already up-to-date */
return;
}
DailyRoundState memory rs;
rs._allocSupplyCached = totalSupply() + g._lockedHeartsTotal;
uint256 day = g._dailyDataCount;
_dailyRoundCalcAndStore(g, rs, day);
/* Stakes started during this day are added to the total the next day */
if (g._nextStakeSharesTotal != 0) {
g._stakeSharesTotal += g._nextStakeSharesTotal;
g._nextStakeSharesTotal = 0;
}
while (++day < beforeDay) {
_dailyRoundCalcAndStore(g, rs, day);
}
_emitDailyDataUpdate(g._dailyDataCount, day, isAutoUpdate);
g._dailyDataCount = day;
if (rs._mintOriginBatch != 0) {
_mint(ORIGIN_ADDR, rs._mintOriginBatch);
}
}
internal GlobalsAndUtility._emitDailyDataUpdate keyboard_arrow_up
Source Code
function _emitDailyDataUpdate(
uint256 beginDay,
uint256 endDay,
bool isAutoUpdate
) private {
emit DailyDataUpdate( // (auto-generated event)
uint256(uint40(block.timestamp)) |
(uint256(uint16(beginDay)) << 40) |
(uint256(uint16(endDay)) << 56) |
(isAutoUpdate ? (1 << 72) : 0),
msg.sender
);
}
internal ERC20._transfer keyboard_arrow_up
Requirements help
Source Code
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(
amount,
"ERC20: transfer amount exceeds balance"
);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
internal ERC20._mint keyboard_arrow_up
Requirements help
Source Code
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
internal ERC20._burn keyboard_arrow_up
Requirements help
Source Code
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(
amount,
"ERC20: burn amount exceeds balance"
);
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
internal ERC20._approve keyboard_arrow_up
Requirements help
Source Code
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
internal ERC20._burnFrom keyboard_arrow_up
Requirements help
Source Code
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(
account,
_msgSender(),
_allowances[account][_msgSender()].sub(
amount,
"ERC20: burn amount exceeds allowance"
)
);
}
internal Context.constructor keyboard_arrow_up
internal Context._msgSender keyboard_arrow_up
internal Context._msgData keyboard_arrow_up
Parameters help
This function has no parameters.
Source Code
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}