-
Notifications
You must be signed in to change notification settings - Fork 163
/
TransferRelay.sol
58 lines (52 loc) · 2.17 KB
/
TransferRelay.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// Minimal ERC20 interface.
interface IERC20 {
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// Allows anyone to execute an ERC20 token transfer on someone else's behalf.
// Payers grant an allowance to this contract on every ERC20 they wish to send.
// Then they sign an off-chain message (hash of the `Message` struct) indicating
// recipient, amount, and time. Afterwards, anyone can submit the message to this
// contract's `executeTransferMessage()` function which consumes the message and
// executes the transfer. Messages are marked consumed using bitmap nonces rather
// than traditional, dedicated nonce slots.
contract TransferRelay {
struct Message {
address from;
address to;
uint256 validAfter;
IERC20 token;
uint256 amount;
uint256 nonce;
}
mapping (address => mapping (uint248 => uint256)) public signerNonceBitmap;
// Consume a signed transfer message and transfer tokens specified within (if valid).
function executeTransferMessage(
Message calldata mess,
uint8 v,
bytes32 r,
bytes32 s
)
external
{
require(mess.from != address(0), 'bad from');
require(mess.validAfter < block.timestamp, 'not ready');
require(!_getSignerNonceState(mess.from, mess.nonce), 'already consumed');
{
bytes32 messHash = keccak256(abi.encode(block.chainid, address(this), mess));
require(ecrecover(messHash, v, r, s) == mess.from, 'bad signature');
}
// Mark the message consumed.
_setSignerNonce(mess.from, mess.nonce);
// Perform the transfer.
mess.token.transferFrom(address(mess.from), mess.to, mess.amount);
}
function _getSignerNonceState(address signer, uint256 nonce) private view returns (bool) {
uint256 bitmap = signerNonceBitmap[signer][uint248(nonce >> 8)];
return bitmap & (1 << (nonce & 0xFF)) != 0;
}
function _setSignerNonce(address signer, uint256 nonce) private {
signerNonceBitmap[signer][uint248(nonce >> 8)] |= 1 << (nonce & 0xFF);
}
}