agoric 0.19.1-dev-acefd94.0 → 0.19.1-dev-d42f8d0.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/package.json +17 -17
- package/src/commands/oracle.js +194 -52
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agoric",
|
|
3
|
-
"version": "0.19.1-dev-
|
|
3
|
+
"version": "0.19.1-dev-d42f8d0.0+d42f8d0",
|
|
4
4
|
"description": "Manage the Agoric Javascript smart contract platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -24,25 +24,25 @@
|
|
|
24
24
|
"lint:eslint": "eslint ."
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@agoric/deploy-script-support": "0.10.1-dev-
|
|
27
|
+
"@agoric/deploy-script-support": "0.10.1-dev-d42f8d0.0+d42f8d0",
|
|
28
28
|
"ava": "^5.2.0",
|
|
29
29
|
"c8": "^7.13.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@agoric/access-token": "0.4.22-dev-
|
|
33
|
-
"@agoric/assert": "0.6.1-dev-
|
|
34
|
-
"@agoric/cache": "0.3.1-dev-
|
|
35
|
-
"@agoric/casting": "0.4.1-dev-
|
|
36
|
-
"@agoric/cosmic-proto": "0.3.1-dev-
|
|
37
|
-
"@agoric/ertp": "0.16.1-dev-
|
|
38
|
-
"@agoric/inter-protocol": "0.14.1-dev-
|
|
39
|
-
"@agoric/internal": "0.3.1-dev-
|
|
40
|
-
"@agoric/smart-wallet": "0.5.1-dev-
|
|
41
|
-
"@agoric/store": "0.9.1-dev-
|
|
42
|
-
"@agoric/swingset-vat": "0.32.1-dev-
|
|
43
|
-
"@agoric/vats": "0.14.1-dev-
|
|
44
|
-
"@agoric/zoe": "0.26.1-dev-
|
|
45
|
-
"@agoric/zone": "0.2.1-dev-
|
|
32
|
+
"@agoric/access-token": "0.4.22-dev-d42f8d0.0+d42f8d0",
|
|
33
|
+
"@agoric/assert": "0.6.1-dev-d42f8d0.0+d42f8d0",
|
|
34
|
+
"@agoric/cache": "0.3.1-dev-d42f8d0.0+d42f8d0",
|
|
35
|
+
"@agoric/casting": "0.4.1-dev-d42f8d0.0+d42f8d0",
|
|
36
|
+
"@agoric/cosmic-proto": "0.3.1-dev-d42f8d0.0+d42f8d0",
|
|
37
|
+
"@agoric/ertp": "0.16.1-dev-d42f8d0.0+d42f8d0",
|
|
38
|
+
"@agoric/inter-protocol": "0.14.1-dev-d42f8d0.0+d42f8d0",
|
|
39
|
+
"@agoric/internal": "0.3.1-dev-d42f8d0.0+d42f8d0",
|
|
40
|
+
"@agoric/smart-wallet": "0.5.1-dev-d42f8d0.0+d42f8d0",
|
|
41
|
+
"@agoric/store": "0.9.1-dev-d42f8d0.0+d42f8d0",
|
|
42
|
+
"@agoric/swingset-vat": "0.32.1-dev-d42f8d0.0+d42f8d0",
|
|
43
|
+
"@agoric/vats": "0.14.1-dev-d42f8d0.0+d42f8d0",
|
|
44
|
+
"@agoric/zoe": "0.26.1-dev-d42f8d0.0+d42f8d0",
|
|
45
|
+
"@agoric/zone": "0.2.1-dev-d42f8d0.0+d42f8d0",
|
|
46
46
|
"@confio/relayer": "^0.9.0",
|
|
47
47
|
"@cosmjs/crypto": "^0.30.1",
|
|
48
48
|
"@cosmjs/encoding": "^0.30.1",
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"timeout": "2m",
|
|
89
89
|
"workerThreads": false
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "d42f8d099cb6457d27a5d04e3c91797948040502"
|
|
92
92
|
}
|
package/src/commands/oracle.js
CHANGED
|
@@ -2,14 +2,22 @@
|
|
|
2
2
|
/* eslint-disable @jessie.js/no-nested-await */
|
|
3
3
|
// @ts-check
|
|
4
4
|
/* eslint-disable func-names */
|
|
5
|
-
/* global fetch */
|
|
5
|
+
/* global fetch, setTimeout, process */
|
|
6
6
|
import { Fail } from '@agoric/assert';
|
|
7
7
|
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
|
|
8
8
|
import { Nat } from '@endo/nat';
|
|
9
9
|
import { Command } from 'commander';
|
|
10
|
+
import * as cp from 'child_process';
|
|
10
11
|
import { inspect } from 'util';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { normalizeAddressWithOptions } from '../lib/chain.js';
|
|
13
|
+
import { getNetworkConfig, makeRpcUtils, storageHelper } from '../lib/rpc.js';
|
|
14
|
+
import {
|
|
15
|
+
getCurrent,
|
|
16
|
+
makeWalletUtils,
|
|
17
|
+
outputAction,
|
|
18
|
+
sendAction,
|
|
19
|
+
} from '../lib/wallet.js';
|
|
20
|
+
import { bigintReplacer } from '../lib/format.js';
|
|
13
21
|
|
|
14
22
|
// XXX support other decimal places
|
|
15
23
|
const COSMOS_UNIT = 1_000_000n;
|
|
@@ -17,10 +25,24 @@ const scaleDecimals = num => BigInt(num * Number(COSMOS_UNIT));
|
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
27
|
* @param {import('anylogger').Logger} logger
|
|
28
|
+
* @param {{
|
|
29
|
+
* delay?: (ms: number) => Promise<void>,
|
|
30
|
+
* execFileSync?: typeof import('child_process').execFileSync,
|
|
31
|
+
* env?: Record<string, string | undefined>,
|
|
32
|
+
* stdout?: Pick<import('stream').Writable,'write'>,
|
|
33
|
+
* }} [io]
|
|
20
34
|
*/
|
|
21
|
-
export const makeOracleCommand = logger => {
|
|
22
|
-
const
|
|
23
|
-
|
|
35
|
+
export const makeOracleCommand = (logger, io = {}) => {
|
|
36
|
+
const {
|
|
37
|
+
delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
38
|
+
execFileSync = cp.execFileSync,
|
|
39
|
+
env = process.env,
|
|
40
|
+
stdout = process.stdout,
|
|
41
|
+
} = io;
|
|
42
|
+
const oracle = new Command('oracle')
|
|
43
|
+
.description('Oracle commands')
|
|
44
|
+
.usage(
|
|
45
|
+
`
|
|
24
46
|
WALLET=my-wallet
|
|
25
47
|
export AGORIC_NET=ollinet
|
|
26
48
|
|
|
@@ -40,9 +62,18 @@ export const makeOracleCommand = logger => {
|
|
|
40
62
|
# sign and send
|
|
41
63
|
agoric wallet send --from $WALLET --offer offer-12.json
|
|
42
64
|
`,
|
|
43
|
-
|
|
65
|
+
)
|
|
66
|
+
.option(
|
|
67
|
+
'--keyring-backend <os|file|test>',
|
|
68
|
+
`keyring's backend (os|file|test) (default "${
|
|
69
|
+
env.AGORIC_KEYRING_BACKEND || 'os'
|
|
70
|
+
}")`,
|
|
71
|
+
env.AGORIC_KEYRING_BACKEND,
|
|
72
|
+
);
|
|
44
73
|
|
|
45
74
|
const rpcTools = async () => {
|
|
75
|
+
// XXX pass fetch to getNetworkConfig() explicitly
|
|
76
|
+
const networkConfig = await getNetworkConfig(env);
|
|
46
77
|
const utils = await makeRpcUtils({ fetch });
|
|
47
78
|
|
|
48
79
|
const lookupPriceAggregatorInstance = ([brandIn, brandOut]) => {
|
|
@@ -55,7 +86,7 @@ export const makeOracleCommand = logger => {
|
|
|
55
86
|
return instance;
|
|
56
87
|
};
|
|
57
88
|
|
|
58
|
-
return { ...utils, lookupPriceAggregatorInstance };
|
|
89
|
+
return { ...utils, networkConfig, lookupPriceAggregatorInstance };
|
|
59
90
|
};
|
|
60
91
|
|
|
61
92
|
oracle
|
|
@@ -96,37 +127,6 @@ export const makeOracleCommand = logger => {
|
|
|
96
127
|
console.warn('Now execute the prepared offer');
|
|
97
128
|
});
|
|
98
129
|
|
|
99
|
-
oracle
|
|
100
|
-
.command('pushPrice')
|
|
101
|
-
.description('add a current price sample to a priceAggregator')
|
|
102
|
-
.option('--offerId <string>', 'Offer id', String, `pushPrice-${Date.now()}`)
|
|
103
|
-
.requiredOption(
|
|
104
|
-
'--oracleAdminAcceptOfferId <number>',
|
|
105
|
-
'offer that had continuing invitation result',
|
|
106
|
-
Number,
|
|
107
|
-
)
|
|
108
|
-
.requiredOption('--price <number>', 'price (format TODO)', String)
|
|
109
|
-
.action(async function (opts) {
|
|
110
|
-
/** @type {import('@agoric/smart-wallet/src/offers.js').OfferSpec} */
|
|
111
|
-
const offer = {
|
|
112
|
-
id: opts.offerId,
|
|
113
|
-
invitationSpec: {
|
|
114
|
-
source: 'continuing',
|
|
115
|
-
previousOffer: opts.oracleAdminAcceptOfferId,
|
|
116
|
-
invitationMakerName: 'PushPrice',
|
|
117
|
-
invitationArgs: harden([opts.price]),
|
|
118
|
-
},
|
|
119
|
-
proposal: {},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
outputAction({
|
|
123
|
-
method: 'executeOffer',
|
|
124
|
-
offer,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
console.warn('Now execute the prepared offer');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
130
|
oracle
|
|
131
131
|
.command('pushPriceRound')
|
|
132
132
|
.description('add a price for a round to a fluxAggregator')
|
|
@@ -162,27 +162,33 @@ export const makeOracleCommand = logger => {
|
|
|
162
162
|
console.warn('Now execute the prepared offer');
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
+
const findOracleCap = async (from, readLatestHead) => {
|
|
166
|
+
const current = await getCurrent(from, { readLatestHead });
|
|
167
|
+
|
|
168
|
+
const { offerToUsedInvitation: entries } = /** @type {any} */ (current);
|
|
169
|
+
Array.isArray(entries) || Fail`entries must be an array: ${entries}`;
|
|
170
|
+
|
|
171
|
+
for (const [offerId, { value }] of entries) {
|
|
172
|
+
/** @type {{ description: string, instance: unknown }[]} */
|
|
173
|
+
const [{ description }] = value;
|
|
174
|
+
if (description === 'oracle invitation') {
|
|
175
|
+
return offerId;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
165
180
|
oracle
|
|
166
181
|
.command('find-continuing-id')
|
|
167
182
|
.description('print id of specified oracle continuing invitation')
|
|
168
183
|
.requiredOption('--from <address>', 'from address', String)
|
|
169
184
|
.action(async opts => {
|
|
170
185
|
const { readLatestHead } = await makeRpcUtils({ fetch });
|
|
171
|
-
const current = await getCurrent(opts.from, { readLatestHead });
|
|
172
|
-
|
|
173
|
-
const { offerToUsedInvitation: entries } = /** @type {any} */ (current);
|
|
174
|
-
Array.isArray(entries) || Fail`entries must be an array: ${entries}`;
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (description === 'oracle invitation') {
|
|
180
|
-
console.log(offerId);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
187
|
+
const offerId = await findOracleCap(opts.from, readLatestHead);
|
|
188
|
+
if (!offerId) {
|
|
189
|
+
console.error('No continuing ids found');
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
console.error('No continuing ids found');
|
|
191
|
+
console.log(offerId);
|
|
186
192
|
});
|
|
187
193
|
|
|
188
194
|
oracle
|
|
@@ -205,5 +211,141 @@ export const makeOracleCommand = logger => {
|
|
|
205
211
|
|
|
206
212
|
console.log(inspect(capDatas[0], { depth: 10, colors: true }));
|
|
207
213
|
});
|
|
214
|
+
|
|
215
|
+
/** @param {string} literalOrName */
|
|
216
|
+
const normalizeAddress = literalOrName =>
|
|
217
|
+
normalizeAddressWithOptions(literalOrName, oracle.opts(), {
|
|
218
|
+
execFileSync,
|
|
219
|
+
});
|
|
220
|
+
const show = (info, indent = false) =>
|
|
221
|
+
stdout.write(
|
|
222
|
+
`${JSON.stringify(info, bigintReplacer, indent ? 2 : undefined)}\n`,
|
|
223
|
+
);
|
|
224
|
+
/** @param {bigint} secs */
|
|
225
|
+
const fmtSecs = secs => new Date(Number(secs) * 1000).toISOString();
|
|
226
|
+
|
|
227
|
+
oracle
|
|
228
|
+
.command('setPrice')
|
|
229
|
+
.description('set price by pushing from multiple operators')
|
|
230
|
+
.requiredOption(
|
|
231
|
+
'--pair [brandIn.brandOut]',
|
|
232
|
+
'token pair (brandIn.brandOut)',
|
|
233
|
+
s => s.split('.'),
|
|
234
|
+
['ATOM', 'USD'],
|
|
235
|
+
)
|
|
236
|
+
.requiredOption(
|
|
237
|
+
'--keys [key1,key2,...]',
|
|
238
|
+
'key names of operators (comma separated)',
|
|
239
|
+
s => s.split(','),
|
|
240
|
+
['gov1', 'gov2'],
|
|
241
|
+
)
|
|
242
|
+
.requiredOption('--price <number>', 'price', Number)
|
|
243
|
+
.action(
|
|
244
|
+
async (
|
|
245
|
+
/**
|
|
246
|
+
* @type {{
|
|
247
|
+
* pair: [brandIn: string, brandOut: string],
|
|
248
|
+
* keys: string[],
|
|
249
|
+
* price: number,
|
|
250
|
+
* }}
|
|
251
|
+
*/ { pair, keys, price },
|
|
252
|
+
) => {
|
|
253
|
+
const { readLatestHead, networkConfig } = await rpcTools();
|
|
254
|
+
const wutil = await makeWalletUtils(
|
|
255
|
+
{ fetch, execFileSync, delay },
|
|
256
|
+
networkConfig,
|
|
257
|
+
);
|
|
258
|
+
const unitPrice = scaleDecimals(price);
|
|
259
|
+
|
|
260
|
+
console.error(`${pair[0]}-${pair[1]}_price_feed: before setPrice`);
|
|
261
|
+
|
|
262
|
+
const readPrice = () =>
|
|
263
|
+
/** @type {Promise<PriceDescription>} */ (
|
|
264
|
+
readLatestHead(
|
|
265
|
+
`published.priceFeed.${pair[0]}-${pair[1]}_price_feed`,
|
|
266
|
+
).catch(err => {
|
|
267
|
+
console.warn(`cannot get ${pair[0]}-${pair[1]}_price_feed`, err);
|
|
268
|
+
return undefined;
|
|
269
|
+
})
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const r4 = x => Math.round(x * 10000) / 10000; // XXX 4 decimals arbitrary
|
|
273
|
+
const fmtFeed = ({
|
|
274
|
+
amountIn: { value: valueIn },
|
|
275
|
+
amountOut: { value: valueOut },
|
|
276
|
+
timestamp: { absValue: ts },
|
|
277
|
+
}) => ({
|
|
278
|
+
timestamp: fmtSecs(ts),
|
|
279
|
+
price: r4(Number(valueOut) / Number(valueIn)),
|
|
280
|
+
});
|
|
281
|
+
const before = await readPrice();
|
|
282
|
+
if (before) {
|
|
283
|
+
show(fmtFeed(before));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.error(
|
|
287
|
+
'Choose lead oracle operator order based on latestRound...',
|
|
288
|
+
);
|
|
289
|
+
const keyOrder = keys.map(normalizeAddress);
|
|
290
|
+
const latestRoundP = readLatestHead(
|
|
291
|
+
`published.priceFeed.${pair[0]}-${pair[1]}_price_feed.latestRound`,
|
|
292
|
+
);
|
|
293
|
+
await Promise.race([
|
|
294
|
+
delay(5000),
|
|
295
|
+
latestRoundP.then(round => {
|
|
296
|
+
// @ts-expect-error XXX get type from contract
|
|
297
|
+
const { roundId, startedAt, startedBy } = round;
|
|
298
|
+
show({
|
|
299
|
+
startedAt: fmtSecs(startedAt.absValue),
|
|
300
|
+
roundId,
|
|
301
|
+
startedBy,
|
|
302
|
+
});
|
|
303
|
+
if (startedBy === keyOrder[0]) {
|
|
304
|
+
keyOrder.reverse();
|
|
305
|
+
}
|
|
306
|
+
}),
|
|
307
|
+
]).catch(err => {
|
|
308
|
+
console.warn(err);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
console.error('pushPrice from each:', keyOrder);
|
|
312
|
+
for await (const from of keyOrder) {
|
|
313
|
+
const oracleAdminAcceptOfferId = await findOracleCap(
|
|
314
|
+
from,
|
|
315
|
+
readLatestHead,
|
|
316
|
+
);
|
|
317
|
+
if (!oracleAdminAcceptOfferId) {
|
|
318
|
+
throw Error(`no oracle invitation found: ${from}`);
|
|
319
|
+
}
|
|
320
|
+
show({ from, oracleAdminAcceptOfferId });
|
|
321
|
+
const offerId = `pushPrice-${Date.now()}`;
|
|
322
|
+
const offer = Offers.fluxAggregator.PushPrice(
|
|
323
|
+
{},
|
|
324
|
+
{ offerId, unitPrice },
|
|
325
|
+
oracleAdminAcceptOfferId,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const { home, keyringBackend: backend } = oracle.opts();
|
|
329
|
+
const tools = { ...networkConfig, execFileSync, delay, stdout };
|
|
330
|
+
const result = await sendAction(
|
|
331
|
+
{ method: 'executeOffer', offer },
|
|
332
|
+
{ keyring: { home, backend }, from, verbose: false, ...tools },
|
|
333
|
+
);
|
|
334
|
+
assert(result); // Not dry-run
|
|
335
|
+
const { timestamp, txhash, height } = result;
|
|
336
|
+
console.error('pushPrice', price, 'offer broadcast');
|
|
337
|
+
show({ timestamp, height, offerId: offer.id, txhash });
|
|
338
|
+
const found = await wutil.pollOffer(from, offer.id, result.height);
|
|
339
|
+
console.error('pushPrice', price, 'offer satisfied');
|
|
340
|
+
show(found);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const after = await readPrice();
|
|
344
|
+
if (after) {
|
|
345
|
+
console.error('price set:');
|
|
346
|
+
show(fmtFeed(after));
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
);
|
|
208
350
|
return oracle;
|
|
209
351
|
};
|