Transaction Hash:
Block:
9779690 at Mar-31-2020 01:08:24 PM +UTC
Transaction Fee:
0.0008751438 ETH
$2.14
Gas Used:
102,356 Gas / 8.55 Gwei
Emitted Events:
137 |
MultiSigWalletWithDailyLimit.Deposit( sender=[Receiver] LandSaleWithReferral, value=291508596151656609 )
|
138 |
Land.Transfer( _from=0x00000000...000000000, _to=[Sender] 0xddd3964d75d59b6b6d5c31eb313bba5ebf076364, _tokenId=76253 )
|
139 |
LandSaleWithReferral.LandQuadPurchased( buyer=[Sender] 0xddd3964d75d59b6b6d5c31eb313bba5ebf076364, to=[Sender] 0xddd3964d75d59b6b6d5c31eb313bba5ebf076364, topCornerId=76253, size=1, price=2667000000000000000000, token=0x00000000...000000000, amountPaid=291508596151656609 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x50f54747...809Fd6d4a | |||||
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 25.500146542790334555 Eth | 25.501021686590334555 Eth | 0.0008751438 | |
0x9695ed50...cBc8FF60b | 2,322.398044287937852763 Eth | 2,322.689552884089509372 Eth | 0.291508596151656609 | ||
0xDDd3964D...EBF076364 |
112.31140146442634714 Eth
Nonce: 5689
|
112.019017724474690531 Eth
Nonce: 5690
| 0.292383739951656609 |
Execution Trace
ETH 0.297338768074689741
LandSaleWithReferral.buyLandWithETH( )
-
Medianizer.STATICCALL( )
- ETH 0.005830171923033132
0xddd3964d75d59b6b6d5c31eb313bba5ebf076364.CALL( )
- ETH 0.291508596151656609
MultiSigWalletWithDailyLimit.CALL( )
-
Land.mintQuad( to=0xDDd3964D75D59B6B6d5c31eB313BBA5EBF076364, size=1, x=365, y=186, data=0x000000000000000000000000DDD3964D75D59B6B6D5C31EB313BBA5EBF07636400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000 )
File 1 of 4: LandSaleWithReferral
File 2 of 4: MultiSigWalletWithDailyLimit
File 3 of 4: Land
File 4 of 4: Medianizer
pragma solidity ^0.5.2; contract Admin { address internal _admin; event AdminChanged(address oldAdmin, address newAdmin); /// @notice gives the current administrator of this contract. /// @return the current administrator of this contract. function getAdmin() external view returns (address) { return _admin; } /// @notice change the administrator to be `newAdmin`. /// @param newAdmin address of the new administrator. function changeAdmin(address newAdmin) external { require(msg.sender == _admin, "only admin can change admin"); emit AdminChanged(_admin, newAdmin); _admin = newAdmin; } modifier onlyAdmin() { require (msg.sender == _admin, "only admin allowed"); _; } } pragma solidity ^0.5.2; import "./Admin.sol"; contract MetaTransactionReceiver is Admin{ mapping(address => bool) internal _metaTransactionContracts; event MetaTransactionProcessor(address metaTransactionProcessor, bool enabled); /// @notice Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights). /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights. /// @param enabled set whether the metaTransactionProcessor is enabled or disabled. function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) public { require( msg.sender == _admin, "only admin can setup metaTransactionProcessors" ); _setMetaTransactionProcessor(metaTransactionProcessor, enabled); } function _setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) internal { _metaTransactionContracts[metaTransactionProcessor] = enabled; emit MetaTransactionProcessor(metaTransactionProcessor, enabled); } /// @notice check whether address `who` is given meta-transaction execution rights. /// @param who The address to query. /// @return whether the address has meta-transaction execution rights. function isMetaTransactionProcessor(address who) external view returns(bool) { return _metaTransactionContracts[who]; } }pragma solidity ^0.5.2; import "./Admin.sol"; contract SuperOperators is Admin { mapping(address => bool) internal _superOperators; event SuperOperator(address superOperator, bool enabled); /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights). /// @param superOperator address that will be given/removed superOperator right. /// @param enabled set whether the superOperator is enabled or disabled. function setSuperOperator(address superOperator, bool enabled) external { require( msg.sender == _admin, "only admin is allowed to add super operators" ); _superOperators[superOperator] = enabled; emit SuperOperator(superOperator, enabled); } /// @notice check whether address `who` is given superOperator rights. /// @param who The address to query. /// @return whether the address has superOperator rights. function isSuperOperator(address who) public view returns (bool) { return _superOperators[who]; } } pragma solidity ^0.5.2; import "./ERC20Basic.sol"; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ /* interface */ contract ERC20 is ERC20Basic { function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); function allowance(address owner, address spender) public view returns (uint256); event Approval( address indexed owner, address indexed spender, uint256 value ); } pragma solidity ^0.5.2; /** * @title ERC20Basic DRAFT * @dev Simpler version of ERC20 interface * See https://github.com/ethereum/EIPs/issues/179 */ /* interface */ contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } pragma solidity ^0.5.2; /** * @title ERC721 Non-Fungible Token Standard basic interface * @dev see https://eips.ethereum.org/EIPS/eip-721 */ interface ERC721Events { 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 ); } pragma solidity ^0.5.2; /** Note: The ERC-165 identifier for this interface is 0x5e8bf644. */ interface ERC721MandatoryTokenReceiver { function onERC721BatchReceived( address operator, address from, uint256[] calldata ids, bytes calldata data ) external returns (bytes4); // needs to return 0x4b808c46 function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); // needs to return 0x150b7a02 // needs to implements EIP-165 // function supportsInterface(bytes4 interfaceId) // external // view // returns (bool); } /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This code has not been reviewed. * Do not use or deploy this code before reviewing it personally first. */ // solhint-disable-next-line compiler-fixed pragma solidity ^0.5.2; interface ERC721TokenReceiver { function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } pragma solidity ^0.5.2; /** * @title Medianizer contract * @dev From MakerDAO (https://etherscan.io/address/0x729D19f657BD0614b4985Cf1D82531c67569197B#code) */ interface Medianizer { function read() external view returns (bytes32); } pragma solidity ^0.5.2; library AddressUtils { function toPayable(address _address) internal pure returns (address payable _payable) { return address(uint160(_address)); } function isContract(address addr) internal view returns (bool) { // for accounts without code, i.e. `keccak256('')`: bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; bytes32 codehash; // solium-disable-next-line security/no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != accountHash); } } pragma solidity ^0.5.2; /** * @title SafeMath * @dev Math operations with safety checks that revert */ library SafeMathWithRequire { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; require(c / a == b, "overflow"); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "undeflow"); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; require(c >= a, "overflow"); return c; } } pragma solidity ^0.5.2; library SigUtil { function recover(bytes32 hash, bytes memory sig) internal pure returns (address recovered) { require(sig.length == 65); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } // Version of signature should be 27 or 28, but 0 and 1 are also possible versions if (v < 27) { v += 27; } require(v == 27 || v == 28); recovered = ecrecover(hash, v, r, s); require(recovered != address(0)); } function recoverWithZeroOnFailure(bytes32 hash, bytes memory sig) internal pure returns (address) { if (sig.length != 65) { return (address(0)); } bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } // Version of signature should be 27 or 28, but 0 and 1 are also possible versions if (v < 27) { v += 27; } if (v != 27 && v != 28) { return (address(0)); } else { return ecrecover(hash, v, r, s); } } // Builds a prefixed hash to mimic the behavior of eth_sign. function prefixed(bytes32 hash) internal pure returns (bytes memory) { return abi.encodePacked("\\x19Ethereum Signed Message:\ 32", hash); } } /* solhint-disable no-empty-blocks */ pragma solidity 0.5.9; import "./Land/erc721/LandBaseToken.sol"; contract Land is LandBaseToken { constructor( address metaTransactionContract, address admin ) public LandBaseToken( metaTransactionContract, admin ) { } /** * @notice Return the name of the token contract * @return The name of the token contract */ function name() external pure returns (string memory) { return "Sandbox's LANDs"; } /** * @notice Return the symbol of the token contract * @return The symbol of the token contract */ function symbol() external pure returns (string memory) { return "LAND"; } // solium-disable-next-line security/no-assign-params function uint2str(uint _i) internal pure returns (string memory) { if (_i == 0) { return "0"; } uint j = _i; uint len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len - 1; while (_i != 0) { bstr[k--] = byte(uint8(48 + _i % 10)); _i /= 10; } return string(bstr); } /** * @notice Return the URI of a specific token * @param id The id of the token * @return The URI of the token */ function tokenURI(uint256 id) public view returns (string memory) { require(_ownerOf(id) != address(0), "Id does not exist"); return string( abi.encodePacked( "https://api.sandbox.game/lands/", uint2str(id), "/metadata.json" ) ); } /** * @notice Check if the contract supports an interface * 0x01ffc9a7 is ERC-165 * 0x80ac58cd is ERC-721 * 0x5b5e139f is ERC-721 metadata * @param id The id of the interface * @return True if the interface is supported */ function supportsInterface(bytes4 id) external pure returns (bool) { return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f; } } /* solhint-disable func-order, code-complexity */ pragma solidity 0.5.9; import "../../../contracts_common/src/Libraries/AddressUtils.sol"; import "../../../contracts_common/src/Interfaces/ERC721TokenReceiver.sol"; import "../../../contracts_common/src/Interfaces/ERC721Events.sol"; import "../../../contracts_common/src/BaseWithStorage/SuperOperators.sol"; import "../../../contracts_common/src/BaseWithStorage/MetaTransactionReceiver.sol"; import "../../../contracts_common/src/Interfaces/ERC721MandatoryTokenReceiver.sol"; contract ERC721BaseToken is ERC721Events, SuperOperators, MetaTransactionReceiver { using AddressUtils for address; bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02; bytes4 internal constant _ERC721_BATCH_RECEIVED = 0x4b808c46; bytes4 internal constant ERC165ID = 0x01ffc9a7; bytes4 internal constant ERC721_MANDATORY_RECEIVER = 0x5e8bf644; mapping (address => uint256) public _numNFTPerAddress; mapping (uint256 => uint256) public _owners; mapping (address => mapping(address => bool)) public _operatorsForAll; mapping (uint256 => address) public _operators; constructor( address metaTransactionContract, address admin ) internal { _admin = admin; _setMetaTransactionProcessor(metaTransactionContract, true); } function _transferFrom(address from, address to, uint256 id) internal { _numNFTPerAddress[from]--; _numNFTPerAddress[to]++; _owners[id] = uint256(to); emit Transfer(from, to, id); } /** * @notice Return the number of Land owned by an address * @param owner The address to look for * @return The number of Land token owned by the address */ function balanceOf(address owner) external view returns (uint256) { require(owner != address(0), "owner is zero address"); return _numNFTPerAddress[owner]; } function _ownerOf(uint256 id) internal view returns (address) { return address(_owners[id]); } function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) { uint256 data = _owners[id]; owner = address(data); operatorEnabled = (data / 2**255) == 1; } /** * @notice Return the owner of a Land * @param id The id of the Land * @return The address of the owner */ function ownerOf(uint256 id) external view returns (address owner) { owner = _ownerOf(id); require(owner != address(0), "token does not exist"); } function _approveFor(address owner, address operator, uint256 id) internal { if(operator == address(0)) { _owners[id] = uint256(owner); // no need to resset the operator, it will be overriden next time } else { _owners[id] = uint256(owner) + 2**255; _operators[id] = operator; } emit Approval(owner, operator, id); } /** * @notice Approve an operator to spend tokens on the sender behalf * @param sender The address giving the approval * @param operator The address receiving the approval * @param id The id of the token */ function approveFor( address sender, address operator, uint256 id ) external { address owner = _ownerOf(id); require(sender != address(0), "sender is zero address"); require( msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender] || _operatorsForAll[sender][msg.sender], "not authorized to approve" ); require(owner == sender, "owner != sender"); _approveFor(owner, operator, id); } /** * @notice Approve an operator to spend tokens on the sender behalf * @param operator The address receiving the approval * @param id The id of the token */ function approve(address operator, uint256 id) external { address owner = _ownerOf(id); require(owner != address(0), "token does not exist"); require( owner == msg.sender || _superOperators[msg.sender] || _operatorsForAll[owner][msg.sender], "not authorized to approve" ); _approveFor(owner, operator, id); } /** * @notice Get the approved operator for a specific token * @param id The id of the token * @return The address of the operator */ function getApproved(uint256 id) external view returns (address) { (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner != address(0), "token does not exist"); if (operatorEnabled) { return _operators[id]; } else { return address(0); } } function _checkTransfer(address from, address to, uint256 id) internal view returns (bool isMetaTx) { (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner != address(0), "token does not exist"); require(owner == from, "not owner in _checkTransfer"); require(to != address(0), "can't send to zero address"); isMetaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !isMetaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender] || (operatorEnabled && _operators[id] == msg.sender), "not approved to transfer" ); } } function _checkInterfaceWith10000Gas(address _contract, bytes4 interfaceId) internal view returns (bool) { bool success; bool result; bytes memory call_data = abi.encodeWithSelector( ERC165ID, interfaceId ); // solium-disable-next-line security/no-inline-assembly assembly { let call_ptr := add(0x20, call_data) let call_size := mload(call_data) let output := mload(0x40) // Find empty storage location using "free memory pointer" mstore(output, 0x0) success := staticcall( 10000, _contract, call_ptr, call_size, output, 0x20 ) // 32 bytes result := mload(output) } // (10000 / 63) "not enough for supportsInterface(...)" // consume all gas, so caller can potentially know that there was not enough gas assert(gasleft() > 158); return success && result; } /** * @notice Transfer a token between 2 addresses * @param from The sender of the token * @param to The recipient of the token * @param id The id of the token */ function transferFrom(address from, address to, uint256 id) external { bool metaTx = _checkTransfer(from, to, id); _transferFrom(from, to, id); if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { require( _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, ""), "erc721 transfer rejected by to" ); } } /** * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer * @param from The sender of the token * @param to The recipient of the token * @param id The id of the token * @param data Additional data */ function safeTransferFrom(address from, address to, uint256 id, bytes memory data) public { bool metaTx = _checkTransfer(from, to, id); _transferFrom(from, to, id); if (to.isContract()) { require( _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, data), "ERC721: transfer rejected by to" ); } } /** * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer * @param from The send of the token * @param to The recipient of the token * @param id The id of the token */ function safeTransferFrom(address from, address to, uint256 id) external { safeTransferFrom(from, to, id, ""); } /** * @notice Transfer many tokens between 2 addresses * @param from The sender of the token * @param to The recipient of the token * @param ids The ids of the tokens * @param data additional data */ function batchTransferFrom(address from, address to, uint256[] calldata ids, bytes calldata data) external { _batchTransferFrom(from, to, ids, data, false); } function _batchTransferFrom(address from, address to, uint256[] memory ids, bytes memory data, bool safe) internal { bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; bool authorized = msg.sender == from || metaTx || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender]; require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); uint256 numTokens = ids.length; for(uint256 i = 0; i < numTokens; i ++) { uint256 id = ids[i]; (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner == from, "not owner in batchTransferFrom"); require(authorized || (operatorEnabled && _operators[id] == msg.sender), "not authorized"); _owners[id] = uint256(to); emit Transfer(from, to, id); } if (from != to) { _numNFTPerAddress[from] -= numTokens; _numNFTPerAddress[to] += numTokens; } if (to.isContract() && (safe || _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER))) { require( _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } /** * @notice Transfer many tokens between 2 addresses ensuring the receiving contract has a receiver method * @param from The sender of the token * @param to The recipient of the token * @param ids The ids of the tokens * @param data additional data */ function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, bytes calldata data) external { _batchTransferFrom(from, to, ids, data, true); } /** * @notice Check if the contract supports an interface * 0x01ffc9a7 is ERC-165 * 0x80ac58cd is ERC-721 * @param id The id of the interface * @return True if the interface is supported */ function supportsInterface(bytes4 id) external pure returns (bool) { return id == 0x01ffc9a7 || id == 0x80ac58cd; } /** * @notice Set the approval for an operator to manage all the tokens of the sender * @param sender The address giving the approval * @param operator The address receiving the approval * @param approved The determination of the approval */ function setApprovalForAllFor( address sender, address operator, bool approved ) external { require(sender != address(0), "Invalid sender address"); require( msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender], "not authorized to approve for all" ); _setApprovalForAll(sender, operator, approved); } /** * @notice Set the approval for an operator to manage all the tokens of the sender * @param operator The address receiving the approval * @param approved The determination of the approval */ function setApprovalForAll(address operator, bool approved) external { _setApprovalForAll(msg.sender, operator, approved); } function _setApprovalForAll( address sender, address operator, bool approved ) internal { require( !_superOperators[operator], "super operator can't have their approvalForAll changed" ); _operatorsForAll[sender][operator] = approved; emit ApprovalForAll(sender, operator, approved); } /** * @notice Check if the sender approved the operator * @param owner The address of the owner * @param operator The address of the operator * @return The status of the approval */ function isApprovedForAll(address owner, address operator) external view returns (bool isOperator) { return _operatorsForAll[owner][operator] || _superOperators[operator]; } function _burn(address from, address owner, uint256 id) public { require(from == owner, "not owner"); _owners[id] = 2**160; // cannot mint it again _numNFTPerAddress[from]--; emit Transfer(from, address(0), id); } /// @notice Burns token `id`. /// @param id token which will be burnt. function burn(uint256 id) external { _burn(msg.sender, _ownerOf(id), id); } /// @notice Burn token`id` from `from`. /// @param from address whose token is to be burnt. /// @param id token which will be burnt. function burnFrom(address from, uint256 id) external { require(from != address(0), "Invalid sender address"); (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require( msg.sender == from || _metaTransactionContracts[msg.sender] || (operatorEnabled && _operators[id] == msg.sender) || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to burn" ); _burn(from, owner, id); } function _checkOnERC721Received(address operator, address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { bytes4 retval = ERC721TokenReceiver(to).onERC721Received(operator, from, tokenId, _data); return (retval == _ERC721_RECEIVED); } function _checkOnERC721BatchReceived(address operator, address from, address to, uint256[] memory ids, bytes memory _data) internal returns (bool) { bytes4 retval = ERC721MandatoryTokenReceiver(to).onERC721BatchReceived(operator, from, ids, _data); return (retval == _ERC721_BATCH_RECEIVED); } } /* solhint-disable func-order, code-complexity */ pragma solidity 0.5.9; import "./ERC721BaseToken.sol"; contract LandBaseToken is ERC721BaseToken { // Our grid is 408 x 408 lands uint256 internal constant GRID_SIZE = 408; uint256 internal constant LAYER = 0xFF00000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_1x1 = 0x0000000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_3x3 = 0x0100000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_6x6 = 0x0200000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_12x12 = 0x0300000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_24x24 = 0x0400000000000000000000000000000000000000000000000000000000000000; mapping(address => bool) internal _minters; event Minter(address superOperator, bool enabled); /// @notice Enable or disable the ability of `minter` to mint tokens /// @param minter address that will be given/removed minter right. /// @param enabled set whether the minter is enabled or disabled. function setMinter(address minter, bool enabled) external { require( msg.sender == _admin, "only admin is allowed to add minters" ); _minters[minter] = enabled; emit Minter(minter, enabled); } /// @notice check whether address `who` is given minter rights. /// @param who The address to query. /// @return whether the address has minter rights. function isMinter(address who) public view returns (bool) { return _minters[who]; } constructor( address metaTransactionContract, address admin ) public ERC721BaseToken(metaTransactionContract, admin) { } /// @notice total width of the map /// @return width function width() external returns(uint256) { return GRID_SIZE; } /// @notice total height of the map /// @return height function height() external returns(uint256) { return GRID_SIZE; } /// @notice x coordinate of Land token /// @param id tokenId /// @return the x coordinates function x(uint256 id) external returns(uint256) { require(_ownerOf(id) != address(0), "token does not exist"); return id % GRID_SIZE; } /// @notice y coordinate of Land token /// @param id tokenId /// @return the y coordinates function y(uint256 id) external returns(uint256) { require(_ownerOf(id) != address(0), "token does not exist"); return id / GRID_SIZE; } /** * @notice Mint a new quad (aligned to a quad tree with size 3, 6, 12 or 24 only) * @param to The recipient of the new quad * @param size The size of the new quad * @param x The top left x coordinate of the new quad * @param y The top left y coordinate of the new quad * @param data extra data to pass to the transfer */ function mintQuad(address to, uint256 size, uint256 x, uint256 y, bytes calldata data) external { require(to != address(0), "to is zero address"); require( isMinter(msg.sender), "Only a minter can mint" ); require(x % size == 0 && y % size == 0, "Invalid coordinates"); require(x <= GRID_SIZE - size && y <= GRID_SIZE - size, "Out of bounds"); uint256 quadId; uint256 id = x + y * GRID_SIZE; if (size == 1) { quadId = id; } else if (size == 3) { quadId = LAYER_3x3 + id; } else if (size == 6) { quadId = LAYER_6x6 + id; } else if (size == 12) { quadId = LAYER_12x12 + id; } else if (size == 24) { quadId = LAYER_24x24 + id; } else { require(false, "Invalid size"); } require(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == 0, "Already minted as 24x24"); uint256 toX = x+size; uint256 toY = y+size; if (size <= 12) { require( _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == 0, "Already minted as 12x12" ); } else { for (uint256 x12i = x; x12i < toX; x12i += 12) { for (uint256 y12i = y; y12i < toY; y12i += 12) { uint256 id12x12 = LAYER_12x12 + x12i + y12i * GRID_SIZE; require(_owners[id12x12] == 0, "Already minted as 12x12"); } } } if (size <= 6) { require(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE] == 0, "Already minted as 6x6"); } else { for (uint256 x6i = x; x6i < toX; x6i += 6) { for (uint256 y6i = y; y6i < toY; y6i += 6) { uint256 id6x6 = LAYER_6x6 + x6i + y6i * GRID_SIZE; require(_owners[id6x6] == 0, "Already minted as 6x6"); } } } if (size <= 3) { require(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE] == 0, "Already minted as 3x3"); } else { for (uint256 x3i = x; x3i < toX; x3i += 3) { for (uint256 y3i = y; y3i < toY; y3i += 3) { uint256 id3x3 = LAYER_3x3 + x3i + y3i * GRID_SIZE; require(_owners[id3x3] == 0, "Already minted as 3x3"); } } } for (uint256 i = 0; i < size*size; i++) { uint256 id = _idInPath(i, size, x, y); require(_owners[id] == 0, "Already minted"); emit Transfer(address(0), to, id); } _owners[quadId] = uint256(to); _numNFTPerAddress[to] += size * size; _checkBatchReceiverAcceptQuad(msg.sender, address(0), to, size, x, y, data); } function _idInPath(uint256 i, uint256 size, uint256 x, uint256 y) internal pure returns(uint256) { uint256 row = i / size; if(row % 2 == 0) { // alow ids to follow a path in a quad return (x + (i%size)) + ((y + row) * GRID_SIZE); } else { return ((x + size) - (1 + i%size)) + ((y + row) * GRID_SIZE); } } /// @notice transfer one quad (aligned to a quad tree with size 3, 6, 12 or 24 only) /// @param from current owner of the quad /// @param to destination /// @param size size of the quad /// @param x The top left x coordinate of the quad /// @param y The top left y coordinate of the quad /// @param data additional data function transferQuad(address from, address to, uint256 size, uint256 x, uint256 y, bytes calldata data) external { require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !metaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to transferQuad" ); } _transferQuad(from, to, size, x, y); _numNFTPerAddress[from] -= size * size; _numNFTPerAddress[to] += size * size; _checkBatchReceiverAcceptQuad(metaTx ? from : msg.sender, from, to, size, x, y, data); } function _checkBatchReceiverAcceptQuad( address operator, address from, address to, uint256 size, uint256 x, uint256 y, bytes memory data ) internal { if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { uint256[] memory ids = new uint256[](size*size); for (uint256 i = 0; i < size*size; i++) { ids[i] = _idInPath(i, size, x, y); } require( _checkOnERC721BatchReceived(operator, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } /// @notice transfer multiple quad (aligned to a quad tree with size 3, 6, 12 or 24 only) /// @param from current owner of the quad /// @param to destination /// @param sizes list of sizes for each quad /// @param xs list of top left x coordinates for each quad /// @param ys list of top left y coordinates for each quad /// @param data additional data function batchTransferQuad( address from, address to, uint256[] calldata sizes, uint256[] calldata xs, uint256[] calldata ys, bytes calldata data ) external { require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); require(sizes.length == xs.length && xs.length == ys.length, "invalid data"); bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !metaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to transferMultiQuads" ); } uint256 numTokensTransfered = 0; for (uint256 i = 0; i < sizes.length; i++) { uint256 size = sizes[i]; _transferQuad(from, to, size, xs[i], ys[i]); numTokensTransfered += size * size; } _numNFTPerAddress[from] -= numTokensTransfered; _numNFTPerAddress[to] += numTokensTransfered; if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { uint256[] memory ids = new uint256[](numTokensTransfered); uint256 counter = 0; for (uint256 j = 0; j < sizes.length; j++) { uint256 size = sizes[j]; for (uint256 i = 0; i < size*size; i++) { ids[counter] = _idInPath(i, size, xs[j], ys[j]); counter++; } } require( _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } function _transferQuad(address from, address to, uint256 size, uint256 x, uint256 y) internal { if (size == 1) { uint256 id1x1 = x + y * GRID_SIZE; address owner = _ownerOf(id1x1); require(owner != address(0), "token does not exist"); require(owner == from, "not owner in _transferQuad"); _owners[id1x1] = uint256(to); } else { _regroup(from, to, size, x, y); } for (uint256 i = 0; i < size*size; i++) { emit Transfer(from, to, _idInPath(i, size, x, y)); } } function _checkAndClear(address from, uint256 id) internal returns(bool) { uint256 owner = _owners[id]; if (owner != 0) { require(address(owner) == from, "not owner"); _owners[id] = 0; return true; } return false; } function _regroup(address from, address to, uint256 size, uint256 x, uint256 y) internal { require(x % size == 0 && y % size == 0, "Invalid coordinates"); require(x <= GRID_SIZE - size && y <= GRID_SIZE - size, "Out of bounds"); if (size == 3) { _regroup3x3(from, to, x, y, true); } else if (size == 6) { _regroup6x6(from, to, x, y, true); } else if (size == 12) { _regroup12x12(from, to, x, y, true); } else if (size == 24) { _regroup24x24(from, to, x, y, true); } else { require(false, "Invalid size"); } } function _regroup3x3(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_3x3 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+3; xi++) { for (uint256 yi = y; yi < y+3; yi++) { ownerOfAll = _checkAndClear(from, xi + yi * GRID_SIZE) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE] == uint256(from) || _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup6x6(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_6x6 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+6; xi += 3) { for (uint256 yi = y; yi < y+6; yi += 3) { bool ownAllIndividual = _regroup3x3(from, to, xi, yi, false); uint256 id3x3 = LAYER_3x3 + xi + yi * GRID_SIZE; uint256 owner3x3 = _owners[id3x3]; if (owner3x3 != 0) { if(!ownAllIndividual) { require(owner3x3 == uint256(from), "not owner of 3x3 quad"); } _owners[id3x3] = 0; } ownerOfAll = (ownAllIndividual || owner3x3 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup12x12(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_12x12 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+12; xi += 6) { for (uint256 yi = y; yi < y+12; yi += 6) { bool ownAllIndividual = _regroup6x6(from, to, xi, yi, false); uint256 id6x6 = LAYER_6x6 + xi + yi * GRID_SIZE; uint256 owner6x6 = _owners[id6x6]; if (owner6x6 != 0) { if(!ownAllIndividual) { require(owner6x6 == uint256(from), "not owner of 6x6 quad"); } _owners[id6x6] = 0; } ownerOfAll = (ownAllIndividual || owner6x6 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup24x24(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_24x24 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+24; xi += 12) { for (uint256 yi = y; yi < y+24; yi += 12) { bool ownAllIndividual = _regroup12x12(from, to, xi, yi, false); uint256 id12x12 = LAYER_12x12 + xi + yi * GRID_SIZE; uint256 owner12x12 = _owners[id12x12]; if (owner12x12 != 0) { if(!ownAllIndividual) { require(owner12x12 == uint256(from), "not owner of 12x12 quad"); } _owners[id12x12] = 0; } ownerOfAll = (ownAllIndividual || owner12x12 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from), "not owner of all sub quads not parent quad" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll || _owners[quadId] == uint256(from); } function _ownerOf(uint256 id) internal view returns (address) { require(id & LAYER == 0, "Invalid token id"); uint256 x = id % GRID_SIZE; uint256 y = id / GRID_SIZE; uint256 owner1x1 = _owners[id]; if (owner1x1 != 0) { return address(owner1x1); // cast to zero } else { address owner3x3 = address(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE]); if (owner3x3 != address(0)) { return owner3x3; } else { address owner6x6 = address(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE]); if (owner6x6 != address(0)) { return owner6x6; } else { address owner12x12 = address(_owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE]); if (owner12x12 != address(0)) { return owner12x12; } else { return address(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE]); } } } } } function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) { require(id & LAYER == 0, "Invalid token id"); uint256 x = id % GRID_SIZE; uint256 y = id / GRID_SIZE; uint256 owner1x1 = _owners[id]; if (owner1x1 != 0) { owner = address(owner1x1); operatorEnabled = (owner1x1 / 2**255) == 1; } else { address owner3x3 = address(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE]); if (owner3x3 != address(0)) { owner = owner3x3; operatorEnabled = false; } else { address owner6x6 = address(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE]); if (owner6x6 != address(0)) { owner = owner6x6; operatorEnabled = false; } else { address owner12x12 = address(_owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE]); if (owner12x12 != address(0)) { owner = owner12x12; operatorEnabled = false; } else { owner = address(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE]); operatorEnabled = false; } } } } } } /* solhint-disable not-rely-on-time, func-order */ pragma solidity 0.5.9; import "../../contracts_common/src/Libraries/SafeMathWithRequire.sol"; import "../Land.sol"; import "../../contracts_common/src/Interfaces/ERC20.sol"; import "../../contracts_common/src/BaseWithStorage/MetaTransactionReceiver.sol"; import "../../contracts_common/src/Interfaces/Medianizer.sol"; import "../ReferralValidator/ReferralValidator.sol"; /** * @title Land Sale contract with referral that supports also DAI and ETH as payment * @notice This contract mananges the sale of our lands */ contract LandSaleWithReferral is MetaTransactionReceiver, ReferralValidator { using SafeMathWithRequire for uint256; uint256 internal constant GRID_SIZE = 408; // 408 is the size of the Land uint256 internal constant daiPrice = 14400000000000000; Land internal _land; ERC20 internal _sand; Medianizer private _medianizer; ERC20 private _dai; address payable internal _wallet; uint256 internal _expiryTime; bytes32 internal _merkleRoot; bool _sandEnabled = false; bool _etherEnabled = true; bool _daiEnabled = false; event LandQuadPurchased( address indexed buyer, address indexed to, uint256 indexed topCornerId, uint256 size, uint256 price, address token, uint256 amountPaid ); constructor( address landAddress, address sandContractAddress, address initialMetaTx, address admin, address payable initialWalletAddress, bytes32 merkleRoot, uint256 expiryTime, address medianizerContractAddress, address daiTokenContractAddress, address initialSigningWallet, uint256 initialMaxCommissionRate ) public ReferralValidator( initialSigningWallet, initialMaxCommissionRate ) { _land = Land(landAddress); _sand = ERC20(sandContractAddress); _setMetaTransactionProcessor(initialMetaTx, true); _wallet = initialWalletAddress; _merkleRoot = merkleRoot; _expiryTime = expiryTime; _medianizer = Medianizer(medianizerContractAddress); _dai = ERC20(daiTokenContractAddress); _admin = admin; } /// @notice set the wallet receiving the proceeds /// @param newWallet address of the new receiving wallet function setReceivingWallet(address payable newWallet) external{ require(newWallet != address(0), "receiving wallet cannot be zero address"); require(msg.sender == _admin, "only admin can change the receiving wallet"); _wallet = newWallet; } /// @notice enable/disable DAI payment for Lands /// @param enabled whether to enable or disable function setDAIEnabled(bool enabled) external { require(msg.sender == _admin, "only admin can enable/disable DAI"); _daiEnabled = enabled; } /// @notice return whether DAI payments are enabled /// @return whether DAI payments are enabled function isDAIEnabled() external view returns (bool) { return _daiEnabled; } /// @notice enable/disable ETH payment for Lands /// @param enabled whether to enable or disable function setETHEnabled(bool enabled) external { require(msg.sender == _admin, "only admin can enable/disable ETH"); _etherEnabled = enabled; } /// @notice return whether ETH payments are enabled /// @return whether ETH payments are enabled function isETHEnabled() external view returns (bool) { return _etherEnabled; } /// @notice enable/disable the specific SAND payment for Lands /// @param enabled whether to enable or disable function setSANDEnabled(bool enabled) external { require(msg.sender == _admin, "only admin can enable/disable SAND"); _sandEnabled = enabled; } /// @notice return whether the specific SAND payments are enabled /// @return whether the specific SAND payments are enabled function isSANDEnabled() external view returns (bool) { return _sandEnabled; } function _checkValidity( address buyer, address reserved, uint256 x, uint256 y, uint256 size, uint256 price, bytes32 salt, bytes32[] memory proof ) internal view { /* solium-disable-next-line security/no-block-members */ require(block.timestamp < _expiryTime, "sale is over"); require(buyer == msg.sender || _metaTransactionContracts[msg.sender], "not authorized"); require(reserved == address(0) || reserved == buyer, "cannot buy reserved Land"); bytes32 leaf = _generateLandHash(x, y, size, price, reserved, salt); require( _verify(proof, leaf), "Invalid land provided" ); } function _mint(address buyer, address to, uint256 x, uint256 y, uint256 size, uint256 price, address token, uint256 tokenAmount) internal { uint256[] memory junctions = new uint256[](0); _land.mintQuad(to, size, x, y, abi.encode(to, junctions)); emit LandQuadPurchased(buyer, to, x + (y * GRID_SIZE), size, price, token, tokenAmount); } /** * @notice buy Land with SAND using the merkle proof associated with it * @param buyer address that perform the payment * @param to address that will own the purchased Land * @param reserved the reserved address (if any) * @param x x coordinate of the Land * @param y y coordinate of the Land * @param size size of the pack of Land to purchase * @param priceInSand price in SAND to purchase that Land * @param proof merkleProof for that particular Land * @return The address of the operator */ function buyLandWithSand( address buyer, address to, address reserved, uint256 x, uint256 y, uint256 size, uint256 priceInSand, bytes32 salt, bytes32[] calldata proof, bytes calldata referral ) external { require(_sandEnabled, "sand payments not enabled"); _checkValidity(buyer, reserved, x, y, size, priceInSand, salt, proof); handleReferralWithERC20( buyer, priceInSand, referral, _wallet, address(_sand) ); _mint(buyer, to, x, y, size, priceInSand, address(_sand), priceInSand); } /** * @notice buy Land with ETH using the merkle proof associated with it * @param buyer address that perform the payment * @param to address that will own the purchased Land * @param reserved the reserved address (if any) * @param x x coordinate of the Land * @param y y coordinate of the Land * @param size size of the pack of Land to purchase * @param priceInSand price in SAND to purchase that Land * @param proof merkleProof for that particular Land * @param referral the referral used by the buyer * @return The address of the operator */ function buyLandWithETH( address buyer, address to, address reserved, uint256 x, uint256 y, uint256 size, uint256 priceInSand, bytes32 salt, bytes32[] calldata proof, bytes calldata referral ) external payable { require(_etherEnabled, "ether payments not enabled"); _checkValidity(buyer, reserved, x, y, size, priceInSand, salt, proof); uint256 ETHRequired = getEtherAmountWithSAND(priceInSand); require(msg.value >= ETHRequired, "not enough ether sent"); if (msg.value - ETHRequired > 0) { msg.sender.transfer(msg.value - ETHRequired); // refund extra } handleReferralWithETH( ETHRequired, referral, _wallet ); _mint(buyer, to, x, y, size, priceInSand, address(0), ETHRequired); } /** * @notice buy Land with DAI using the merkle proof associated with it * @param buyer address that perform the payment * @param to address that will own the purchased Land * @param reserved the reserved address (if any) * @param x x coordinate of the Land * @param y y coordinate of the Land * @param size size of the pack of Land to purchase * @param priceInSand price in SAND to purchase that Land * @param proof merkleProof for that particular Land * @return The address of the operator */ function buyLandWithDAI( address buyer, address to, address reserved, uint256 x, uint256 y, uint256 size, uint256 priceInSand, bytes32 salt, bytes32[] calldata proof, bytes calldata referral ) external { require(_daiEnabled, "dai payments not enabled"); _checkValidity(buyer, reserved, x, y, size, priceInSand, salt, proof); uint256 DAIRequired = priceInSand.mul(daiPrice).div(1000000000000000000); handleReferralWithERC20( buyer, DAIRequired, referral, _wallet, address(_dai) ); _mint(buyer, to, x, y, size, priceInSand, address(_dai), DAIRequired); } /** * @notice Gets the expiry time for the current sale * @return The expiry time, as a unix epoch */ function getExpiryTime() external view returns(uint256) { return _expiryTime; } /** * @notice Gets the Merkle root associated with the current sale * @return The Merkle root, as a bytes32 hash */ function merkleRoot() external view returns(bytes32) { return _merkleRoot; } function _generateLandHash( uint256 x, uint256 y, uint256 size, uint256 price, address reserved, bytes32 salt ) internal pure returns ( bytes32 ) { return keccak256( abi.encodePacked( x, y, size, price, reserved, salt ) ); } 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; } /** * @notice Returns the amount of ETH for a specific amount of SAND * @param sandAmount An amount of SAND * @return The amount of ETH */ function getEtherAmountWithSAND(uint256 sandAmount) public view returns (uint256) { uint256 ethUsdPair = getEthUsdPair(); return sandAmount.mul(daiPrice).div(ethUsdPair); } /** * @notice Gets the ETHUSD pair from the Medianizer contract * @return The pair as an uint256 */ function getEthUsdPair() internal view returns (uint256) { bytes32 pair = _medianizer.read(); return uint256(pair); } } /* solhint-disable not-rely-on-time, func-order */ pragma solidity 0.5.9; import "../../contracts_common/src/Libraries/SigUtil.sol"; import "../../contracts_common/src/Libraries/SafeMathWithRequire.sol"; import "../../contracts_common/src/Interfaces/ERC20.sol"; import "../../contracts_common/src/BaseWithStorage/Admin.sol"; /** * @title Referral Validator * @notice This contract verifies if a referral is valid */ contract ReferralValidator is Admin { address private _signingWallet; uint256 private _maxCommissionRate; mapping (address => uint256) private _previousSigningWallets; uint256 private _previousSigningDelay = 60 * 60 * 24 * 10; event ReferralUsed( address indexed referrer, address indexed referee, address indexed token, uint256 amount, uint256 commission, uint256 commissionRate ); constructor( address initialSigningWallet, uint256 initialMaxCommissionRate ) public { _signingWallet = initialSigningWallet; _maxCommissionRate = initialMaxCommissionRate; } /** * @notice Update the signing wallet * @param newSigningWallet The new address of the signing wallet */ function updateSigningWallet(address newSigningWallet) external { require(_admin == msg.sender, "Sender not admin"); _previousSigningWallets[_signingWallet] = now + _previousSigningDelay; _signingWallet = newSigningWallet; } // TODO: Check if this function is really useful /** * @notice Update the maximum commission rate * @param newMaxCommissionRate The new maximum commission rate */ function updateMaxCommissionRate(uint256 newMaxCommissionRate) external { require(_admin == msg.sender, "Sender not admin"); _maxCommissionRate = newMaxCommissionRate; } function handleReferralWithETH( uint256 amount, bytes memory referral, address payable destination ) internal { uint256 amountForDestination = amount; if (referral.length > 0) { ( bytes memory signature, address referrer, address referee, uint256 expiryTime, uint256 commissionRate ) = decodeReferral(referral); uint256 commission = 0; if (isReferralValid(signature, referrer, referee, expiryTime, commissionRate)) { commission = SafeMathWithRequire.div( SafeMathWithRequire.mul(amount, commissionRate), 10000 ); emit ReferralUsed( referrer, referee, address(0), amount, commission, commissionRate ); amountForDestination = SafeMathWithRequire.sub( amountForDestination, commission ); } if (commission > 0) { address(uint160(referrer)).transfer(commission); } } destination.transfer(amountForDestination); } function handleReferralWithERC20( address buyer, uint256 amount, bytes memory referral, address payable destination, address tokenAddress ) internal { ERC20 token = ERC20(tokenAddress); uint256 amountForDestination = amount; if (referral.length > 0) { ( bytes memory signature, address referrer, address referee, uint256 expiryTime, uint256 commissionRate ) = decodeReferral(referral); uint256 commission = 0; if (isReferralValid(signature, referrer, referee, expiryTime, commissionRate)) { commission = SafeMathWithRequire.div( SafeMathWithRequire.mul(amount, commissionRate), 10000 ); emit ReferralUsed( referrer, referee, tokenAddress, amount, commission, commissionRate ); amountForDestination = SafeMathWithRequire.sub( amountForDestination, commission ); } if (commission > 0) { require(token.transferFrom(buyer, referrer, commission), "commision transfer failed"); } } require(token.transferFrom(buyer, destination, amountForDestination), "payment transfer failed"); } /** * @notice Check if a referral is valid * @param signature The signature to check (signed referral) * @param referrer The address of the referrer * @param referee The address of the referee * @param expiryTime The expiry time of the referral * @param commissionRate The commissionRate of the referral * @return True if the referral is valid */ function isReferralValid( bytes memory signature, address referrer, address referee, uint256 expiryTime, uint256 commissionRate ) public view returns ( bool ) { if (commissionRate > _maxCommissionRate || referrer == referee || now > expiryTime) { return false; } bytes32 hashedData = keccak256( abi.encodePacked( referrer, referee, expiryTime, commissionRate ) ); address signer = SigUtil.recover( keccak256( abi.encodePacked("\\x19Ethereum Signed Message:\ 32", hashedData) ), signature ); if (_previousSigningWallets[signer] >= now) { return true; } return _signingWallet == signer; } function decodeReferral( bytes memory referral ) public pure returns ( bytes memory, address, address, uint256, uint256 ) { ( bytes memory signature, address referrer, address referee, uint256 expiryTime, uint256 commissionRate ) = abi.decode(referral, (bytes, address, address, uint256, uint256)); return ( signature, referrer, referee, expiryTime, commissionRate ); } }
File 2 of 4: MultiSigWalletWithDailyLimit
contract Factory { /* * Events */ event ContractInstantiation(address sender, address instantiation); /* * Storage */ mapping(address => bool) public isInstantiation; mapping(address => address[]) public instantiations; /* * Public functions */ /// @dev Returns number of instantiations by creator. /// @param creator Contract creator. /// @return Returns number of instantiations by creator. function getInstantiationCount(address creator) public constant returns (uint) { return instantiations[creator].length; } /* * Internal functions */ /// @dev Registers contract in factory registry. /// @param instantiation Address of contract instantiation. function register(address instantiation) internal { isInstantiation[instantiation] = true; instantiations[msg.sender].push(instantiation); ContractInstantiation(msg.sender, instantiation); } } /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. /// @author Stefan George - <[email protected]> contract MultiSigWallet { /* * Events */ event Confirmation(address indexed sender, uint indexed transactionId); event Revocation(address indexed sender, uint indexed transactionId); event Submission(uint indexed transactionId); event Execution(uint indexed transactionId); event ExecutionFailure(uint indexed transactionId); event Deposit(address indexed sender, uint value); event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event RequirementChange(uint required); /* * Constants */ uint constant public MAX_OWNER_COUNT = 50; /* * Storage */ mapping (uint => Transaction) public transactions; mapping (uint => mapping (address => bool)) public confirmations; mapping (address => bool) public isOwner; address[] public owners; uint public required; uint public transactionCount; struct Transaction { address destination; uint value; bytes data; bool executed; } /* * Modifiers */ modifier onlyWallet() { require(msg.sender == address(this)); _; } modifier ownerDoesNotExist(address owner) { require(!isOwner[owner]); _; } modifier ownerExists(address owner) { require(isOwner[owner]); _; } modifier transactionExists(uint transactionId) { require(transactions[transactionId].destination != 0); _; } modifier confirmed(uint transactionId, address owner) { require(confirmations[transactionId][owner]); _; } modifier notConfirmed(uint transactionId, address owner) { require(!confirmations[transactionId][owner]); _; } modifier notExecuted(uint transactionId) { require(!transactions[transactionId].executed); _; } modifier notNull(address _address) { require(_address != 0); _; } modifier validRequirement(uint ownerCount, uint _required) { require(ownerCount <= MAX_OWNER_COUNT && _required <= ownerCount && _required != 0 && ownerCount != 0); _; } /// @dev Fallback function allows to deposit ether. function() payable { if (msg.value > 0) Deposit(msg.sender, msg.value); } /* * Public functions */ /// @dev Contract constructor sets initial owners and required number of confirmations. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. function MultiSigWallet(address[] _owners, uint _required) public validRequirement(_owners.length, _required) { for (uint i=0; i<_owners.length; i++) { require(!isOwner[_owners[i]] && _owners[i] != 0); isOwner[_owners[i]] = true; } owners = _owners; required = _required; } /// @dev Allows to add a new owner. Transaction has to be sent by wallet. /// @param owner Address of new owner. function addOwner(address owner) public onlyWallet ownerDoesNotExist(owner) notNull(owner) validRequirement(owners.length + 1, required) { isOwner[owner] = true; owners.push(owner); OwnerAddition(owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. /// @param owner Address of owner. function removeOwner(address owner) public onlyWallet ownerExists(owner) { isOwner[owner] = false; for (uint i=0; i<owners.length - 1; i++) if (owners[i] == owner) { owners[i] = owners[owners.length - 1]; break; } owners.length -= 1; if (required > owners.length) changeRequirement(owners.length); OwnerRemoval(owner); } /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. /// @param owner Address of owner to be replaced. /// @param newOwner Address of new owner. function replaceOwner(address owner, address newOwner) public onlyWallet ownerExists(owner) ownerDoesNotExist(newOwner) { for (uint i=0; i<owners.length; i++) if (owners[i] == owner) { owners[i] = newOwner; break; } isOwner[owner] = false; isOwner[newOwner] = true; OwnerRemoval(owner); OwnerAddition(newOwner); } /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. /// @param _required Number of required confirmations. function changeRequirement(uint _required) public onlyWallet validRequirement(owners.length, _required) { required = _required; RequirementChange(_required); } /// @dev Allows an owner to submit and confirm a transaction. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function submitTransaction(address destination, uint value, bytes data) public returns (uint transactionId) { transactionId = addTransaction(destination, value, data); confirmTransaction(transactionId); } /// @dev Allows an owner to confirm a transaction. /// @param transactionId Transaction ID. function confirmTransaction(uint transactionId) public ownerExists(msg.sender) transactionExists(transactionId) notConfirmed(transactionId, msg.sender) { confirmations[transactionId][msg.sender] = true; Confirmation(msg.sender, transactionId); executeTransaction(transactionId); } /// @dev Allows an owner to revoke a confirmation for a transaction. /// @param transactionId Transaction ID. function revokeConfirmation(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { confirmations[transactionId][msg.sender] = false; Revocation(msg.sender, transactionId); } /// @dev Allows anyone to execute a confirmed transaction. /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction storage txn = transactions[transactionId]; txn.executed = true; if (txn.destination.call.value(txn.value)(txn.data)) Execution(transactionId); else { ExecutionFailure(transactionId); txn.executed = false; } } } /// @dev Returns the confirmation status of a transaction. /// @param transactionId Transaction ID. /// @return Confirmation status. function isConfirmed(uint transactionId) public constant returns (bool) { uint count = 0; for (uint i=0; i<owners.length; i++) { if (confirmations[transactionId][owners[i]]) count += 1; if (count == required) return true; } } /* * Internal functions */ /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function addTransaction(address destination, uint value, bytes data) internal notNull(destination) returns (uint transactionId) { transactionId = transactionCount; transactions[transactionId] = Transaction({ destination: destination, value: value, data: data, executed: false }); transactionCount += 1; Submission(transactionId); } /* * Web3 call functions */ /// @dev Returns number of confirmations of a transaction. /// @param transactionId Transaction ID. /// @return Number of confirmations. function getConfirmationCount(uint transactionId) public constant returns (uint count) { for (uint i=0; i<owners.length; i++) if (confirmations[transactionId][owners[i]]) count += 1; } /// @dev Returns total number of transactions after filers are applied. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Total number of transactions after filters are applied. function getTransactionCount(bool pending, bool executed) public constant returns (uint count) { for (uint i=0; i<transactionCount; i++) if ( pending && !transactions[i].executed || executed && transactions[i].executed) count += 1; } /// @dev Returns list of owners. /// @return List of owner addresses. function getOwners() public constant returns (address[]) { return owners; } /// @dev Returns array with owner addresses, which confirmed transaction. /// @param transactionId Transaction ID. /// @return Returns array of owner addresses. function getConfirmations(uint transactionId) public constant returns (address[] _confirmations) { address[] memory confirmationsTemp = new address[](owners.length); uint count = 0; uint i; for (i=0; i<owners.length; i++) if (confirmations[transactionId][owners[i]]) { confirmationsTemp[count] = owners[i]; count += 1; } _confirmations = new address[](count); for (i=0; i<count; i++) _confirmations[i] = confirmationsTemp[i]; } /// @dev Returns list of transaction IDs in defined range. /// @param from Index start position of transaction array. /// @param to Index end position of transaction array. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Returns array of transaction IDs. function getTransactionIds(uint from, uint to, bool pending, bool executed) public constant returns (uint[] _transactionIds) { uint[] memory transactionIdsTemp = new uint[](transactionCount); uint count = 0; uint i; for (i=0; i<transactionCount; i++) if ( pending && !transactions[i].executed || executed && transactions[i].executed) { transactionIdsTemp[count] = i; count += 1; } _transactionIds = new uint[](to - from); for (i=from; i<to; i++) _transactionIds[i - from] = transactionIdsTemp[i]; } } /// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig. /// @author Stefan George - <[email protected]> contract MultiSigWalletWithDailyLimit is MultiSigWallet { /* * Events */ event DailyLimitChange(uint dailyLimit); /* * Storage */ uint public dailyLimit; uint public lastDay; uint public spentToday; /* * Public functions */ /// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit) public MultiSigWallet(_owners, _required) { dailyLimit = _dailyLimit; } /// @dev Allows to change the daily limit. Transaction has to be sent by wallet. /// @param _dailyLimit Amount in wei. function changeDailyLimit(uint _dailyLimit) public onlyWallet { dailyLimit = _dailyLimit; DailyLimitChange(_dailyLimit); } /// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached. /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { Transaction storage txn = transactions[transactionId]; bool _confirmed = isConfirmed(transactionId); if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) { txn.executed = true; if (!_confirmed) spentToday += txn.value; if (txn.destination.call.value(txn.value)(txn.data)) Execution(transactionId); else { ExecutionFailure(transactionId); txn.executed = false; if (!_confirmed) spentToday -= txn.value; } } } /* * Internal functions */ /// @dev Returns if amount is within daily limit and resets spentToday after one day. /// @param amount Amount to withdraw. /// @return Returns if amount is under daily limit. function isUnderLimit(uint amount) internal returns (bool) { if (now > lastDay + 24 hours) { lastDay = now; spentToday = 0; } if (spentToday + amount > dailyLimit || spentToday + amount < spentToday) return false; return true; } /* * Web3 call functions */ /// @dev Returns maximum withdraw amount. /// @return Returns amount. function calcMaxWithdraw() public constant returns (uint) { if (now > lastDay + 24 hours) return dailyLimit; if (dailyLimit < spentToday) return 0; return dailyLimit - spentToday; } }
File 3 of 4: Land
pragma solidity ^0.5.2; contract Admin { address internal _admin; event AdminChanged(address oldAdmin, address newAdmin); /// @notice gives the current administrator of this contract. /// @return the current administrator of this contract. function getAdmin() external view returns (address) { return _admin; } /// @notice change the administrator to be `newAdmin`. /// @param newAdmin address of the new administrator. function changeAdmin(address newAdmin) external { require(msg.sender == _admin, "only admin can change admin"); emit AdminChanged(_admin, newAdmin); _admin = newAdmin; } modifier onlyAdmin() { require (msg.sender == _admin, "only admin allowed"); _; } } pragma solidity ^0.5.2; import "./Admin.sol"; contract MetaTransactionReceiver is Admin{ mapping(address => bool) internal _metaTransactionContracts; event MetaTransactionProcessor(address metaTransactionProcessor, bool enabled); /// @notice Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights). /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights. /// @param enabled set whether the metaTransactionProcessor is enabled or disabled. function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) public { require( msg.sender == _admin, "only admin can setup metaTransactionProcessors" ); _setMetaTransactionProcessor(metaTransactionProcessor, enabled); } function _setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) internal { _metaTransactionContracts[metaTransactionProcessor] = enabled; emit MetaTransactionProcessor(metaTransactionProcessor, enabled); } /// @notice check whether address `who` is given meta-transaction execution rights. /// @param who The address to query. /// @return whether the address has meta-transaction execution rights. function isMetaTransactionProcessor(address who) external view returns(bool) { return _metaTransactionContracts[who]; } }pragma solidity ^0.5.2; import "./Admin.sol"; contract SuperOperators is Admin { mapping(address => bool) internal _superOperators; event SuperOperator(address superOperator, bool enabled); /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights). /// @param superOperator address that will be given/removed superOperator right. /// @param enabled set whether the superOperator is enabled or disabled. function setSuperOperator(address superOperator, bool enabled) external { require( msg.sender == _admin, "only admin is allowed to add super operators" ); _superOperators[superOperator] = enabled; emit SuperOperator(superOperator, enabled); } /// @notice check whether address `who` is given superOperator rights. /// @param who The address to query. /// @return whether the address has superOperator rights. function isSuperOperator(address who) public view returns (bool) { return _superOperators[who]; } } pragma solidity ^0.5.2; /** * @title ERC721 Non-Fungible Token Standard basic interface * @dev see https://eips.ethereum.org/EIPS/eip-721 */ interface ERC721Events { 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 ); } pragma solidity ^0.5.2; /** Note: The ERC-165 identifier for this interface is 0x5e8bf644. */ interface ERC721MandatoryTokenReceiver { function onERC721BatchReceived( address operator, address from, uint256[] calldata ids, bytes calldata data ) external returns (bytes4); // needs to return 0x4b808c46 function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); // needs to return 0x150b7a02 // needs to implements EIP-165 // function supportsInterface(bytes4 interfaceId) // external // view // returns (bool); } /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This code has not been reviewed. * Do not use or deploy this code before reviewing it personally first. */ // solhint-disable-next-line compiler-fixed pragma solidity ^0.5.2; interface ERC721TokenReceiver { function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } pragma solidity ^0.5.2; library AddressUtils { function toPayable(address _address) internal pure returns (address payable _payable) { return address(uint160(_address)); } function isContract(address addr) internal view returns (bool) { // for accounts without code, i.e. `keccak256('')`: bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; bytes32 codehash; // solium-disable-next-line security/no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != accountHash); } } /* solhint-disable no-empty-blocks */ pragma solidity 0.5.9; import "./Land/erc721/LandBaseToken.sol"; contract Land is LandBaseToken { constructor( address metaTransactionContract, address admin ) public LandBaseToken( metaTransactionContract, admin ) { } /** * @notice Return the name of the token contract * @return The name of the token contract */ function name() external pure returns (string memory) { return "Sandbox's LANDs"; } /** * @notice Return the symbol of the token contract * @return The symbol of the token contract */ function symbol() external pure returns (string memory) { return "LAND"; } // solium-disable-next-line security/no-assign-params function uint2str(uint _i) internal pure returns (string memory) { if (_i == 0) { return "0"; } uint j = _i; uint len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len - 1; while (_i != 0) { bstr[k--] = byte(uint8(48 + _i % 10)); _i /= 10; } return string(bstr); } /** * @notice Return the URI of a specific token * @param id The id of the token * @return The URI of the token */ function tokenURI(uint256 id) public view returns (string memory) { require(_ownerOf(id) != address(0), "Id does not exist"); return string( abi.encodePacked( "https://api.sandbox.game/lands/", uint2str(id), "/metadata.json" ) ); } /** * @notice Check if the contract supports an interface * 0x01ffc9a7 is ERC-165 * 0x80ac58cd is ERC-721 * 0x5b5e139f is ERC-721 metadata * @param id The id of the interface * @return True if the interface is supported */ function supportsInterface(bytes4 id) external pure returns (bool) { return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f; } } /* solhint-disable func-order, code-complexity */ pragma solidity 0.5.9; import "../../../contracts_common/src/Libraries/AddressUtils.sol"; import "../../../contracts_common/src/Interfaces/ERC721TokenReceiver.sol"; import "../../../contracts_common/src/Interfaces/ERC721Events.sol"; import "../../../contracts_common/src/BaseWithStorage/SuperOperators.sol"; import "../../../contracts_common/src/BaseWithStorage/MetaTransactionReceiver.sol"; import "../../../contracts_common/src/Interfaces/ERC721MandatoryTokenReceiver.sol"; contract ERC721BaseToken is ERC721Events, SuperOperators, MetaTransactionReceiver { using AddressUtils for address; bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02; bytes4 internal constant _ERC721_BATCH_RECEIVED = 0x4b808c46; bytes4 internal constant ERC165ID = 0x01ffc9a7; bytes4 internal constant ERC721_MANDATORY_RECEIVER = 0x5e8bf644; mapping (address => uint256) public _numNFTPerAddress; mapping (uint256 => uint256) public _owners; mapping (address => mapping(address => bool)) public _operatorsForAll; mapping (uint256 => address) public _operators; constructor( address metaTransactionContract, address admin ) internal { _admin = admin; _setMetaTransactionProcessor(metaTransactionContract, true); } function _transferFrom(address from, address to, uint256 id) internal { _numNFTPerAddress[from]--; _numNFTPerAddress[to]++; _owners[id] = uint256(to); emit Transfer(from, to, id); } /** * @notice Return the number of Land owned by an address * @param owner The address to look for * @return The number of Land token owned by the address */ function balanceOf(address owner) external view returns (uint256) { require(owner != address(0), "owner is zero address"); return _numNFTPerAddress[owner]; } function _ownerOf(uint256 id) internal view returns (address) { return address(_owners[id]); } function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) { uint256 data = _owners[id]; owner = address(data); operatorEnabled = (data / 2**255) == 1; } /** * @notice Return the owner of a Land * @param id The id of the Land * @return The address of the owner */ function ownerOf(uint256 id) external view returns (address owner) { owner = _ownerOf(id); require(owner != address(0), "token does not exist"); } function _approveFor(address owner, address operator, uint256 id) internal { if(operator == address(0)) { _owners[id] = uint256(owner); // no need to resset the operator, it will be overriden next time } else { _owners[id] = uint256(owner) + 2**255; _operators[id] = operator; } emit Approval(owner, operator, id); } /** * @notice Approve an operator to spend tokens on the sender behalf * @param sender The address giving the approval * @param operator The address receiving the approval * @param id The id of the token */ function approveFor( address sender, address operator, uint256 id ) external { address owner = _ownerOf(id); require(sender != address(0), "sender is zero address"); require( msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender] || _operatorsForAll[sender][msg.sender], "not authorized to approve" ); require(owner == sender, "owner != sender"); _approveFor(owner, operator, id); } /** * @notice Approve an operator to spend tokens on the sender behalf * @param operator The address receiving the approval * @param id The id of the token */ function approve(address operator, uint256 id) external { address owner = _ownerOf(id); require(owner != address(0), "token does not exist"); require( owner == msg.sender || _superOperators[msg.sender] || _operatorsForAll[owner][msg.sender], "not authorized to approve" ); _approveFor(owner, operator, id); } /** * @notice Get the approved operator for a specific token * @param id The id of the token * @return The address of the operator */ function getApproved(uint256 id) external view returns (address) { (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner != address(0), "token does not exist"); if (operatorEnabled) { return _operators[id]; } else { return address(0); } } function _checkTransfer(address from, address to, uint256 id) internal view returns (bool isMetaTx) { (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner != address(0), "token does not exist"); require(owner == from, "not owner in _checkTransfer"); require(to != address(0), "can't send to zero address"); isMetaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !isMetaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender] || (operatorEnabled && _operators[id] == msg.sender), "not approved to transfer" ); } } function _checkInterfaceWith10000Gas(address _contract, bytes4 interfaceId) internal view returns (bool) { bool success; bool result; bytes memory call_data = abi.encodeWithSelector( ERC165ID, interfaceId ); // solium-disable-next-line security/no-inline-assembly assembly { let call_ptr := add(0x20, call_data) let call_size := mload(call_data) let output := mload(0x40) // Find empty storage location using "free memory pointer" mstore(output, 0x0) success := staticcall( 10000, _contract, call_ptr, call_size, output, 0x20 ) // 32 bytes result := mload(output) } // (10000 / 63) "not enough for supportsInterface(...)" // consume all gas, so caller can potentially know that there was not enough gas assert(gasleft() > 158); return success && result; } /** * @notice Transfer a token between 2 addresses * @param from The sender of the token * @param to The recipient of the token * @param id The id of the token */ function transferFrom(address from, address to, uint256 id) external { bool metaTx = _checkTransfer(from, to, id); _transferFrom(from, to, id); if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { require( _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, ""), "erc721 transfer rejected by to" ); } } /** * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer * @param from The sender of the token * @param to The recipient of the token * @param id The id of the token * @param data Additional data */ function safeTransferFrom(address from, address to, uint256 id, bytes memory data) public { bool metaTx = _checkTransfer(from, to, id); _transferFrom(from, to, id); if (to.isContract()) { require( _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, data), "ERC721: transfer rejected by to" ); } } /** * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer * @param from The send of the token * @param to The recipient of the token * @param id The id of the token */ function safeTransferFrom(address from, address to, uint256 id) external { safeTransferFrom(from, to, id, ""); } /** * @notice Transfer many tokens between 2 addresses * @param from The sender of the token * @param to The recipient of the token * @param ids The ids of the tokens * @param data additional data */ function batchTransferFrom(address from, address to, uint256[] calldata ids, bytes calldata data) external { _batchTransferFrom(from, to, ids, data, false); } function _batchTransferFrom(address from, address to, uint256[] memory ids, bytes memory data, bool safe) internal { bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; bool authorized = msg.sender == from || metaTx || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender]; require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); uint256 numTokens = ids.length; for(uint256 i = 0; i < numTokens; i ++) { uint256 id = ids[i]; (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require(owner == from, "not owner in batchTransferFrom"); require(authorized || (operatorEnabled && _operators[id] == msg.sender), "not authorized"); _owners[id] = uint256(to); emit Transfer(from, to, id); } if (from != to) { _numNFTPerAddress[from] -= numTokens; _numNFTPerAddress[to] += numTokens; } if (to.isContract() && (safe || _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER))) { require( _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } /** * @notice Transfer many tokens between 2 addresses ensuring the receiving contract has a receiver method * @param from The sender of the token * @param to The recipient of the token * @param ids The ids of the tokens * @param data additional data */ function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, bytes calldata data) external { _batchTransferFrom(from, to, ids, data, true); } /** * @notice Check if the contract supports an interface * 0x01ffc9a7 is ERC-165 * 0x80ac58cd is ERC-721 * @param id The id of the interface * @return True if the interface is supported */ function supportsInterface(bytes4 id) external pure returns (bool) { return id == 0x01ffc9a7 || id == 0x80ac58cd; } /** * @notice Set the approval for an operator to manage all the tokens of the sender * @param sender The address giving the approval * @param operator The address receiving the approval * @param approved The determination of the approval */ function setApprovalForAllFor( address sender, address operator, bool approved ) external { require(sender != address(0), "Invalid sender address"); require( msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender], "not authorized to approve for all" ); _setApprovalForAll(sender, operator, approved); } /** * @notice Set the approval for an operator to manage all the tokens of the sender * @param operator The address receiving the approval * @param approved The determination of the approval */ function setApprovalForAll(address operator, bool approved) external { _setApprovalForAll(msg.sender, operator, approved); } function _setApprovalForAll( address sender, address operator, bool approved ) internal { require( !_superOperators[operator], "super operator can't have their approvalForAll changed" ); _operatorsForAll[sender][operator] = approved; emit ApprovalForAll(sender, operator, approved); } /** * @notice Check if the sender approved the operator * @param owner The address of the owner * @param operator The address of the operator * @return The status of the approval */ function isApprovedForAll(address owner, address operator) external view returns (bool isOperator) { return _operatorsForAll[owner][operator] || _superOperators[operator]; } function _burn(address from, address owner, uint256 id) public { require(from == owner, "not owner"); _owners[id] = 2**160; // cannot mint it again _numNFTPerAddress[from]--; emit Transfer(from, address(0), id); } /// @notice Burns token `id`. /// @param id token which will be burnt. function burn(uint256 id) external { _burn(msg.sender, _ownerOf(id), id); } /// @notice Burn token`id` from `from`. /// @param from address whose token is to be burnt. /// @param id token which will be burnt. function burnFrom(address from, uint256 id) external { require(from != address(0), "Invalid sender address"); (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id); require( msg.sender == from || _metaTransactionContracts[msg.sender] || (operatorEnabled && _operators[id] == msg.sender) || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to burn" ); _burn(from, owner, id); } function _checkOnERC721Received(address operator, address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { bytes4 retval = ERC721TokenReceiver(to).onERC721Received(operator, from, tokenId, _data); return (retval == _ERC721_RECEIVED); } function _checkOnERC721BatchReceived(address operator, address from, address to, uint256[] memory ids, bytes memory _data) internal returns (bool) { bytes4 retval = ERC721MandatoryTokenReceiver(to).onERC721BatchReceived(operator, from, ids, _data); return (retval == _ERC721_BATCH_RECEIVED); } } /* solhint-disable func-order, code-complexity */ pragma solidity 0.5.9; import "./ERC721BaseToken.sol"; contract LandBaseToken is ERC721BaseToken { // Our grid is 408 x 408 lands uint256 internal constant GRID_SIZE = 408; uint256 internal constant LAYER = 0xFF00000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_1x1 = 0x0000000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_3x3 = 0x0100000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_6x6 = 0x0200000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_12x12 = 0x0300000000000000000000000000000000000000000000000000000000000000; uint256 internal constant LAYER_24x24 = 0x0400000000000000000000000000000000000000000000000000000000000000; mapping(address => bool) internal _minters; event Minter(address superOperator, bool enabled); /// @notice Enable or disable the ability of `minter` to mint tokens /// @param minter address that will be given/removed minter right. /// @param enabled set whether the minter is enabled or disabled. function setMinter(address minter, bool enabled) external { require( msg.sender == _admin, "only admin is allowed to add minters" ); _minters[minter] = enabled; emit Minter(minter, enabled); } /// @notice check whether address `who` is given minter rights. /// @param who The address to query. /// @return whether the address has minter rights. function isMinter(address who) public view returns (bool) { return _minters[who]; } constructor( address metaTransactionContract, address admin ) public ERC721BaseToken(metaTransactionContract, admin) { } /// @notice total width of the map /// @return width function width() external returns(uint256) { return GRID_SIZE; } /// @notice total height of the map /// @return height function height() external returns(uint256) { return GRID_SIZE; } /// @notice x coordinate of Land token /// @param id tokenId /// @return the x coordinates function x(uint256 id) external returns(uint256) { require(_ownerOf(id) != address(0), "token does not exist"); return id % GRID_SIZE; } /// @notice y coordinate of Land token /// @param id tokenId /// @return the y coordinates function y(uint256 id) external returns(uint256) { require(_ownerOf(id) != address(0), "token does not exist"); return id / GRID_SIZE; } /** * @notice Mint a new quad (aligned to a quad tree with size 3, 6, 12 or 24 only) * @param to The recipient of the new quad * @param size The size of the new quad * @param x The top left x coordinate of the new quad * @param y The top left y coordinate of the new quad * @param data extra data to pass to the transfer */ function mintQuad(address to, uint256 size, uint256 x, uint256 y, bytes calldata data) external { require(to != address(0), "to is zero address"); require( isMinter(msg.sender), "Only a minter can mint" ); require(x % size == 0 && y % size == 0, "Invalid coordinates"); require(x <= GRID_SIZE - size && y <= GRID_SIZE - size, "Out of bounds"); uint256 quadId; uint256 id = x + y * GRID_SIZE; if (size == 1) { quadId = id; } else if (size == 3) { quadId = LAYER_3x3 + id; } else if (size == 6) { quadId = LAYER_6x6 + id; } else if (size == 12) { quadId = LAYER_12x12 + id; } else if (size == 24) { quadId = LAYER_24x24 + id; } else { require(false, "Invalid size"); } require(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == 0, "Already minted as 24x24"); uint256 toX = x+size; uint256 toY = y+size; if (size <= 12) { require( _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == 0, "Already minted as 12x12" ); } else { for (uint256 x12i = x; x12i < toX; x12i += 12) { for (uint256 y12i = y; y12i < toY; y12i += 12) { uint256 id12x12 = LAYER_12x12 + x12i + y12i * GRID_SIZE; require(_owners[id12x12] == 0, "Already minted as 12x12"); } } } if (size <= 6) { require(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE] == 0, "Already minted as 6x6"); } else { for (uint256 x6i = x; x6i < toX; x6i += 6) { for (uint256 y6i = y; y6i < toY; y6i += 6) { uint256 id6x6 = LAYER_6x6 + x6i + y6i * GRID_SIZE; require(_owners[id6x6] == 0, "Already minted as 6x6"); } } } if (size <= 3) { require(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE] == 0, "Already minted as 3x3"); } else { for (uint256 x3i = x; x3i < toX; x3i += 3) { for (uint256 y3i = y; y3i < toY; y3i += 3) { uint256 id3x3 = LAYER_3x3 + x3i + y3i * GRID_SIZE; require(_owners[id3x3] == 0, "Already minted as 3x3"); } } } for (uint256 i = 0; i < size*size; i++) { uint256 id = _idInPath(i, size, x, y); require(_owners[id] == 0, "Already minted"); emit Transfer(address(0), to, id); } _owners[quadId] = uint256(to); _numNFTPerAddress[to] += size * size; _checkBatchReceiverAcceptQuad(msg.sender, address(0), to, size, x, y, data); } function _idInPath(uint256 i, uint256 size, uint256 x, uint256 y) internal pure returns(uint256) { uint256 row = i / size; if(row % 2 == 0) { // alow ids to follow a path in a quad return (x + (i%size)) + ((y + row) * GRID_SIZE); } else { return ((x + size) - (1 + i%size)) + ((y + row) * GRID_SIZE); } } /// @notice transfer one quad (aligned to a quad tree with size 3, 6, 12 or 24 only) /// @param from current owner of the quad /// @param to destination /// @param size size of the quad /// @param x The top left x coordinate of the quad /// @param y The top left y coordinate of the quad /// @param data additional data function transferQuad(address from, address to, uint256 size, uint256 x, uint256 y, bytes calldata data) external { require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !metaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to transferQuad" ); } _transferQuad(from, to, size, x, y); _numNFTPerAddress[from] -= size * size; _numNFTPerAddress[to] += size * size; _checkBatchReceiverAcceptQuad(metaTx ? from : msg.sender, from, to, size, x, y, data); } function _checkBatchReceiverAcceptQuad( address operator, address from, address to, uint256 size, uint256 x, uint256 y, bytes memory data ) internal { if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { uint256[] memory ids = new uint256[](size*size); for (uint256 i = 0; i < size*size; i++) { ids[i] = _idInPath(i, size, x, y); } require( _checkOnERC721BatchReceived(operator, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } /// @notice transfer multiple quad (aligned to a quad tree with size 3, 6, 12 or 24 only) /// @param from current owner of the quad /// @param to destination /// @param sizes list of sizes for each quad /// @param xs list of top left x coordinates for each quad /// @param ys list of top left y coordinates for each quad /// @param data additional data function batchTransferQuad( address from, address to, uint256[] calldata sizes, uint256[] calldata xs, uint256[] calldata ys, bytes calldata data ) external { require(from != address(0), "from is zero address"); require(to != address(0), "can't send to zero address"); require(sizes.length == xs.length && xs.length == ys.length, "invalid data"); bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender]; if (msg.sender != from && !metaTx) { require( _superOperators[msg.sender] || _operatorsForAll[from][msg.sender], "not authorized to transferMultiQuads" ); } uint256 numTokensTransfered = 0; for (uint256 i = 0; i < sizes.length; i++) { uint256 size = sizes[i]; _transferQuad(from, to, size, xs[i], ys[i]); numTokensTransfered += size * size; } _numNFTPerAddress[from] -= numTokensTransfered; _numNFTPerAddress[to] += numTokensTransfered; if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) { uint256[] memory ids = new uint256[](numTokensTransfered); uint256 counter = 0; for (uint256 j = 0; j < sizes.length; j++) { uint256 size = sizes[j]; for (uint256 i = 0; i < size*size; i++) { ids[counter] = _idInPath(i, size, xs[j], ys[j]); counter++; } } require( _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data), "erc721 batch transfer rejected by to" ); } } function _transferQuad(address from, address to, uint256 size, uint256 x, uint256 y) internal { if (size == 1) { uint256 id1x1 = x + y * GRID_SIZE; address owner = _ownerOf(id1x1); require(owner != address(0), "token does not exist"); require(owner == from, "not owner in _transferQuad"); _owners[id1x1] = uint256(to); } else { _regroup(from, to, size, x, y); } for (uint256 i = 0; i < size*size; i++) { emit Transfer(from, to, _idInPath(i, size, x, y)); } } function _checkAndClear(address from, uint256 id) internal returns(bool) { uint256 owner = _owners[id]; if (owner != 0) { require(address(owner) == from, "not owner"); _owners[id] = 0; return true; } return false; } function _regroup(address from, address to, uint256 size, uint256 x, uint256 y) internal { require(x % size == 0 && y % size == 0, "Invalid coordinates"); require(x <= GRID_SIZE - size && y <= GRID_SIZE - size, "Out of bounds"); if (size == 3) { _regroup3x3(from, to, x, y, true); } else if (size == 6) { _regroup6x6(from, to, x, y, true); } else if (size == 12) { _regroup12x12(from, to, x, y, true); } else if (size == 24) { _regroup24x24(from, to, x, y, true); } else { require(false, "Invalid size"); } } function _regroup3x3(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_3x3 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+3; xi++) { for (uint256 yi = y; yi < y+3; yi++) { ownerOfAll = _checkAndClear(from, xi + yi * GRID_SIZE) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE] == uint256(from) || _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup6x6(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_6x6 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+6; xi += 3) { for (uint256 yi = y; yi < y+6; yi += 3) { bool ownAllIndividual = _regroup3x3(from, to, xi, yi, false); uint256 id3x3 = LAYER_3x3 + xi + yi * GRID_SIZE; uint256 owner3x3 = _owners[id3x3]; if (owner3x3 != 0) { if(!ownAllIndividual) { require(owner3x3 == uint256(from), "not owner of 3x3 quad"); } _owners[id3x3] = 0; } ownerOfAll = (ownAllIndividual || owner3x3 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup12x12(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_12x12 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+12; xi += 6) { for (uint256 yi = y; yi < y+12; yi += 6) { bool ownAllIndividual = _regroup6x6(from, to, xi, yi, false); uint256 id6x6 = LAYER_6x6 + xi + yi * GRID_SIZE; uint256 owner6x6 = _owners[id6x6]; if (owner6x6 != 0) { if(!ownAllIndividual) { require(owner6x6 == uint256(from), "not owner of 6x6 quad"); } _owners[id6x6] = 0; } ownerOfAll = (ownAllIndividual || owner6x6 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from) || _owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE] == uint256(from), "not owner of all sub quads nor parent quads" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll; } function _regroup24x24(address from, address to, uint256 x, uint256 y, bool set) internal returns (bool) { uint256 id = x + y * GRID_SIZE; uint256 quadId = LAYER_24x24 + id; bool ownerOfAll = true; for (uint256 xi = x; xi < x+24; xi += 12) { for (uint256 yi = y; yi < y+24; yi += 12) { bool ownAllIndividual = _regroup12x12(from, to, xi, yi, false); uint256 id12x12 = LAYER_12x12 + xi + yi * GRID_SIZE; uint256 owner12x12 = _owners[id12x12]; if (owner12x12 != 0) { if(!ownAllIndividual) { require(owner12x12 == uint256(from), "not owner of 12x12 quad"); } _owners[id12x12] = 0; } ownerOfAll = (ownAllIndividual || owner12x12 != 0) && ownerOfAll; } } if(set) { if(!ownerOfAll) { require( _owners[quadId] == uint256(from), "not owner of all sub quads not parent quad" ); } _owners[quadId] = uint256(to); return true; } return ownerOfAll || _owners[quadId] == uint256(from); } function _ownerOf(uint256 id) internal view returns (address) { require(id & LAYER == 0, "Invalid token id"); uint256 x = id % GRID_SIZE; uint256 y = id / GRID_SIZE; uint256 owner1x1 = _owners[id]; if (owner1x1 != 0) { return address(owner1x1); // cast to zero } else { address owner3x3 = address(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE]); if (owner3x3 != address(0)) { return owner3x3; } else { address owner6x6 = address(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE]); if (owner6x6 != address(0)) { return owner6x6; } else { address owner12x12 = address(_owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE]); if (owner12x12 != address(0)) { return owner12x12; } else { return address(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE]); } } } } } function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) { require(id & LAYER == 0, "Invalid token id"); uint256 x = id % GRID_SIZE; uint256 y = id / GRID_SIZE; uint256 owner1x1 = _owners[id]; if (owner1x1 != 0) { owner = address(owner1x1); operatorEnabled = (owner1x1 / 2**255) == 1; } else { address owner3x3 = address(_owners[LAYER_3x3 + (x/3) * 3 + ((y/3) * 3) * GRID_SIZE]); if (owner3x3 != address(0)) { owner = owner3x3; operatorEnabled = false; } else { address owner6x6 = address(_owners[LAYER_6x6 + (x/6) * 6 + ((y/6) * 6) * GRID_SIZE]); if (owner6x6 != address(0)) { owner = owner6x6; operatorEnabled = false; } else { address owner12x12 = address(_owners[LAYER_12x12 + (x/12) * 12 + ((y/12) * 12) * GRID_SIZE]); if (owner12x12 != address(0)) { owner = owner12x12; operatorEnabled = false; } else { owner = address(_owners[LAYER_24x24 + (x/24) * 24 + ((y/24) * 24) * GRID_SIZE]); operatorEnabled = false; } } } } } }
File 4 of 4: Medianizer
/// return median value of feeds // Copyright (C) 2017 DappHub, LLC // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND (express or implied). pragma solidity ^0.4.8; contract DSAuthority { function canCall( address src, address dst, bytes4 sig ) constant returns (bool); } contract DSAuthEvents { event LogSetAuthority (address indexed authority); event LogSetOwner (address indexed owner); } contract DSAuth is DSAuthEvents { DSAuthority public authority; address public owner; function DSAuth() { owner = msg.sender; LogSetOwner(msg.sender); } function setOwner(address owner_) auth { owner = owner_; LogSetOwner(owner); } function setAuthority(DSAuthority authority_) auth { authority = authority_; LogSetAuthority(authority); } modifier auth { assert(isAuthorized(msg.sender, msg.sig)); _; } modifier authorized(bytes4 sig) { assert(isAuthorized(msg.sender, sig)); _; } function isAuthorized(address src, bytes4 sig) internal returns (bool) { if (src == address(this)) { return true; } else if (src == owner) { return true; } else if (authority == DSAuthority(0)) { return false; } else { return authority.canCall(src, this, sig); } } function assert(bool x) internal { if (!x) throw; } } contract DSNote { event LogNote( bytes4 indexed sig, address indexed guy, bytes32 indexed foo, bytes32 indexed bar, uint wad, bytes fax ) anonymous; modifier note { bytes32 foo; bytes32 bar; assembly { foo := calldataload(4) bar := calldataload(36) } LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data); _; } } contract DSMath { /* standard uint256 functions */ function add(uint256 x, uint256 y) constant internal returns (uint256 z) { assert((z = x + y) >= x); } function sub(uint256 x, uint256 y) constant internal returns (uint256 z) { assert((z = x - y) <= x); } function mul(uint256 x, uint256 y) constant internal returns (uint256 z) { assert((z = x * y) >= x); } function div(uint256 x, uint256 y) constant internal returns (uint256 z) { z = x / y; } function min(uint256 x, uint256 y) constant internal returns (uint256 z) { return x <= y ? x : y; } function max(uint256 x, uint256 y) constant internal returns (uint256 z) { return x >= y ? x : y; } /* uint128 functions (h is for half) */ function hadd(uint128 x, uint128 y) constant internal returns (uint128 z) { assert((z = x + y) >= x); } function hsub(uint128 x, uint128 y) constant internal returns (uint128 z) { assert((z = x - y) <= x); } function hmul(uint128 x, uint128 y) constant internal returns (uint128 z) { assert((z = x * y) >= x); } function hdiv(uint128 x, uint128 y) constant internal returns (uint128 z) { z = x / y; } function hmin(uint128 x, uint128 y) constant internal returns (uint128 z) { return x <= y ? x : y; } function hmax(uint128 x, uint128 y) constant internal returns (uint128 z) { return x >= y ? x : y; } /* int256 functions */ function imin(int256 x, int256 y) constant internal returns (int256 z) { return x <= y ? x : y; } function imax(int256 x, int256 y) constant internal returns (int256 z) { return x >= y ? x : y; } /* WAD math */ uint128 constant WAD = 10 ** 18; function wadd(uint128 x, uint128 y) constant internal returns (uint128) { return hadd(x, y); } function wsub(uint128 x, uint128 y) constant internal returns (uint128) { return hsub(x, y); } function wmul(uint128 x, uint128 y) constant internal returns (uint128 z) { z = cast((uint256(x) * y + WAD / 2) / WAD); } function wdiv(uint128 x, uint128 y) constant internal returns (uint128 z) { z = cast((uint256(x) * WAD + y / 2) / y); } function wmin(uint128 x, uint128 y) constant internal returns (uint128) { return hmin(x, y); } function wmax(uint128 x, uint128 y) constant internal returns (uint128) { return hmax(x, y); } /* RAY math */ uint128 constant RAY = 10 ** 27; function radd(uint128 x, uint128 y) constant internal returns (uint128) { return hadd(x, y); } function rsub(uint128 x, uint128 y) constant internal returns (uint128) { return hsub(x, y); } function rmul(uint128 x, uint128 y) constant internal returns (uint128 z) { z = cast((uint256(x) * y + RAY / 2) / RAY); } function rdiv(uint128 x, uint128 y) constant internal returns (uint128 z) { z = cast((uint256(x) * RAY + y / 2) / y); } function rpow(uint128 x, uint64 n) constant internal returns (uint128 z) { // This famous algorithm is called "exponentiation by squaring" // and calculates x^n with x as fixed-point and n as regular unsigned. // // It's O(log n), instead of O(n) for naive repeated multiplication. // // These facts are why it works: // // If n is even, then x^n = (x^2)^(n/2). // If n is odd, then x^n = x * x^(n-1), // and applying the equation for even x gives // x^n = x * (x^2)^((n-1) / 2). // // Also, EVM division is flooring and // floor[(n-1) / 2] = floor[n / 2]. z = n % 2 != 0 ? x : RAY; for (n /= 2; n != 0; n /= 2) { x = rmul(x, x); if (n % 2 != 0) { z = rmul(z, x); } } } function rmin(uint128 x, uint128 y) constant internal returns (uint128) { return hmin(x, y); } function rmax(uint128 x, uint128 y) constant internal returns (uint128) { return hmax(x, y); } function cast(uint256 x) constant internal returns (uint128 z) { assert((z = uint128(x)) == x); } } contract DSThing is DSAuth, DSNote, DSMath { } contract DSValue is DSThing { bool has; bytes32 val; function peek() constant returns (bytes32, bool) { return (val,has); } function read() constant returns (bytes32) { var (wut, has) = peek(); assert(has); return wut; } function poke(bytes32 wut) note auth { val = wut; has = true; } function void() note auth { // unset the value has = false; } } contract Medianizer is DSValue { mapping (bytes12 => address) public values; mapping (address => bytes12) public indexes; bytes12 public next = 0x1; uint96 public min = 0x1; function set(address wat) auth { bytes12 nextId = bytes12(uint96(next) + 1); assert(nextId != 0x0); set(next, wat); next = nextId; } function set(bytes12 pos, address wat) note auth { if (pos == 0x0) throw; if (wat != 0 && indexes[wat] != 0) throw; indexes[values[pos]] = 0; // Making sure to remove a possible existing address in that position if (wat != 0) { indexes[wat] = pos; } values[pos] = wat; } function setMin(uint96 min_) note auth { if (min_ == 0x0) throw; min = min_; } function setNext(bytes12 next_) note auth { if (next_ == 0x0) throw; next = next_; } function unset(bytes12 pos) { set(pos, 0); } function unset(address wat) { set(indexes[wat], 0); } function poke() { poke(0); } function poke(bytes32) note { (val, has) = compute(); } function compute() constant returns (bytes32, bool) { bytes32[] memory wuts = new bytes32[](uint96(next) - 1); uint96 ctr = 0; for (uint96 i = 1; i < uint96(next); i++) { if (values[bytes12(i)] != 0) { var (wut, wuz) = DSValue(values[bytes12(i)]).peek(); if (wuz) { if (ctr == 0 || wut >= wuts[ctr - 1]) { wuts[ctr] = wut; } else { uint96 j = 0; while (wut >= wuts[j]) { j++; } for (uint96 k = ctr; k > j; k--) { wuts[k] = wuts[k - 1]; } wuts[j] = wut; } ctr++; } } } if (ctr < min) return (val, false); bytes32 value; if (ctr % 2 == 0) { uint128 val1 = uint128(wuts[(ctr / 2) - 1]); uint128 val2 = uint128(wuts[ctr / 2]); value = bytes32(wdiv(hadd(val1, val2), 2 ether)); } else { value = wuts[(ctr - 1) / 2]; } return (value, true); } }