Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 13 from a total of 13 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Release | 16555826 | 586 days ago | IN | 0 ETH | 0.00139332 | ||||
Transfer | 15271659 | 771 days ago | IN | 0.00506449 ETH | 0.00030425 | ||||
Transfer | 14481857 | 898 days ago | IN | 0.0175025 ETH | 0.00110811 | ||||
Transfer | 14200255 | 942 days ago | IN | 0.0262385 ETH | 0.00191066 | ||||
Release | 14194698 | 943 days ago | IN | 0 ETH | 0.00295001 | ||||
Release | 14051969 | 965 days ago | IN | 0 ETH | 0.01890982 | ||||
Release | 14000818 | 973 days ago | IN | 0 ETH | 0.01781726 | ||||
Release | 14000551 | 973 days ago | IN | 0 ETH | 0.02159604 | ||||
Release | 13999922 | 973 days ago | IN | 0 ETH | 0.01217431 | ||||
Release | 13999398 | 973 days ago | IN | 0 ETH | 0.01171859 | ||||
Transfer | 13999152 | 973 days ago | IN | 0.01 ETH | 0.00362604 | ||||
Release | 13997149 | 973 days ago | IN | 0 ETH | 0.00981997 | ||||
Release | 13996041 | 973 days ago | IN | 0 ETH | 0.01863539 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18633334 | 294 days ago | 0.000025 ETH | ||||
18293405 | 342 days ago | 0.000125 ETH | ||||
18293395 | 342 days ago | 0.00012 ETH | ||||
17071331 | 513 days ago | 0.000049 ETH | ||||
17033620 | 519 days ago | 0.000125 ETH | ||||
17023899 | 520 days ago | 0.00003 ETH | ||||
16980395 | 526 days ago | 0.000125 ETH | ||||
16831644 | 547 days ago | 0.00003 ETH | ||||
16780512 | 555 days ago | 0.000275 ETH | ||||
16731349 | 561 days ago | 0.00005 ETH | ||||
16612592 | 578 days ago | 0.00025 ETH | ||||
16601900 | 580 days ago | 0.0002475 ETH | ||||
16555826 | 586 days ago | 0.01822664 ETH | ||||
16555826 | 586 days ago | 0.01822664 ETH | ||||
16555826 | 586 days ago | 0.01822664 ETH | ||||
16555826 | 586 days ago | 0.00607554 ETH | ||||
16445617 | 601 days ago | 0.000195 ETH | ||||
16436719 | 603 days ago | 0.0002225 ETH | ||||
16406207 | 607 days ago | 0.0002475 ETH | ||||
16378429 | 611 days ago | 0.000275 ETH | ||||
16360858 | 613 days ago | 0.0003 ETH | ||||
16122175 | 647 days ago | 0.0003 ETH | ||||
16031201 | 659 days ago | 0.000375 ETH | ||||
16007967 | 663 days ago | 0.0003 ETH | ||||
15871998 | 682 days ago | 0.0005 ETH |
Loading...
Loading
Contract Name:
TeamReferral
Compiler Version
v0.8.11+commit.d7f03943
Contract Source Code (Solidity Multiple files format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import "./ERC20.sol"; import "./WTF.sol"; import "./StakingRewards.sol"; contract FeeManager { WTF private wtf; constructor() { wtf = WTF(msg.sender); } function disburse() external { wtf.claimRewards(); uint256 _balance = wtf.balanceOf(address(this)); if (_balance > 0) { uint256 _oneFifth = _balance / 5; Treasury(payable(wtf.treasuryAddress())).collect(); wtf.transfer(wtf.treasuryAddress(), _oneFifth); // 20% StakingRewards(wtf.stakingRewardsAddress()).disburse(_oneFifth); // 20% StakingRewards(wtf.lpStakingRewardsAddress()).disburse(3 * _oneFifth); // 60% } } function wtfAddress() external view returns (address) { return address(wtf); } } contract TeamReferral { receive() external payable {} function release() external { address _this = address(this); require(_this.balance > 0); payable(0x6129E7bCb71C0d7D4580141C4E6a995f16293F42).transfer(_this.balance / 10); // 10% payable(0xc9AebdD8fD0d52c35A32fD9155467Cf28Ce474c3).transfer(_this.balance / 3); // 30% payable(0xdEE79eD62B42e30EA7EbB6f1b7A3f04143D18b7F).transfer(_this.balance / 2); // 30% payable(0x575446Aa9E9647C40edB7a467e45C5916add1538).transfer(_this.balance); // 30% } } contract Treasury { address public owner; uint256 public lockedUntil; WTF private wtf; modifier _onlyOwner() { require(msg.sender == owner); _; } constructor() { owner = 0x65dd4990719bE9B20322e4E8D3Bd77a4401a0357; lockedUntil = block.timestamp + 30 days; wtf = WTF(msg.sender); } receive() external payable {} function setOwner(address _owner) external _onlyOwner { owner = _owner; } function transferETH(address payable _destination, uint256 _amount) external _onlyOwner { require(isUnlocked()); _destination.transfer(_amount); } function transferTokens(ERC20 _token, address _destination, uint256 _amount) external _onlyOwner { require(isUnlocked()); _token.transfer(_destination, _amount); } function collect() external { wtf.claimRewards(); } function isUnlocked() public view returns (bool) { return block.timestamp > lockedUntil; } function wtfAddress() external view returns (address) { return address(wtf); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; interface ERC20 { function allowance(address, address) external view returns (uint256); function balanceOf(address) external view returns (uint256); function transfer(address, uint256) external returns (bool); function transferFrom(address, address, uint256) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import "./WTFNFT.sol"; interface PriceOracle { function getPrice() external view returns (uint256); } contract Metadata { string public name = "fees.wtf NFT"; string public symbol = "fees.wtf"; string constant private TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; WTFNFT public nft; PriceOracle public oracle; constructor(WTFNFT _nft) { nft = _nft; oracle = PriceOracle(0xe89b5B2770Aa1a6BcfAc6F3517510aB8e9146651); } function setPriceOracle(PriceOracle _oracle) external { require(msg.sender == nft.owner()); oracle = _oracle; } function tokenURI(uint256 _tokenId) external view returns (string memory) { ( , , address _user, uint256[7] memory _info) = nft.getToken(_tokenId); return rawTokenURI(_user, _info[0], _info[1], _info[2], _info[3], _info[4], _info[5], _info[6], oracle.getPrice()); } function rawTokenURI(address _user, uint256 _totalFees, uint256 _failFees, uint256 _totalGas, uint256 _avgGwei, uint256 _totalDonated, uint256 _totalTxs, uint256 _failTxs, uint256 _price) public pure returns (string memory) { string memory _json = string(abi.encodePacked('{"name":"', _trimAddress(_user, 6), '","description":"[fees.wtf](https://fees.wtf) snapshot at block 13916450 for [', _address2str(_user), '](https://etherscan.io/address/', _address2str(_user), ')",')); _json = string(abi.encodePacked(_json, '"image":"data:image/svg+xml;base64,', _encode(bytes(getRawSVG(_totalFees, _failFees, _totalGas, _avgGwei, _totalDonated, _totalTxs, _failTxs, _price))), '","attributes":[')); if (_totalFees > 0) { _json = string(abi.encodePacked(_json, '{"trait_type":"Total Fees","value":', _uint2str(_totalFees, 18, 5, false, true), '}')); _json = string(abi.encodePacked(_json, ',{"trait_type":"Fail Fees","value":', _uint2str(_failFees, 18, 5, false, true), '}')); _json = string(abi.encodePacked(_json, ',{"trait_type":"Total Gas","value":', _uint2str(_totalGas, 0, 0, false, false), '}')); _json = string(abi.encodePacked(_json, ',{"trait_type":"Average Gwei","value":', _uint2str(_avgGwei, 9, 5, false, true), '}')); _json = string(abi.encodePacked(_json, ',{"trait_type":"Total Transactions","value":', _uint2str(_totalTxs, 0, 0, false, false), '}')); _json = string(abi.encodePacked(_json, ',{"trait_type":"Failed Transactions","value":', _uint2str(_failTxs, 0, 0, false, false), '}')); _json = string(abi.encodePacked(_json, ',{"display_type":"number","trait_type":"Spender Level","value":', _uint2str(_logn(_totalFees / 1e13, 2), 0, 0, false, false), '}')); _json = string(abi.encodePacked(_json, ',{"display_type":"number","trait_type":"Oof Level","value":', _uint2str(_logn(_failFees / 1e13, 2), 0, 0, false, false), '}')); } if (_totalDonated > 0) { _json = string(abi.encodePacked(_json, _totalFees > 0 ? ',' : '', '{"display_type":"number","trait_type":"Donator Level","value":', _uint2str(_logn(_totalDonated / 1e14, 10) + 1, 0, 0, false, false), '}')); } _json = string(abi.encodePacked(_json, ']}')); return string(abi.encodePacked("data:application/json;base64,", _encode(bytes(_json)))); } function getSVG(uint256 _tokenId) public view returns (string memory) { uint256[7] memory _info = nft.getTokenCompressedInfo(_tokenId); return getRawSVG(_info[0], _info[1], _info[2], _info[3], _info[4], _info[5], _info[6], oracle.getPrice()); } function getRawSVG(uint256 _totalFees, uint256 _failFees, uint256 _totalGas, uint256 _avgGwei, uint256 _totalDonated, uint256 _totalTxs, uint256 _failTxs, uint256 _price) public pure returns (string memory svg) { svg = string(abi.encodePacked("<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='xMidYMid meet' viewBox='0 0 512 512' width='100%' height='100%'>")); svg = string(abi.encodePacked(svg, "<defs><style type='text/css'>text{text-anchor:middle;alignment-baseline:central;}tspan>tspan{fill:#03a9f4;font-weight:700;}</style></defs>")); svg = string(abi.encodePacked(svg, "<rect width='100%' height='100%' fill='#222222' />")); svg = string(abi.encodePacked(svg, "<text x='0' y='256' transform='translate(256)' fill='#f0f8ff' font-family='Arial,sans-serif' font-weight='600' font-size='30'>")); if (_totalFees > 0) { svg = string(abi.encodePacked(svg, unicode"<tspan x='0' dy='-183'>You spent <tspan>Ξ", _uint2str(_totalFees, 18, 5, true, false), "</tspan> on gas</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>before block 13916450.</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>Right now, that's</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'><tspan>$", _uint2str(_totalFees * _price / 1e18, 18, 2, true, true), "</tspan>.</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='70'>You used <tspan>", _uint2str(_totalGas, 0, 0, true, false), "</tspan></tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>gas to send <tspan>", _uint2str(_totalTxs, 0, 0, true, false), "</tspan></tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>transaction", _totalTxs == 1 ? "" : "s", ", with an average</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>price of <tspan>", _uint2str(_avgGwei, 9, 3, true, false), "</tspan> Gwei.</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='70'><tspan>", _uint2str(_failTxs, 0, 0, true, false), "</tspan> of them failed,</tspan>")); svg = string(abi.encodePacked(svg, "<tspan x='0' dy='35'>costing you <tspan>", _failFees == 0 ? "nothing" : string(abi.encodePacked(unicode"Ξ", _uint2str(_failFees, 18, 5, true, false))), "</tspan>.</tspan></text>")); } else { svg = string(abi.encodePacked(svg, "<tspan x='0' dy='8'>Did not qualify.</tspan></text>")); } if (_totalDonated > 0) { for (uint256 i = 0; i <= _logn(_totalDonated / 1e14, 10); i++) { for (uint256 j = 0; j < 4; j++) { string memory _prefix = string(abi.encodePacked("<text x='", j < 2 ? "16" : "496", "' y='", j % 2 == 0 ? "18" : "498", "' font-size='10' transform='translate(")); svg = string(abi.encodePacked(svg, _prefix, j < 2 ? "" : "-", _uint2str(16 * i, 0, 0, false, false), ")'>", unicode"❤️</text>")); if (i > 0) { svg = string(abi.encodePacked(svg, _prefix, "0,", j % 2 == 0 ? "" : "-", _uint2str(16 * i, 0, 0, false, false), ")'>", unicode"❤️</text>")); } } } } svg = string(abi.encodePacked(svg, "<text x='0' y='500' transform='translate(256)' fill='#f0f8ff' font-family='Arial,sans-serif' font-weight='600' font-size='10'><tspan>fees<tspan>.wtf</tspan></tspan></text></svg>")); } function _logn(uint256 _num, uint256 _n) internal pure returns (uint256) { require(_n > 0); uint256 _count = 0; while (_num > _n - 1) { _num /= _n; _count++; } return _count; } function _address2str(address _address) internal pure returns (string memory str) { str = "0x"; for (uint256 i; i < 40; i++) { uint256 _hex = (uint160(_address) >> (4 * (39 - i))) % 16; bytes memory _char = new bytes(1); _char[0] = bytes1(uint8(_hex) + (_hex > 9 ? 87 : 48)); str = string(abi.encodePacked(str, string(_char))); } } function _trimAddress(address _address, uint256 _padding) internal pure returns (string memory str) { require(_padding < 20); str = ""; bytes memory _strAddress = bytes(_address2str(_address)); uint256 _length = 2 * _padding + 2; for (uint256 i = 0; i < 2 * _padding + 2; i++) { bytes memory _char = new bytes(1); _char[0] = _strAddress[i < _padding + 2 ? i : 42 + i - _length]; str = string(abi.encodePacked(str, string(_char))); if (i == _padding + 1) { str = string(abi.encodePacked(str, unicode"…")); } } } function _uint2str(uint256 _value, uint256 _scale, uint256 _maxDecimals, bool _commas, bool _full) internal pure returns (string memory str) { uint256 _d = _scale > _maxDecimals ? _maxDecimals : _scale; uint256 _n = _value / 10**(_scale > _d ? _scale - _d : 0); if (_n == 0) { return "0"; } uint256 _digits = 1; uint256 _tmp = _n; while (_tmp > 9) { _tmp /= 10; _digits++; } _tmp = _digits > _d ? _digits : _d + 1; uint256 _offset = (!_full && _tmp > _d + 1 ? _tmp - _d - 1 > _d ? _d : _tmp - _d - 1 : 0); for (uint256 i = 0; i < _tmp - _offset; i++) { uint256 _dec = i < _tmp - _digits ? 0 : (_n / (10**(_tmp - i - 1))) % 10; bytes memory _char = new bytes(1); _char[0] = bytes1(uint8(_dec) + 48); str = string(abi.encodePacked(str, string(_char))); if (i < _tmp - _d - 1) { if (_commas && (i + 1) % 3 == (_tmp - _d) % 3) { str = string(abi.encodePacked(str, ",")); } } else { if (!_full && (_n / 10**_offset) % 10**(_tmp - _offset - i - 1) == 0) { break; } else if (i == _tmp - _d - 1) { str = string(abi.encodePacked(str, ".")); } } } } function _encode(bytes memory _data) internal pure returns (string memory result) { if (_data.length == 0) return ''; string memory _table = TABLE; uint256 _encodedLen = 4 * ((_data.length + 2) / 3); result = new string(_encodedLen + 32); assembly { mstore(result, _encodedLen) let tablePtr := add(_table, 1) let dataPtr := _data let endPtr := add(dataPtr, mload(_data)) let resultPtr := add(result, 32) for {} lt(dataPtr, endPtr) {} { dataPtr := add(dataPtr, 3) let input := mload(dataPtr) mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr( 6, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(248, mload(add(tablePtr, and( input, 0x3F))))) resultPtr := add(resultPtr, 1) } switch mod(mload(_data), 3) case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } } return result; } }
// SPDX-License-Identifier: Unlicense pragma solidity >=0.8.4; /// @notice Emitted when the result overflows uint256. error PRBMath__MulDivFixedPointOverflow(uint256 prod1); /// @notice Emitted when the result overflows uint256. error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator); /// @notice Emitted when one of the inputs is type(int256).min. error PRBMath__MulDivSignedInputTooSmall(); /// @notice Emitted when the intermediary absolute result overflows int256. error PRBMath__MulDivSignedOverflow(uint256 rAbs); /// @notice Emitted when the input is MIN_SD59x18. error PRBMathSD59x18__AbsInputTooSmall(); /// @notice Emitted when ceiling a number overflows SD59x18. error PRBMathSD59x18__CeilOverflow(int256 x); /// @notice Emitted when one of the inputs is MIN_SD59x18. error PRBMathSD59x18__DivInputTooSmall(); /// @notice Emitted when one of the intermediary unsigned results overflows SD59x18. error PRBMathSD59x18__DivOverflow(uint256 rAbs); /// @notice Emitted when the input is greater than 133.084258667509499441. error PRBMathSD59x18__ExpInputTooBig(int256 x); /// @notice Emitted when the input is greater than 192. error PRBMathSD59x18__Exp2InputTooBig(int256 x); /// @notice Emitted when flooring a number underflows SD59x18. error PRBMathSD59x18__FloorUnderflow(int256 x); /// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18. error PRBMathSD59x18__FromIntOverflow(int256 x); /// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18. error PRBMathSD59x18__FromIntUnderflow(int256 x); /// @notice Emitted when the product of the inputs is negative. error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y); /// @notice Emitted when multiplying the inputs overflows SD59x18. error PRBMathSD59x18__GmOverflow(int256 x, int256 y); /// @notice Emitted when the input is less than or equal to zero. error PRBMathSD59x18__LogInputTooSmall(int256 x); /// @notice Emitted when one of the inputs is MIN_SD59x18. error PRBMathSD59x18__MulInputTooSmall(); /// @notice Emitted when the intermediary absolute result overflows SD59x18. error PRBMathSD59x18__MulOverflow(uint256 rAbs); /// @notice Emitted when the intermediary absolute result overflows SD59x18. error PRBMathSD59x18__PowuOverflow(uint256 rAbs); /// @notice Emitted when the input is negative. error PRBMathSD59x18__SqrtNegativeInput(int256 x); /// @notice Emitted when the calculating the square root overflows SD59x18. error PRBMathSD59x18__SqrtOverflow(int256 x); /// @notice Emitted when addition overflows UD60x18. error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y); /// @notice Emitted when ceiling a number overflows UD60x18. error PRBMathUD60x18__CeilOverflow(uint256 x); /// @notice Emitted when the input is greater than 133.084258667509499441. error PRBMathUD60x18__ExpInputTooBig(uint256 x); /// @notice Emitted when the input is greater than 192. error PRBMathUD60x18__Exp2InputTooBig(uint256 x); /// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18. error PRBMathUD60x18__FromUintOverflow(uint256 x); /// @notice Emitted when multiplying the inputs overflows UD60x18. error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y); /// @notice Emitted when the input is less than 1. error PRBMathUD60x18__LogInputTooSmall(uint256 x); /// @notice Emitted when the calculating the square root overflows UD60x18. error PRBMathUD60x18__SqrtOverflow(uint256 x); /// @notice Emitted when subtraction underflows UD60x18. error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y); /// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library /// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point /// representation. When it does not, it is explicitly mentioned in the NatSpec documentation. library PRBMath { /// STRUCTS /// struct SD59x18 { int256 value; } struct UD60x18 { uint256 value; } /// STORAGE /// /// @dev How many trailing decimals can be represented. uint256 internal constant SCALE = 1e18; /// @dev Largest power of two divisor of SCALE. uint256 internal constant SCALE_LPOTD = 262144; /// @dev SCALE inverted mod 2^256. uint256 internal constant SCALE_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281; /// FUNCTIONS /// /// @notice Calculates the binary exponent of x using the binary fraction method. /// @dev Has to use 192.64-bit fixed-point numbers. /// See https://ethereum.stackexchange.com/a/96594/24693. /// @param x The exponent as an unsigned 192.64-bit fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function exp2(uint256 x) internal pure returns (uint256 result) { unchecked { // Start from 0.5 in the 192.64-bit fixed-point format. result = 0x800000000000000000000000000000000000000000000000; // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows // because the initial result is 2^191 and all magic factors are less than 2^65. if (x & 0x8000000000000000 > 0) { result = (result * 0x16A09E667F3BCC909) >> 64; } if (x & 0x4000000000000000 > 0) { result = (result * 0x1306FE0A31B7152DF) >> 64; } if (x & 0x2000000000000000 > 0) { result = (result * 0x1172B83C7D517ADCE) >> 64; } if (x & 0x1000000000000000 > 0) { result = (result * 0x10B5586CF9890F62A) >> 64; } if (x & 0x800000000000000 > 0) { result = (result * 0x1059B0D31585743AE) >> 64; } if (x & 0x400000000000000 > 0) { result = (result * 0x102C9A3E778060EE7) >> 64; } if (x & 0x200000000000000 > 0) { result = (result * 0x10163DA9FB33356D8) >> 64; } if (x & 0x100000000000000 > 0) { result = (result * 0x100B1AFA5ABCBED61) >> 64; } if (x & 0x80000000000000 > 0) { result = (result * 0x10058C86DA1C09EA2) >> 64; } if (x & 0x40000000000000 > 0) { result = (result * 0x1002C605E2E8CEC50) >> 64; } if (x & 0x20000000000000 > 0) { result = (result * 0x100162F3904051FA1) >> 64; } if (x & 0x10000000000000 > 0) { result = (result * 0x1000B175EFFDC76BA) >> 64; } if (x & 0x8000000000000 > 0) { result = (result * 0x100058BA01FB9F96D) >> 64; } if (x & 0x4000000000000 > 0) { result = (result * 0x10002C5CC37DA9492) >> 64; } if (x & 0x2000000000000 > 0) { result = (result * 0x1000162E525EE0547) >> 64; } if (x & 0x1000000000000 > 0) { result = (result * 0x10000B17255775C04) >> 64; } if (x & 0x800000000000 > 0) { result = (result * 0x1000058B91B5BC9AE) >> 64; } if (x & 0x400000000000 > 0) { result = (result * 0x100002C5C89D5EC6D) >> 64; } if (x & 0x200000000000 > 0) { result = (result * 0x10000162E43F4F831) >> 64; } if (x & 0x100000000000 > 0) { result = (result * 0x100000B1721BCFC9A) >> 64; } if (x & 0x80000000000 > 0) { result = (result * 0x10000058B90CF1E6E) >> 64; } if (x & 0x40000000000 > 0) { result = (result * 0x1000002C5C863B73F) >> 64; } if (x & 0x20000000000 > 0) { result = (result * 0x100000162E430E5A2) >> 64; } if (x & 0x10000000000 > 0) { result = (result * 0x1000000B172183551) >> 64; } if (x & 0x8000000000 > 0) { result = (result * 0x100000058B90C0B49) >> 64; } if (x & 0x4000000000 > 0) { result = (result * 0x10000002C5C8601CC) >> 64; } if (x & 0x2000000000 > 0) { result = (result * 0x1000000162E42FFF0) >> 64; } if (x & 0x1000000000 > 0) { result = (result * 0x10000000B17217FBB) >> 64; } if (x & 0x800000000 > 0) { result = (result * 0x1000000058B90BFCE) >> 64; } if (x & 0x400000000 > 0) { result = (result * 0x100000002C5C85FE3) >> 64; } if (x & 0x200000000 > 0) { result = (result * 0x10000000162E42FF1) >> 64; } if (x & 0x100000000 > 0) { result = (result * 0x100000000B17217F8) >> 64; } if (x & 0x80000000 > 0) { result = (result * 0x10000000058B90BFC) >> 64; } if (x & 0x40000000 > 0) { result = (result * 0x1000000002C5C85FE) >> 64; } if (x & 0x20000000 > 0) { result = (result * 0x100000000162E42FF) >> 64; } if (x & 0x10000000 > 0) { result = (result * 0x1000000000B17217F) >> 64; } if (x & 0x8000000 > 0) { result = (result * 0x100000000058B90C0) >> 64; } if (x & 0x4000000 > 0) { result = (result * 0x10000000002C5C860) >> 64; } if (x & 0x2000000 > 0) { result = (result * 0x1000000000162E430) >> 64; } if (x & 0x1000000 > 0) { result = (result * 0x10000000000B17218) >> 64; } if (x & 0x800000 > 0) { result = (result * 0x1000000000058B90C) >> 64; } if (x & 0x400000 > 0) { result = (result * 0x100000000002C5C86) >> 64; } if (x & 0x200000 > 0) { result = (result * 0x10000000000162E43) >> 64; } if (x & 0x100000 > 0) { result = (result * 0x100000000000B1721) >> 64; } if (x & 0x80000 > 0) { result = (result * 0x10000000000058B91) >> 64; } if (x & 0x40000 > 0) { result = (result * 0x1000000000002C5C8) >> 64; } if (x & 0x20000 > 0) { result = (result * 0x100000000000162E4) >> 64; } if (x & 0x10000 > 0) { result = (result * 0x1000000000000B172) >> 64; } if (x & 0x8000 > 0) { result = (result * 0x100000000000058B9) >> 64; } if (x & 0x4000 > 0) { result = (result * 0x10000000000002C5D) >> 64; } if (x & 0x2000 > 0) { result = (result * 0x1000000000000162E) >> 64; } if (x & 0x1000 > 0) { result = (result * 0x10000000000000B17) >> 64; } if (x & 0x800 > 0) { result = (result * 0x1000000000000058C) >> 64; } if (x & 0x400 > 0) { result = (result * 0x100000000000002C6) >> 64; } if (x & 0x200 > 0) { result = (result * 0x10000000000000163) >> 64; } if (x & 0x100 > 0) { result = (result * 0x100000000000000B1) >> 64; } if (x & 0x80 > 0) { result = (result * 0x10000000000000059) >> 64; } if (x & 0x40 > 0) { result = (result * 0x1000000000000002C) >> 64; } if (x & 0x20 > 0) { result = (result * 0x10000000000000016) >> 64; } if (x & 0x10 > 0) { result = (result * 0x1000000000000000B) >> 64; } if (x & 0x8 > 0) { result = (result * 0x10000000000000006) >> 64; } if (x & 0x4 > 0) { result = (result * 0x10000000000000003) >> 64; } if (x & 0x2 > 0) { result = (result * 0x10000000000000001) >> 64; } if (x & 0x1 > 0) { result = (result * 0x10000000000000001) >> 64; } // We're doing two things at the same time: // // 1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for // the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191 // rather than 192. // 2. Convert the result to the unsigned 60.18-decimal fixed-point format. // // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n". result *= SCALE; result >>= (191 - (x >> 64)); } } /// @notice Finds the zero-based index of the first one in the binary representation of x. /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set /// @param x The uint256 number for which to find the index of the most significant bit. /// @return msb The index of the most significant bit as an uint256. function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) { if (x >= 2**128) { x >>= 128; msb += 128; } if (x >= 2**64) { x >>= 64; msb += 64; } if (x >= 2**32) { x >>= 32; msb += 32; } if (x >= 2**16) { x >>= 16; msb += 16; } if (x >= 2**8) { x >>= 8; msb += 8; } if (x >= 2**4) { x >>= 4; msb += 4; } if (x >= 2**2) { x >>= 2; msb += 2; } if (x >= 2**1) { // No need to shift x any more. msb += 1; } } /// @notice Calculates floor(x*y÷denominator) with full precision. /// /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv. /// /// Requirements: /// - The denominator cannot be zero. /// - The result must fit within uint256. /// /// Caveats: /// - This function does not work with fixed-point numbers. /// /// @param x The multiplicand as an uint256. /// @param y The multiplier as an uint256. /// @param denominator The divisor as an uint256. /// @return result The result as an uint256. function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { unchecked { result = prod0 / denominator; } return result; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (prod1 >= denominator) { revert PRBMath__MulDivOverflow(prod1, denominator); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. unchecked { // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 lpotdod = denominator & (~denominator + 1); assembly { // Divide denominator by lpotdod. denominator := div(denominator, lpotdod) // Divide [prod1 prod0] by lpotdod. prod0 := div(prod0, lpotdod) // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one. lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * lpotdod; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /// @notice Calculates floor(x*y÷1e18) with full precision. /// /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of /// being rounded to 1e-18. See "Listing 6" and text above it at https://accu.org/index.php/journals/1717. /// /// Requirements: /// - The result must fit within uint256. /// /// Caveats: /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works. /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations: /// 1. x * y = type(uint256).max * SCALE /// 2. (x * y) % SCALE >= SCALE / 2 /// /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number. /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) { uint256 prod0; uint256 prod1; assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } if (prod1 >= SCALE) { revert PRBMath__MulDivFixedPointOverflow(prod1); } uint256 remainder; uint256 roundUpUnit; assembly { remainder := mulmod(x, y, SCALE) roundUpUnit := gt(remainder, 499999999999999999) } if (prod1 == 0) { unchecked { result = (prod0 / SCALE) + roundUpUnit; return result; } } assembly { result := add( mul( or( div(sub(prod0, remainder), SCALE_LPOTD), mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1)) ), SCALE_INVERSE ), roundUpUnit ) } } /// @notice Calculates floor(x*y÷denominator) with full precision. /// /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately. /// /// Requirements: /// - None of the inputs can be type(int256).min. /// - The result must fit within int256. /// /// @param x The multiplicand as an int256. /// @param y The multiplier as an int256. /// @param denominator The divisor as an int256. /// @return result The result as an int256. function mulDivSigned( int256 x, int256 y, int256 denominator ) internal pure returns (int256 result) { if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) { revert PRBMath__MulDivSignedInputTooSmall(); } // Get hold of the absolute values of x, y and the denominator. uint256 ax; uint256 ay; uint256 ad; unchecked { ax = x < 0 ? uint256(-x) : uint256(x); ay = y < 0 ? uint256(-y) : uint256(y); ad = denominator < 0 ? uint256(-denominator) : uint256(denominator); } // Compute the absolute value of (x*y)÷denominator. The result must fit within int256. uint256 rAbs = mulDiv(ax, ay, ad); if (rAbs > uint256(type(int256).max)) { revert PRBMath__MulDivSignedOverflow(rAbs); } // Get the signs of x, y and the denominator. uint256 sx; uint256 sy; uint256 sd; assembly { sx := sgt(x, sub(0, 1)) sy := sgt(y, sub(0, 1)) sd := sgt(denominator, sub(0, 1)) } // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs. // If yes, the result should be negative. result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs); } /// @notice Calculates the square root of x, rounding down. /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. /// /// Caveats: /// - This function does not work with fixed-point numbers. /// /// @param x The uint256 number for which to calculate the square root. /// @return result The result as an uint256. function sqrt(uint256 x) internal pure returns (uint256 result) { if (x == 0) { return 0; } // Set the initial guess to the least power of two that is greater than or equal to sqrt(x). uint256 xAux = uint256(x); result = 1; if (xAux >= 0x100000000000000000000000000000000) { xAux >>= 128; result <<= 64; } if (xAux >= 0x10000000000000000) { xAux >>= 64; result <<= 32; } if (xAux >= 0x100000000) { xAux >>= 32; result <<= 16; } if (xAux >= 0x10000) { xAux >>= 16; result <<= 8; } if (xAux >= 0x100) { xAux >>= 8; result <<= 4; } if (xAux >= 0x10) { xAux >>= 4; result <<= 2; } if (xAux >= 0x8) { result <<= 1; } // The operations can never overflow because the result is max 2^127 when it enters this block. unchecked { result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; result = (result + x / result) >> 1; // Seven iterations should be enough uint256 roundedDownResult = x / result; return result >= roundedDownResult ? roundedDownResult : result; } } }
// SPDX-License-Identifier: Unlicense pragma solidity >=0.8.4; import "./PRBMath.sol"; /// @title PRBMathUD60x18 /// @author Paul Razvan Berg /// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18 /// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60 /// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the /// maximum values permitted by the Solidity type uint256. library PRBMathUD60x18 { /// @dev Half the SCALE number. uint256 internal constant HALF_SCALE = 5e17; /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number. uint256 internal constant LOG2_E = 1_442695040888963407; /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have. uint256 internal constant MAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935; /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have. uint256 internal constant MAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000; /// @dev How many trailing decimals can be represented. uint256 internal constant SCALE = 1e18; /// @notice Calculates the arithmetic average of x and y, rounding down. /// @param x The first operand as an unsigned 60.18-decimal fixed-point number. /// @param y The second operand as an unsigned 60.18-decimal fixed-point number. /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number. function avg(uint256 x, uint256 y) internal pure returns (uint256 result) { // The operations can never overflow. unchecked { // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice. result = (x >> 1) + (y >> 1) + (x & y & 1); } } /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x. /// /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// /// Requirements: /// - x must be less than or equal to MAX_WHOLE_UD60x18. /// /// @param x The unsigned 60.18-decimal fixed-point number to ceil. /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number. function ceil(uint256 x) internal pure returns (uint256 result) { if (x > MAX_WHOLE_UD60x18) { revert PRBMathUD60x18__CeilOverflow(x); } assembly { // Equivalent to "x % SCALE" but faster. let remainder := mod(x, SCALE) // Equivalent to "SCALE - remainder" but faster. let delta := sub(SCALE, remainder) // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster. result := add(x, mul(delta, gt(remainder, 0))) } } /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number. /// /// @dev Uses mulDiv to enable overflow-safe multiplication and division. /// /// Requirements: /// - The denominator cannot be zero. /// /// @param x The numerator as an unsigned 60.18-decimal fixed-point number. /// @param y The denominator as an unsigned 60.18-decimal fixed-point number. /// @param result The quotient as an unsigned 60.18-decimal fixed-point number. function div(uint256 x, uint256 y) internal pure returns (uint256 result) { result = PRBMath.mulDiv(x, SCALE, y); } /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number. /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant). function e() internal pure returns (uint256 result) { result = 2_718281828459045235; } /// @notice Calculates the natural exponent of x. /// /// @dev Based on the insight that e^x = 2^(x * log2(e)). /// /// Requirements: /// - All from "log2". /// - x must be less than 133.084258667509499441. /// /// @param x The exponent as an unsigned 60.18-decimal fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function exp(uint256 x) internal pure returns (uint256 result) { // Without this check, the value passed to "exp2" would be greater than 192. if (x >= 133_084258667509499441) { revert PRBMathUD60x18__ExpInputTooBig(x); } // Do the fixed-point multiplication inline to save gas. unchecked { uint256 doubleScaleProduct = x * LOG2_E; result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE); } } /// @notice Calculates the binary exponent of x using the binary fraction method. /// /// @dev See https://ethereum.stackexchange.com/q/79903/24693. /// /// Requirements: /// - x must be 192 or less. /// - The result must fit within MAX_UD60x18. /// /// @param x The exponent as an unsigned 60.18-decimal fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function exp2(uint256 x) internal pure returns (uint256 result) { // 2^192 doesn't fit within the 192.64-bit format used internally in this function. if (x >= 192e18) { revert PRBMathUD60x18__Exp2InputTooBig(x); } unchecked { // Convert x to the 192.64-bit fixed-point format. uint256 x192x64 = (x << 64) / SCALE; // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation. result = PRBMath.exp2(x192x64); } } /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x. /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. /// @param x The unsigned 60.18-decimal fixed-point number to floor. /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number. function floor(uint256 x) internal pure returns (uint256 result) { assembly { // Equivalent to "x % SCALE" but faster. let remainder := mod(x, SCALE) // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster. result := sub(x, mul(remainder, gt(remainder, 0))) } } /// @notice Yields the excess beyond the floor of x. /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part. /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of. /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number. function frac(uint256 x) internal pure returns (uint256 result) { assembly { result := mod(x, SCALE) } } /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation. /// /// @dev Requirements: /// - x must be less than or equal to MAX_UD60x18 divided by SCALE. /// /// @param x The basic integer to convert. /// @param result The same number in unsigned 60.18-decimal fixed-point representation. function fromUint(uint256 x) internal pure returns (uint256 result) { unchecked { if (x > MAX_UD60x18 / SCALE) { revert PRBMathUD60x18__FromUintOverflow(x); } result = x * SCALE; } } /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down. /// /// @dev Requirements: /// - x * y must fit within MAX_UD60x18, lest it overflows. /// /// @param x The first operand as an unsigned 60.18-decimal fixed-point number. /// @param y The second operand as an unsigned 60.18-decimal fixed-point number. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function gm(uint256 x, uint256 y) internal pure returns (uint256 result) { if (x == 0) { return 0; } unchecked { // Checking for overflow this way is faster than letting Solidity do it. uint256 xy = x * y; if (xy / x != y) { revert PRBMathUD60x18__GmOverflow(x, y); } // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE // during multiplication. See the comments within the "sqrt" function. result = PRBMath.sqrt(xy); } } /// @notice Calculates 1 / x, rounding toward zero. /// /// @dev Requirements: /// - x cannot be zero. /// /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse. /// @return result The inverse as an unsigned 60.18-decimal fixed-point number. function inv(uint256 x) internal pure returns (uint256 result) { unchecked { // 1e36 is SCALE * SCALE. result = 1e36 / x; } } /// @notice Calculates the natural logarithm of x. /// /// @dev Based on the insight that ln(x) = log2(x) / log2(e). /// /// Requirements: /// - All from "log2". /// /// Caveats: /// - All from "log2". /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision. /// /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm. /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number. function ln(uint256 x) internal pure returns (uint256 result) { // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x) // can return is 196205294292027477728. unchecked { result = (log2(x) * SCALE) / LOG2_E; } } /// @notice Calculates the common logarithm of x. /// /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common /// logarithm based on the insight that log10(x) = log2(x) / log2(10). /// /// Requirements: /// - All from "log2". /// /// Caveats: /// - All from "log2". /// /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm. /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number. function log10(uint256 x) internal pure returns (uint256 result) { if (x < SCALE) { revert PRBMathUD60x18__LogInputTooSmall(x); } // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined // in this contract. // prettier-ignore assembly { switch x case 1 { result := mul(SCALE, sub(0, 18)) } case 10 { result := mul(SCALE, sub(1, 18)) } case 100 { result := mul(SCALE, sub(2, 18)) } case 1000 { result := mul(SCALE, sub(3, 18)) } case 10000 { result := mul(SCALE, sub(4, 18)) } case 100000 { result := mul(SCALE, sub(5, 18)) } case 1000000 { result := mul(SCALE, sub(6, 18)) } case 10000000 { result := mul(SCALE, sub(7, 18)) } case 100000000 { result := mul(SCALE, sub(8, 18)) } case 1000000000 { result := mul(SCALE, sub(9, 18)) } case 10000000000 { result := mul(SCALE, sub(10, 18)) } case 100000000000 { result := mul(SCALE, sub(11, 18)) } case 1000000000000 { result := mul(SCALE, sub(12, 18)) } case 10000000000000 { result := mul(SCALE, sub(13, 18)) } case 100000000000000 { result := mul(SCALE, sub(14, 18)) } case 1000000000000000 { result := mul(SCALE, sub(15, 18)) } case 10000000000000000 { result := mul(SCALE, sub(16, 18)) } case 100000000000000000 { result := mul(SCALE, sub(17, 18)) } case 1000000000000000000 { result := 0 } case 10000000000000000000 { result := SCALE } case 100000000000000000000 { result := mul(SCALE, 2) } case 1000000000000000000000 { result := mul(SCALE, 3) } case 10000000000000000000000 { result := mul(SCALE, 4) } case 100000000000000000000000 { result := mul(SCALE, 5) } case 1000000000000000000000000 { result := mul(SCALE, 6) } case 10000000000000000000000000 { result := mul(SCALE, 7) } case 100000000000000000000000000 { result := mul(SCALE, 8) } case 1000000000000000000000000000 { result := mul(SCALE, 9) } case 10000000000000000000000000000 { result := mul(SCALE, 10) } case 100000000000000000000000000000 { result := mul(SCALE, 11) } case 1000000000000000000000000000000 { result := mul(SCALE, 12) } case 10000000000000000000000000000000 { result := mul(SCALE, 13) } case 100000000000000000000000000000000 { result := mul(SCALE, 14) } case 1000000000000000000000000000000000 { result := mul(SCALE, 15) } case 10000000000000000000000000000000000 { result := mul(SCALE, 16) } case 100000000000000000000000000000000000 { result := mul(SCALE, 17) } case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) } case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) } case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) } case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) } case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) } case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) } case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) } case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) } case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) } case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) } case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) } case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) } case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) } case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) } case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) } case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) } case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) } case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) } case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) } case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) } case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) } case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) } case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) } case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) } case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) } case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) } case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) } case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) } case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) } case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) } case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) } case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) } case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) } case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) } case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) } case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) } case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) } case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) } case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) } case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) } case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) } case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) } default { result := MAX_UD60x18 } } if (result == MAX_UD60x18) { // Do the fixed-point division inline to save gas. The denominator is log2(10). unchecked { result = (log2(x) * SCALE) / 3_321928094887362347; } } } /// @notice Calculates the binary logarithm of x. /// /// @dev Based on the iterative approximation algorithm. /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation /// /// Requirements: /// - x must be greater than or equal to SCALE, otherwise the result would be negative. /// /// Caveats: /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation. /// /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm. /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number. function log2(uint256 x) internal pure returns (uint256 result) { if (x < SCALE) { revert PRBMathUD60x18__LogInputTooSmall(x); } unchecked { // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). uint256 n = PRBMath.mostSignificantBit(x / SCALE); // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow // because n is maximum 255 and SCALE is 1e18. result = n * SCALE; // This is y = x * 2^(-n). uint256 y = x >> n; // If y = 1, the fractional part is zero. if (y == SCALE) { return result; } // Calculate the fractional part via the iterative approximation. // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) { y = (y * y) / SCALE; // Is y^2 > 2 and so in the range [2,4)? if (y >= 2 * SCALE) { // Add the 2^(-m) factor to the logarithm. result += delta; // Corresponds to z/2 on Wikipedia. y >>= 1; } } } } /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal /// fixed-point number. /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function. /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number. /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number. /// @return result The product as an unsigned 60.18-decimal fixed-point number. function mul(uint256 x, uint256 y) internal pure returns (uint256 result) { result = PRBMath.mulDivFixedPoint(x, y); } /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number. function pi() internal pure returns (uint256 result) { result = 3_141592653589793238; } /// @notice Raises x to the power of y. /// /// @dev Based on the insight that x^y = 2^(log2(x) * y). /// /// Requirements: /// - All from "exp2", "log2" and "mul". /// /// Caveats: /// - All from "exp2", "log2" and "mul". /// - Assumes 0^0 is 1. /// /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number. /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number. /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number. function pow(uint256 x, uint256 y) internal pure returns (uint256 result) { if (x == 0) { result = y == 0 ? SCALE : uint256(0); } else { result = exp2(mul(log2(x), y)); } } /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the /// famous algorithm "exponentiation by squaring". /// /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring /// /// Requirements: /// - The result must fit within MAX_UD60x18. /// /// Caveats: /// - All from "mul". /// - Assumes 0^0 is 1. /// /// @param x The base as an unsigned 60.18-decimal fixed-point number. /// @param y The exponent as an uint256. /// @return result The result as an unsigned 60.18-decimal fixed-point number. function powu(uint256 x, uint256 y) internal pure returns (uint256 result) { // Calculate the first iteration of the loop in advance. result = y & 1 > 0 ? x : SCALE; // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster. for (y >>= 1; y > 0; y >>= 1) { x = PRBMath.mulDivFixedPoint(x, x); // Equivalent to "y % 2 == 1" but faster. if (y & 1 > 0) { result = PRBMath.mulDivFixedPoint(result, x); } } } /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number. function scale() internal pure returns (uint256 result) { result = SCALE; } /// @notice Calculates the square root of x, rounding down. /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. /// /// Requirements: /// - x must be less than MAX_UD60x18 / SCALE. /// /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root. /// @return result The result as an unsigned 60.18-decimal fixed-point . function sqrt(uint256 x) internal pure returns (uint256 result) { unchecked { if (x > MAX_UD60x18 / SCALE) { revert PRBMathUD60x18__SqrtOverflow(x); } // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root). result = PRBMath.sqrt(x * SCALE); } } /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process. /// @param x The unsigned 60.18-decimal fixed-point number to convert. /// @return result The same number in basic integer form. function toUint(uint256 x) internal pure returns (uint256 result) { unchecked { result = x / SCALE; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import "./WTF.sol"; import "./ERC20.sol"; import "./PRBMathUD60x18.sol"; contract StakingRewards { using PRBMathUD60x18 for uint256; uint256 constant private FLOAT_SCALAR = 2**64; uint256 constant private PERCENT_FEE = 5; // only for WTF staking uint256 constant private X_TICK = 30 days; struct User { uint256 deposited; int256 scaledPayout; } struct Info { uint256 totalRewards; uint256 startTime; uint256 lastUpdated; uint256 pendingFee; uint256 scaledRewardsPerToken; uint256 totalDeposited; mapping(address => User) users; WTF wtf; ERC20 token; } Info private info; event Deposit(address indexed user, uint256 amount, uint256 fee); event Withdraw(address indexed user, uint256 amount, uint256 fee); event Claim(address indexed user, uint256 amount); event Reinvest(address indexed user, uint256 amount); event Reward(uint256 amount); constructor(uint256 _totalRewards, uint256 _stakingRewardsStart, ERC20 _token) { info.totalRewards = _totalRewards; info.startTime = block.timestamp < _stakingRewardsStart ? _stakingRewardsStart : block.timestamp; info.lastUpdated = startTime(); info.wtf = WTF(msg.sender); info.token = _token; } function update() public { uint256 _now = block.timestamp; if (_now > info.lastUpdated && totalDeposited() > 0) { uint256 _reward = info.totalRewards.mul(_delta(_getX(info.lastUpdated), _getX(_now))); if (info.pendingFee > 0) { _reward += info.pendingFee; info.pendingFee = 0; } uint256 _balanceBefore = info.wtf.balanceOf(address(this)); info.wtf.claimRewards(); _reward += info.wtf.balanceOf(address(this)) - _balanceBefore; info.lastUpdated = _now; _disburse(_reward); } } function deposit(uint256 _amount) external { depositFor(msg.sender, _amount); } function depositFor(address _user, uint256 _amount) public { require(_amount > 0); update(); uint256 _balanceBefore = info.token.balanceOf(address(this)); info.token.transferFrom(msg.sender, address(this), _amount); uint256 _amountReceived = info.token.balanceOf(address(this)) - _balanceBefore; _deposit(_user, _amountReceived); } function tokenCallback(address _from, uint256 _tokens, bytes calldata) external returns (bool) { require(_isWTF() && msg.sender == tokenAddress()); require(_tokens > 0); update(); _deposit(_from, _tokens); return true; } function disburse(uint256 _amount) public { require(_amount > 0); update(); uint256 _balanceBefore = info.wtf.balanceOf(address(this)); info.wtf.transferFrom(msg.sender, address(this), _amount); uint256 _amountReceived = info.wtf.balanceOf(address(this)) - _balanceBefore; _processFee(_amountReceived); } function withdrawAll() public { uint256 _deposited = depositedOf(msg.sender); if (_deposited > 0) { withdraw(_deposited); } } function withdraw(uint256 _amount) public { require(_amount > 0 && _amount <= depositedOf(msg.sender)); update(); info.totalDeposited -= _amount; info.users[msg.sender].deposited -= _amount; info.users[msg.sender].scaledPayout -= int256(_amount * info.scaledRewardsPerToken); uint256 _fee = _calculateFee(_amount); info.token.transfer(msg.sender, _amount - _fee); _processFee(_fee); emit Withdraw(msg.sender, _amount, _fee); } function claim() public { update(); uint256 _rewards = rewardsOf(msg.sender); if (_rewards > 0) { info.users[msg.sender].scaledPayout += int256(_rewards * FLOAT_SCALAR); info.wtf.transfer(msg.sender, _rewards); emit Claim(msg.sender, _rewards); } } function reinvest() public { require(_isWTF()); update(); uint256 _rewards = rewardsOf(msg.sender); if (_rewards > 0) { info.users[msg.sender].scaledPayout += int256(_rewards * FLOAT_SCALAR); _deposit(msg.sender, _rewards); emit Reinvest(msg.sender, _rewards); } } function wtfAddress() public view returns (address) { return address(info.wtf); } function tokenAddress() public view returns (address) { return address(info.token); } function startTime() public view returns (uint256) { return info.startTime; } function totalDeposited() public view returns (uint256) { return info.totalDeposited; } function depositedOf(address _user) public view returns (uint256) { return info.users[_user].deposited; } function rewardsOf(address _user) public view returns (uint256) { return uint256(int256(info.scaledRewardsPerToken * depositedOf(_user)) - info.users[_user].scaledPayout) / FLOAT_SCALAR; } function currentRatePerDay() public view returns (uint256) { if (block.timestamp < startTime()) { return info.totalRewards.mul(_delta(_getX(startTime()), _getX(startTime() + 24 hours))); } else { return info.totalRewards.mul(_delta(_getX(block.timestamp), _getX(block.timestamp + 24 hours))); } } function totalDistributed() public view returns (uint256) { return info.totalRewards.mul(_sum(_getX(block.timestamp))); } function allInfoFor(address _user) external view returns (uint256 startingTime, uint256 totalRewardsDistributed, uint256 rewardsRatePerDay, uint256 currentFeePercent, uint256 totalTokensDeposited, uint256 virtualRewards, uint256 userWTF, uint256 userBalance, uint256 userAllowance, uint256 userDeposited, uint256 userRewards) { startingTime = startTime(); totalRewardsDistributed = totalDistributed(); rewardsRatePerDay = currentRatePerDay(); currentFeePercent = _calculateFee(1e20); totalTokensDeposited = totalDeposited(); virtualRewards = block.timestamp > info.lastUpdated ? info.totalRewards.mul(_delta(_getX(info.lastUpdated), _getX(block.timestamp))) : 0; userWTF = info.wtf.balanceOf(_user); userBalance = info.token.balanceOf(_user); userAllowance = info.token.allowance(_user, address(this)); userDeposited = depositedOf(_user); userRewards = rewardsOf(_user); } function _deposit(address _user, uint256 _amount) internal { uint256 _fee = _calculateFee(_amount); uint256 _deposited = _amount - _fee; info.totalDeposited += _deposited; info.users[_user].deposited += _deposited; info.users[_user].scaledPayout += int256(_deposited * info.scaledRewardsPerToken); _processFee(_fee); emit Deposit(_user, _amount, _fee); } function _processFee(uint256 _fee) internal { if (_fee > 0) { if (block.timestamp < startTime() || totalDeposited() == 0) { info.pendingFee += _fee; } else { _disburse(_fee); } } } function _disburse(uint256 _amount) internal { info.scaledRewardsPerToken += _amount * FLOAT_SCALAR / totalDeposited(); emit Reward(_amount); } function _isWTF() internal view returns (bool) { return wtfAddress() == tokenAddress(); } function _calculateFee(uint256 _amount) internal view returns (uint256) { return _isWTF() ? (_amount * PERCENT_FEE / 100).mul(1e18 - _sum(_getX(block.timestamp))) : 0; } function _getX(uint256 t) internal view returns (uint256) { uint256 _start = startTime(); if (t < _start) { return 0; } else { return ((t - _start) * 1e18).div(X_TICK * 1e18); } } function _sum(uint256 x) internal pure returns (uint256) { uint256 _e2x = x.exp2(); return (_e2x - 1e18).div(_e2x); } function _delta(uint256 x1, uint256 x2) internal pure returns (uint256) { require(x2 >= x1); return _sum(x2) - _sum(x1); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import "./WTFNFT.sol"; import "./Treasury.sol"; import "./StakingRewards.sol"; interface Callable { function tokenCallback(address _from, uint256 _tokens, bytes calldata _data) external returns (bool); } interface Router { function WETH() external pure returns (address); function factory() external pure returns (address); } interface Factory { function createPair(address, address) external returns (address); } interface Pair { function token0() external view returns (address); function totalSupply() external view returns (uint256); function balanceOf(address) external view returns (uint256); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); } contract WTF { uint256 constant private FLOAT_SCALAR = 2**64; uint256 constant private UINT_MAX = type(uint256).max; uint256 constant private TRANSFER_FEE_SCALE = 1000; // 1 = 0.1% uint256 constant private WTF_STAKING_SUPPLY = 2e25; // 20M WTF uint256 constant private LP_STAKING_SUPPLY = 4e25; // 40M WTF uint256 constant private TREASURY_SUPPLY = 4e25; // 40M WTF uint256 constant private BASE_UPGRADE_COST = 1e19; // 10 WTF uint256 constant private SERVICE_FEE = 0.01 ether; string constant public name = "fees.wtf"; string constant public symbol = "WTF"; uint8 constant public decimals = 18; struct User { uint256 balance; mapping(address => uint256) allowance; int256 scaledPayout; uint256 reflinkLevel; bool unlocked; } struct Info { bytes32 merkleRoot; uint256 openingTime; uint256 closingTime; uint256 totalSupply; uint256 scaledRewardsPerToken; mapping(uint256 => uint256) claimedWTFBitMap; mapping(uint256 => uint256) claimedNFTBitMap; mapping(address => User) users; mapping(address => bool) toWhitelist; mapping(address => bool) fromWhitelist; address owner; Router router; Pair pair; bool weth0; WTFNFT nft; TeamReferral team; Treasury treasury; StakingRewards stakingRewards; StakingRewards lpStakingRewards; address feeManager; uint256 transferFee; uint256 feeManagerPercent; } Info private info; event Transfer(address indexed from, address indexed to, uint256 tokens); event Approval(address indexed owner, address indexed spender, uint256 tokens); event WhitelistUpdated(address indexed user, bool fromWhitelisted, bool toWhitelisted); event ReflinkRewards(address indexed referrer, uint256 amount); event ClaimRewards(address indexed user, uint256 amount); event Reward(uint256 amount); modifier _onlyOwner() { require(msg.sender == owner()); _; } constructor(bytes32 _merkleRoot, uint256 _openingTime, uint256 _stakingRewardsStart) { info.merkleRoot = _merkleRoot; info.openingTime = block.timestamp < _openingTime ? _openingTime : block.timestamp; info.closingTime = openingTime() + 30 days; info.router = Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); info.pair = Pair(Factory(info.router.factory()).createPair(info.router.WETH(), address(this))); info.weth0 = info.pair.token0() == info.router.WETH(); info.transferFee = 40; // 4% info.feeManagerPercent = 25; // 25% info.owner = 0x65dd4990719bE9B20322e4E8D3Bd77a4401a0357; info.nft = new WTFNFT(); info.team = new TeamReferral(); info.treasury = new Treasury(); _mint(treasuryAddress(), TREASURY_SUPPLY); info.stakingRewards = new StakingRewards(WTF_STAKING_SUPPLY, _stakingRewardsStart, ERC20(address(this))); _mint(stakingRewardsAddress(), WTF_STAKING_SUPPLY); info.lpStakingRewards = new StakingRewards(LP_STAKING_SUPPLY, _stakingRewardsStart, ERC20(pairAddress())); _mint(lpStakingRewardsAddress(), LP_STAKING_SUPPLY); info.feeManager = address(new FeeManager()); _approve(feeManagerAddress(), stakingRewardsAddress(), UINT_MAX); _approve(feeManagerAddress(), lpStakingRewardsAddress(), UINT_MAX); } function setOwner(address _owner) external _onlyOwner { info.owner = _owner; } function setFeeManager(address _feeManager) external _onlyOwner { info.feeManager = _feeManager; } function setClosingTime(uint256 _closingTime) external _onlyOwner { info.closingTime = _closingTime; } function setTransferFee(uint256 _transferFee) external _onlyOwner { require(_transferFee <= 100); // ≤10% info.transferFee = _transferFee; } function setFeeManagerPercent(uint256 _feeManagerPercent) external _onlyOwner { require(_feeManagerPercent <= 100); info.feeManagerPercent = _feeManagerPercent; } function setWhitelisted(address _address, bool _fromWhitelisted, bool _toWhitelisted) external _onlyOwner { info.fromWhitelist[_address] = _fromWhitelisted; info.toWhitelist[_address] = _toWhitelisted; emit WhitelistUpdated(_address, _fromWhitelisted, _toWhitelisted); } function disburse(uint256 _amount) external { require(_amount > 0); uint256 _balanceBefore = balanceOf(address(this)); _transfer(msg.sender, address(this), _amount); uint256 _amountReceived = balanceOf(address(this)) - _balanceBefore; _disburse(_amountReceived); } function sweep() external { if (address(this).balance > 0) { teamAddress().transfer(address(this).balance); } } function upgradeReflink(uint256 _toLevel) external { uint256 _currentLevel = reflinkLevel(msg.sender); require(_currentLevel < _toLevel); uint256 _totalCost = 0; for (uint256 i = _currentLevel; i < _toLevel; i++) { _totalCost += upgradeCost(i); } burn(_totalCost); info.users[msg.sender].reflinkLevel = _toLevel; } function unlock(address _account, address payable _referrer) external payable { require(block.timestamp < closingTime()); require(!isUnlocked(_account)); require(msg.value == SERVICE_FEE); uint256 _refFee = 0; if (_referrer != address(0x0)) { _refFee = SERVICE_FEE * reflinkPercent(_referrer) / 100; !_referrer.send(_refFee); emit ReflinkRewards(_referrer, _refFee); } uint256 _remaining = SERVICE_FEE - _refFee; teamAddress().transfer(_remaining); emit ReflinkRewards(teamAddress(), _remaining); info.users[_account].unlocked = true; } function claim(address _account, uint256[9] calldata _data, bytes32[] calldata _proof) external { // Data array in format: (index, amount, totalFees, failFees, totalGas, avgGwei, totalDonated, totalTxs, failTxs) claimWTF(_account, _data, _proof); claimNFT(_account, _data, _proof); } function claimWTF(address _account, uint256[9] calldata _data, bytes32[] calldata _proof) public { require(isOpen()); require(isUnlocked(_account)); uint256 _index = _data[0]; uint256 _amount = _data[1]; require(!isClaimedWTF(_index)); require(_verify(_proof, keccak256(abi.encodePacked(_account, _data)))); uint256 _claimedWordIndex = _index / 256; uint256 _claimedBitIndex = _index % 256; info.claimedWTFBitMap[_claimedWordIndex] = info.claimedWTFBitMap[_claimedWordIndex] | (1 << _claimedBitIndex); _mint(_account, _amount); } function claimNFT(address _account, uint256[9] calldata _data, bytes32[] calldata _proof) public { require(isOpen()); require(isUnlocked(_account)); uint256 _index = _data[0]; require(!isClaimedNFT(_index)); require(_verify(_proof, keccak256(abi.encodePacked(_account, _data)))); uint256 _claimedWordIndex = _index / 256; uint256 _claimedBitIndex = _index % 256; info.claimedNFTBitMap[_claimedWordIndex] = info.claimedNFTBitMap[_claimedWordIndex] | (1 << _claimedBitIndex); info.nft.mint(_account, _data[2], _data[3], _data[4], _data[5], _data[6], _data[7], _data[8]); } function claimRewards() external { boostRewards(); uint256 _rewards = rewardsOf(msg.sender); if (_rewards > 0) { info.users[msg.sender].scaledPayout += int256(_rewards * FLOAT_SCALAR); _transfer(address(this), msg.sender, _rewards); emit ClaimRewards(msg.sender, _rewards); } } function boostRewards() public { address _this = address(this); uint256 _rewards = rewardsOf(_this); if (_rewards > 0) { info.users[_this].scaledPayout += int256(_rewards * FLOAT_SCALAR); _disburse(_rewards); emit ClaimRewards(_this, _rewards); } } function burn(uint256 _tokens) public { require(balanceOf(msg.sender) >= _tokens); info.totalSupply -= _tokens; info.users[msg.sender].balance -= _tokens; info.users[msg.sender].scaledPayout -= int256(_tokens * info.scaledRewardsPerToken); emit Transfer(msg.sender, address(0x0), _tokens); } function transfer(address _to, uint256 _tokens) external returns (bool) { return _transfer(msg.sender, _to, _tokens); } function approve(address _spender, uint256 _tokens) external returns (bool) { return _approve(msg.sender, _spender, _tokens); } function transferFrom(address _from, address _to, uint256 _tokens) external returns (bool) { uint256 _allowance = allowance(_from, msg.sender); require(_allowance >= _tokens); if (_allowance != UINT_MAX) { info.users[_from].allowance[msg.sender] -= _tokens; } return _transfer(_from, _to, _tokens); } function transferAndCall(address _to, uint256 _tokens, bytes calldata _data) external returns (bool) { uint256 _balanceBefore = balanceOf(_to); _transfer(msg.sender, _to, _tokens); uint256 _tokensReceived = balanceOf(_to) - _balanceBefore; uint32 _size; assembly { _size := extcodesize(_to) } if (_size > 0) { require(Callable(_to).tokenCallback(msg.sender, _tokensReceived, _data)); } return true; } function pairAddress() public view returns (address) { return address(info.pair); } function nftAddress() external view returns (address) { return address(info.nft); } function teamAddress() public view returns (address payable) { return payable(address(info.team)); } function treasuryAddress() public view returns (address) { return address(info.treasury); } function stakingRewardsAddress() public view returns (address) { return address(info.stakingRewards); } function lpStakingRewardsAddress() public view returns (address) { return address(info.lpStakingRewards); } function feeManagerAddress() public view returns (address) { return info.feeManager; } function owner() public view returns (address) { return info.owner; } function transferFee() public view returns (uint256) { return info.transferFee; } function feeManagerPercent() public view returns (uint256) { return info.feeManagerPercent; } function isFromWhitelisted(address _address) public view returns (bool) { return info.fromWhitelist[_address]; } function isToWhitelisted(address _address) public view returns (bool) { return info.toWhitelist[_address]; } function merkleRoot() public view returns (bytes32) { return info.merkleRoot; } function openingTime() public view returns (uint256) { return info.openingTime; } function closingTime() public view returns (uint256) { return info.closingTime; } function isOpen() public view returns (bool) { return block.timestamp > openingTime() && block.timestamp < closingTime(); } function isUnlocked(address _user) public view returns (bool) { return info.users[_user].unlocked; } function isClaimedWTF(uint256 _index) public view returns (bool) { uint256 _claimedWordIndex = _index / 256; uint256 _claimedBitIndex = _index % 256; uint256 _claimedWord = info.claimedWTFBitMap[_claimedWordIndex]; uint256 _mask = (1 << _claimedBitIndex); return _claimedWord & _mask == _mask; } function isClaimedNFT(uint256 _index) public view returns (bool) { uint256 _claimedWordIndex = _index / 256; uint256 _claimedBitIndex = _index % 256; uint256 _claimedWord = info.claimedNFTBitMap[_claimedWordIndex]; uint256 _mask = (1 << _claimedBitIndex); return _claimedWord & _mask == _mask; } function totalSupply() public view returns (uint256) { return info.totalSupply; } function balanceOf(address _user) public view returns (uint256) { return info.users[_user].balance; } function rewardsOf(address _user) public view returns (uint256) { return uint256(int256(info.scaledRewardsPerToken * balanceOf(_user)) - info.users[_user].scaledPayout) / FLOAT_SCALAR; } function allowance(address _user, address _spender) public view returns (uint256) { return info.users[_user].allowance[_spender]; } function reflinkLevel(address _user) public view returns (uint256) { return info.users[_user].reflinkLevel; } function reflinkPercent(address _user) public view returns (uint256) { return 10 * (reflinkLevel(_user) + 1); } function upgradeCost(uint256 _reflinkLevel) public pure returns (uint256) { require(_reflinkLevel < 4); return BASE_UPGRADE_COST * 10**_reflinkLevel; } function reflinkInfoFor(address _user) external view returns (uint256 balance, uint256 level, uint256 percent) { return (balanceOf(_user), reflinkLevel(_user), reflinkPercent(_user)); } function claimInfoFor(uint256 _index, address _user) external view returns (uint256 openTime, uint256 closeTime, bool unlocked, bool claimedWTF, bool claimedNFT, uint256 wethReserve, uint256 wtfReserve) { openTime = openingTime(); closeTime = closingTime(); unlocked = isUnlocked(_user); claimedWTF = isClaimedWTF(_index); claimedNFT = isClaimedNFT(_index); ( , , wethReserve, wtfReserve, , , ) = allInfoFor(address(0x0)); } function allInfoFor(address _user) public view returns (uint256 totalTokens, uint256 totalLPTokens, uint256 wethReserve, uint256 wtfReserve, uint256 userBalance, uint256 userRewards, uint256 userLPBalance) { totalTokens = totalSupply(); totalLPTokens = info.pair.totalSupply(); (uint256 _res0, uint256 _res1, ) = info.pair.getReserves(); wethReserve = info.weth0 ? _res0 : _res1; wtfReserve = info.weth0 ? _res1 : _res0; userBalance = balanceOf(_user); userRewards = rewardsOf(_user); userLPBalance = info.pair.balanceOf(_user); } function _mint(address _account, uint256 _amount) internal { info.totalSupply += _amount; info.users[_account].balance += _amount; info.users[_account].scaledPayout += int256(_amount * info.scaledRewardsPerToken); emit Transfer(address(0x0), _account, _amount); } function _approve(address _owner, address _spender, uint256 _tokens) internal returns (bool) { info.users[_owner].allowance[_spender] = _tokens; emit Approval(_owner, _spender, _tokens); return true; } function _transfer(address _from, address _to, uint256 _tokens) internal returns (bool) { require(balanceOf(_from) >= _tokens); info.users[_from].balance -= _tokens; info.users[_from].scaledPayout -= int256(_tokens * info.scaledRewardsPerToken); uint256 _fee = 0; if (!_isExcludedFromFee(_from, _to)) { _fee = _tokens * transferFee() / TRANSFER_FEE_SCALE; address _this = address(this); info.users[_this].balance += _fee; info.users[_this].scaledPayout += int256(_fee * info.scaledRewardsPerToken); emit Transfer(_from, _this, _fee); } uint256 _transferred = _tokens - _fee; info.users[_to].balance += _transferred; info.users[_to].scaledPayout += int256(_transferred * info.scaledRewardsPerToken); emit Transfer(_from, _to, _transferred); if (_fee > 0) { uint256 _feeManagerRewards = _fee * feeManagerPercent() / 100; info.users[feeManagerAddress()].scaledPayout -= int256(_feeManagerRewards * FLOAT_SCALAR); _disburse(_fee - _feeManagerRewards); } return true; } function _disburse(uint256 _amount) internal { if (_amount > 0) { info.scaledRewardsPerToken += _amount * FLOAT_SCALAR / totalSupply(); emit Reward(_amount); } } function _isExcludedFromFee(address _from, address _to) internal view returns (bool) { return isFromWhitelisted(_from) || isToWhitelisted(_to) || _from == address(this) || _to == address(this) || _from == feeManagerAddress() || _to == feeManagerAddress() || _from == treasuryAddress() || _to == treasuryAddress() || _from == stakingRewardsAddress() || _to == stakingRewardsAddress() || _from == lpStakingRewardsAddress() || _to == lpStakingRewardsAddress(); } function _verify(bytes32[] memory _proof, bytes32 _leaf) internal view returns (bool) { bytes32 _computedHash = _leaf; for (uint256 i = 0; i < _proof.length; i++) { bytes32 _proofElement = _proof[i]; if (_computedHash <= _proofElement) { _computedHash = keccak256(abi.encodePacked(_computedHash, _proofElement)); } else { _computedHash = keccak256(abi.encodePacked(_proofElement, _computedHash)); } } return _computedHash == merkleRoot(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.11; import "./Metadata.sol"; interface Receiver { function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns (bytes4); } contract WTFNFT { struct User { uint256 balance; mapping(uint256 => uint256) list; mapping(address => bool) approved; mapping(uint256 => uint256) indexOf; uint256 tokenIndex; } struct Token { address user; address owner; address approved; uint128 totalFees; uint128 failFees; uint128 totalGas; uint128 avgGwei; uint128 totalDonated; uint64 totalTxs; uint64 failTxs; } struct Info { uint256 totalSupply; mapping(uint256 => Token) list; mapping(address => User) users; Metadata metadata; address wtf; address owner; } Info private info; mapping(bytes4 => bool) public supportsInterface; event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); event Mint(address indexed owner, uint256 indexed tokenId, uint256 totalFees, uint256 failFees, uint256 totalGas, uint256 avgGwei, uint256 totalDonated, uint256 totalTxs, uint256 failTxs); modifier _onlyOwner() { require(msg.sender == owner()); _; } constructor() { info.metadata = new Metadata(this); info.wtf = msg.sender; info.owner = 0xdEE79eD62B42e30EA7EbB6f1b7A3f04143D18b7F; supportsInterface[0x01ffc9a7] = true; // ERC-165 supportsInterface[0x80ac58cd] = true; // ERC-721 supportsInterface[0x5b5e139f] = true; // Metadata supportsInterface[0x780e9d63] = true; // Enumerable } function setOwner(address _owner) external _onlyOwner { info.owner = _owner; } function setMetadata(Metadata _metadata) external _onlyOwner { info.metadata = _metadata; } function mint(address _receiver, uint256 _totalFees, uint256 _failFees, uint256 _totalGas, uint256 _avgGwei, uint256 _totalDonated, uint256 _totalTxs, uint256 _failTxs) public { require(msg.sender == wtfAddress()); uint256 _tokenId = info.totalSupply++; info.users[_receiver].tokenIndex = totalSupply(); Token storage _newToken = info.list[_tokenId]; _newToken.user = _receiver; _newToken.owner = _receiver; _newToken.totalFees = uint128(_totalFees); _newToken.failFees = uint128(_failFees); _newToken.totalGas = uint128(_totalGas); _newToken.avgGwei = uint128(_avgGwei); _newToken.totalDonated = uint128(_totalDonated); _newToken.totalTxs = uint64(_totalTxs); _newToken.failTxs = uint64(_failTxs); uint256 _index = info.users[_receiver].balance++; info.users[_receiver].indexOf[_tokenId] = _index + 1; info.users[_receiver].list[_index] = _tokenId; emit Transfer(address(0x0), _receiver, _tokenId); emit Mint(_receiver, _tokenId, _totalFees, _failFees, _totalGas, _avgGwei, _totalDonated, _totalTxs, _failTxs); } function approve(address _approved, uint256 _tokenId) external { require(msg.sender == ownerOf(_tokenId)); info.list[_tokenId].approved = _approved; emit Approval(msg.sender, _approved, _tokenId); } function setApprovalForAll(address _operator, bool _approved) external { info.users[msg.sender].approved[_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } function transferFrom(address _from, address _to, uint256 _tokenId) external { _transfer(_from, _to, _tokenId); } function safeTransferFrom(address _from, address _to, uint256 _tokenId) external { safeTransferFrom(_from, _to, _tokenId, ""); } function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public { _transfer(_from, _to, _tokenId); uint32 _size; assembly { _size := extcodesize(_to) } if (_size > 0) { require(Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) == 0x150b7a02); } } function name() external view returns (string memory) { return info.metadata.name(); } function symbol() external view returns (string memory) { return info.metadata.symbol(); } function tokenURI(uint256 _tokenId) external view returns (string memory) { return info.metadata.tokenURI(_tokenId); } function metadataAddress() public view returns (address) { return address(info.metadata); } function wtfAddress() public view returns (address) { return info.wtf; } function owner() public view returns (address) { return info.owner; } function totalSupply() public view returns (uint256) { return info.totalSupply; } function balanceOf(address _owner) public view returns (uint256) { return info.users[_owner].balance; } function ownerOf(uint256 _tokenId) public view returns (address) { require(_tokenId < totalSupply()); return info.list[_tokenId].owner; } function getUser(uint256 _tokenId) public view returns (address) { require(_tokenId < totalSupply()); return info.list[_tokenId].user; } function getApproved(uint256 _tokenId) public view returns (address) { require(_tokenId < totalSupply()); return info.list[_tokenId].approved; } function getTotalFees(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].totalFees; } function getFailFees(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].failFees; } function getTotalGas(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].totalGas; } function getAvgGwei(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].avgGwei; } function getTotalDonated(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].totalDonated; } function getTotalTxs(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].totalTxs; } function getFailTxs(uint256 _tokenId) public view returns (uint256) { require(_tokenId < totalSupply()); return info.list[_tokenId].failTxs; } function isApprovedForAll(address _owner, address _operator) public view returns (bool) { return info.users[_owner].approved[_operator]; } function tokenIdOf(address _user) public view returns (uint256) { uint256 _index = info.users[_user].tokenIndex; require(_index > 0); return _index - 1; } function tokenByIndex(uint256 _index) public view returns (uint256) { require(_index < totalSupply()); return _index; } function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256) { require(_index < balanceOf(_owner)); return info.users[_owner].list[_index]; } function getTokenCompressedInfo(uint256 _tokenId) public view returns (uint256[7] memory compressedInfo) { compressedInfo[0] = getTotalFees(_tokenId); compressedInfo[1] = getFailFees(_tokenId); compressedInfo[2] = getTotalGas(_tokenId); compressedInfo[3] = getAvgGwei(_tokenId); compressedInfo[4] = getTotalDonated(_tokenId); compressedInfo[5] = getTotalTxs(_tokenId); compressedInfo[6] = getFailTxs(_tokenId); } function getToken(uint256 _tokenId) public view returns (address tokenOwner, address approved, address user, uint256[7] memory compressedInfo) { return (ownerOf(_tokenId), getApproved(_tokenId), getUser(_tokenId), getTokenCompressedInfo(_tokenId)); } function getTokens(uint256[] memory _tokenIds) public view returns (address[] memory owners, address[] memory approveds, address[] memory users, uint256[7][] memory compressedInfos) { uint256 _length = _tokenIds.length; owners = new address[](_length); approveds = new address[](_length); users = new address[](_length); compressedInfos = new uint256[7][](_length); for (uint256 i = 0; i < _length; i++) { (owners[i], approveds[i], users[i], compressedInfos[i]) = getToken(_tokenIds[i]); } } function getTokensTable(uint256 _limit, uint256 _page, bool _isAsc) public view returns (uint256[] memory tokenIds, address[] memory owners, address[] memory approveds, address[] memory users, uint256[7][] memory compressedInfos, uint256 totalTokens, uint256 totalPages) { require(_limit > 0); totalTokens = totalSupply(); if (totalTokens > 0) { totalPages = (totalTokens / _limit) + (totalTokens % _limit == 0 ? 0 : 1); require(_page < totalPages); uint256 _offset = _limit * _page; if (_page == totalPages - 1 && totalTokens % _limit != 0) { _limit = totalTokens % _limit; } tokenIds = new uint256[](_limit); for (uint256 i = 0; i < _limit; i++) { tokenIds[i] = tokenByIndex(_isAsc ? _offset + i : totalTokens - _offset - i - 1); } } else { totalPages = 0; tokenIds = new uint256[](0); } (owners, approveds, users, compressedInfos) = getTokens(tokenIds); } function getOwnerTokensTable(address _owner, uint256 _limit, uint256 _page, bool _isAsc) public view returns (uint256[] memory tokenIds, address[] memory approveds, address[] memory users, uint256[7][] memory compressedInfos, uint256 totalTokens, uint256 totalPages) { require(_limit > 0); totalTokens = balanceOf(_owner); if (totalTokens > 0) { totalPages = (totalTokens / _limit) + (totalTokens % _limit == 0 ? 0 : 1); require(_page < totalPages); uint256 _offset = _limit * _page; if (_page == totalPages - 1 && totalTokens % _limit != 0) { _limit = totalTokens % _limit; } tokenIds = new uint256[](_limit); for (uint256 i = 0; i < _limit; i++) { tokenIds[i] = tokenOfOwnerByIndex(_owner, _isAsc ? _offset + i : totalTokens - _offset - i - 1); } } else { totalPages = 0; tokenIds = new uint256[](0); } ( , approveds, users, compressedInfos) = getTokens(tokenIds); } function allInfoFor(address _owner) external view returns (uint256 supply, uint256 ownerBalance) { return (totalSupply(), balanceOf(_owner)); } function _transfer(address _from, address _to, uint256 _tokenId) internal { address _owner = ownerOf(_tokenId); address _approved = getApproved(_tokenId); require(_from == _owner); require(msg.sender == _owner || msg.sender == _approved || isApprovedForAll(_owner, msg.sender)); info.list[_tokenId].owner = _to; if (_approved != address(0x0)) { info.list[_tokenId].approved = address(0x0); emit Approval(address(0x0), address(0x0), _tokenId); } uint256 _index = info.users[_from].indexOf[_tokenId] - 1; uint256 _moved = info.users[_from].list[info.users[_from].balance - 1]; info.users[_from].list[_index] = _moved; info.users[_from].indexOf[_moved] = _index + 1; info.users[_from].balance--; delete info.users[_from].indexOf[_tokenId]; uint256 _newIndex = info.users[_to].balance++; info.users[_to].indexOf[_tokenId] = _newIndex + 1; info.users[_to].list[_newIndex] = _tokenId; emit Transfer(_from, _to, _tokenId); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"release","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801561001057600080fd5b506101f9806100206000396000f3fe6080604052600436106100225760003560e01c806386d1a69f1461002e57600080fd5b3661002957005b600080fd5b34801561003a57600080fd5b50610043610045565b005b30803161005157600080fd5b736129e7bcb71c0d7d4580141c4e6a995f16293f426108fc61007e600a6001600160a01b038516316101a1565b6040518115909202916000818181858888f193505050501580156100a6573d6000803e3d6000fd5b5073c9aebdd8fd0d52c35a32fd9155467cf28ce474c36108fc6100d460036001600160a01b038516316101a1565b6040518115909202916000818181858888f193505050501580156100fc573d6000803e3d6000fd5b5073dee79ed62b42e30ea7ebb6f1b7a3f04143d18b7f6108fc61012a60026001600160a01b038516316101a1565b6040518115909202916000818181858888f19350505050158015610152573d6000803e3d6000fd5b5060405173575446aa9e9647c40edb7a467e45c5916add1538906001600160a01b0383163180156108fc02916000818181858888f1935050505015801561019d573d6000803e3d6000fd5b5050565b6000826101be57634e487b7160e01b600052601260045260246000fd5b50049056fea2646970667358221220fe3f3505f2bdd4705e1008de1d644cef4162746ee8a345c0e6b11248eafa64d564736f6c634300080b0033
Deployed Bytecode
0x6080604052600436106100225760003560e01c806386d1a69f1461002e57600080fd5b3661002957005b600080fd5b34801561003a57600080fd5b50610043610045565b005b30803161005157600080fd5b736129e7bcb71c0d7d4580141c4e6a995f16293f426108fc61007e600a6001600160a01b038516316101a1565b6040518115909202916000818181858888f193505050501580156100a6573d6000803e3d6000fd5b5073c9aebdd8fd0d52c35a32fd9155467cf28ce474c36108fc6100d460036001600160a01b038516316101a1565b6040518115909202916000818181858888f193505050501580156100fc573d6000803e3d6000fd5b5073dee79ed62b42e30ea7ebb6f1b7a3f04143d18b7f6108fc61012a60026001600160a01b038516316101a1565b6040518115909202916000818181858888f19350505050158015610152573d6000803e3d6000fd5b5060405173575446aa9e9647c40edb7a467e45c5916add1538906001600160a01b0383163180156108fc02916000818181858888f1935050505015801561019d573d6000803e3d6000fd5b5050565b6000826101be57634e487b7160e01b600052601260045260246000fd5b50049056fea2646970667358221220fe3f3505f2bdd4705e1008de1d644cef4162746ee8a345c0e6b11248eafa64d564736f6c634300080b0033
Deployed Bytecode Sourcemap
750:510:5:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;806:452;;;;;;;;;;;;;:::i;:::-;;;862:4;879:13;;871:26;;;;;;909:42;901:80;962:18;978:2;-1:-1:-1;;;;;962:13:5;;;:18;:::i;:::-;901:80;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1000:42:5;992:79;1053:17;1069:1;-1:-1:-1;;;;;1053:13:5;;;:17;:::i;:::-;992:79;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1090:42:5;1082:79;1143:17;1159:1;-1:-1:-1;;;;;1143:13:5;;;:17;:::i;:::-;1082:79;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1172:75:5;;1180:42;;-1:-1:-1;;;;;1233:13:5;;;1172:75;;;;;;;;;1233:13;1180:42;1172:75;;;;;;;;;;;;;;;;;;;;;834:424;806:452::o;14:217:8:-;54:1;80;70:132;;124:10;119:3;115:20;112:1;105:31;159:4;156:1;149:15;187:4;184:1;177:15;70:132;-1:-1:-1;216:9:8;;14:217::o
Swarm Source
ipfs://fe3f3505f2bdd4705e1008de1d644cef4162746ee8a345c0e6b11248eafa64d5
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.