-
Notifications
You must be signed in to change notification settings - Fork 217
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
feat(amm): support maxOut
optional arg to swap
#5125
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the test failure is legit. Debt is empty in this block:
// An emergency liquidation got less than full value
const debtAfterLiquidation = await E(vault).getCurrentDebt();
); | ||
|
||
// Three (!) awaits coming here. We can't use Promise.all because | ||
// Multiple awaits coming here. We can't use Promise.all because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just one now, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -193,6 +193,7 @@ const start = async (zcf, privateArgs) => { | |||
} | |||
|
|||
const pool = isSecondary(brandOut) ? getPool(brandOut) : getPool(brandIn); | |||
// @ts-expect-error cast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Chris-Hibbert is it safe to cast to DoublePoolInternalFacet
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 184-193 are the doublePool case. This is handling a single pool, so the renaming of VPoolInternalFacet
to DoublePoolInternalFacet
looks like a mistake. This correctly returns VpoolWrapper
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renaming of
VPoolInternalFacet
toDoublePoolInternalFacet
looks like a mistake
All properties of VPoolInternalFacet
specified double,
/**
* @typedef {Object} VPoolInternalFacet - virtual pool for price quotes and trading
* @property {GetDoublePoolSwapQuote} getPriceForInput
* @property {GetDoublePoolSwapQuote} getPriceForOutput
* @property {(ZCFSeat, DoublePoolSwapResult) => string} allocateGainsAndLosses
*/
Is there a type for VPoolInternalFacet
that doesn't presume pool type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, no. So the types are not generic here, even though the code kinda is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like someone made the type a union type, so it's now actually accurate.
@@ -193,6 +193,7 @@ const start = async (zcf, privateArgs) => { | |||
} | |||
|
|||
const pool = isSecondary(brandOut) ? getPool(brandOut) : getPool(brandIn); | |||
// @ts-expect-error cast |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lines 184-193 are the doublePool case. This is handling a single pool, so the renaming of VPoolInternalFacet
to DoublePoolInternalFacet
looks like a mistake. This correctly returns VpoolWrapper
.
AmountMath.coerce(amountOut.brand, maxOut); | ||
prices = pool.getPriceForOutput(amountIn, maxOut); | ||
} | ||
if (!prices || !AmountMath.isGTE(prices.swapperGets, maxOut)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are scenarios where current calls to swapOut()
would fail to trade, and this code would fall through to getPriceForInput()
and sell anyway. One I found is if there is insufficient liquidity to satisfy the swapOut
. In that case, I think the failure to trade is the correct outcome, even though that's not what you want in the liquidation case.
I would also like to see reasonably thorough testing of the swapIn(amountIn, { maxOut })
cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it also passes the want.Out
as a want.Out
for the swapIn
, I believe in all those cases it should properly fail to reallocate because the want is not satisfied.
cad92bb
to
e1688f5
Compare
2def151
to
1aa1371
Compare
switching between The first consequence I can see is that the way fees are calculated is supposed to depend on whether they called That's probably defensible, but we haven't done so yet. I'm not sure those are the only consequences. I am convinced that offer safety constraints are not broken by this approach. When the user specifies You suggested that we could change to an approach where we charge a flat fee on top of the transaction and allocate it to pool and protocol fees separately from calculating prices. That would also be defensible, but that's not what this change does. |
* Move swap implementation into shared `swap.js` * Implement `maxOut` using existing pricing operations * implement `swapOut` in terms of `swapIn` with `maxOut` * remove now-unneeded code in pool implementations * make `allocateGainsAndLosses` API the same for single and double pools
* remove fallback `swapOut` since it's not needed anymore * suppress problematic typescript warnings
* to preclude any difference from documented behavior fro `swapOut` keep using the direct implementation for now
2b33b8b
to
83a84f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for listening to my concerns.
This is good.
|
||
/** | ||
* @param {ZCF} zcf | ||
* @param {(brandIn: Brand, brandOut: Brand) => VPoolWrapper<unknown>} provideVPool | ||
* @param {(brandIn: Brand, brandOut: Brand) => VPoolWrapper<DoublePoolInternalFacet>} provideVPool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
provideVPool can provide either singlePool or doublePool, though no one should care. I'd make it VPoolWrapper<unknown>
, which allows future extensions and declares that we don't care.
refs: #4856
Description
Implements the new API in terms of the existing underlying implementation.
swap.js
maxOut
using existing pricing operationsswapOut
in terms ofswapIn
withmaxOut
allocateGainsAndLosses
API the same for single and double poolsSecurity Considerations
N/A
Documentation Considerations
Need to deprecate the old API and add the new argument to Swap.
Testing Considerations
SwapOut is defined in terms of the new API, so it gets lot of testing now. Additional specific cases should be added for which the
want
is not the same as themaxOut
.Test cases from the liquidation branch