Skip to content

Commit

Permalink
test: verify that vaults and auctions upgrade correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris-Hibbert committed Apr 24, 2024
1 parent eae60be commit a30f7be
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 111 deletions.
3 changes: 2 additions & 1 deletion a3p-integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"doctor": "yarn synthetic-chain doctor"
},
"dependencies": {
"@agoric/synthetic-chain": "^0.0.10"
"@agoric/synthetic-chain": "^0.0.10",
"@types/better-sqlite3": "^7.6.9"
},
"packageManager": "[email protected]",
"license": "Apache-2.0"
Expand Down
138 changes: 138 additions & 0 deletions a3p-integration/proposals/a:upgrade-next/agd-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
agd,
agops,
agopsLocation,
executeCommand,
VALIDATORADDR,
executeOffer,
GOV1ADDR,
GOV2ADDR,
GOV3ADDR,
newOfferId,
CHAINID,
} from '@agoric/synthetic-chain';

const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR];

export const BID_OFFER_ID = 'bid-vaultUpgrade-test3';

const agdQuery = path =>
agd.query('vstorage', 'data', '--output', 'json', path);

const getQuoteBody = async path => {
const queryout = await agdQuery(path);

const body = JSON.parse(JSON.parse(queryout.value).values[0]);
return JSON.parse(body.body.substring(1));
};

export const getOracleInstance = async price => {
const instanceRec = await agdQuery(`published.agoricNames.instance`);

const value = JSON.parse(instanceRec.value);
const body = JSON.parse(value.values.at(-1));

const feeds = JSON.parse(body.body.substring(1));
const feedName = `${price}-USD price feed`;

const key = Object.keys(feeds).find(k => feeds[k][0] === feedName);
if (key) {
return body.slots[key];
}
return null;
};

export const checkForOracle = async (t, name) => {
const instance = await getOracleInstance(name);
t.truthy(instance);
};

export const addOraclesForBrand = async (brandIn, oraclesByBrand) => {
await null;
const promiseArray = [];

const oraclesWithID = [];
// newOfferId() waits 1 second
const offerIdBase = await newOfferId();
for (let i = 0; i < ORACLE_ADDRESSES.length; i += 1) {
const oracleAddress = ORACLE_ADDRESSES[i];
const offerId = `${offerIdBase}.${i}`;
oraclesWithID.push({ address: oracleAddress, offerId });

promiseArray.push(
executeOffer(
oracleAddress,
agops.oracle('accept', '--offerId', offerId, `--pair ${brandIn}.USD`),
),
);
}
oraclesByBrand.set(brandIn, oraclesWithID);

return Promise.all(promiseArray);
};

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,
),
),
);
}

return Promise.all(promiseArray);
};

export const getPriceQuote = async price => {
const path = `published.priceFeed.${price}-USD_price_feed`;
const body = await getQuoteBody(path);
return body.amountOut.value;
};

export const agopsInter = (...params) => {
const newParams = ['inter', ...params];
return executeCommand(agopsLocation, newParams);
};

export const createBid = (price, addr, offerId) => {
return agopsInter(
'bid',
'by-price',
`--price ${price}`,
`--give 1.0IST`,
'--from',
addr,
'--keyring-backend test',
`--offer-id ${offerId}`,
);
};

export const getLiveOffers = async addr => {
const path = `published.wallet.${addr}.current`;
const body = await getQuoteBody(path);
return body.liveOffers;
};

export const getAuctionCollateral = async index => {
const path = `published.auction.book${index}`;
const body = await getQuoteBody(path);
return body.collateralAvailable.value;
};

export const bankSend = (addr, wanted) => {
const chain = ['--chain-id', CHAINID];
const from = ['--from', VALIDATORADDR];
const testKeyring = ['--keyring-backend', 'test'];
const noise = [...from, ...chain, ...testKeyring, '--yes'];

return agd.tx('bank', 'send', VALIDATORADDR, addr, wanted, ...noise);
};
15 changes: 15 additions & 0 deletions a3p-integration/proposals/a:upgrade-next/post.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import test from 'ava';

import { USER1ADDR, waitForBlock } from '@agoric/synthetic-chain';

import { getLiveOffers } from './agd-tools.js';

// We might have to wait a full cycle for the auction to settle. That's too
// long for a test, so never mind.
test.serial.skip('trigger auction', async t => {
await waitForBlock(2);

const liveOffer = await getLiveOffers(USER1ADDR);
t.log({ liveOffer });
t.is(liveOffer.length, 0, 'There should be no liveOffers remaining');
});
176 changes: 67 additions & 109 deletions a3p-integration/proposals/a:upgrade-next/priceFeed.test.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,25 @@
import test from 'ava';

import {
agd,
agops,
executeOffer,
ATOM_DENOM,
getISTBalance,
getVatDetails,
GOV1ADDR,
GOV2ADDR,
GOV3ADDR,
newOfferId,
openVault,
USER1ADDR,
} from '@agoric/synthetic-chain';

const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR];

const getOracleInstance = async price => {
const instanceRec = await agd.query(
'vstorage',
'data',
'--output',
'json',
`published.agoricNames.instance`,
);

// agd query -o json vstorage data published.agoricNames.instance
// |& jq '.value | fromjson | .values[-1] | fromjson | .body[1:]
// | fromjson | .[-2] '

const value = JSON.parse(instanceRec.value);
const body = JSON.parse(value.values.at(-1));

const feeds = JSON.parse(body.body.substring(1));
const feedName = `${price}-USD price feed`;

const key = Object.keys(feeds).find(k => feeds[k][0] === feedName);
if (key) {
return body.slots[key];
}
return null;
};

const checkForOracle = async (t, name) => {
const instance = await getOracleInstance(name);
t.truthy(instance);
};
import { getDetailsMatchingVats } from './vatDetails.js';
import {
addOraclesForBrand,
bankSend,
BID_OFFER_ID,
checkForOracle,
createBid,
getLiveOffers,
getPriceQuote,
pushPrices,
} from './agd-tools.js';

test.serial('check all priceFeed vats updated', async t => {
const atomDetails = await getVatDetails('ATOM-USD_price_feed');
Expand All @@ -54,91 +31,34 @@ test.serial('check all priceFeed vats updated', async t => {
t.is(stOsmoDetails.incarnation, 0);
const stTiaDetails = await getVatDetails('stTIA');
t.is(stTiaDetails.incarnation, 0);
await checkForOracle(t, 'ATOM');
await checkForOracle(t, 'stATOM');
await checkForOracle(t, 'stTIA');
await checkForOracle(t, 'stOSMO');
await Promise.all([
checkForOracle(t, 'ATOM'),
checkForOracle(t, 'stATOM'),
checkForOracle(t, 'stTIA'),
checkForOracle(t, 'stOSMO'),
]);
});

const oraclesByBrand = new Map();

const addOraclesForBrand = async brandIn => {
await null;
const promiseArray = [];

const oraclesWithID = [];
for (const oracleAddress of ORACLE_ADDRESSES) {
const offerId = await newOfferId();
oraclesWithID.push({ address: oracleAddress, offerId });

promiseArray.push(
executeOffer(
oracleAddress,
agops.oracle('accept', '--offerId', offerId, `--pair ${brandIn}.USD`),
),
);
}
oraclesByBrand.set(brandIn, oraclesWithID);

return Promise.all(promiseArray);
};

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

for (const oracle of oraclesByBrand.get(brandIn)) {
promiseArray.push(
executeOffer(
oracle.address,
agops.oracle(
'pushPriceRound',
'--price',
price,
'--oracleAdminAcceptOfferId',
oracle.offerId,
),
),
);
}

return Promise.all(promiseArray);
};

const getPriceQuote = async price => {
const priceQuote = await agd.query(
'vstorage',
'data',
'--output',
'json',
`published.priceFeed.${price}-USD_price_feed`,
);

const body = JSON.parse(JSON.parse(priceQuote.value).values[0]);
const bodyTruncated = JSON.parse(body.body.substring(1));
return bodyTruncated.amountOut.value;
};

test.serial('push prices', async t => {
// There are no old prices for the other currencies.
t.log('awaiting ATOM price pre');
const atomOutPre = await getPriceQuote('ATOM');
t.is(atomOutPre, '+12010000');

t.log('adding oracle for each brand');
await addOraclesForBrand('ATOM');
await addOraclesForBrand('stATOM');
await addOraclesForBrand('stTIA');
await addOraclesForBrand('stOSMO');
await addOraclesForBrand('ATOM', oraclesByBrand);
await addOraclesForBrand('stATOM', oraclesByBrand);
await addOraclesForBrand('stTIA', oraclesByBrand);
await addOraclesForBrand('stOSMO', oraclesByBrand);

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

t.log('awaiting new quotes');
// agd query -o json vstorage data published.priceFeed.stOSMO-USD_price_feed |&
// jq '.value | fromjson | .values[0] | fromjson | .body[1:] | fromjson | .amountOut.value'
const atomOut = await getPriceQuote('ATOM');
t.is(atomOut, '+11200000');
const tiaOut = await getPriceQuote('stTIA');
Expand All @@ -148,3 +68,41 @@ test.serial('push prices', async t => {
const osmoOut = await getPriceQuote('stOSMO');
t.is(osmoOut, '+11500000');
});

test.serial('create new bid', async t => {
await createBid('20', USER1ADDR, BID_OFFER_ID);
const liveOffer = await getLiveOffers(USER1ADDR);
t.true(liveOffer[0].includes(BID_OFFER_ID));
});

test.serial('open a marginal vault', async t => {
let user1IST = await getISTBalance(USER1ADDR);
await bankSend(USER1ADDR, `20000000${ATOM_DENOM}`);
const currentVaults = await agops.vaults('list', '--from', USER1ADDR);

t.log('opening a vault');
await openVault(USER1ADDR, 5, 10);
user1IST += 5;
const istBalanceAfterVaultOpen = await getISTBalance(USER1ADDR);
t.is(istBalanceAfterVaultOpen, user1IST);

const activeVaultsAfter = await agops.vaults('list', '--from', USER1ADDR);
t.log(currentVaults, activeVaultsAfter);
t.true(
activeVaultsAfter.length > currentVaults.length,
`vaults count should increase, ${activeVaultsAfter.length}, ${currentVaults.length}`,
);
});

test.serial('trigger auction', async t => {
await pushPrices(5.2, 'ATOM', oraclesByBrand);

const atomOut = await getPriceQuote('ATOM');
t.is(atomOut, '+5200000');
});

test.serial('new auction vat', async t => {
const details = await getDetailsMatchingVats('auctioneer');
// This query matches both the auction and its governor, so double the count
t.true(Object.keys(details).length > 2);
});
5 changes: 4 additions & 1 deletion a3p-integration/proposals/a:upgrade-next/test.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#!/bin/bash

GLOBIGNORE=initial.test.js:post.test.js

# Place here any test that should be executed using the executed proposal.
# The effects of this step are not persisted in further proposal layers.

# test the state right after upgrade
yarn ava initial.test.js

# test more, in ways that changes system state
GLOBIGNORE=initial.test.js
yarn ava ./*.test.js

yarn ava post.test.js
Loading

0 comments on commit a30f7be

Please sign in to comment.