snt-gas-relay/contracts/status/SNTController.sol

298 lines
9.0 KiB
Solidity

pragma solidity ^0.4.17;
import "../token/TokenController.sol";
import "../common/Owned.sol";
import "../token/ERC20Token.sol";
import "../token/MiniMeToken.sol";
/**
* @title SNTController
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @notice enables economic abstraction for SNT
*/
contract SNTController is TokenController, Owned {
bytes4 public constant TRANSFER_PREFIX = bytes4(keccak256("transferSNT(address,uint256,uint256,uint256)"));
bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed(address,bytes,uint256,uint256,uint256)"));
MiniMeToken public snt;
mapping (address => uint256) public signNonce;
mapping (address => bool) public allowPublicExecution;
event PublicExecutionEnabled(address indexed contractAddress, bool enabled);
event GasRelayedExecution(address indexed msgSigner, bytes32 signedHash, bool executed);
event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount);
event ControllerChanged(address indexed _newController);
/**
* @notice Constructor
* @param _owner Authority address
* @param _snt SNT token
*/
function SNTController(address _owner, address _snt) public {
owner = _owner;
snt = MiniMeToken(_snt);
}
/**
* @notice allows externally owned address sign a message to transfer SNT and pay
* @param _to address receving the tokens from message signer
* @param _amount total being transfered
* @param _nonce current signNonce of message signer
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _signature concatenated rsv of message
*/
function transferSNT(
address _to,
uint256 _amount,
uint256 _nonce,
uint256 _gasPrice,
bytes _signature
)
external
{
uint256 startGas = gasleft();
bytes32 msgSigned = getSignHash(
getTransferSNTHash(
_to,
_amount,
_nonce,
_gasPrice
)
);
address msgSigner = recoverAddress(msgSigned, _signature);
require(signNonce[msgSigner] == _nonce);
signNonce[msgSigner]++;
if (snt.transferFrom(msgSigner, _to, _amount)) {
require(snt.transferFrom(msgSigner, msg.sender, (21000 + startGas-gasleft()) * _gasPrice));
}
}
/**
* @notice allows externally owned address sign a message to offer SNT for a execution
* @param _allowedContract address of a contracts in execution trust list;
* @param _data msg.data to be sent to `_allowedContract`
* @param _nonce current signNonce of message signer
* @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 _signature concatenated rsv of message
*/
function executeGasRelayed(
address _allowedContract,
bytes _data,
uint256 _nonce,
uint256 _gasPrice,
uint256 _gasMinimal,
bytes _signature
)
external
{
uint256 startGas = gasleft();
require(startGas >= _gasMinimal);
bytes32 msgSigned = getSignHash(
getExecuteGasRelayedHash(
_allowedContract,
_data,
_nonce,
_gasPrice,
_gasMinimal
)
);
address msgSigner = recoverAddress(msgSigned, _signature);
require(signNonce[msgSigner] == _nonce);
signNonce[msgSigner]++;
bool success = _allowedContract.call(_data);
emit GasRelayedExecution(msgSigner, msgSigned, success);
require(
snt.transferFrom(
msgSigner,
msg.sender,
(21000 + startGas-gasleft()) * _gasPrice
)
);
}
/**
* @notice The owner of this contract can change the controller of the SNT token
* Please, be sure that the owner is a trusted agent or 0x0 address.
* @param _newController The address of the new controller
*/
function changeController(address _newController) public onlyOwner {
snt.changeController(_newController);
emit ControllerChanged(_newController);
}
function enablePublicExecution(address _contract, bool _enable) public onlyOwner {
allowPublicExecution[_contract] = _enable;
emit PublicExecutionEnabled(_contract, _enable);
}
//////////
// Safety Methods
//////////
/**
* @notice This method can be used by the controller to extract mistakenly
* sent tokens to this contract.
* @param _token The address of the token contract that you want to recover
* set to 0 in case you want to extract ether.
*/
function claimTokens(address _token) public onlyOwner {
if (snt.controller() == address(this)) {
snt.claimTokens(_token);
}
if (_token == 0x0) {
address(owner).transfer(this.balance);
return;
}
ERC20Token token = ERC20Token(_token);
uint256 balance = token.balanceOf(this);
token.transfer(owner, balance);
emit ClaimedTokens(_token, owner, balance);
}
//////////
// MiniMe Controller Interface functions
//////////
// In between the offering and the network. Default settings for allowing token transfers.
function proxyPayment(address) public payable returns (bool) {
return false;
}
function onTransfer(address, address, uint256) public returns (bool) {
return true;
}
function onApprove(address, address, uint256) public returns (bool) {
return true;
}
/**
* @notice get execution hash
* @param _allowedContract address of a contracts in execution trust list;
* @param _data msg.data to be sent to `_allowedContract`
* @param _nonce current signNonce of message signer
* @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
*/
function getExecuteGasRelayedHash(
address _allowedContract,
bytes _data,
uint256 _nonce,
uint256 _gasPrice,
uint256 _gasMinimal
)
public
view
returns (bytes32 execHash)
{
execHash = keccak256(
address(this),
EXECUTE_PREFIX,
_allowedContract,
keccak256(_data),
_nonce,
_gasPrice,
_gasMinimal
);
}
/**
* @notice get transfer hash
* @param _to address receving the tokens from message signer
* @param _amount total being transfered
* @param _nonce current signNonce of message signer
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
*/
function getTransferSNTHash(
address _to,
uint256 _amount,
uint256 _nonce,
uint256 _gasPrice
)
public
view
returns (bytes32 txHash)
{
txHash = keccak256(
address(this),
TRANSFER_PREFIX,
_to,
_amount,
_nonce,
_gasPrice
);
}
/**
* @notice recovers address who signed the message
* @param _signHash operation ethereum signed message hash
* @param _messageSignature message `_signHash` signature
*/
function recoverAddress(
bytes32 _signHash,
bytes _messageSignature
)
pure
public
returns(address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v,r,s) = signatureSplit(_messageSignature);
return ecrecover(
_signHash,
v,
r,
s
);
}
/**
* @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
*/
function signatureSplit(bytes _signature)
pure
public
returns (uint8 v, bytes32 r, bytes32 s)
{
// The signature format is a compact form of:
// {bytes32 r}{bytes32 s}{uint8 v}
// Compact means, uint8 is not padded to 32 bytes.
assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
// Here we are loading the last 32 bytes, including 31 bytes
// of 's'. There is no 'mload8' to do this.
//
// 'byte' is not working due to the Solidity parser, so lets
// use the second best option, 'and'
v := and(mload(add(_signature, 65)), 0xff)
}
require(v == 27 || v == 28);
}
/**
* @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
* @param _hash Sign to hash.
* @return signHash Hash to be signed.
*/
function getSignHash(
bytes32 _hash
)
pure
public
returns (bytes32 signHash)
{
signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
}
}