use std::sync::Arc;
use clap::ValueEnum;
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use tycho_common::{
models::protocol::ProtocolComponent, simulation::protocol_sim::ProtocolSim, Bytes,
};
use crate::encoding::serde_primitives::biguint_string;
#[derive(Clone, Debug, PartialEq, ValueEnum, Serialize, Deserialize, Default)]
pub enum UserTransferType {
TransferFromPermit2,
#[default]
TransferFrom,
UseVaultsFunds,
}
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub struct Solution {
sender: Bytes,
receiver: Bytes,
token_in: Bytes,
#[serde(with = "biguint_string")]
amount_in: BigUint,
token_out: Bytes,
#[serde(with = "biguint_string")]
min_amount_out: BigUint,
swaps: Vec<Swap>,
user_transfer_type: UserTransferType,
}
impl Solution {
pub fn new(
sender: Bytes,
receiver: Bytes,
token_in: Bytes,
token_out: Bytes,
amount_in: BigUint,
min_amount_out: BigUint,
swaps: Vec<Swap>,
) -> Self {
Self {
sender,
receiver,
token_in,
token_out,
amount_in,
min_amount_out,
swaps,
user_transfer_type: UserTransferType::TransferFrom,
}
}
pub fn sender(&self) -> &Bytes {
&self.sender
}
pub fn receiver(&self) -> &Bytes {
&self.receiver
}
pub fn token_in(&self) -> &Bytes {
&self.token_in
}
pub fn amount_in(&self) -> &BigUint {
&self.amount_in
}
pub fn token_out(&self) -> &Bytes {
&self.token_out
}
pub fn min_amount_out(&self) -> &BigUint {
&self.min_amount_out
}
pub fn swaps(&self) -> &[Swap] {
&self.swaps
}
pub fn user_transfer_type(&self) -> &UserTransferType {
&self.user_transfer_type
}
pub fn with_sender(mut self, sender: Bytes) -> Self {
self.sender = sender;
self
}
pub fn with_receiver(mut self, receiver: Bytes) -> Self {
self.receiver = receiver;
self
}
pub fn with_token_in(mut self, token_in: Bytes) -> Self {
self.token_in = token_in;
self
}
pub fn with_amount_in(mut self, amount_in: BigUint) -> Self {
self.amount_in = amount_in;
self
}
pub fn with_token_out(mut self, token_out: Bytes) -> Self {
self.token_out = token_out;
self
}
pub fn with_min_amount_out(mut self, min_amount_out: BigUint) -> Self {
self.min_amount_out = min_amount_out;
self
}
pub fn with_swaps(mut self, swaps: Vec<Swap>) -> Self {
self.swaps = swaps;
self
}
pub fn with_user_transfer_type(mut self, user_transfer_type: UserTransferType) -> Self {
self.user_transfer_type = user_transfer_type;
self
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Swap {
component: ProtocolComponent,
token_in: Bytes,
token_out: Bytes,
#[serde(default)]
split: f64,
user_data: Option<Bytes>,
#[serde(skip)]
protocol_state: Option<Arc<dyn ProtocolSim>>,
estimated_amount_in: Option<BigUint>,
}
impl Swap {
pub fn new<T: Into<ProtocolComponent>>(
component: T,
token_in: Bytes,
token_out: Bytes,
) -> Self {
Self {
component: component.into(),
token_in,
token_out,
split: 0.0,
user_data: None,
protocol_state: None,
estimated_amount_in: None,
}
}
pub fn with_split(mut self, split: f64) -> Self {
self.split = split;
self
}
pub fn with_user_data(mut self, user_data: Bytes) -> Self {
self.user_data = Some(user_data);
self
}
pub fn with_protocol_state(mut self, protocol_state: Arc<dyn ProtocolSim>) -> Self {
self.protocol_state = Some(protocol_state);
self
}
pub fn with_estimated_amount_in(mut self, estimated_amount_in: BigUint) -> Self {
self.estimated_amount_in = Some(estimated_amount_in);
self
}
pub fn component(&self) -> &ProtocolComponent {
&self.component
}
pub fn token_in(&self) -> &Bytes {
&self.token_in
}
pub fn token_out(&self) -> &Bytes {
&self.token_out
}
pub fn split(&self) -> f64 {
self.split
}
pub fn user_data(&self) -> &Option<Bytes> {
&self.user_data
}
pub fn protocol_state(&self) -> &Option<Arc<dyn ProtocolSim>> {
&self.protocol_state
}
pub fn estimated_amount_in(&self) -> &Option<BigUint> {
&self.estimated_amount_in
}
}
impl PartialEq for Swap {
fn eq(&self, other: &Self) -> bool {
self.component() == other.component() &&
self.token_in() == other.token_in() &&
self.token_out() == other.token_out() &&
self.split() == other.split() &&
self.user_data() == other.user_data() &&
self.estimated_amount_in() == other.estimated_amount_in()
}
}
#[derive(Clone, Debug)]
pub struct EncodedSolution {
swaps: Vec<u8>,
interacting_with: Bytes,
function_signature: String,
n_tokens: usize,
permit: Option<PermitSingle>,
}
impl EncodedSolution {
pub(crate) fn new(
swaps: Vec<u8>,
interacting_with: Bytes,
function_signature: String,
n_tokens: usize,
) -> Self {
Self { swaps, interacting_with, function_signature, n_tokens, permit: None }
}
pub(crate) fn with_permit(mut self, permit: PermitSingle) -> Self {
self.permit = Some(permit);
self
}
pub fn swaps(&self) -> &[u8] {
&self.swaps
}
pub fn interacting_with(&self) -> &Bytes {
&self.interacting_with
}
pub fn function_signature(&self) -> &str {
&self.function_signature
}
pub fn n_tokens(&self) -> usize {
self.n_tokens
}
pub fn permit(&self) -> Option<&PermitSingle> {
self.permit.as_ref()
}
}
#[derive(Debug, Clone)]
pub struct PermitSingle {
details: PermitDetails,
spender: Bytes,
sig_deadline: BigUint,
}
impl PermitSingle {
pub fn new(details: PermitDetails, spender: Bytes, sig_deadline: BigUint) -> Self {
Self { details, spender, sig_deadline }
}
pub fn details(&self) -> &PermitDetails {
&self.details
}
pub fn spender(&self) -> &Bytes {
&self.spender
}
pub fn sig_deadline(&self) -> &BigUint {
&self.sig_deadline
}
}
#[derive(Debug, Clone)]
pub struct PermitDetails {
token: Bytes,
amount: BigUint,
expiration: BigUint,
nonce: BigUint,
}
impl PermitDetails {
pub fn new(token: Bytes, amount: BigUint, expiration: BigUint, nonce: BigUint) -> Self {
Self { token, amount, expiration, nonce }
}
pub fn token(&self) -> &Bytes {
&self.token
}
pub fn amount(&self) -> &BigUint {
&self.amount
}
pub fn expiration(&self) -> &BigUint {
&self.expiration
}
pub fn nonce(&self) -> &BigUint {
&self.nonce
}
}
impl PartialEq for PermitSingle {
fn eq(&self, other: &Self) -> bool {
self.details == other.details && self.spender == other.spender
}
}
impl PartialEq for PermitDetails {
fn eq(&self, other: &Self) -> bool {
self.token == other.token && self.amount == other.amount && self.nonce == other.nonce
}
}
#[derive(Clone, Debug)]
pub struct EncodingContext {
pub router_address: Option<Bytes>,
pub group_token_in: Bytes,
pub group_token_out: Bytes,
}
mod tests {
use super::*;
struct MockProtocolComponent {
id: String,
protocol_system: String,
}
impl From<MockProtocolComponent> for ProtocolComponent {
fn from(component: MockProtocolComponent) -> Self {
ProtocolComponent {
id: component.id,
protocol_system: component.protocol_system,
tokens: vec![],
protocol_type_name: "".to_string(),
chain: Default::default(),
contract_addresses: vec![],
static_attributes: Default::default(),
change: Default::default(),
creation_tx: Default::default(),
created_at: Default::default(),
}
}
}
#[test]
fn test_swap_new() {
let component = MockProtocolComponent {
id: "i-am-an-id".to_string(),
protocol_system: "uniswap_v2".to_string(),
};
let user_data = Bytes::from("0x1234");
let swap = Swap::new(component, Bytes::from("0x12"), Bytes::from("0x34"))
.with_split(0.5)
.with_user_data(user_data.clone());
assert_eq!(*swap.token_in(), Bytes::from("0x12"));
assert_eq!(*swap.token_out(), Bytes::from("0x34"));
assert_eq!(swap.component().protocol_system, "uniswap_v2");
assert_eq!(swap.component().id, "i-am-an-id");
assert_eq!(swap.split(), 0.5);
assert_eq!(swap.user_data(), &Some(user_data));
}
}