status.im.md
1 # SNT Gas Relay 2 3 **Links of project**: 4 5 - https://github.com/status-im/snt-gas-relay 6 - https://ideas.status.im/ideas/150-gas-abstraction 7 8 With the goals of simplifying the onboarding experience of users at Status.im, the idea of the gas relay network was created to have a series of nodes connected to Whisper, and competing between them for processing transactions, being able to accept specific tokens as a reward for the gas fees spent for processing transactions 9 10 **Operation** 11 1. User sends an availability request to a whisper symmetric key (equivalent to a public channel), containing the details of the transaction: contract to execute, function, gas price, gas token 12 2. Relayers will analyze the request and if they accept the gas token, price and trust the contract, will reply back indicating their availability and min gas price accepted. 13 3. In the user end, they will pick a relayer that has replied back, and proceed to send a whisper message to the relayer, with the same parameters and specifying the transaction data (to, value data) 14 4. Once the relayer receives the transaction, it will perform the following validations: 15 a. Validate the contract to invoke is known by the relayer 16 b. Validate the gas token is accepted by the relayer and the gas price is greater or equal than the minimum accepted 17 c. Validation of balances / estimation of gas 18 d. Validation of transaction via estimation or simulation through Ganache 19 5. If the transaction is valid, it will be broadcasted and a whisper message with the transaction hash will be sent back to the user 20 6. Once the transaction is mined, a transaction receipt will be sent via whisper to the user 21 22 For these operations, two contracts were developed: 23 - **[SNTController](https://github.com/status-im/snt-gas-relay/blob/master/test-dapp/contracts/status/SNTController.sol)**: This contract is intended to be the owner of [SNT](https://etherscan.io/token/0x744d70fdbe2ba4cf95131626614a1763df805b9e) (a MiniMeToken). This contract allows the etherless transfer of SNT among parties, and contracts execution (this part intended to be used to generate new Identities via a factory) 24 - **[IdentityGasRelay](https://github.com/status-im/snt-gas-relay/blob/master/test-dapp/contracts/identity/IdentityGasRelay.sol)**: Provides additional meta-transaction related functions to our implementation of ERC725/735 - Identities & Claims 25 26 27 **How do we prevent a malicious relayer from either censoring a DApp they don't like or compete with, or just costing them gas by submitting a TX and then simultaneously submitting the same tx to their own relayer node, paying a slightly higher gas fee and then the honest relayers TX will fail and they will lose gas. It seems having miners act as relayers will solve this problem, but we don't want just miners to run the software right, any desktop client should be able to do it?** 28 Our gas relayer does not work like that, we are doing gas abstraction, and it's decentralized, therefore it don't matter what a gas relayer thinks, their only incentive is to include a transaction for getting SNT (or other erc20 that gas relayer have interest) for it. 29 30 **What about state channels with deterministic contract addresses for handling gas-less transactions? So, we agree that the state channel contract will be at 0xdeadbeef, then - as soon as some funds show up there, I as the operator deploy the contract and open the channel that the user can then use to send tokens without paying gas at any point?** 31 32 State channels can be used with gas relayer just if they would be used with ether gas. We are doing gas abstraction in top of account contract, so everything that works with EOA+ethergas works with Identity+ERC20gas. You basically could use any erc20 to open a state channel and transfer those tokens there, all only ever holding SNT. 33 34 35 **Would it not be possible for a user to submit a tx to an honest relayer node, and then submit that same tx to their own, malicious relayer node, have their node broadcast the tx at a higher gas fee, get it included first, and then the honest relayer's tx will fail and they will lose the gas they spent without receiving any tokens?** 36 37 We don't solve that problem right now. We expect to solve that by integrating the gas relayer in PoS. The gas cost wasted in the case mentioned would be low. To avoid that, gas relayers would black list addresses that behave like this. Integrating with PoS means validators themselves include that transactions using ether gas price zero. At the moment we will not deal with this, unless it proven to be a problem. The most natural solution is to, instead "transaction signer" making a signature than anyone can include, it would select one of many and make a signature specifically to that. There, we might not need a blocklist, while the "transaction signer" can still sign the same nonce to multiple accounts, however then we can punish the transaction signer. 38 39 40 41 **Video DEMO Link:** 42 43 - https://youtu.be/8AeIe2YMKnk 44 45 **Video DEMO summary** 46 47 - On the video it's shown the process of creating a etherless transaction for the transfer of tokens and a contract function invokation. 48 49 ## Code 50 51 ### Meta-tx format 52 53 #### Format 54 55 Signed message hashes are built via the following functions: 56 57 ##### SNTController 58 ``` 59 /** 60 * @notice get execution hash 61 * @param _allowedContract address of a contract in execution trust list; 62 * @param _data msg.data to be sent to `_allowedContract` 63 * @param _nonce current signNonce of message signer 64 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 65 * @param _gasMinimal minimal amount of gas needed to complete the execution 66 */ 67 function getExecuteGasRelayedHash( 68 address _allowedContract, 69 bytes _data, 70 uint256 _nonce, 71 uint256 _gasPrice, 72 uint256 _gasMinimal 73 ) public view returns (bytes32 execHash); 74 75 /** 76 * @notice get transfer hash 77 * @param _to address receving the tokens from message signer 78 * @param _amount total being transfered 79 * @param _nonce current signNonce of message signer 80 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 81 */ 82 function getTransferSNTHash( 83 address _to, 84 uint256 _amount, 85 uint256 _nonce, 86 uint256 _gasPrice 87 ) public view returns (bytes32 txHash); 88 ``` 89 90 ##### IdentityGasRelay 91 ``` 92 /** 93 * @notice get callGasRelay Hash 94 * @param _to destination of call 95 * @param _value call value (ether) 96 * @param _dataHash call data hash 97 * @param _nonce current identity nonce 98 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 99 * @param _gasLimit minimal gasLimit required to execute this call 100 * @param _gasToken token being used for paying `msg.sender` 101 * @return callGasRelayHash the hash to be signed by wallet 102 */ 103 function callGasRelayHash( 104 address _to, 105 uint256 _value, 106 bytes32 _dataHash, 107 uint _nonce, 108 uint256 _gasPrice, 109 uint256 _gasLimit, 110 address _gasToken 111 ) public view returns (bytes32 _callGasRelayHash); 112 113 /** 114 * @notice get deployGasRelay Hash 115 * @param _value call value (ether) 116 * @param _dataHash call data hash 117 * @param _nonce current identity nonce 118 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 119 * @param _gasLimit minimal gasLimit required to execute this call 120 * @param _gasToken token being used for paying `msg.sender` 121 * @return callGasRelayHash the hash to be signed by wallet 122 */ 123 function deployGasRelayHash( 124 uint256 _value, 125 bytes32 _dataHash, 126 uint256 _nonce, 127 uint256 _gasPrice, 128 uint256 _gasLimit, 129 address _gasToken 130 ) public view returns (bytes32 _callGasRelayHash); 131 132 /** 133 * @notice get approveAndCallHash 134 * @param _to destination of call 135 * @param _value call value (ether) 136 * @param _dataHash call data hash 137 * @param _nonce current identity nonce 138 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 139 * @param _gasLimit minimal gasLimit required to execute this call 140 * @param _gasToken token being used for paying `msg.sender` 141 * @return callGasRelayHash the hash to be signed by wallet 142 */ 143 function approveAndCallGasRelayHash( 144 address _baseToken, 145 address _to, 146 uint256 _value, 147 bytes32 _dataHash, 148 uint _nonce, 149 uint256 _gasPrice, 150 uint256 _gasLimit, 151 address _gasToken 152 ) public view returns (bytes32 _callGasRelayHash); 153 154 ``` 155 156 #### Tx 157 158 - [Protocol](https://github.com/status-im/snt-gas-relay/blob/master/relayer-protocol.md) 159 - [JS library for building mrelayer whisper comms](https://github.com/status-im/snt-gas-relay/blob/master/javascript-library.md) 160 161 #### Rx 162 TODO - Documentation for transaction receipts is still in progress 163 164 ### Contract interface + Execution function 165 166 #### SNTController 167 168 ``` 169 /** 170 * @notice allows externally owned address sign a message to transfer SNT and pay for the fees in SNT 171 * @param _to address receving the tokens from message signer 172 * @param _amount total being transfered 173 * @param _nonce current signNonce of message signer 174 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 175 * @param _signature concatenated rsv of message 176 */ 177 function transferSNT( 178 address _to, 179 uint256 _amount, 180 uint256 _nonce, 181 uint256 _gasPrice, 182 bytes _signature 183 ) external; 184 185 /** 186 * @notice allows externally owned address sign a message to offer SNT for an execution 187 * @param _allowedContract address of a contracts in execution trust list; 188 * @param _data msg.data to be sent to `_allowedContract` 189 * @param _nonce current signNonce of message signer 190 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 191 * @param _gasMinimal minimal amount of gas needed to complete the execution 192 * @param _signature concatenated rsv of message 193 */ 194 function executeGasRelayed( 195 address _allowedContract, 196 bytes _data, 197 uint256 _nonce, 198 uint256 _gasPrice, 199 uint256 _gasMinimal, 200 bytes _signature 201 ) external; 202 203 ``` 204 205 #### IdentityGasRelay 206 207 ``` 208 /** 209 * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken` 210 * allows identity of being controlled without requiring ether in key balace 211 * @param _to destination of call 212 * @param _value call value (ether) 213 * @param _data call data 214 * @param _nonce current identity nonce 215 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 216 * @param _gasLimit minimal gasLimit required to execute this call 217 * @param _gasToken token being used for paying `msg.sender` 218 * @param _messageSignatures rsv concatenated ethereum signed message signatures required 219 */ 220 function callGasRelayed( 221 address _to, 222 uint256 _value, 223 bytes _data, 224 uint _nonce, 225 uint _gasPrice, 226 uint _gasLimit, 227 address _gasToken, 228 bytes _messageSignatures 229 ); 230 231 /** 232 * @notice deploys contract in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken` 233 * allows identity of being controlled without requiring ether in key balace 234 * @param _value call value (ether) to be sent to newly created contract 235 * @param _data contract code data 236 * @param _nonce current identity nonce 237 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 238 * @param _gasLimit minimal gasLimit required to execute this call 239 * @param _gasToken token being used for paying `msg.sender` 240 * @param _messageSignatures rsv concatenated ethereum signed message signatures required 241 */ 242 function deployGasRelayed( 243 uint256 _value, 244 bytes _data, 245 uint _nonce, 246 uint _gasPrice, 247 uint _gasLimit, 248 address _gasToken, 249 bytes _messageSignatures 250 ) external returns(address deployedAddress); 251 252 /** 253 * @notice include ethereum signed approve ERC20 and call hash 254 * (`ERC20Token(baseToken).approve(_to, _value)` + `_to.call(_data)`). 255 * in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken` 256 * fixes race condition in double transaction for ERC20. 257 * @param _baseToken token approved for `_to` 258 * @param _to destination of call 259 * @param _value call value (in `_baseToken`) 260 * @param _data call data 261 * @param _nonce current identity nonce 262 * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used 263 * @param _gasLimit minimal gasLimit required to execute this call 264 * @param _gasToken token being used for paying `msg.sender` 265 * @param _messageSignatures rsv concatenated ethereum signed message signatures required 266 */ 267 function approveAndCallGasRelayed( 268 address _baseToken, 269 address _to, 270 uint256 _value, 271 bytes _data, 272 uint _nonce, 273 uint _gasPrice, 274 uint _gasLimit, 275 address _gasToken, 276 bytes _messageSignatures 277 ) external returns(bool success); 278 ```