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

Measure allocations in priceFeeds #9867

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
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
36 changes: 19 additions & 17 deletions a3p-integration/proposals/a:upgrade-next/agd-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
GOV3ADDR,
newOfferId,
VALIDATORADDR,
waitForBlock,
} from '@agoric/synthetic-chain';

const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR];
Expand Down Expand Up @@ -105,25 +106,26 @@ export const generateOracleMap = (baseId, brandNames) => {
return oraclesByBrand;
};

export const pushPrices = (price, brandIn, oraclesByBrand) => {
const promiseArray = [];

for (const oracle of oraclesByBrand.get(brandIn)) {
promiseArray.push(
executeOffer(
oracle.address,
agops.oracle(
'pushPriceRound',
'--price',
price,
'--oracleAdminAcceptOfferId',
oracle.offerId,
),
),
export const pushPrices = async (price, brandIn, oraclesByBrand, round) => {
await waitForBlock(1);
// rotate which oracle is first. Use the round number
const oracles = oraclesByBrand.get(brandIn);
for (let i = 0; i < oracles.length; i += 1) {
const offset = (i + round) % oracles.length;
debugger;
console.log('AGDTool', offset, brandIn, oraclesByBrand.get(brandIn));
const oracle = oraclesByBrand.get(brandIn)[offset];
const oracleCmd = await agops.oracle(
'pushPriceRound',
'--price',
price,
'--oracleAdminAcceptOfferId',
oracle.offerId,
'--roundId',
round,
);
await executeOffer(oracle.address, oracleCmd);
}

return Promise.all(promiseArray);
};

export const getPriceQuote = async price => {
Expand Down
61 changes: 61 additions & 0 deletions a3p-integration/proposals/a:upgrade-next/sql-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import dbOpenAmbient from 'better-sqlite3';

export const HOME = process.env.HOME;

/** @type {<T>(val: T | undefined) => T} */
export const NonNullish = val => {
if (!val) throw Error('required');
return val;
};
const swingstorePath = '~/.agoric/data/agoric/swingstore.sqlite';

/** @param {import('better-sqlite3').Database} db */
export const dbTool = db => {
const prepare = (strings, ...params) => {
const dml = strings.join('?');
return { stmt: db.prepare(dml), params };
};
const sql = (strings, ...args) => {
const { stmt, params } = prepare(strings, ...args);
return stmt.all(...params);
};
sql.get = (strings, ...args) => {
const { stmt, params } = prepare(strings, ...args);
return stmt.get(...params);
};
return sql;
};

/** @param {import('better-sqlite3').Database} db */
const objectCensus = db => {
const sql = dbTool(db);

const getCount = () => sql.get`select COUNT(*) from kvStore`;

const kv = getCount();
return kv['COUNT(*)'];
};

export const getObjectCount = async () => {
const fullPath = swingstorePath.replace(/^~/, NonNullish(HOME));
return objectCensus(dbOpenAmbient(fullPath, { readonly: true }));
};

/**
* @param {import('better-sqlite3').Database} db
* @param {string} vatId
*/
const vatObjectCensus = (db, vatId) => {
const sql = dbTool(db);

const getCount = () =>
sql.get([`SELECT COUNT(*) from kvStore WHERE key LIKE '${vatId}.c.%'`]);

const kv = getCount();
return kv['COUNT(*)'];
};

export const getVatObjectCount = async vatId => {
const fullPath = swingstorePath.replace(/^~/, NonNullish(HOME));
return vatObjectCensus(dbOpenAmbient(fullPath, { readonly: true }), vatId);
};
38 changes: 25 additions & 13 deletions a3p-integration/proposals/a:upgrade-next/upgradeVaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,36 @@ import {
pushPrices,
registerOraclesForBrand,
} from './agd-tools.js';
import { getDetailsMatchingVats } from './vatDetails.js';

const BRANDNAMES = ['ATOM', 'stATOM', 'stTIA', 'stOSMO', 'stkATOM'];
const BRANDNAMES = ['ATOM', 'stATOM'];
const oraclesByBrand = generateOracleMap('u16', BRANDNAMES);

// There are no old prices for the other currencies.
const atomOutPre = await getPriceQuote('ATOM');
assert.equal(atomOutPre, '+12010000');

console.log('adding oracle for each brand');
console.log('UPGV: adding oracle for each brand');
await registerOraclesForBrand('ATOM', oraclesByBrand);
await registerOraclesForBrand('stATOM', oraclesByBrand);
await registerOraclesForBrand('stTIA', oraclesByBrand);
await registerOraclesForBrand('stOSMO', oraclesByBrand);
await registerOraclesForBrand('stkATOM', oraclesByBrand);

console.log('pushing new prices');
await pushPrices(11.2, 'ATOM', oraclesByBrand);
await pushPrices(11.3, 'stTIA', oraclesByBrand);
await pushPrices(11.4, 'stATOM', oraclesByBrand);
await pushPrices(11.5, 'stOSMO', oraclesByBrand);
await pushPrices(11.6, 'stkATOM', oraclesByBrand);

const round = 1;
console.log('UPGV: pushing new prices');
await pushPrices(11.2, 'ATOM', oraclesByBrand, round);
await pushPrices(11.4, 'stATOM', oraclesByBrand, round);

// price_feed and governor, old and new for two tokens
const priceFeedDetails = await getDetailsMatchingVats('price_feed');
assert.equal(Object.keys(priceFeedDetails).length, 8);

// Two old SPAs, and two new ones
const details = await getDetailsMatchingVats('scaledPriceAuthority');
assert.equal(Object.keys(details).length, 4, Object.keys(details));
console.log('UPGV 8 price feeds and 4 scaledPriceAuthorities found');

// We previously created price feeds for some tokens that aren't in A3P
const osmoDetails = await getDetailsMatchingVats('stOSMO');
assert.equal(Object.keys(osmoDetails).length, 0);
const tiaDetails = await getDetailsMatchingVats('stTIA');
assert.equal(Object.keys(tiaDetails).length, 0);
const stkAtomDetails = await getDetailsMatchingVats('stkATOM');
assert.equal(Object.keys(stkAtomDetails).length, 0);
148 changes: 122 additions & 26 deletions a3p-integration/proposals/a:upgrade-next/upgradeVaults.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import test from 'ava';

import {
agd,
agops,
ATOM_DENOM,
getISTBalance,
getVatDetails,
GOV1ADDR,
GOV2ADDR,
GOV3ADDR,
mintIST,
openVault,
USER1ADDR,
} from '@agoric/synthetic-chain';
Expand All @@ -20,42 +25,98 @@ import {
getVaultPrices,
pushPrices,
} from './agd-tools.js';
import { getDetailsMatchingVats } from './vatDetails.js';
import { getDetailsByVatId, getDetailsMatchingVats } from './vatDetails.js';
import { getObjectCount, getVatObjectCount } from './sql-tools.js';

const logGovBalances = async () => {
const [IST1, IST2, IST3] = await Promise.all([
getISTBalance(GOV1ADDR),
getISTBalance(GOV2ADDR),
getISTBalance(GOV3ADDR),
]);
console.log(' BALANCES', {
[GOV1ADDR]: IST1,
[GOV2ADDR]: IST2,
[GOV3ADDR]: IST3,
});
};

await null;

const INTERESTING_VATS = [7, 9, 29, 43, 45, 46, 48, 68, 70, 73, 74];

async function printAllVatLevels() {
const deets7 = await getDetailsByVatId(`v${7}`);
const n7 = await getVatObjectCount(`v7`);
console.log('UPG ', '7', n7, deets7.name);

const deets9 = await getDetailsByVatId(`v9`);
const n9 = await getVatObjectCount(`v9`);
console.log('UPG ', '9', n9, deets9.name);

for (let vatId = 25; vatId < 75; vatId += 1) {
const deets = await getDetailsByVatId(`v${vatId}`);
const n = await getVatObjectCount(`v${vatId}`);
console.log('UPG ', vatId, n, deets.name);
}
}
await printAllVatLevels();

const surveyVats = async (index = '') => {
await null;
const counts = [];
for (const v of INTERESTING_VATS) {
const n = await getVatObjectCount(`v${v}`);
counts.push(n);
}
const tot = await getObjectCount();
console.log(`SURVEY: ${index}, ${tot}, ${counts}`);
return counts;
};

const checkPriceFeedVatsUpdated = async t => {
const atomDetails = await getVatDetails('ATOM-USD_price_feed');
const atomDetails = await getVatDetails('-ATOM-USD_price_feed');
// both the original and the new ATOM vault are incarnation 0
t.is(atomDetails.incarnation, 0);
const stAtomDetails = await getVatDetails('stATOM');
t.is(stAtomDetails.incarnation, 0);
const stOsmoDetails = await getVatDetails('stOSMO');
t.is(stOsmoDetails.incarnation, 0);
const stTiaDetails = await getVatDetails('stTIA');
t.is(stTiaDetails.incarnation, 0);
console.log('UPG ATOM', atomDetails.incarnation);

await Promise.all([checkForOracle(t, 'ATOM'), checkForOracle(t, 'stATOM')]);
const balances = await agd.query('bank', 'balances', GOV3ADDR);
console.log('GOV3 Balances', balances);

await surveyVats();

await logGovBalances();
};

const replenishBalances = async t => {
await Promise.all([
checkForOracle(t, 'ATOM'),
checkForOracle(t, 'stATOM'),
checkForOracle(t, 'stTIA'),
checkForOracle(t, 'stOSMO'),
checkForOracle(t, 'stkATOM'),
// gov1 has plenty
// mintIST(GOV1ADDR, 100000, 600, 100),
mintIST(GOV2ADDR, 100000, 600, 100),
mintIST(GOV3ADDR, 100000, 600, 100),
]);
await logGovBalances();
t.pass('foo');
};

const BRANDNAMES = ['ATOM', 'stATOM', 'stTIA', 'stOSMO', 'stkATOM'];
const BRANDNAMES = ['ATOM', 'stATOM'];
console.log('adding oracle for each brand');
const oraclesByBrand = generateOracleMap('u16', BRANDNAMES);
let currentRound = 1;

const tryPushPrices = async t => {
const atomOutPre = await getPriceQuote('ATOM');
t.is(atomOutPre, '+11200000');

t.log('pushing new prices');
await pushPrices(11.9, 'ATOM', oraclesByBrand, currentRound);
await logGovBalances();
currentRound += 1;

const checkNewQuotes = async t => {
t.log('awaiting new quotes');
const atomOut = await getPriceQuote('ATOM');
t.is(atomOut, '+11200000');
const tiaOut = await getPriceQuote('stTIA');
t.is(tiaOut, '+11300000');
const stAtomOut = await getPriceQuote('stATOM');
t.is(stAtomOut, '+11400000');
const osmoOut = await getPriceQuote('stOSMO');
t.is(osmoOut, '+11500000');
const stkAtomOut = await getPriceQuote('stkATOM');
t.is(stkAtomOut, '+11600000');
};

const createNewBid = async t => {
Expand Down Expand Up @@ -84,7 +145,9 @@ const openMarginalVault = async t => {
};

const triggerAuction = async t => {
await pushPrices(5.2, 'ATOM', oraclesByBrand);
await pushPrices(5.2, 'ATOM', oraclesByBrand, currentRound);
currentRound += 1;
await logGovBalances();

const atomOut = await getPriceQuote('ATOM');
t.is(atomOut, '+5200000');
Expand All @@ -96,6 +159,33 @@ const checkAuctionVat = async t => {
t.true(Object.keys(details).length > 2);
};

const pushNewPrice = async (t, i) => {
const j = i % 2 ? -5 : 5;
await pushPrices(42.5 + j, 'ATOM', oraclesByBrand, currentRound);
currentRound += 1;
await logGovBalances();

return surveyVats(i);
};

const sendManyPrices = async t => {
await null;
const balances = await agd.query('bank', 'balances', GOV3ADDR);
console.log('GOV3 Balances', balances);

const p = [];
for (let i = 0; i < 200; i += 1) {
// for (let i = 0; i < 10; i += 1) {
p.push(await pushNewPrice(t, i));
}

console.log('SURVEY', p);

await printAllVatLevels();

t.pass();
};

const verifyVaultPriceUpdate = async t => {
const quote = await getVaultPrices(0);

Expand All @@ -108,8 +198,11 @@ test('liquidation post upgrade', async t => {
t.log('starting upgrade vaults test');
await checkPriceFeedVatsUpdated(t);

t.log('check new price quotes');
await checkNewQuotes(t);
console.log('replenish Balances');
await replenishBalances(t);

t.log('push Prices');
await tryPushPrices(t);

t.log('create a new Bid for the auction');
await createNewBid(t);
Expand All @@ -123,6 +216,9 @@ test('liquidation post upgrade', async t => {
t.log('make new auction');
await checkAuctionVat(t);

t.log('send many price updates');
await sendManyPrices(t);

t.log('vault price updated');
await verifyVaultPriceUpdate(t);
});
16 changes: 15 additions & 1 deletion a3p-integration/proposals/a:upgrade-next/vatDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,25 @@ export const getDetailsMatchingVats = async vatName => {
const infos = [];
for (const vatID of vatIDs) {
const vatInfo = kStore.lookupVat(vatID);
const name = vatInfo.options().name;
const source = vatInfo.source();
// @ts-expect-error cast
const { incarnation } = vatInfo.currentSpan();
infos.push({ vatName, vatID, incarnation, ...source });
infos.push({ vatName: name, vatID, incarnation, ...source });
}

return infos;
};

export const getDetailsByVatId = async vatId => {
const kStore = makeSwingstore(
dbOpenAmbient(swingstorePath, { readonly: true }),
);

const vatInfo = kStore.lookupVat(vatId);
const source = vatInfo.source();
// @ts-expect-error cast
const { incarnation } = vatInfo.currentSpan();
const options = vatInfo.options();
return { name: options.name, vatId, incarnation, options, ...source };
};
Loading
Loading