-
Notifications
You must be signed in to change notification settings - Fork 229
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
Extract AMM from Vaults (ex-Treasury) #4026
Conversation
99234ed
to
516c4a2
Compare
e806367
to
88c2d18
Compare
fbd685c
to
dca1757
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.
I've looked at all the non-test changes; I think it's worth sharing my comments so far.
@@ -63,7 +129,7 @@ export async function installOnChain({ | |||
/** @type {Array<[string, SourceBundle]>} */ | |||
const nameBundles = [ | |||
['liquidate', liquidateBundle], | |||
['autoswap', autoswapBundle], | |||
['amm', ammBundle], |
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.
Using string literals suggests we're free to choose any string we like, when actually, there are external constraints. IMO, it makes at least as much sense to use a manifest constant here as for DEFAULT_POOL_FEE
. But we didn't do that before this PR, so I'm OK leaving it out of scope.
agoricNames
is somewhat under-documented, like much of the wallet / REPL API. I should ensure that we have an issue about that...
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'd be very happy to conform to some standard practice about consistent names for the wallet and other user-facing code.
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 opened #4084 in case any of us gets inspired.
} | ||
|
||
return { | ||
governor: g, |
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.
why return governor
and then ignore it below?
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 return it here because someone will need it to change AMM parameters. This is the code that knows that it has a valuable facet. I ignore it below because I don't have a use for it in install-on-chain. I can imagine that there's an appropriate place to put it, but it's surely not in agoric-names. Is there a plausible place to put it currently? I expect we'll eventually want someone to have that facet, but I don't know who it'll be.
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.
Hm... setupAmm
isn't exported. So it's not possible for anyone to use governor
without changing this file. My style would be to leave this out until then, but I suppose it's not critical.
Looking around to see if there's an issue to straighten this out... #3924 looks close enough, though it sure would be nice to see a plan to actually make sense of the bootstrap process.
) | ||
|
||
// todo(hibbert): set up an initial AMM pool with RUN and BLD | ||
// const liqSeat = createLiquidityPool(...); |
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 made a note to track this TODO in #2877
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 guess that's now happening in bootstrap.js
, so maybe this todo is done now. It took me a while to figure out that bootstrap was that place.
trace(`onSwapoutFail`); | ||
assert( | ||
error.message.match(AutoswapInsufficientMsg), |
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.
good riddance to that one. errors are best treated as sealed to normal callers.
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.
Hmm. I don't have that rule of thumb in my repertoire. Why is it a mistake to say "if that call failed for this reason, here's a fallback plan"?
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.
That might not be a mistake, but there's a slippery slope to peeking at "this reason" and getting stuff that you shouldn't; it was enough of a risk that Joe-E ruled out catching exceptions altogether.
@erights I'm not sure we have a good answer to this question documented. The closest I can find is:
A pervasive concern is hiding diagnostic information—both for confidentiality and for deterministic replay. Code that obtains access to an error object, for example by catching it, should not have access this hidden diagnostic information. -- https://github.com/endojs/endo/tree/master/packages/ses/src/error#hiding-and-revealing-local-diagnostic-information
The preferred idiom in E was ejectors: if you think something might fail, you pass in an ejector that the callee can use to signal that yup, it failed for one of the expected reasons. The callee can pass args to the ejector in the clear. Adding those to agoric-sdk / endo is on the todo list endojs/endo#414 ->
https://github.com/Agoric/SES/blob/0.5.0/src/old/ejectorsGuardsTrademarks.js
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.
Ooh, I like the idea of ejectors. Declaring code to be executed in case of particular expected exceptions seems a cleaner way to deal with this.
// Create new governance tokens, trade them with the incoming offer for | ||
// collateral. The offer uses the keywords Collateral and Governance. | ||
// govSeat stores the collateral as Secondary. We then mint new RUN for | ||
// govSeat and store them as Central. govSeat then creates a liquidity | ||
// pool for autoswap, trading in Central and Secondary for governance | ||
// tokens as Liquidity. These governance tokens are held by govSeat |
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.
This looks like a contortion that we are well rid of too.
divideBy(getAmountOut(quote), liquidationMargin), | ||
ceilDivideBy(getAmountOut(quote), liquidationMargin), |
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 tried a little mutation testing (changed this to floorDivideBy
): our tests are not sensitive to this change.
That seems relatively important, if not critical.
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 added a test in test-stablecoin
that fails when this is floorDivideBy
and succeeds with ceilDivideBy
. The test asserts that Alice retains enough collateral that her vault doesn't get liquidated.
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.
Good stuff! Especially deleting a bunch of iffy code.
I'm comfortable with the changes that move stuff around; less so with the rounding changes. Our tests are not sensitive to changing one of the ceilMultiply
to floorMultiply
. Hm.
interest: AmountMath.make(brand, 6n), | ||
newDebt: AmountMath.make(brand, 100006n), | ||
interest: AmountMath.make(brand, 7n), | ||
newDebt: AmountMath.make(brand, 100007n), |
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.
mixing this rounding change in with this PR is hurting my brain a little. It feels like a very different angle to review from...
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.
Yeah, that turned out to be a mistake. I'll try not to mix that kind of change in for the future.
// Ask the treasury for enough RUN to fund both AMM and bank. | ||
const bootstrapPaymentValue = bankBootstrapSupply + ammDepositValue; |
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, @michaelfig , for the assist.
// NOTE: no use of the voteCreator. We'll need it to initiate votes on | ||
// changing Treasury parameters. | ||
const { treasuryCreator, _voteCreator } = await installEconomy( | ||
const { treasuryCreator, _voteCreator, ammFacets } = await installEconomy( |
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.
This voteCreator NOTE looks like a TODO; is it covered by an issue? #3924 looks close enough.
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 was intended more as a highlight. Until we connect up governance on chain, it's important to be able to tell that the facet is unused. But you're right that eventually we want to make use of it.
// We still create demo currencies, but not obviously fake ones unless | ||
// $FAKE_CURRENCIES is given. | ||
...demoIssuerEntries(noFakeCurrencies), | ||
...demoIssuers, |
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.
update / delete the comment with this change?
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 moved the call to create demoIssuers
earlier, since it wants to be shared, but this is the place that cares that rawIssuerEntries
excludes the fakes.
The tension that kept me from being able to initialize rawIssuerEntries
earlier is that it wants the bootstrapPayment, which depends on other info from issuers.js
. What a tangle!
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.
Yes, what a tangle. I suppose #4021 represents at least some of the work we should do in this area.
// TODO: what happens if unusedBankPayment doesn't have enough | ||
// remaining? |
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 we fail safe in this case (i.e. the whole thing comes crashing down) so I'm not checking for an issue.
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.
My preference would have been to not add the amount the bank wants to the bootstrap payment if there won't be a bank. I think this code would have used a mint instead of splitting payments, and the coupling would have been less and the risk I mention would go away, but that change would touch even more of this file.
@@ -58,12 +63,13 @@ test('fee distribution', async t => { | |||
const brand = brands.get('moola'); | |||
const { feeDepositFacet, getPayments } = makeFakeFeeDepositFacet(issuer); | |||
const treasury = makeFakeTreasury(); | |||
const amm = makeFakeTreasury(); |
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.
awkward naming here; I can read it as makeFakeFeeProducer
or some such, right? (not critical)
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.
Yes, that would be a better name. changed.
83792fc
to
242d94a
Compare
242d94a
to
090aee4
Compare
090aee4
to
103c16d
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.
looks good.
} | ||
|
||
return { | ||
governor: g, |
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.
Hm... setupAmm
isn't exported. So it's not possible for anyone to use governor
without changing this file. My style would be to leave this out until then, but I suppose it's not critical.
Looking around to see if there's an issue to straighten this out... #3924 looks close enough, though it sure would be nice to see a plan to actually make sense of the bootstrap process.
@@ -63,7 +129,7 @@ export async function installOnChain({ | |||
/** @type {Array<[string, SourceBundle]>} */ | |||
const nameBundles = [ | |||
['liquidate', liquidateBundle], | |||
['autoswap', autoswapBundle], | |||
['amm', ammBundle], |
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 opened #4084 in case any of us gets inspired.
trace(`onSwapoutFail`); | ||
assert( | ||
error.message.match(AutoswapInsufficientMsg), |
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.
That might not be a mistake, but there's a slippery slope to peeking at "this reason" and getting stuff that you shouldn't; it was enough of a risk that Joe-E ruled out catching exceptions altogether.
@erights I'm not sure we have a good answer to this question documented. The closest I can find is:
A pervasive concern is hiding diagnostic information—both for confidentiality and for deterministic replay. Code that obtains access to an error object, for example by catching it, should not have access this hidden diagnostic information. -- https://github.com/endojs/endo/tree/master/packages/ses/src/error#hiding-and-revealing-local-diagnostic-information
The preferred idiom in E was ejectors: if you think something might fail, you pass in an ejector that the callee can use to signal that yup, it failed for one of the expected reasons. The callee can pass args to the ejector in the clear. Adding those to agoric-sdk / endo is on the todo list endojs/endo#414 ->
https://github.com/Agoric/SES/blob/0.5.0/src/old/ejectorsGuardsTrademarks.js
// We still create demo currencies, but not obviously fake ones unless | ||
// $FAKE_CURRENCIES is given. | ||
...demoIssuerEntries(noFakeCurrencies), | ||
...demoIssuers, |
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.
Yes, what a tangle. I suppose #4021 represents at least some of the work we should do in this area.
See also: Agoric/agoric-sdk#4026 So far, I've updated the README to discard fungible faucet and add directions for dealing with Zoe fees.
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 don't understand why Promise.all
won't work.
// await deposited, but we don't need the value. We'll need it to have | ||
// resolved in both branches, so can't put it in Promise.all. | ||
await deposited; | ||
await E(liqSeat).getOfferResult(); |
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 don't understand why Promise.all
doesn't work; is there an ordering constraint between the two?
// await deposited, but we don't need the value. We'll need it to have | |
// resolved in both branches, so can't put it in Promise.all. | |
await deposited; | |
await E(liqSeat).getOfferResult(); | |
await Promise.all([deposited, E(liqSeat).getOfferResult()]); |
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.
There is an ordering constraint. deposited is a promise that resolves when offerTo
's transfer is complete, and thus it's safe to call getOfferResult
.
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.
OK; I didn't get that from "We'll need it to have resolved in both branches"; maybe rephrase the code comment to state the ordering constraint as you did here?
4a9c831
to
ebc7865
Compare
The name of the AMM changed from autoswap to amm in Agoric/agoric-sdk#4026. Pick whichever is available for backwards compatibility
Vaults switch to using constantProduct AMM Rewards are no longer collected from the AMM in the Vaults Vaults consistently specify rounding mode with Ratios Vaults is unaware of AMM governed params Vaults uses feeMintAccess for access to RUN mint governance tokens in Vaults are no longer supported collect fees from AMM rename autoswap to amm pervasively install-on-chain needed some help initializing AMM pools, since the RUN funds had toj come from somewhere different
add a test remove some outdated comments deposit more plausible values in AMM pools
ebc7865
to
d80429c
Compare
Mostly adding missing harden() calls. Also added collateralIssuer to startInstance() (!)
closes: #3861
closes: #3019
closes: #4032
closes: #2877
Description
Vaults switch to using constantProduct AMM
Rewards are no longer collected from the AMM in the Vaults
Vaults consistently specify rounding mode with Ratios
Vaults is unaware of AMM governed params
Vaults uses feeMintAccess for access to RUN mint
governance tokens in Vaults are no longer supported
fix up install-on-chain
Security Considerations
The normal for a core part of our economy
Documentation Considerations
Documentation for the constantProduct AMM is in 566
An update to Treasury docs has been prepared.
Testing Considerations
tests are included and updated.