@agoric/inter-protocol 0.13.3 → 0.13.4-dev-d32a66a.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,370 @@
|
|
|
1
|
+
import { E } from '@endo/eventual-send';
|
|
2
|
+
import { TimeMath } from '@agoric/time';
|
|
3
|
+
import { Far } from '@endo/marshal';
|
|
4
|
+
import { makeTracer } from '@agoric/internal';
|
|
5
|
+
import { observeIteration, subscribeEach } from '@agoric/notifier';
|
|
6
|
+
|
|
7
|
+
import { AuctionState, makeCancelTokenMaker } from './util.js';
|
|
8
|
+
import {
|
|
9
|
+
computeRoundTiming,
|
|
10
|
+
nextDescendingStepTime,
|
|
11
|
+
timeVsSchedule,
|
|
12
|
+
} from './scheduleMath.js';
|
|
13
|
+
|
|
14
|
+
const { details: X, Fail, quote: q } = assert;
|
|
15
|
+
|
|
16
|
+
const trace = makeTracer('SCHED', true);
|
|
17
|
+
|
|
18
|
+
// If the startAuction wakeup is no more than 5 minutes late, go ahead with it.
|
|
19
|
+
const MAX_LATE_TICK = 300n;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @file The scheduler is presumed to be quiescent between auction rounds. Each
|
|
23
|
+
* Auction round consists of a sequence of steps with decreasing prices. There
|
|
24
|
+
* should always be a next schedule, but between rounds, liveSchedule is null.
|
|
25
|
+
*
|
|
26
|
+
* The lock period that the liquidators use might start before the previous
|
|
27
|
+
* round has finished, so we need to schedule the next round each time an
|
|
28
|
+
* auction starts. This means if the scheduling parameters change, it'll be a
|
|
29
|
+
* full cycle before we switch. Otherwise, the vaults wouldn't know when to
|
|
30
|
+
* start their lock period. If the lock period for the next auction hasn't
|
|
31
|
+
* started when each aucion ends, we recalculate it, in case the parameters have
|
|
32
|
+
* changed.
|
|
33
|
+
*
|
|
34
|
+
* If the clock skips forward (because of a chain halt, for instance), the
|
|
35
|
+
* scheduler will try to cleanly and quickly finish any round already in
|
|
36
|
+
* progress. It would take additional work on the manual timer to test this
|
|
37
|
+
* thoroughly.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const makeCancelToken = makeCancelTokenMaker('scheduler');
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {object} AuctionDriver
|
|
44
|
+
* @property {() => void} reducePriceAndTrade
|
|
45
|
+
* @property {() => void} finalize
|
|
46
|
+
* @property {() => void} startRound
|
|
47
|
+
* @property {() => void} capturePrices
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {object} ScheduleNotification
|
|
52
|
+
*
|
|
53
|
+
* @property {Timestamp | null} activeStartTime start time of current
|
|
54
|
+
* auction if auction is active
|
|
55
|
+
* @property {Timestamp | null} nextStartTime start time of next auction
|
|
56
|
+
* @property {Timestamp | null} nextDescendingStepTime when the next descending step
|
|
57
|
+
* will take place
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
const safelyComputeRoundTiming = (params, baseTime) => {
|
|
61
|
+
try {
|
|
62
|
+
return computeRoundTiming(params, baseTime);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error('🚨 No Next Auction', e);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const nominalStartTime = nextSchedule =>
|
|
70
|
+
TimeMath.subtractAbsRel(nextSchedule.startTime, nextSchedule.startDelay);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {AuctionDriver} auctionDriver
|
|
74
|
+
* @param {import('@agoric/time/src/types').TimerService} timer
|
|
75
|
+
* @param {Awaited<import('./params.js').AuctionParamManager>} params
|
|
76
|
+
* @param {import('@agoric/time/src/types').TimerBrand} timerBrand
|
|
77
|
+
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').Recorder<ScheduleNotification>} scheduleRecorder
|
|
78
|
+
* @param {StoredSubscription<GovernanceSubscriptionState>} paramUpdateSubscription
|
|
79
|
+
*/
|
|
80
|
+
export const makeScheduler = async (
|
|
81
|
+
auctionDriver,
|
|
82
|
+
timer,
|
|
83
|
+
params,
|
|
84
|
+
timerBrand,
|
|
85
|
+
scheduleRecorder,
|
|
86
|
+
paramUpdateSubscription,
|
|
87
|
+
) => {
|
|
88
|
+
/**
|
|
89
|
+
* live version is defined when an auction is active.
|
|
90
|
+
*
|
|
91
|
+
* @type {Schedule | null}
|
|
92
|
+
*/
|
|
93
|
+
let liveSchedule = null;
|
|
94
|
+
|
|
95
|
+
/** @returns {Promise<{ now: Timestamp, nextSchedule: Schedule | null }>} */
|
|
96
|
+
const initializeNextSchedule = async () => {
|
|
97
|
+
return E.when(
|
|
98
|
+
// XXX manualTimer returns a bigint, not a timeRecord.
|
|
99
|
+
E(timer).getCurrentTimestamp(),
|
|
100
|
+
now => {
|
|
101
|
+
const nextSchedule = safelyComputeRoundTiming(
|
|
102
|
+
params,
|
|
103
|
+
TimeMath.coerceTimestampRecord(now, timerBrand),
|
|
104
|
+
);
|
|
105
|
+
return { now, nextSchedule };
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
let { now, nextSchedule } = await initializeNextSchedule();
|
|
110
|
+
const setTimeMonotonically = time => {
|
|
111
|
+
TimeMath.compareAbs(time, now) >= 0 || Fail`Time only moves forward`;
|
|
112
|
+
now = time;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const stepCancelToken = makeCancelToken();
|
|
116
|
+
|
|
117
|
+
/** @type {typeof AuctionState[keyof typeof AuctionState]} */
|
|
118
|
+
let auctionState = AuctionState.WAITING;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Publish the schedule. To be called after clockTick() (which processes a
|
|
122
|
+
* descending step) or when the next round is scheduled.
|
|
123
|
+
*/
|
|
124
|
+
const publishSchedule = () => {
|
|
125
|
+
const sched = harden({
|
|
126
|
+
activeStartTime: liveSchedule?.startTime || null,
|
|
127
|
+
nextStartTime: nextSchedule?.startTime || null,
|
|
128
|
+
nextDescendingStepTime: nextDescendingStepTime(
|
|
129
|
+
liveSchedule,
|
|
130
|
+
nextSchedule,
|
|
131
|
+
now,
|
|
132
|
+
),
|
|
133
|
+
});
|
|
134
|
+
return scheduleRecorder.write(sched);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {Schedule | null} schedule
|
|
139
|
+
* @returns {void}
|
|
140
|
+
*/
|
|
141
|
+
const clockTick = schedule => {
|
|
142
|
+
trace('clockTick', schedule?.startTime, now);
|
|
143
|
+
if (!schedule) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @type {() => void} */
|
|
148
|
+
const finishAuctionRound = () => {
|
|
149
|
+
auctionState = AuctionState.WAITING;
|
|
150
|
+
auctionDriver.finalize();
|
|
151
|
+
|
|
152
|
+
if (nextSchedule) {
|
|
153
|
+
// only recalculate the next schedule at this point if the lock time has
|
|
154
|
+
// not been reached.
|
|
155
|
+
const nextLock = nextSchedule.lockTime;
|
|
156
|
+
if (nextLock && TimeMath.compareAbs(now, nextLock) < 0) {
|
|
157
|
+
const afterNow = TimeMath.addAbsRel(
|
|
158
|
+
now,
|
|
159
|
+
TimeMath.coerceRelativeTimeRecord(1n, timerBrand),
|
|
160
|
+
);
|
|
161
|
+
nextSchedule = safelyComputeRoundTiming(params, afterNow);
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
console.error(
|
|
165
|
+
'🛠️ finishAuctionRound without scheduling the next; repair with new auctioneer params',
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
liveSchedule = null;
|
|
170
|
+
|
|
171
|
+
// Async because a remote call. In rare event of failure, wakeup is robust.
|
|
172
|
+
void E(timer).cancel(stepCancelToken);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const advanceRound = () => {
|
|
176
|
+
if (auctionState === AuctionState.ACTIVE) {
|
|
177
|
+
auctionDriver.reducePriceAndTrade();
|
|
178
|
+
} else {
|
|
179
|
+
auctionState = AuctionState.ACTIVE;
|
|
180
|
+
try {
|
|
181
|
+
auctionDriver.startRound();
|
|
182
|
+
// This has been observed to fail because prices hadn't been locked.
|
|
183
|
+
// This may be an issue about timing during chain start-up.
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.error(
|
|
186
|
+
'Unable to start auction cleanly. skipping this auction round.',
|
|
187
|
+
err,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
switch (timeVsSchedule(now, schedule)) {
|
|
194
|
+
case 'before':
|
|
195
|
+
break;
|
|
196
|
+
case 'during':
|
|
197
|
+
advanceRound();
|
|
198
|
+
break;
|
|
199
|
+
case 'endExactly':
|
|
200
|
+
// do both the "during" and "after" behaviors
|
|
201
|
+
advanceRound();
|
|
202
|
+
finishAuctionRound();
|
|
203
|
+
break;
|
|
204
|
+
case 'after':
|
|
205
|
+
finishAuctionRound();
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
Fail`invalid case`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
void publishSchedule();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// set wakeups for the steps of this round
|
|
215
|
+
const scheduleDescendingSteps = () => {
|
|
216
|
+
if (!liveSchedule) {
|
|
217
|
+
console.error('🚨 scheduleDescendingSteps called with no liveSchedule');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const { startTime } = liveSchedule;
|
|
222
|
+
trace('START ', startTime);
|
|
223
|
+
|
|
224
|
+
const delayFromNow =
|
|
225
|
+
TimeMath.compareAbs(startTime, now) > 0
|
|
226
|
+
? TimeMath.subtractAbsAbs(startTime, now)
|
|
227
|
+
: TimeMath.subtractAbsAbs(startTime, startTime);
|
|
228
|
+
|
|
229
|
+
trace('scheduleDescendingSteps repeating', now, delayFromNow, startTime);
|
|
230
|
+
|
|
231
|
+
void E(timer).repeatAfter(
|
|
232
|
+
delayFromNow,
|
|
233
|
+
liveSchedule.clockStep,
|
|
234
|
+
Far('PriceStepWaker', {
|
|
235
|
+
wake(time) {
|
|
236
|
+
setTimeMonotonically(time);
|
|
237
|
+
trace('wake step', now);
|
|
238
|
+
void clockTick(liveSchedule);
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
stepCancelToken,
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// schedule a wakeup for the next round
|
|
246
|
+
const scheduleNextRound = start => {
|
|
247
|
+
trace(`nextRound`, start);
|
|
248
|
+
void E(timer).setWakeup(
|
|
249
|
+
start,
|
|
250
|
+
Far('SchedulerWaker', {
|
|
251
|
+
wake(time) {
|
|
252
|
+
setTimeMonotonically(time);
|
|
253
|
+
auctionDriver.capturePrices();
|
|
254
|
+
// eslint-disable-next-line no-use-before-define
|
|
255
|
+
return startAuction();
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
);
|
|
259
|
+
void publishSchedule();
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const relativeTime = t => TimeMath.coerceRelativeTimeRecord(t, timerBrand);
|
|
263
|
+
|
|
264
|
+
const startAuction = async () => {
|
|
265
|
+
!liveSchedule || Fail`can't start an auction round while one is active`;
|
|
266
|
+
|
|
267
|
+
if (!nextSchedule) {
|
|
268
|
+
console.error(
|
|
269
|
+
assert.error(X`tried to start auction when none is scheduled`),
|
|
270
|
+
);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// The clock tick may have arrived too late to trigger the next scheduled
|
|
275
|
+
// round, for example because of a chain halt. When this happens the oracle
|
|
276
|
+
// quote may out of date and so must be ignored. Recover by returning
|
|
277
|
+
// deposits and scheduling the next round. If it's only a little late,
|
|
278
|
+
// continue with auction, just starting late.
|
|
279
|
+
const nominalStart = nominalStartTime(nextSchedule);
|
|
280
|
+
if (TimeMath.compareAbs(now, nominalStart) > 0) {
|
|
281
|
+
const late = TimeMath.subtractAbsAbs(now, nominalStart);
|
|
282
|
+
const maxLate = relativeTime(MAX_LATE_TICK);
|
|
283
|
+
|
|
284
|
+
if (TimeMath.compareRel(late, maxLate) > 0) {
|
|
285
|
+
console.warn(
|
|
286
|
+
`Auction time jumped to ${q(now)} before next scheduled start ${q(
|
|
287
|
+
nextSchedule.startTime,
|
|
288
|
+
)}. Skipping that round.`,
|
|
289
|
+
);
|
|
290
|
+
nextSchedule = safelyComputeRoundTiming(params, now);
|
|
291
|
+
} else {
|
|
292
|
+
console.warn(`Auction started late by ${q(late)}. Starting ${q(now)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!nextSchedule) {
|
|
297
|
+
// nothing new to schedule
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// activate the nextSchedule as the live one
|
|
301
|
+
// (read here and in function calls below)
|
|
302
|
+
liveSchedule = nextSchedule;
|
|
303
|
+
scheduleDescendingSteps();
|
|
304
|
+
nextSchedule = safelyComputeRoundTiming(
|
|
305
|
+
params,
|
|
306
|
+
TimeMath.addAbsRel(liveSchedule.endTime, relativeTime(1n)),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
if (nextSchedule) {
|
|
310
|
+
scheduleNextRound(nominalStartTime(nextSchedule));
|
|
311
|
+
} else {
|
|
312
|
+
console.warn(
|
|
313
|
+
'no nextSchedule so cannot schedule next round or price capture',
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// initial setting: firstStart is startDelay before next's startTime
|
|
319
|
+
const startSchedulingFromScratch = () => {
|
|
320
|
+
trace('startSchedulingFromScratch');
|
|
321
|
+
if (nextSchedule) {
|
|
322
|
+
scheduleNextRound(nominalStartTime(nextSchedule));
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
startSchedulingFromScratch();
|
|
326
|
+
|
|
327
|
+
// when auction parameters change, schedule a next auction if one isn't
|
|
328
|
+
// already scheduled.
|
|
329
|
+
// NB: what is already scheduled (live or next) is unaffected by param changes
|
|
330
|
+
void observeIteration(
|
|
331
|
+
subscribeEach(paramUpdateSubscription),
|
|
332
|
+
harden({
|
|
333
|
+
// NB: may be fired with the initial params as well
|
|
334
|
+
async updateState(_newState) {
|
|
335
|
+
trace('received param update', _newState);
|
|
336
|
+
if (!nextSchedule) {
|
|
337
|
+
trace('repairing nextSchedule and restarting');
|
|
338
|
+
({ nextSchedule } = await initializeNextSchedule());
|
|
339
|
+
startSchedulingFromScratch();
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
return Far('scheduler', {
|
|
346
|
+
getSchedule: () =>
|
|
347
|
+
harden({
|
|
348
|
+
liveAuctionSchedule: liveSchedule,
|
|
349
|
+
nextAuctionSchedule: nextSchedule,
|
|
350
|
+
}),
|
|
351
|
+
getAuctionState: () => auctionState,
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @typedef {object} Schedule
|
|
357
|
+
* @property {import('@agoric/time/src/types').TimestampRecord} startTime
|
|
358
|
+
* @property {import('@agoric/time/src/types').TimestampRecord} endTime
|
|
359
|
+
* @property {NatValue} steps
|
|
360
|
+
* @property {NatValue} endRate
|
|
361
|
+
* @property {RelativeTime} startDelay
|
|
362
|
+
* @property {RelativeTime} clockStep
|
|
363
|
+
* @property {Timestamp} [lockTime]
|
|
364
|
+
*/
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @typedef {object} FullSchedule
|
|
368
|
+
* @property {Schedule | null} nextAuctionSchedule
|
|
369
|
+
* @property {Schedule | null} liveAuctionSchedule
|
|
370
|
+
*/
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// @jessie-check
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
makeRatio,
|
|
5
|
+
ratioToNumber,
|
|
6
|
+
} from '@agoric/zoe/src/contractSupport/index.js';
|
|
7
|
+
import { M, mustMatch } from '@agoric/store';
|
|
8
|
+
import { RatioShape } from '@agoric/ertp';
|
|
9
|
+
|
|
10
|
+
import { decodeData, encodeData } from '../vaultFactory/storeUtils.js';
|
|
11
|
+
|
|
12
|
+
const { Fail } = assert;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @file we use a floating point representation of the price or rate as the
|
|
16
|
+
* first part of the key in the store. The second part is the sequence number of
|
|
17
|
+
* the bid, but it doesn't matter for sorting. When we retrieve multiple bids,
|
|
18
|
+
* it's only by bid value, so we don't care how the sequence numbers sort.
|
|
19
|
+
*
|
|
20
|
+
* We take advantage of the fact that encodeData takes a passable and turns it
|
|
21
|
+
* into a sort key. Arrays of passable data sort like composite keys.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Return a sort key that will compare based only on price. Price is the prefix
|
|
26
|
+
* of the complete sort key, which is sufficient to find offers below a cutoff.
|
|
27
|
+
*
|
|
28
|
+
* @param {Ratio} offerPrice
|
|
29
|
+
*/
|
|
30
|
+
export const toPartialOfferKey = offerPrice => {
|
|
31
|
+
assert(offerPrice);
|
|
32
|
+
const mostSignificantPart = ratioToNumber(offerPrice);
|
|
33
|
+
return encodeData(harden([mostSignificantPart, 0n]));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return a sort key that distinguishes by Price and sequence number
|
|
38
|
+
*
|
|
39
|
+
* @param {Ratio} offerPrice IST/collateral
|
|
40
|
+
* @param {bigint} sequenceNumber
|
|
41
|
+
* @returns {string} lexically sortable string in which highest price is first,
|
|
42
|
+
* ties will be broken by sequenceNumber of offer
|
|
43
|
+
*/
|
|
44
|
+
export const toPriceOfferKey = (offerPrice, sequenceNumber) => {
|
|
45
|
+
mustMatch(offerPrice, RatioShape);
|
|
46
|
+
offerPrice.numerator.brand !== offerPrice.denominator.brand ||
|
|
47
|
+
Fail`offer prices must have different numerator and denominator`;
|
|
48
|
+
mustMatch(sequenceNumber, M.nat());
|
|
49
|
+
|
|
50
|
+
const mostSignificantPart = ratioToNumber(offerPrice);
|
|
51
|
+
return encodeData(harden([mostSignificantPart, sequenceNumber]));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {number} floatPrice
|
|
56
|
+
* @param {Brand<'nat'>} numBrand
|
|
57
|
+
* @param {Brand<'nat'>} denomBrand
|
|
58
|
+
* @param {number} useDecimals
|
|
59
|
+
* @returns {Ratio}
|
|
60
|
+
*/
|
|
61
|
+
const priceRatioFromFloat = (floatPrice, numBrand, denomBrand, useDecimals) => {
|
|
62
|
+
const denominatorValue = 10 ** useDecimals;
|
|
63
|
+
return makeRatio(
|
|
64
|
+
BigInt(Math.round(floatPrice * denominatorValue)),
|
|
65
|
+
numBrand,
|
|
66
|
+
BigInt(denominatorValue),
|
|
67
|
+
denomBrand,
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const bidScalingRatioFromKey = (bidScaleFloat, numBrand, useDecimals) => {
|
|
72
|
+
const denominatorValue = 10 ** useDecimals;
|
|
73
|
+
return makeRatio(
|
|
74
|
+
BigInt(Math.round(bidScaleFloat * denominatorValue)),
|
|
75
|
+
numBrand,
|
|
76
|
+
BigInt(denominatorValue),
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* fromPriceOfferKey is only used for diagnostics.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} key
|
|
84
|
+
* @param {Brand<'nat'>} numBrand
|
|
85
|
+
* @param {Brand<'nat'>} denomBrand
|
|
86
|
+
* @param {number} useDecimals
|
|
87
|
+
* @returns {[normalizedPrice: Ratio, sequenceNumber: bigint]}
|
|
88
|
+
*/
|
|
89
|
+
export const fromPriceOfferKey = (key, numBrand, denomBrand, useDecimals) => {
|
|
90
|
+
const [pricePart, sequenceNumberPart] = decodeData(key);
|
|
91
|
+
return [
|
|
92
|
+
priceRatioFromFloat(pricePart, numBrand, denomBrand, useDecimals),
|
|
93
|
+
sequenceNumberPart,
|
|
94
|
+
];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** @type {(rate: Ratio) => string} */
|
|
98
|
+
export const toBidScalingComparator = rate => {
|
|
99
|
+
assert(rate);
|
|
100
|
+
const mostSignificantPart = ratioToNumber(rate);
|
|
101
|
+
return encodeData(harden([mostSignificantPart, 0n]));
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sorts offers expressed as percentage of the current oracle price.
|
|
106
|
+
*
|
|
107
|
+
* @param {Ratio} rate discount/markup rate expressed as a ratio IST/IST
|
|
108
|
+
* @param {bigint} sequenceNumber
|
|
109
|
+
* @returns {string} lexically sortable string in which highest price is first,
|
|
110
|
+
* ties will be broken by sequenceNumber of offer
|
|
111
|
+
*/
|
|
112
|
+
export const toScaledRateOfferKey = (rate, sequenceNumber) => {
|
|
113
|
+
mustMatch(rate, RatioShape);
|
|
114
|
+
rate.numerator.brand === rate.denominator.brand ||
|
|
115
|
+
Fail`bid scaling rate must have the same numerator and denominator`;
|
|
116
|
+
mustMatch(sequenceNumber, M.nat());
|
|
117
|
+
|
|
118
|
+
const mostSignificantPart = ratioToNumber(rate);
|
|
119
|
+
return encodeData(harden([mostSignificantPart, sequenceNumber]));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* fromScaledRateOfferKey is only used for diagnostics.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} key
|
|
126
|
+
* @param {Brand} brand
|
|
127
|
+
* @param {number} useDecimals
|
|
128
|
+
* @returns {[normalizedPrice: Ratio, sequenceNumber: bigint]}
|
|
129
|
+
*/
|
|
130
|
+
export const fromScaledRateOfferKey = (key, brand, useDecimals) => {
|
|
131
|
+
const [bidScalingPart, sequenceNumberPart] = decodeData(key);
|
|
132
|
+
return [
|
|
133
|
+
bidScalingRatioFromKey(bidScalingPart, brand, useDecimals),
|
|
134
|
+
sequenceNumberPart,
|
|
135
|
+
];
|
|
136
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @jessie-check
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
makeRatioFromAmounts,
|
|
5
|
+
multiplyRatios,
|
|
6
|
+
ratioGTE,
|
|
7
|
+
} from '@agoric/zoe/src/contractSupport/index.js';
|
|
8
|
+
import { Far } from '@endo/marshal';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Constants for Auction State.
|
|
12
|
+
*
|
|
13
|
+
* @type {{ ACTIVE: 'active', WAITING: 'waiting' }}
|
|
14
|
+
*/
|
|
15
|
+
export const AuctionState = {
|
|
16
|
+
ACTIVE: 'active',
|
|
17
|
+
WAITING: 'waiting',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {{ brand: Brand, value: Pattern }} numeratorAmountShape
|
|
22
|
+
* @param {{ brand: Brand, value: Pattern }} denominatorAmountShape
|
|
23
|
+
*/
|
|
24
|
+
export const makeBrandedRatioPattern = (
|
|
25
|
+
numeratorAmountShape,
|
|
26
|
+
denominatorAmountShape,
|
|
27
|
+
) => {
|
|
28
|
+
return harden({
|
|
29
|
+
numerator: numeratorAmountShape,
|
|
30
|
+
denominator: denominatorAmountShape,
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Ratio} bidScaling
|
|
36
|
+
* @param {Ratio} currentPrice
|
|
37
|
+
* @param {Ratio} oraclePrice
|
|
38
|
+
* @returns {boolean} TRUE iff the discount(/markup) applied to the price is
|
|
39
|
+
* higher than the quote.
|
|
40
|
+
*/
|
|
41
|
+
export const isScaledBidPriceHigher = (bidScaling, currentPrice, oraclePrice) =>
|
|
42
|
+
ratioGTE(multiplyRatios(oraclePrice, bidScaling), currentPrice);
|
|
43
|
+
|
|
44
|
+
/** @type {(quote: PriceQuote) => Ratio} */
|
|
45
|
+
export const priceFrom = quote =>
|
|
46
|
+
makeRatioFromAmounts(
|
|
47
|
+
quote.quoteAmount.value[0].amountOut,
|
|
48
|
+
quote.quoteAmount.value[0].amountIn,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const makeCancelTokenMaker = name => {
|
|
52
|
+
let tokenCount = 1;
|
|
53
|
+
|
|
54
|
+
return () => Far(`cancelToken-${name}-${(tokenCount += 1)}`, {});
|
|
55
|
+
};
|