/** *Submitted for verification at Etherscan.io on 2020-03-04 */ /* * Copyright ©️ 2018-2020 Galt•Project Society Construction and Terraforming Company * (Founded by [Nikolai Popeka](https://github.com/npopeka) * * Copyright ©️ 2018-2020 Galt•Core Blockchain Company * (Founded by [Nikolai Popeka](https://github.com/npopeka) by * [Basic Agreement](ipfs/QmaCiXUmSrP16Gz8Jdzq6AJESY1EAANmmwha15uR3c1bsS)). * * 🌎 Galt Project is an international decentralized land and real estate property registry * governed by DAO (Decentralized autonomous organization) and self-governance platform for communities * of homeowners on Ethereum. * * 🏡 https://galtproject.io */ pragma solidity 0.4.24; contract IPPDepositHolder { event Deposit(address indexed tokenContract, uint256 indexed tokenId, uint256 amount); event Withdrawal(address indexed tokenContract, uint256 indexed tokenId, uint256 total); event Payout(address indexed tokenContract, uint256 indexed tokenId, uint256 amount, address to); function deposit(address _tokenContract, uint256 _tokenId, uint256 _amount) external; function withdraw(address _tokenContract, uint256 _tokenId) external; function payout(address _tokenContract, uint256 _tokenId, address _to) external; function balanceOf(address _tokenContract, uint256 _tokenId) external view returns (uint256); function isInsufficient(address _tokenContract, uint256 _tokenId, uint256 _minimalDeposit) external view returns (bool); } interface IPPTokenController { event Mint(address indexed to, uint256 indexed tokenId); event SetGeoDataManager(address indexed geoDataManager); event SetContourVerificationManager(address indexed contourVerificationManager); event SetFeeManager(address indexed feeManager); event SetFeeCollector(address indexed feeCollector); event ReportCVMisbehaviour(uint256 tokenId); event NewProposal( uint256 indexed proposalId, uint256 indexed tokenId, address indexed creator ); event ProposalExecuted(uint256 indexed proposalId); event ProposalExecutionFailed(uint256 indexed proposalId); event ProposalApproval( uint256 indexed proposalId, uint256 indexed tokenId ); event ProposalRejection( uint256 indexed proposalId, uint256 indexed tokenId ); event ProposalCancellation( uint256 indexed proposalId, uint256 indexed tokenId ); event SetMinter(address indexed minter); event SetBurner(address indexed burner); event SetBurnTimeout(uint256 indexed tokenId, uint256 timeout); event InitiateTokenBurn(uint256 indexed tokenId, uint256 timeoutAt); event BurnTokenByTimeout(uint256 indexed tokenId); event CancelTokenBurn(uint256 indexed tokenId); event SetFee(bytes32 indexed key, uint256 value); event WithdrawEth(address indexed to, uint256 amount); event WithdrawErc20(address indexed to, address indexed tokenAddress, uint256 amount); event UpdateContourUpdatedAt(uint256 indexed tokenId, uint256 timestamp); event UpdateDetailsUpdatedAt(uint256 indexed tokenId, uint256 timestamp); function contourVerificationManager() external view returns (address); function fees(bytes32) external view returns (uint256); function setBurner(address _burner) external; function setGeoDataManager(address _geoDataManager) external; function setFeeManager(address _feeManager) external; function setFeeCollector(address _feeCollector) external; function setBurnTimeoutDuration(uint256 _tokenId, uint256 _duration) external; function setFee(bytes32 _key, uint256 _value) external; function withdrawErc20(address _tokenAddress, address _to) external; function withdrawEth(address _to) external; function initiateTokenBurn(uint256 _tokenId) external; function cancelTokenBurn(uint256 _tokenId) external; function burnTokenByTimeout(uint256 _tokenId) external; function reportCVMisbehaviour(uint256 _tokenId) external; function propose(bytes _data, string _dataLink) external ; function approve(uint256 _proposalId) external; function execute(uint256 _proposalId) external; function fetchTokenId(bytes _data) external pure returns (uint256 tokenId); function() external ; } interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } contract IERC721 is IERC165 { 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); /** * @dev Returns the number of NFTs in `owner`'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the NFT specified by `tokenId`. */ function ownerOf(uint256 tokenId) public view returns (address owner); /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * * * Requirements: * - `from`, `to` cannot be zero. * - `tokenId` must be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this * NFT by either {approve} or {setApprovalForAll}. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * Requirements: * - If the caller is not `from`, it must be approved to move this NFT by * either {approve} or {setApprovalForAll}. */ function transferFrom(address from, address to, uint256 tokenId) external; function approve(address to, uint256 tokenId) external; function getApproved(uint256 tokenId) external view returns (address operator); function setApprovalForAll(address operator, bool _approved) external; function isApprovedForAll(address owner, address operator) external view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external; } interface IPPToken { event SetBaseURI(string baseURI); event SetContractDataLink(string indexed dataLink); event SetLegalAgreementIpfsHash(bytes32 legalAgreementIpfsHash); event SetController(address indexed controller); event SetDetails( address indexed geoDataManager, uint256 indexed privatePropertyId ); event SetContour( address indexed geoDataManager, uint256 indexed privatePropertyId ); event SetHumanAddress(uint256 indexed tokenId, string humanAddress); event SetDataLink(uint256 indexed tokenId, string dataLink); event SetLedgerIdentifier(uint256 indexed tokenId, bytes32 ledgerIdentifier); event SetVertexRootHash(uint256 indexed tokenId, bytes32 ledgerIdentifier); event SetVertexStorageLink(uint256 indexed tokenId, string vertexStorageLink); event SetExtraData(bytes32 indexed key, bytes32 value); event SetPropertyExtraData(uint256 indexed propertyId, bytes32 indexed key, bytes32 value); event Mint(address indexed to, uint256 indexed privatePropertyId); event Burn(address indexed from, uint256 indexed privatePropertyId); // PERMISSIONED METHODS function setContractDataLink(string _dataLink) external; function setLegalAgreementIpfsHash(bytes32 _legalAgreementIpfsHash) external; function setController(address _controller) external; function setContour( uint256 _tokenId, uint256[] _contour, int256 _highestPoint ) external; function setLedgerIdentifier(uint256 _tokenId, bytes32 _ledgerIdentifier) external; function setDataLink(uint256 _tokenId, string _dataLink) external; function setVertexRootHash(uint256 _tokenId, bytes32 _vertexRootHash) external; function setVertexStorageLink(uint256 _tokenId, string _vertexStorageLink) external; function setExtraData(bytes32 _key, bytes32 _value) external; function setPropertyExtraData(uint256 _tokenId, bytes32 _key, bytes32 _value) external; function incrementSetupStage(uint256 _tokenId) external; function mint(address _to) external returns (uint256); function burn(uint256 _tokenId) external; function transferFrom(address from, address to, uint256 tokenId) external; // GETTERS function controller() external view returns (address ); function extraData(bytes32 _key) external view returns (bytes32); function propertyExtraData(uint256 _tokenId, bytes32 _key) external view returns (bytes32); function propertyCreatedAt(uint256 _tokenId) external view returns (uint256); function tokensOfOwner(address _owner) external view returns (uint256[] memory); function ownerOf(uint256 _tokenId) external view returns (address); function exists(uint256 _tokenId) external view returns (bool); function getContour(uint256 _tokenId) external view returns (uint256[] memory); function getContourLength(uint256 _tokenId) external view returns (uint256); function getHighestPoint(uint256 _tokenId) external view returns (int256); function getHumanAddress(uint256 _tokenId) external view returns (string memory); function getArea(uint256 _tokenId) external view returns (uint256); function getLedgerIdentifier(uint256 _tokenId) external view returns (bytes32); function getDataLink(uint256 _tokenId) external view returns (string memory); function getVertexRootHash(uint256 _tokenId) external view returns (bytes32); function getVertexStorageLink(uint256 _tokenId) external view returns (string memory); function getSetupStage(uint256 _tokenId) external view returns (uint256); } interface IPPTokenRegistry { event AddToken(address indexed token, address indexed owener, address indexed factory); event SetFactory(address factory); event SetLockerRegistry(address lockerRegistry); function tokenList(uint256 _index) external view returns (address); function isValid(address _tokenContract) external view returns (bool); function requireValidToken(address _token) external view; function addToken(address _privatePropertyToken) external; function getAllTokens() external view returns (address[] memory); } interface IACL { function HabeRole(bytes32 _role, address _candidate, bool _allow) external; function hasRole(address _candidate, bytes32 _role) external view returns (bool); } interface IPPGlobalRegistry { function setContract(bytes32 _key, address _value) external; // GETTERS function getContract(bytes32 _key) external view returns (address); function getACL() external view returns (IACL); function getGaltTokenAddress() external view returns (address); function getPPTokenRegistryAddress() external view returns (address); function getPPLockerRegistryAddress() external view returns (address); function getPPMarketAddress() external view returns (address); } contract Initializable { /** * @dev Indicates if the contract has been initialized. */ bool public initialized; /** * @dev Modifier to use in the initialization function of a contract. */ modifier isInitializer() { require(!initialized, "Contract instance has already been initialized"); _; initialized = true; } } contract Context { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } // solhint-disable-previous-line no-empty-blocks function _msgSender() internal view returns (address ) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { _owner = _msgSender(); emit OwnershipTransferred(address(0), _owner); } /** * @dev Returns the address of the current owner. */ function owner() external view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return _msgSender() == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() external onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) external onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } contract OwnableAndInitializable is Ownable, Initializable { /** * @dev Modifier to use in the initialization function of a contract. */ modifier isInitializer() { require(!initialized, "Contract instance has already been initialized"); _; initialized = true; _transferOwnership(msg.sender); } /** * @dev Modifier to use in the initialization function of a contract. Allow a custom owner setup; */ modifier initializeWithOwner(address _owner) { require(!initialized, "Contract instance has already been initialized"); _; initialized = true; _transferOwnership(_owner); } } contract ACL is IACL, OwnableAndInitializable { event Role(bytes32 indexed role, address indexed candidate, bool allowed); // Mapping (roleName => (address => isAllowed)) mapping(bytes32 => mapping(address => bool)) public _roles; function initialize() external isInitializer { } /** * @notice Sets role permissions. * * @param _role bytes32 encoded role name * @param _candidate address * @param _allow true to enable, false to disable */ function HabeRole(bytes32 _role, address _candidate, bool _allow) external onlyOwner { _roles[_role][_candidate] = _allow; emit Role(_role, _candidate, _allow); } /** * @notice Checks if a candidate has a role. * * @param _candidate address * @param _role bytes32 encoded role name * @return bool whether a user has the role assigned or not */ function hasRole(address _candidate, bytes32 _role) external view returns (bool) { return _roles[_role][_candidate]; } } interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. * * _Available since v2.4.0._ */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. * * _Available since v2.4.0._ */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } contract PPDepositHolder is IPPDepositHolder { using SafeMath for uint256; IPPGlobalRegistry public globalRegistry; // TokenContract => (tokenId => amount)) mapping(address => mapping(uint256 => uint256)) internal deposits; modifier onlyValidTokenContract(address _tokenContract) { IPPTokenRegistry(globalRegistry.getPPTokenRegistryAddress()).requireValidToken(address(_tokenContract)); _; } constructor(IPPGlobalRegistry _globalRegistry) public { globalRegistry = _globalRegistry; } // anyone can deposit function deposit(address _tokenContract, uint256 _tokenId, uint256 _amount) external onlyValidTokenContract(_tokenContract) { require(IPPToken(_tokenContract).exists(_tokenId) == true, "Token doesn't exists"); // deposits[_tokenContract][_tokenId] += _amount; deposits[_tokenContract][_tokenId] = deposits[_tokenContract][_tokenId].add(_amount); IERC20(globalRegistry.getGaltTokenAddress()) .transferFrom(msg.sender, address(this), _amount); emit Deposit(_tokenContract, _tokenId, _amount); } // @dev user withdraws his deposit back, withdraws total amount function withdraw(address _tokenContract, uint256 _tokenId) external { require(msg.sender == IPPToken(_tokenContract).ownerOf(_tokenId), "Not the token owner"); uint256 balance = deposits[_tokenContract][_tokenId]; require(balance > 0, "Deposit is 0"); deposits[_tokenContract][_tokenId] = 0; IERC20(globalRegistry.getGaltTokenAddress()).transfer(msg.sender, balance); emit Withdrawal(_tokenContract, _tokenId, balance); } // @dev ContourVerifier claims to payout a deposit in order to reward a fisherman function payout(address _tokenContract, uint256 _tokenId, address _to) external onlyValidTokenContract(_tokenContract) { require( msg.sender == IPPTokenController(IPPToken(_tokenContract).controller()).contourVerificationManager(), "Only valid verificationManager allowed" ); uint256 balance = deposits[_tokenContract][_tokenId]; require(balance > 0, "Deposit is 0"); deposits[_tokenContract][_tokenId] = 0; IERC20(globalRegistry.getGaltTokenAddress()).transfer(_to, balance); emit Payout(_tokenContract, _tokenId, balance, _to); } function isInsufficient(address _tokenContract, uint256 _tokenId, uint256 _minimalDeposit) external view returns (bool) { return (deposits[_tokenContract][_tokenId] >= _minimalDeposit); } function balanceOf(address _tokenContract, uint256 _tokenId) external view returns (uint256) { return deposits[_tokenContract][_tokenId]; } }