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

added features to priceAggregatorChainlink #6752

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 89 additions & 50 deletions packages/inter-protocol/src/priceAggregatorChainlink.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,12 @@ const start = async (zcf, privateArgs) => {
/**
* @typedef {object} RoundDetails
* @property {bigint[]} submissions
* @property {String[]} submissionsBy
* @property {number} maxSubmissions
* @property {number} minSubmissions
* @property {number} roundTimeout
* @property {Timestamp} startedAt
* @property {string} startedBy
*/
/** @type {MapStore<bigint, RoundDetails>} */
const details = makeScalarBigMapStore('details', { durable: true });
Expand Down Expand Up @@ -343,6 +346,7 @@ const start = async (zcf, privateArgs) => {

const roundTimedOut = timedOut(roundId, blockTimestamp);
if (!roundTimedOut) return;
console.log("round " + roundId + " timed out")

const prevRound = rounds.get(subtract(roundId, 1));

Expand All @@ -360,14 +364,17 @@ const start = async (zcf, privateArgs) => {
* @param {bigint} roundId
*/
const newRound = roundId => {
return roundId === add(reportingRoundId, 1);
let createRound = roundId === add(reportingRoundId, 1)
console.log("creating new round", roundId, createRound)
return createRound;
};

/**
* @param {bigint} roundId
* @param {Timestamp} blockTimestamp
* @param {string} oracleAddr
*/
const initializeNewRound = (roundId, blockTimestamp) => {
const initializeNewRound = (roundId, blockTimestamp, oracleAddr) => {
newRound(roundId) || Fail`Round ${roundId} already started`;

updateTimedOutRoundInfo(subtract(roundId, 1), blockTimestamp);
Expand All @@ -376,15 +383,20 @@ const start = async (zcf, privateArgs) => {
latestRoundPublisher.publish({
roundId: reportingRoundId,
startedAt: blockTimestamp,
startedBy: oracleAddr,
submissionsBy: []
});

details.init(
roundId,
harden({
submissions: [],
submissionsBy: [],
maxSubmissions: maxSubmissionCount,
minSubmissions: minSubmissionCount,
roundTimeout: timeout,
startedAt: blockTimestamp,
startedBy: oracleAddr
}),
);

Expand All @@ -397,21 +409,30 @@ const start = async (zcf, privateArgs) => {
answeredInRound: 0n,
}),
);

rounds.set(roundId, {
...rounds.get(roundId),
startedAt: blockTimestamp,
});
};

/**
* @param {bigint} roundId
* @param {string} oracleId
* @param {string} oracleAddr
* @param {Timestamp} blockTimestamp
*/
const proposeNewRound = (roundId, oracleId, blockTimestamp) => {
const proposeNewRound = (roundId, oracleAddr, blockTimestamp) => {
if (!newRound(roundId)) return;
const lastStarted = oracleStatuses.get(oracleId).lastStartedRound; // cache storage reads
if (roundId <= add(lastStarted, restartDelay) && lastStarted !== 0n) return;
initializeNewRound(roundId, blockTimestamp);
const lastStarted = oracleStatuses.get(oracleAddr).lastStartedRound; // cache storage reads
if (roundId <= add(lastStarted, restartDelay) && lastStarted !== 0n) {
console.log("Cannot propose round.")
console.log("Restart delay -", restartDelay, "Last started -", lastStarted)
return;
}
initializeNewRound(roundId, blockTimestamp, oracleAddr);

oracleStatuses.set(oracleId, {
...oracleStatuses.get(oracleId),
oracleStatuses.set(oracleAddr, {
...oracleStatuses.get(oracleAddr),
lastStartedRound: roundId,
});
};
Expand All @@ -426,22 +447,33 @@ const start = async (zcf, privateArgs) => {
/**
* @param {bigint} submission
* @param {bigint} roundId
* @param {string} oracleId
* @param {string} oracleAddr
*/
const recordSubmission = (submission, roundId, oracleId) => {
const recordSubmission = (submission, roundId, oracleAddr) => {
acceptingSubmissions(roundId) || Fail`round not accepting submissions`;

console.error("Value to be submitted", submission);
const lastRoundDetails = details.get(roundId);
details.set(roundId, {
...lastRoundDetails,
submissions: [...lastRoundDetails.submissions, submission],
submissionsBy: [...lastRoundDetails.submissionsBy, oracleAddr]
});

oracleStatuses.set(oracleId, {
...oracleStatuses.get(oracleId),
oracleStatuses.set(oracleAddr, {
...oracleStatuses.get(oracleAddr),
lastReportedRound: roundId,
latestSubmission: submission,
})

//update vstorage
latestRoundPublisher.publish({
roundId: roundId,
startedAt: details.get(roundId).startedAt,
startedBy: details.get(roundId).startedBy,
submissionsBy: details.get(roundId).submissionsBy
});
return true;
};

/**
Expand Down Expand Up @@ -473,6 +505,7 @@ const start = async (zcf, privateArgs) => {
answeredInRound: roundId,
});

console.error("newAnswer", newAnswer);
lastValueOutForUnitIn = newAnswer;
answerUpdator.updateState(undefined);

Expand Down Expand Up @@ -525,14 +558,14 @@ const start = async (zcf, privateArgs) => {
};

/**
* @param {string} oracleId
* @param {string} oracleAddr
* @param {bigint} roundId
* @param {Timestamp} blockTimestamp
* @returns {string?} error message, if there is one
*/
const validateOracleRound = (oracleId, roundId, blockTimestamp) => {
const validateOracleRound = (oracleAddr, roundId, blockTimestamp) => {
// cache storage reads
const startingRound = oracleStatuses.get(oracleId).startingRound;
const startingRound = oracleStatuses.get(oracleAddr).startingRound;
const rrId = reportingRoundId;

let canSupersede = true;
Expand All @@ -542,9 +575,9 @@ const start = async (zcf, privateArgs) => {

if (startingRound === 0n) return 'not enabled oracle';
if (startingRound > roundId) return 'not yet enabled oracle';
if (oracleStatuses.get(oracleId).endingRound < roundId)
if (oracleStatuses.get(oracleAddr).endingRound < roundId)
return 'no longer allowed oracle';
if (oracleStatuses.get(oracleId).lastReportedRound >= roundId)
if (oracleStatuses.get(oracleAddr).lastReportedRound >= roundId)
return 'cannot report on previous rounds';
if (
roundId !== rrId &&
Expand All @@ -558,23 +591,23 @@ const start = async (zcf, privateArgs) => {
};

/**
* @param {string} oracleId
* @param {string} oracleAddr
* @param {bigint} roundId
*/
const delayed = (oracleId, roundId) => {
const lastStarted = oracleStatuses.get(oracleId).lastStartedRound;
const delayed = (oracleAddr, roundId) => {
const lastStarted = oracleStatuses.get(oracleAddr).lastStartedRound;
return roundId > add(lastStarted, restartDelay) || lastStarted === 0n;
};

/**
* a method to provide all current info oracleStatuses need. Intended only
* only to be callable by oracleStatuses. Not for use by contracts to read state.
*
* @param {string} oracleId
* @param {string} oracleAddr
* @param {Timestamp} blockTimestamp
*/
const oracleRoundStateSuggestRound = (oracleId, blockTimestamp) => {
const oracle = oracleStatuses.get(oracleId);
const oracleRoundStateSuggestRound = (oracleAddr, blockTimestamp) => {
const oracle = oracleStatuses.get(oracleAddr);

const shouldSupersede =
oracle.lastReportedRound === reportingRoundId ||
Expand All @@ -588,7 +621,7 @@ const start = async (zcf, privateArgs) => {
let eligibleToSubmit;
if (canSupersede && shouldSupersede) {
roundId = add(reportingRoundId, 1);
eligibleToSubmit = delayed(oracleId, roundId);
eligibleToSubmit = delayed(oracleAddr, roundId);
} else {
roundId = reportingRoundId;
eligibleToSubmit = acceptingSubmissions(roundId);
Expand All @@ -606,7 +639,7 @@ const start = async (zcf, privateArgs) => {
roundTimeout = 0;
}

const error = validateOracleRound(oracleId, roundId, blockTimestamp);
const error = validateOracleRound(oracleAddr, roundId, blockTimestamp);
if (error !== null) {
eligibleToSubmit = false;
}
Expand All @@ -622,31 +655,35 @@ const start = async (zcf, privateArgs) => {
};

/**
* @param {string} oracleId
* @param {string} oracleAddr
* @param {bigint} queriedRoundId
* @param {Timestamp} blockTimestamp
*/
const eligibleForSpecificRound = (
oracleId,
oracleAddr,
queriedRoundId,
blockTimestamp,
) => {
const error = validateOracleRound(oracleId, queriedRoundId, blockTimestamp);
const error = validateOracleRound(
oracleAddr,
queriedRoundId,
blockTimestamp,
);
if (TimeMath.absValue(rounds.get(queriedRoundId).startedAt) > 0n) {
return acceptingSubmissions(queriedRoundId) && error === null;
} else {
return delayed(oracleId, queriedRoundId) && error === null;
return delayed(oracleAddr, queriedRoundId) && error === null;
}
};

/**
* @param {string} oracleId
* @param {string} oracleAddr
*/
const getStartingRound = oracleId => {
const getStartingRound = oracleAddr => {
const currentRound = reportingRoundId;
if (
currentRound !== 0n &&
currentRound === oracleStatuses.get(oracleId).endingRound
currentRound === oracleStatuses.get(oracleAddr).endingRound
) {
return currentRound;
}
Expand All @@ -662,19 +699,20 @@ const start = async (zcf, privateArgs) => {
* directly to manage the price submissions as well as to terminate the
* relationship.
*
* @param {string} oracleId unique per contract instance
* @param {string} oracleAddr Bech32 of oracle operator smart wallet
Comment on lines -665 to +702
Copy link
Member

Choose a reason for hiding this comment

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

The caller of this contract may use an address for the oracleId, but the contract doesn't depend on that. See earlier discussion: #6629 (comment)

*/
makeOracleInvitation: async oracleId => {
makeOracleInvitation: async oracleAddr => {
/**
* If custom arguments are supplied to the `zoe.offer` call, they can
* indicate an OraclePriceSubmission notifier and a corresponding
* `shiftValueOut` that should be adapted as part of the priceAuthority's
* reported data.
*
* @param {ZCFSeat} seat
* @returns {Promise<{admin: OracleAdmin<PriceRound>, invitationMakers: {makePushPriceInvitation: (result: PriceRound) => Promise<Invitation<void>>} }>}
*/
const offerHandler = async seat => {
const admin = await creatorFacet.initOracle(oracleId);
const admin = await creatorFacet.initOracle(oracleAddr);
const invitationMakers = Far('invitation makers', {
/** @param {PriceRound} result */
PushPrice(result) {
Expand All @@ -700,16 +738,17 @@ const start = async (zcf, privateArgs) => {
oracleStatuses.delete(oracleId);
},

// unlike the median case, no query argument is passed, since polling behavior is undesired
/**
* @param {string} oracleId unique per contract instance
* @param {string} oracleAddr uBech32 of oracle operator smart wallet
*/
async initOracle(oracleId) {
assert.typeof(oracleId, 'string');
async initOracle(oracleAddr) {
assert.typeof(oracleAddr, 'string');

oracleStatuses.init(
oracleId,
oracleAddr,
harden({
startingRound: getStartingRound(oracleId),
startingRound: getStartingRound(oracleAddr),
endingRound: ROUND_MAX,
lastReportedRound: 0n,
lastStartedRound: 0n,
Expand Down Expand Up @@ -740,7 +779,7 @@ const start = async (zcf, privateArgs) => {
let roundId;
if (roundIdRaw === undefined) {
const suggestedRound = oracleRoundStateSuggestRound(
oracleId,
oracleAddr,
blockTimestamp,
);
roundId = suggestedRound.eligibleForSpecificRound
Expand All @@ -751,7 +790,7 @@ const start = async (zcf, privateArgs) => {
}

const errorMsg = validateOracleRound(
oracleId,
oracleAddr,
roundId,
blockTimestamp,
);
Expand All @@ -760,8 +799,8 @@ const start = async (zcf, privateArgs) => {
assert.fail(errorMsg);
}

proposeNewRound(roundId, oracleId, blockTimestamp);
recordSubmission(value, roundId, oracleId);
proposeNewRound(roundId, oracleAddr, blockTimestamp);
recordSubmission(value, roundId, oracleAddr)

updateRoundAnswer(roundId, blockTimestamp);
deleteRoundDetails(roundId);
Expand Down Expand Up @@ -801,28 +840,28 @@ const start = async (zcf, privateArgs) => {
* a method to provide all current info oracleStatuses need. Intended only
* only to be callable by oracleStatuses. Not for use by contracts to read state.
*
* @param {string} oracleId unique per contract instance
* @param {string} oracleAddr Bech32 of oracle operator smart wallet
* @param {bigint} queriedRoundId
*/
async oracleRoundState(oracleId, queriedRoundId) {
async oracleRoundState(oracleAddr, queriedRoundId) {
const blockTimestamp = await E(timer).getCurrentTimestamp();
if (queriedRoundId > 0) {
const round = rounds.get(queriedRoundId);
const detail = details.get(queriedRoundId);
return {
eligibleForSpecificRound: eligibleForSpecificRound(
oracleId,
oracleAddr,
queriedRoundId,
blockTimestamp,
),
queriedRoundId,
oracleStatus: oracleStatuses.get(oracleId).latestSubmission,
oracleStatus: oracleStatuses.get(oracleAddr).latestSubmission,
startedAt: round.startedAt,
roundTimeout: detail.roundTimeout,
oracleCount: oracleCount(),
};
} else {
return oracleRoundStateSuggestRound(oracleId, blockTimestamp);
return oracleRoundStateSuggestRound(oracleAddr, blockTimestamp);
}
},
});
Expand Down