This repository has been archived by the owner on Oct 10, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Controller Gas Proxy and CHI token integration (#605)
* Create gas token refundable and gas token proxy contracts * Add ENS set node method to the ENS resolvable interface * Add the the gas proxy contract to the build script and regenerate bindings * Add gas proxy tests and rename token_whitelist test directory to token-whitelist * Remove ENS from gasRefundable contract and set the default gas token to CHI * Run formatter on new contracts * Add gasProxy to CI and run tools * Create tests for the gas proxy and gas refundable contracts and add associated mock contracts * Rerun slither and formatter * Address pull request comments - merge set methods and other improvements * Add gasProxy binding * Revert controllable change and remove gas token address from constructor * Upgrade to ethertest v0.9.0 * Add executeTransaction tests and increase the test coverage * Add gas estimation tests and verify the amount of gas freed by the proxy * Update ethertest branch, update go version, remove unnecessary test function * Remove parens from refundGas modifier * Check the amount of gas refunded and amount of tokens burned * Add test for meta-transaction via gas proxy * Add test for ExecutedTransaction event emission * Renamed a variable ... this is a nothing operation * This fixes the slither tests, they broke because i changed a parameter name from returndata to returnData * Adjust gas cost values in the test suite Co-authored-by: nostdm <[email protected]> Co-authored-by: i-stam <[email protected]> Co-authored-by: Mischa Tuffield <[email protected]>
- Loading branch information
1 parent
c1e8aa6
commit 11e3788
Showing
45 changed files
with
3,852 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/** | ||
* Copyright (C) 2019 The Contract Wallet Company Limited | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
pragma solidity ^0.5.17; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./internals/controllable.sol"; | ||
import "./internals/gasRefundable.sol"; | ||
|
||
|
||
contract GasProxy is Controllable, GasRefundable { | ||
/// @notice Emits the transaction executed by the controller. | ||
event ExecutedTransaction(address _destination, uint256 _value, bytes _data, bytes _returnData); | ||
|
||
/// @param _ens_ is the address of the ENS registry. | ||
/// @param _controllerNode_ ENS node of the controller contract. | ||
constructor(address _ens_, bytes32 _controllerNode_) public { | ||
_initializeENSResolvable(_ens_); | ||
_initializeControllable(_controllerNode_); | ||
} | ||
|
||
/// @param _gasTokenAddress Address of the gas token used to refund gas. | ||
/// @param _parameters Gas cost of the gas token free method call and amount of gas refunded per unit of gas token. | ||
function setGasToken(address _gasTokenAddress, GasTokenParameters calldata _parameters) external onlyAdmin { | ||
_setGasToken(_gasTokenAddress, _parameters); | ||
} | ||
|
||
/// @notice Executes a controller operation and refunds gas using gas tokens. | ||
/// @param _destination Destination address of the executed transaction. | ||
/// @param _value Amount of ETH (wei) to be sent together with the transaction. | ||
/// @param _data Data payload of the controller transaction. | ||
function executeTransaction(address _destination, uint256 _value, bytes calldata _data) external onlyController refundGas returns (bytes memory) { | ||
(bool success, bytes memory returnData) = _destination.call.value(_value)(_data); | ||
require(success, "external call failed"); | ||
emit ExecutedTransaction(_destination, _value, _data, returnData); | ||
return returnData; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* Copyright (C) 2019 The Contract Wallet Company Limited | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
pragma solidity ^0.5.17; | ||
pragma experimental ABIEncoderV2; | ||
|
||
|
||
interface IGasToken { | ||
function freeUpTo(uint256) external returns (uint256); | ||
} | ||
|
||
|
||
contract GasRefundable { | ||
/// @notice Emits the new gas token information when it is set. | ||
event SetGasToken(address _gasTokenAddress, GasTokenParameters _gasTokenParameters); | ||
|
||
struct GasTokenParameters { | ||
uint256 freeCallGasCost; | ||
uint256 gasRefundPerUnit; | ||
} | ||
|
||
/// @notice Address of the gas token used to refund gas (default: CHI). | ||
IGasToken private _gasToken = IGasToken(0x0000000000004946c0e9F43F4Dee607b0eF1fA1c); | ||
/// @notice Gas token parameters parameters used in the gas refund calcualtion (default: CHI). | ||
GasTokenParameters private _gasTokenParameters = GasTokenParameters({freeCallGasCost: 14154, gasRefundPerUnit: 41130}); | ||
|
||
/// @notice Refunds gas based on the amount of gas spent in the transaction and the gas token parameters. | ||
modifier refundGas() { | ||
uint256 gasStart = gasleft(); | ||
_; | ||
uint256 gasSpent = 21000 + gasStart - gasleft() + 16 * msg.data.length; | ||
_gasToken.freeUpTo((gasSpent + _gasTokenParameters.freeCallGasCost) / _gasTokenParameters.gasRefundPerUnit); | ||
} | ||
|
||
/// @param _gasTokenAddress Address of the gas token used to refund gas. | ||
/// @param _parameters Gas cost of the gas token free method call and amount of gas refunded per unit of gas token. | ||
function _setGasToken(address _gasTokenAddress, GasTokenParameters memory _parameters) internal { | ||
require(_gasTokenAddress != address(0), "gas token address is 0x0"); | ||
require(_parameters.freeCallGasCost != 0, "free call gas cost is 0"); | ||
require(_parameters.gasRefundPerUnit != 0, "gas refund per unit is 0"); | ||
_gasToken = IGasToken(_gasTokenAddress); | ||
_gasTokenParameters.freeCallGasCost = _parameters.freeCallGasCost; | ||
_gasTokenParameters.gasRefundPerUnit = _parameters.gasRefundPerUnit; | ||
emit SetGasToken(_gasTokenAddress, _parameters); | ||
} | ||
|
||
/// @return Address of the gas token used to refund gas. | ||
function gasToken() external view returns (address) { | ||
return address(_gasToken); | ||
} | ||
|
||
/// @return Gas cost of the gas token free method call. | ||
/// @return Amount of gas refunded per unit of gas token. | ||
function gasTokenParameters() external view returns (GasTokenParameters memory) { | ||
return _gasTokenParameters; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
pragma solidity ^0.5.17; | ||
|
||
|
||
contract GasBurner { | ||
function dummy() public pure { | ||
assembly { | ||
invalid() | ||
} | ||
} | ||
|
||
function burnGas(uint256 burn) public { | ||
// Calls self.dummy() to burn gas. | ||
assembly { | ||
mstore(0x0, 0x32e43a1100000000000000000000000000000000000000000000000000000000) | ||
let ret := call(burn, address(), 0, 0x0, 0x04, 0x0, 0) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
pragma solidity ^0.5.17; | ||
|
||
import "../externals/SafeMath.sol"; | ||
|
||
|
||
contract GasToken { | ||
using SafeMath for uint256; | ||
|
||
uint256 public totalMinted; | ||
uint256 public totalBurned; | ||
|
||
mapping(address => uint256) private _balances; | ||
|
||
function balanceOf(address account) public view returns (uint256) { | ||
return _balances[account]; | ||
} | ||
|
||
function totalSupply() public view returns (uint256) { | ||
return totalMinted - totalBurned; | ||
} | ||
|
||
function mint(uint256 value) public { | ||
uint256 offset = totalMinted; | ||
assembly { | ||
mstore(0, 0x766ffa233a79675b0530301caf58abcfa2eb3318585733ff60005260176009f3) | ||
|
||
for { | ||
let i := div(value, 32) | ||
} i { | ||
i := sub(i, 1) | ||
} { | ||
pop(create2(0, 0, 32, add(offset, 0))) | ||
pop(create2(0, 0, 32, add(offset, 1))) | ||
pop(create2(0, 0, 32, add(offset, 2))) | ||
pop(create2(0, 0, 32, add(offset, 3))) | ||
pop(create2(0, 0, 32, add(offset, 4))) | ||
pop(create2(0, 0, 32, add(offset, 5))) | ||
pop(create2(0, 0, 32, add(offset, 6))) | ||
pop(create2(0, 0, 32, add(offset, 7))) | ||
pop(create2(0, 0, 32, add(offset, 8))) | ||
pop(create2(0, 0, 32, add(offset, 9))) | ||
pop(create2(0, 0, 32, add(offset, 10))) | ||
pop(create2(0, 0, 32, add(offset, 11))) | ||
pop(create2(0, 0, 32, add(offset, 12))) | ||
pop(create2(0, 0, 32, add(offset, 13))) | ||
pop(create2(0, 0, 32, add(offset, 14))) | ||
pop(create2(0, 0, 32, add(offset, 15))) | ||
pop(create2(0, 0, 32, add(offset, 16))) | ||
pop(create2(0, 0, 32, add(offset, 17))) | ||
pop(create2(0, 0, 32, add(offset, 18))) | ||
pop(create2(0, 0, 32, add(offset, 19))) | ||
pop(create2(0, 0, 32, add(offset, 20))) | ||
pop(create2(0, 0, 32, add(offset, 21))) | ||
pop(create2(0, 0, 32, add(offset, 22))) | ||
pop(create2(0, 0, 32, add(offset, 23))) | ||
pop(create2(0, 0, 32, add(offset, 24))) | ||
pop(create2(0, 0, 32, add(offset, 25))) | ||
pop(create2(0, 0, 32, add(offset, 26))) | ||
pop(create2(0, 0, 32, add(offset, 27))) | ||
pop(create2(0, 0, 32, add(offset, 28))) | ||
pop(create2(0, 0, 32, add(offset, 29))) | ||
pop(create2(0, 0, 32, add(offset, 30))) | ||
pop(create2(0, 0, 32, add(offset, 31))) | ||
offset := add(offset, 32) | ||
} | ||
|
||
for { | ||
let i := and(value, 0x1F) | ||
} i { | ||
i := sub(i, 1) | ||
} { | ||
pop(create2(0, 0, 32, offset)) | ||
offset := add(offset, 1) | ||
} | ||
} | ||
|
||
_mint(msg.sender, value); | ||
totalMinted = offset; | ||
} | ||
|
||
function _destroyChildren(uint256 value) internal { | ||
assembly { | ||
let i := sload(totalBurned_slot) | ||
let end := add(i, value) | ||
sstore(totalBurned_slot, end) | ||
|
||
let data := mload(0x40) | ||
mstore(data, 0xff00000000fa233a79675b0530301caf58abcfa2eb0000000000000000000000) | ||
mstore(add(data, 53), 0x841da0d3b4b49d75c2a11068e21bceeb2e5d8c9e31ab7cea45c9ce114a2033dc) | ||
let ptr := add(data, 21) | ||
for { | ||
|
||
} lt(i, end) { | ||
i := add(i, 1) | ||
} { | ||
mstore(ptr, i) | ||
pop(call(gas(), keccak256(data, 85), 0, 0, 0, 0, 0)) | ||
} | ||
} | ||
} | ||
|
||
function free(uint256 value) public returns (uint256) { | ||
if (value > 0) { | ||
_burn(msg.sender, value); | ||
_destroyChildren(value); | ||
} | ||
return value; | ||
} | ||
|
||
function freeUpTo(uint256 value) public returns (uint256) { | ||
return free(_min(value, balanceOf(msg.sender))); | ||
} | ||
|
||
function transfer(address recipient, uint256 amount) public { | ||
_balances[msg.sender] = _balances[msg.sender].sub(amount); | ||
_balances[recipient] = _balances[recipient].add(amount); | ||
} | ||
|
||
function _min(uint256 a, uint256 b) private pure returns (uint256) { | ||
return a < b ? a : b; | ||
} | ||
|
||
function _mint(address account, uint256 amount) private { | ||
_balances[account] = _balances[account].add(amount); | ||
} | ||
|
||
function _burn(address account, uint256 amount) private { | ||
_balances[account] = _balances[account].sub(amount); | ||
} | ||
} |
Oops, something went wrong.