-
Notifications
You must be signed in to change notification settings - Fork 165
/
TransferRelay.t.sol
135 lines (124 loc) · 4.73 KB
/
TransferRelay.t.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "./TestUtils.sol";
import "../patterns/bitmap-nonces/TransferRelay.sol";
import "solmate/tokens/ERC20.sol";
contract TestERC20 is ERC20("TEST", "TEST", 18) {
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract TransferRelayTest is TestUtils {
TransferRelay relay = new TransferRelay();
TestERC20 token = new TestERC20();
address receiver;
address signer;
uint256 signerKey;
function setUp() external {
receiver = makeAddr('receiver');
(signer, signerKey) = makeAddrAndKey('sender');
token.mint(signer, 1e6);
vm.prank(signer);
token.approve(address(relay), type(uint256).max);
}
function test_canExecuteTransfer() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: signer,
to: receiver,
validAfter: block.timestamp - 1,
token: IERC20(address(token)),
amount: 0.25e6,
nonce: _randomUint256()
});
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _hashMessage(mess));
relay.executeTransferMessage(mess, v, r, s);
assertEq(token.balanceOf(receiver), 0.25e6);
assertEq(token.balanceOf(signer), 0.75e6);
}
function test_cannotExecuteTransferFromZero() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: address(0),
to: receiver,
validAfter: block.timestamp - 1,
token: IERC20(address(token)),
amount: 0.25e6,
nonce: _randomUint256()
});
vm.expectRevert('bad from');
relay.executeTransferMessage(mess, 0, 0, 0);
}
function test_cannotExecuteTransferTooEarly() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: signer,
to: receiver,
validAfter: block.timestamp,
token: IERC20(address(token)),
amount: 0.25e6,
nonce: _randomUint256()
});
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _hashMessage(mess));
vm.expectRevert('not ready');
relay.executeTransferMessage(mess, v, r, s);
}
function test_cannotExecuteTransferWithBadSignature() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: signer,
to: receiver,
validAfter: block.timestamp - 1,
token: IERC20(address(token)),
amount: 0.25e6,
nonce: _randomUint256()
});
(, uint256 notSignerKey) = makeAddrAndKey('not signer');
(uint8 v, bytes32 r, bytes32 s) = vm.sign(notSignerKey, _hashMessage(mess));
vm.expectRevert('bad signature');
relay.executeTransferMessage(mess, v, r, s);
}
// This checks that the bitmap nonce works.
function test_cannotExecuteTransferTwice() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: signer,
to: receiver,
validAfter: block.timestamp - 1,
token: IERC20(address(token)),
amount: 0.25e6,
nonce: _randomUint256()
});
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _hashMessage(mess));
relay.executeTransferMessage(mess, v, r, s);
vm.expectRevert('already consumed');
relay.executeTransferMessage(mess, v, r, s);
}
// This checks that the bitmap nonce writes to the same storage slot.
function test_reusesNonceSlot() external {
TransferRelay.Message memory mess = TransferRelay.Message({
from: signer,
to: receiver,
validAfter: block.timestamp - 1,
token: IERC20(address(token)),
amount: 0.1e6,
nonce: _randomUint256() & ~uint256(0xFF)
});
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _hashMessage(mess));
// Use gas usage to determine if the same slot was written to or not.
uint256 gasUsed = gasleft();
relay.executeTransferMessage(mess, v, r, s);
gasUsed -= gasleft();
// Writing to an empty slot costs at least 20k.
assertGt(gasUsed, 20e3);
mess.nonce += 1;
(v, r, s) = vm.sign(signerKey, _hashMessage(mess));
gasUsed = gasleft();
relay.executeTransferMessage(mess, v, r, s);
gasUsed -= gasleft();
// Writing to a non-empty slot costs less than 20k.
assertLt(gasUsed, 20e3);
}
function _hashMessage(TransferRelay.Message memory mess)
private
view
returns (bytes32 h)
{
return keccak256(abi.encode(block.chainid, address(relay), mess));
}
}