|
| 1 | +pragma solidity >=0.5.0 <0.6.0; |
| 2 | + |
| 3 | +/** |
| 4 | + * @title ETHFaucet implementation |
| 5 | + * @author AZTEC |
| 6 | + * Copyright Spilbury Holdings Ltd 2019. All rights reserved. |
| 7 | + **/ |
| 8 | + |
| 9 | +import "@openzeppelin/upgrades/contracts/Initializable.sol"; |
| 10 | + |
| 11 | +import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol"; |
| 12 | +import "@openzeppelin/contracts-ethereum-package/contracts/cryptography/ECDSA.sol"; |
| 13 | + |
| 14 | + |
| 15 | +contract AccountRegistry { |
| 16 | + mapping(address => bytes) public accountMapping; |
| 17 | +} |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +contract EthFaucet is Initializable, GSNRecipient { |
| 22 | + mapping(address =>uint256) public faucetMapping; |
| 23 | + address trustedSigner; |
| 24 | + address accountRegistryAddress; |
| 25 | + event GSNTransactionProcessed(bytes32 indexed signatureHash, bool indexed success, uint actualCharge); |
| 26 | + |
| 27 | + constructor(address _accountRegistryAddress, address _trustedAddress) public { |
| 28 | + accountRegistryAddress = _accountRegistryAddress; |
| 29 | + initialize(_trustedAddress); |
| 30 | + } |
| 31 | + |
| 32 | + /** |
| 33 | + * @dev Sets the trusted signer that is going to be producing signatures to approve relayed calls. |
| 34 | + */ |
| 35 | + function initialize(address _trustedSigner) public initializer { |
| 36 | + require(_trustedSigner != address(0), "GSNRecipientSignature: trusted signer is the zero address"); |
| 37 | + trustedSigner = _trustedSigner; |
| 38 | + |
| 39 | + GSNRecipient.initialize(); |
| 40 | + } |
| 41 | + |
| 42 | + // @dev allow users to request 0.1eth every 24 hours |
| 43 | + function requestTestEth(address _recipient) public payable { |
| 44 | + // bytes memory linkedPublicKey = AccountRegistry(accountRegistryAddress).accountMapping(_recipient); |
| 45 | + |
| 46 | + // require(linkedPublicKey.length > 0, 'Please register this address with the SDK to request ETH'); |
| 47 | + require(faucetMapping[_recipient] + 1 days <= block.timestamp, 'Greedy please wait 24hours between requests'); |
| 48 | + |
| 49 | + faucetMapping[_recipient] = block.timestamp; |
| 50 | + |
| 51 | + address payable recipient = address(uint160(_recipient)); // Correct since Solidity >= 0.5.0 |
| 52 | + |
| 53 | + _recipient.call.value(100000000000000000).gas(20317)(""); |
| 54 | + } |
| 55 | + |
| 56 | + |
| 57 | + function () external payable { |
| 58 | + |
| 59 | + } |
| 60 | + |
| 61 | + |
| 62 | + using ECDSA for bytes32; |
| 63 | + |
| 64 | + uint256 constant private RELAYED_CALL_REJECTED = 11; |
| 65 | + |
| 66 | + enum GSNRecipientSignatureErrorCodes { |
| 67 | + INVALID_SIGNER, |
| 68 | + INVALID_TIMESTAMP |
| 69 | + } |
| 70 | + |
| 71 | + |
| 72 | + /** |
| 73 | + * @dev Return this in acceptRelayedCall to impede execution of a relayed call. No fees will be charged. |
| 74 | + */ |
| 75 | + function _rejectRelayedCall(uint256 errorCode, bytes memory context) internal pure returns (uint256, bytes memory) { |
| 76 | + return (RELAYED_CALL_REJECTED + errorCode, context); |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + * @dev Ensures that only transactions with a trusted signature can be relayed through the GSN. |
| 81 | + */ |
| 82 | + function acceptRelayedCall( |
| 83 | + address relay, |
| 84 | + address from, |
| 85 | + bytes calldata encodedFunction, |
| 86 | + uint256 transactionFee, |
| 87 | + uint256 gasPrice, |
| 88 | + uint256 gasLimit, |
| 89 | + uint256 nonce, |
| 90 | + bytes calldata approvalData, |
| 91 | + uint256 |
| 92 | + ) |
| 93 | + external |
| 94 | + view |
| 95 | + returns (uint256, bytes memory context) |
| 96 | + { |
| 97 | + ( |
| 98 | + uint256 maxTimestamp, |
| 99 | + bytes memory signature |
| 100 | + ) = abi.decode(approvalData, (uint256, bytes)); |
| 101 | + |
| 102 | + bytes memory blob = abi.encodePacked( |
| 103 | + relay, |
| 104 | + from, |
| 105 | + encodedFunction, |
| 106 | + transactionFee, |
| 107 | + gasPrice, |
| 108 | + gasLimit, |
| 109 | + nonce, // Prevents replays on RelayHub |
| 110 | + getHubAddr(), // Prevents replays in multiple RelayHubs |
| 111 | + address(this), // Prevents replays in multiple recipients |
| 112 | + maxTimestamp // Prevents sends tx after long perion of time |
| 113 | + ); |
| 114 | + context = abi.encode(signature); |
| 115 | + |
| 116 | + if (keccak256(blob).toEthSignedMessageHash().recover(signature) == trustedSigner) { |
| 117 | + if (block.timestamp > maxTimestamp) { |
| 118 | + return _rejectRelayedCall(uint256(GSNRecipientSignatureErrorCodes.INVALID_TIMESTAMP), context); |
| 119 | + } |
| 120 | + return _approveRelayedCall(context); |
| 121 | + } else { |
| 122 | + return _rejectRelayedCall(uint256(GSNRecipientSignatureErrorCodes.INVALID_SIGNER), context); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + |
| 127 | + function _preRelayedCall(bytes memory) internal returns (bytes32) { |
| 128 | + // solhint-disable-previous-line no-empty-blocks |
| 129 | + } |
| 130 | + |
| 131 | + function _postRelayedCall(bytes memory context, bool success, uint256 actualCharge, bytes32 preRetVal) internal { |
| 132 | + (bytes memory approveData) = abi.decode(context, (bytes)); |
| 133 | + emit GSNTransactionProcessed(keccak256(approveData), success, actualCharge); |
| 134 | + } |
| 135 | + |
| 136 | +} |
| 137 | + |
0 commit comments