-
Notifications
You must be signed in to change notification settings - Fork 64
/
EIP712MetaTransaction.sol
88 lines (75 loc) · 3.5 KB
/
EIP712MetaTransaction.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
76
77
78
79
80
81
82
83
84
85
86
87
88
//SPDX-License-Identifier: MIT
pragma solidity 0.6.6;
import "./lib/EIP712Base.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
contract EIP712MetaTransaction is EIP712Base {
using SafeMath for uint256;
bytes32 private constant META_TRANSACTION_TYPEHASH = keccak256(bytes("MetaTransaction(uint256 nonce,address from,bytes functionSignature)"));
event MetaTransactionExecuted(address userAddress, address payable relayerAddress, bytes functionSignature);
mapping(address => uint256) private nonces;
/*
* Meta transaction structure.
* No point of including value field here as if user is doing value transfer then he has the funds to pay for gas
* He should call the desired function directly in that case.
*/
struct MetaTransaction {
uint256 nonce;
address from;
bytes functionSignature;
}
constructor(string memory name, string memory version) public EIP712Base(name, version) {}
function convertBytesToBytes4(bytes memory inBytes) internal returns (bytes4 outBytes4) {
if (inBytes.length == 0) {
return 0x0;
}
assembly {
outBytes4 := mload(add(inBytes, 32))
}
}
function executeMetaTransaction(address userAddress,
bytes memory functionSignature, bytes32 sigR, bytes32 sigS, uint8 sigV) public payable returns(bytes memory) {
bytes4 destinationFunctionSig = convertBytesToBytes4(functionSignature);
require(destinationFunctionSig != msg.sig, "functionSignature can not be of executeMetaTransaction method");
MetaTransaction memory metaTx = MetaTransaction({
nonce: nonces[userAddress],
from: userAddress,
functionSignature: functionSignature
});
require(verify(userAddress, metaTx, 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 hashMetaTransaction(MetaTransaction memory metaTx) internal pure returns (bytes32) {
return keccak256(abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
keccak256(metaTx.functionSignature)
));
}
function getNonce(address user) external view returns(uint256 nonce) {
nonce = nonces[user];
}
function verify(address user, MetaTransaction memory metaTx, bytes32 sigR, bytes32 sigS, uint8 sigV) internal view returns (bool) {
address signer = ecrecover(toTypedMessageHash(hashMetaTransaction(metaTx)), sigV, sigR, sigS);
require(signer != address(0), "Invalid signature");
return signer == user;
}
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 {
sender = msg.sender;
}
return sender;
}
}