Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support swaps with stablepool #114

Closed
wants to merge 4 commits into from
Closed

Support swaps with stablepool #114

wants to merge 4 commits into from

Conversation

JanKuczma
Copy link
Contributor

@JanKuczma JanKuczma commented Aug 21, 2024

The new Router contract will support swaps along paths that include Pair and StablePool types of pools.

Swap Path

The path is a vector of Steps and must consist of at least 2 Steps. Each step is a struct Step(PoolId, TokenId). The TokenId describes the token in of the swap for the given PoolId, except for the last Step - the last Step in the path always describes the token out. The PoolId of the last step is ignored.

E.g path [(pool_1, token_a) , (pool_2, token_b) , (pool_x, token_c)] can be visualized like this:

        pool_1          pool_2          pool_x - ignored
       /      \        /      \ 
token_a   ->   token_b    ->    token_c

Compatibility

The old Router had a "pair caching" mechanism which allowed caching custom Pair contracts. Also, when adding liquidity to a non-existent pair, the pair was created through the Factory contract.

To keep these features, the new Router has to cache the StablePool contracts before they can be used for swaps.
When swapping, the Router checks for the pool in the following order:

  1. Cached pools by PoolId
  2. Cached pairs by (token_in,token_out)
  3. Pairs created through Factory by (token_in,token_out)

@JanKuczma JanKuczma requested a review from deuszx August 21, 2024 08:59
@JanKuczma JanKuczma marked this pull request as ready for review August 22, 2024 13:50
Copy link
Collaborator

@deuszx deuszx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start with a general comment – I'd add a new router alongside the old one, not replace its code. Call it however you want (maybe even RouterV2) but let's not replace the current code (including traits). The Router(v1) is already deployed and live, we have its metadata (artifacts) committed to the repo, with deployment addresses, so let's not "erase" its existence from the main.

Copy link
Collaborator

@deuszx deuszx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small errors around caching.

let (reserve_0, reserve_1, _) = pair.get_reserves();
(vec![reserve_0, reserve_1], *fee)
}
Pool::StablePool(_, _) => unimplemented!(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add here a comment with the reason for which it's not implemented? Right now it looks like a temporary solution.

};
use traits::{MathError, Pair, RouterError, StablePool};

const TRADING_FEE_DENOM: u128 = 1000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const TRADING_FEE_DENOM: u128 = 1000;
const TRADING_FEE_DENOM_PAIR: u128 = 1000;

(Or however you prefer) but let's make sure it's clear that this is for V2 style pools only.

}
}

pub fn get_reserves_with_fee(&self) -> (Vec<u128>, u8) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be pub? You're risking caller using this on a Pool::Stable which has unimplemented!().

Comment on lines +141 to +142
let pool_ref: contract_ref!(StablePool) = pool_id.into();
let tokens = pool_ref.tokens();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which line will fail if pool_id turns out to not be a Stable pool? ie pool_ref.tokens() should then return an error, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if pool_id does not implement StablePool then that line should panic

Comment on lines +40 to +41
cached_pairs: Default::default(),
cached_pools: Mapping::default(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's make it consistent.

amm/contracts/router/lib.rs Show resolved Hide resolved
match pool {
Pool::Pair(tokens, _, _) => {
self.cached_pairs.remove(tokens);
self.cached_pairs.remove(tokens);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to destructure the tuple and remove twice – switching the order.

Copy link
Contributor

@krzysztofziobro krzysztofziobro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't read the whole thing yet, first few things that caught my eye

@@ -220,7 +220,10 @@ fn test_fees(mut session: Session) {
.execute(router.swap_exact_tokens_for_tokens(
swap_amount,
0,
vec![ice.into(), wood.into()],
vec![
(ice_wood_pair.into(), ice.into()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I missed something, there are no new tests for interacting with stable pools - I think it's important to include some.

@@ -87,7 +92,7 @@ pub trait Router {
deadline: u64,
) -> Result<(u128, Balance), RouterError>;

/// Exchanges tokens along `path` tokens.
/// Exchanges tokens along `path` pools and tokens.
/// Starts with `amount_in` and pair under `(path[0], path[1])` address.
Copy link
Contributor

@krzysztofziobro krzysztofziobro Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the comment is still a bit outdated

  • it should be explained what are the expected values (eg. you could add a new type Path and explain that there), since without reading the code I wouldn't know whether its [(unused_pool, token_1), (pool_1, token_2)] or [(pool_1, token_1), (token_2, unused_pool)] - for me the first version seems more intuitive, but turns out it's wrong.
  • would be nice to have a mention about what the return value is supposed to represent

@@ -96,12 +101,12 @@ pub trait Router {
&mut self,
amount_in: u128,
amount_out_min: u128,
path: Vec<AccountId>,
path: Vec<Step>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that with this type you need to pass at least one "unused" pool address, I would change it to either:

  • path: Vec<Step> + first_token: AccountId, or
  • make Step field holding the pool Option<AccountId>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Imho the path: Vec<Step>, token_in: AccountId is cleaner and easier to understand.

@@ -185,45 +190,13 @@ pub trait Router {
#[ink(message)]
fn quote(&self, amount_0: u128, reserve_0: u128, reserve_1: u128) -> Result<u128, RouterError>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems weird for me to have this as a message when we have different pool types - do you know if we are currently using it somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I'm not sure if this method is used somewhere by FE but I doubt it (it does a simple calculation which can be done off-chain).

@JanKuczma
Copy link
Contributor Author

The development continued in #117.

@JanKuczma JanKuczma closed this Sep 3, 2024
@JanKuczma JanKuczma deleted the new-router branch November 12, 2024 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants