From 6e9cd6146513347d98f02505d409036d75c22aca Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Wed, 7 Jun 2023 12:27:44 -0700 Subject: [PATCH] test(bootstrap): liq after add-collateral --- packages/inter-protocol/package.json | 1 + packages/inter-protocol/scripts/add-STARS.js | 44 ++ packages/vats/package.json | 1 + packages/vats/test/bootstrapTests/drivers.js | 12 +- .../vats/test/bootstrapTests/liquidation.js | 70 ++- packages/vats/test/bootstrapTests/supports.js | 2 +- .../test/bootstrapTests/test-liquidation-1.js | 551 ++++++++++-------- 7 files changed, 420 insertions(+), 261 deletions(-) create mode 100644 packages/inter-protocol/scripts/add-STARS.js diff --git a/packages/inter-protocol/package.json b/packages/inter-protocol/package.json index 32ace4e02516..9462af14ee05 100644 --- a/packages/inter-protocol/package.json +++ b/packages/inter-protocol/package.json @@ -10,6 +10,7 @@ "scripts": { "build": "yarn build:bundles", "build:bundles": "node ./scripts/build-bundles.js", + "build:add-STARS-proposal": "agoric run scripts/add-STARS.js", "test": "ava", "test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js", "test:xs": "exit 0", diff --git a/packages/inter-protocol/scripts/add-STARS.js b/packages/inter-protocol/scripts/add-STARS.js new file mode 100644 index 000000000000..8183e9830f91 --- /dev/null +++ b/packages/inter-protocol/scripts/add-STARS.js @@ -0,0 +1,44 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; +import { defaultProposalBuilder as vaultProposalBuilder } from './add-collateral-core.js'; +import { defaultProposalBuilder as oraclesProposalBuilder } from './price-feed-core.js'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const starsVaultProposalBuilder = async powers => { + return vaultProposalBuilder(powers, { + interchainAssetOptions: { + // Values for the Stargaze token on Osmosis + denom: + 'ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4', + decimalPlaces: 6, + keyword: 'STARS', + oracleBrand: 'STARS', + proposedName: 'STARS', + }, + }); +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const starsOraclesProposalBuilder = async powers => { + return oraclesProposalBuilder(powers, { + AGORIC_INSTANCE_NAME: `STARS-USD price feed`, + IN_BRAND_LOOKUP: ['agoricNames', 'oracleBrand', 'STARS'], + IN_BRAND_DECIMALS: 6, + OUT_BRAND_LOOKUP: ['agoricNames', 'oracleBrand', 'USD'], + OUT_BRAND_DECIMALS: 4, + oracleAddresses: [ + // copied from decentral-main-vaults-config.json + 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr', + 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8', + 'agoric144rrhh4m09mh7aaffhm6xy223ym76gve2x7y78', + 'agoric19d6gnr9fyp6hev4tlrg87zjrzsd5gzr5qlfq2p', + 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj', + ], + }); +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('add-STARS', starsVaultProposalBuilder); + await writeCoreProposal('add-STARS-oracles', starsOraclesProposalBuilder); +}; diff --git a/packages/vats/package.json b/packages/vats/package.json index ad128928217f..d712ae3fb0b0 100644 --- a/packages/vats/package.json +++ b/packages/vats/package.json @@ -14,6 +14,7 @@ "build:boot-viz-sim": "node src/authorityViz.js --sim-chain >docs/boot-sim.dot && dot -Tsvg docs/boot-sim.dot >docs/boot-sim.dot.svg", "build:boot-viz-sim-gov": "node src/authorityViz.js --sim-chain --gov >docs/boot-sim-gov.dot && dot -Tsvg docs/boot-sim-gov.dot >docs/boot-sim-gov.dot.svg", "build:restart-vats-proposal": "agoric run scripts/restart-vats.js", + "build:add-STARS-proposal": "agoric run scripts/add-STARS.js", "prepack": "tsc --build jsconfig.build.json", "postpack": "git clean -f '*.d.ts*'", "test": "ava", diff --git a/packages/vats/test/bootstrapTests/drivers.js b/packages/vats/test/bootstrapTests/drivers.js index 237b2a099992..4bfa1dbb684f 100644 --- a/packages/vats/test/bootstrapTests/drivers.js +++ b/packages/vats/test/bootstrapTests/drivers.js @@ -145,28 +145,28 @@ export const makeWalletFactoryDriver = async ( }; /** - * @param {import('@agoric/internal/src/storage-test-utils.js').FakeStorageKit} storage + * @param {string} collateralBrandKey * @param {import('../../tools/board-utils.js').AgoricNamesRemotes} agoricNamesRemotes * @param {Awaited>} walletFactoryDriver * @param {string[]} oracleAddresses */ export const makePriceFeedDriver = async ( - storage, + collateralBrandKey, agoricNamesRemotes, walletFactoryDriver, oracleAddresses, ) => { - // XXX assumes this one feed - const priceFeedName = instanceNameFor('ATOM', 'USD'); + const priceFeedName = instanceNameFor(collateralBrandKey, 'USD'); const oracleWallets = await Promise.all( oracleAddresses.map(addr => walletFactoryDriver.provideSmartWallet(addr)), ); const priceFeedInstance = agoricNamesRemotes.instance[priceFeedName]; - const adminOfferId = 'acceptOracleInvitation'; + priceFeedInstance || Fail`no price feed ${priceFeedName}`; + const adminOfferId = `accept-${collateralBrandKey}-oracleInvitation`; - // accept invitations (TODO move into driver) + // accept invitations await Promise.all( oracleWallets.map(w => w.executeOffer({ diff --git a/packages/vats/test/bootstrapTests/liquidation.js b/packages/vats/test/bootstrapTests/liquidation.js index 8d4b07240212..be8a203577dc 100644 --- a/packages/vats/test/bootstrapTests/liquidation.js +++ b/packages/vats/test/bootstrapTests/liquidation.js @@ -39,9 +39,15 @@ export const makeLiquidationTestContext = async t => { console.timeLog('DefaultTestContext', 'vaultFactoryKit'); // has to be late enough for agoricNames data to have been published - const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage( - swingsetTestKit.storage, - ); + /** @type {import('../../tools/board-utils.js').AgoricNamesRemotes} */ + const agoricNamesRemotes = {}; + const refreshAgoricNamesRemotes = () => { + Object.assign( + agoricNamesRemotes, + makeAgoricNamesRemotesFromFakeStorage(swingsetTestKit.storage), + ); + }; + refreshAgoricNamesRemotes(); agoricNamesRemotes.brand.ATOM || Fail`ATOM missing from agoricNames`; console.timeLog('DefaultTestContext', 'agoricNamesRemotes'); @@ -65,30 +71,42 @@ export const makeLiquidationTestContext = async t => { ); console.timeLog('DefaultTestContext', 'governanceDriver'); - const priceFeedDriver = await makePriceFeedDriver( - storage, - agoricNamesRemotes, - walletFactoryDriver, - // TODO read from the config file - [ - 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr', - 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8', - 'agoric144rrhh4m09mh7aaffhm6xy223ym76gve2x7y78', - 'agoric19d6gnr9fyp6hev4tlrg87zjrzsd5gzr5qlfq2p', - 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj', - ], - ); + const priceFeedDrivers = {}; console.timeLog('DefaultTestContext', 'priceFeedDriver'); console.timeEnd('DefaultTestContext'); - const setupStartingState = async () => { + /** + * + * @param {object} opts + * @param {string} opts.collateralBrandKey + * @param {number} opts.managerIndex + */ + const setupStartingState = async ({ collateralBrandKey, managerIndex }) => { + const managerPath = `published.vaultFactory.managers.manager${managerIndex}`; const { advanceTimeBy, readLatest } = swingsetTestKit; + + !priceFeedDrivers[collateralBrandKey] || + Fail`setup for ${collateralBrandKey} already ran`; + priceFeedDrivers[collateralBrandKey] = await makePriceFeedDriver( + collateralBrandKey, + agoricNamesRemotes, + walletFactoryDriver, + // TODO read from the config file + [ + 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr', + 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8', + 'agoric144rrhh4m09mh7aaffhm6xy223ym76gve2x7y78', + 'agoric19d6gnr9fyp6hev4tlrg87zjrzsd5gzr5qlfq2p', + 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj', + ], + ); + // price feed logic treats zero time as "unset" so advance to nonzero await advanceTimeBy(1, 'seconds'); - await priceFeedDriver.setPrice(12.34); + await priceFeedDrivers[collateralBrandKey].setPrice(12.34); // raise the VaultFactory DebtLimit await governanceDriver.changeParams( @@ -102,7 +120,7 @@ export const makeLiquidationTestContext = async t => { { paramPath: { key: { - collateralBrand: agoricNamesRemotes.brand.ATOM, + collateralBrand: agoricNamesRemotes.brand[collateralBrandKey], }, }, }, @@ -120,7 +138,7 @@ export const makeLiquidationTestContext = async t => { ); // confirm Relevant Governance Parameter Assumptions - t.like(readLatest('published.vaultFactory.managers.manager0.governance'), { + t.like(readLatest(`${managerPath}.governance`), { current: { DebtLimit: { value: { value: DebtLimitValue } }, InterestRate: { @@ -168,11 +186,16 @@ export const makeLiquidationTestContext = async t => { }; const check = { - vaultNotification(vaultIndex, partial) { + /** + * @param {number} managerIndex + * @param {number} vaultIndex + * @param {Record} partial + */ + vaultNotification(managerIndex, vaultIndex, partial) { const { readLatest } = swingsetTestKit; const notification = readLatest( - `published.vaultFactory.managers.manager0.vaults.vault${vaultIndex}`, + `published.vaultFactory.managers.manager${managerIndex}.vaults.vault${vaultIndex}`, ); t.like(notification, partial); }, @@ -183,7 +206,8 @@ export const makeLiquidationTestContext = async t => { agoricNamesRemotes, check, governanceDriver, - priceFeedDriver, + priceFeedDrivers, + refreshAgoricNamesRemotes, setupStartingState, walletFactoryDriver, }; diff --git a/packages/vats/test/bootstrapTests/supports.js b/packages/vats/test/bootstrapTests/supports.js index 9e41b7652b15..377bb2440185 100644 --- a/packages/vats/test/bootstrapTests/supports.js +++ b/packages/vats/test/bootstrapTests/supports.js @@ -28,7 +28,7 @@ E; const sink = () => {}; -const trace = makeTracer('BSTSupport', false); +const trace = makeTracer('BSTSupport'); /** @typedef {Awaited>} BootstrapRootObject */ diff --git a/packages/vats/test/bootstrapTests/test-liquidation-1.js b/packages/vats/test/bootstrapTests/test-liquidation-1.js index ecb397d649d5..b84531998bb6 100644 --- a/packages/vats/test/bootstrapTests/test-liquidation-1.js +++ b/packages/vats/test/bootstrapTests/test-liquidation-1.js @@ -1,6 +1,6 @@ // @ts-check /** - * @file Bootstrap test integration vaults with smart-wallet + * @file Bootstrap test of liquidation across multiple collaterals */ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; @@ -9,6 +9,7 @@ import { makeParseAmount, Offers, } from '@agoric/inter-protocol/src/clientSupport.js'; +import process from 'process'; import { likePayouts, makeLiquidationTestContext, @@ -20,9 +21,6 @@ import { */ const test = anyTest; -// presently all these tests use one collateral manager -const collateralBrandKey = 'ATOM'; - //#region Product spec const setup = /** @type {const} */ ({ vaults: [ @@ -92,6 +90,7 @@ const outcome = /** @type {const} */ ({ reserve: { allocations: { ATOM: 0.309852, + STARS: 0.309852, }, shortfall: 0, }, @@ -129,247 +128,337 @@ test.after.always(t => { }); // Reference: Flow 1 from https://github.com/Agoric/agoric-sdk/issues/7123 -test.serial('scenario: Flow 1', async t => { - const { - advanceTimeBy, - advanceTimeTo, - agoricNamesRemotes, - check, - setupStartingState, - priceFeedDriver, - readLatest, - walletFactoryDriver, - } = t.context; - - await setupStartingState(); - - const minter = await walletFactoryDriver.provideSmartWallet('agoric1minter'); - - for (let i = 0; i < setup.vaults.length; i += 1) { - const offerId = `open-vault${i}`; - await minter.executeOfferMaker(Offers.vaults.OpenVault, { - offerId, - collateralBrandKey, - wantMinted: setup.vaults[i].ist, - giveCollateral: setup.vaults[i].atom, +const checkFlow1 = test.macro( + async (t, { collateralBrandKey, managerIndex }, _expected) => { + // fail if there are any unhandled rejections + process.on('unhandledRejection', error => { + t.fail(/** @type {Error} */ (error).message); }); - t.like(minter.getLatestUpdateRecord(), { - updated: 'offerStatus', - status: { id: offerId, numWantsSatisfied: 1 }, - }); - } - // Verify starting balances - for (let i = 0; i < setup.vaults.length; i += 1) { - check.vaultNotification(i, { - debtSnapshot: { debt: { value: scale6(setup.vaults[i].debt) } }, - locked: { value: scale6(setup.vaults[i].atom) }, - vaultState: 'active', - }); - } + const { + advanceTimeBy, + advanceTimeTo, + agoricNamesRemotes, + check, + setupStartingState, + priceFeedDrivers, + readLatest, + walletFactoryDriver, + } = t.context; - const buyer = await walletFactoryDriver.provideSmartWallet('agoric1buyer'); - { - // --------------- - // Place bids - // --------------- - - const parseAmount = makeParseAmount(agoricNamesRemotes, Error); - await buyer.sendOffer( - Offers.psm.swap( - agoricNamesRemotes.instance['psm-IST-USDC_axl'], - agoricNamesRemotes.brand, - { - offerId: 'print-ist', - wantMinted: 1_000, - pair: ['IST', 'USDC_axl'], - }, - ), - ); + const metricsPath = `published.vaultFactory.managers.manager${managerIndex}.metrics`; - const maxBuy = '10000ATOM'; - - // bids are long-lasting offers so we can't wait here for completion - await buyer.sendOfferMaker(Offers.auction.Bid, { - offerId: 'bid1', - ...setup.bids[0], - maxBuy, - parseAmount, - }); - - t.like(readLatest('published.wallet.agoric1buyer'), { - status: { - id: 'bid1', - result: 'Your bid has been accepted', - payouts: undefined, - }, - }); - await buyer.sendOfferMaker(Offers.auction.Bid, { - offerId: 'bid2', - ...setup.bids[1], - maxBuy, - parseAmount, - }); - t.like(readLatest('published.wallet.agoric1buyer'), { - status: { - id: 'bid2', - result: 'Your bid has been accepted', - payouts: undefined, - }, - }); - await buyer.sendOfferMaker(Offers.auction.Bid, { - offerId: 'bid3', - ...setup.bids[2], - maxBuy, - parseAmount, - }); - t.like(readLatest('published.wallet.agoric1buyer'), { - status: { - id: 'bid3', - result: 'Your bid has been accepted', - payouts: undefined, - }, - }); - } - - { - // --------------- - // Change price to trigger liquidation - // --------------- - - await priceFeedDriver.setPrice(9.99); - - // check nothing liquidating yet - /** @type {import('@agoric/inter-protocol/src/auction/scheduler.js').ScheduleNotification} */ - const liveSchedule = readLatest('published.auction.schedule'); - t.is(liveSchedule.activeStartTime, null); - t.like(readLatest('published.vaultFactory.managers.manager0.metrics'), { - numActiveVaults: setup.vaults.length, - numLiquidatingVaults: 0, - lockedQuote: null, + await setupStartingState({ + collateralBrandKey, + managerIndex, }); - // advance time to start an auction - console.log('step 0 of 10'); - await advanceTimeTo(NonNullish(liveSchedule.nextDescendingStepTime)); - t.like(readLatest('published.vaultFactory.managers.manager0.metrics'), { - numActiveVaults: 0, - numLiquidatingVaults: setup.vaults.length, - liquidatingCollateral: { - value: scale6(setup.auction.start.collateral), - }, - liquidatingDebt: { value: scale6(setup.auction.start.debt) }, - lockedQuote: null, - }); + const minter = await walletFactoryDriver.provideSmartWallet( + 'agoric1minter', + ); - console.log('step 1 of 10'); - await advanceTimeBy(3, 'minutes'); - t.like(readLatest('published.auction.book0'), { - collateralAvailable: { value: scale6(setup.auction.start.collateral) }, - startCollateral: { value: scale6(setup.auction.start.collateral) }, - startProceedsGoal: { value: scale6(setup.auction.start.debt) }, - }); + for (let i = 0; i < setup.vaults.length; i += 1) { + const offerId = `open-${collateralBrandKey}-vault${i}`; + await minter.executeOfferMaker(Offers.vaults.OpenVault, { + offerId, + collateralBrandKey, + wantMinted: setup.vaults[i].ist, + giveCollateral: setup.vaults[i].atom, + }); + t.like(minter.getLatestUpdateRecord(), { + updated: 'offerStatus', + status: { id: offerId, numWantsSatisfied: 1 }, + }); + } + + // Verify starting balances + for (let i = 0; i < setup.vaults.length; i += 1) { + check.vaultNotification(managerIndex, i, { + debtSnapshot: { debt: { value: scale6(setup.vaults[i].debt) } }, + locked: { value: scale6(setup.vaults[i].atom) }, + vaultState: 'active', + }); + } + + const buyer = await walletFactoryDriver.provideSmartWallet('agoric1buyer'); + { + // --------------- + // Place bids + // --------------- + + const parseAmount = makeParseAmount(agoricNamesRemotes, Error); + await buyer.sendOffer( + Offers.psm.swap( + agoricNamesRemotes.instance['psm-IST-USDC_axl'], + agoricNamesRemotes.brand, + { + offerId: `print-${collateralBrandKey}-ist`, + wantMinted: 1_000, + pair: ['IST', 'USDC_axl'], + }, + ), + ); + + const maxBuy = `10000${collateralBrandKey}`; + + // bids are long-lasting offers so we can't wait here for completion + await buyer.sendOfferMaker(Offers.auction.Bid, { + offerId: `${collateralBrandKey}-bid1`, + ...setup.bids[0], + maxBuy, + parseAmount, + }); + + t.like(readLatest('published.wallet.agoric1buyer'), { + status: { + id: `${collateralBrandKey}-bid1`, + result: 'Your bid has been accepted', + payouts: undefined, + }, + }); + await buyer.sendOfferMaker(Offers.auction.Bid, { + offerId: `${collateralBrandKey}-bid2`, + ...setup.bids[1], + maxBuy, + parseAmount, + }); + t.like(readLatest('published.wallet.agoric1buyer'), { + status: { + id: `${collateralBrandKey}-bid2`, + result: 'Your bid has been accepted', + payouts: undefined, + }, + }); + await buyer.sendOfferMaker(Offers.auction.Bid, { + offerId: `${collateralBrandKey}-bid3`, + ...setup.bids[2], + maxBuy, + parseAmount, + }); + t.like(readLatest('published.wallet.agoric1buyer'), { + status: { + id: `${collateralBrandKey}-bid3`, + result: 'Your bid has been accepted', + payouts: undefined, + }, + }); + } - console.log('step 2 of 10'); - await advanceTimeBy(3, 'minutes'); - - console.log('step 3 of 10'); - await advanceTimeBy(3, 'minutes'); - // XXX updates for bid1 and bid2 are appended in the same turn so readLatest gives bid2 - // NB: console output shows 8897786n payout which matches spec 8.897ATOM - // t.like(readLatest('published.wallet.agoric1buyer'), { - // status: { - // id: 'bid1', - // payouts: { - // Bid: { value: 0n }, - // Collateral: { value: scale6(outcome.bids[0].payouts.Collateral) }, - // }, - // }, - // }); - - t.like(readLatest('published.wallet.agoric1buyer'), { - status: { - id: 'bid2', - payouts: likePayouts(outcome.bids[1].payouts), + { + // --------------- + // Change price to trigger liquidation + // --------------- + + await priceFeedDrivers[collateralBrandKey].setPrice(9.99); + + // check nothing liquidating yet + /** @type {import('@agoric/inter-protocol/src/auction/scheduler.js').ScheduleNotification} */ + const liveSchedule = readLatest('published.auction.schedule'); + t.is(liveSchedule.activeStartTime, null); + t.like(readLatest(metricsPath), { + numActiveVaults: setup.vaults.length, + numLiquidatingVaults: 0, + }); + + // advance time to start an auction + console.log(collateralBrandKey, 'step 0 of 10'); + await advanceTimeTo(NonNullish(liveSchedule.nextDescendingStepTime)); + t.like(readLatest(metricsPath), { + numActiveVaults: 0, + numLiquidatingVaults: setup.vaults.length, + liquidatingCollateral: { + value: scale6(setup.auction.start.collateral), + }, + liquidatingDebt: { value: scale6(setup.auction.start.debt) }, + lockedQuote: null, + }); + + console.log(collateralBrandKey, 'step 1 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + t.like(readLatest(`published.auction.book${managerIndex}`), { + collateralAvailable: { value: scale6(setup.auction.start.collateral) }, + startCollateral: { value: scale6(setup.auction.start.collateral) }, + startProceedsGoal: { value: scale6(setup.auction.start.debt) }, + }); + + console.log(collateralBrandKey, 'step 2 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + + console.log(collateralBrandKey, 'step 3 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + // XXX updates for bid1 and bid2 are appended in the same turn so readLatest gives bid2 + // NB: console output shows 8897786n payout which matches spec 8.897ATOM + // t.like(readLatest('published.wallet.agoric1buyer'), { + // status: { + // id: `${collateralBrandKey}-bid1`, + // payouts: { + // Bid: { value: 0n }, + // Collateral: { value: scale6(outcome.bids[0].payouts.Collateral) }, + // }, + // }, + // }); + + t.like(readLatest('published.wallet.agoric1buyer'), { + status: { + id: `${collateralBrandKey}-bid2`, + payouts: likePayouts(outcome.bids[1].payouts), + }, + }); + + console.log(collateralBrandKey, 'step 4 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + + console.log(collateralBrandKey, 'step 5 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + t.like(readLatest(`published.auction.book${managerIndex}`), { + collateralAvailable: { value: 9659301n }, + }); + + console.log(collateralBrandKey, 'step 6 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + + console.log(collateralBrandKey, 'step 7 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + + console.log(collateralBrandKey, 'step 8 of 10'); + readLatest('published.auction.schedule'); + await advanceTimeBy(3, 'minutes'); + // Not part of product spec + t.like(readLatest(metricsPath), { + numActiveVaults: 0, + numLiquidationsCompleted: setup.vaults.length, + numLiquidatingVaults: 0, + retainedCollateral: { value: 0n }, + totalCollateral: { value: 0n }, + totalCollateralSold: { value: 35340699n }, + totalDebt: { value: 0n }, + totalOverageReceived: { value: 0n }, + totalProceedsReceived: { value: 309540000n }, + totalShortfallReceived: { value: 0n }, + }); + + console.log(collateralBrandKey, 'step 9 of 10'); + readLatest('published.auction.schedule'); + // continuing after now would start a new auction + { + /** @type {Record} */ + const { nextDescendingStepTime, nextStartTime } = readLatest( + 'published.auction.schedule', + ); + t.is(nextDescendingStepTime.absValue, nextStartTime.absValue); + } + + // console.log(collateralBrandKey, 'step 10 of 10'); + // readLatest('published.auction.schedule'); + // await advanceTimeBy(3, 'minutes'); + + // console.log(collateralBrandKey, 'step 11 of 10'); + // readLatest('published.auction.schedule'); + // await advanceTimeBy(3, 'minutes'); + + // bid3 still live because it's not fully satisfied + const { liveOffers } = readLatest( + 'published.wallet.agoric1buyer.current', + ); + t.is(liveOffers[0][1].id, `${collateralBrandKey}-bid3`); + // exit to get payouts + await buyer.tryExitOffer(`${collateralBrandKey}-bid3`); + t.like(readLatest('published.wallet.agoric1buyer'), { + status: { + id: `${collateralBrandKey}-bid3`, + payouts: likePayouts(outcome.bids[2].payouts), + }, + }); + + // TODO express spec up top in a way it can be passed in here + check.vaultNotification(managerIndex, 0, { + debt: undefined, + vaultState: 'liquidated', + locked: { + value: scale6(outcome.vaultsActual[0].locked), + }, + }); + check.vaultNotification(managerIndex, 1, { + debt: undefined, + vaultState: 'liquidated', + locked: { + value: scale6(outcome.vaultsActual[1].locked), + }, + }); + } + + // check reserve balances + t.like(readLatest('published.reserve.metrics'), { + allocations: { + [collateralBrandKey]: { + value: scale6(outcome.reserve.allocations[collateralBrandKey]), + }, }, + shortfallBalance: { value: scale6(outcome.reserve.shortfall) }, }); + }, +); - console.log('step 4 of 10'); - await advanceTimeBy(3, 'minutes'); - - console.log('step 5 of 10'); - await advanceTimeBy(3, 'minutes'); - t.like(readLatest('published.auction.book0'), { - collateralAvailable: { value: 9659301n }, - }); - - console.log('step 6 of 10'); - await advanceTimeBy(3, 'minutes'); - - console.log('step 7 of 10'); - await advanceTimeBy(3, 'minutes'); - - console.log('step 8 of 10'); - await advanceTimeBy(3, 'minutes'); - // Not part of product spec - t.like(readLatest('published.vaultFactory.managers.manager0.metrics'), { - numActiveVaults: 0, - numLiquidationsCompleted: setup.vaults.length, - numLiquidatingVaults: 0, - retainedCollateral: { value: 0n }, - totalCollateral: { value: 0n }, - totalCollateralSold: { value: 35340699n }, - totalDebt: { value: 0n }, - totalOverageReceived: { value: 0n }, - totalProceedsReceived: { value: 309540000n }, - totalShortfallReceived: { value: 0n }, - }); - - console.log('step 9 of 10'); - await advanceTimeBy(3, 'minutes'); +test.skip('setup ATOM', async t => { + // no need to eval a proposal because it's part of the bootstrap under test + await t.context.setupStartingState({ + collateralBrandKey: 'ATOM', + managerIndex: 0, + }); + t.pass(); // reached here without throws +}); - console.log('step 10 of 10'); - await advanceTimeBy(3, 'minutes'); +test.serial( + 'liquidate ATOM', + checkFlow1, + { collateralBrandKey: 'ATOM', managerIndex: 0 }, + {}, +); - console.log('step 11 of 10'); - await advanceTimeBy(3, 'minutes'); +test.serial('add STARS collateral', async t => { + const { controller, buildProposal } = t.context; - // bid3 still live because it's not fully satisfied - const { liveOffers } = readLatest('published.wallet.agoric1buyer.current'); - t.is(liveOffers[0][1].id, 'bid3'); - // exit to get payouts - await buyer.tryExitOffer('bid3'); - t.like(readLatest('published.wallet.agoric1buyer'), { - status: { - id: 'bid3', - payouts: likePayouts(outcome.bids[2].payouts), - }, - }); + t.log('building proposal'); + const proposal = await buildProposal({ + package: 'inter-protocol', + packageScriptName: 'build:add-STARS-proposal', + }); - // TODO express spec up top in a way it can be passed in here - check.vaultNotification(0, { - debt: undefined, - vaultState: 'liquidated', - locked: { - value: scale6(outcome.vaultsActual[0].locked), - }, - }); - check.vaultNotification(1, { - debt: undefined, - vaultState: 'liquidated', - locked: { - value: scale6(outcome.vaultsActual[1].locked), - }, - }); + for await (const bundle of proposal.bundles) { + await controller.validateAndInstallBundle(bundle); } - - // check reserve balances - t.like(readLatest('published.reserve.metrics'), { - allocations: { - ATOM: { value: scale6(outcome.reserve.allocations.ATOM) }, - // not part of product spec - Fee: { value: scale6(1.54) }, - }, - shortfallBalance: { value: scale6(outcome.reserve.shortfall) }, - }); + t.log('installed', proposal.bundles.length, 'bundles'); + + t.log('launching proposal'); + const bridgeMessage = { + type: 'CORE_EVAL', + evals: proposal.evals, + }; + t.log({ bridgeMessage }); + + const { EV } = t.context.runUtils; + /** @type {ERef} */ + const coreEvalBridgeHandler = await EV.vat('bootstrap').consumeItem( + 'coreEvalBridgeHandler', + ); + await EV(coreEvalBridgeHandler).fromBridge(bridgeMessage); + + t.context.refreshAgoricNamesRemotes(); + + t.log('restart-vats proposal executed'); + t.pass(); // reached here without throws }); + +test.serial( + 'liquidate STARS', + checkFlow1, + { collateralBrandKey: 'STARS', managerIndex: 1 }, + {}, +);