Skip to content

Commit

Permalink
chore(run-protocol)!: centralSupply contract for bootstrapPayment
Browse files Browse the repository at this point in the history
closes #4021

BREAKING CHANGE: removes getBootstrapPayment from VaultFactory
  • Loading branch information
dckc authored and dtribble committed Feb 22, 2022
1 parent 43baae5 commit 9547818
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 142 deletions.
1 change: 1 addition & 0 deletions packages/run-protocol/scripts/build-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ async function main() {
`${srcDir}/vaultFactory/vaultFactory.js`,
`${bundlesDir}/bundle-vaultFactory.js`,
],
[`${srcDir}/centralSupply.js`, `${bundlesDir}/bundle-centralSupply.js`],
[
`${srcDir}/vaultFactory/liquidateMinimum.js`,
`${bundlesDir}/bundle-liquidateMinimum.js`,
Expand Down
57 changes: 57 additions & 0 deletions packages/run-protocol/src/centralSupply.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// @ts-check

import { AmountMath } from '@agoric/ertp';
import { E, Far } from '@endo/far';

const { details: X } = assert;

/**
* The sole purpose of this contract is to mint the initial
* supply of the central currency, RUN.
*
* @param {ContractFacet} zcf
* @param {Object} privateArgs
* @param {FeeMintAccess} privateArgs.feeMintAccess
*/
export const start = async (zcf, { feeMintAccess }) => {
const { bootstrapPaymentValue } = zcf.getTerms();
assert.typeof(bootstrapPaymentValue, 'bigint');

const runMint = await zcf.registerFeeMint('RUN', feeMintAccess);
const { brand: runBrand } = await E(runMint).getIssuerRecord();
const mintBootstrapPayment = () => {
const { zcfSeat: bootstrapZCFSeat, userSeat: bootstrapUserSeat } =
zcf.makeEmptySeatKit();
const bootstrapAmount = AmountMath.make(runBrand, bootstrapPaymentValue);
runMint.mintGains(
harden({
Bootstrap: bootstrapAmount,
}),
bootstrapZCFSeat,
);
bootstrapZCFSeat.exit();
const bootstrapPayment = E(bootstrapUserSeat).getPayout('Bootstrap');

/**
* @param {Amount=} expectedAmount - if provided, assert that the bootstrap
* payment is at least the expected amount
*/
const getBootstrapPayment = expectedAmount => {
if (expectedAmount) {
assert(
AmountMath.isGTE(bootstrapAmount, expectedAmount),
X`${bootstrapAmount} is not at least ${expectedAmount}`,
);
}
return bootstrapPayment;
};
return getBootstrapPayment;
};

return {
creatorFacet: Far('creator', {
getBootstrapPayment: mintBootstrapPayment(),
}),
};
};
harden(start);
2 changes: 2 additions & 0 deletions packages/run-protocol/src/importedBundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import binaryVoteCounterBundle from '../bundles/bundle-binaryVoteCounter.js';
import liquidateBundle from '../bundles/bundle-liquidateMinimum.js';
import ammBundle from '../bundles/bundle-amm.js';
import vaultFactoryBundle from '../bundles/bundle-vaultFactory.js';
import centralSupplyBundle from '../bundles/bundle-centralSupply.js';

/** @type { Record<string, { moduleFormat: string }>} */
export const governanceBundles = {
Expand All @@ -21,6 +22,7 @@ export const economyBundles = {
liquidate: liquidateBundle,
amm: ammBundle,
VaultFactory: vaultFactoryBundle,
centralSupply: centralSupplyBundle,
};
harden(economyBundles);

Expand Down
1 change: 0 additions & 1 deletion packages/run-protocol/src/vaultFactory/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
* @property {AddVaultType} addVaultType
* @property {() => Promise<Array<Collateral>>} getCollaterals
* @property {() => Allocation} getRewardAllocation,
* @property {() => Promise<Payment>} getBootstrapPayment
* @property {() => Instance} getContractGovernor
* @property {() => Promise<Invitation>} makeCollectFeesInvitation
*/
Expand Down
33 changes: 0 additions & 33 deletions packages/run-protocol/src/vaultFactory/vaultFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
getAmountIn,
} from '@agoric/zoe/src/contractSupport/index.js';
import { makeRatioFromAmounts } from '@agoric/zoe/src/contractSupport/ratio.js';
import { AmountMath } from '@agoric/ertp';
import { Far } from '@endo/marshal';
import { CONTRACT_ELECTORATE } from '@agoric/governance';

Expand All @@ -46,7 +45,6 @@ export const start = async (zcf, privateArgs) => {
priceAuthority,
timerService,
liquidationInstall,
bootstrapPaymentValue = 0n,
electionManager,
main: { [CONTRACT_ELECTORATE]: electorateParam },
loanTimingParams,
Expand Down Expand Up @@ -194,36 +192,6 @@ export const start = async (zcf, privateArgs) => {
// bookkeeping. It's needed in tests.
const getRewardAllocation = () => rewardPoolSeat.getCurrentAllocation();

// TODO(#4021) remove this method
const mintBootstrapPayment = () => {
const { zcfSeat: bootstrapZCFSeat, userSeat: bootstrapUserSeat } =
zcf.makeEmptySeatKit();
const bootstrapAmount = AmountMath.make(runBrand, bootstrapPaymentValue);
runMint.mintGains(
harden({
Bootstrap: bootstrapAmount,
}),
bootstrapZCFSeat,
);
bootstrapZCFSeat.exit();
const bootstrapPayment = E(bootstrapUserSeat).getPayout('Bootstrap');

/**
* @param {Amount=} expectedAmount - if provided, assert that the bootstrap
* payment is at least the expected amount
*/
const getBootstrapPayment = expectedAmount => {
if (expectedAmount) {
assert(
AmountMath.isGTE(bootstrapAmount, expectedAmount),
X`${bootstrapAmount} is not at least ${expectedAmount}`,
);
}
return bootstrapPayment;
};
return getBootstrapPayment;
};

const getRatioParamState = paramDesc => {
return vaultParamManagers
.get(paramDesc.collateralBrand)
Expand Down Expand Up @@ -275,7 +243,6 @@ export const start = async (zcf, privateArgs) => {
addVaultType,
getCollaterals,
getRewardAllocation,
getBootstrapPayment: mintBootstrapPayment(),
makeCollectFeesInvitation,
getContractGovernor: () => electionManager,
});
Expand Down
124 changes: 16 additions & 108 deletions packages/run-protocol/test/vaultFactory/test-bootstrapPayment.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,14 @@ import { E } from '@agoric/eventual-send';
import bundleSource from '@endo/bundle-source';
import { makeFakeVatAdmin } from '@agoric/zoe/tools/fakeVatAdmin.js';
import { makeZoeKit } from '@agoric/zoe';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { AmountMath } from '@agoric/ertp';
import { resolve as importMetaResolve } from 'import-meta-resolve';
import { makeLoopback } from '@endo/captp';
import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js';

import {
makeLoanParams,
makeElectorateParams,
} from '../../src/vaultFactory/params.js';

const BASIS_POINTS = 10_000n;

const pathname = new URL(import.meta.url).pathname;
const dirname = path.dirname(pathname);

const vaultFactoryRoot = `${dirname}/../../src/vaultFactory/vaultFactory.js`;
const liquidationRoot = `${dirname}/../../src/vaultFactory/liquidateMinimum.js`;
const autoswapRoot = `${dirname}/../../src/vpool-xyk-amm/multipoolMarketMaker.js`;
const governanceRoot = '@agoric/governance/src/contractGovernor.js';
const electorateRoot = '@agoric/governance/src/committee.js';
const centralSupplyRoot = `${dirname}/../../src/centralSupply.js`;

const makeBundle = async sourceRoot => {
const url = await importMetaResolve(sourceRoot, import.meta.url);
Expand All @@ -40,18 +27,8 @@ const makeBundle = async sourceRoot => {
};

// makeBundle is slow, so we bundle each contract once and reuse in all tests.
const [
autoswapBundle,
vaultFactoryBundle,
liquidationBundle,
governanceBundle,
electorateBundle,
] = await Promise.all([
makeBundle(autoswapRoot),
makeBundle(vaultFactoryRoot),
makeBundle(liquidationRoot),
makeBundle(governanceRoot),
makeBundle(electorateRoot),
const [centralSupplyBundle] = await Promise.all([
makeBundle(centralSupplyRoot),
]);

const installBundle = (zoe, contractBundle) => E(zoe).install(contractBundle);
Expand All @@ -70,88 +47,34 @@ const setUpZoeForTest = async setJig => {
};
};

const makeRates = runBrand =>
harden({
liquidationMargin: makeRatio(105n, runBrand),
interestRate: makeRatio(100n, runBrand, BASIS_POINTS),
loanFee: makeRatio(500n, runBrand, BASIS_POINTS),
});

const startTreasury = async (
manualTimer,
bootstrapPaymentValue,
electorateTerms,
) => {
const startContract = async bootstrapPaymentValue => {
const { zoe, feeMintAccess } = await setUpZoeForTest(() => {});
const runIssuer = E(zoe).getFeeIssuer();
const runBrand = await E(runIssuer).getBrand();

const autoswapInstall = await installBundle(zoe, autoswapBundle);
const vaultFactoryInstall = await installBundle(zoe, vaultFactoryBundle);
const liquidationInstall = await installBundle(zoe, liquidationBundle);
const electorateInstall = await installBundle(zoe, electorateBundle);
const governanceInstall = await installBundle(zoe, governanceBundle);

const { instance: electorateInstance, creatorFacet: electorateCreatorFacet } =
await E(zoe).startInstance(electorateInstall, harden({}), electorateTerms);

const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation();
const [poserInvitation, poserInvitationAmount] = await Promise.all([
poserInvitationP,
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP),
]);

/** @type {LoanTiming} */
const loanTiming = {
chargingPeriod: 2n,
recordingPeriod: 10n,
};

const rates = makeRates(runBrand);
const loanParamTerms = makeLoanParams(loanTiming, rates);
const centralSupplyInstall = await installBundle(zoe, centralSupplyBundle);

const treasuryTerms = {
autoswapInstall,
liquidationInstall,
manualTimer,
const terms = {
bootstrapPaymentValue,
main: makeElectorateParams(poserInvitationAmount),
loanParams: loanParamTerms,
};
const governed = {
terms: treasuryTerms,
issuerKeywordRecord: {},
privateArgs: { feeMintAccess, initialPoserInvitation: poserInvitation },
};

const governorTerms = {
electorateInstance,
timer: manualTimer,
governedContractInstallation: vaultFactoryInstall,
governed,
};

const { creatorFacet } = await E(zoe).startInstance(
governanceInstall,
centralSupplyInstall,
harden({}),
governorTerms,
harden({ electorateCreatorFacet }),
terms,
harden({ feeMintAccess }),
);

return { runIssuer, creatorFacet, runBrand };
};

test('bootstrap payment', async t => {
const bootstrapPaymentValue = 20000n * 10n ** 6n;
const { runIssuer, creatorFacet, runBrand } = await startTreasury(
buildManualTimer(console.log),
const { runIssuer, creatorFacet, runBrand } = await startContract(
bootstrapPaymentValue,
{ committeeName: 'bandOfAngels', committeeSize: 5 },
);

const bootstrapPayment = E(
E(creatorFacet).getCreatorFacet(),
).getBootstrapPayment();
const bootstrapPayment = E(creatorFacet).getBootstrapPayment();

const bootstrapAmount = await E(runIssuer).getAmountOf(bootstrapPayment);

Expand All @@ -168,15 +91,11 @@ test('bootstrap payment - only minted once', async t => {
// be minted
const bootstrapPaymentValue = 20000n * 10n ** 6n;

const { runIssuer, creatorFacet, runBrand } = await startTreasury(
buildManualTimer(console.log),
const { runIssuer, creatorFacet, runBrand } = await startContract(
bootstrapPaymentValue,
{ committeeName: 'bandOfAngels', committeeSize: 5 },
);

const bootstrapPayment = E(
E(creatorFacet).getCreatorFacet(),
).getBootstrapPayment();
const bootstrapPayment = E(creatorFacet).getBootstrapPayment();

const issuers = { RUN: runIssuer };

Expand All @@ -192,30 +111,19 @@ test('bootstrap payment - only minted once', async t => {

// Try getting another payment

const bootstrapPayment2 = E(
E(creatorFacet).getCreatorFacet(),
).getBootstrapPayment();
const bootstrapPayment2 = E(creatorFacet).getBootstrapPayment();

await t.throwsAsync(() => E(issuers.RUN).claim(bootstrapPayment2), {
message: /was not a live payment/,
});
});

test('bootstrap payment - default value is 0n', async t => {
const { runIssuer, creatorFacet, runBrand } = await startTreasury(
buildManualTimer(console.log),
0n,
{
committeeName: 'bandOfAngels',
committeeSize: 5,
},
);
const { runIssuer, creatorFacet, runBrand } = await startContract(0n);

const issuers = { RUN: runIssuer };

const bootstrapPayment = E(
E(creatorFacet).getCreatorFacet(),
).getBootstrapPayment();
const bootstrapPayment = E(creatorFacet).getBootstrapPayment();

const bootstrapAmount = await E(issuers.RUN).getAmountOf(bootstrapPayment);

Expand Down

0 comments on commit 9547818

Please sign in to comment.