An NFT with Huff, using polynomials over a finite field of the largest 20-byte prime, instead of mappings.
huffd1
is a non-fungible token implementation in Huff, where the ownership of a token is given by the evaluation of a polynomial
Notice the final hexadecimals, which is where the name of the project comes from. The degree of the polynomial is equal to total supply minus one, so for
At contract deployment, all tokens are owned by the contract owner; and as a result the main polynomial is simply a constant polynomial equal to the owner address
To transfer a token, we simply have to find the difference between the sender and recipient addresses, multiply that value with the basis polynomial of the respective token, and add that to the main polynomial. See transfer
subsection below for more details.
The stack comments are written in reverse order:
opcode // [top-N, ..., top-1, top] pop // [top-N, ..., top-1] 0x01 // [top-N, ..., top-1, 0x01]unlike the usual order described in the Style Guide.
Let's describe each function of huffd1
:
To find the owner of a token
Initially, all tokens are owned by the contract deployer, which can be represented by the constant polynomial that is equal to the owner address.
To find the balance of an address, iterate over all tokens and call ownerOf
, counting the number of matching addresses along the way.
To transfer a token
We have a Sage script that can export the basis polynomials, one for each token id, as a codetable where the coefficients of each polynomial are concatenated.
// basis polynomials coefficients
#define table Basis {
// 0x...
}
// number of tokens
#define constant TOTAL_SUPPLY = 0xa
// number of bytes per coefficient
#define constant COEFF_SIZE = 0x14
// order of the finite field
#define constant ORDER = 0xffffffffffffffffffffffffffffffffffffffd1
Using these, we can load polynomials from the code table.
The codetable grows pretty large for 20-byte coefficient size, and may easily get past the 24KB maximum contract size. To be more specific, for coefficient size
S
and total supplyN
, you will haveN
polynomials withN
coefficients each, withS
bytes per coefficient, resulting in total number of bytes ofN * N * S
.
This operation results in multiplying the coefficients of MULMOD
, and afterwards summation of the coefficients of ADDMOD
.
Also note that
Returns the string "Huffd1"
.
Returns the string "FFD1"
.
You can use the following commands for testing.
# run all tests
forge t -vvv
# run a specific test
forge t -vvv --mc Polynomial
forge t -vvv --mc Huffd1
I use -vvv
to see reverts in detail.
-
This project was done for the Huff hackathon!
-
This is a very inefficient contract both in contract size and gas usage, and was done mostly for the nerd-snipability factors of using polynomials instead of mappings.
-
We can implement approvals with another polynomial too, but time did not permit. Also, there are many optimizations to do in many different places within the code.
-
We could also use
$p = 2^{160} + 7$ , but I wanted all coefficients to be strictly 160-bits, which is not the case with that prime. In fact, the concept works for any prime order, but we would like to use an order that can fit almost all the addresses while being as large as an address. -
Maybe use foundry FFI to generate the basis polynomials during contract creation?