Engineering Swapper

In our journey to give people full custody and control over their earnings, we regularly work with all kinds of creators, collectives, DAOs, and businesses operating onchain. Recently, we noticed complaints floating around the ecosystem about how to best programmatically swap onchain revenue into different tokens. While Uniswap has simplified the process of doing this manually, we began to wonder how we might be able to help our partners do this programmatically as part of the payment flow itself.

When it comes to designing smart contracts, we follow the Unix philosophy: develop minimalist, modular software and compose it together to handle more complex tasks. We quickly realized from user conversations that we needed a new building block—Swapper—that could trustlessly swap any token it received into a predefined output token. It could be used on its own if you only wanted to receive a single token (such as ETH or USDC), or it could be composed beneath a Split—creating a Diversifier—to handle more nuanced payment flows.

Before we get into the design decisions we made, check out Introducing Swapper for a less technical overview.

Design Decisions

How should it swap?

First, we decided each Swapper should trade directly with third parties, known as "traders." By swapping directly with traders, Swapper can theoretically handle any token on any chain. It also significantly increases the breadth and depth of liquidity that traders can tap into to process funds on behalf of the Swapper's beneficiary.

Swapper trades directly with third-party traders
Swapper trades directly with third-party traders

In this direct exchange, we follow a flash pattern that allows traders to actually use the Swappers’ funds to generate the desired output token, rather than having to supply their own liquidity. This means traders can take the tokens from the Swapper, execute bespoke exchange logic across any onchain entities, and then return the Swapper’s desired token in its expected amount. The biggest downside to the flash pattern is that traders must now use intermediating smart contracts to handle the callback logic. However, we generally assume traders will be sophisticated entities and the inconvenience here is minor.

Not every token has liquidity onchain though. And many protocols that produce user revenue onchain expect msg.sender to be capable of a wide variety of actions including receiving NFTs or pulling funds. In the mutable case when the Swapper has an owner, our security model already assumes the owner must be trusted (because they can update the beneficiary). Given that, we decided to grant owners full execution access from the Swapper. This means the owner can at any time pause the Swapper and/or take any funds out and do as they please with them. Without altering the trust model, this creates two additional advantages: 1) owners can use this module in virtually any onchain situation (as described above), and 2) any tokens that traders can’t reasonably swap can be handled specially by the owner.

How should it price swaps?

We spent a lot of time internally debating the merits of the two mainnet pricing oracle giants, Chainlink and Uniswap, before settling into our usual modular pattern: why not both? By defining a sufficiently generic oracle interface, we preserved Swapper’s theoretical ability to handle any token on any chain, now or in the future. An implementation based on Uniswap v3’s TWAP is available via our UI, and we expect to have a version supporting Chainlink out soon.

Swapper features a modular oracle to determine prices
Swapper features a modular oracle to determine prices

Swappers must trust their Oracles. We have deployed a reasonably broad default immutable Oracle that is available in our UI today. It is our hope that at some point in the future a consortium of onchain entities can govern a more comprehensive & up-to-date Oracle via a multisig or DAO.

Swappers may add a discount or premium to the oracle price. This functionality allows owners to make their own trade-offs between the frequency and efficiency of payouts. If an Oracle doesn’t handle a token pair the Swapper needs, it is expected to revert to allow the Swapper’s owner to handle the tokens manually. Handling the long tail of tokens securely and efficiently is an important topic for future exploration.

How should it be governed?

Different situations call for different trust models. As with Splits v1, we wanted to respect the entire spectrum of governance situations that may exist. Some Swappers may need to be immutable. Others may need to be governed by a DAO, multisig, or even an EOA depending on where they’re used and the expected beneficiary.

Swapper can be owned by any smart contract or EOA
Swapper can be owned by any smart contract or EOA

In a departure from our Splits v1 architecture, we decided to lean more into onchain ownership with Swapper. The trust model of a mutable Split is entirely dependent on its controller. Knowing this is the same with Swapper and its owner, we gave the owner additional flexibility including executing arbitrary calls. Splits v1 has run into a number of situations that would greatly benefit from the controller having similar flexibility, and since it doesn’t alter the trust model, we decided to learn from it and lean into trust here when desired.

The original design for Diversifier—a Split pointing to one or more Swappers—had to be augmented slightly based on the Split’s limited ability to act as a wallet and receive NFTs. We designed another building block—Pass-Through Wallet—which permissionlessly forwards funds while giving the owner the ability to execute arbitrary calls, receive 721s and 1155s, and pause the forwarding. This Pass-Through Wallet then becomes the owner/controller of all the downstream modules, thus representing a cohesive onchain entity.

Areas for future improvement

The contracts are now audited and live. Here are several areas we’d like to further explore and improve:

  • Develop a Chainlink Oracle implementation

  • Improve the Uniswap v3 TWAP implementation via

    • Allowing the trader to supply the older TWAP observation index in the trade calldata blob (can be cheaply verified at runtime & removes the need for an onchain binary search)

    • Removing pool whitelist in favor of a default fee tier relying on some other onchain data for protection against illiquid pool manipulation

    • Allowing the Oracle to get prices by chaining together multiple pools (some of these benefits are currently available by chaining Swappers)

  • Develop an Oracle-less Swapper by obtaining pricing via some other competitive onchain system (e.g. dutch or sealed-bid auction)

If this sounds interesting and like something you’d like to build, we’d love to hear from you – we’re hiring engineers so please reach out.

Subscribe to 0xSplits
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
This entry has been permanently stored onchain and signed by its creator.
More from 0xSplits