This repository contains the contracts Textile uses to settle swap fills: a
pinned UniswapX LimitOrderReactor, Textile's native output-fee controller, and
the operator vault a filler quotes from. It is meant for integrators,
stakeholders, and auditors who need the on-chain swap surface without unrelated
application code.
The contract set is intentionally small:
contracts/v3/filler/SellFirstFeeController.sol- Textile's output-token fee controller for sell-first swaps.contracts/v3/filler/PreferredFillerValidation.sol- the reactor validation callback a maker uses to steer an order at specific fillers.contracts/v3/filler/vault/**-OperatorVault, its factory, the order executor, the Aave v3 yield adapter, and the ERC-1271 policy that lets a vault sign reactor orders.contracts/v3/filler/vendor/uniswapx/reactors/LimitOrderReactor.sol- the pinned UniswapX limit-order reactor Textile deploys.contracts/v3/filler/vendor/**- the exact Permit2 interfaces, solmate files, and UniswapX libraries needed by the reactor.contracts/v3/mocks/andcontracts/mocks/ERC20Mock.sol- test doubles. Never deployed.test/v3/FillerReactor/- Hardhat tests for deployment, fee wiring, Permit2 signatures, and the reactor fill path.test/v3/OperatorVault/- Hardhat tests for the vault: share math, epochs, fees, yield, signature policy, and regressions for the audit findings.test/foundry/v3/security/SellFirstFeeControllerAuditFindings.t.sol- Foundry regression tests for the fee-controller audit findings.constants/src/- the TypeScript order, share-math, and NAV-attestation modules the tests check the Solidity against.addresses/- deployment snapshots for the Textile swap contracts.
Textile sell-first swaps use UniswapX fixed-price limit orders. A maker signs a
Permit2-backed order that targets Textile's LimitOrderReactor. The taker fills
that order through the reactor, and the reactor calls SellFirstFeeController
through UniswapX's native ProtocolFees hook to append Textile's output-token
fee.
The fee controller has no mutable fee state after deployment. Each deployment sets an immutable fee recipient and fee bps. The controller aggregates duplicate output tokens before fee calculation and reverts dust outputs whose floored fee would be zero.
OperatorVault is an immutable two-asset maker vault. Depositors hold ERC-20
shares; deposits and redemptions settle in aggregate epochs. The operator
never custodies deposits: it can only sign LimitOrders the vault's ERC-1271
policy accepts, so Permit2 cannot move vault tokens without a vault-validated
envelope. Idle balances earn through the Aave v3 yield adapter.
The share math and the NAV attestation the policy checks are mirrored in
constants/src/, and the Hardhat tests assert the TypeScript and the Solidity
agree.
Textile uses a narrow, pinned UniswapX closure for the swap reactor.
| Area | Official UniswapX | Textile swap contracts |
|---|---|---|
| Source pin | UniswapX v2.1.0 at df1dbfe2439c3c648ab5e3089953780ab7fc40b7 |
Same pinned source for the vendored reactor closure |
| Reactor used | Uniswap ships Dutch, Exclusive Dutch, V2/V3 Dutch, Priority, and Limit reactors | Textile uses only the LimitOrderReactor closure for fixed-price swap fills |
| Solidity logic changes | Canonical UniswapX source | No Solidity logic changes inside vendored UniswapX, Permit2 interface, or solmate files |
| Import changes | Foundry remappings in the upstream repo | Import prefixes are rewritten to relative paths plus @openzeppelin/contracts so Hardhat and Foundry compile in this package |
| OpenZeppelin | Upstream v2.1.0 imports openzeppelin-contracts/... |
Resolved to this package's pinned @openzeppelin/contracts@5.4.0; the APIs used are unchanged |
| Textile-owned logic | None | SellFirstFeeController adds Textile's output-token fee through UniswapX's ProtocolFees hook |
| Deployment ownership | Uniswap deploys and owns its reactors | Textile deploys and owns the reactor and fee controller addresses listed below |
Pinned third-party source details are in
contracts/v3/filler/vendor/VENDORED.md.
Fee bps is the deployed SellFirstFeeController's immutable FEE_BPS, charged
on the swap output. A chain with no PreferredFillerValidation cannot steer
orders at specific fillers.
These are the public UniswapX deployments on the same chains, for comparison.
Textile does not reuse them; it fills through its own LimitOrderReactor
instances shown above. Permit2 is left out here because it is the canonical
address on every chain, already in the table above.
Source: UniswapX deployment table.
The upstream reports below are linked at the same v2.1.0 source tag used for
the vendored Textile closure:
Permit2 is not vendored as runtime code here. Textile uses the canonical Permit2
deployment address through UniswapX's IPermit2 interface.
corepack enable
yarn install --immutable
yarn test:v3
yarn test:foundry:v3yarn test:v3 runs the Hardhat suite for the reactor, the fee controller, and
the operator vault. yarn test:foundry:v3 runs the fee-controller audit
regression tests and needs Foundry.
Textile-owned smart contracts are licensed under the Business Source License
1.1. See licenses/BUSL_LICENSE. After the applicable
Change Date, the contracts become available under MIT as described in the
license terms.