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

exp/orderbook: Use the correct asset when calculating liquidity pool disbursements #4018

Merged
merged 3 commits into from
Oct 20, 2021

Conversation

Shaptic
Copy link
Contributor

@Shaptic Shaptic commented Oct 19, 2021

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've updated the relevant CHANGELOG (here for Horizon) if
    needed with deprecations, added features, breaking changes, and DB schema changes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

Flips the order in which assets are evaluated when calculating deposit requirements (or "pool expectations"). This is the actual fix:

func (state *sellingGraphSearchState) consumePool(
	pool xdr.LiquidityPoolEntry,
	currentAsset xdr.Asset,
	currentAssetAmount xdr.Int64,
) (xdr.Int64, error) {
-	return makeTrade(pool, currentAsset, tradeTypeExpectation, currentAssetAmount)
+	// How many of the previous hop do we need to get this amount?
+	return makeTrade(pool, getOtherAsset(currentAsset, pool),
+		tradeTypeExpectation, currentAssetAmount)
}

The problem is not with liquidity pool math, but rather in the way we consume liquidity pools during the orderbook search.

The other changes are to add/update test cases accordingly; refer to the commit message in 6f77891 for a thorough breakdown and justification of them.

Why

#4014 raised a valid issue about the /paths/strict-receive endpoint returning impossible paths. A follow-up investigation (comment) puts us down the right path: somehow, somewhere, assets are being swapped.

In the issue, we attempt to find an ETH -> USDC path and get results when there shouldn't be any. If we track this through the path-finding algorithm, without this fix, it claims to find the following path:

700000000000 USDC -> 11340000 BTC -> 603359 YBX -> 20400907 ETH

The first of these is true: we need 0.1134 BTC to get 70000 USDC. This is a standard offer evaluation. The second hop, though, is nonsense. The BTC/YBX pool in question has reserves of 453280/19884933, respectively. It would be impossible to get 11340000 BTC from it, since obviously 453280 < 11340000. The liquidity pool math is tested extensively (see TestLiquidityPoolMath and TestLiquidityPoolExchanges in pools_test.go), so the issue must be elsewhere.

Turns out, this 603359 number is legitimate: this is how many BTC it would take to get 11340000 YBX. That's the wrong exchange direction! When we're working backwards from the destination (as is the case of strict-receive and FindPaths), we need to see how much of the asset at the previous hop we'd need to get a payout of the current asset amount. The bug is that we find how much of the current hop we'd need to get the previous amount...

Essentially, the mistake is using the incorrect assets corresponding to X/x and Y/y in the formula from CAP-38.

This closes #4014.

Known limitations

Should we do a minor version bump?

Two tests needed to be updated, both involving FindPaths
(naturally):

 - TestPathThroughLiquidityPools/more_hops: Here, we changed the
   expected value from 342 to 73. This should've been caught
   during testing, because this test adds a single hop that
   passes through the CHF/USD liquidity pool. This pool has a 1:2
   ratio, so the previous result (127 USD) should've been *~halved*
   not *~doubled* if we start from CHF instead.

 - TestInterleavedPaths: This test had a bug already-we were adding
   the EUR/XLM pool to the graph, so the EUR/XLM offers could've
   been ignored.

   With the fix, the CHF -> USD hop is once again flipped (see prev
   note), so the whole path needs to be adjusted. In fact, wanting
   10 XLM is too small of a value (we can get that for very cheap
   from CHF now), so we need to use 100 instead.

   The rates down the chain are:
     * XLM -> EUR -> USD -> CHF which is
          1:1    1:1    2:1
       meaning around 1 CHF for 2 XLM (spot price), so 64 CHF for
       100 XLM passes the sanity check

     * XLM -> USD -> CHF which is
          4:1    2:1
       meaning around 1 CHF for 8 XLM (spot). This makes 90 CHF
       for 100 XLM seem pricey, but this is because we're almost
       completely tapping out the XLM/USD pool for this trade.
       We get 152 USD for our CHF (this ratio is close), but then
       there are only 120 XLM and we want 100 of them, so the
       spot price there gets *heavily* out-weighed.

There are some other changes, but those are pure, minor refactors,
e.g. moving the pool-less tests into a subtest.
@Shaptic Shaptic added bug amm support cap 38 (automated market makers) in horizon labels Oct 19, 2021
@Shaptic Shaptic requested review from tamirms and a team October 19, 2021 22:47
@Shaptic Shaptic self-assigned this Oct 19, 2021
@Shaptic Shaptic changed the base branch from master to release-horizon-v2.10.0 October 20, 2021 22:26
@Shaptic Shaptic merged commit 3730494 into stellar:release-horizon-v2.10.0 Oct 20, 2021
@Shaptic Shaptic deleted the fix-lp-expectations branch October 20, 2021 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
amm support cap 38 (automated market makers) in horizon bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Protocol 18: Horizon path/strict-receive Bug
2 participants