/
BasicMetaTransaction.sol
75 lines (61 loc) · 3.05 KB
/
BasicMetaTransaction.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
pragma solidity 0.6.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
contract BasicMetaTransaction {
using SafeMath for uint256;
event MetaTransactionExecuted(address userAddress, address payable relayerAddress, bytes functionSignature);
mapping(address => uint256) private nonces;
function getChainID() public pure returns (uint256) {
uint256 id;
assembly {
id := chainid()
}
return id;
}
/**
* Main function to be called when user wants to execute meta transaction.
* The actual function to be called should be passed as param with name functionSignature
* Here the basic signature recovery is being used. Signature is expected to be generated using
* personal_sign method.
* @param userAddress Address of user trying to do meta transaction
* @param functionSignature Signature of the actual function to be called via meta transaction
* @param sigR R part of the signature
* @param sigS S part of the signature
* @param sigV V part of the signature
*/
function executeMetaTransaction(address userAddress, bytes memory functionSignature,
bytes32 sigR, bytes32 sigS, uint8 sigV) public payable returns(bytes memory) {
require(verify(userAddress, nonces[userAddress], getChainID(), functionSignature, sigR, sigS, sigV), "Signer and signature do not match");
nonces[userAddress] = nonces[userAddress].add(1);
// Append userAddress at the end to extract it from calling context
(bool success, bytes memory returnData) = address(this).call(abi.encodePacked(functionSignature, userAddress));
require(success, "Function call not successful");
emit MetaTransactionExecuted(userAddress, msg.sender, functionSignature);
return returnData;
}
function getNonce(address user) external view returns(uint256 nonce) {
nonce = nonces[user];
}
// Builds a prefixed hash to mimic the behavior of eth_sign.
function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
function verify(address owner, uint256 nonce, uint256 chainID, bytes memory functionSignature,
bytes32 sigR, bytes32 sigS, uint8 sigV) public view returns (bool) {
bytes32 hash = prefixed(keccak256(abi.encodePacked(nonce, this, chainID, functionSignature)));
address signer = ecrecover(hash, sigV, sigR, sigS);
require(signer != address(0), "Invalid signature");
return (owner == signer);
}
function msgSender() internal view returns(address sender) {
if(msg.sender == address(this)) {
bytes memory array = msg.data;
uint256 index = msg.data.length;
assembly {
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
sender := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
} else {
return msg.sender;
}
}
}