Skip to content

Commit 74dccdd

Browse files
authored
Merge pull request #52 from pentagonxyz/feat/safe-transfer-lib
feat: SafeTransferLib
2 parents 19a31df + 68400b1 commit 74dccdd

14 files changed

+1369
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ Huffmate is still a work in progress and the majority of contracts have yet to b
1818

1919
Use these contracts at your own risk!
2020

21-
2221
### Usage
2322

2423
To install with [**Foundry**](https://github.com/foundry-rs/foundry):
@@ -33,7 +32,6 @@ To install with [**Hardhat**](https://github.com/nomiclabs/hardhat) or [**Truffl
3332
npm install @pentagonxyz/huffmate
3433
```
3534

36-
3735
### Contracts
3836

3937
```ml
@@ -72,6 +70,7 @@ utils
7270
├─ Multicallable — "Enables a single call to call multiple methods within a contract"
7371
├─ TSOwnable — "An Ownable Implementation using Two-Step Transfer Pattern"
7472
├─ ReentrancyGuard — "Gas optimized reentrancy protection for smart contracts"
73+
├─ SafeTransferLib — "Safe ETH and ERC20 transfer library that gracefully handles missing return values."
7574
├─ SSTORE2 — TODO
7675
```
7776

@@ -81,6 +80,7 @@ utils
8180
These contracts were inspired by or directly modified from many sources, primarily:
8281

8382
- [solmate](https://github.com/transmissions11/solmate)
83+
- [solady](https://github.com/Vectorized/solady)
8484
- [huff-examples](https://github.com/huff-language/huff-examples)
8585
- [huff-rs](https://github.com/huff-language/huff-rs)
8686
- [huff-clones](https://github.com/clabby/huff-clones)

foundry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ fs_permissions = [
2727
{ access = "read", path = "./test/utils/mocks/MerkleProofLibWrappers.huff" },
2828
{ access = "read", path = "./test/utils/mocks/MulticallableWrappers.huff" },
2929
{ access = "read", path = "./test/utils/mocks/ReentrancyGuardWrappers.huff" },
30+
{ access = "read", path = "./test/utils/mocks/SafeTransferLibWrappers.huff" }
3031
]
3132
remappings = [
3233
"forge-std/=lib/forge-std/src/",

src/utils/SafeTransferLib.huff

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
2+
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
3+
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
4+
/// @author clabby <https://github.com/clabby>
5+
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
6+
7+
#define macro SAFE_TRANSFER_ETH() = takes (2) {
8+
// Input stack: [to, amount]
9+
0x00 dup1 dup1 dup1 // [0x00, 0x00, 0x00, 0x00, to, amount]
10+
swap5 swap1 swap4 // [to, amount, 0x00, 0x00, 0x00, 0x00]
11+
gas call // [call_success]
12+
success jumpi // []
13+
14+
// Revert with `ETHTransferFailed()` error
15+
0xb12d13eb 0x00 mstore
16+
0x04 0x1c revert
17+
18+
success:
19+
// Return stack: []
20+
}
21+
22+
#define macro SAFE_TRANSFER_FROM(mem_ptr) = takes (4) {
23+
// Input stack: [from, to, amount, token]
24+
__RIGHTPAD(0x23b872dd) // [transferFrom_selector, from, to, amount, token]
25+
<mem_ptr> mstore // [from, to, amount, token]
26+
27+
// TODO: If we designate a 128 byte scratch space for this macro,
28+
// no need to do these additions at runtime.
29+
<mem_ptr> 0x04 add // [mem_ptr + 0x04, from, to, amount, token]
30+
mstore // [to, amount, token]
31+
<mem_ptr> 0x24 add // [mem_ptr + 0x24, to, amount, token]
32+
mstore // [amount, token]
33+
<mem_ptr> 0x44 add // [mem_ptr + 0x44, amount, token]
34+
mstore // [token]
35+
36+
<mem_ptr> // [mem_ptr, token]
37+
0x64 dup2 0x00 // [0x00, mem_ptr, 0x64, mem_ptr, token]
38+
0x20 swap5 // [token, 0x00, mem_ptr, 0x64, mem_ptr, 0x20]
39+
gas call // [success]
40+
41+
returndatasize // [returndatasize, success]
42+
iszero // [returndatasize == 0, success]
43+
<mem_ptr> mload // [data, returndatasize == 0, success]
44+
0x01 eq // [data == 0x01, returndatasize == 0, success]
45+
or // [data == 0x01 | returndatasize == 0, success]
46+
47+
and // [success & (data == 0x01 | returndatasize == 0)]
48+
success jumpi // []
49+
50+
0x7939f424 0x00 mstore
51+
0x04 0x1c revert
52+
53+
success:
54+
// Return stack: []
55+
}
56+
57+
#define macro SAFE_TRANSFER(mem_ptr) = takes (3) {
58+
// Input stack: [to, amount, token]
59+
60+
__RIGHTPAD(0xa9059cbb) // [transfer_selector, to, amount, token]
61+
<mem_ptr> mstore // [to, amount, token]
62+
63+
// TODO: If we designate a 96 byte scratch space for this macro,
64+
// no need to do these additions at runtime.
65+
<mem_ptr> 0x04 add // [mem_ptr + 0x04, to, amount, token]
66+
mstore // [amount, token]
67+
<mem_ptr> 0x24 add // [mem_ptr + 0x24, amount, token]
68+
mstore
69+
70+
<mem_ptr> // [mem_ptr, token]
71+
0x44 dup2 0x00 // [0x00, mem_ptr, 0x44, mem_ptr, token]
72+
0x20 swap5 // [token, 0x00, mem_ptr, 0x44, mem_ptr, 0x20]
73+
gas call // [success]
74+
75+
returndatasize // [returndatasize, success]
76+
iszero // [returndatasize == 0, success]
77+
<mem_ptr> mload // [data, returndatasize == 0, success]
78+
0x01 eq // [data == 0x01, returndatasize == 0, success]
79+
or // [data == 0x01 | returndatasize == 0, success]
80+
81+
and // [success & (data == 0x01 | returndatasize == 0)]
82+
success jumpi // []
83+
84+
0x90b8ec18 0x00 mstore
85+
0x04 0x1c revert
86+
87+
success:
88+
// Return stack: []
89+
}
90+
91+
#define macro SAFE_APPROVE(mem_ptr) = takes (3) {
92+
// Input stack: [to, amount, token]
93+
94+
__RIGHTPAD(0x095ea7b3) // [transfer_selector, to, amount, token]
95+
<mem_ptr> mstore // [to, amount, token]
96+
97+
// TODO: If we designate a 96 byte scratch space for this macro,
98+
// no need to do these additions at runtime.
99+
<mem_ptr> 0x04 add // [mem_ptr + 0x04, to, amount, token]
100+
mstore // [amount, token]
101+
<mem_ptr> 0x24 add // [mem_ptr + 0x24, amount, token]
102+
mstore
103+
104+
<mem_ptr> // [mem_ptr, token]
105+
0x44 dup2 0x00 // [0x00, mem_ptr, 0x44, mem_ptr, token]
106+
0x20 swap5 // [token, 0x00, mem_ptr, 0x44, mem_ptr, 0x20]
107+
gas call // [success]
108+
109+
returndatasize // [returndatasize, success]
110+
iszero // [returndatasize == 0, success]
111+
<mem_ptr> mload // [data, returndatasize == 0, success]
112+
0x01 eq // [data == 0x01, returndatasize == 0, success]
113+
or // [data == 0x01 | returndatasize == 0, success]
114+
115+
and // [success & (data == 0x01 | returndatasize == 0)]
116+
success jumpi // []
117+
118+
0x3e3f8f73 0x00 mstore
119+
0x04 0x1c revert
120+
121+
success:
122+
// Return stack: []
123+
}

test/tokens/ERC1155.t.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,7 @@ contract ERC1155Test is Test, ERC1155Recipient, FuzzingUtils{
12261226
for (uint256 i = 0; i < minLength; i++) {
12271227
tos[i] = tos[i] == address(0) ? address(0xBEEF) : tos[i];
12281228
tos[i] = tos[i] == address(this) ? address(0xBEEF) : tos[i];
1229+
tos[i] = tos[i] == address(token) ? address(0xBEEF) : tos[i];
12291230
}
12301231

12311232
address[] memory normalizedTos = new address[](minLength);
@@ -1242,6 +1243,8 @@ contract ERC1155Test is Test, ERC1155Recipient, FuzzingUtils{
12421243

12431244
uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId);
12441245

1246+
// We only want EOAs
1247+
vm.assume(address(to).code.length == 0);
12451248
token.mint(to, id, mintAmount, mintData);
12461249

12471250
userMintAmounts[to][id] += mintAmount;

0 commit comments

Comments
 (0)