Simulation
Simulate interactions with any protocol.
Last updated
Simulate interactions with any protocol.
Last updated
Tycho Simulation is a Rust crate that provides powerful tools for interacting with protocol states, calculating spot prices, and simulating token swaps.
The repository is available here.
The tycho-simulation
package is available on crates.io.
To use the simulation tools with Ethereum Virtual Machine (EVM) chains, add the optional evm
feature flag to your dependency configuration:
tycho-simulation = {
version = "x.y.z", # Replace with latest version
features = ["evm"]
}
Add this to your project's Cargo.toml
file.
All protocols implement the ProtocolSim
trait (see definition here). It has the main methods:
spot_price
returns the pool's current marginal price.
fn spot_price(&self, base: &Token, quote: &Token) -> Result<f64, SimulationError>;
get_amount_out
simulates token swaps.
fn get_amount_out(
&self,
amount_in: BigUint,
token_in: &Token,
token_out: &Token,
) -> Result<GetAmountOutResult, SimulationError>;
You receive a GetAmountOutResult
, which is defined as follows:
pub struct GetAmountOutResult {
pub amount: BigUint, // token_out amount you receive
pub gas: BigUint, // gas cost
pub new_state: Box<dyn ProtocolSim>, // state of the protocol after the swap
}
new state
allows you to, for example, simulate consecutive swaps in the same protocol.
Please refer to the in-code documentation of the ProtocolSim
trait and its methods for more in-depth information.
fee
returns the fee of the protocol as a ratio.
For example if the fee is 1%, the value returned would be 0.01.
fn fee(&self) -> f64;
get_limits
returns a tuple containing the maximum amount in and out that can be traded between two tokens.
fn get_limits(
&self,
sell_token: Address,
buy_token: Address,
) -> Result<(BigUint, BigUint), SimulationError>;
To maintain up-to-date states of the protocols you wish to simulate over, you can use a Tycho Indexer stream. Such a stream can be set up in 2 easy steps:
It is necessary to collect all tokens you are willing to support/swap over as this must be set on the stream builder in step 2. You can either set up custom logic to define this, or use the Tycho Indexer RPC to fetch and filter for tokens of interest. To simplify this, a util function called load_all_tokens
is supplied and can be used as follows:
use tycho_simulation::utils::load_all_tokens;
use tycho_core::models::Chain;
let all_tokens = load_all_tokens(
"tycho-beta.propellerheads.xyz", // tycho url
false, // use tsl (this flag disables tsl)
Some("sampletoken"), // auth key
Chain::Ethereum, // chain
None, // min quality (defaults to 100: ERC20-like tokens only)
None, // days since last trade (has chain specific defaults)
).await;
You can use the ProtocolStreamBuilder to easily set up and manage multiple protocols within one stream. An example of creating such a stream with Uniswap V2 and Balancer V2 protocols is as follows:
use tycho_simulation::evm::{
engine_db::tycho_db::PreCachedDB,
protocol::{
filters::balancer_pool_filter, uniswap_v2::state::UniswapV2State, vm::state::EVMPoolState,
},
stream::ProtocolStreamBuilder,
};
use tycho_core::models::Chain;
use tycho_client::feed::component_tracker::ComponentFilter;
let tvl_filter = ComponentFilter::with_tvl_range(9, 10); // filter buffer of 9-10ETH
let mut protocol_stream = ProtocolStreamBuilder::new("tycho-beta.propellerheads.xyz", Chain::Ethereum)
.exchange::<UniswapV2State>("uniswap_v2", tvl_filter.clone(), None)
.exchange::<EVMPoolState<PreCachedDB>>(
"vm:balancer_v2",
tvl_filter.clone(),
Some(balancer_pool_filter),
)
.auth_key(Some("sampletoken"))
.skip_state_decode_failures(true) // skips the pool instead of panicking if it errors on decode
.set_tokens(all_tokens.clone())
.await
.build()
.await
.expect("Failed building protocol stream");
Some protocols, such as Balancer V2 and Curve, require a pool filter to be defined to filter out unsupported pools. If a protocol needs a pool filter and the user does not provide one, a warning will be raised during the stream setup process.
The stream created emits BlockUpdate
messages which consist of:
block number
- the block this update message refers to
new_pairs
- new components witnessed (either recently created or newly meeting filter criteria)
removed_pairs
- components no longer tracked (either deleted due to a reorg or no longer meeting filter criteria)
states
- the updated ProtocolSim
states for all components modified in this block
The first message received will contain states for all protocol components registered to. Thereafter, further block updates will only contain data for updated or new components.
Note: For efficiency,
ProtocolSim
states contain simulation-critical data only. Reference data such as protocol names and token information is provided in theProtocolComponent
objects within thenew_pairs
field. Consider maintaining a store of these components if you need this metadata.
For a full list of supported protocols and the simulation state implementations they use, see Supported Protocols.
You can find an example of a price printer here.
Clone the repo, then run:
export RPC_URL=<your-eth-rpc-url>
cargo run --release --example price_printer -- --tvl-threshold 1000
You will see a UI where you can select any pool, press enter, and simulate different trade amounts on the pool.
The program prints logs automatically to a file in the logs
directory in the repo.