Skip to content

Commit 98d397f

Browse files
authored
Merge pull request #72 from pentagonxyz/dev
staging: Huffmate V1.1
2 parents e870794 + b57f42b commit 98d397f

35 files changed

+1813
-423
lines changed

.gas-snapshot

Lines changed: 134 additions & 112 deletions
Large diffs are not rendered by default.

.github/workflows/test.yml

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
name: ci
22

3-
on:
4-
# manual trigger
5-
workflow_dispatch:
6-
7-
# on push
8-
push:
9-
paths:
10-
- test/**
11-
- src/**
3+
on: [push]
124

135
jobs:
146
check:
15-
strategy:
16-
fail-fast: true
177
name: Foundry project
188
runs-on: ubuntu-latest
199
steps:
@@ -33,8 +23,6 @@ jobs:
3323
id: build
3424

3525
tests:
36-
strategy:
37-
fail-fast: true
3826
name: Foundry project
3927
runs-on: ubuntu-latest
4028
steps:
@@ -69,9 +57,8 @@ jobs:
6957
with:
7058
version: nightly
7159
- name: Huff Tests
72-
uses: abigger87/huff-tests@v2.0.0
60+
uses: huff-language/huff-tests-action@v3
7361
with:
74-
with-forge-tests: false
75-
test-location: "src"
76-
test-extension: "*.huff"
77-
62+
with-location: "src"
63+
with-extension: "*.huff"
64+
with-format: "table"

README.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<img align="right" width="150" height="150" top="100" src="./assets/huff.jpg">
22

3-
# huffmate • [![ci](https://github.com/pentagonxyz/huffmate/actions/workflows/test.yml/badge.svg)](https://github.com/pentagonxyz/huffmate/actions/workflows/test.yml) [![version](https://img.shields.io/badge/version-v1-ff69b4)](https://github.com/pentagonxyz/huffmate/releases/tag/v1) [![license](https://img.shields.io/badge/License-MIT-orange.svg?label=license)](https://opensource.org/licenses/MIT) ![Discord](https://img.shields.io/discord/980519274600882306?color=blue)
3+
# huffmate • [![ci](https://github.com/pentagonxyz/huffmate/actions/workflows/test.yml/badge.svg)](https://github.com/pentagonxyz/huffmate/actions/workflows/test.yml) [![version](https://img.shields.io/badge/version-v1.1-ff69b4)](https://github.com/pentagonxyz/huffmate/releases/tag/v1.1) [![license](https://img.shields.io/badge/License-MIT-orange.svg?label=license)](https://opensource.org/licenses/MIT) ![Discord](https://img.shields.io/discord/980519274600882306?color=blue)
44

55
A set of **modern**, **opinionated**, and **secure** [Huff](https://github.com/huff-language) contracts.
66

@@ -37,6 +37,7 @@ auth
3737
data-structures
3838
├─ Arrays — "Memory translation handlers for arrays"
3939
├─ Hashmap — "Simple mapping utilities for 32 byte words"
40+
├─ Bytes — "Helpers for working with Bytes"
4041
proxies
4142
├─ Clones — "Clones library for deploying minimal proxy contracts"
4243
├─ Proxy — "Minimal ERC1967-compliant + upgradeable proxy contract"
@@ -61,20 +62,28 @@ tokens
6162
├─ ERC1155 — "Minimalist and gas efficient standard ERC1155 implementation"
6263
├─ ERC4626 — "Minimal ERC4626 tokenized Vault implementation"
6364
utils
64-
├─ Calls — "Minimal wrappers for constructing calls to other contracts"
65+
├─ Address — "Simple Utils for working with addresses"
6566
├─ BitPackLib — "Efficient bit packing library"
66-
├─ CustomErrors — "Wrappers for reverting with common error messages"
67+
├─ Calls — "Minimal wrappers for constructing calls to other contracts"
68+
├─ CommonErrors — "Wrappers for reverting with common error messages"
69+
├─ CREATE3 — "Deploy to deterministic addresses without the initcode factor"
70+
├─ ECDSA — "An optimised ECDSA wrapper"
6771
├─ ERC1155Receiver — "A minimal interface for receiving ERC1155 tokens"
72+
├─ ERC20Transfer — "Efficient ERC20 transfer wrappers"
6873
├─ Errors — "Custom error utilities"
74+
├─ Ethers — "Utilities for working with ether at a low level"
6975
├─ JumpTableUtil — "Utility macros for retrieving jumpdest pcs from jump tables"
7076
├─ LibBit — "A library ported from solady for bit twiddling operations"
7177
├─ MerkleProofLib — "Gas optimized merkle proof verification library"
7278
├─ Multicallable — "Enables a single call to call multiple methods within a contract"
73-
├─ TSOwnable — "An Ownable Implementation using Two-Step Transfer Pattern"
79+
├─ Pausable — "An implementation of the Pausable standard"
7480
├─ ReentrancyGuard — "Gas optimized reentrancy protection for smart contracts"
81+
├─ Refunded — "Efficient gas refunds distributed through a modifier"
7582
├─ SafeTransferLib — "Safe ETH and ERC20 transfer library that gracefully handles missing return values"
7683
├─ Shuffling — "Refactored algorithms for shuffling and other bitwise algorithms"
7784
└─ SSTORE2 — "Faster & cheaper contract key-value storage for Ethereum Contracts"
85+
└─ Ternary — "A collection of ternary operations with abstracted conditional logic"
86+
└─ TSOwnable — "An Ownable Implementation using Two-Step Transfer Pattern"
7887
```
7988

8089
### Acknowledgements
@@ -88,7 +97,7 @@ These contracts were inspired by or directly modified from many sources, primari
8897
- [huff-rs](https://github.com/huff-language/huff-rs)
8998
- [huff-clones](https://github.com/clabby/huff-clones)
9099
- [huff-vrgda](https://github.com/cheethas/huff-vrgda)
91-
- [huff-tests](https://github.com/abigger87/huff-tests)
100+
- [huff-tests](https://github.com/huff-language/huff-tests-action)
92101
- [erc721h](https://github.com/philogy/erc721h)
93102
- [Gnosis](https://github.com/gnosis/gp-v2-contracts)
94103
- [Uniswap](https://github.com/Uniswap/uniswap-lib)

foundry.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fs_permissions = [
1010

1111
{ access = "read", path = "./test/data-structures/mocks/ArrayWrappers.huff" },
1212
{ access = "read", path = "./test/data-structures/mocks/HashmapWrappers.huff" },
13+
{ access = "read", path = "./test/data-structures/mocks/BytesWrappers.huff" },
1314

1415
{ access = "read", path = "./test/math/mocks/FixedPointMathWrappers.huff" },
1516
{ access = "read", path = "./test/math/mocks/SafeMathWrappers.huff" },
@@ -32,18 +33,21 @@ fs_permissions = [
3233
{ access = "read", path = "./test/utils/mocks/BitPackLibWrappers.huff" },
3334
{ access = "read", path = "./test/utils/mocks/CallWrappers.huff" },
3435
{ access = "read", path = "./test/utils/mocks/ErrorWrappers.huff" },
36+
{ access = "read", path = "./test/utils/mocks/EthersWrappers.huff" },
3537
{ access = "read", path = "./test/utils/mocks/JumpTableUtilWrappers.huff" },
3638
{ access = "read", path = "./test/utils/mocks/LibBitWrappers.huff" },
3739
{ access = "read", path = "./test/utils/mocks/MerkleDistributorWrappers.huff" },
3840
{ access = "read", path = "./test/utils/mocks/MerkleProofLibWrappers.huff" },
3941
{ access = "read", path = "./test/utils/mocks/MulticallableWrappers.huff" },
4042
{ access = "read", path = "./test/utils/mocks/Create3Wrappers.huff" },
43+
{ access = "read", path = "./test/utils/mocks/PausableWrappers.huff" },
4144
{ access = "read", path = "./test/utils/mocks/ReentrancyGuardWrappers.huff" },
4245
{ access = "read", path = "./test/utils/mocks/RefundedWrappers.huff" },
4346
{ access = "read", path = "./test/utils/mocks/SafeTransferLibWrappers.huff" },
4447
{ access = "read", path = "./test/utils/mocks/ShufflingWrappers.huff" },
4548
{ access = "read", path = "./test/utils/mocks/SSTORE2Wrappers.huff" },
46-
{ access = "read", path = "./test/utils/mocks/TSOwnableWrappers.huff" }
49+
{ access = "read", path = "./test/utils/mocks/TSOwnableWrappers.huff" },
50+
{ access = "read", path = "./test/utils/mocks/ECDSAWrappers.huff" },
4751
]
4852
remappings = [
4953
"forge-std/=lib/forge-std/src/",

src/data-structures/Bytes.huff

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/// @title Bytes
2+
/// @notice SPDX-License-Identifier: MIT
3+
/// @author Franfran <https://github.com/iFrostizz>
4+
/// @notice Low-level operations on bytes
5+
/// @notice Adapted from BytesLib (https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol)
6+
7+
/// @notice Concatenate two bytes arrays
8+
/// @notice Takes in two pointers of the bytes to concatenate that must be sorted
9+
/// @return Pointer of the new appended concatenated bytes array in the memory
10+
/// @dev Warning! This assumes that the pointer in the memory of the second bytes chunk is after mem_ptr1 + 0x20
11+
#define macro CONCAT_MEMORY() = takes(2) returns(1) {
12+
// input stack // [mem_ptr1, mem_ptr2]
13+
14+
// setup stack and memory for the next iterations
15+
dup2 mload swap1 // [mem_ptr1, len2, mem_ptr2]
16+
msize swap1 // [mem_ptr1, free_loc_pos, len2, mem_ptr2]
17+
dup1 mload dup4 // [len2, len1, mem_ptr1, free_loc_pos, len2, mem_ptr2]
18+
dup2 add msize mstore // [len1, mem_ptr1, free_loc_pos, len2, mem_ptr2]
19+
20+
swap1 0x20 add // [index(i), len1, free_loc_pos, len2, mem_ptr2]
21+
msize // [index(j), index(i), len1, free_loc_pos, len2, mem_ptr2]
22+
swap2 0x00 // [is_sec_loop, len1, index(i), index(j), free_loc_pos, len2, mem_ptr2]
23+
24+
// i is the index where we get (mload) the array element and j is the index where we store (mstore) the array at j
25+
loop: // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
26+
dup2 iszero empty_slot jumpi // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
27+
28+
dup3 mload // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
29+
dup3 0x20 gt iszero // [is_full_slot, word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
30+
full_slot jumpi // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
31+
32+
// else it's not a full slot, we're hitting an end. Then clean memory slot and update j with a partial length
33+
dup3 0x20 sub // [pad_len, word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
34+
0x08 mul swap1 dup2 // [shift, word, shift, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
35+
shr // [left_padded_word, shift, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
36+
swap1 shl // [clean_word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
37+
dup5 mstore // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
38+
swap3 add swap2 // [is_sec_loop, index(i), index(j + 1), free_loc_pos, len2, mem_ptr2]
39+
40+
// here we check if current loop is for the 2nd array
41+
swap1 pop // [is_sec_loop, index(j + 1), free_loc_pos, len2, mem_ptr2]
42+
iszero bridge jumpi // [index(j + 1), free_loc_pos, len2, mem_ptr2]
43+
pop break jump // [free_loc_pos, len2, mem_ptr2]
44+
45+
empty_slot: // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
46+
swap2 pop pop // [is_sec_loop, index(j), free_loc_pos, len2, mem_ptr2]
47+
iszero bridge jumpi // [index(j), free_loc_pos, len2, mem_ptr2]
48+
pop break jump // [free_loc_pos, len2, mem_ptr2]
49+
50+
bridge: // [index(j), free_loc_pos, len2, mem_ptr2]
51+
dup4 0x20 add // [index(i), index(j), free_loc_pos, len2, mem_ptr2]
52+
dup5 // [len2, index(i), index(j), free_loc_pos, len2, mem_ptr2]
53+
0x01 // [is_sec_loop, len2, index(i), index(j), free_loc_pos, len2, mem_ptr2]
54+
loop jump
55+
56+
full_slot: // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
57+
dup5 mstore // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2]
58+
swap1 0x20 swap1 sub // [len_left - 0x20, is_sec_loop, index(i), index(j), free_loc_pos, len2, mem_ptr2]
59+
swap2 0x20 add // [index(i + 1), is_sec_loop, len_left - 0x20, index(j), free_loc_pos, len2, mem_ptr2]
60+
swap3 0x20 add // [index(j + 1), is_sec_loop, len_left - 0x20, index(i + 1), free_loc_pos, len2, mem_ptr2]
61+
swap3 swap2 swap1 // [is_sec_loop, len_left - 0x20, index(i + 1), index(j + 1), free_loc_pos, len2, mem_ptr2]
62+
loop jump
63+
64+
break: // [free_loc_pos, len2, mem_ptr2]
65+
swap2 pop pop // [free_loc_pos]
66+
}
67+
68+
/// @param Pointer in memory of the start of the bytes array
69+
/// @param Start position of the slice relative to the array
70+
/// @param Length of the output slice
71+
/// @return Pointer of the new appended concatenated bytes array in the memory
72+
/// @dev Warning! This assumes that the length of the output slice is less or equal the length of the bytes array (bytes.len < slice.len)
73+
/// @dev Warning! This assumes that the start of the bytes array is not out of bounds (start < len + mem_ptr)
74+
#define macro SLICE_MEMORY() = takes(3) returns(1) {
75+
// input stack // [mem_ptr, start, length]
76+
77+
msize dup4 msize mstore // [free_loc_pos, mem_ptr, start, length]
78+
msize swap4 // [length, free_loc_pos, mem_ptr, start, index(j)]
79+
// index(i) = mem_ptr + start + 0x20
80+
swap1 swap3 // [start, length, mem_ptr, free_loc_pos, index(j)]
81+
swap1 swap2 // [mem_ptr, start, length, free_loc_pos, index(j)]
82+
0x20 add add // [index(i), length, free_loc_pos, index(j)]
83+
84+
// we load our slice chunk at i and store it in a free memory location at j
85+
loop: // [index(i), length_left, free_loc_pos, index(j)]
86+
dup1 mload // [slice_chunk, index(i), length_left, free_loc_pos, index(j)]
87+
88+
// if current is not full slot, then load the last bytes and break
89+
0x20 dup4 lt // [is_not_full_slot, slice_chunk, index(i), length_left, free_loc_pos, index(j)]
90+
break jumpi // [slice_chunk, index(i), length_left, free_loc_pos, index(j)]
91+
92+
dup5 mstore // [index(i), length_left, free_loc_pos, index(j)]
93+
94+
0x20 add swap3 // [free_loc, length_left, free_loc_pos, index(i+1)]
95+
0x20 add swap3 // [index(i+1), length_left, free_loc_pos, free_loc + 1]
96+
swap1 0x20 // [0x20, length_left, index(i+1), free_loc_pos, free_loc + 1]
97+
swap1 sub // [length_left - 1, index(i+1), free_loc_pos, free_loc + 1]
98+
swap1 // [index(i+1), length_left - 1, free_loc_pos, free_loc + 1]
99+
100+
loop jump
101+
102+
break: // [slice_chunk, index(i), length, free_loc_pos, index(j)]
103+
// store the remaining length
104+
dup3 0x20 sub // [zero_length, slice_chunk, index(i), length, free_loc_pos, index(j)]
105+
0x08 mul swap1 dup2 // [shift, slice_chunk, shift, index(i), length, free_loc_pos, index(j)]
106+
shr // [left_pad_slice, shift, index(i), length, free_loc_pos, index(j)]
107+
swap1 shl // [slice_chunk, index(i), length, free_loc_pos, index(j)]
108+
dup5 mstore // [index(i), length, free_loc_pos, index(j)]
109+
110+
pop pop swap1 pop // [free_loc_pos]
111+
}

src/data-structures/Hashmap.huff

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,49 @@
6262
sha3 // [key]
6363
}
6464

65+
/// @notice Calculate the slot from three keys
66+
#define macro GET_SLOT_FROM_KEYS_3D(mem_ptr) = takes(4) returns (1) {
67+
// Input stack: [slot, key1, key2, key3]
68+
// Load the data into memory
69+
<mem_ptr> // [<mem_ptr>, slot, key1, key2, key3]
70+
mstore // [key1, key2, key3]
71+
72+
// next byte
73+
<mem_ptr> 0x20 add // [<mem_ptr> + 32, key1, key2, key3]
74+
swap1 dup2 // [<mem_ptr> + 32, key1, <mem_ptr> + 32, key2, key3]
75+
mstore // [<mem_ptr> + 32, key2, key3]
76+
77+
0x40 // [0x40, <mem_ptr> + 32, key2, key3]
78+
<mem_ptr> // [<mem_ptr>, 0x40, <mem_ptr> + 32, key2, key3]
79+
sha3 // [slot1, <mem_ptr> + 32, key2, key3]
80+
81+
// concat the first two keys
82+
dup2 // [<mem_ptr> + 32, slot1, <mem_ptr> + 32, key2, key3] put slot1 in memory
83+
mstore // [<mem_ptr> + 32, key2, key3]
84+
85+
// put key2 in memory, before slot1
86+
swap1 // [key2, <mem_ptr> + 32, key3]
87+
<mem_ptr> // [<mem_ptr>, key2, <mem_ptr> + 32, key3]
88+
mstore // [key3]
89+
90+
0x40 // [0x40, <mem_ptr> + 32, key3]
91+
<mem_ptr> // [<mem_ptr>, 0x40, <mem_ptr> + 32, key3]
92+
sha3 // [slot2, <mem_ptr> + 32, key3]
93+
94+
// concat with the third key
95+
swap1 // [<mem_ptr> + 32, slot2, key3] put slot2 in memory
96+
mstore // [key3]
97+
98+
// put key3 in memory, before slot2
99+
<mem_ptr> // [<mem_ptr>, key3]
100+
mstore // []
101+
102+
// Hash the data, generating the final slot3
103+
0x40 // [0x40]
104+
<mem_ptr> // [<mem_ptr>, 0x40]
105+
sha3 // [slot3]
106+
}
107+
65108
/// @notice Load an element onto the stack from a key
66109
#define macro LOAD_ELEMENT(mem_ptr) = takes(1) returns(1) {
67110
// Input stack: [key]
@@ -83,6 +126,13 @@
83126
sload // [value]
84127
}
85128

129+
/// @notice Load an element onto the stack from a slot and three keys
130+
#define macro LOAD_ELEMENT_FROM_KEYS_3D(mem_ptr) = takes(4) returns(1) {
131+
// Input stack: [slot, key1, key2, key3]
132+
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) // [slot]
133+
sload // [value]
134+
}
135+
86136
/// @notice Store an element from a key
87137
#define macro STORE_ELEMENT(mem_ptr) = takes(2) returns(0) {
88138
// Input stack: [key, value]
@@ -103,3 +153,10 @@
103153
GET_SLOT_FROM_KEYS_2D(<mem_ptr>) // [slot, value]
104154
sstore // []
105155
}
156+
157+
/// @notice Store an element from a slot and three keys
158+
#define macro STORE_ELEMENT_FROM_KEYS_3D(mem_ptr) = takes(5) returns (0) {
159+
// Input stack: [slot, key1, key2, key3, value]
160+
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) // [slot, value]
161+
sstore // []
162+
}

src/math/SafeMath.huff

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@
6565
/// @notice Divides two numbers and reverts on division by zero
6666
#define macro SAFE_DIV() = takes (2) returns (1) {
6767
// input stack // [num1, num2]
68-
dup2 // [num2, num1, num2]
69-
0x00 swap1 // [num2, 0, num1, num2]
68+
0x00 dup3 // [num2, 0, num1, num2]
7069
gt // [is_not_div_zero, num1, num2]
7170
is_not_div_zero jumpi
7271
[DIVIDE_BY_ZERO] PANIC()
@@ -77,8 +76,7 @@
7776
/// @notice Divides two numbers and reverts on division by zero or modulo zero
7877
#define macro SAFE_MOD() = takes (2) returns (1) {
7978
// input stack // [num1, num2]
80-
dup2 // [num2, num1, num2]
81-
0x00 swap1 // [num2, 0, num1, num2]
79+
0x00 dup3 // [num2, 0, num1, num2]
8280
gt // [is_not_mod_zero, num1, num2]
8381
is_not_mod_zero jumpi
8482
[ARITHMETIC_OVERFLOW] PANIC()

src/tokens/ERC20.huff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@
570570
dup1 __FUNC_SIG(approve) eq approveJump jumpi
571571

572572
// Bubble up to the parent macro
573-
no_match jumpi
573+
no_match jump
574574

575575
allowanceJump:
576576
ALLOWANCE()
@@ -598,4 +598,4 @@
598598
TRANSFER()
599599

600600
no_match:
601-
}
601+
}

0 commit comments

Comments
 (0)