-
Notifications
You must be signed in to change notification settings - Fork 212
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
make Chainlink the canonical priceAggregator #6629
Conversation
30f8d44
to
c905450
Compare
4cfd391
to
6ff21e3
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.
To what extent does closing #6628 mean claiming we're done with this contract?
Comparing the scope of this to the 5 tasks listed in #6571, it seems to claim to address:
- make it support continuing invitation
- FIXMEs (e.g. We throw away the updater but shouldn’t)
- wire it into integration tests and fix whatever else comes up
but not these:
- make durable
- replace makeLegacyMap usage
I'm also curious whether...
... is in scope.
Is there more vstorage work to do after this? (#5854)
I suppose #5060 is likely to impact only the vaultFactory contract and not this one.
bin/agops oracle pushPrice --price 1.01 --oracleAdminAcceptOfferId "$ORACLE_OFFER_ID" >|"$PROPOSAL_OFFER" | ||
bin/agops oracle pushPriceRound --price 101 --roundId 1 --oracleAdminAcceptOfferId "$ORACLE_OFFER_ID" >|"$PROPOSAL_OFFER" |
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 makes me wonder whether the oracle operators aware of quoting prices in integers without decimals. I'm looking around.
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.
The vstorage path segment is still ATOM-USD_price_feed
. Is the number of decimals available at a nearby vstorage key?
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.
Not presently. I expect the proposal config to suffice:
agoric-sdk/packages/inter-protocol/src/proposals/price-feed-proposal.js
Lines 292 to 296 in a3962a7
priceFeedOptions: { | |
IN_BRAND_NAME: inBrandName, | |
IN_BRAND_DECIMALS: '6', | |
OUT_BRAND_NAME: outBrandName, | |
OUT_BRAND_DECIMALS: '6', |
minSubmissionCount: 2, | ||
minSubmissionValue: 1, | ||
maxSubmissionCount: 5, | ||
maxSubmissionValue: 99999, |
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.
What's this limitation of 100k - 1? I aim to look around a bit more...
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.
Nothing purposeful. It's much bigger in
maxSubmissionValue: 2n ** 256n, |
Any suggestions?
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 the purpose of the limitation. What is it limiting? What bad thing might happen if there's no limit? Why is 100k a plausible number?
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.
What is it limiting?
It's limiting the maximum value in a submission.
I don't understand the purpose of the limitation.
One is that it came in the port of https://github.com/smartcontractkit/chainlink/blob/b045416ebca769aa69bde2da23b5109abe07a8b5/contracts/src/v0.6/FluxAggregator.sol#L155-L156
What bad thing might happen if there's no limit?
If enough oracles were compromised they could push the median up to a ludicrous price. Or if there is a software bug affecting all of them it wouldn't even require malice. TMK it's a hedge against run-away values.
Why is 100k a plausible number?
I don't know that it is. This code isn't of the economics or risk, just the functionality. Isn't that sufficient?
import { INVITATION_MAKERS_DESC } from '@agoric/zoe/src/contracts/priceAggregator.js'; | ||
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; | ||
import { AmountMath } from '@agoric/ertp'; | ||
import { coalesceUpdates } from '@agoric/smart-wallet/src/utils.js'; | ||
import { INVITATION_MAKERS_DESC } from '@agoric/zoe/src/contracts/priceAggregatorChainlink.js'; | ||
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; | ||
import { ensureOracleBrands } from '../../src/proposals/price-feed-proposal.js'; | ||
import { makeDefaultTestContext } from './contexts.js'; | ||
import { headValue } from '../supports.js'; | ||
import { makeDefaultTestContext } from './contexts.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.
Is there anything to this change besides line order? I can't tell. Is there some motivation for changing the order?
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 was some import to remove and I used Organize imports in VS Code (from TypeScript LSP)
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.
convention calls for capitalizing makePushPriceInvitation
and I don't believe oracleAddr
is actually always a string. The tests seem to use an instance.
I haven't tried reproducing the integration testing results; that's next...
const aggregatorInstallation = await E(zoe).installBundleID( | ||
'b1-aggregator', | ||
); | ||
const defaultConfig = { |
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.
Should defaultConfig
be exposed more widely? Is it something operators should know about? Or is it just arbitrary test constants?
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.
arbitrary test constants. "default" merely for this module's tests. I think the "in this test module" is implied but let me know if you think it needs a comment or something
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's fine.
I was just asking about the possibility that the numbers are not arbitrary and in fact have some relevance to production operation.
await E(pricePushAdminA).pushResult({ roundId: 1, unitPrice: 100n }); | ||
await E(pricePushAdminB).pushResult({ roundId: 1, unitPrice: 200n }); | ||
await E(pricePushAdminC).pushResult({ roundId: 1, unitPrice: 300n }); | ||
await oracleTimer.tick(); | ||
|
||
const round1Attempt1 = await E(aggregator.creatorFacet).getRoundData(1); |
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.
100, 200, and 300 seem pretty far apart; does the aggregator consume them no matter how far apart they are? Or is there a point where it says "sources don't agree; nothing available at this time"?
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.
Right, it lets the prices push as long as they're within the minSubmissionValue
and maxSubmissionValue
. Extremity of gap doesn't matter because the feed only exposes the median.
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'm missing something about how scale works in this contract. DAI has 18 decimals. the .value
of an ERTP amount of 1 DAI is bigger than 100K. How could this possibly work?
* @param {Timestamp} blockTimestamp | ||
*/ | ||
const oracleRoundStateSuggestRound = (_oracle, blockTimestamp) => { | ||
const oracle = oracleStatuses.get(_oracle); | ||
const oracleRoundStateSuggestRound = (oracleAddr, blockTimestamp) => { |
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 wonder what an oracleAddr
is... I think it's: an address to which an invitation to this contract was sent. Why would this contract know the addresses? hm.
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 wonder what an oracleAddr is... I think it's: an address to which an invitation to this contract was sent.
Correct. I'll make clearer by documentation.
Why would this contract know the addresses?
It needs to know the oracles and it's keyed on their public address. What's the concern?
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.
The fact that the .instance
test works suggests that the keys don't need to be addresses of the validators; the validators could be identified as 'A', 'B', and 'C' and the contract would work fine, yes? The convention of using addresses seems to be external to the contract.
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.
Oh yes, good point. I'll change it to be oracleKey.
/** | ||
* An "oracle invitation" is an invitation to be able to submit data to | ||
* include in the priceAggregator's results. | ||
* | ||
* The offer result from this invitation is a OracleAdmin, which can be used | ||
* directly to manage the price submissions as well as to terminate the | ||
* relationship. | ||
* | ||
* @param {string} oracleAddr | ||
*/ | ||
makeOracleInvitation: async oracleAddr => { |
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.
how refreshing! docs!
* reported data. | ||
* | ||
* @param {ZCFSeat} seat | ||
* @returns {Promise<{admin: OracleAdmin<PriceRound>, invitationMakers: {makePushPriceInvitation: (result: PriceRound) => Promise<Invitation<void>>} }>} |
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.
* @returns {Promise<{admin: OracleAdmin<PriceRound>, invitationMakers: {makePushPriceInvitation: (result: PriceRound) => Promise<Invitation<void>>} }>} | |
* @returns {Promise<{admin: OracleAdmin<PriceRound>, invitationMakers: {MakePushPriceInvitation: (result: PriceRound) => Promise<Invitation<void>>} }>} |
The sparsely-documented continuing invitation pattern seems to including capitalizing the fields of invitation makers. For example:
invitationMakers: Far('invitation makers', {
AdjustBalances: vault.makeAdjustBalancesInvitation,
CloseVault: vault.makeCloseInvitation,
TransferVault: vault.makeTransferInvitation,
}),
I think that's to avoid colliding with existing property names, as noted for assertKeywordName
in Zoe.
I can imagine this argument is not very strong, but if we're not going to follow the pattern in this respect, I would want to discuss it more widely.
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 the AdjustBalances
example suggests just PushPrice
.
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.
Ah... and I think the method name is supposed to match the invitation description, which is also PushPrice
.
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 call. invitationMakers.makePushPriceInvitation
is redundant. Changed to invitationMakers.PushPrice
.
/** @param {string} oracleAddr */ | ||
async initOracle(oracleAddr) { |
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.
Is this really a string? The tests suggest otherwise:
const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
);
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 raising. That test was written for the agsolo era. We only support smart wallet oracles now so I've amended to make that clear. This is also necessary for durability.
6ff21e3
to
d0ef5e9
Compare
d0ef5e9
to
8e61373
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.
let's go.
I'm fuzzy on some of the details. And I didn't reproduce the scripted integration test. But... onward.
closes: #6628
part of #6571
Description
Make
priceAggregatorChainlink
the price aggregator contract. Updates CLI command and smoketest script to use the new result struct.Updates the contract itself for code quality and compatibility with the Smart Wallet (using continuing invitation pattern and keying oracles by wallet address).
Security Considerations
Increases security by implementing round constraints on oracle price pushes. No new privileges.
Documentation Considerations
This changes the
priceAggregator
to be Chainlink style, which is different than https://github.com/Agoric/dapp-oracle/ expects. OTOH it's already broken and serves mostly an illustration instead of a functioning dapp.Testing Considerations
Tested manually with https://github.com/Agoric/agoric-sdk/blob/6ff21e34936bdaad17ecd3d0e40dc7493a75cac3/packages/agoric-cli/test/agops-oracle-smoketest.sh