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

Upgrade VaultFactory in A3P, and use new auctions and price feeds #8757

Closed
wants to merge 2 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
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