Skip to content

Instantly share code, notes, and snippets.

@arefaslani
Created August 13, 2020 14:16
Show Gist options
  • Save arefaslani/2cf18a162c32da2cd8497782370714a3 to your computer and use it in GitHub Desktop.
Save arefaslani/2cf18a162c32da2cd8497782370714a3 to your computer and use it in GitHub Desktop.
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./EIP712Base.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) 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 executeMetaTransaction(
address userAddress,
bytes memory functionSignature,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) public payable returns (bytes memory) {
MetaTransaction memory metaTx = MetaTransaction({
nonce: nonces[userAddress],
from: userAddress,
functionSignature: functionSignature
});
require(
verify(userAddress, metaTx, sigR, sigS, sigV),
"Signer and signature do not match"
);
// Append userAddress and relayer address 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 successfull");
nonces[userAddress] = nonces[userAddress].add(1);
emit MetaTransactionExecuted(
userAddress,
msg.sender,
functionSignature
);
return returnData;
}
function hashMetaTransaction(MetaTransaction memory metaTx)
internal
view
returns (bytes32)
{
return
keccak256(
abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
keccak256(metaTx.functionSignature)
)
);
}
function getNonce(address user) public view returns (uint256 nonce) {
nonce = nonces[user];
}
function verify(
address signer,
MetaTransaction memory metaTx,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) internal view returns (bool) {
return
signer ==
ecrecover(
toTypedMessageHash(hashMetaTransaction(metaTx)),
sigV,
sigR,
sigS
);
}
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;
}
// To recieve ether in contract
receive() external payable {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment