Encoding
The first step to execute a trade on chain is encoding.
Our Rust crate offers functionality to convert your trades into calldata, which the Tycho contracts can execute.
See this Quickstart section for an example of how to encode your trade.
Models
These are the models used as input and output of the encoding crate.
Solution Struct
The Solution
struct specifies the details of your order and how it should be filled. This is the input of the encoding module.
The Solution
struct consists of the following attributes:
given_token
Bytes
The token being sold (exact in) or bought (exact out)
given_amount
BigUint
Amount of the given token
checked_token
Bytes
The token being bought. This token's final balance will be checked by the router using checked_amount
.
sender
Bytes
Address of the sender of the given token
receiver
Bytes
Address of the receiver of the checked token
exact_out
bool
False if the solution is an exact input solution (i.e. solves a sell order). Currently only exact input solutions are supported.
router_address
Bytes
swaps
Vec<Swap>
List of swaps to fulfil the solution.
checked_amount
BigUint
Minimum amount out to be checked for the solution to be valid if passing through the TychoRouter
.
native_action
Option<NativeAction>
Wrapping and Unwrapping
Our router accepts wrapping native tokens to wrapped token before performing the first swap, and unwrapping wrapped tokens to native tokens after the final swap, before sending the funds to the receiver.
In order to perform this, the native_action
parameter of the solution must be set to either Some(NativeAction.WRAP)
or Some(NativeAction.UNWRAP)
.
When wrapping:
The
given_token
of the solution should be ETHThe
token_in
of the first swap should be WETH
When unwrapping:
The
checked_token
of the solution should be ETHThe
token_out
of the final swap should be WETH
Swap Struct
A solution consists of one or more swaps. A swap represents a swap operation to be performed on a pool.
The Swap
struct has the following attributes:
component
ProtocolComponent
Protocol component from Tycho core
token_in
Bytes
Token you provide to the pool
token_out
Bytes
Token you expect from the pool
split
f64
Percentage of the amount in to be swapped in this operation (for example, 0.5 means 50%)
To create a Swap
, use the new
function where you can pass any struct that implements Into<ProtocolComponent>
.
Split Swaps
Solutions can have splits where one or more token hops are split between two or more pools. This means that the output of one swap can be split into several parts, each used as the input for subsequent swaps. The following are examples of different split configurations:
By combining splits creatively, you can build highly customized and complex trade paths.
We perform internal validation on split swaps. A split swap is considered valid if:
The checked token is reachable from the given token through the swap path
There are no tokens that are unconnected
Each split amount is small than 1 (100%) and larger or equal to 0 (0%)
For each set of splits, set the split for the last swap to 0. This tells the router to send all tokens not assigned to the previous splits in the set (i.e., the remainder) to this pool.
The sum of all non-remainder splits for each token is smaller than 1 (100%)
Swap Group
Certain protocols, such as Uniswap V4, allow you to save token transfers between consecutive swaps thanks to their flash accounting. In case your solution contains sequential (non-split) swaps of such protocols, our encoders compress these consecutive swaps into a single swap group, meaning that a single call to our executor is sufficient for performing these multiple swaps.
In the example above, the encoder will compress three consecutive swaps into the following swap group to call the Executor:
SwapGroup {
input_token: weth_address,
output_token: dai_address,
protocol_system: "uniswap_v4",
swaps: vec![weth_wbtc_swap, wbtc_usdc_swap, usdc_dai_swap],
split: 0,
}
One solution will contain multiple swap groups if different protocols are used.
Encoded Solution struct
The output of encoding is EncodedSolution
. It has the following attributes.
swaps
Vec<u8>
The encoded calldata for the swaps.
interacting_with
Bytes
The address of the contract to be called (it can be the Tycho Router or an Executor)
selector
String
The selector of the function to be called.
n_tokens
usize
The number of tokens in the trade.
permit
Option<PermitSingle>
Optional permit object for the trade (if permit2 is enabled).
Encoders
Tycho Execution provides two main encoder types:
TychoRouterEncoder: This encoder prepares calldata for execution via the Tycho Router contract. It supports complex swap strategies, including multi-hop and split swaps. Use this when you want Tycho to handle routing and execution within its own router contract.
TychoExecutorEncoder: This encoder prepares calldata for direct execution of individual swaps using the Executor contracts, bypassing the router entirely. It encodes one swap at a time and is ideal when integrating Tycho Executors into your own router contract. See more details here.
Choose the encoder that aligns with how you plan to route and execute trades.
Builder
For each encoder, there is a corresponding builder:
TychoRouterEncoderBuilder
TychoExecutorEncoderBuilder
Both builders require the target chain to be set.
Builder Example Usage
let encoder = TychoRouterEncoderBuilder::new()
.chain(Chain::Ethereum)
.user_transfer_type(UserTransferType::TransferFromPermit2)
.build()
.expect("Failed to build encoder");
Encode
You can convert solutions into calldata using:
let encoded_solutions = encoder.encode_solutions(solutions);
This method returns a Vec<
EncodedSolution
>
, which contains only the encoded swaps of the solutions. It does not build the full calldata. You must encode the full method call yourself. If you are using Permit2 for token transfers, you need to sign the permit object as well.
The full method call includes the following parameters, which act as execution guardrails:
amountIn
andtokenIn
– the amount and token to be transferred into the TychoRouter/Executor from youminAmountOut
andtokenOut
– the minimum amount you want to receive of token out. For maximum security, this min amount should be determined from a third party source.receiver
– who receives the final outputwrap/unwrap
flags – if native token wrapping is neededisTransferFromAllowed
– if this should perform atransferFrom
to retrieve the input funds. This will be false if you send tokens to the router in the same transaction before the swap.
These execution guardrails protect against exploits such as MEV. Correctly setting these guardrails yourself gives you full control over your swap security and ensures that the transaction cannot be exploited in any way.
Refer to the quickstart code for an example of how to convert an EncodedSolution
into full calldata. You must tailor this example to your use case to ensure that arguments are safe and correct. See the functions defined in the TychoRouter
contract for reference.
Run as a Binary
Installation
First, build and install the binary:
# Build the project
cargo build --release
# Install the binary to your system
cargo install --path .
After installation, you can use the tycho-encode
command from any directory in your terminal.
Commands
The command lets you choose the encoder:
tycho-router
: Encodes a transaction using theTychoRouterEncoder
.tycho-execution
: Encodes a transaction using theTychoExecutorEncoder
.
The commands accept the same options as the builders (more here).
Example
Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2 and the TychoRouterEncoder
with Permit2 on Ethereum:
echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"checked_amount":"990000000000000000","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","contract_addresses":[], "chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":0.0}],"direct_execution":true}' | tycho-encode --chain ethereum --user-transfer-type transfer-from-permit2 tycho-router
Last updated