-
Notifications
You must be signed in to change notification settings - Fork 209
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start a new auction in a3p-integration (#9158)
refs: #8740 closes: #8918 refs: #8400 ## Description Add a new Auction instance in A3P, so #8757 can make use of it. Also provides upgrade proposals which can be be applied to MainNet and other chains. ### Security Considerations N/A ### Scaling Considerations This is largely in service of #8400, which reports that priceFeed vats are accumulating garbage. This PR gives a new auction which can rely on new priceFeeds. The existing auction is not upgradeable and its pricefeeds can't be updated. ### Documentation Considerations No user-visible changes to behavior. ### Testing Considerations Tested in A3P ### Upgrade Considerations Auctions are not upgradeable, so we have to replace them and update their clients.
- Loading branch information
Showing
8 changed files
with
330 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import test from 'ava'; | ||
import { getDetailsMatchingVats } from './vatDetails.js'; | ||
|
||
test('new auction vat', async t => { | ||
const details = await getDetailsMatchingVats('auctioneer'); | ||
// This query matches both the auction and its governor, so 2*2 | ||
t.is(Object.keys(details).length, 4); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import dbOpenAmbient from 'better-sqlite3'; | ||
|
||
const HOME = process.env.HOME; | ||
|
||
/** @type {<T>(val: T | undefined) => T} */ | ||
export const NonNullish = val => { | ||
if (!val) throw Error('required'); | ||
return val; | ||
}; | ||
|
||
/** | ||
* @file look up vat incarnation from kernel DB | ||
* @see {getIncarnation} | ||
*/ | ||
|
||
const swingstorePath = `${HOME}/.agoric/data/agoric/swingstore.sqlite`; | ||
|
||
/** | ||
* SQL short-hand | ||
* | ||
* @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 makeSwingstore = db => { | ||
const sql = dbTool(db); | ||
|
||
/** @param {string} key */ | ||
const kvGet = key => sql.get`select * from kvStore where key = ${key}`.value; | ||
/** @param {string} key */ | ||
const kvGetJSON = key => JSON.parse(kvGet(key)); | ||
|
||
/** @param {string} vatID */ | ||
const lookupVat = vatID => { | ||
return Object.freeze({ | ||
source: () => kvGetJSON(`${vatID}.source`), | ||
options: () => kvGetJSON(`${vatID}.options`), | ||
currentSpan: () => | ||
sql.get`select * from transcriptSpans where isCurrent = 1 and vatID = ${vatID}`, | ||
}); | ||
}; | ||
|
||
return Object.freeze({ | ||
/** @param {string} vatName */ | ||
findVat: vatName => { | ||
/** @type {string[]} */ | ||
const dynamicIDs = kvGetJSON('vat.dynamicIDs'); | ||
const targetVat = dynamicIDs.find(vatID => | ||
lookupVat(vatID).options().name.includes(vatName), | ||
); | ||
if (!targetVat) throw Error(`vat not found: ${vatName}`); | ||
return targetVat; | ||
}, | ||
/** @param {string} vatName */ | ||
findVats: vatName => { | ||
/** @type {string[]} */ | ||
const dynamicIDs = kvGetJSON('vat.dynamicIDs'); | ||
return dynamicIDs.filter(vatID => | ||
lookupVat(vatID).options().name.includes(vatName), | ||
); | ||
}, | ||
lookupVat, | ||
}); | ||
}; | ||
|
||
/** @param {string} vatName */ | ||
export const getDetailsMatchingVats = async vatName => { | ||
const kStore = makeSwingstore( | ||
dbOpenAmbient(swingstorePath, { readonly: true }), | ||
); | ||
|
||
const vatIDs = kStore.findVats(vatName); | ||
const infos = []; | ||
for (const vatID of vatIDs) { | ||
const vatInfo = kStore.lookupVat(vatID); | ||
const source = vatInfo.source(); | ||
// @ts-expect-error cast | ||
const { incarnation } = vatInfo.currentSpan(); | ||
infos.push({ vatName, vatID, incarnation, ...source }); | ||
} | ||
|
||
return infos; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { makeHelpers } from '@agoric/deploy-script-support'; | ||
|
||
/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ | ||
export const defaultProposalBuilder = async () => { | ||
return harden({ | ||
sourceSpec: '@agoric/inter-protocol/src/proposals/add-auction.js', | ||
getManifestCall: ['getManifestForAddAuction'], | ||
}); | ||
}; | ||
|
||
export default async (homeP, endowments) => { | ||
const { writeCoreProposal } = await makeHelpers(homeP, endowments); | ||
await writeCoreProposal('add-auction', defaultProposalBuilder); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import { deeplyFulfilledObject, makeTracer } from '@agoric/internal'; | ||
import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; | ||
import { E } from '@endo/far'; | ||
import { Stable } from '@agoric/internal/src/tokens.js'; | ||
import { makeGovernedTerms as makeGovernedATerms } from '../auction/params.js'; | ||
|
||
const trace = makeTracer('NewAuction', true); | ||
|
||
/** @param {import('./econ-behaviors.js').EconomyBootstrapPowers} powers */ | ||
export const addAuction = async ({ | ||
consume: { | ||
zoe, | ||
board, | ||
chainTimerService, | ||
priceAuthority, | ||
chainStorage, | ||
economicCommitteeCreatorFacet: electorateCreatorFacet, | ||
auctioneerKit: legacyKitP, | ||
}, | ||
produce: { newAuctioneerKit }, | ||
instance: { | ||
consume: { reserve: reserveInstance }, | ||
}, | ||
installation: { | ||
consume: { | ||
auctioneer: auctionInstallation, | ||
contractGovernor: contractGovernorInstallation, | ||
}, | ||
}, | ||
issuer: { | ||
consume: { [Stable.symbol]: stableIssuerP }, | ||
}, | ||
}) => { | ||
trace('addAuction start'); | ||
const STORAGE_PATH = 'auction'; | ||
|
||
const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); | ||
|
||
const [ | ||
initialPoserInvitation, | ||
electorateInvitationAmount, | ||
stableIssuer, | ||
legacyKit, | ||
] = await Promise.all([ | ||
poserInvitationP, | ||
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), | ||
stableIssuerP, | ||
legacyKitP, | ||
]); | ||
|
||
// Each field has an extra layer of type + value: | ||
// AuctionStartDelay: { type: 'relativeTime', value: { relValue: 2n, timerBrand: Object [Alleged: timerBrand] {} } } | ||
/** @type {any} */ | ||
const paramValues = await E(legacyKit.publicFacet).getGovernedParams(); | ||
const params = harden({ | ||
StartFrequency: paramValues.StartFrequency.value, | ||
ClockStep: paramValues.ClockStep.value, | ||
StartingRate: paramValues.StartingRate.value, | ||
LowestRate: paramValues.LowestRate.value, | ||
DiscountStep: paramValues.DiscountStep.value, | ||
AuctionStartDelay: paramValues.AuctionStartDelay.value, | ||
PriceLockPeriod: paramValues.PriceLockPeriod.value, | ||
}); | ||
const timerBrand = await E(chainTimerService).getTimerBrand(); | ||
|
||
const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH); | ||
const marshaller = await E(board).getReadonlyMarshaller(); | ||
|
||
const reservePublicFacet = await E(zoe).getPublicFacet(reserveInstance); | ||
|
||
const auctionTerms = makeGovernedATerms( | ||
{ storageNode, marshaller }, | ||
chainTimerService, | ||
priceAuthority, | ||
reservePublicFacet, | ||
{ | ||
...params, | ||
ElectorateInvitationAmount: electorateInvitationAmount, | ||
TimerBrand: timerBrand, | ||
}, | ||
); | ||
|
||
const governorTerms = await deeplyFulfilledObject( | ||
harden({ | ||
timer: chainTimerService, | ||
governedContractInstallation: auctionInstallation, | ||
governed: { | ||
terms: auctionTerms, | ||
issuerKeywordRecord: { Bid: stableIssuer }, | ||
storageNode, | ||
marshaller, | ||
label: 'auctioneer', | ||
}, | ||
}), | ||
); | ||
|
||
/** @type {GovernorStartedInstallationKit<typeof auctionInstallation>} */ | ||
const governorStartResult = await E(zoe).startInstance( | ||
contractGovernorInstallation, | ||
undefined, | ||
governorTerms, | ||
harden({ | ||
electorateCreatorFacet, | ||
governed: { | ||
initialPoserInvitation, | ||
storageNode, | ||
marshaller, | ||
}, | ||
}), | ||
'auctioneer.governor', | ||
); | ||
|
||
const [governedInstance, governedCreatorFacet, governedPublicFacet] = | ||
await Promise.all([ | ||
E(governorStartResult.creatorFacet).getInstance(), | ||
E(governorStartResult.creatorFacet).getCreatorFacet(), | ||
E(governorStartResult.creatorFacet).getPublicFacet(), | ||
]); | ||
|
||
const allIssuers = await E(zoe).getIssuers(legacyKit.instance); | ||
const { Bid: _istIssuer, ...auctionIssuers } = allIssuers; | ||
await Promise.all( | ||
Object.keys(auctionIssuers).map(kwd => | ||
E(governedCreatorFacet).addBrand(auctionIssuers[kwd], kwd), | ||
), | ||
); | ||
|
||
// don't overwrite auctioneerKit yet | ||
newAuctioneerKit.resolve( | ||
harden({ | ||
label: 'auctioneer', | ||
creatorFacet: governedCreatorFacet, | ||
adminFacet: governorStartResult.adminFacet, | ||
publicFacet: governedPublicFacet, | ||
instance: governedInstance, | ||
|
||
governor: governorStartResult.instance, | ||
governorCreatorFacet: governorStartResult.creatorFacet, | ||
governorAdminFacet: governorStartResult.adminFacet, | ||
}), | ||
); | ||
// don't replace auction instance yet. | ||
}; | ||
|
||
export const ADD_AUCTION_MANIFEST = harden({ | ||
[addAuction.name]: { | ||
consume: { | ||
zoe: true, | ||
board: true, | ||
chainTimerService: true, | ||
priceAuthority: true, | ||
chainStorage: true, | ||
economicCommitteeCreatorFacet: true, | ||
auctioneerKit: true, | ||
}, | ||
produce: { | ||
newAuctioneerKit: true, | ||
}, | ||
instance: { | ||
consume: { reserve: true }, | ||
}, | ||
installation: { | ||
consume: { | ||
auctioneer: true, | ||
contractGovernor: true, | ||
}, | ||
}, | ||
issuer: { | ||
consume: { [Stable.symbol]: true }, | ||
}, | ||
}, | ||
}); | ||
|
||
/* Add a new auction to a chain that already has one. */ | ||
export const getManifestForAddAuction = async () => { | ||
return { manifest: ADD_AUCTION_MANIFEST }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters