Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(AMM)!: remove the AMM and cleanup bootstrap etc. dependencies
Browse files Browse the repository at this point in the history
The most visible consequence is to vault liquidation. Some pieces are
left hanging, and will be repaired by #7047.
Chris-Hibbert committed Feb 24, 2023
1 parent 4f17b91 commit afd8d53
Showing 80 changed files with 153 additions and 9,684 deletions.
10 changes: 3 additions & 7 deletions packages/SwingSet/misc-tools/measure-metering/measure.js
Original file line number Diff line number Diff line change
@@ -50,11 +50,6 @@ async function run() {
const bootFn = new URL('measurement-bootstrap.js', import.meta.url).pathname;
const targetFn = new URL('measurement-target.js', import.meta.url).pathname;
const zoeFn = new URL('measurement-zoe.js', import.meta.url).pathname;
const autoswapFn = new URL(
'../../../inter-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js',
import.meta.url,
).pathname;
const autoswapBundle = await bundleSource(autoswapFn);
const config = {
defaultManagerType: 'xs-worker',
bootstrap: 'bootstrap',
@@ -139,8 +134,9 @@ async function run() {
const zoeInstallVaultFactory = await doCounted('zoeInstallVaultFactory');
console.log(`zoe install (vaultFactory): ${zoeInstallVaultFactory}`);

const zoeInstallAMM = await doCounted('zoeInstallBundle', [autoswapBundle]);
console.log(`zoe install (AMM): ${zoeInstallAMM}`);
// XXX Does this need to be replace by something else in order to be useful?
// const zoeInstallAMM = await doCounted('zoeInstallBundle', [autoswapBundle]);
// console.log(`zoe install (AMM): ${zoeInstallAMM}`);
const zoeInstantiate = await doCounted('zoeInstantiate');
console.log(`zoe instantiate (AMM): ${zoeInstantiate}`);

1 change: 0 additions & 1 deletion packages/inter-protocol/exported.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import './src/stakeFactory/types.js';
import './src/psm/types.js';
import './src/vaultFactory/types.js';
import './src/vpool-xyk-amm/types.js';
2 changes: 0 additions & 2 deletions packages/inter-protocol/scripts/deploy-contracts.js
Original file line number Diff line number Diff line change
@@ -10,9 +10,7 @@ const contractRefs = [
'../../governance/bundles/bundle-committee.js',
'../../governance/bundles/bundle-binaryVoteCounter.js',
'../bundles/bundle-stakeFactory.js',
'../bundles/bundle-amm.js',
'../bundles/bundle-vaultFactory.js',
'../bundles/bundle-liquidateMinimum.js',
'../bundles/bundle-reserve.js',
'../bundles/bundle-psm.js',
'../../vats/bundles/bundle-mintHolder.js',
12 changes: 0 additions & 12 deletions packages/inter-protocol/scripts/init-core.js
Original file line number Diff line number Diff line change
@@ -36,10 +36,6 @@ const installKeyGroups = {
],
},
main: {
amm: [
'../src/vpool-xyk-amm/multipoolMarketMaker.js',
'../bundles/bundle-amm.js',
],
interchainPool: [
'../src/interchainPool.js',
'../bundles/bundle-interchainPool.js',
@@ -52,14 +48,6 @@ const installKeyGroups = {
'../src/feeDistributor.js',
'../bundles/bundle-feeDistributor.js',
],
liquidateMinimum: [
'../src/vaultFactory/liquidateMinimum.js',
'../bundles/bundle-liquidateMinimum.js',
],
liquidate: [
'../src/vaultFactory/liquidateIncrementally.js',
'../bundles/bundle-liquidateIncrementally.js',
],
reserve: ['../src/reserve/assetReserve.js', '../bundles/bundle-reserve.js'],
},
};
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/collectFees.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { atomicTransfer } from '@agoric/zoe/src/contractSupport';
import { atomicTransfer } from '@agoric/zoe/src/contractSupport/index.js';

/**
* Provide shared support for providing access to fees from a service contract.
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/feeDistributor.js
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ export const customTermsShape = harden({
*/

/**
* wrapper to take the vaultFactory or AMM's creatorFacet, and make a call that
* wrapper to take the vaultFactory's creatorFacet, and make a call that
* will request an invitation and return a promise for a payment.
*
* @param {ERef<ZoeService>} zoe
50 changes: 1 addition & 49 deletions packages/inter-protocol/src/interchainPool.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import { E, Far } from '@endo/far';
import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp';
import {
offerTo,
atomicTransfer,
} from '@agoric/zoe/src/contractSupport/index.js';
import { MIN_INITIAL_POOL_LIQUIDITY_KEY } from './vpool-xyk-amm/params.js';

const { Fail, quote: q } = assert;
import { atomicTransfer } from '@agoric/zoe/src/contractSupport/index.js';

const COSMOS_DECIMALS = 6;

/**
* Given sufficient IST, create an issuer for an IBC denom
* and an invitation to add a pool for it to the AMM.
*
* @param {ZCF<InterchainPoolTerms>} zcf
* @param {{
* bankManager: ERef<BankManager>,
* }} privateArgs
*
* @typedef {{
* amm: Instance,
* }} InterchainPoolTerms
*
* @typedef {{
@@ -29,12 +21,6 @@ const COSMOS_DECIMALS = 6;
* }} AssetDetail
*/
export const start = (zcf, { bankManager }) => {
const { amm } = zcf.getTerms();

const zoe = zcf.getZoeService();
/** @type {ERef<GovernedPublicFacet<XYKAMMPublicFacet>>} */
const ammPub = E(zoe).getPublicFacet(amm);

let kwNonce = 0;

/**
@@ -51,15 +37,9 @@ export const start = (zcf, { bankManager }) => {
assert.typeof(denom, 'string');
assert.typeof(decimalPlaces, 'number');

const minimumCentral = await E(ammPub).getAmount(
MIN_INITIAL_POOL_LIQUIDITY_KEY,
);

const {
give: { Central: centralAmt },
} = seat.getProposal();
AmountMath.isGTE(centralAmt, minimumCentral) ||
Fail`at least ${q(minimumCentral)} required; only ${q(centralAmt)} given`;

const interAsset = makeIssuerKit(
denom,
@@ -77,35 +57,7 @@ export const start = (zcf, { bankManager }) => {
} = seat2.getProposal();
AmountMath.coerce(interAsset.brand, secondaryAmt);

const liquidityIssuer = await E(ammPub).addIssuer(
interAsset.issuer,
`Interchain${kwNonce}`,
);
await zcf.saveIssuer(liquidityIssuer, `Liquidity${kwNonce}`);

const proposal = harden({
give: {
Central: centralAmt,
Secondary: secondaryAmt,
},
});

atomicTransfer(zcf, seat, seat2, { Central: centralAmt });

const invitation = await E(ammPub).addPoolInvitation();
const { userSeatPromise, deposited } = await offerTo(
zcf,
invitation,
undefined,
proposal,
seat2,
);
return E.when(deposited, _ => {
seat.exit();
seat2.exit();

return E(userSeatPromise).getOfferResult();
});
};

const keyword = denom; // ISSUE #5412: should not show up in all wallets.
61 changes: 4 additions & 57 deletions packages/inter-protocol/src/proposals/addAssetToVault.js
Original file line number Diff line number Diff line change
@@ -44,44 +44,6 @@ export const publishInterchainAssetFromBoardId = async (
]);
};

const addPool = async (
zoe,
amm,
issuer,
keyword,
brand,
runBrand,
runIssuer,
) => {
const ammPub = E(zoe).getPublicFacet(amm);
const [addPoolInvitation] = await Promise.all([
E(ammPub).addPoolInvitation(),
E(ammPub).addIssuer(issuer, keyword),
]);
const proposal = harden({
give: {
Secondary: AmountMath.makeEmpty(brand),
Central: AmountMath.makeEmpty(runBrand),
},
});
const centralPurse = E(runIssuer).makeEmptyPurse();
const secondaryPurse = E(issuer).makeEmptyPurse();
const [emptyCentral, emptySecondary] = await Promise.all([
E(centralPurse).withdraw(proposal.give.Central),
E(secondaryPurse).withdraw(proposal.give.Secondary),
]);
const payments = harden({
Central: emptyCentral,
Secondary: emptySecondary,
});
const addLiquiditySeat = await E(zoe).offer(
addPoolInvitation,
proposal,
payments,
);
await E(addLiquiditySeat).getOfferResult();
};

/**
* @param {EconomyBootstrapPowers} powers
* @param {object} config
@@ -90,20 +52,11 @@ const addPool = async (
*/
export const publishInterchainAssetFromBank = async (
{
consume: { zoe, bankManager, agoricNamesAdmin, bankMints },
consume: { zoe, bankManager, agoricNamesAdmin, bankMints, reserveKit },
produce: { bankMints: produceBankMints },
installation: {
consume: { mintHolder },
},
instance: {
consume: { amm },
},
issuer: {
consume: { [Stable.symbol]: runIssuer },
},
brand: {
consume: { [Stable.symbol]: stableBrandP },
},
},
{ options: { interchainAssetOptions } },
) => {
@@ -129,14 +82,10 @@ export const publishInterchainAssetFromBank = async (
E(zoe).startInstance(mintHolder, {}, terms),
);

const [issuer, brand, runBrand] = await Promise.all([
issuerP,
E(issuerP).getBrand(),
stableBrandP,
]);
const [issuer, brand] = await Promise.all([issuerP, E(issuerP).getBrand()]);
const kit = { mint: mintP, issuer, brand };

await addPool(zoe, amm, issuer, keyword, brand, runBrand, runIssuer);
await E(E.get(reserveKit).creatorFacet).addIssuer(issuer, keyword);

// Create the mint list if it doesn't exist and wasn't already rejected.
produceBankMints.resolve([]);
@@ -318,14 +267,12 @@ export const getManifestForAddAssetToVault = (
bankManager: true,
agoricNamesAdmin: true,
bankMints: true,
reserveKit: true,
},
produce: { bankMints: true },
installation: {
consume: { mintHolder: true },
},
instance: { consume: { amm: 'amm' } },
issuer: { consume: { [Stable.symbol]: 'zoe' } },
brand: { consume: { [Stable.symbol]: 'zoe' } },
},
}),
[registerScaledPriceAuthority.name]: {
9 changes: 3 additions & 6 deletions packages/inter-protocol/src/proposals/committee-proposal.js
Original file line number Diff line number Diff line change
@@ -75,17 +75,16 @@ harden(startEconCharter);
* @param {import('./econ-behaviors').EconomyBootstrapPowers} powers
*/
export const addGovernorsToEconCharter = async ({
consume: { reserveKit, ammKit, vaultFactoryKit, econCharterKit },
consume: { reserveKit, vaultFactoryKit, econCharterKit },
instance: {
consume: { amm, reserve, VaultFactory },
consume: { reserve, VaultFactory },
},
}) => {
const { creatorFacet } = E.get(econCharterKit);

// Introduce charter to governed creator facets.
await Promise.all(
[
{ instanceP: amm, facetP: E.get(ammKit).governorCreatorFacet },
{ instanceP: reserve, facetP: E.get(reserveKit).governorCreatorFacet },
{
instanceP: VaultFactory,
@@ -148,22 +147,20 @@ export const getManifestForInviteCommittee = async (
[addGovernorsToEconCharter.name]: {
consume: {
reserveGovernorCreatorFacet: t,
ammGovernorCreatorFacet: t,
vaultFactoryGovernorCreator: t,
econCharterKit: t,
zoe: t,
agoricNames: t,
namesByAddressAdmin: t,
economicCommitteeCreatorFacet: t,
reserveKit: t,
ammKit: t,
vaultFactoryKit: t,
},
installation: {
consume: { binaryVoteCounter: t },
},
instance: {
consume: { amm: t, reserve: t, VaultFactory: t },
consume: { reserve: t, VaultFactory: t },
},
},
[inviteToEconCharter.name]: {
73 changes: 1 addition & 72 deletions packages/inter-protocol/src/proposals/core-proposal.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Stable } from '@agoric/vats/src/tokens.js';
import * as econBehaviors from './econ-behaviors.js';
import { ECON_COMMITTEE_MANIFEST } from './startEconCommittee.js';
import * as simBehaviors from './sim-behaviors.js';

export * from './econ-behaviors.js';
export * from './sim-behaviors.js';
@@ -13,40 +12,6 @@ export * from './startEconCommittee.js'; // eslint-disable-line import/export
/** @type {import('@agoric/vats/src/core/manifest.js').BootstrapManifest} */
const SHARED_MAIN_MANIFEST = harden({
/** @type {import('@agoric/vats/src/core/manifest.js').BootstrapManifestPermit} */
[econBehaviors.setupAmm.name]: {
consume: {
board: 'board',
chainStorage: true,
chainTimerService: 'timer',
zoe: 'zoe',
economicCommitteeCreatorFacet: 'economicCommittee',
},
produce: { ammKit: 'amm' },
issuer: { consume: { [Stable.symbol]: 'zoe' } },
brand: { consume: { [Stable.symbol]: 'zoe' } },
installation: {
consume: { contractGovernor: 'zoe', amm: 'zoe' },
},
instance: {
produce: { ammGovernor: 'ammGovernor' },
},
},
[econBehaviors.startInterchainPool.name]: {
consume: { bankManager: 'bank', zoe: 'zoe', agoricNamesAdmin: true },
installation: {
consume: { interchainPool: 'zoe' },
},
brand: {
consume: { [Stable.symbol]: 'zoe' },
},
issuer: {
consume: { [Stable.symbol]: 'zoe' },
},
instance: {
consume: { amm: 'amm' },
produce: { interchainPool: 'interchainPool' },
},
},
[econBehaviors.startVaultFactory.name]: {
consume: {
board: 'board',
@@ -65,12 +30,10 @@ const SHARED_MAIN_MANIFEST = harden({
consume: {
contractGovernor: 'zoe',
VaultFactory: 'zoe',
liquidate: 'zoe',
},
},
instance: {
consume: {
amm: 'amm',
reserve: 'reserve',
},
produce: {
@@ -90,7 +53,6 @@ const SHARED_MAIN_MANIFEST = harden({

[econBehaviors.setupReserve.name]: {
consume: {
ammKit: 'amm',
board: 'board',
chainStorage: true,
feeMintAccess: 'zoe',
@@ -105,9 +67,7 @@ const SHARED_MAIN_MANIFEST = harden({
consume: { contractGovernor: 'zoe', reserve: 'zoe' },
},
instance: {
consume: { amm: 'amm' },
produce: {
amm: 'amm',
reserve: 'reserve',
reserveGovernor: 'ReserveGovernor',
},
@@ -122,7 +82,6 @@ const REWARD_MANIFEST = harden({
bankManager: true,
vaultFactoryKit: true,
periodicFeeCollectors: true,
ammKit: true,
stakeFactoryKit: true,
reserveKit: true,
zoe: true,
@@ -174,35 +133,7 @@ const STAKE_FACTORY_MANIFEST = harden({
},
});

export const SIM_CHAIN_MANIFEST = harden({
[simBehaviors.fundAMM.name]: {
consume: {
centralSupplyBundle: true,
mintHolderBundle: true,
chainTimerService: 'timer',
bldIssuerKit: true,
feeMintAccess: true,
loadVat: true,
mints: 'mints',
priceAuthorityVat: 'priceAuthority',
priceAuthorityAdmin: 'priceAuthority',
vaultFactoryKit: 'VaultFactory',
zoe: true,
},
installation: {
consume: { centralSupply: 'zoe' },
},
issuer: {
consume: { [Stable.symbol]: 'zoe' },
},
brand: {
consume: { [Stable.symbol]: 'zoe' },
},
instance: {
consume: { amm: 'amm' },
},
},
});
export const SIM_CHAIN_MANIFEST = harden({});

export const getManifestForEconCommittee = (
{ restoreRef },
@@ -233,10 +164,8 @@ export const getManifestForMain = (
return {
manifest: SHARED_MAIN_MANIFEST,
installations: {
amm: restoreRef(installKeys.amm),
VaultFactory: restoreRef(installKeys.vaultFactory),
feeDistributor: restoreRef(installKeys.feeDistributor),
liquidate: restoreRef(installKeys.liquidate),
reserve: restoreRef(installKeys.reserve),
interchainPool: restoreRef(installKeys.interchainPool),
},
68 changes: 5 additions & 63 deletions packages/inter-protocol/src/proposals/demoIssuers.js
Original file line number Diff line number Diff line change
@@ -462,33 +462,24 @@ export const poolRates = (issuerName, record, kits, central) => {
* consume: { mints }
* }} powers
*/
// XXX Is this dead? I can't find references to it. It seems to do more than
// fund the AMM, and I don't know if it's needed somewhere.
export const fundAMM = async ({
consume: {
bldIssuerKit,
chainTimerService,
feeMintAccess,
mints,
priceAuthorityVat,
priceAuthorityAdmin,
vaultFactoryKit,
zoe,
},
installation: {
consume: { centralSupply: centralSupplyInstall },
},
issuer: {
consume: { IST: centralIssuer },
},
brand: {
consume: { IST: centralBrand },
},
instance: {
consume: { amm: ammInstance },
},
}) => {
const { ammTotal: ammDepositValue, balances } =
ammPoolRunDeposits(AMMDemoState);

const vats = { mints, priceAuthority: priceAuthorityVat };

const kits = await Collect.allValues(
@@ -516,25 +507,10 @@ export const fundAMM = async ({
kits[Stable.symbol]
);

/** @type {[ XYKAMMPublicFacet, import('@agoric/time/src/types').TimerService]} */
const [ammPublicFacet, timer] = await Promise.all([
E(zoe).getPublicFacet(ammInstance),
chainTimerService,
]);

const ammBootstrapPayment = await mintRunPayment(ammDepositValue, {
centralSupplyInstall,
feeMintAccess,
zoe,
});
/** @type {import('@agoric/time/src/types').TimerService} */
const timer = await chainTimerService;

async function addAllCollateral() {
const issuerMap = await splitAllCentralPayments(
ammBootstrapPayment,
balances,
central,
);

return Promise.all(
entries(AMMDemoState).map(async ([issuerName, record]) => {
if (!record.config) {
@@ -557,29 +533,6 @@ export const fundAMM = async ({
);
secondaryPayment || Fail`no payment for ${q(issuerName)}`;
kit.issuer || Fail`No issuer for ${q(issuerName)}`;
const liquidityIssuer = /** @type {Promise<Issuer<'nat'>>} */ (
E(ammPublicFacet).addIssuer(kit.issuer, issuerName)
);
const [secondaryAmount, liquidityBrand] = await Promise.all([
// Error: (an undefined) was not a live payment for brand
// at (.../vats/src/demoIssuers.js:591)
E(kit.issuer).getAmountOf(secondaryPayment),
E(liquidityIssuer).getBrand(),
]);
const centralAmount = issuerMap[issuerName].amount;
const proposal = harden({
want: { Liquidity: AmountMath.makeEmpty(liquidityBrand) },
give: { Secondary: secondaryAmount, Central: centralAmount },
});

await E(zoe).offer(
E(ammPublicFacet).addPoolInvitation(),
proposal,
harden({
Secondary: secondaryPayment,
Central: issuerMap[issuerName].payment,
}),
);

const issuerPresence = await kit.issuer;
return E(E.get(vaultFactoryKit).creatorFacet).addVaultType(
@@ -607,13 +560,11 @@ export const fundAMM = async ({

await addAllCollateral();

const brandsWithPriceAuthorities = await E(ammPublicFacet).getAllPoolBrands();

await Promise.all(
// TODO: exactly what is the list of things to iterate here?
entries(AMMDemoState).map(async ([issuerName, record]) => {
// Create priceAuthority pairs for centralIssuer based on the
// AMM or FakePriceAuthority.
// FakePriceAuthority.
console.debug(`Creating ${issuerName}-${Stable.symbol}`);
const issuer = kits[issuerName].issuer;
const { trades } = record;
@@ -631,15 +582,6 @@ export const fundAMM = async ({
let toCentral;
let fromCentral;

if (brandsWithPriceAuthorities.includes(brand)) {
({ toCentral, fromCentral } = await E(ammPublicFacet)
.getPriceAuthorities(brand)
.catch(_e => {
// console.warn('could not get AMM priceAuthorities', _e);
return { toCentral: undefined, fromCentral: undefined };
}));
}

if (!fromCentral && tradesGivenCentral) {
// We have no amm from-central price authority, make one from trades.
if (issuerName !== Stable.symbol) {
203 changes: 10 additions & 193 deletions packages/inter-protocol/src/proposals/econ-behaviors.js
Original file line number Diff line number Diff line change
@@ -12,19 +12,14 @@ import { E } from '@endo/far';
import { LienBridgeId, makeStakeReporter } from '../my-lien.js';
import { makeReserveTerms } from '../reserve/params.js';
import { makeStakeFactoryTerms } from '../stakeFactory/params.js';
import { liquidationDetailTerms } from '../vaultFactory/liquidation.js';
import { makeGovernedTerms } from '../vaultFactory/params.js';
import { makeAmmTerms } from '../vpool-xyk-amm/params.js';

const trace = makeTracer('RunEconBehaviors', false);

const { Fail } = assert;

const SECONDS_PER_HOUR = 60n * 60n;
const SECONDS_PER_DAY = 24n * SECONDS_PER_HOUR;

const BASIS_POINTS = 10_000n;
const MILLI = 1_000_000n;

/**
* @typedef {GovernedCreatorFacet<import('../stakeFactory/stakeFactory.js').StakeFactoryCreator>} StakeFactoryCreator
@@ -46,13 +41,6 @@ const MILLI = 1_000_000n;
* @typedef { WellKnownSpaces & ChainBootstrapSpace & EconomyBootstrapSpace
* } EconomyBootstrapPowers
* @typedef {PromiseSpaceOf<{
* ammKit: {
* instanceWithoutReserve: Instance,
* creatorFacet: XYKAMMCreatorFacet,
* governorCreatorFacet: GovernedContractFacetAccess<XYKAMMPublicFacet,XYKAMMCreatorFacet>,
* adminFacet: AdminFacet,
* publicFacet: XYKAMMPublicFacet,
* },
* economicCommitteeCreatorFacet: import('@agoric/governance/src/committee.js').CommitteeElectorateCreatorFacet,
* feeDistributorKit: {
* creatorFacet: import('../feeDistributor.js').FeeDistributorCreatorFacet,
@@ -96,164 +84,9 @@ const MILLI = 1_000_000n;
* In production called by @agoric/vats to bootstrap.
*/

/**
* @param {EconomyBootstrapPowers} powers
* @param {{
* interchainPoolOptions?: { minimumCentral?: bigint }
* }} [options]
*/
export const startInterchainPool = async (
{
consume: { bankManager: mgrP, zoe, agoricNamesAdmin },
installation: {
consume: { interchainPool: installation },
},
instance: {
consume: { amm },
produce: { interchainPool: _viaAgoricNamesAdmin },
},
brand: {
consume: { [Stable.symbol]: centralBrandP },
},
issuer: {
consume: { [Stable.symbol]: centralIssuerP },
},
},
{ interchainPoolOptions = {} } = {},
) => {
trace('startInterchainPool');
// TODO: get minimumCentral dynamically from the AMM
const { minimumCentral = 100n * MILLI } = interchainPoolOptions;
const [centralIssuer, centralBrand, bankManager] = await Promise.all([
centralIssuerP,
centralBrandP,
mgrP,
]);

const terms = await deeplyFulfilledObject(
harden({
minimumCentral: AmountMath.make(centralBrand, minimumCentral),
amm,
}),
);
const { instance } = await E(zoe).startInstance(
installation,
{ Central: centralIssuer },
terms,
{
bankManager,
},
);

const instanceAdmin = E(agoricNamesAdmin).lookupAdmin('instance');
await E(instanceAdmin).update('interchainPool', instance);
};
harden(startInterchainPool);

const AMM_STORAGE_PATH = 'amm'; // TODO: share with agoricNames?

/**
* @param {EconomyBootstrapPowers} powers
* @param {{ options?: { minInitialPoolLiquidity?: bigint }}} opts
*/
export const setupAmm = async (
{
consume: {
board,
chainTimerService,
zoe,
economicCommitteeCreatorFacet: committeeCreator,
chainStorage,
},
produce: { ammKit },
brand: {
consume: { [Stable.symbol]: runBrandP },
},
issuer: {
consume: { [Stable.symbol]: centralIssuer },
},
instance: {
produce: { ammGovernor },
},
installation: {
consume: { contractGovernor: governorInstallation, amm: ammInstallation },
},
},
{ options = {} } = {},
) => {
trace('setupAmm');

const poserInvitationP = E(committeeCreator).getPoserInvitation();
const [poserInvitation, runBrand] = await Promise.all([
poserInvitationP,
runBrandP,
]);
const { minInitialPoolLiquidity = 1_000_000_000n } = options;

const ammTerms = makeAmmTerms(
chainTimerService,
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitation),
AmountMath.make(runBrand, minInitialPoolLiquidity),
);

const storageNode = await makeStorageNodeChild(
chainStorage,
AMM_STORAGE_PATH,
);
const marshaller = await E(board).getPublishingMarshaller();

const ammGovernorTerms = await deeplyFulfilledObject(
harden({
timer: chainTimerService,
governedContractInstallation: ammInstallation,
governed: {
terms: ammTerms,
issuerKeywordRecord: { Central: centralIssuer },
},
}),
);

/** @type {{ creatorFacet: GovernedContractFacetAccess<XYKAMMPublicFacet,XYKAMMCreatorFacet>, publicFacet: GovernorPublic, instance: Instance, adminFacet: AdminFacet }} */
const g = await E(zoe).startInstance(
governorInstallation,
{},
ammGovernorTerms,
{
electorateCreatorFacet: committeeCreator,
governed: {
initialPoserInvitation: poserInvitation,
storageNode,
marshaller,
},
},
);

const [creatorFacet, ammPublicFacet, instance] = await Promise.all([
E(g.creatorFacet).getCreatorFacet(),
E(g.creatorFacet).getPublicFacet(),
E(g.publicFacet).getGovernedContract(),
]);
ammKit.resolve(
harden({
creatorFacet,
instanceWithoutReserve: instance,
governorCreatorFacet: g.creatorFacet,
adminFacet: g.adminFacet,
publicFacet: ammPublicFacet,
}),
);

// Confirm that the amm was indeed setup
ammPublicFacet || Fail`ammPublicFacet broken ${ammPublicFacet}`;

ammGovernor.resolve(g.instance);
return ammInstallation;
};

/** @param {EconomyBootstrapPowers} powers */
export const setupReserve = async ({
consume: {
ammKit: ammKitP,
board,
feeMintAccess: feeMintAccessP,
chainStorage,
@@ -266,11 +99,7 @@ export const setupReserve = async ({
consume: { [Stable.symbol]: centralIssuer },
},
instance: {
produce: {
amm: ammInstanceProducer,
reserve: reserveInstanceProducer,
reserveGovernor,
},
produce: { reserve: reserveInstanceProducer, reserveGovernor },
},
installation: {
consume: {
@@ -282,17 +111,21 @@ export const setupReserve = async ({
const STORAGE_PATH = 'reserve';
trace('setupReserve');
const poserInvitationP = E(committeeCreator).getPoserInvitation();
const [poserInvitation, poserInvitationAmount, feeMintAccess, ammKit] =

await poserInvitationP;
await E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP);
await feeMintAccessP;

const [poserInvitation, poserInvitationAmount, feeMintAccess] =
await Promise.all([
poserInvitationP,
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP),
feeMintAccessP,
ammKitP,
]);

const reserveTerms = makeReserveTerms(
poserInvitationAmount,
ammKit.instanceWithoutReserve,
chainTimerService,
);

const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH);
@@ -342,14 +175,6 @@ export const setupReserve = async ({
reserveInstanceProducer.resolve(instance);
reserveGovernor.resolve(g.instance);

// AMM requires Reserve in order to add pools, but we can't provide it at startInstance
// time because Reserve requires AMM itself in order to be started.
// Now that we have the reserve, provide it to the AMM.
trace('Resolving the reserve public facet on the AMM');
await E(ammKit.creatorFacet).resolveReserveFacet(publicFacet);
// it now has the reserve
ammInstanceProducer.resolve(ammKit.instanceWithoutReserve);

return reserveInstallation;
};

@@ -379,12 +204,11 @@ export const startVaultFactory = async (
},
instance: {
produce: instanceProduce,
consume: { amm: ammInstance, reserve: reserveInstance },
consume: { reserve: reserveInstance },
},
installation: {
consume: {
VaultFactory: vaultFactoryInstallation,
liquidate: liquidateInstallationP,
contractGovernor: contractGovernorInstallation,
},
},
@@ -405,25 +229,23 @@ export const startVaultFactory = async (
const shortfallInvitationP = E(
E.get(reserveKit).creatorFacet,
).makeShortfallReportingInvitation();

const [
initialPoserInvitation,
poserInvitationAmount,
initialShortfallInvitation,
shortfallInvitationAmount,
liquidateInstallation,
feeMintAccess,
] = await Promise.all([
poserInvitationP,
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP),
shortfallInvitationP,
E(E(zoe).getInvitationIssuer()).getAmountOf(shortfallInvitationP),
liquidateInstallationP,
feeMintAccessP,
]);

const centralBrand = await centralBrandP;

const ammPublicFacet = await E(zoe).getPublicFacet(ammInstance);
const reservePublicFacet = await E(zoe).getPublicFacet(reserveInstance);
const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH);
const marshaller = await E(board).getReadonlyMarshaller();
@@ -434,11 +256,8 @@ export const startVaultFactory = async (
priceAuthority,
reservePublicFacet,
loanTiming: loanParams,
liquidationInstall: liquidateInstallation,
timer: chainTimerService,
electorateInvitationAmount: poserInvitationAmount,
ammPublicFacet,
liquidationTerms: liquidationDetailTerms(centralBrand),
minInitialDebt: AmountMath.make(centralBrand, minInitialDebt),
bootstrapPaymentValue: 0n,
shortfallInvitationAmount,
@@ -540,7 +359,6 @@ export const startRewardDistributor = async ({
bankManager,
vaultFactoryKit,
periodicFeeCollectors,
ammKit,
stakeFactoryKit,
reserveKit,
zoe,
@@ -620,7 +438,6 @@ export const startRewardDistributor = async ({

const collectorKit = {
vaultFactory: E.get(vaultFactoryKit).creatorFacet,
amm: E.get(ammKit).creatorFacet,
runStake: E.get(stakeFactoryKit).creatorFacet,
};
await Promise.all(
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/proposals/sim-behaviors.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { E, Far } from '@endo/far';
import { addRemote } from '@agoric/vats/src/core/utils.js';

export { connectFaucet, fundAMM } from './demoIssuers.js';
export { connectFaucet } from './demoIssuers.js';

/** @param {BootstrapPowers} powers */
export const installSimEgress = async ({
179 changes: 9 additions & 170 deletions packages/inter-protocol/src/reserve/assetReserve.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import { E, Far } from '@endo/far';
import { AmountMath } from '@agoric/ertp';
import { handleParamGovernance, ParamTypes } from '@agoric/governance';
import {
offerTo,
atomicTransfer,
} from '@agoric/zoe/src/contractSupport/index.js';
import { handleParamGovernance } from '@agoric/governance';
import { atomicTransfer } from '@agoric/zoe/src/contractSupport/index.js';
import { provideDurableMapStore, prepareKindMulti } from '@agoric/vat-data';

import { makeTracer } from '@agoric/internal';
import { AMM_INSTANCE } from './params.js';
import { makeMetricsPublisherKit } from '../contractSupport.js';

const { Fail, quote: q } = assert;

const trace = makeTracer('Reserve', false);

const makeLiquidityKeyword = keyword => `${keyword}Liquidity`;

const nonalphanumeric = /[^A-Za-z0-9]/g;

/**
* @typedef {object} MetricsNotification
*
@@ -56,18 +48,11 @@ const nonalphanumeric = /[^A-Za-z0-9]/g;

/**
* Asset Reserve holds onto assets for the Inter Protocol, and can
* dispense it for various purposes under governance control. It currently
* supports governance decisions to add liquidity to an AMM pool.
* dispense it for various purposes under governance control.
*
* This contract has the ability to mint Fee tokens, granted through its private
* arguments. When adding liquidity to an AMM pool, it mints new Fee tokens and
* merges them with the specified amount of collateral on hand. It then deposits
* both into an AMM pool by using the AMM's method that allows the pool balance
* to be determined based on the contributed funds.
*
* @param {ZCF<GovernanceTerms<{AmmInstance: 'instance'}> &
* @param {ZCF<GovernanceTerms<{}> &
* {
* governedApis: ['addLiquidityToAmmPool'],
* governedApis: ['burnFeesToReduceShortfall'],
* }
* >} zcf
* @param {{
@@ -96,13 +81,6 @@ const start = async (zcf, privateArgs, baggage) => {
* @type {MapStore<Keyword, Brand>}
*/
const brandForKeyword = provideDurableMapStore(baggage, 'brandForKeyword');
/**
* @type {MapStore<Brand<'nat'>, Brand<'nat'>>}
*/
const liquidityBrandForBrand = provideDurableMapStore(
baggage,
'liquidityBrandForBrand',
);

/** @type {() => Promise<ZCFMint<'nat'>>} */
const takeFeeMint = async () => {
@@ -133,22 +111,15 @@ const start = async (zcf, privateArgs, baggage) => {
saveBrandKeyword(feeKit.brand, 'Fee');
// no need to saveIssuer() b/c registerFeeMint did it

const { augmentVirtualPublicFacet, makeVirtualGovernorFacet, params } =
const { augmentVirtualPublicFacet, makeVirtualGovernorFacet } =
await handleParamGovernance(
zcf,
privateArgs.initialPoserInvitation,
{
[AMM_INSTANCE]: ParamTypes.INSTANCE,
},
{},
privateArgs.storageNode,
privateArgs.marshaller,
);

/** @type {Promise<XYKAMMPublicFacet>} */
const ammPublicFacet = E(zcf.getZoeService()).getPublicFacet(
params.getAmmInstance(),
);

/**
* @param {MethodContext} _context
* @param {Issuer} issuer
@@ -171,71 +142,6 @@ const start = async (zcf, privateArgs, baggage) => {
await zcf.saveIssuer(issuer, keyword);
};

/**
* @param {MethodContext} _context
* @param {Issuer<'nat'>} baseIssuer on which the liquidity issuer is based
*/
const addLiquidityIssuer = async (_context, baseIssuer) => {
const getBrand = () => {
try {
return zcf.getBrandForIssuer(baseIssuer);
} catch {
assert.fail(
`baseIssuer ${baseIssuer} not known; try addIssuer() first`,
);
}
};
const baseBrand = getBrand();
const baseKeyword = keywordForBrand.get(baseBrand);
const liquidityIssuer = E(ammPublicFacet).getLiquidityIssuer(baseBrand);
const liquidityBrand = await E(liquidityIssuer).getBrand();
const liquidityKeyword = makeLiquidityKeyword(baseKeyword);

trace('addLiquidityIssuer', {
baseBrand,
baseKeyword,
liquidityBrand,
liquidityKeyword,
});
saveBrandKeyword(liquidityBrand, liquidityKeyword);
liquidityBrandForBrand.init(baseBrand, liquidityBrand);

await zcf.saveIssuer(liquidityIssuer, liquidityKeyword);
};

const conjureKeyword = async baseBrand => {
const allegedName = await E(baseBrand).getAllegedName();
const safeName = allegedName.replace(nonalphanumeric, '');
let keyword;
let keywordNum = 0;
do {
// 'R' to guarantee leading uppercase
keyword = `R${safeName}${keywordNum || ''}`;
keywordNum += 1;
} while (brandForKeyword.has(keyword));
return keyword;
};

/**
* @param {MethodContext} context
* @param {Brand<'nat'>} ammSecondaryBrand
* @returns {Promise<void>}
*/
const addIssuerFromAmm = async (context, ammSecondaryBrand) => {
assert(
ammSecondaryBrand !== feeKit.brand,
'Fee is a special case handled by the reserve contract',
);

const keyword = await conjureKeyword(ammSecondaryBrand);
trace('addIssuerFromAmm', { ammSecondaryBrand, keyword });
// validate the AMM has this brand and match its issuer
const issuer = await E(ammPublicFacet).getSecondaryIssuer(
ammSecondaryBrand,
);
await addIssuer(context, issuer, keyword);
};

const getKeywordForBrand = brand => {
keywordForBrand.has(brand) ||
Fail`Issuer not defined for brand ${q(brand)}; first call addIssuer()`;
@@ -319,70 +225,6 @@ const start = async (zcf, privateArgs, baggage) => {
return initialState;
};

/**
* Takes collateral from the reserve, mints Fee tokens to accompany it, and uses both
* to add Liquidity to a pool in the AMM.
*
* @param {MethodContext} context
* @param {Amount<'nat'>} collateral
* @param {Amount<'nat'>} fee
*/
const addLiquidityToAmmPool = async ({ state }, collateral, fee) => {
// verify we have the funds
const collateralKeyword = getKeywordForBrand(collateral.brand);
if (
!AmountMath.isGTE(
collateralSeat.getCurrentAllocation()[collateralKeyword],
collateral,
)
) {
throw new Error('insufficient reserves for that transaction');
}

// create the Fee tokens
const offerToSeat = feeMint.mintGains(harden({ Fee: fee }));
state.totalFeeMinted = AmountMath.add(state.totalFeeMinted, fee);

atomicTransfer(zcf, collateralSeat, offerToSeat, {
[collateralKeyword]: collateral,
});

// Add Fee tokens and collateral to the AMM
const invitation = await E(
ammPublicFacet,
).makeAddLiquidityAtRateInvitation();
const mapping = harden({
Fee: 'Central',
[collateralKeyword]: 'Secondary',
});

const liqBrand = liquidityBrandForBrand.get(collateral.brand);
const proposal = harden({
give: {
Central: fee,
Secondary: collateral,
},
want: { Liquidity: AmountMath.makeEmpty(liqBrand) },
});

// chain await the completion of both the offer and the `deposited` promise
await E.get(offerTo(zcf, invitation, mapping, proposal, offerToSeat))
.deposited;

// transfer from userSeat to LiquidityToken holdings
const liquidityAmount = offerToSeat.getCurrentAllocation();
const liquidityKeyword = makeLiquidityKeyword(collateralKeyword);

atomicTransfer(
zcf,
offerToSeat,
collateralSeat,
{ Liquidity: liquidityAmount.Liquidity },
{ [liquidityKeyword]: liquidityAmount.Liquidity },
);
updateMetrics({ state });
};

/**
*
* @param {MethodContext} context
@@ -431,13 +273,11 @@ const start = async (zcf, privateArgs, baggage) => {
getMetrics: () => metricsSubscription,
});

const governedApis = { addLiquidityToAmmPool, burnFeesToReduceShortfall };
const governedApis = { burnFeesToReduceShortfall };

const publicFacet = augmentVirtualPublicFacet(
Far('Collateral Reserve public', {
makeAddCollateralInvitation,
addIssuerFromAmm,
addLiquidityIssuer,
}),
);

@@ -467,15 +307,14 @@ export { start };
* @typedef {object} OriginalAssetReserveCreatorFacet
* @property {() => Invitation} makeAddCollateralInvitation
* @property {() => Allocation} getAllocations
* @property {(issuer: Issuer) => void} addIssuer
* @property {(issuer: Issuer, kwd: string) => void} addIssuer
* @property {() => Invitation<ShortfallReporter>} makeShortfallReportingInvitation
* @property {() => StoredSubscription<MetricsNotification>} getMetrics
*/

/**
* @typedef {object} OriginalAssetReservePublicFacet
* @property {() => Invitation} makeAddCollateralInvitation
* @property {(brand: Brand) => void} addIssuerFromAmm
* @property {(issuer: Issuer) => void} addLiquidityIssuer
*/

5 changes: 1 addition & 4 deletions packages/inter-protocol/src/reserve/params.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { CONTRACT_ELECTORATE, ParamTypes } from '@agoric/governance';

export const AMM_INSTANCE = 'AmmInstance';

const makeReserveTerms = (poserInvitationAmount, ammInstance, timer) => ({
const makeReserveTerms = (poserInvitationAmount, timer) => ({
timer,
governedParams: harden({
[AMM_INSTANCE]: { type: ParamTypes.INSTANCE, value: ammInstance },
[CONTRACT_ELECTORATE]: {
type: ParamTypes.INVITATION,
value: poserInvitationAmount,
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ const trace = makeTracer('LiqI', false);

/**
* @typedef {{
* amm: XYKAMMPublicFacet,
* amm: any,
* priceAuthority: PriceAuthority,
* reservePublicFacet: AssetReservePublicFacet,
* timerService: import('@agoric/time/src/types').TimerService,
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ const trace = makeTracer('LiqMin', false);
* uses the AMM's swapIn instead.
*
* @param {ZCF<{
* amm: XYKAMMPublicFacet,
* amm: any,
* }>} zcf
*/
const start = async zcf => {
29 changes: 0 additions & 29 deletions packages/inter-protocol/src/vaultFactory/params.js
Original file line number Diff line number Diff line change
@@ -21,24 +21,18 @@ export const LIQUIDATION_PADDING_KEY = 'LiquidationPadding';
export const LIQUIDATION_PENALTY_KEY = 'LiquidationPenalty';
export const INTEREST_RATE_KEY = 'InterestRate';
export const LOAN_FEE_KEY = 'LoanFee';
export const LIQUIDATION_INSTALL_KEY = 'LiquidationInstall';
export const LIQUIDATION_TERMS_KEY = 'LiquidationTerms';
export const MIN_INITIAL_DEBT_KEY = 'MinInitialDebt';
export const SHORTFALL_INVITATION_KEY = 'ShortfallInvitation';
export const ENDORSED_UI_KEY = 'EndorsedUI';

/**
* @param {Amount} electorateInvitationAmount
* @param {Installation} liquidationInstall
* @param {import('./liquidation.js').LiquidationTerms} liquidationTerms
* @param {Amount} minInitialDebt
* @param {Amount} shortfallInvitationAmount
* @param {string} endorsedUi
*/
const makeVaultDirectorParams = (
electorateInvitationAmount,
liquidationInstall,
liquidationTerms,
minInitialDebt,
shortfallInvitationAmount,
endorsedUi,
@@ -48,14 +42,6 @@ const makeVaultDirectorParams = (
type: ParamTypes.INVITATION,
value: electorateInvitationAmount,
},
[LIQUIDATION_INSTALL_KEY]: {
type: ParamTypes.INSTALLATION,
value: liquidationInstall,
},
[LIQUIDATION_TERMS_KEY]: {
type: ParamTypes.UNKNOWN,
value: liquidationTerms,
},
[MIN_INITIAL_DEBT_KEY]: {
type: ParamTypes.AMOUNT,
value: minInitialDebt,
@@ -118,8 +104,6 @@ export const vaultParamPattern = M.splitRecord(
* @param {import('@agoric/notifier').StoredPublisherKit<GovernanceSubscriptionState>} publisherKit
* @param {ERef<ZoeService>} zoe
* @param {Invitation} electorateInvitation
* @param {Installation} liquidationInstall
* @param {object} liquidationTerms
* @param {Amount} minInitialDebt
* @param {Invitation} shortfallInvitation
* @param {string} [endorsedUi]
@@ -128,8 +112,6 @@ export const makeVaultDirectorParamManager = async (
publisherKit,
zoe,
electorateInvitation,
liquidationInstall,
liquidationTerms,
minInitialDebt,
shortfallInvitation,
endorsedUi = 'NO ENDORSEMENT',
@@ -138,8 +120,6 @@ export const makeVaultDirectorParamManager = async (
publisherKit,
{
[CONTRACT_ELECTORATE]: [ParamTypes.INVITATION, electorateInvitation],
[LIQUIDATION_INSTALL_KEY]: [ParamTypes.INSTALLATION, liquidationInstall],
[LIQUIDATION_TERMS_KEY]: [ParamTypes.UNKNOWN, liquidationTerms],
[MIN_INITIAL_DEBT_KEY]: [ParamTypes.AMOUNT, minInitialDebt],
[SHORTFALL_INVITATION_KEY]: [ParamTypes.INVITATION, shortfallInvitation],
[ENDORSED_UI_KEY]: [ParamTypes.STRING, endorsedUi],
@@ -158,22 +138,16 @@ harden(makeVaultDirectorParamManager);
* priceAuthority: ERef<PriceAuthority>,
* timer: ERef<import('@agoric/time/src/types').TimerService>,
* reservePublicFacet: AssetReservePublicFacet,
* liquidationInstall: Installation,
* loanTiming: LoanTiming,
* liquidationTerms: import('./liquidation.js').LiquidationTerms,
* ammPublicFacet: XYKAMMPublicFacet,
* shortfallInvitationAmount: Amount,
* endorsedUi?: string,
* }} opts
*/
export const makeGovernedTerms = (
{ storageNode, marshaller },
{
ammPublicFacet,
bootstrapPaymentValue,
electorateInvitationAmount,
liquidationInstall,
liquidationTerms,
loanTiming,
minInitialDebt,
priceAuthority,
@@ -198,15 +172,12 @@ export const makeGovernedTerms = (
).getParams();

return harden({
ammPublicFacet,
priceAuthority,
loanTimingParams,
reservePublicFacet,
timerService: timer,
governedParams: makeVaultDirectorParams(
electorateInvitationAmount,
liquidationInstall,
liquidationTerms,
minInitialDebt,
shortfallInvitationAmount,
endorsedUi,
6 changes: 0 additions & 6 deletions packages/inter-protocol/src/vaultFactory/types.js
Original file line number Diff line number Diff line change
@@ -100,12 +100,6 @@
* @property {RelativeTime} recordingPeriod in seconds
*/

/**
* @typedef {object} AMMFees
* @property {bigint} poolFee
* @property {bigint} protocolFee
*/

/**
* @typedef {object} LiquidationStrategy
* @property {() => KeywordKeywordRecord} keywordMapping
4 changes: 1 addition & 3 deletions packages/inter-protocol/src/vaultFactory/vault.js
Original file line number Diff line number Diff line change
@@ -137,9 +137,7 @@ const allocationsChangedSinceQuote = (
if (AmountMath.isGTE(newCollateralPre, newCollateral)) {
// The collateral did not go up. If the collateral decreased, we pro-rate maxDebt.
// We can pro-rate maxDebt because the quote is either linear (price is
// unchanging) or super-linear (also called "convex"). Super-linear is from
// AMMs: selling less collateral would mean an even smaller price impact, so
// this is a conservative choice.
// unchanging) or super-linear (also called "convex").
const debtPerCollateral = makeRatioFromAmounts(
maxDebtPre,
newCollateralPre,
34 changes: 1 addition & 33 deletions packages/inter-protocol/src/vaultFactory/vaultDirector.js
Original file line number Diff line number Diff line change
@@ -3,13 +3,12 @@ import { AmountMath, AmountShape, BrandShape, IssuerShape } from '@agoric/ertp';
import {
makePublicTopic,
makeStoredPublisherKit,
observeIteration,
pipeTopicToStorage,
prepareDurablePublishKit,
SubscriberShape,
TopicsRecordShape,
} from '@agoric/notifier';
import { keyEQ, M, makeScalarMapStore, mustMatch } from '@agoric/store';
import { M, makeScalarMapStore, mustMatch } from '@agoric/store';
import {
defineDurableExoClassKit,
makeKindHandle,
@@ -131,11 +130,6 @@ export const prepareVaultDirector = (
};
};

const getLiquidationConfig = () => ({
install: directorParamManager.getLiquidationInstall(),
terms: directorParamManager.getLiquidationTerms(),
});

/**
* @returns {MetricsNotification}
*/
@@ -146,29 +140,6 @@ export const prepareVaultDirector = (
});
};

/**
*
* @param {VaultManager} vaultManager
* @param {Installation<unknown>} oldInstall
* @param {unknown} oldTerms
*/
const watchGovernance = (vaultManager, oldInstall, oldTerms) => {
const subscription = directorParamManager.getSubscription();
void observeIteration(subscription, {
updateState(_paramUpdate) {
const { install, terms } = getLiquidationConfig();
if (install === oldInstall && keyEQ(terms, oldTerms)) {
return;
}
oldInstall = install;
oldTerms = terms;
vaultManager
.setupLiquidator(install, terms)
.catch(e => console.error('Failed to setup liquidator', e));
},
});
};

const updateShortfallReporter = async () => {
const oldInvitation = baggage.has(shortfallInvitationKey)
? baggage.get(shortfallInvitationKey)
@@ -405,9 +376,6 @@ export const prepareVaultDirector = (

const { self: vm } = makeVaultManager();
collateralTypes.init(collateralBrand, vm);
const { install, terms } = getLiquidationConfig();
await vm.setupLiquidator(install, terms);
watchGovernance(vm, install, terms);
facets.machine.updateMetrics();
state.managerCounter += 1;
return vm;
14 changes: 2 additions & 12 deletions packages/inter-protocol/src/vaultFactory/vaultFactory.js
Original file line number Diff line number Diff line change
@@ -6,10 +6,8 @@ import '@agoric/zoe/src/contracts/exported.js';
//
// addVaultType is a closely held method that adds a brand new collateral type.
// It specifies the initial exchange rate for that type. It depends on a
// separately specified AMM to provide the ability to liquidate loans that are
// in arrears. We could check that the AMM has sufficient liquidity, but for the
// moment leave that to those participating in the governance process for adding
// new collateral type to ascertain.
// separately specified mechanism to liquidate loans that are
// in arrears.

// This contract wants to be managed by a contractGovernor, but it isn't
// compatible with contractGovernor, since it has a separate paramManager for
@@ -22,17 +20,13 @@ import { makeStoredPublisherKit } from '@agoric/notifier';
import { assertAllDefined } from '@agoric/internal';
import {
makeVaultDirectorParamManager,
LIQUIDATION_INSTALL_KEY,
LIQUIDATION_TERMS_KEY,
MIN_INITIAL_DEBT_KEY,
ENDORSED_UI_KEY,
} from './params.js';
import { prepareVaultDirector } from './vaultDirector.js';

/**
* @typedef {ZCF<GovernanceTerms<import('./params').VaultDirectorParams> & {
* ammPublicFacet: AutoswapPublicFacet,
* liquidationInstall: Installation<import('./liquidateMinimum.js').start>,
* loanTimingParams: {ChargingPeriod: ParamValueTyped<'nat'>, RecordingPeriod: ParamValueTyped<'nat'>},
* minInitialDebt: Amount,
* priceAuthority: ERef<PriceAuthority>,
@@ -74,8 +68,6 @@ export const start = async (zcf, privateArgs, baggage) => {
}));

const {
[LIQUIDATION_INSTALL_KEY]: { value: liqInstall },
[LIQUIDATION_TERMS_KEY]: { value: liqTerms },
[MIN_INITIAL_DEBT_KEY]: { value: minInitialDebt },
[ENDORSED_UI_KEY]: { value: endorsedUi },
} = zcf.getTerms().governedParams;
@@ -84,8 +76,6 @@ export const start = async (zcf, privateArgs, baggage) => {
makeStoredPublisherKit(storageNode, marshaller, 'governance'),
zcf.getZoeService(),
initialPoserInvitation,
liqInstall,
liqTerms,
minInitialDebt,
initialShortfallInvitation,
endorsedUi,
233 changes: 5 additions & 228 deletions packages/inter-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
@@ -39,20 +39,15 @@ import {
} from '@agoric/vat-data';
import {
assertProposalShape,
atomicTransfer,
ceilDivideBy,
getAmountIn,
getAmountOut,
makeRatio,
makeRatioFromAmounts,
provideEmptySeat,
} from '@agoric/zoe/src/contractSupport/index.js';
import { InstallationShape, SeatShape } from '@agoric/zoe/src/typeGuards.js';
import { SeatShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/eventual-send';
import { TransferPartShape } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
import { checkDebtLimit } from '../contractSupport.js';
import { chargeInterest } from '../interest.js';
import { liquidate, makeQuote, updateQuote } from './liquidation.js';
import { updateQuote } from './liquidation.js';
import { maxDebtForVault } from './math.js';
import { makePrioritizedVaults } from './prioritizedVaults.js';
import { Phase, prepareVault } from './vault.js';
@@ -72,8 +67,6 @@ const trace = makeTracer('VM', false);
* @property {number} numLiquidatingVaults present count of liquidating vaults
* @property {Amount<'nat'>} totalCollateral present sum of collateral across all vaults
* @property {Amount<'nat'>} totalDebt present sum of debt across all vaults
* @property {Amount<'nat'>} retainedCollateral collateral held as a result of not returning excess refunds
* from AMM to owners of vaults liquidated with shortfalls
*
* @property {Amount<'nat'>} totalCollateralSold running sum of collateral sold in liquidation
* @property {Amount<'nat'>} totalOverageReceived running sum of overages, central received greater than debt
@@ -207,11 +200,6 @@ export const prepareVaultManagerKit = (
);

const poolIncrementSeat = provideEmptySeat(zcf, baggage, 'pool increment');
const retainedCollateralSeat = provideEmptySeat(
zcf,
baggage,
'retained collateral',
);

const topics = harden({
asset: makePublicTopic(
@@ -230,7 +218,7 @@ export const prepareVaultManagerKit = (
/** @type {boolean} */
let liquidationQueueing = false;
/** @type {Promise<MutableQuote>?} */
let outstandingQuote = null;
const outstandingQuote = null;

/**
* This class is a singleton kind so initState will be called only once per prepare.
@@ -297,11 +285,7 @@ export const prepareVaultManagerKit = (
}),
self: M.interface('self', {
getGovernedParams: M.call().returns(M.remotable()),
liquidateAll: M.call().returns(M.promise()),
makeVaultKit: M.call(SeatShape).returns(M.promise()),
setupLiquidator: M.call(InstallationShape, M.record()).returns(
M.promise(),
),
getCollateralQuote: M.call().returns(M.promise()),
getPublicFacet: M.call().returns(M.remotable()),
}),
@@ -409,16 +393,12 @@ export const prepareVaultManagerKit = (
updateMetrics() {
const { state } = this;

const retainedCollateral =
retainedCollateralSeat.getCurrentAllocation()?.Collateral ??
AmountMath.makeEmpty(collateralBrand, 'nat');
/** @type {MetricsNotification} */
const payload = harden({
numActiveVaults: prioritizedVaults.getCount(),
numLiquidatingVaults: liquidatingVaults.getSize(),
totalCollateral: state.totalCollateral,
totalDebt: state.totalDebt,
retainedCollateral,

numLiquidationsCompleted: state.numLiquidationsCompleted,
totalCollateralSold: state.totalCollateralSold,
@@ -445,7 +425,6 @@ export const prepareVaultManagerKit = (
* @returns {Promise<void>}
*/
async reschedulePriceCheck(highestRatio) {
const { facets } = this;
trace('reschedulePriceCheck', collateralBrand, {
liquidationQueueing,
outstandingQuote: !!outstandingQuote,
@@ -454,13 +433,8 @@ export const prepareVaultManagerKit = (
// and process liquidations over time.
if (!liquidationQueueing) {
liquidationQueueing = true;
// eslint-disable-next-line consistent-return
return facets.helper
.processLiquidations()
.catch(e => console.error('Liquidator failed', e))
.finally(() => {
liquidationQueueing = false;
});
// TODO(7047) replace with new approach to liquidation
return;
}

if (!outstandingQuote) {
@@ -485,150 +459,6 @@ export const prepareVaultManagerKit = (
updateQuote(outstandingQuote, highestDebtRatio, liquidationMargin);
trace('update quote', collateralBrand, highestDebtRatio);
},

async processLiquidations() {
const { facets } = this;
const govParams = factoryPowers.getGovernedParams();

async function* eventualLiquidations() {
while (true) {
const highestDebtRatio = prioritizedVaults.highestRatio();
if (!highestDebtRatio) {
return;
}
const liquidationMargin = govParams.getLiquidationMargin();

// ask to be alerted when the price level falls enough that the vault
// with the highest debt to collateral ratio will no longer be valued at the
// liquidationMargin above its debt.
outstandingQuote = makeQuote(
priceAuthority,
highestDebtRatio,
liquidationMargin,
);
trace('posted quote request', collateralBrand, highestDebtRatio);

// The rest of this method will not happen until after a quote is received.
// This may not happen until much later, when the market changes.
// eslint-disable-next-line no-await-in-loop, @jessie.js/no-nested-await -- loop/nesting to yield each unconditionally
const quote = await E(outstandingQuote).getPromise();
outstandingQuote = null;
// When we receive a quote, we check whether the vault with the highest
// ratio of debt to collateral is below the liquidationMargin, and if so,
// we liquidate it. We use ceilDivide to round up because ratios above
// this will be liquidated.
const quoteRatioPlusMargin = makeRatioFromAmounts(
ceilDivideBy(getAmountOut(quote), liquidationMargin),
getAmountIn(quote),
);
trace('quote', collateralBrand, quote, quoteRatioPlusMargin);

// Liquidate the head of the queue
const [next] =
prioritizedVaults.entriesPrioritizedGTE(quoteRatioPlusMargin);
if (next) {
yield next;
}
}
}
for await (const next of eventualLiquidations()) {
await facets.helper.liquidateAndRemove(next);
trace('price check liq', collateralBrand, next && next[0]);
}
},

/**
* @param {[key: string, vaultKit: Vault]} record
*/
liquidateAndRemove([key, vault]) {
const { state, facets } = this;
const vaultSeat = vault.getVaultSeat();
trace('liquidating', collateralBrand, vaultSeat.getProposal());

const collateralPre = vault.getCollateralAmount();

// Start liquidation (vaultState: LIQUIDATING)
const liquidator = state.liquidator;
assert(liquidator);
liquidatingVaults.add(vault);
prioritizedVaults.removeVault(key);

return liquidate(
zcf,
vault,
liquidator,
collateralBrand,
factoryPowers.getGovernedParams().getLiquidationPenalty(),
)
.then(accounting => {
facets.manager.burnAndRecord(accounting.toBurn, vaultSeat);

// current values

// Sometimes, the AMM will sell less than all the collateral. If there
// was a shortfall, the investor doesn't keep the change, so we get it.
// If there was no shortfall, the collateral is returned.
const collateralPost = vault.getCollateralAmount();
if (
!AmountMath.isEmpty(collateralPost) &&
!AmountMath.isEmpty(accounting.shortfall)
) {
// The borrower doesn't get the excess collateral remaining when
// liquidation results in a shortfall. We currently do nothing with
// it. We could hold it until it crosses some threshold, then sell it
// to the AMM, or we could transfer it to the reserve. At least it's
// visible in the accounting.
atomicTransfer(zcf, vaultSeat, retainedCollateralSeat, {
Collateral: collateralPost,
});
}

// Reduce totalCollateral by collateralPre, since all the collateral was
// sold, returned to the vault owner, or held by the VaultManager.
state.totalCollateral = AmountMath.subtract(
state.totalCollateral,
collateralPre,
);
state.totalDebt = AmountMath.subtract(
state.totalDebt,
accounting.shortfall,
);

// cumulative values
state.totalProceedsReceived = AmountMath.add(
state.totalProceedsReceived,
accounting.proceeds,
);
state.totalOverageReceived = AmountMath.add(
state.totalOverageReceived,
accounting.overage,
);
state.totalShortfallReceived = AmountMath.add(
state.totalShortfallReceived,
accounting.shortfall,
);
liquidatingVaults.delete(vault);
trace('liquidated', collateralBrand);
state.numLiquidationsCompleted += 1;
facets.helper.updateMetrics();

if (!AmountMath.isEmpty(accounting.shortfall)) {
E(factoryPowers.getShortfallReporter())
.increaseLiquidationShortfall(accounting.shortfall)
.catch(reason =>
console.error(
'liquidateAndRemove failed to increaseLiquidationShortfall',
reason,
),
);
}
})
.catch(e => {
// XXX should notify interested parties
console.error('liquidateAndRemove failed with', e);
throw e;
});
},
},
manager: {
getGovernedParams() {
@@ -779,20 +609,6 @@ export const prepareVaultManagerKit = (
return factoryPowers.getGovernedParams();
},

/**
* In extreme situations, system health may require liquidating all vaults.
* This starts the liquidations all in parallel.
*/
async liquidateAll() {
const {
facets: { helper },
} = this;
const toLiquidate = Array.from(prioritizedVaults.entries()).map(
entry => helper.liquidateAndRemove(entry),
);
await Promise.all(toLiquidate);
},

/**
* @param {ZCFSeat} seat
*/
@@ -868,45 +684,6 @@ export const prepareVaultManagerKit = (
}
},

/**
*
* @param {Installation} liquidationInstall
* @param {object} liquidationTerms
*/
async setupLiquidator(liquidationInstall, liquidationTerms) {
const { state, facets } = this;
const { ammPublicFacet, reservePublicFacet } = zcf.getTerms();
const zoe = zcf.getZoeService();
const collateralIssuer = zcf.getIssuerForBrand(collateralBrand);
const debtIssuer = zcf.getIssuerForBrand(debtBrand);
trace('setup liquidator', collateralBrand, {
debtBrand,
debtIssuer,
collateralBrand,
liquidationTerms,
});
const { creatorFacet, instance } = await E(zoe).startInstance(
liquidationInstall,
harden({ Minted: debtIssuer, Collateral: collateralIssuer }),
harden({
...liquidationTerms,
amm: ammPublicFacet,
debtBrand,
reservePublicFacet,
priceAuthority,
timerService,
}),
);
trace('setup liquidator complete', collateralBrand, {
instance,
old: state.liquidatorInstance,
equal: state.liquidatorInstance === instance,
});
state.liquidatorInstance = instance;
state.liquidator = creatorFacet;
facets.helper.assetNotify();
},

async getCollateralQuote() {
// get a quote for one unit of the collateral
return E(priceAuthority).quoteGiven(collateralUnit, debtBrand);
89 changes: 0 additions & 89 deletions packages/inter-protocol/src/vpool-xyk-amm/AttackersGuide.md

This file was deleted.

221 changes: 0 additions & 221 deletions packages/inter-protocol/src/vpool-xyk-amm/addLiquidity.js

This file was deleted.

200 changes: 0 additions & 200 deletions packages/inter-protocol/src/vpool-xyk-amm/addPool.js

This file was deleted.

Binary file not shown.
204 changes: 0 additions & 204 deletions packages/inter-protocol/src/vpool-xyk-amm/constantProduct/README.md

This file was deleted.

This file was deleted.

This file was deleted.

145 changes: 0 additions & 145 deletions packages/inter-protocol/src/vpool-xyk-amm/constantProduct/core.js

This file was deleted.

This file was deleted.

48 changes: 0 additions & 48 deletions packages/inter-protocol/src/vpool-xyk-amm/constantProduct/getXY.js

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit afd8d53

Please sign in to comment.