Ternary funding and joint tokens: a slightly deeper dive
Ternary funding is a mechanism for not-for-profit causes that relies on a subset of validators to attest to an outcome. A joint token is a procedurally generated NFT with a phenotype derived from the addresses of validators.
A basic familiarity with the paper is recommended before jumping into this post.
A recap
Ternary funding:
Stablecoins are locked until the delivery of the public good
A random subset of validators attest to its delivery, and either release or reverse the funds
Validators that attest outside of the ¾ majority face slashing
Other expenditures include the opportunity cost represented by the risk-free rate of return \((R_{f})\) and gas fees
By design, there is no cryptoeconomic incentive for validators participate
Joint tokens:
This is a novel type of NFT that's used in lieu of stablecoins
It's locked in the same way, and either released to the party that delivered the public good or burned
It's procedurally generated by mapping each validator address to an entry, and storing this data structure within the token itself
The data structure needs to be parsed off-chain, which in turn allows for composability
It derives its value from not only validators attesting to the delivery of the public good, but the mapping of the same validators within the token itself, time taken to attest and risk of slashing
Ultimately, the transferred token represents delivery of the public good, endorsement by a set of validators and a series of expenditures
As most participants receive their stake back, it's often more efficient than a pool of stablecoins
It can be sold by the beneficiary on any ERC-721 marketplace
Other aspects:
A keccak256 hash function determines the mapping within the token, taking the validator address and block timestamp as parameters.
The efficiency of a joint token over a donation pool can be shown as \(S - f(O,R,F)\), where \(S\) is the sale price, \(O\) is the opportunity cost represented by the risk-free rate \((R_{f})\), \(R\) is realised rate of slashing, and \(F\) are fees involved in interacting with the staking contract. It can also be expressed as a factor \(\frac{S}{f(O,R,F)}\).
The consensus can be shown as \(\sum_{i=1}^{v}attestation_i\), where \(v\) is the total amount of validators chosen to attest, and \(attestation ∈ \{0,1\}\).
Each validator chosen to attest submits a zk-SNARK of a key, together with their attestation \((0,1)\). A zk-SNARK of the key is needed to ensure only validators that are chosen to attest can call the necessary function.
Parsing the token
For simplicity, let's map every address to the same value.
Even our seemingly blank canvas has more to it than at first glance. Hovering over each entry reveals each contributor's address or ENS name (the latter is possible when reverse resolution is enabled). The number of times the contributor has participated in previous rounds is also displayed. This is only one way to parse the data structure, and its inherent composability allows it to be parsed in different ways.
Skipping the colour examples in Section 2.4.4 of the paper, let's move on to a more complex mapping.
As the hexadecimal representation of every Unicode character can be stored cheaply in bytes2s (e.g. ╟ → 0x255F), we can map uncommon character sets (such as CP437) to each validator address.
For practical purposes, only four mappings are shown.
Hovering over each quadrant displays each validator's details.
At this point, we’re still using relatively little gas by mapping each validator to 256 Unicode characters. In the next example we push closer to the limit of what can be realistically stored on layer 1 at relatively low cost.
By modifying the pseudorandom function in Section 2.4.4 of the paper, we can provide the values for each rect in our next example, while preserving the use of the validator address as part of the seed. This also produces something more cohesive than an assortment of Unicode characters.
Storing XML on-chain would be incredibly inefficient (even as bytecode), so a better solution is to only store an array of values corresponding to each variable, and then map it on the client side to a rect template.
It's important to note that most of the above variables correspond to floats, which Solidity obviously doesn't support, therefore need to be stored on-chain as integers together with the appropriate factor. It's also worth pointing out that unlike in previous examples, rand() is called multiple times for the same validator, hence the addition of _shape to ensure that rects associated with a single validator are not homogeneous.
Once the array of rects corresponding to each validator is populated off-chain, the final SVG can be constructed.
Similar to the previous example, each quadrant displays each validator's details, however we take advantage of the greater complexity by assigning additional traits off-chain such as luminance and variance.
Implications
The locking mechanism of ternary funding not only allows for criteria to be met before release, but for the implicit mapping of endorsements and expenditures within a single non-fungible token. Use of the latter also often leads to greater funding efficiency as the majority of validators are expected to receive their stake back at the end of a round.
A by-product of the trustlessness introduced by attestation is the enabling of anonymous development — unlike a regular grant, ternary funding is contingent on delivery, encouraging more radical projects.