TJAR Overview
Learn how TJAR accumulates protocol fees in any asset and converts them into a single chosen token through permissionless releases.
TJAR is a pair of immutable contracts that turn fees collected in many assets into a single token. A Token Jar accumulates whatever its fee sources send it. A Releaser releases the jar's contents to any searcher who sends a fixed amount of a token the jar's operator chooses.
Any protocol that collects fees in mixed assets can use TJAR without running its own trading operations. The jar does not care where assets come from, and the releaser does not care what the chosen token is or what happens to it afterward.
Uniswap uses the same primitive for its own protocol fees, where the chosen token is UNI and the destination is a burn address. That deployment is documented separately in Uniswap Protocol Fees.
Flow of Assets
in: fee sources ──▶ Token Jar
any ERC-20 or native asset, from any number of sources
out: a searcher calls release() on the Releaser
sends threshold ──▶ resource recipient
sends protocol fee ──▶ fee recipient (only if one is set)
takes the named assets ──▶ a recipient the searcher choosesThe jar holds assets and never prices them. The releaser sets one price for the whole jar and lets anyone take the trade.
Components
| Contract | Role |
|---|---|
| Token Jar | Collects fees. Authorizes exactly one releaser at a time and has no other way out. |
| Releaser | Releases the jar's assets to a searcher who sends a fixed threshold of a chosen resource token. |
| Fee Controller | Chain-wide singleton for an optional protocol fee. No fee is set today. Owned by the Uniswap DAO governance. |
| Factory | Deploys a jar and releaser together, hands both to the operator, and retains no authority. |
How a Release Works
- Fee sources send assets to the jar. Nothing is emitted and nothing is recorded: the jar's holdings are exactly its balances.
- A searcher decides which of those assets are worth taking and calls
release(nonce, assets, recipient, maxFee)on the releaser. - The releaser pulls
thresholdof theresourcetoken from the searcher to the resource recipient, plus the protocol fee to the fee recipient if one is set. - The jar transfers its full balance of every named asset to the recipient the searcher chose.
A release is profitable when
value(the assets named) > threshold + gasNo oracle sets the price. The threshold moves only when the operator changes it, and a protocol fee, if one is ever set, is read from the Fee Controller at execution time.
The release threshold is static:
- A jar gets more profitable the longer nobody sweeps it. The price does not rise with the contents. The first searcher who finds a jar profitable clears it, so searchers compete on the smallest margin they will accept.
- A searcher sends the full
thresholdregardless of how many assets they name. Naming fewer assets does not make a release cheaper. It only leaves value behind for whoever comes next.
Who Can Change What
| Party | Can change | Cannot change |
|---|---|---|
| Jar owner | Which releaser the jar authorizes, through setReleaser. Can transfer ownership of the jar. | Any deployed contract's code |
| Releaser owner | The release terms through setConfig, and who holds the configSetter role through setConfigSetter. Can transfer ownership of the releaser. | Which releaser the jar authorizes |
configSetter, if appointed | The release terms only | Who holds the configSetter role, ownership of the releaser, and which releaser the jar authorizes |
| Uniswap governance | The protocol fee and its recipient. Can transfer ownership of the Fee Controller. | Anything about an operator's jar or releaser |
The Factory hands ownership of both contracts to the same address at deployment, so the first two rows start as one party. Every ownership transfer is single-step, so a transfer to the wrong address is final.
Release terms are resource, resourceRecipient, threshold, and maxReleaseLength, and they change only together through a single setConfig call. Every such call advances a nonce, so a release priced against terms that have since moved reverts rather than executing.
An operator can also give up control permanently. Deploying with the owner set to 0xdead and no configSetter freezes the pair completely: the terms, the recipients, and the wiring between jar and releaser can never change again.
Security
TJAR has been reviewed by OpenZeppelin, as a diff against the upstream Uniswap protocol fee contracts it derives from. That upstream code carries three further OpenZeppelin reviews and one by Cantina. All reports are listed in Resources.
Deployments
The Factory and the Fee Controller sit at the same address on every chain, and an operator deploying from the same account with the same salt gets the same jar address on every chain. Key everything by chain id and address, never by address alone. See Deployments.
Where to Go Next
- Deploy a Token Jar: collect fees in any asset and convert them into one token.
- Search Token Jars: find jars, view their parameters, and release them.