mirror of
https://github.com/status-im/snt-gas-relay.git
synced 2025-02-26 12:45:22 +00:00
reorg
This commit is contained in:
commit
6b56999401
364
contracts/identity/IdentityGasRelay.sol
Normal file
364
contracts/identity/IdentityGasRelay.sol
Normal file
@ -0,0 +1,364 @@
|
||||
pragma solidity ^0.4.21;
|
||||
|
||||
import "./Identity.sol";
|
||||
import "../token/ERC20Token.sol";
|
||||
|
||||
/**
|
||||
* @title IdentityGasRelay
|
||||
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||
* @notice enables economic abstraction for Identity
|
||||
*/
|
||||
contract IdentityGasRelay is Identity {
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @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 _gasLimit minimal gasLimit required to execute this call
|
||||
* @param _gasToken token being used for paying `msg.sender`
|
||||
* @param _messageSignatures rsv concatenated ethereum signed message signatures required
|
||||
*/
|
||||
function callGasRelayed(
|
||||
address _to,
|
||||
uint256 _value,
|
||||
bytes _data,
|
||||
uint _nonce,
|
||||
uint _gasPrice,
|
||||
uint _gasLimit,
|
||||
address _gasToken,
|
||||
bytes _messageSignatures
|
||||
)
|
||||
external
|
||||
{
|
||||
uint startGas = gasleft();
|
||||
//verify transaction parameters
|
||||
require(startGas >= _gasLimit);
|
||||
require(_nonce == nonce);
|
||||
// calculates signHash
|
||||
bytes32 signHash = getSignHash(
|
||||
callGasRelayHash(
|
||||
_to,
|
||||
_value,
|
||||
keccak256(_data),
|
||||
_nonce,
|
||||
_gasPrice,
|
||||
_gasLimit,
|
||||
_gasToken
|
||||
)
|
||||
);
|
||||
|
||||
//verify if signatures are valid and came from correct actors;
|
||||
verifySignatures(
|
||||
_to == address(this) ? MANAGEMENT_KEY : ACTION_KEY,
|
||||
signHash,
|
||||
_messageSignatures
|
||||
);
|
||||
|
||||
//executes transaction
|
||||
nonce++;
|
||||
bool success = _to.call.value(_value)(_data);
|
||||
emit ExecutedGasRelayed(
|
||||
signHash,
|
||||
success
|
||||
);
|
||||
|
||||
//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 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 _gasLimit minimal gasLimit required to execute this call
|
||||
* @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 _gasLimit,
|
||||
address _gasToken,
|
||||
bytes _messageSignatures
|
||||
)
|
||||
external
|
||||
{
|
||||
uint startGas = gasleft();
|
||||
//verify transaction parameters
|
||||
require(startGas >= _gasLimit);
|
||||
require(_nonce == nonce);
|
||||
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,
|
||||
_gasLimit,
|
||||
_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.
|
||||
* @param _requiredKey key required to call, if _to from payload is the identity itself, is `MANAGEMENT_KEY`, else `ACTION_KEY`
|
||||
* @param _signHash ethereum signable callGasRelayHash message provided for the payload
|
||||
* @param _messageSignatures ethereum signed `_signHash` messages
|
||||
* @return true case valid
|
||||
*/
|
||||
function verifySignatures(
|
||||
uint256 _requiredKey,
|
||||
bytes32 _signHash,
|
||||
bytes _messageSignatures
|
||||
)
|
||||
public
|
||||
view
|
||||
returns(bool)
|
||||
{
|
||||
uint _amountSignatures = _messageSignatures.length / 72;
|
||||
require(_amountSignatures == purposeThreshold[_requiredKey]);
|
||||
bytes32 _lastKey = 0;
|
||||
for (uint256 i = 0; i < _amountSignatures; i++) {
|
||||
bytes32 _currentKey = recoverKey(
|
||||
_signHash,
|
||||
_messageSignatures,
|
||||
i
|
||||
);
|
||||
require(_currentKey > _lastKey); //assert keys are different
|
||||
require(isKeyPurpose(_currentKey, _requiredKey));
|
||||
_lastKey = _currentKey;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 _gasLimit minimal gasLimit required to execute this call
|
||||
* @param _gasToken token being used for paying `msg.sender`
|
||||
* @return callGasRelayHash the hash to be signed by wallet
|
||||
*/
|
||||
function callGasRelayHash(
|
||||
address _to,
|
||||
uint256 _value,
|
||||
bytes32 _dataHash,
|
||||
uint _nonce,
|
||||
uint256 _gasPrice,
|
||||
uint256 _gasLimit,
|
||||
address _gasToken
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bytes32 _callGasRelayHash)
|
||||
{
|
||||
_callGasRelayHash = keccak256(
|
||||
address(this),
|
||||
CALL_PREFIX,
|
||||
_to,
|
||||
_value,
|
||||
_dataHash,
|
||||
_nonce,
|
||||
_gasPrice,
|
||||
_gasLimit,
|
||||
_gasToken
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 _gasLimit minimal gasLimit required to execute this call
|
||||
* @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 _gasLimit,
|
||||
address _gasToken
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bytes32 _callGasRelayHash)
|
||||
{
|
||||
_callGasRelayHash = keccak256(
|
||||
address(this),
|
||||
APPROVEANDCALL_PREFIX,
|
||||
_baseToken,
|
||||
_to,
|
||||
_value,
|
||||
_dataHash,
|
||||
_nonce,
|
||||
_gasPrice,
|
||||
_gasLimit,
|
||||
_gasToken
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
|
||||
* @param _hash Sign to hash.
|
||||
* @return signHash Hash ethereum wallet signs.
|
||||
*/
|
||||
function getSignHash(
|
||||
bytes32 _hash
|
||||
)
|
||||
pure
|
||||
public
|
||||
returns(bytes32 signHash)
|
||||
{
|
||||
signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice recovers address who signed the message
|
||||
* @param _signHash operation ethereum signed message hash
|
||||
* @param _messageSignature message `_signHash` signature
|
||||
* @param _pos which signature to read
|
||||
*/
|
||||
function recoverKey (
|
||||
bytes32 _signHash,
|
||||
bytes _messageSignature,
|
||||
uint256 _pos
|
||||
)
|
||||
pure
|
||||
public
|
||||
returns(bytes32)
|
||||
{
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
(v,r,s) = signatureSplit(_messageSignature, _pos);
|
||||
return bytes32(
|
||||
ecrecover(
|
||||
_signHash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
|
||||
* @param _pos which signature to read
|
||||
* @param _signatures concatenated vrs signatures
|
||||
*/
|
||||
function signatureSplit(bytes _signatures, uint256 _pos)
|
||||
pure
|
||||
public
|
||||
returns (uint8 v, bytes32 r, bytes32 s)
|
||||
{
|
||||
uint pos = _pos + 1;
|
||||
// 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(_signatures, mul(32,pos)))
|
||||
s := mload(add(_signatures, mul(64,pos)))
|
||||
// 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(_signatures, mul(65,pos))), 0xff)
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
298
contracts/status/SNTController.sol
Normal file
298
contracts/status/SNTController.sol
Normal file
@ -0,0 +1,298 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user