@agoric/inter-protocol 0.13.3 → 0.13.4-dev-800c2bb.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -20
- package/bundles/bundle-auctioneer-js-meta.json +722 -0
- package/bundles/bundle-auctioneer.js +1 -0
- package/bundles/bundle-econCommitteeCharter-js-meta.json +514 -0
- package/bundles/bundle-econCommitteeCharter.js +1 -0
- package/bundles/bundle-feeDistributor-js-meta.json +458 -0
- package/bundles/bundle-feeDistributor.js +1 -1
- package/bundles/bundle-fluxAggregatorKit-js-meta.json +674 -0
- package/bundles/bundle-fluxAggregatorKit.js +1 -0
- package/bundles/bundle-psm-js-meta.json +666 -0
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-js-meta.json +662 -0
- package/bundles/bundle-reserve.js +1 -1
- package/bundles/bundle-scaledPriceAuthority-js-meta.json +542 -0
- package/bundles/bundle-scaledPriceAuthority.js +1 -1
- package/bundles/bundle-vaultFactory-js-meta.json +786 -0
- package/bundles/bundle-vaultFactory.js +1 -1
- package/exported.js +0 -2
- package/package.json +34 -31
- package/scripts/add-collateral-core.js +14 -4
- package/scripts/build-bundles.js +9 -2
- package/scripts/deploy-contracts.js +1 -4
- package/scripts/init-core.js +20 -38
- package/scripts/invite-committee-core.js +1 -1
- package/scripts/manual-price-feed.js +2 -2
- package/scripts/price-feed-core.js +7 -6
- package/scripts/start-local-chain.sh +84 -0
- package/src/auction/auctionBook.js +755 -0
- package/src/auction/auctioneer.js +720 -0
- package/src/auction/offerBook.js +221 -0
- package/src/auction/params.js +174 -0
- package/src/auction/scheduleMath.js +167 -0
- package/src/auction/scheduler.js +370 -0
- package/src/auction/sortedOffers.js +136 -0
- package/src/auction/util.js +55 -0
- package/src/clientSupport.js +324 -0
- package/src/collectFees.js +8 -14
- package/src/contractSupport.js +24 -68
- package/src/econCommitteeCharter.js +161 -77
- package/src/feeDistributor.js +60 -20
- package/src/index.js +2 -0
- package/src/interest-math.js +1 -4
- package/src/interest.js +20 -13
- package/src/price/README.md +57 -0
- package/src/price/fluxAggregatorContract.js +190 -0
- package/src/price/fluxAggregatorKit.js +375 -0
- package/src/price/priceOracleKit.js +127 -0
- package/src/price/roundsManager.js +726 -0
- package/src/proposals/addAssetToVault.js +89 -94
- package/src/proposals/committee-proposal.js +141 -40
- package/src/proposals/core-proposal.js +100 -167
- package/src/proposals/econ-behaviors.js +320 -564
- package/src/proposals/price-feed-proposal.js +175 -78
- package/src/proposals/startEconCommittee.js +25 -12
- package/src/proposals/startPSM.js +231 -211
- package/src/proposals/utils.js +31 -17
- package/src/psm/README.md +71 -0
- package/src/psm/psm.js +272 -160
- package/src/psm/types.js +2 -0
- package/src/reserve/assetReserve.js +56 -413
- package/src/reserve/assetReserveKit.js +303 -0
- package/src/reserve/doc.md +1 -3
- package/src/reserve/params.js +2 -5
- package/src/typeGuards.js +13 -0
- package/src/vaultFactory/Liquidation.md +100 -0
- package/src/vaultFactory/burn.js +2 -1
- package/src/vaultFactory/liquidation.js +270 -108
- package/src/vaultFactory/math.js +95 -0
- package/src/vaultFactory/orderedVaultStore.js +0 -1
- package/src/vaultFactory/params.js +133 -112
- package/src/vaultFactory/prioritizedVaults.js +36 -79
- package/src/vaultFactory/storeUtils.js +67 -9
- package/src/vaultFactory/type-imports.js +2 -0
- package/src/vaultFactory/types.js +22 -26
- package/src/vaultFactory/vault.js +772 -709
- package/src/vaultFactory/vaultDirector.js +480 -483
- package/src/vaultFactory/vaultFactory.js +99 -60
- package/src/vaultFactory/vaultHolder.js +121 -95
- package/src/vaultFactory/vaultKit.js +39 -31
- package/src/vaultFactory/vaultManager.js +1249 -868
- package/bundles/bundle-amm-meta.js +0 -618
- package/bundles/bundle-amm.js +0 -1
- package/bundles/bundle-feeDistributor-meta.js +0 -346
- package/bundles/bundle-interchainPool-meta.js +0 -534
- package/bundles/bundle-interchainPool.js +0 -1
- package/bundles/bundle-liquidateIncrementally-meta.js +0 -442
- package/bundles/bundle-liquidateIncrementally.js +0 -1
- package/bundles/bundle-liquidateMinimum-meta.js +0 -442
- package/bundles/bundle-liquidateMinimum.js +0 -1
- package/bundles/bundle-psm-meta.js +0 -538
- package/bundles/bundle-psmCharter-meta.js +0 -418
- package/bundles/bundle-psmCharter.js +0 -1
- package/bundles/bundle-reserve-meta.js +0 -542
- package/bundles/bundle-scaledPriceAuthority-meta.js +0 -442
- package/bundles/bundle-stakeFactory-meta.js +0 -574
- package/bundles/bundle-stakeFactory.js +0 -1
- package/bundles/bundle-vaultFactory-meta.js +0 -598
- package/scripts/add-initial-liquidity.js +0 -180
- package/src/collect.js +0 -13
- package/src/interchainPool.js +0 -135
- package/src/makeTracer.js +0 -36
- package/src/my-lien.js +0 -85
- package/src/proposals/demoIssuers.js +0 -685
- package/src/proposals/sim-behaviors.js +0 -40
- package/src/psm/psmCharter.js +0 -139
- package/src/stakeFactory/README.md +0 -62
- package/src/stakeFactory/attestation.js +0 -267
- package/src/stakeFactory/attestationTool.js +0 -66
- package/src/stakeFactory/constants.js +0 -16
- package/src/stakeFactory/params.js +0 -31
- package/src/stakeFactory/stakeFactory.js +0 -236
- package/src/stakeFactory/stakeFactoryKit.js +0 -455
- package/src/stakeFactory/stakeFactoryManager.js +0 -296
- package/src/stakeFactory/types.js +0 -42
- package/src/vaultFactory/liquidateIncrementally.js +0 -350
- package/src/vaultFactory/liquidateMinimum.js +0 -100
- package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
- package/src/vpool-xyk-amm/addLiquidity.js +0 -222
- package/src/vpool-xyk-amm/addPool.js +0 -197
- package/src/vpool-xyk-amm/constantProduct/AMM-Trade.jpeg +0 -0
- package/src/vpool-xyk-amm/constantProduct/README.md +0 -204
- package/src/vpool-xyk-amm/constantProduct/calcFees.js +0 -99
- package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -85
- package/src/vpool-xyk-amm/constantProduct/core.js +0 -153
- package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -5
- package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -52
- package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -116
- package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -43
- package/src/vpool-xyk-amm/constantProduct/swap.js +0 -272
- package/src/vpool-xyk-amm/constantProduct/types.js +0 -32
- package/src/vpool-xyk-amm/doublePool.js +0 -194
- package/src/vpool-xyk-amm/estimate.js +0 -155
- package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -520
- package/src/vpool-xyk-amm/params.js +0 -61
- package/src/vpool-xyk-amm/pool.js +0 -396
- package/src/vpool-xyk-amm/priceAuthority.js +0 -94
- package/src/vpool-xyk-amm/removeLiquidity.js +0 -31
- package/src/vpool-xyk-amm/singlePool.js +0 -73
- package/src/vpool-xyk-amm/swap.js +0 -69
- package/src/vpool-xyk-amm/types.js +0 -154
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// @jessie-check
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
hasIssuer,
|
|
5
|
+
makeDurableIssuerKit,
|
|
6
|
+
prepareIssuerKit,
|
|
7
|
+
} from '@agoric/ertp';
|
|
8
|
+
import { handleParamGovernance } from '@agoric/governance';
|
|
9
|
+
import { makeTracer, StorageNodeShape } from '@agoric/internal';
|
|
10
|
+
import { prepareDurablePublishKit } from '@agoric/notifier';
|
|
11
|
+
import { M } from '@agoric/store';
|
|
12
|
+
import { provideAll } from '@agoric/zoe/src/contractSupport/durability.js';
|
|
13
|
+
import { prepareRecorder } from '@agoric/zoe/src/contractSupport/recorder.js';
|
|
14
|
+
import { E } from '@endo/eventual-send';
|
|
15
|
+
import { reserveThenDeposit } from '../proposals/utils.js';
|
|
16
|
+
import { prepareFluxAggregatorKit } from './fluxAggregatorKit.js';
|
|
17
|
+
|
|
18
|
+
const trace = makeTracer('FluxAgg');
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {import('@agoric/vat-data').Baggage} Baggage
|
|
21
|
+
* @typedef {import('@agoric/time/src/types').TimerService} TimerService
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export const privateArgsShape = M.splitRecord(
|
|
25
|
+
harden({
|
|
26
|
+
storageNode: StorageNodeShape,
|
|
27
|
+
marshaller: M.eref(M.remotable('marshaller')),
|
|
28
|
+
namesByAddressAdmin: M.any(),
|
|
29
|
+
}),
|
|
30
|
+
harden({
|
|
31
|
+
// always optional. XXX some code is including the key, set to null
|
|
32
|
+
highPrioritySendersManager: M.or(
|
|
33
|
+
M.remotable('prioritySenders manager'),
|
|
34
|
+
M.null(),
|
|
35
|
+
),
|
|
36
|
+
// only necessary on first invocation, not subsequent
|
|
37
|
+
initialPoserInvitation: M.remotable('Invitation'),
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* PriceAuthority for their median. Unlike the simpler `priceAggregator.js`, this approximates
|
|
43
|
+
* the *Node Operator Aggregation* logic of [Chainlink price
|
|
44
|
+
* feeds](https://blog.chain.link/levels-of-data-aggregation-in-chainlink-price-feeds/).
|
|
45
|
+
*
|
|
46
|
+
* @param {ZCF<import('./fluxAggregatorKit.js').ChainlinkConfig & {
|
|
47
|
+
* timer: TimerService,
|
|
48
|
+
* brandIn: Brand<'nat'>,
|
|
49
|
+
* brandOut: Brand<'nat'>,
|
|
50
|
+
* description: string,
|
|
51
|
+
* unitAmountIn?: Amount<'nat'>,
|
|
52
|
+
* }>} zcf
|
|
53
|
+
* @param {{
|
|
54
|
+
* highPrioritySendersManager?: import('@agoric/internal/src/priority-senders.js').PrioritySendersManager,
|
|
55
|
+
* initialPoserInvitation: Invitation,
|
|
56
|
+
* marshaller: ERef<Marshaller>,
|
|
57
|
+
* namesByAddressAdmin: ERef<import('@agoric/vats').NameAdmin>,
|
|
58
|
+
* storageNode: StorageNode,
|
|
59
|
+
* }} privateArgs
|
|
60
|
+
* @param {Baggage} baggage
|
|
61
|
+
*/
|
|
62
|
+
export const prepare = async (zcf, privateArgs, baggage) => {
|
|
63
|
+
trace('prepare with baggage keys', [...baggage.keys()]);
|
|
64
|
+
|
|
65
|
+
// xxx uses contract baggage as issuerBagage, assumes one issuer in this contract
|
|
66
|
+
/** @type {import('./roundsManager.js').QuoteKit} */
|
|
67
|
+
const quoteIssuerKit = hasIssuer(baggage)
|
|
68
|
+
? prepareIssuerKit(baggage)
|
|
69
|
+
: makeDurableIssuerKit(baggage, 'quote', 'set');
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
highPrioritySendersManager,
|
|
73
|
+
initialPoserInvitation,
|
|
74
|
+
marshaller,
|
|
75
|
+
namesByAddressAdmin,
|
|
76
|
+
storageNode,
|
|
77
|
+
} = privateArgs;
|
|
78
|
+
|
|
79
|
+
const { description, timer } = zcf.getTerms();
|
|
80
|
+
|
|
81
|
+
trace('awaited args');
|
|
82
|
+
|
|
83
|
+
const makeDurablePublishKit = prepareDurablePublishKit(
|
|
84
|
+
baggage,
|
|
85
|
+
'Price Aggregator publish kit',
|
|
86
|
+
);
|
|
87
|
+
const makeRecorder = prepareRecorder(baggage, marshaller);
|
|
88
|
+
|
|
89
|
+
const makeFluxAggregatorKit = await prepareFluxAggregatorKit(
|
|
90
|
+
baggage,
|
|
91
|
+
zcf,
|
|
92
|
+
timer,
|
|
93
|
+
quoteIssuerKit,
|
|
94
|
+
storageNode,
|
|
95
|
+
makeDurablePublishKit,
|
|
96
|
+
makeRecorder,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const { faKit } = await provideAll(baggage, {
|
|
100
|
+
faKit: () => makeFluxAggregatorKit(),
|
|
101
|
+
});
|
|
102
|
+
trace('got faKit', faKit);
|
|
103
|
+
|
|
104
|
+
// cannot be stored in baggage because not durable
|
|
105
|
+
// UNTIL https://github.com/Agoric/agoric-sdk/issues/4343
|
|
106
|
+
const { makeDurableGovernorFacet } = handleParamGovernance(
|
|
107
|
+
// @ts-expect-error FIXME include Governance params
|
|
108
|
+
zcf,
|
|
109
|
+
initialPoserInvitation,
|
|
110
|
+
{
|
|
111
|
+
// No governed parameters. Governance just for API methods.
|
|
112
|
+
},
|
|
113
|
+
storageNode,
|
|
114
|
+
marshaller,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
trace('got makeDurableGovernorFacet', makeDurableGovernorFacet);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Initialize a new oracle and send an invitation to control it.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} addr
|
|
123
|
+
*/
|
|
124
|
+
const addOracle = async addr => {
|
|
125
|
+
trace('addOracle', addr);
|
|
126
|
+
const invitation = await E(faKit.creator).makeOracleInvitation(addr);
|
|
127
|
+
// XXX imported from 'proposals' path
|
|
128
|
+
await reserveThenDeposit(
|
|
129
|
+
`fluxAggregator oracle ${addr}`,
|
|
130
|
+
namesByAddressAdmin,
|
|
131
|
+
addr,
|
|
132
|
+
[invitation],
|
|
133
|
+
);
|
|
134
|
+
if (highPrioritySendersManager) {
|
|
135
|
+
// eslint-disable-next-line @jessie.js/no-nested-await -- after another await
|
|
136
|
+
await E(highPrioritySendersManager).add(description, addr);
|
|
137
|
+
}
|
|
138
|
+
return `added ${addr}`;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Remove an oracle from aggregation and disable its facet.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} addr
|
|
145
|
+
*/
|
|
146
|
+
const removeOracle = async addr => {
|
|
147
|
+
trace('removeOracle', addr);
|
|
148
|
+
await E(faKit.creator).removeOracle(addr);
|
|
149
|
+
if (highPrioritySendersManager) {
|
|
150
|
+
// eslint-disable-next-line @jessie.js/no-nested-await -- after another await
|
|
151
|
+
await E(highPrioritySendersManager).remove(description, addr);
|
|
152
|
+
}
|
|
153
|
+
return `removed ${addr}`;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const governedApis = {
|
|
157
|
+
/**
|
|
158
|
+
* Add the specified oracles. May partially fail, such that some oracles are added and others aren't.
|
|
159
|
+
*
|
|
160
|
+
* @param {string[]} addrs
|
|
161
|
+
* @returns {Promise<Array<PromiseSettledResult<string>>>}
|
|
162
|
+
*/
|
|
163
|
+
addOracles: addrs => {
|
|
164
|
+
return Promise.allSettled(addrs.map(addOracle));
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Remove the specified oracles. May partially fail, such that some oracles are removed and others aren't.
|
|
168
|
+
* If the oracle was never part of the set that's a PromiseRejectedResult
|
|
169
|
+
*
|
|
170
|
+
* @param {string[]} addrs
|
|
171
|
+
* @returns {Promise<Array<PromiseSettledResult<string>>>}
|
|
172
|
+
*/
|
|
173
|
+
removeOracles: addrs => {
|
|
174
|
+
return Promise.allSettled(addrs.map(removeOracle));
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const { governorFacet } = makeDurableGovernorFacet(
|
|
179
|
+
baggage,
|
|
180
|
+
faKit.creator,
|
|
181
|
+
governedApis,
|
|
182
|
+
);
|
|
183
|
+
trace('made governorFacet', governorFacet);
|
|
184
|
+
|
|
185
|
+
return harden({
|
|
186
|
+
creatorFacet: governorFacet,
|
|
187
|
+
publicFacet: faKit.public,
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
harden(prepare);
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* Adaptation of Chainlink algorithm to the Agoric platform.
|
|
4
|
+
* Modeled on https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.6/FluxAggregator.sol (version?)
|
|
5
|
+
*/
|
|
6
|
+
import { AmountMath } from '@agoric/ertp';
|
|
7
|
+
import { assertAllDefined, makeTracer } from '@agoric/internal';
|
|
8
|
+
import {
|
|
9
|
+
makeNotifierFromSubscriber,
|
|
10
|
+
observeNotifier,
|
|
11
|
+
SubscriberShape,
|
|
12
|
+
} from '@agoric/notifier';
|
|
13
|
+
import { M, makeScalarBigMapStore, prepareExoClassKit } from '@agoric/vat-data';
|
|
14
|
+
import {
|
|
15
|
+
defineRecorderKit,
|
|
16
|
+
makeOnewayPriceAuthorityKit,
|
|
17
|
+
makeRecorderTopic,
|
|
18
|
+
provideAll,
|
|
19
|
+
} from '@agoric/zoe/src/contractSupport/index.js';
|
|
20
|
+
import { E } from '@endo/eventual-send';
|
|
21
|
+
import { Far } from '@endo/marshal';
|
|
22
|
+
import { prepareOracleAdminKit } from './priceOracleKit.js';
|
|
23
|
+
import { prepareRoundsManagerKit } from './roundsManager.js';
|
|
24
|
+
|
|
25
|
+
const trace = makeTracer('FlxAgg', true);
|
|
26
|
+
|
|
27
|
+
export const INVITATION_MAKERS_DESC = 'oracle invitation';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {import('@agoric/vat-data').Baggage} Baggage
|
|
31
|
+
* @typedef {import('@agoric/time/src/types').Timestamp} Timestamp
|
|
32
|
+
* @typedef {import('@agoric/time/src/types').RelativeTime} RelativeTime
|
|
33
|
+
* // TODO: use RelativeTime, not RelativeTimeValue
|
|
34
|
+
* @typedef {import('@agoric/time/src/types').RelativeTimeValue} RelativeTimeValue
|
|
35
|
+
* @typedef {import('@agoric/time/src/types').TimerService} TimerService
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/** @type {(quote: PriceQuote) => PriceDescription} */
|
|
39
|
+
const priceDescriptionFromQuote = quote => quote.quoteAmount.value[0];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {object} RoundState
|
|
43
|
+
* @property {boolean} eligibleForSpecificRound
|
|
44
|
+
* @property {bigint} queriedRoundId
|
|
45
|
+
* @property {bigint} latestSubmission
|
|
46
|
+
* @property {Timestamp} startedAt
|
|
47
|
+
* @property {number} roundTimeout
|
|
48
|
+
* @property {number} oracleCount
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
// Partly documented at https://github.com/smartcontractkit/chainlink/blob/b045416ebca769aa69bde2da23b5109abe07a8b5/contracts/src/v0.6/FluxAggregator.sol#L153
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {object} ChainlinkConfig
|
|
54
|
+
* @property {number} maxSubmissionCount
|
|
55
|
+
* @property {number} minSubmissionCount
|
|
56
|
+
* @property {bigint} restartDelay the number of rounds an Oracle has to wait before they can initiate a round
|
|
57
|
+
* @property {number} minSubmissionValue an immutable check for a lower bound of what
|
|
58
|
+
* submission values are accepted from an oracle
|
|
59
|
+
* @property {number} maxSubmissionValue an immutable check for an upper bound of what
|
|
60
|
+
* submission values are accepted from an oracle
|
|
61
|
+
* @property {number} timeout the number of seconds after the previous round that
|
|
62
|
+
* allowed to lapse before allowing an oracle to skip an unfinished round
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Returns a maker for a single durable FluxAggregatorKit, closed over the prepare() arguments.
|
|
67
|
+
*
|
|
68
|
+
* The kit aggregates price inputs to produce a PriceAuthority. Unlike the
|
|
69
|
+
* simpler `priceAggregator.js`, this approximates the *Node Operator
|
|
70
|
+
* Aggregation* logic of [Chainlink price
|
|
71
|
+
* feeds](https://blog.chain.link/levels-of-data-aggregation-in-chainlink-price-feeds/).
|
|
72
|
+
*
|
|
73
|
+
* @param {Baggage} baggage
|
|
74
|
+
* @param {ZCF<ChainlinkConfig & {
|
|
75
|
+
* timer: TimerService,
|
|
76
|
+
* brandIn: Brand<'nat'>,
|
|
77
|
+
* brandOut: Brand<'nat'>,
|
|
78
|
+
* unitAmountIn?: Amount<'nat'>,
|
|
79
|
+
* }>} zcf
|
|
80
|
+
* @param {TimerService} timerPresence
|
|
81
|
+
* @param {import('./roundsManager.js').QuoteKit} quoteKit
|
|
82
|
+
* @param {StorageNode} storageNode
|
|
83
|
+
* @param {() => PublishKit<any>} makeDurablePublishKit
|
|
84
|
+
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorder} makeRecorder
|
|
85
|
+
*/
|
|
86
|
+
export const prepareFluxAggregatorKit = async (
|
|
87
|
+
baggage,
|
|
88
|
+
zcf,
|
|
89
|
+
timerPresence,
|
|
90
|
+
quoteKit,
|
|
91
|
+
storageNode,
|
|
92
|
+
makeDurablePublishKit,
|
|
93
|
+
makeRecorder,
|
|
94
|
+
) => {
|
|
95
|
+
// brands come from named terms instead of `brands` key because the latter is
|
|
96
|
+
// a StandardTerm that Zoe creates from the `issuerKeywordRecord` argument and
|
|
97
|
+
// Oracle brands are inert (without issuers or mints).
|
|
98
|
+
const {
|
|
99
|
+
brandIn,
|
|
100
|
+
brandOut,
|
|
101
|
+
maxSubmissionCount,
|
|
102
|
+
maxSubmissionValue,
|
|
103
|
+
minSubmissionCount,
|
|
104
|
+
minSubmissionValue,
|
|
105
|
+
restartDelay,
|
|
106
|
+
timeout,
|
|
107
|
+
|
|
108
|
+
unitAmountIn = AmountMath.make(brandIn, 1n),
|
|
109
|
+
} = zcf.getTerms();
|
|
110
|
+
|
|
111
|
+
assertAllDefined({
|
|
112
|
+
brandIn,
|
|
113
|
+
brandOut,
|
|
114
|
+
maxSubmissionCount,
|
|
115
|
+
maxSubmissionValue,
|
|
116
|
+
minSubmissionCount,
|
|
117
|
+
minSubmissionValue,
|
|
118
|
+
restartDelay,
|
|
119
|
+
timeout,
|
|
120
|
+
unitAmountIn,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const makeRoundsManagerKit = prepareRoundsManagerKit(baggage);
|
|
124
|
+
const makeOracleAdminKit = prepareOracleAdminKit(baggage);
|
|
125
|
+
|
|
126
|
+
const makeRecorderKit = defineRecorderKit({
|
|
127
|
+
// @ts-expect-error XXX
|
|
128
|
+
makeDurablePublishKit,
|
|
129
|
+
makeRecorder,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// end of maker definitions /////////////////////////////////
|
|
133
|
+
|
|
134
|
+
const { answerKit, latestRoundKit, priceKit } = await provideAll(baggage, {
|
|
135
|
+
/** This is just a signal that there's a new answer, which is read from `lastValueOutForUnitIn` */
|
|
136
|
+
answerKit: () => makeDurablePublishKit(),
|
|
137
|
+
/** For publishing priceAuthority values to off-chain storage */
|
|
138
|
+
priceKit: () =>
|
|
139
|
+
makeRecorderKit(
|
|
140
|
+
storageNode,
|
|
141
|
+
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<PriceDescription>} */ (
|
|
142
|
+
M.any()
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
latestRoundKit: () =>
|
|
146
|
+
E.when(E(storageNode).makeChildNode('latestRound'), node =>
|
|
147
|
+
makeRecorderKit(
|
|
148
|
+
node,
|
|
149
|
+
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<import('./roundsManager.js').LatestRound>} */ (
|
|
150
|
+
M.any()
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const { roundsManagerKit } = await provideAll(baggage, {
|
|
157
|
+
roundsManagerKit: () =>
|
|
158
|
+
makeRoundsManagerKit(
|
|
159
|
+
harden({
|
|
160
|
+
answerPublisher: answerKit.publisher,
|
|
161
|
+
brandIn,
|
|
162
|
+
brandOut,
|
|
163
|
+
latestRoundPublisher: latestRoundKit.recorder,
|
|
164
|
+
minSubmissionCount,
|
|
165
|
+
maxSubmissionCount,
|
|
166
|
+
minSubmissionValue,
|
|
167
|
+
maxSubmissionValue,
|
|
168
|
+
quoteKit,
|
|
169
|
+
restartDelay,
|
|
170
|
+
timerPresence,
|
|
171
|
+
timeout,
|
|
172
|
+
unitAmountIn,
|
|
173
|
+
}),
|
|
174
|
+
),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// not durable, held in closure and remade in every call of enclosing
|
|
178
|
+
const { priceAuthority } = makeOnewayPriceAuthorityKit({
|
|
179
|
+
createQuote: roundsManagerKit.contract.makeCreateQuote(),
|
|
180
|
+
notifier: makeNotifierFromSubscriber(answerKit.subscriber),
|
|
181
|
+
quoteIssuer: quoteKit.issuer,
|
|
182
|
+
timer: timerPresence,
|
|
183
|
+
actualBrandIn: brandIn,
|
|
184
|
+
actualBrandOut: brandOut,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// for each new quote from the priceAuthority, publish it to off-chain storage
|
|
188
|
+
void observeNotifier(
|
|
189
|
+
priceAuthority.makeQuoteNotifier(unitAmountIn, brandOut),
|
|
190
|
+
{
|
|
191
|
+
updateState: quote => {
|
|
192
|
+
void priceKit.recorder.write(priceDescriptionFromQuote(quote));
|
|
193
|
+
},
|
|
194
|
+
fail: reason => {
|
|
195
|
+
throw Error(`priceAuthority observer failed: ${reason}`);
|
|
196
|
+
},
|
|
197
|
+
finish: done => {
|
|
198
|
+
throw Error(`priceAuthority observer died: ${done}`);
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const makeFluxAggregatorKit = prepareExoClassKit(
|
|
204
|
+
baggage,
|
|
205
|
+
'fluxAggregator',
|
|
206
|
+
{
|
|
207
|
+
creator: M.interface('fluxAggregator creatorFacet', {}, { sloppy: true }),
|
|
208
|
+
public: M.interface('fluxAggregator publicFacet', {
|
|
209
|
+
getPriceAuthority: M.call().returns(M.any()),
|
|
210
|
+
getSubscriber: M.call().returns(SubscriberShape),
|
|
211
|
+
getRoundStartNotifier: M.call().returns(SubscriberShape),
|
|
212
|
+
getPublicTopics: M.call().returns({
|
|
213
|
+
quotes: M.any(),
|
|
214
|
+
latestRound: M.any(),
|
|
215
|
+
}),
|
|
216
|
+
}),
|
|
217
|
+
},
|
|
218
|
+
() => {
|
|
219
|
+
/** @type {MapStore<string, import('./priceOracleKit.js').OracleKit>} */
|
|
220
|
+
const oracles = makeScalarBigMapStore('oracles', {
|
|
221
|
+
durable: true,
|
|
222
|
+
});
|
|
223
|
+
return { oracles };
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
creator: {
|
|
227
|
+
/**
|
|
228
|
+
* An "oracle invitation" is an invitation to be able to submit data to
|
|
229
|
+
* include in the priceAggregator's results.
|
|
230
|
+
*
|
|
231
|
+
* The offer result from this invitation is a OracleAdmin, which can be used
|
|
232
|
+
* directly to manage the price submissions as well as to terminate the
|
|
233
|
+
* relationship.
|
|
234
|
+
*
|
|
235
|
+
* @param {string} oracleId unique per contract instance
|
|
236
|
+
*/
|
|
237
|
+
async makeOracleInvitation(oracleId) {
|
|
238
|
+
const { facets } = this;
|
|
239
|
+
trace('makeOracleInvitation', oracleId);
|
|
240
|
+
/**
|
|
241
|
+
* If custom arguments are supplied to the `zoe.offer` call, they can
|
|
242
|
+
* indicate an OraclePriceSubmission notifier and a corresponding
|
|
243
|
+
* `shiftValueOut` that should be adapted as part of the priceAuthority's
|
|
244
|
+
* reported data.
|
|
245
|
+
*
|
|
246
|
+
* @param {ZCFSeat} seat
|
|
247
|
+
*/
|
|
248
|
+
const offerHandler = async seat => {
|
|
249
|
+
const { oracle } = await facets.creator.initOracle(oracleId);
|
|
250
|
+
const invitationMakers = Far('invitation makers', {
|
|
251
|
+
/** @param {import('./roundsManager.js').PriceRound} result */
|
|
252
|
+
PushPrice(result) {
|
|
253
|
+
return zcf.makeInvitation(
|
|
254
|
+
/** @param {ZCFSeat} cSeat */
|
|
255
|
+
async cSeat => {
|
|
256
|
+
cSeat.exit();
|
|
257
|
+
await oracle.pushPrice(result);
|
|
258
|
+
},
|
|
259
|
+
'PushPrice',
|
|
260
|
+
);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
seat.exit();
|
|
264
|
+
|
|
265
|
+
return harden({
|
|
266
|
+
invitationMakers,
|
|
267
|
+
oracle,
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
return zcf.makeInvitation(offerHandler, INVITATION_MAKERS_DESC);
|
|
272
|
+
},
|
|
273
|
+
/** @param {string} oracleId */
|
|
274
|
+
async removeOracle(oracleId) {
|
|
275
|
+
const { oracles } = this.state;
|
|
276
|
+
trace('deleteOracle', oracleId);
|
|
277
|
+
const kit = oracles.get(oracleId);
|
|
278
|
+
kit.admin.disable();
|
|
279
|
+
oracles.delete(oracleId);
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
getRoundData: roundIdRaw => {
|
|
283
|
+
return roundsManagerKit.contract.getRoundData(roundIdRaw);
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
/** @param {string} oracleId */
|
|
287
|
+
async initOracle(oracleId) {
|
|
288
|
+
const { oracles } = this.state;
|
|
289
|
+
trace('initOracle', oracleId);
|
|
290
|
+
assert.typeof(oracleId, 'string');
|
|
291
|
+
|
|
292
|
+
const oracleKit = makeOracleAdminKit(
|
|
293
|
+
harden({
|
|
294
|
+
minSubmissionValue,
|
|
295
|
+
maxSubmissionValue,
|
|
296
|
+
oracleId, // must be unique per vat
|
|
297
|
+
roundPowers: roundsManagerKit.oracle,
|
|
298
|
+
timer: timerPresence,
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
oracles.init(oracleId, oracleKit);
|
|
302
|
+
|
|
303
|
+
return oracleKit;
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* a method to provide all current info oracleStatuses need. Intended only
|
|
308
|
+
* only to be callable by oracleStatuses. Not for use by contracts to read state.
|
|
309
|
+
*
|
|
310
|
+
* @param {string} oracleId
|
|
311
|
+
* @param {bigint} queriedRoundId
|
|
312
|
+
* @returns {Promise<RoundState>}
|
|
313
|
+
*/
|
|
314
|
+
async oracleRoundState(oracleId, queriedRoundId) {
|
|
315
|
+
const { oracles } = this.state;
|
|
316
|
+
const blockTimestamp = await E(timerPresence).getCurrentTimestamp();
|
|
317
|
+
const status = await E(oracles.get(oracleId).oracle).getStatus();
|
|
318
|
+
|
|
319
|
+
const oracleCount = oracles.getSize();
|
|
320
|
+
|
|
321
|
+
const { contract } = roundsManagerKit;
|
|
322
|
+
if (queriedRoundId > 0) {
|
|
323
|
+
const roundStatus = contract.getRoundStatus(queriedRoundId);
|
|
324
|
+
return {
|
|
325
|
+
eligibleForSpecificRound: contract.eligibleForSpecificRound(
|
|
326
|
+
status,
|
|
327
|
+
queriedRoundId,
|
|
328
|
+
blockTimestamp,
|
|
329
|
+
),
|
|
330
|
+
queriedRoundId,
|
|
331
|
+
latestSubmission: status.latestSubmission,
|
|
332
|
+
startedAt: roundStatus.startedAt,
|
|
333
|
+
roundTimeout: roundStatus.roundTimeout,
|
|
334
|
+
oracleCount,
|
|
335
|
+
};
|
|
336
|
+
} else {
|
|
337
|
+
return {
|
|
338
|
+
...contract.oracleRoundStateSuggestRound(status, blockTimestamp),
|
|
339
|
+
oracleCount,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
public: {
|
|
345
|
+
getPriceAuthority() {
|
|
346
|
+
return priceAuthority;
|
|
347
|
+
},
|
|
348
|
+
/** @deprecated use getPublicTopics */
|
|
349
|
+
getSubscriber: () => {
|
|
350
|
+
return priceKit.subscriber;
|
|
351
|
+
},
|
|
352
|
+
/** @deprecated use getPublicTopics */
|
|
353
|
+
getRoundStartNotifier() {
|
|
354
|
+
return latestRoundKit.subscriber;
|
|
355
|
+
},
|
|
356
|
+
getPublicTopics() {
|
|
357
|
+
return {
|
|
358
|
+
quotes: makeRecorderTopic(
|
|
359
|
+
'Quotes from this price aggregator',
|
|
360
|
+
priceKit,
|
|
361
|
+
),
|
|
362
|
+
latestRound: makeRecorderTopic(
|
|
363
|
+
'Notification of each round',
|
|
364
|
+
latestRoundKit,
|
|
365
|
+
),
|
|
366
|
+
};
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
return makeFluxAggregatorKit;
|
|
373
|
+
};
|
|
374
|
+
harden(prepareFluxAggregatorKit);
|
|
375
|
+
/** @typedef {ReturnType<Awaited<ReturnType<typeof prepareFluxAggregatorKit>>>} FluxAggregatorKit */
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Fail } from '@agoric/assert';
|
|
2
|
+
import { makeTracer } from '@agoric/internal';
|
|
3
|
+
import { M, prepareExoClassKit } from '@agoric/vat-data';
|
|
4
|
+
|
|
5
|
+
const trace = makeTracer('OrKit', true);
|
|
6
|
+
|
|
7
|
+
export const INVITATION_MAKERS_DESC = 'oracle invitation';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* oracleId: string,
|
|
12
|
+
* roundPowers: { handlePush: (status: OracleStatus, result: import('./roundsManager.js').PriceRound) => Promise<OracleStatus> }
|
|
13
|
+
* }} HeldParams
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {{ roundId: number | undefined, unitPrice: NatValue }} PriceDatum
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {object} OracleStatus
|
|
22
|
+
* @property {boolean} [disabled]
|
|
23
|
+
* @property {bigint} lastReportedRound
|
|
24
|
+
* @property {bigint} lastStartedRound
|
|
25
|
+
* @property {bigint} latestSubmission
|
|
26
|
+
* @property {string} oracleId
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Readonly<HeldParams & {
|
|
30
|
+
* }>} ImmutableState
|
|
31
|
+
*
|
|
32
|
+
* @typedef {OracleStatus & {
|
|
33
|
+
* }} MutableState
|
|
34
|
+
*/
|
|
35
|
+
/** @typedef {ImmutableState & MutableState} State */
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {HeldParams} heldParams
|
|
39
|
+
* @returns {State}
|
|
40
|
+
*/
|
|
41
|
+
const initState = ({ oracleId, roundPowers }) => {
|
|
42
|
+
return {
|
|
43
|
+
oracleId,
|
|
44
|
+
roundPowers,
|
|
45
|
+
disabled: false,
|
|
46
|
+
lastReportedRound: 0n,
|
|
47
|
+
lastStartedRound: 0n,
|
|
48
|
+
latestSubmission: 0n,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const AdminI = M.interface('OracleKitAdmin', {
|
|
53
|
+
disable: M.call().returns(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const OracleI = M.interface('Oracle', {
|
|
57
|
+
pushPrice: M.call(
|
|
58
|
+
M.splitRecord(
|
|
59
|
+
{ unitPrice: M.bigint() },
|
|
60
|
+
{ roundId: M.or(M.bigint(), M.number()) },
|
|
61
|
+
),
|
|
62
|
+
).returns(M.promise()),
|
|
63
|
+
getStatus: M.call().returns(M.record()),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const prepareOracleAdminKit = baggage =>
|
|
67
|
+
prepareExoClassKit(
|
|
68
|
+
baggage,
|
|
69
|
+
'OracleKit',
|
|
70
|
+
{ admin: AdminI, oracle: OracleI },
|
|
71
|
+
initState,
|
|
72
|
+
{
|
|
73
|
+
admin: {
|
|
74
|
+
disable() {
|
|
75
|
+
trace(`oracle ${this.state.oracleId} disabled`);
|
|
76
|
+
this.state.disabled = true;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
oracle: {
|
|
80
|
+
/**
|
|
81
|
+
* push a unitPrice result from this oracle
|
|
82
|
+
*
|
|
83
|
+
* @param {PriceDatum} datum
|
|
84
|
+
*/
|
|
85
|
+
async pushPrice({
|
|
86
|
+
roundId: roundIdRaw = undefined,
|
|
87
|
+
unitPrice: valueRaw,
|
|
88
|
+
}) {
|
|
89
|
+
const { state } = this;
|
|
90
|
+
!state.disabled || Fail`pushPrice for disabled oracle`;
|
|
91
|
+
const { roundPowers } = state;
|
|
92
|
+
const result = await roundPowers.handlePush(
|
|
93
|
+
{
|
|
94
|
+
oracleId: state.oracleId,
|
|
95
|
+
lastReportedRound: state.lastReportedRound,
|
|
96
|
+
lastStartedRound: state.lastStartedRound,
|
|
97
|
+
latestSubmission: state.latestSubmission,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
roundId: roundIdRaw,
|
|
101
|
+
unitPrice: valueRaw,
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
state.lastReportedRound = result.lastReportedRound;
|
|
106
|
+
state.lastStartedRound = result.lastStartedRound;
|
|
107
|
+
state.latestSubmission = result.latestSubmission;
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
*
|
|
111
|
+
* @returns {OracleStatus}
|
|
112
|
+
*/
|
|
113
|
+
getStatus() {
|
|
114
|
+
const { state } = this;
|
|
115
|
+
return {
|
|
116
|
+
oracleId: state.oracleId,
|
|
117
|
+
disabled: state.disabled,
|
|
118
|
+
lastReportedRound: state.lastReportedRound,
|
|
119
|
+
lastStartedRound: state.lastStartedRound,
|
|
120
|
+
latestSubmission: state.latestSubmission,
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
/** @typedef {ReturnType<ReturnType<typeof prepareOracleAdminKit>>} OracleKit */
|