Skip to main content
An adaptor lets a user reach your protocol from a shielded balance. Your protocol does not change. You write one small contract that turns tokens in into tokens out, and the pool owner registers it. Read Adaptors first for the idea. This page is the how-to.

What an adaptor must do

The interface:
AdaptorBase gives you the pool lookups. getAsset(assetId) and getAssetId(address) revert if the asset is not active, so you do not need to check that yourself. It also defines the errors ZeroValue, UnsupportedAsset and InvalidInputAssetLength.

Write the adaptor

This example deposits into an ERC-4626 vault and redeems from it. It compiles against the protocol repo. It has not been run against a pool, so treat it as a starting point and not as an audited adaptor.
Points that matter:
  • Slippage. Put a minimum output in the payload and revert below it, as minOut does here. The Uniswap adaptor does the same. Nobody else will protect the user.
  • Recipient. Return outputs to address(this), the handler, so the pool turns them into notes for the user. The Uniswap adaptor also accepts a beneficiary address. If it is set, the swap pays that public address and the adaptor returns no outputs. That sends the result outside the pool and shows the destination.
  • Payload. Keep it small and make it decode in one abi.decode. The payload is public.
  • One input asset is the common case. The handler can pass several, but each extra input asset needs its own check in your code.
  • Anything that cannot finish in one call leaks. The Lido unstake path asks for a withdrawAddress because Lido queues the withdrawal and pays later. Say so in your adaptor’s notes.

Register it

Only the pool owner can register an adaptor, with addAdaptorSupport. The pool checks the adaptor address named in the transaction against that list and rejects any other. Output tokens must already be active assets in the pool, and the owner adds them with the pool’s asset functions. How to ask for both: [ADAPTOR_ONBOARDING_PROCESS]. Adaptors are stateless, so registering a new one does not need an upgrade of the pool.

Call it from the SDK

An adaptor call is a CALL_ADAPTER transaction. to is the adaptor address. assetIds and values are the tokens you send in. payload is what your handleAssets decodes.
You sign, prove and submit it as in the quickstart. The SDK sets the refund address for you. It is a blinded form of the user’s own address, so the output notes belong to the user and nobody can link them to the user’s public address. Two things to plan for:
  • Gas. An adaptor call runs your code and the protocol’s code in one transaction. On the bundler path, pass a larger callGasLimit than a plain transfer needs. A vault deposit needs more than a swap. The app sets this per adaptor.
  • Public data. The input tokens, the amounts, the adaptor and the payload are public. The output amounts are public too, because the pool builds the output notes from what the adaptor returns.

Test it

The protocol repo tests each adaptor against a fork of the network it targets. A test deploys the adaptor at the fixed address that a recorded proof names, sends that shielded transaction through a pool fixture and checks the result. The proof binds the adaptor address, so a new adaptor needs a new recorded proof. Add a test in the same style under test/adaptors/.

Next