balancer_sdk/
structs.rs

1use ethcontract::tokens::{Bytes, Tokenize};
2use ethcontract::{H160, U256};
3use ethcontract_common::abi::Token::FixedBytes;
4use ethers_core::utils::parse_units;
5
6pub use std::str::FromStr;
7
8use crate::{Address, Bytes32, PoolBalanceOpKind, SwapKind, Variable, IERC20};
9
10#[derive(Clone, Copy, Debug)]
11pub struct PoolId(pub Bytes32);
12impl FromStr for PoolId {
13    type Err = ethcontract::tokens::Error;
14
15    fn from_str(s: &str) -> Result<Self, Self::Err> {
16        let bytes = hexutil::read_hex(s);
17        let bytes = bytes.unwrap();
18        let bytes = Bytes::from_token(FixedBytes(bytes))?;
19
20        Ok(PoolId(bytes))
21    }
22}
23impl From<PoolId> for Bytes32 {
24    fn from(pool_id: PoolId) -> Self {
25        pool_id.0
26    }
27}
28
29#[derive(Debug, Clone, Copy)]
30pub struct UserData(pub &'static str);
31impl From<UserData> for ethcontract::tokens::Bytes<Vec<u8>> {
32    fn from(data: UserData) -> Self {
33        let data = hexutil::read_hex(data.0).unwrap();
34        ethcontract::tokens::Bytes::<Vec<u8>>(data)
35    }
36}
37
38#[derive(Debug, Clone)]
39pub struct FromDecStrErr;
40
41/// A struct for working with SwapFeePercentages
42///
43/// SwapFeePercentages needs to be in wei. This struct's methods allow
44/// us to easily go from a string (ie. "0.15" percent) to a wei (150000000000000000).
45///
46/// # Example
47///
48/// ```rust
49/// # use balancer_sdk::*;
50/// let fee = SwapFeePercentage::from_human_readable_str("0.15").unwrap();
51/// let value: U256 = fee.into();
52///
53/// assert_eq!(value, u256!("150000000000000000"));
54/// ```
55#[derive(Debug, Clone, Copy)]
56pub struct SwapFeePercentage(pub U256);
57impl SwapFeePercentage {
58    /// Input human readble percentage string: "0.15"
59    pub fn from_human_readable_str(s: &str) -> Result<Self, FromDecStrErr> {
60        let wei = parse_units(s, "ether").unwrap();
61        SwapFeePercentage::from_str(&wei.to_string())
62    }
63}
64impl FromStr for SwapFeePercentage {
65    type Err = FromDecStrErr;
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        let percentage = U256::from_dec_str(s).unwrap();
69        Ok(SwapFeePercentage(percentage))
70    }
71}
72impl From<SwapFeePercentage> for U256 {
73    fn from(percentage: SwapFeePercentage) -> Self {
74        percentage.0
75    }
76}
77
78/// The SingleSwapTuple es the data structure used by the contract interface.
79/// This is what is created when calling single_swap_instance.into()
80type SingleSwapTuple = (
81    ethcontract::Bytes<[u8; 32]>,
82    u8,
83    H160,
84    H160,
85    U256,
86    ethcontract::Bytes<Vec<u8>>,
87);
88/// The SingleSwap struct defines which pool we're trading with and what kind of swap we want to perform.
89#[derive(Debug, Clone)]
90pub struct SingleSwap {
91    pub pool_id: PoolId,
92    pub kind: SwapKind,
93    pub asset_in: Address,
94    pub asset_out: Address,
95    pub amount: U256,
96    pub user_data: ethcontract::tokens::Bytes<Vec<u8>>,
97}
98/// Allows for conversion of a BatchSwapStep to a tuple
99impl From<SingleSwap> for SingleSwapTuple {
100    fn from(swap: SingleSwap) -> SingleSwapTuple {
101        (
102            swap.pool_id.into(),
103            swap.kind as u8,
104            swap.asset_in,
105            swap.asset_out,
106            swap.amount,
107            swap.user_data,
108        )
109    }
110}
111
112type BatchSwapTuple = (
113    // pool_id
114    ethcontract::tokens::Bytes<[u8; 32]>,
115    // asset_in_index
116    ethcontract::U256,
117    // asset_out_index
118    ethcontract::U256,
119    // amount
120    ethcontract::U256,
121    // user_data
122    ethcontract::tokens::Bytes<Vec<u8>>,
123);
124
125#[derive(Clone, Debug)]
126pub struct BatchSwapStep {
127    pub pool_id: PoolId,
128    pub asset_in_index: usize,
129    pub asset_out_index: usize,
130    pub amount: U256,
131    pub user_data: UserData,
132}
133/// Allows for conversion of a BatchSwapStep to a tuple
134impl From<BatchSwapStep> for BatchSwapTuple {
135    fn from(swap_step: BatchSwapStep) -> BatchSwapTuple {
136        (
137            swap_step.pool_id.into(),
138            swap_step.asset_in_index.into(),
139            swap_step.asset_out_index.into(),
140            swap_step.amount,
141            swap_step.user_data.into(),
142        )
143    }
144}
145
146type FundManagementTuple = (
147    ethcontract::Address, // sender
148    bool,                 // from_internal_balance
149    ethcontract::Address, // recipient
150    bool,                 // to_internal_balance
151);
152/// The FundManagement struct defines where the input tokens for the first swap are coming from and where any tokens received from swaps should be sent.
153/// [See Balancer documentation](https://dev.balancer.fi/resources/swaps/batch-swaps#fundmanagement-struct)
154pub struct FundManagement {
155    /// The address from which tokens will be taken to perform the trade
156    pub sender: ethcontract::Address,
157    /// Whether the trade should use tokens owned by the sender which are already stored in the Vault.
158    pub from_internal_balance: bool,
159    /// The address to which tokens will be sent to after the trade.
160    pub recipient: ethcontract::Address,
161    /// Whether the tokens should be sent to the recipient or stored within their internal balance within the Vault.
162    /// For more information on internal balances see [Core Concepts](https://dev.balancer.fi/resources/swaps/batch-swaps?q=%2F#:~:text=For%20more%20information%20on%20internal%20balances%20see%20Core%20Concepts.).
163    pub to_internal_balance: bool,
164}
165/// Allows for conversion of a BatchSwapStep to a tuple
166impl From<FundManagement> for FundManagementTuple {
167    fn from(funds: FundManagement) -> FundManagementTuple {
168        (
169            funds.sender,
170            funds.from_internal_balance,
171            funds.recipient,
172            funds.to_internal_balance,
173        )
174    }
175}
176
177pub struct SwapRequest {
178    pub kind: SwapKind,
179    pub token_in: IERC20,
180    pub token_out: IERC20,
181    pub amount: U256,
182
183    // Misc data
184    pub pool_id: PoolId,
185    pub last_change_block: U256,
186    pub from: Address,
187    pub to: Address,
188    pub user_data: UserData,
189}
190impl From<SwapRequest>
191    for (
192        u8,
193        ethcontract::H160,
194        ethcontract::H160,
195        ethcontract::U256,
196        ethcontract::Bytes<[u8; 32]>,
197        ethcontract::U256,
198        ethcontract::H160,
199        ethcontract::H160,
200        ethcontract::Bytes<std::vec::Vec<u8>>,
201    )
202{
203    fn from(
204        swap_request: SwapRequest,
205    ) -> (
206        u8,
207        ethcontract::H160,
208        ethcontract::H160,
209        ethcontract::U256,
210        ethcontract::Bytes<[u8; 32]>,
211        ethcontract::U256,
212        ethcontract::H160,
213        ethcontract::H160,
214        ethcontract::Bytes<std::vec::Vec<u8>>,
215    ) {
216        let SwapRequest {
217            kind,
218            token_in,
219            token_out,
220            amount,
221            pool_id,
222            last_change_block,
223            from,
224            to,
225            user_data,
226        } = swap_request;
227        (
228            kind as u8,
229            token_in,
230            token_out,
231            amount,
232            pool_id.into(),
233            last_change_block,
234            from,
235            to,
236            user_data.into(),
237        )
238    }
239}
240
241/// [See Balancer documentation](https://dev.balancer.fi/resources/joins-and-exits/pool-joins)
242pub struct JoinPoolRequest {
243    /// Sorted list of all tokens in pool
244    pub assets: Vec<Address>,
245    /// Maximum token send amounts
246    pub max_amounts_in: Vec<U256>,
247    /// Custom bytes field
248    pub user_data: UserData,
249    /// True if sending from internal token balances. False if sending ERC20.
250    pub from_internal_balance: bool,
251}
252impl From<JoinPoolRequest>
253    for (
254        Vec<ethcontract::H160>,
255        Vec<ethcontract::U256>,
256        ethcontract::tokens::Bytes<Vec<u8>>,
257        bool,
258    )
259{
260    fn from(
261        request: JoinPoolRequest,
262    ) -> (
263        Vec<ethcontract::H160>,
264        Vec<ethcontract::U256>,
265        ethcontract::tokens::Bytes<Vec<u8>>,
266        bool,
267    ) {
268        let JoinPoolRequest {
269            assets,
270            max_amounts_in,
271            user_data,
272            from_internal_balance,
273        } = request;
274        (
275            assets,
276            max_amounts_in,
277            user_data.into(),
278            from_internal_balance,
279        )
280    }
281}
282
283/// [See Balancer documentation](https://dev.balancer.fi/resources/joins-and-exits/pool-exits#api)
284pub struct ExitPoolRequest {
285    /// List of your tokens, ordered
286    pub assets: Vec<Address>,
287    /// Minimum token receive amounts
288    pub max_amounts_out: Vec<U256>,
289    /// Custom bytes field
290    pub user_data: UserData,
291    /// True if you receiving tokens as internal token balances. False if receiving as ERC20.
292    pub to_internal_balance: bool,
293}
294impl From<ExitPoolRequest>
295    for (
296        Vec<ethcontract::H160>,
297        Vec<ethcontract::U256>,
298        ethcontract::tokens::Bytes<Vec<u8>>,
299        bool,
300    )
301{
302    fn from(
303        request: ExitPoolRequest,
304    ) -> (
305        Vec<ethcontract::H160>,
306        Vec<ethcontract::U256>,
307        ethcontract::tokens::Bytes<Vec<u8>>,
308        bool,
309    ) {
310        let ExitPoolRequest {
311            assets,
312            max_amounts_out,
313            user_data,
314            to_internal_balance,
315        } = request;
316        (
317            assets,
318            max_amounts_out,
319            user_data.into(),
320            to_internal_balance,
321        )
322    }
323}
324
325/// PoolBalanceOp describes the type of operation (deposit/withdraw/update), the pool ID, the token, and the amount.
326pub struct PoolBalanceOp {
327    pub kind: PoolBalanceOpKind,
328    pub pool_id: PoolId,
329    pub token: Address,
330    pub amount: U256,
331}
332impl From<PoolBalanceOp> for (u8, Bytes<[u8; 32]>, Address, U256) {
333    fn from(op: PoolBalanceOp) -> (u8, Bytes<[u8; 32]>, Address, U256) {
334        let PoolBalanceOp {
335            kind,
336            pool_id,
337            token,
338            amount,
339        } = op;
340        (kind as u8, pool_id.into(), token, amount)
341    }
342}
343
344pub struct OracleAverageQuery {
345    pub variable: Variable,
346    pub secs: U256,
347    pub ago: U256,
348}
349impl From<OracleAverageQuery> for (u8, U256, U256) {
350    fn from(query: OracleAverageQuery) -> (u8, U256, U256) {
351        let OracleAverageQuery {
352            variable,
353            secs,
354            ago,
355        } = query;
356        (variable as u8, secs, ago)
357    }
358}
359
360pub struct OracleAccumulatorQuery {
361    pub variable: Variable,
362    pub ago: U256,
363}
364impl From<OracleAccumulatorQuery> for (u8, U256) {
365    fn from(query: OracleAccumulatorQuery) -> (u8, U256) {
366        let OracleAccumulatorQuery { variable, ago } = query;
367        (variable as u8, ago)
368    }
369}