Blockwell

HEX

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

Approval Event

Parameters help
owner
address help
spender
address help
value
uint256 help

Claim Event

Parameters help
data0
uint256 help
data1
uint256 help
btcAddr
bytes20 help
claimToAddr
address help
referrerAddr
address help

ClaimAssist Event

Parameters help
data0
uint256 help
data1
uint256 help
data2
uint256 help
senderAddr
address help

DailyDataUpdate Event

Parameters help
data0
uint256 help
updaterAddr
address help

ShareRateChange Event

Parameters help
data0
uint256 help
stakeId
uint40 help

StakeEnd Event

Parameters help
data0
uint256 help
data1
uint256 help
stakerAddr
address help
stakeId
uint40 help

StakeGoodAccounting Event

Parameters help
data0
uint256 help
data1
uint256 help
stakerAddr
address help
stakeId
uint40 help
senderAddr
address help

StakeStart Event

Parameters help
data0
uint256 help
stakerAddr
address help
stakeId
uint40 help

Transfer Event

Parameters help
from
address help
to
address help
value
uint256 help

XfLobbyEnter Event

Parameters help
data0
uint256 help
memberAddr
address help
entryId
uint256 help
referrerAddr
address help

XfLobbyExit Event

Parameters help
data0
uint256 help
memberAddr
address help
entryId
uint256 help
referrerAddr
address help

ORIGIN_ADDR Constant

address help

FLUSH_ADDR Constant

address help

name Constant

string help
HEX

symbol Constant

string help
HEX

decimals Constant

uint8 help
8

HEARTS_PER_HEX Constant

uint256 help
UNKNOWN VALUE

HEX_PER_BTC Constant

uint256 help
1e4

SATOSHIS_PER_BTC Constant

uint256 help
1e8

LAUNCH_TIME Constant

uint256 help
1575331200

HEART_UINT_SIZE Constant

uint256 help
72

XF_LOBBY_ENTRY_INDEX_SIZE Constant

uint256 help
40

XF_LOBBY_ENTRY_INDEX_MASK Constant

uint256 help
UNKNOWN VALUE - 1

WAAS_LOBBY_SEED_HEX Constant

uint256 help
1e9

WAAS_LOBBY_SEED_HEARTS Constant

uint256 help

PRE_CLAIM_DAYS Constant

uint256 help
1

CLAIM_PHASE_START_DAY Constant

uint256 help

CLAIM_PHASE_WEEKS Constant

uint256 help
50

CLAIM_PHASE_DAYS Constant

uint256 help

CLAIM_PHASE_END_DAY Constant

uint256 help

XF_LOBBY_DAY_WORDS Constant

uint256 help
UNKNOWN VALUE

BIG_PAY_DAY Constant

uint256 help

MERKLE_TREE_ROOT Constant

bytes32 help
0x4e831acb4223b66de3b3d2e54a2edeefb0de3d7916e2886a4b134d9764d41bec

MERKLE_LEAF_SATOSHI_SIZE Constant

uint256 help
45

MERKLE_LEAF_FILL_SIZE Constant

uint256 help

MERKLE_LEAF_FILL_BASE Constant

uint256 help
UNKNOWN VALUE - 1

MERKLE_LEAF_FILL_MASK Constant

uint256 help
UNKNOWN VALUE

SATOSHI_UINT_SIZE Constant

uint256 help
51

SATOSHI_UINT_MASK Constant

uint256 help
UNKNOWN VALUE - 1

FULL_SATOSHIS_TOTAL Constant

uint256 help
1807766732160668

CLAIMABLE_SATOSHIS_TOTAL Constant

uint256 help
910087996911001

CLAIMABLE_BTC_ADDR_COUNT Constant

uint256 help
27997742

MAX_BTC_ADDR_BALANCE_SATOSHIS Constant

uint256 help
25550214098481

AUTO_STAKE_CLAIM_PERCENT Constant

uint256 help
90

MIN_STAKE_DAYS Constant

uint256 help
1

MIN_AUTO_STAKE_DAYS Constant

uint256 help
350

MAX_STAKE_DAYS Constant

uint256 help
5555

EARLY_PENALTY_MIN_DAYS Constant

uint256 help
90

LATE_PENALTY_GRACE_WEEKS Constant

uint256 help
2

LATE_PENALTY_GRACE_DAYS Constant

uint256 help

LATE_PENALTY_SCALE_WEEKS Constant

uint256 help
100

LATE_PENALTY_SCALE_DAYS Constant

uint256 help

LPB_BONUS_PERCENT Constant

uint256 help
20

LPB_BONUS_MAX_PERCENT Constant

uint256 help
200

LPB Constant

uint256 help

LPB_MAX_DAYS Constant

uint256 help

BPB_BONUS_PERCENT Constant

uint256 help
10

BPB_MAX_HEX Constant

uint256 help
150 * 1e6

BPB_MAX_HEARTS Constant

uint256 help

BPB Constant

uint256 help

SHARE_RATE_SCALE Constant

uint256 help
1e5

SHARE_RATE_UINT_SIZE Constant

uint256 help
40

SHARE_RATE_MAX Constant

uint256 help
UNKNOWN VALUE - 1

ETH_ADDRESS_BYTE_LEN Constant

uint8 help
20

ETH_ADDRESS_HEX_LEN Constant

uint8 help

CLAIM_PARAM_HASH_BYTE_LEN Constant

uint8 help
12

CLAIM_PARAM_HASH_HEX_LEN Constant

uint8 help

BITCOIN_SIG_PREFIX_LEN Constant

uint8 help
24

BITCOIN_SIG_PREFIX_STR Constant

bytes24 help
Bitcoin Signed Message:\n

STD_CLAIM_PREFIX_STR Constant

bytes help
Claim_HEX_to_0x

OLD_CLAIM_PREFIX_STR Constant

bytes help
Claim_BitcoinHEX_to_0x

HEX_DIGITS Constant

bytes16 help
0123456789abcdef

CLAIM_FLAG_MSG_PREFIX_OLD Constant

uint8 help
UNKNOWN VALUE

CLAIM_FLAG_BTC_ADDR_COMPRESSED Constant

uint8 help
UNKNOWN VALUE

CLAIM_FLAG_BTC_ADDR_P2WPKH_IN_P2SH Constant

uint8 help
UNKNOWN VALUE

CLAIM_FLAG_BTC_ADDR_BECH32 Constant

uint8 help
UNKNOWN VALUE

CLAIM_FLAG_ETH_ADDR_LOWERCASE Constant

uint8 help
UNKNOWN VALUE

globals Variable

GlobalsStore help

btcAddressClaims Variable

mapping(bytes20 => bool) help

xfLobby Variable

mapping(uint256 => uint256) help

dailyData Variable

mapping(uint256 => DailyDataStore) help
Internal Variable

stakeLists Variable

mapping(address => StakeStore[]) help
Internal Variable

xfLobbyMembers Variable

mapping(uint256 => mapping(address => XfLobbyQueueStore)) help
Internal Variable

_balances Variable

mapping(address => uint256) help
Internal Variable

_allowances Variable

mapping(address => mapping(address => uint256)) help
Internal Variable

_totalSupply Variable

uint256 help
Internal Variable

Functions Expand All Collapse All

Parameters help

This function has no parameters.

Properties

Visibility help public
Mutability help view
Source Code
function totalSupply() public view returns (uint256) {
  return _totalSupply;
}

Parameters help

Name Type
account
address help

Properties

Visibility help public
Mutability help view
Source Code
function balanceOf(address account) public view returns (uint256) {
  return _balances[account];
}

Parameters help

Name Type
recipient
address help
amount
uint256 help

Properties

Visibility help public
Mutability help transaction

Requirements help

Source Code
function transfer(address recipient, uint256 amount) public returns (bool) {
  _transfer(_msgSender(), recipient, amount);
  return true;
}

Parameters help

Name Type
owner
address help
spender
address help

Properties

Visibility help public
Mutability help view
Source Code
function allowance(address owner, address spender)
  public
  view
  returns (uint256)
{
  return _allowances[owner][spender];
}

Parameters help

Name Type
spender
address help
amount
uint256 help

Properties

Visibility help public
Mutability help transaction

Requirements help

Source Code
function approve(address spender, uint256 amount) public returns (bool) {
  _approve(_msgSender(), spender, amount);
  return true;
}

Parameters help

Name Type
sender
address help
recipient
address help
amount
uint256 help

Properties

Visibility help public
Mutability help transaction

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;
}

Parameters help

Name Type
spender
address help
addedValue
uint256 help

Properties

Visibility help public
Mutability help transaction

Requirements help

Source Code
function increaseAllowance(address spender, uint256 addedValue)
  public
  returns (bool)
{
  _approve(
    _msgSender(),
    spender,
    _allowances[_msgSender()][spender].add(addedValue)
  );
  return true;
}

Parameters help

Name Type
spender
address help
subtractedValue
uint256 help

Properties

Visibility help public
Mutability help transaction

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

Parameters help

Name Type
beforeDay
uint256 help

Properties

Visibility help public
Mutability help transaction
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

Parameters help

Name Type
beginDay
uint256 help
endDay
uint256 help

Properties

Visibility help public
Mutability help view
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.

Properties

Visibility help public
Mutability help view
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

Parameters help

This function has no parameters.

Properties

Visibility help public
Mutability help view
Source Code
function allocatedSupply() external view returns (uint256) {
  return totalSupply() + globals.lockedHeartsTotal;
}

currentDay keyboard_arrow_up

Parameters help

This function has no parameters.

Properties

Visibility help public
Mutability help view
Source Code
function currentDay() external view returns (uint256) {
  return _currentDay();
}

stakeStart keyboard_arrow_up

Parameters help

Name Type
newStakedHearts
uint256 help
newStakedDays
uint256 help

Properties

Visibility help public
Mutability help transaction
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

Parameters help

Name Type
stakerAddr
address help
stakeIndex
uint256 help
stakeIdParam
uint40 help

Properties

Visibility help public
Mutability help transaction
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

Parameters help

Name Type
stakeIndex
uint256 help
stakeIdParam
uint40 help

Properties

Visibility help public
Mutability help transaction
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

Parameters help

Name Type
stakerAddr
address help

Properties

Visibility help public
Mutability help view
Source Code
function stakeCount(address stakerAddr) external view returns (uint256) {
  return stakeLists[stakerAddr].length;
}

btcAddressIsClaimable keyboard_arrow_up

Parameters help

Name Type
btcAddr
bytes20 help
rawSatoshis
uint256 help
proof
bytes32[] help

Properties

Visibility help public
Mutability help view
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

Parameters help

Name Type
btcAddr
bytes20 help
rawSatoshis
uint256 help
proof
bytes32[] help

Properties

Visibility help public
Mutability help pure

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

Parameters help

Name Type
merkleLeaf
bytes32 help
proof
bytes32[] help

Properties

Visibility help public
Mutability help pure
Source Code
function merkleProofIsValid(bytes32 merkleLeaf, bytes32[] calldata proof)
  external
  pure
  returns (bool)
{
  return _merkleProofIsValid(merkleLeaf, proof);
}

claimMessageMatchesSignature keyboard_arrow_up

Parameters help

Name Type
claimToAddr
address help
claimParamHash
bytes32 help
pubKeyX
bytes32 help
pubKeyY
bytes32 help
claimFlags
uint8 help
v
uint8 help
r
bytes32 help
s
bytes32 help

Properties

Visibility help public
Mutability help pure

Requirements 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

Parameters help

Name Type
pubKeyX
bytes32 help
pubKeyY
bytes32 help

Properties

Visibility help public
Mutability help pure
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

Parameters help

Name Type
pubKeyX
bytes32 help
pubKeyY
bytes32 help
claimFlags
uint8 help

Properties

Visibility help public
Mutability help pure
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

Name Type
rawSatoshis
uint256 help
proof
bytes32[] help
claimToAddr
address help
pubKeyX
bytes32 help
pubKeyY
bytes32 help
claimFlags
uint8 help
v
uint8 help
r
bytes32 help
s
bytes32 help
autoStakeDays
uint256 help
referrerAddr
address help

Properties

Visibility help public
Mutability help transaction
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

Parameters help

Name Type
referrerAddr
address help

Properties

Visibility help public
Mutability help payable
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

Parameters help

Name Type
enterDay
uint256 help
count
uint256 help

Properties

Visibility help public
Mutability help transaction

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

Parameters help

This function has no parameters.

Properties

Visibility help public
Mutability help transaction

Requirements help

Source Code
function xfLobbyFlush() external {
  require(address(this).balance != 0, "HEX: No value");

  FLUSH_ADDR.transfer(address(this).balance);
}

xfLobbyRange keyboard_arrow_up

Parameters help

Name Type
beginDay
uint256 help
endDay
uint256 help

Properties

Visibility help public
Mutability help view
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

Parameters help

Name Type
memberAddr
address help
entryId
uint256 help

Properties

Visibility help public
Mutability help view

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

Parameters help

Name Type
memberAddr
address help

Properties

Visibility help public
Mutability help view
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;
}

constructor keyboard_arrow_up

Parameters help

This function has no parameters.

Properties

Visibility help public
Mutability help payable
Source Code
function() external payable {}

Internal Functions Expand All Collapse All

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

Parameters help

Name Type
enterDay
uint256 help

Properties

Visibility help private
Mutability help transaction
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

Name Type
enterDay
uint256 help
entryIndex
uint256 help
rawAmount
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction
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

Name Type
enterDay
uint256 help
entryIndex
uint256 help
xfAmount
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction
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

Name Type
rawSatoshis
uint256 help
claimToAddr
address help
btcAddr
bytes20 help
claimFlags
uint8 help
autoStakeDays
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction
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

Name Type
g
GlobalsCache help
rawSatoshis
uint256 help
claimToAddr
address help
btcAddr
bytes20 help
claimFlags
uint8 help
autoStakeDays
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction

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

Name Type
claimToAddr
address help
btcAddr
bytes20 help
claimFlags
uint8 help
rawSatoshis
uint256 help
adjSatoshis
uint256 help
claimedHearts
uint256 help
claimBonusHearts
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction

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

Name Type
claimToAddr
address help
btcAddr
bytes20 help
claimFlags
uint8 help
rawSatoshis
uint256 help
adjSatoshis
uint256 help
claimedHearts
uint256 help
referrerAddr
address help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
g
GlobalsCache help
rawSatoshis
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
rawSatoshis
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
adjSatoshis
uint256 help
daysRemaining
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
claimedHearts
uint256 help
daysRemaining
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
btcAddr
bytes20 help
rawSatoshis
uint256 help
proof
bytes32[] help

Properties

Visibility help internal
Mutability help pure

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

Parameters help

Name Type
merkleLeaf
bytes32 help
proof
bytes32[] help

Properties

Visibility help private
Mutability help pure
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

Name Type
claimToAddr
address help
claimParamHash
bytes32 help
claimFlags
uint8 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
addr
address help
includeAddrChecksum
bool help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
addrStr
bytes help
offset
uint256 help
hashNybble
uint8 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
hexStr
bytes help
data
bytes32 help
dataLen
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
data
bytes help

Properties

Visibility help private
Mutability help pure
Source Code
function _hash256(bytes memory data) private pure returns (bytes32) {
  return sha256(abi.encodePacked(sha256(data)));
}

internal UTXOClaimValidation._hash160 keyboard_arrow_up

Parameters help

Name Type
data
bytes help

Properties

Visibility help private
Mutability help pure
Source Code
function _hash160(bytes memory data) private pure returns (bytes20) {
  return ripemd160(abi.encodePacked(sha256(data)));
}

internal StakeableToken._stakeStart keyboard_arrow_up

Parameters help

Name Type
g
GlobalsCache help
newStakedHearts
uint256 help
newStakedDays
uint256 help
newAutoStake
bool help

Properties

Visibility help internal
Mutability help transaction
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

Name Type
g
GlobalsCache help
stakeSharesParam
uint256 help
beginDay
uint256 help
endDay
uint256 help

Properties

Visibility help private
Mutability help view
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

Parameters help

Name Type
newStakedHearts
uint256 help
newStakedDays
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
st
StakeCache help

Properties

Visibility help private
Mutability help pure
Source Code
function _stakeUnlock(GlobalsCache memory g, StakeCache memory st)
  private
  pure
{
  g._stakeSharesTotal -= st._stakeShares;
  st._unlockedDay = g._currentDay;
}

internal StakeableToken._stakePerformance keyboard_arrow_up

Parameters help

Name Type
g
GlobalsCache help
st
StakeCache help
servedDays
uint256 help

Properties

Visibility help private
Mutability help view
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

Name Type
g
GlobalsCache help
lockedDayParam
uint256 help
stakedDaysParam
uint256 help
servedDays
uint256 help
stakeSharesParam
uint256 help

Properties

Visibility help private
Mutability help view
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

Name Type
lockedDayParam
uint256 help
stakedDaysParam
uint256 help
unlockedDayParam
uint256 help
rawStakeReturn
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
penalty
uint256 help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
g
GlobalsCache help
st
StakeCache help
stakeReturn
uint256 help

Properties

Visibility help private
Mutability help transaction
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

Name Type
stakeId
uint40 help
stakedHearts
uint256 help
stakeShares
uint256 help
stakedDays
uint256 help
isAutoStake
bool help

Properties

Visibility help private
Mutability help transaction
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

Name Type
stakerAddr
address help
stakeId
uint40 help
stakedHearts
uint256 help
stakeShares
uint256 help
payout
uint256 help
penalty
uint256 help

Properties

Visibility help private
Mutability help transaction
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

Name Type
stakeId
uint40 help
stakedHearts
uint256 help
stakeShares
uint256 help
payout
uint256 help
penalty
uint256 help
servedDays
uint256 help
prevUnlocked
bool help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
shareRate
uint256 help
stakeId
uint40 help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

This function has no parameters.

Properties

Visibility help internal
Mutability help view
Source Code
function _currentDay() internal view returns (uint256) {
  return (block.timestamp - LAUNCH_TIME) / 1 days;
}

internal GlobalsAndUtility._dailyDataUpdateAuto keyboard_arrow_up

Parameters help

Name Type
g
GlobalsCache help

Properties

Visibility help internal
Mutability help transaction
Source Code
function _dailyDataUpdateAuto(GlobalsCache memory g) internal {
  _dailyDataUpdate(g, g._currentDay, true);
}

internal GlobalsAndUtility._globalsLoad keyboard_arrow_up

Parameters help

Name Type
g
GlobalsCache help
gSnapshot
GlobalsCache help

Properties

Visibility help internal
Mutability help view
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

Parameters help

Name Type
g
GlobalsCache help
gSnapshot
GlobalsCache help

Properties

Visibility help internal
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
gSnapshot
GlobalsCache help

Properties

Visibility help internal
Mutability help transaction
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

Parameters help

Name Type
stRef
StakeStore help
stakeIdParam
uint40 help
st
StakeCache help

Properties

Visibility help internal
Mutability help view

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

Parameters help

Name Type
stRef
StakeStore help
st
StakeCache help

Properties

Visibility help internal
Mutability help transaction
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

Name Type
stakeListRef
StakeStore[] help
newStakeId
uint40 help
newStakedHearts
uint256 help
newStakeShares
uint256 help
newLockedDay
uint256 help
newStakedDays
uint256 help
newAutoStake
bool help

Properties

Visibility help internal
Mutability help transaction
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

Parameters help

Name Type
stakeListRef
StakeStore[] help
stakeIndex
uint256 help

Properties

Visibility help internal
Mutability help transaction
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

Name Type
_claimedBtcAddrCount
uint256 help
_claimedSatoshisTotal
uint256 help
_unclaimedSatoshisTotal
uint256 help

Properties

Visibility help internal
Mutability help pure
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

Parameters help

Name Type
v
uint128 help

Properties

Visibility help internal
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
stakeSharesParam
uint256 help
day
uint256 help

Properties

Visibility help internal
Mutability help view
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

Parameters help

Name Type
g
GlobalsCache help
payout
uint256 help

Properties

Visibility help internal
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
rs
DailyRoundState help
day
uint256 help

Properties

Visibility help private
Mutability help pure
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

Parameters help

Name Type
g
GlobalsCache help
rs
DailyRoundState help
day
uint256 help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
g
GlobalsCache help
beforeDay
uint256 help
isAutoUpdate
bool help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
beginDay
uint256 help
endDay
uint256 help
isAutoUpdate
bool help

Properties

Visibility help private
Mutability help transaction
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

Parameters help

Name Type
sender
address help
recipient
address help
amount
uint256 help

Properties

Visibility help internal
Mutability help transaction

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

Parameters help

Name Type
account
address help
amount
uint256 help

Properties

Visibility help internal
Mutability help transaction

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

Parameters help

Name Type
account
address help
amount
uint256 help

Properties

Visibility help internal
Mutability help transaction

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

Parameters help

Name Type
owner
address help
spender
address help
amount
uint256 help

Properties

Visibility help internal
Mutability help transaction

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

Parameters help

Name Type
account
address help
amount
uint256 help

Properties

Visibility help internal
Mutability help transaction

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

Parameters help

This function has no parameters.

Properties

Visibility help internal
Mutability help transaction
Source Code
constructor() internal {}

internal Context._msgSender keyboard_arrow_up

Parameters help

This function has no parameters.

Properties

Visibility help internal
Mutability help view
Source Code
function _msgSender() internal view returns (address payable) {
  return msg.sender;
}

internal Context._msgData keyboard_arrow_up

Parameters help

This function has no parameters.

Properties

Visibility help internal
Mutability help view
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;
}