added approve and call gas relayed for ERC20

This commit is contained in:
Ricardo Guilherme Schmidt 2018-03-24 01:26:06 -03:00
parent fe4a10dfcd
commit 4c5cb43da0
1 changed files with 137 additions and 1 deletions

View File

@ -11,11 +11,12 @@ import "../token/ERC20Token.sol";
contract IdentityGasRelay is Identity { contract IdentityGasRelay is Identity {
bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)")); bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)"));
bytes4 public constant APPROVEANDCALL_PREFIX = bytes4(keccak256("approveAndCallGasRelay(address,address,uint256,bytes32,uint256,uint256)"));
event ExecutedGasRelayed(bytes32 signHash, bool success); event ExecutedGasRelayed(bytes32 signHash, bool success);
/** /**
* @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken` * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
* allows identity of being controlled without requiring ether in key balace * allows identity of being controlled without requiring ether in key balace
* @param _to destination of call * @param _to destination of call
* @param _value call value (ether) * @param _value call value (ether)
@ -83,6 +84,82 @@ contract IdentityGasRelay is Identity {
} }
} }
/**
* @notice include ethereum signed approve ERC20 and call hash
* (`ERC20Token(baseToken).approve(_to, _value)` + `_to.call(_data)`).
* in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
* fixes race condition in double transaction for ERC20.
* @param _baseToken token approved for `_to`
* @param _to destination of call
* @param _value call value (ether)
* @param _data call data
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _gasMinimal minimal amount of gas needed to complete the execution
* @param _gasToken token being used for paying `msg.sender`
* @param _messageSignatures rsv concatenated ethereum signed message signatures required
*/
function approveAndCallGasRelayed(
address _baseToken,
address _to,
uint256 _value,
bytes _data,
uint _nonce,
uint _gasPrice,
uint _gasMinimal,
address _gasToken,
bytes _messageSignatures
)
external
{
//verify transaction parameters
require(_nonce == nonce);
uint startGas = gasleft();
require(startGas >= _gasMinimal);
require(_baseToken != address(0)); //_baseToken should be something!
require(_to != address(this)); //no management with approveAndCall
// calculates signHash
bytes32 signHash = getSignHash(
approveAndCallGasRelayHash(
_baseToken,
_to,
_value,
keccak256(_data),
_nonce,
_gasPrice,
_gasMinimal,
_gasToken
)
);
//verify if signatures are valid and came from correct actors;
verifySignatures(
ACTION_KEY, //no management with approveAndCall
signHash,
_messageSignatures
);
approveAndCall(
signHash,
_baseToken,
_to,
_value,
_data
);
//refund gas used using contract held ERC20 tokens or ETH
if (_gasPrice > 0) {
uint256 _amount = 21000 + (startGas - gasleft());
_amount = _amount * _gasPrice;
if (_gasToken == address(0)) {
address(msg.sender).transfer(_amount);
} else {
ERC20Token(_gasToken).transfer(msg.sender, _amount);
}
}
}
/** /**
* @notice reverts if signatures are not valid for the signed hash and required key type. * @notice reverts if signatures are not valid for the signed hash and required key type.
@ -154,6 +231,46 @@ contract IdentityGasRelay is Identity {
); );
} }
/**
* @notice get callHash
* @param _to destination of call
* @param _value call value (ether)
* @param _dataHash call data hash
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _gasMinimal minimal amount of gas needed to complete the execution
* @param _gasToken token being used for paying `msg.sender`
* @return callGasRelayHash the hash to be signed by wallet
*/
function approveAndCallGasRelayHash(
address _baseToken,
address _to,
uint256 _value,
bytes32 _dataHash,
uint _nonce,
uint256 _gasPrice,
uint256 _gasMinimal,
address _gasToken
)
public
view
returns (bytes32 _callGasRelayHash)
{
_callGasRelayHash = keccak256(
address(this),
APPROVEANDCALL_PREFIX,
_baseToken,
_to,
_value,
_dataHash,
_nonce,
_gasPrice,
_gasMinimal,
_gasToken
);
}
/** /**
* @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"` * @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
* @param _hash Sign to hash. * @param _hash Sign to hash.
@ -226,4 +343,23 @@ contract IdentityGasRelay is Identity {
require(v == 27 || v == 28); require(v == 27 || v == 28);
} }
function approveAndCall(
bytes32 _signHash,
address _token,
address _to,
uint256 _value,
bytes _data
)
private
{
//executes transaction
nonce++;
ERC20Token(_token).approve(_to, _value);
emit ExecutedGasRelayed(
_signHash,
_to.call(_data)
);
}
} }