From 24d8051f9f794801a430af5125aeb7035c13356d Mon Sep 17 00:00:00 2001 From: anilhelvaci Date: Fri, 20 May 2022 19:58:48 +0300 Subject: [PATCH] #9 The protocol is now able to work with agoric version 0.15, lendingPool contract is governed. --- contract/deploy/deploy.js | 297 +++--- contract/package.json | 28 +- contract/src/lendingPool/lendingPool.js | 29 +- contract/src/lendingPool/orderedVaultStore.js | 70 -- contract/src/lendingPool/params.js | 85 +- contract/src/lendingPool/poolManager.js | 12 - contract/src/lendingPool/prioritizedVaults.js | 180 ---- contract/src/lendingPool/types.js | 1 + contract/test/lendingPool/helpers.js | 180 +--- contract/test/lendingPool/setup.js | 406 ++++++-- contract/test/lendingPool/test-lendingPool.js | 917 ++++++------------ 11 files changed, 831 insertions(+), 1374 deletions(-) delete mode 100644 contract/src/lendingPool/orderedVaultStore.js delete mode 100644 contract/src/lendingPool/prioritizedVaults.js diff --git a/contract/deploy/deploy.js b/contract/deploy/deploy.js index e3ea64e..ffde0c8 100644 --- a/contract/deploy/deploy.js +++ b/contract/deploy/deploy.js @@ -2,88 +2,107 @@ import '@agoric/zoe/exported.js'; import { - setUpZoeForTest, - setupAmmServices, + startLendingPool, + setupAmmAndElectorate, } from '../test/lendingPool/setup.js'; import { E } from '@agoric/eventual-send'; import '@agoric/zoe/src/contractSupport/index.js'; -import { depositMoney, addPool, makeRates, setupAssets, makeBundle, getLiquidityFromFaucet, startPriceManager } from '../test/lendingPool/helpers.js'; -import { makePriceManager } from '../src/lendingPool/priceManager.js'; -import { startLendingPool, startFaucets } from '../test/lendingPool/helpers.js'; +import { + addPool, + makeRates, + makeBundle, + getLiquidityFromFaucet, + startPriceManager, +} from '../test/lendingPool/helpers.js'; +import { startFaucets } from '../test/lendingPool/helpers.js'; import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp'; import { SECONDS_PER_YEAR } from '../src/interest.js'; import * as Collect from '@agoric/run-protocol/src/collect.js'; +import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; +import { makeScriptedPriceAuthority } from '@agoric/zoe/tools/scriptedPriceAuthority.js'; + const contractRoots = { lendingPoolFaucet: './lendingPoolFaucet.js', priceAuthorityFaucet: './priceAuthorityFaucet.js', liquidate: '../../src/lendingPool/liquidateMinimum.js', LendingPool: '../../src/lendingPool/lendingPool.js', priceManagerContract: '../../src/lendingPool/priceManagerContract.js', + amm: '@agoric/run-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js', }; -const setupAmmAndElectorate = async (timer, - zoe, - vanLiquidity, - usdLiquidity, - usdIssuer, - vanIssuer, - electorateTerms) => { - - const usdBrand = await E(usdIssuer).getBrand(); - const centralR = { issuer: usdIssuer, brand: usdBrand }; - +async function setupServices( + t, + timer = buildManualTimer(console.log), +) { + const { + zoe, + loanTiming, + vanInitialLiquidity, + compareInitialLiquidity, + priceManager, + } = t.context; + t.context.timer = timer; + const { amm: ammFacets, space } = await setupAmmAndElectorate( + t, + vanInitialLiquidity, + compareInitialLiquidity, + ); + const { consume, produce, instance } = space; + // trace(t, 'amm', { ammFacets }); const { - amm, - committeeCreator, - governor, - invitationAmount, - electorateInstance, - space, - } = await setupAmmServices(electorateTerms, centralR, timer, zoe); + installation: { produce: iProduce }, + } = space; + iProduce.LendingPool.resolve(t.context.installation.LendingPool); + iProduce.liquidate.resolve(t.context.installation.liquidate); + produce.priceManager.resolve(priceManager); - const liquidityIssuer = E(amm.ammPublicFacet).addPool(vanIssuer, 'VAN'); - const liquidityBrand = await E(liquidityIssuer).getBrand(); + await startLendingPool(space, { loanParams: loanTiming }); - const liqProposal = harden({ - give: { - Secondary: vanLiquidity.proposal, - Central: usdLiquidity.proposal, - }, - want: { Liquidity: AmountMath.makeEmpty(liquidityBrand) }, - }); - const liqInvitation = await E( - amm.ammPublicFacet, - ).makeAddLiquidityInvitation(); - - console.log("liqProposal", liqProposal); - - const ammLiquiditySeat = await E(zoe).offer( - liqInvitation, - liqProposal, - harden({ - Secondary: vanLiquidity.payment, - Central: usdLiquidity.payment, - }), + const governorCreatorFacet = consume.lendingPoolGovernorCreator; + /** @type {Promise>} */ + const lendingPoolCreatorFacetP = /** @type { any } */ ( + E(governorCreatorFacet).getCreatorFacet() ); - const newAmm = { - ammCreatorFacet: amm.ammCreatorFacet, - ammPublicFacet: amm.ammPublicFacet, - instance: amm.governedInstance, - ammLiquidity: E(ammLiquiditySeat).getPayout('Liquidity'), + /** @type {[any, VaultFactory, VFC['publicFacet'], VaultManager, PriceAuthority]} */ + // @ts-expect-error cast + const [ + governorInstance, + lendingPoolCreatorFacet, + lendingPoolInstance, + lendingPoolPublicFacet, + ] = await Promise.all([ + instance.consume.lendingPoolGovernor, + lendingPoolCreatorFacetP, + instance.consume.lendingPool, + E(governorCreatorFacet).getPublicFacet(), + ]); + // trace(t, { governorInstance, lendingPoolCreatorFacet, lendingPoolPublicFacet }); + + const { g, l } = { + g: { + governorInstance, + governorPublicFacet: E(zoe).getPublicFacet(governorInstance), + governorCreatorFacet, + }, + l: { + lendingPoolCreatorFacet, + lendingPoolPublicFacet, + lendingPoolInstance, + }, }; return { - governor, - amm: newAmm, - committeeCreator, - electorateInstance, - invitationAmount, + zoe, + governor: g, + lendingPool: l, + ammFacets, + timer, space, }; -}; +} /** * What we need to deploy the lendingPool contract @@ -104,20 +123,26 @@ export default async function deployContract( const secondsPerDay = SECONDS_PER_YEAR / 365n; - const bundlePs = { + const bundles = await Collect.allValues({ lendingPoolFaucet: makeBundle(bundleSource, contractRoots.lendingPoolFaucet), priceAuthorityFaucet: makeBundle(bundleSource, contractRoots.priceAuthorityFaucet), liquidate: makeBundle(bundleSource, contractRoots.liquidate), LendingPool: makeBundle(bundleSource, contractRoots.LendingPool), priceManagerContract: makeBundle(bundleSource, contractRoots.priceManagerContract), - }; - - const faucetBundles = await Collect.allValues({ - lendingPoolFaucet: bundlePs.lendingPoolFaucet, - priceAuthorityFaucet: bundlePs.priceAuthorityFaucet, + amm: makeBundle(bundleSource, contractRoots.amm), }); - const { vanAsset, panAsset, usdAsset, priceAuthorityFaucet, installations } = await startFaucets(zoe, faucetBundles); + const contractInstallations = Collect.mapValues(bundles, bundle => + E(zoe).install(bundle), + ); + + const { + vanAsset, + panAsset, + usdAsset, + priceAuthorityFaucet, + installations, + } = await startFaucets(zoe, contractInstallations); // get issuers and brands const vanIssuer = await E(vanAsset.publicFacet).getIssuer(); @@ -136,7 +161,7 @@ export default async function deployContract( timer, undefined, unitAmountIn: AmountMath.make(vanBrand, 100n), - quoteInterval: secondsPerDay * 7n + quoteInterval: 7n, }); const panUsdPriceAuthority = await E(priceAuthorityFaucet.creatorFacet).makeScriptedPriceAuthority({ @@ -146,35 +171,33 @@ export default async function deployContract( timer, undefined, unitAmountIn: AmountMath.make(panBrand, 100n), - quoteInterval: secondsPerDay * 7n, + quoteInterval: 7n, }); - const priceManBundle = await Collect.allValues({ - priceManagerContract: bundlePs.priceManagerContract, - }); + const priceManInstallation = await contractInstallations.priceManagerContract; - const { priceAuthorityManagerPublicFacet: priceManager, priceAuthorityManagerInstance } = await startPriceManager(zoe, priceManBundle); - - // console.log("vanUsdPriceAuthority", vanUsdPriceAuthority); - // console.log("panUsdPriceAuthority", panUsdPriceAuthority); + const { + priceAuthorityManagerPublicFacet: priceManager, + priceAuthorityManagerInstance, + } = await startPriceManager(zoe, priceManInstallation); // get liquidity - const vanLiquidity = await getLiquidityFromFaucet(zoe, E(vanAsset.creatorFacet).makeFaucetInvitation(), 5n, vanBrand, "VAN"); + const vanLiquidity = await getLiquidityFromFaucet(zoe, E(vanAsset.creatorFacet).makeFaucetInvitation(), 5n, vanBrand, 'VAN'); const vanLiquidityAmount = await E(vanIssuer).getAmountOf(vanLiquidity); - const panLiquidity = await getLiquidityFromFaucet(zoe, E(panAsset.creatorFacet).makeFaucetInvitation(), 5n, panBrand, "PAN"); + const panLiquidity = await getLiquidityFromFaucet(zoe, E(panAsset.creatorFacet).makeFaucetInvitation(), 5n, panBrand, 'PAN'); const panLiquidityAmount = await E(panIssuer).getAmountOf(panLiquidity); - const usdLiquidity = await getLiquidityFromFaucet(zoe, E(usdAsset.creatorFacet).makeFaucetInvitation(), 5n, usdBrand, "USD"); + const usdLiquidity = await getLiquidityFromFaucet(zoe, E(usdAsset.creatorFacet).makeFaucetInvitation(), 5n, usdBrand, 'USD'); const usdLiquidityAmount = await E(usdIssuer).getAmountOf(usdLiquidity); - console.log("vanLiquidity", vanLiquidity); - console.log("vanLiquidityAmount", vanLiquidityAmount); - console.log("panLiquidity", panLiquidity); - console.log("panLiquidityAmount", panLiquidityAmount); - console.log("usdLiquidity", usdLiquidity); - console.log("usdLiquidityAmount", usdLiquidityAmount); + console.log('vanLiquidity', vanLiquidity); + console.log('vanLiquidityAmount', vanLiquidityAmount); + console.log('panLiquidity', panLiquidity); + console.log('panLiquidityAmount', panLiquidityAmount); + console.log('usdLiquidity', usdLiquidity); + console.log('usdLiquidityAmount', usdLiquidityAmount); const vanLiquidityAMM = { proposal: harden(vanLiquidityAmount), @@ -189,49 +212,38 @@ export default async function deployContract( const loanTiming = { chargingPeriod: secondsPerDay, recordingPeriod: secondsPerDay * 7n, - priceCheckPeriod: secondsPerDay * 7n * 2n + priceCheckPeriod: secondsPerDay * 7n * 2n, }; const electorateTerms = { committeeName: 'TheCabal', committeeSize: 5 }; - const { amm: ammFacets, space } = await setupAmmAndElectorate( - timer, + const params = {}; + params.context = { zoe, - vanLiquidityAMM, - usdLiquidityAMM, - usdIssuer, - vanIssuer, + compareCurrencyKit: { issuer: usdIssuer, brand: usdBrand }, + vanKit: { issuer: vanIssuer, brand: vanBrand }, + loanTiming, + vanInitialLiquidity: vanLiquidityAMM, + compareInitialLiquidity: usdLiquidityAMM, electorateTerms, - ); - - const { consume, produce } = space; + priceManager, + installation: contractInstallations, + }; - const quoteMint = makeIssuerKit('quote', AssetKind.SET).mint; - // const priceManager = makePriceManager({}); - produce.priceManager.resolve(priceManager); - const vaultBundles = await Collect.allValues({ - LendingPool: bundlePs.LendingPool, - liquidate: bundlePs.liquidate, - }); - produce.vaultBundles.resolve(vaultBundles); - produce.bootstrappedAssets.resolve({ }); - produce.compareCurrencyBrand.resolve(usdBrand); const { - lendingPoolCreatorFacet, - lendingPoolPublicFacet, - lendingPoolInstance, - installations: lendingPoolInstallations - } = await startLendingPool(space, { loanParams: loanTiming }); - - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); + space, + lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet, lendingPoolInstance }, + } = await setupServices(params, timer); // Make the rates for the pools const vanPoolRates = makeRates(vanBrand, usdBrand); const panPoolRates = makeRates(panBrand, usdBrand); + console.log('lendingPoolCreatorFacet', lendingPoolCreatorFacet); + // Add the pools - const vanPoolMan = await addPool(zoe, vanPoolRates, lendingPool, vanIssuer, "VAN", vanUsdPriceAuthority); - const panPoolMan = await addPool(zoe, panPoolRates, lendingPool, panIssuer, "PAN", panUsdPriceAuthority); + const vanPoolMan = await addPool(zoe, vanPoolRates, lendingPoolCreatorFacet, vanIssuer, 'VAN', vanUsdPriceAuthority); + const panPoolMan = await addPool(zoe, panPoolRates, lendingPoolCreatorFacet, panIssuer, 'PAN', panUsdPriceAuthority); // Check the protocol tokens received const agVanIssuer = await E(vanPoolMan).getProtocolIssuer(); @@ -239,8 +251,8 @@ export default async function deployContract( const agPanIssuer = await E(panPoolMan).getProtocolIssuer(); const agPanBrand = await E(panPoolMan).getProtocolBrand(); - console.log("agVanIssuer", agVanIssuer); - console.log("agPanIssuer", agPanIssuer); + console.log('agVanIssuer', agVanIssuer); + console.log('agPanIssuer', agPanIssuer); const [ LENDING_POOL_INSTANCE_BOARD_ID, @@ -259,7 +271,7 @@ export default async function deployContract( PRICE_MANAGER_INSTANCE_BOARD_ID, ] = await Promise.all([ E(board).getId(lendingPoolInstance), - E(board).getId(lendingPoolInstallations.LendingPool), + E(board).getId(await contractInstallations.LendingPool), E(board).getId(vanAsset.instance), E(board).getId(panAsset.instance), E(board).getId(usdAsset.instance), @@ -269,16 +281,16 @@ export default async function deployContract( E(board).getId(usdIssuer), E(board).getId(agVanIssuer), E(board).getId(agPanIssuer), - E(board).getId(installations.priceAuthorityFaucet), - E(board).getId(installations.lendingPoolFaucet), + E(board).getId(await contractInstallations.priceAuthorityFaucet), + E(board).getId(await contractInstallations.lendingPoolFaucet), E(board).getId(priceAuthorityManagerInstance), ]); const walletBridge = await E(wallet).getBridge(); - await E(walletBridge).suggestIssuer("VAN Purse", VAN_ISSUER_BOARD_ID); - await E(walletBridge).suggestIssuer("PAN Purse", PAN_ISSUER_BOARD_ID); - await E(walletBridge).suggestIssuer("AgVAN Purse", AGVAN_ISSUER_BOARD_ID); - await E(walletBridge).suggestIssuer("AgPAN Purse", AGPAN_ISSUER_BOARD_ID); + await E(walletBridge).suggestIssuer('VAN Purse', VAN_ISSUER_BOARD_ID); + await E(walletBridge).suggestIssuer('PAN Purse', PAN_ISSUER_BOARD_ID); + await E(walletBridge).suggestIssuer('AgVAN Purse', AGVAN_ISSUER_BOARD_ID); + await E(walletBridge).suggestIssuer('AgPAN Purse', AGPAN_ISSUER_BOARD_ID); let vanLiqInvitation = await E(vanAsset.creatorFacet).makeFaucetInvitation(); @@ -293,7 +305,7 @@ export default async function deployContract( VAN: { // The pursePetname identifies which purse we want to use pursePetname: 'VAN Purse', - value: 5n * 10n ** 8n , + value: 5n * 10n ** 8n, }, }, }, @@ -314,7 +326,7 @@ export default async function deployContract( PAN: { // The pursePetname identifies which purse we want to use pursePetname: 'PAN Purse', - value: 10n * 10n ** 8n , + value: 10n * 10n ** 8n, }, }, }, @@ -332,20 +344,20 @@ export default async function deployContract( Protocol: { // The pursePetname identifies which purse we want to uselib pursePetname: 'AgVAN Purse', - value: 1n * 10n ** 8n * 50n , + value: 5n * 10n ** 8n * 50n, }, }, give: { Underlying: { // The pursePetname identifies which purse we want to use pursePetname: 'VAN Purse', - value: 1n * 10n ** 8n , + value: 5n * 10n ** 8n, }, }, }, }; - console.log("depositVanOfferConfig", depositVanOfferConfig); + console.log('depositVanOfferConfig', depositVanOfferConfig); const depositVanfferID = await E(walletBridge).addOffer(depositVanOfferConfig); const depositPanOfferConfig = { @@ -358,22 +370,51 @@ export default async function deployContract( Protocol: { // The pursePetname identifies which purse we want to uselib pursePetname: 'AgPAN Purse', - value: 10n * 10n ** 8n * 50n , + value: 10n * 10n ** 8n * 50n, }, }, give: { Underlying: { // The pursePetname identifies which purse we want to use pursePetname: 'PAN Purse', - value: 10n * 10n ** 8n , + value: 10n * 10n ** 8n, }, }, }, }; - console.log("depositPanOfferConfig", depositPanOfferConfig); + console.log('depositPanOfferConfig', depositPanOfferConfig); const depositPanOfferID = await E(walletBridge).addOffer(depositPanOfferConfig); + const borrowPanOfferConfig = { + id: `${Date.now()}`, + invitation: E(lendingPoolPublicFacet).makeBorrowInvitation(), + installationHandleBoardId: LENDING_POOL_INSTALL_BOARD_ID, + instanceHandleBoardId: LENDING_POOL_INSTANCE_BOARD_ID, + proposalTemplate: { + want: { + Debt: { + // The pursePetname identifies which purse we want to uselib + pursePetname: 'PAN Purse', + value: 4n * 10n ** 6n, + }, + }, + give: { + Collateral: { + // The pursePetname identifies which purse we want to use + pursePetname: 'AgVAN Purse', + value: 1n * 10n ** 8n * 50n, + }, + }, + arguments: { + collateralUnderlyingBrand: vanBrand, + }, + }, + }; + + console.log('borrowPanOfferConfig', borrowPanOfferConfig); + const borrowPanOfferID = await E(walletBridge).addOffer(borrowPanOfferConfig); + console.log(`-- LENDING_POOL_INSTANCE_BOARD_ID: ${LENDING_POOL_INSTANCE_BOARD_ID} --`); console.log(`-- LENDING_POOL_INSTALL_BOARD_ID: ${LENDING_POOL_INSTALL_BOARD_ID} --`); console.log(`-- VAN_ASSET_INSTANCE_BOARD_ID: ${VAN_ASSET_INSTANCE_BOARD_ID} --`); @@ -392,5 +433,5 @@ export default async function deployContract( console.log(`-- DEPOSIT_VAN_OFFER_ID: ${depositVanfferID} --`); console.log(`-- DEPOSIT_PAN_OFFER_ID: ${depositPanOfferID} --`); console.log(`-- PRICE_MANAGER_INSTANCE_BOARD_ID: ${PRICE_MANAGER_INSTANCE_BOARD_ID} --`); - // console.log(`-- BORROW_PAN_OFFER_ID: ${borrowPanOfferID} --`); + console.log(`-- BORROW_PAN_OFFER_ID: ${borrowPanOfferID} --`); } \ No newline at end of file diff --git a/contract/package.json b/contract/package.json index fd95669..7e0e690 100644 --- a/contract/package.json +++ b/contract/package.json @@ -4,21 +4,21 @@ "type": "module", "private": true, "dependencies": { - "@agoric/assert": "0.3.16", - "@agoric/ertp": "0.13.3", + "@agoric/assert": "0.4.0", + "@agoric/ertp": "0.14.1", "@agoric/eventual-send": "0.14.1", - "@agoric/governance": "0.4.3", - "@agoric/nat": "^4.1.0", - "@agoric/notifier": "0.3.35", - "@agoric/store": "0.6.10", - "@agoric/swingset-vat": "0.25.1", - "@agoric/vats": "0.7.0", - "@agoric/zoe": "0.21.3", - "@endo/bundle-source": "^2.1.1", - "@endo/captp": "2.0.3", - "@endo/far": "0.1.9", - "@endo/marshal": "0.6.3", - "@endo/promise-kit": "0.2.37" + "@agoric/governance": "0.6.0", + "@agoric/nat": "4.1.0", + "@agoric/notifier": "0.4.0", + "@agoric/store": "0.7.1", + "@agoric/swingset-vat": "0.27.0", + "@agoric/vats": "0.9.0", + "@agoric/zoe": "0.23.0", + "@endo/bundle-source": "2.2.0", + "@endo/captp": "2.0.7", + "@endo/far": "0.2.3", + "@endo/marshal": "0.6.7", + "@endo/promise-kit": "0.2.41" }, "devDependencies": { "@endo/init": "0.5.37", diff --git a/contract/src/lendingPool/lendingPool.js b/contract/src/lendingPool/lendingPool.js index b32e074..867e572 100644 --- a/contract/src/lendingPool/lendingPool.js +++ b/contract/src/lendingPool/lendingPool.js @@ -3,23 +3,8 @@ import '@agoric/zoe/exported.js'; import '@agoric/zoe/src/contracts/exported.js'; -// The vaultFactory owns a number of VaultManagers and a mint for RUN. -// -// 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. - -// This contract wants to be managed by a contractGovernor, but it isn't -// compatible with contractGovernor, since it has a separate paramManager for -// each Vault. This requires it to manually replicate the API of contractHelper -// to satisfy contractGovernor. It needs to return a creatorFacet with -// { getParamMgrRetriever, getInvitation, getLimitedCreatorFacet }. - import { E } from '@agoric/eventual-send'; -import '@agoric/governance/src/exported'; +import '@agoric/governance/src/exported.js'; import { AmountMath, AssetKind } from '@agoric/ertp'; import { makeScalarMap, keyEQ } from '@agoric/store'; @@ -53,11 +38,13 @@ export const start = async (zcf, privateArgs) => { timerService, liquidationInstall, electionManager, - main: { [CONTRACT_ELECTORATE]: electorateParam }, + governedParams: { [CONTRACT_ELECTORATE]: electorateParam }, loanTimingParams, bootstrappedAssets: bootstrappedAssetIssuers, compareCurrencyBrand } = zcf.getTerms(); + const { initialPoserInvitation } = privateArgs; + const electorateParamManager = await makeElectorateParamManager(E(zcf).getZoeService(), initialPoserInvitation); console.log('[PRICE_MANAGER]', priceManager); console.log('[COMPARE_CURRENCY_BRAND]', compareCurrencyBrand); @@ -87,7 +74,7 @@ export const start = async (zcf, privateArgs) => { const poolParamManager = makePoolParamManager(ratesUpdated); poolParamManagers.init(underlyingBrand, poolParamManager); - //TODO Create liquadition for dynamic underdlying assets + // TODO Create liquadition for dynamic underdlying assets // const { creatorFacet: liquidationFacet } = await E(zoe).startInstance( // liquidationInstall, // harden({ RUN: runIssuer, Collateral: underlyingIssuer }), @@ -190,10 +177,10 @@ export const start = async (zcf, privateArgs) => { const getParamMgrRetriever = () => Far('paramManagerRetriever', { get: paramDesc => { - if (paramDesc.key === 'main') { + if (paramDesc.key === 'governedParams') { return electorateParamManager; } else { - return vaultParamManagers.get(paramDesc.collateralBrand); + return poolParamManagers.get(paramDesc.collateralBrand); } }, }); @@ -207,6 +194,8 @@ export const start = async (zcf, privateArgs) => { const lendingPoolWrapper = Far('powerful lendingPool wrapper', { getParamMgrRetriever, getLimitedCreatorFacet: () => lendingPool, + getGovernedApis: () => harden({}), + getGovernedApiNames: () => harden({}), }); return harden({ diff --git a/contract/src/lendingPool/orderedVaultStore.js b/contract/src/lendingPool/orderedVaultStore.js deleted file mode 100644 index 18c2622..0000000 --- a/contract/src/lendingPool/orderedVaultStore.js +++ /dev/null @@ -1,70 +0,0 @@ -// @ts-check -// XXX avoid deep imports https://github.com/Agoric/agoric-sdk/issues/4255#issuecomment-1032117527 -import { makeScalarBigMapStore } from '@agoric/swingset-vat/src/storeModule.js'; -import { fromVaultKey, toVaultKey } from './storeUtils.js'; - -/** - * Used by prioritizedVaults to wrap the Collections API for this use case. - * - * Designed to be replaceable by naked Collections API when composite keys are available. - * - * In this module debts are encoded as the inverse quotient (collateral over debt) so that - * greater collaterization sorts after lower. (Higher debt-to-collateral come - * first.) - */ - -/** @typedef {import('./vault').InnerVault} InnerVault */ -/** @typedef {import('./storeUtils').CompositeKey} CompositeKey */ - -export const makeOrderedVaultStore = () => { - // TODO make it work durably https://github.com/Agoric/agoric-sdk/issues/4550 - /** @type {MapStore} */ - const store = makeScalarBigMapStore('orderedVaultStore', { durable: false }); - - /** - * - * @param {string} vaultId - * @param {InnerVault} vault - */ - const addVault = (vaultId, vault) => { - const debt = vault.getCurrentDebtValueInCompareCurrencyForm(); - const collateral = vault.getCurrentDebtValueInCompareCurrencyForm(); - const key = toVaultKey(debt, collateral, vaultId); - store.init(key, vault); - return key; - }; - - /** - * - * @param {string} key - * @returns {InnerVault} - */ - const removeByKey = key => { - try { - const vault = store.get(key); - assert(vault); - store.delete(key); - return vault; - } catch (e) { - const keys = Array.from(store.keys()); - console.error( - 'removeByKey failed to remove', - key, - 'parts:', - fromVaultKey(key), - ); - console.error(' key literals:', keys); - console.error(' key parts:', keys.map(fromVaultKey)); - throw e; - } - }; - - return harden({ - addVault, - removeByKey, - keys: store.keys, - entries: store.entries, - getSize: store.getSize, - values: store.values, - }); -}; diff --git a/contract/src/lendingPool/params.js b/contract/src/lendingPool/params.js index 424df46..07e83c4 100644 --- a/contract/src/lendingPool/params.js +++ b/contract/src/lendingPool/params.js @@ -3,11 +3,10 @@ import './types.js'; import { - makeGovernedNat, - makeGovernedInvitation, - makeGovernedRatio, - makeParamManagerBuilder, + makeParamManagerSync, + makeParamManager, CONTRACT_ELECTORATE, + ParamTypes } from '@agoric/governance'; export const CHARGING_PERIOD_KEY = 'ChargingPeriod'; @@ -26,7 +25,7 @@ export const MULTIPILIER_RATE_KEY = 'MultipilierRate'; */ const makeElectorateParams = electorateInvitationAmount => { return harden({ - [CONTRACT_ELECTORATE]: makeGovernedInvitation(electorateInvitationAmount), + [CONTRACT_ELECTORATE]: [ParamTypes.INVITATION, electorateInvitationAmount], }); }; @@ -35,60 +34,51 @@ const makeElectorateParams = electorateInvitationAmount => { * @param {Rates} rates */ const makeLoanParams = (loanTiming, rates) => { - return harden({ - [CHARGING_PERIOD_KEY]: makeGovernedNat(loanTiming.chargingPeriod), - [RECORDING_PERIOD_KEY]: makeGovernedNat(loanTiming.recordingPeriod), - [LIQUIDATION_MARGIN_KEY]: makeGovernedRatio(rates.liquidationMargin), - [INTEREST_RATE_KEY]: makeGovernedRatio(rates.interestRate), - [LOAN_FEE_KEY]: makeGovernedRatio(rates.loanFee), - [BASE_RATE_KEY]: makeGovernedRatio(rates.baseRate), - [MULTIPILIER_RATE_KEY]: makeGovernedRatio(rates.multipilierRate), - }); + return { + [CHARGING_PERIOD_KEY]: [ParamTypes.NAT, loanTiming.chargingPeriod], + [RECORDING_PERIOD_KEY]: [ParamTypes.NAT, loanTiming.recordingPeriod], + [LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, rates.liquidationMargin], + [INTEREST_RATE_KEY]: [ParamTypes.RATIO, rates.interestRate], + [LOAN_FEE_KEY]: [ParamTypes.RATIO, rates.loanFee], + [BASE_RATE_KEY]: [ParamTypes.RATIO, rates.baseRate], + [MULTIPILIER_RATE_KEY]: [ParamTypes.RATIO, rates.multipilierRate], + }; }; /** * @param {LoanTiming} initialValues - * @returns {ParamManagerFull & { - * updateChargingPeriod: (period: bigint) => void, - * updateRecordingPeriod: (period: bigint) => void, - * }} */ const makeLoanTimingManager = initialValues => { - // @ts-expect-error until makeParamManagerBuilder can be generic */ - return makeParamManagerBuilder() - .addNat(CHARGING_PERIOD_KEY, initialValues.chargingPeriod) - .addNat(RECORDING_PERIOD_KEY, initialValues.recordingPeriod) - .addNat(PRICE_CHECK_PERIOD_KEY, initialValues.priceCheckPeriod) - .build(); + return makeParamManagerSync({ + [CHARGING_PERIOD_KEY]: [ParamTypes.NAT, initialValues.chargingPeriod], + [RECORDING_PERIOD_KEY]: [ParamTypes.NAT, initialValues.recordingPeriod], + [PRICE_CHECK_PERIOD_KEY]: [ParamTypes.NAT, initialValues.priceCheckPeriod] + }) }; /** * @param {Rates} rates - * @returns {VaultParamManager} */ const makeVaultParamManager = rates => { - // @ts-expect-error until makeParamManagerBuilder can be generic */ - return makeParamManagerBuilder() - .addBrandedRatio(LIQUIDATION_MARGIN_KEY, rates.liquidationMargin) - .addBrandedRatio(INTEREST_RATE_KEY, rates.interestRate) - .addBrandedRatio(LOAN_FEE_KEY, rates.loanFee) - .build(); + return makeParamManagerSync({ + [LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, rates.liquidationMargin], + [INTEREST_RATE_KEY]: [ParamTypes.RATIO, rates.interestRate], + [LOAN_FEE_KEY]: [ParamTypes.RATIO, rates.loanFee], + }) }; /** * @param {Rates} rates - * @returns {VaultParamManager} */ const makePoolParamManager = rates => { - // @ts-expect-error until makeParamManagerBuilder can be generic */ - return makeParamManagerBuilder() - .addBrandedRatio(LIQUIDATION_MARGIN_KEY, rates.liquidationMargin) - .addBrandedRatio(INTEREST_RATE_KEY, rates.interestRate) - .addBrandedRatio(LOAN_FEE_KEY, rates.loanFee) - .addBrandedRatio(INITIAL_EXCHANGE_RATE_KEY, rates.initialExchangeRate) - .addBrandedRatio(BASE_RATE_KEY, rates.baseRate) - .addBrandedRatio(MULTIPILIER_RATE_KEY, rates.multipilierRate) - .build(); + return makeParamManagerSync({ + [LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, rates.liquidationMargin], + [INTEREST_RATE_KEY]: [ParamTypes.RATIO, rates.interestRate], + [LOAN_FEE_KEY]: [ParamTypes.RATIO, rates.loanFee], + [INITIAL_EXCHANGE_RATE_KEY]: [ParamTypes.RATIO, rates.initialExchangeRate], + [BASE_RATE_KEY]: [ParamTypes.RATIO, rates.baseRate], + [MULTIPILIER_RATE_KEY]: [ParamTypes.RATIO, rates.multipilierRate], + }) }; /** @@ -102,10 +92,10 @@ const makePoolParamManager = rates => { * }>} */ const makeElectorateParamManager = async (zoe, electorateInvitation) => { - // @ts-expect-error casting to ElectorateParamManager - return makeParamManagerBuilder(zoe) - .addInvitation(CONTRACT_ELECTORATE, electorateInvitation) - .then(builder => builder.build()); + return makeParamManager({ + [CONTRACT_ELECTORATE]: [ParamTypes.INVITATION, electorateInvitation], + }, + zoe); }; /** @@ -116,7 +106,6 @@ const makeElectorateParamManager = async (zoe, electorateInvitation) => { * @param {Amount} invitationAmount * @param {Rates} rates * @param {XYKAMMPublicFacet} ammPublicFacet - * @param {[]} bootstrappedAssets * @param {bigint=} bootstrapPaymentValue * @param {Brand} compareCurrencyBrand */ @@ -128,7 +117,6 @@ const makeGovernedTerms = ( invitationAmount, rates, ammPublicFacet, - bootstrappedAssets, bootstrapPaymentValue = 0n, compareCurrencyBrand ) => { @@ -143,9 +131,8 @@ const makeGovernedTerms = ( loanTimingParams: timingParamMgr.getParams(), timerService, liquidationInstall, - main: makeElectorateParams(invitationAmount), + governedParams: makeElectorateParams(invitationAmount), bootstrapPaymentValue, - bootstrappedAssets, compareCurrencyBrand }); }; diff --git a/contract/src/lendingPool/poolManager.js b/contract/src/lendingPool/poolManager.js index 391b964..105e7b1 100644 --- a/contract/src/lendingPool/poolManager.js +++ b/contract/src/lendingPool/poolManager.js @@ -17,10 +17,8 @@ import { makeNotifierKit, observeNotifier } from '@agoric/notifier'; import { AmountMath } from '@agoric/ertp'; import { Far } from '@endo/marshal'; -// import { makeScalarBigMapStore } from '@agoric/swingset-vat/src/storeModule'; import { makeScalarMap } from '@agoric/store'; import { makeInnerVault } from './vault.js'; -// import { makePrioritizedVaults } from './prioritizedVaults.js'; import { liquidate } from './liquidation.js'; import { makeTracer } from '../makeTracer.js'; import { @@ -137,16 +135,6 @@ export const makePoolManager = ( let vaultCounter = 0; - /** - * A store for vaultKits prioritized by their collaterization ratio. - * - * It should be set only once but it's a `let` because it can't be set until after the - * definition of reschedulePriceCheck, which refers to sortedVaultKits - * - * @type {ReturnType=} - */ - // XXX misleading mutability and confusing flow control; could be refactored with a listener - let prioritizedVaults; /** * @type {MapStore} */ diff --git a/contract/src/lendingPool/prioritizedVaults.js b/contract/src/lendingPool/prioritizedVaults.js deleted file mode 100644 index e27fe9c..0000000 --- a/contract/src/lendingPool/prioritizedVaults.js +++ /dev/null @@ -1,180 +0,0 @@ -// @ts-check - -import { makeRatioFromAmounts } from '@agoric/zoe/src/contractSupport/index.js'; -import { AmountMath } from '@agoric/ertp'; -import { ratioGTE } from '@agoric/zoe/src/contractSupport/ratio.js'; -import { makeOrderedVaultStore } from './orderedVaultStore.js'; -import { toVaultKey } from './storeUtils.js'; - -/** @typedef {import('./vault').InnerVault} InnerVault */ - -/** - * - * @param {Amount} debtAmount - * @param {Amount} collateralAmount - * @returns {Ratio} - */ -const calculateDebtToCollateral = (debtAmount, collateralAmount) => { - if (AmountMath.isEmpty(collateralAmount)) { - return makeRatioFromAmounts( - debtAmount, - AmountMath.make(collateralAmount.brand, 1n), - ); - } - return makeRatioFromAmounts(debtAmount, collateralAmount); -}; - -/** - * - * @param {InnerVault} vault - * @returns {Ratio} - */ -export const currentDebtToCollateral = vault => - calculateDebtToCollateral( - vault.getCurrentDebtValueInCompareCurrencyForm(), - vault.getCurrentCollateralValueInCompareCurrencyForm(), - ); - -/** @typedef {{debtToCollateral: Ratio, vault: InnerVault}} VaultRecord */ - -/** - * InnerVaults, ordered by their liquidation ratio so that all the - * vaults below a threshold can be quickly found and liquidated. - * - * @param {() => void} reschedulePriceCheck called when there is a new - * least-collateralized vault - */ -export const makePrioritizedVaults = reschedulePriceCheck => { - const vaults = makeOrderedVaultStore(); - - // To deal with fluctuating prices and varying collateralization, we schedule a - // new request to the priceAuthority when some vault's debtToCollateral ratio - // surpasses the current high-water mark. When the request that is at the - // current high-water mark fires, we reschedule at the new (presumably - // lower) rate. - // Without this we'd be calling reschedulePriceCheck() unnecessarily - /** @type {Ratio=} */ - let oracleQueryThreshold; - - // Check if this ratio of debt to collateral would be the highest known. If - // so, reset our highest and invoke the callback. This can be called on new - // vaults and when we get a state update for a vault changing balances. - /** @param {Ratio} collateralToDebt */ - - // Caches and reschedules - const rescheduleIfHighest = collateralToDebt => { - if ( - !oracleQueryThreshold || - !ratioGTE(oracleQueryThreshold, collateralToDebt) - ) { - oracleQueryThreshold = collateralToDebt; - reschedulePriceCheck(); - } - }; - - /** - * - * @returns {Ratio=} actual debt over collateral - */ - const firstDebtRatio = () => { - if (vaults.getSize() === 0) { - return undefined; - } - - const [vault] = vaults.values(); - const collateralAmount = vault.getCollateralAmount(); - if (AmountMath.isEmpty(collateralAmount)) { - // Would be an infinite ratio - return undefined; - } - const actualDebtAmount = vault.getCurrentDebt(); - return makeRatioFromAmounts(actualDebtAmount, vault.getCollateralAmount()); - }; - - /** - * @param {string} key - * @returns {InnerVault} - */ - const removeVault = key => { - const vault = vaults.removeByKey(key); - const debtToCollateral = currentDebtToCollateral(vault); - if ( - !oracleQueryThreshold || - ratioGTE(debtToCollateral, oracleQueryThreshold) - ) { - // don't call reschedulePriceCheck, but do reset the highest. - // This could be expensive if we delete individual entries in order. Will know once we have perf data. - oracleQueryThreshold = firstDebtRatio(); - } - return vault; - }; - - /** - * - * @param {Amount} oldDebt - * @param {Amount} oldCollateral - * @param {string} vaultId - */ - const removeVaultByAttributes = (oldDebt, oldCollateral, vaultId) => { - const key = toVaultKey(oldDebt, oldCollateral, vaultId); - return removeVault(key); - }; - - /** - * - * @param {VaultId} vaultId - * @param {InnerVault} vault - */ - const addVault = (vaultId, vault) => { - const key = vaults.addVault(vaultId, vault); - - const debtToCollateral = currentDebtToCollateral(vault); - rescheduleIfHighest(debtToCollateral); - return key; - }; - - /** - * Invoke a function for vaults with debt to collateral at or above the ratio. - * - * Results are returned in order of priority, with highest debt to collateral first. - * - * Redundant tags until https://github.com/Microsoft/TypeScript/issues/23857 - * - * @param {Ratio} ratio - * @yields {[string, InnerVault]>} - * @returns {IterableIterator<[string, InnerVault]>} - */ - // eslint-disable-next-line func-names - function* entriesPrioritizedGTE(ratio) { - // TODO use a Pattern to limit the query https://github.com/Agoric/agoric-sdk/issues/4550 - for (const [key, vault] of vaults.entries()) { - const debtToCollateral = currentDebtToCollateral(vault); - if (ratioGTE(debtToCollateral, ratio)) { - yield [key, vault]; - } else { - // stop once we are below the target ratio - break; - } - } - } - - /** - * @param {Amount} oldDebt - * @param {Amount} oldCollateral - * @param {string} vaultId - */ - const refreshVaultPriority = (oldDebt, oldCollateral, vaultId) => { - const vault = removeVaultByAttributes(oldDebt, oldCollateral, vaultId); - addVault(vaultId, vault); - }; - - return harden({ - addVault, - entries: vaults.entries, - entriesPrioritizedGTE, - highestRatio: () => oracleQueryThreshold, - refreshVaultPriority, - removeVault, - removeVaultByAttributes, - }); -}; diff --git a/contract/src/lendingPool/types.js b/contract/src/lendingPool/types.js index 213482e..c482138 100644 --- a/contract/src/lendingPool/types.js +++ b/contract/src/lendingPool/types.js @@ -147,6 +147,7 @@ * @typedef {Object} LoanTiming * @property {bigint} chargingPeriod * @property {bigint} recordingPeriod + * @property {bigint} priceCheckPeriod */ /** diff --git a/contract/test/lendingPool/helpers.js b/contract/test/lendingPool/helpers.js index e4e435c..635b412 100644 --- a/contract/test/lendingPool/helpers.js +++ b/contract/test/lendingPool/helpers.js @@ -5,9 +5,7 @@ import { makeRatio, } from '@agoric/zoe/src/contractSupport/index.js'; import { resolve as importMetaResolve } from 'import-meta-resolve'; -import bundleSource from '@endo/bundle-source'; import { makeTracer } from '../../src/makeTracer.js'; -import { makeGovernedTerms } from '../../src/lendingPool/params.js'; import * as Collect from '@agoric/run-protocol/src/collect.js'; const trace = makeTracer('Helper'); @@ -44,11 +42,11 @@ export const depositMoney = async (zoe, pm, underlyingMint, amountInUnit) => { const seat = await E(zoe).offer( invitation, proposal, - paymentKeywordRecord + paymentKeywordRecord, ); const { - Protocol: protocolReceived + Protocol: protocolReceived, } = await E(seat).getPayouts(); const protocolAmount = await E(protocolIssuer).getAmountOf(protocolReceived); @@ -70,7 +68,7 @@ export const addPool = async (zoe, rates, lendingPool, underlyingIssuer, underly const pm = await E(lendingPool).addPoolType(underlyingIssuer, underlyingKeyword, rates, underlyingPriceAuthority); return pm; -} +}; export const makeRates = (underlyingBrand, compareBrand) => { return harden({ @@ -85,7 +83,7 @@ export const makeRates = (underlyingBrand, compareBrand) => { // multipilier rate for utilizitaion rate multipilierRate: makeRatio(20n, underlyingBrand), }); -} +}; export const setupAssets = () => { // setup collateral assets @@ -102,9 +100,9 @@ export const setupAssets = () => { panKit, usdKit, agVanKit, - agPanKit + agPanKit, }); -} +}; export const makeBundle = async (bundleSource, sourceRoot) => { const url = await importMetaResolve(sourceRoot, import.meta.url); @@ -112,133 +110,13 @@ export const makeBundle = async (bundleSource, sourceRoot) => { const contractBundle = await bundleSource(path); trace('makeBundle', sourceRoot); return contractBundle; -} - -export const startLendingPool = async ( - { - consume: { - vaultBundles, - chainTimerService, - priceManager, - zoe, - feeMintAccess: feeMintAccessP, // ISSUE: why doeszn't Zoe await this? - economicCommitteeCreatorFacet: electorateCreatorFacet, - bootstrappedAssets, - compareCurrencyBrand - }, - produce, // { vaultFactoryCreator } - brand: { - consume: { RUN: centralBrandP }, - }, - instance, - installation, - }, - { loanParams } = { - loanParams: { - chargingPeriod: SECONDS_PER_HOUR, - recordingPeriod: SECONDS_PER_DAY, - }, - }, -) => { - const bundles = await vaultBundles; - const installations = await Collect.allValues({ - LendingPool: E(zoe).install(bundles.LendingPool), - liquidate: E(zoe).install(bundles.liquidate), - }); - - const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); - const [initialPoserInvitation, invitationAmount] = await Promise.all([ - poserInvitationP, - E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), - ]); - - const centralBrand = await centralBrandP; - - // declare governed params for the vaultFactory; addVaultType() sets actual rates - const rates = { - liquidationMargin: makeRatio(105n, centralBrand), - interestRate: makeRatio(250n, centralBrand, BASIS_POINTS), - loanFee: makeRatio(200n, centralBrand, BASIS_POINTS), - }; - - const [ammInstance, electorateInstance, contractGovernorInstall] = - await Promise.all([ - instance.consume.amm, - instance.consume.economicCommittee, - installation.consume.contractGovernor, - ]); - const ammPublicFacet = await E(zoe).getPublicFacet(ammInstance); - - const lendingPoolTerms = makeGovernedTerms( - await priceManager, - loanParams, - installations.liquidate, - chainTimerService, - invitationAmount, - rates, - ammPublicFacet, - await bootstrappedAssets, - undefined, - await compareCurrencyBrand - ); - /** - * This is for if we want to govern the lendingPool contract via a DAO. - * Commented out right now might need this later - */ - // const governorTerms = harden({ - // timer: chainTimerService, - // electorateInstance, - // governedContractInstallation: installations.VaultFactory, - // governed: { - // terms: vaultFactoryTerms, - // issuerKeywordRecord: {}, - // privateArgs: harden({ feeMintAccess, initialPoserInvitation }), - // }, - // }); - - const { - creatorFacet: lendingPoolCreatorFacet, - publicFacet: lendingPoolPublicFacet, - instance: lendingPoolInstance, - } = await E(zoe).startInstance( - installations.LendingPool, - undefined, - lendingPoolTerms, - harden({ initialPoserInvitation }), - ); - - // const [vaultFactoryInstance, vaultFactoryCreator] = await Promise.all([ - // E(governorCreatorFacet).getInstance(), - // E(governorCreatorFacet).getCreatorFacet(), - // ]); - // const voteCreator = Far('vaultFactory vote creator', { - // voteOnParamChange: E(governorCreatorFacet).voteOnParamChange, - // }); - // produce.vaultFactoryCreator.resolve(vaultFactoryCreator); - // produce.vaultFactoryGovernorCreator.resolve(governorCreatorFacet); - // produce.vaultFactoryVoteCreator.resolve(voteCreator); - // // Advertise the installations, instances in agoricNames. - // instance.produce.VaultFactory.resolve(vaultFactoryInstance); - // instance.produce.Treasury.resolve(vaultFactoryInstance); - // instance.produce.VaultFactoryGovernor.resolve(governorInstance); - // entries(installations).forEach(([name, install]) => - // installation.produce[name].resolve(install), - // ); - console.log('PUBLIC_FACET', lendingPoolPublicFacet); - return harden({ - lendingPoolCreatorFacet, - lendingPoolPublicFacet, - lendingPoolInstance, - installations - }) - }; -export const startFaucets = async (zoe, faucetBundles) => { +export const startFaucets = async (zoe, installation) => { const installations = await Collect.allValues({ - priceAuthorityFaucet: E(zoe).install(faucetBundles.priceAuthorityFaucet), - lendingPoolFaucet: E(zoe).install(faucetBundles.lendingPoolFaucet), + priceAuthorityFaucet: installation.priceAuthorityFaucet, + lendingPoolFaucet: installation.lendingPoolFaucet, }); // start priceAuthorityFaucet @@ -247,7 +125,7 @@ export const startFaucets = async (zoe, faucetBundles) => { publicFacet: priceAuthorityFaucetPublicFacet, instance: priceAuthorityFaucetInstance, } = await E(zoe).startInstance( - installations.priceAuthorityFaucet + installations.priceAuthorityFaucet, ); // start vanFaucet @@ -302,45 +180,42 @@ export const startFaucets = async (zoe, faucetBundles) => { vanAsset: { creatorFacet: vanFaucetCreatorFacet, publicFacet: vanFaucetPublicFacet, - instance: vanFaucetInstance + instance: vanFaucetInstance, }, panAsset: { creatorFacet: panFaucetCreatorFacet, publicFacet: panFaucetPublicFacet, - instance: panFaucetInstance + instance: panFaucetInstance, }, usdAsset: { creatorFacet: usdFaucetCreatorFacet, publicFacet: usdFaucetPublicFacet, - instance: usdFaucetInstance + instance: usdFaucetInstance, }, priceAuthorityFaucet: { creatorFacet: priceAuthorityFaucetCreatorFacet, publicFacet: priceAuthorityFaucetPublicFacet, - instance: priceAuthorityFaucetInstance + instance: priceAuthorityFaucetInstance, }, - installations - } -} + installations, + }; +}; -export const startPriceManager = async (zoe, priceManBundle) => { - const installations = await Collect.allValues({ - priceManagerContract: E(zoe).install(priceManBundle.priceManagerContract), - }); +export const startPriceManager = async (zoe, priceManInstallation) => { const { creatorFacet: priceAuthorityManagerCreatorFacet, publicFacet: priceAuthorityManagerPublicFacet, instance: priceAuthorityManagerInstance, } = await E(zoe).startInstance( - installations.priceManagerContract + priceManInstallation, ); return { priceAuthorityManagerPublicFacet, - priceAuthorityManagerInstance - } -} + priceAuthorityManagerInstance, + }; +}; export const getLiquidityFromFaucet = async (zoe, invitation, unit, brand, keyword) => { const displayInfo = await E(brand).getDisplayInfo(); @@ -348,15 +223,14 @@ export const getLiquidityFromFaucet = async (zoe, invitation, unit, brand, keywo proposalAmountKeywordRecord[keyword] = AmountMath.make(brand, unit * 10n ** BigInt(displayInfo.decimalPlaces)); const liquidityProposal = { give: {}, - want: proposalAmountKeywordRecord - } + want: proposalAmountKeywordRecord, + }; const faucetSeat = E(zoe).offer( invitation, harden(liquidityProposal), - harden({}) + harden({}), ); - return await E(faucetSeat).getPayout(keyword); - -} \ No newline at end of file + return await E(faucetSeat).getPayout(keyword); +}; \ No newline at end of file diff --git a/contract/test/lendingPool/setup.js b/contract/test/lendingPool/setup.js index be983b1..5a65a3b 100644 --- a/contract/test/lendingPool/setup.js +++ b/contract/test/lendingPool/setup.js @@ -1,7 +1,7 @@ // @ts-check import bundleSource from '@endo/bundle-source'; -import { E } from '@agoric/eventual-send'; +import { E, Far } from '@endo/far'; import { makeLoopback } from '@endo/captp'; import { resolve as importMetaResolve } from 'import-meta-resolve'; @@ -9,45 +9,42 @@ import { makeFakeVatAdmin } from '@agoric/zoe/tools/fakeVatAdmin.js'; import { makeZoeKit } from '@agoric/zoe'; import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; -import { - makeAgoricNamesAccess, - makePromiseSpace, -} from '@agoric/vats/src/core/utils.js'; +import { makeAgoricNamesAccess, makePromiseSpace } from '@agoric/vats/src/core/utils.js'; import * as Collect from '@agoric/run-protocol/src/collect.js'; -import { - setupAmm, - startEconomicCommittee, -} from '@agoric/run-protocol/src/econ-behaviors.js'; +import committeeBundle from '@agoric/governance/bundles/bundle-committee.js'; +import contractGovernorBundle from '@agoric/governance/bundles/bundle-contractGovernor.js'; +import binaryVoteCounterBundle from '@agoric/governance/bundles/bundle-binaryVoteCounter.js'; +import * as utils from '@agoric/vats/src/core/utils.js'; +import { makeAmmTerms } from '@agoric/run-protocol/src/vpool-xyk-amm/params.js'; +import { AmountMath } from '@agoric/ertp'; +import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js'; +import { makeGovernedTerms } from '../../src/lendingPool/params.js'; +import { liquidationDetailTerms } from '@agoric/run-protocol/src/vaultFactory/liquidation.js'; -const ammRoot = '@agoric/run-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js'; +const { details: X } = assert; -const contractGovernorRoot = '@agoric/governance/src/contractGovernor.js'; -const committeeRoot = '@agoric/governance/src/committee.js'; -const voteCounterRoot = '@agoric/governance/src/binaryVoteCounter.js'; +const COMPARE_CURRENCY_ISSUER_NAME = "USD"; -const makeBundle = async sourceRoot => { +const SECONDS_PER_HOUR = 60n * 60n; +const SECONDS_PER_DAY = 24n * SECONDS_PER_HOUR; + +const BASIS_POINTS = 10_000n; + +export const getPath = async (sourceRoot) => { const url = await importMetaResolve(sourceRoot, import.meta.url); - const path = new URL(url).pathname; - const contractBundle = await bundleSource(path); - console.log(`makeBundle ${sourceRoot}`); - return contractBundle; + return new URL(url).pathname; }; -// makeBundle is a slow step, so we do it once for all the tests. -const ammBundleP = makeBundle(ammRoot); -const contractGovernorBundleP = makeBundle(contractGovernorRoot); -const committeeBundleP = makeBundle(committeeRoot); -const voteCounterBundleP = makeBundle(voteCounterRoot); - -export const setUpZoeForTest = async () => { +export const setUpZoeForTest = (setJig = () => { +}) => { const { makeFar } = makeLoopback('zoeTest'); const { zoeService, feeMintAccess: nonFarFeeMintAccess } = makeZoeKit( - makeFakeVatAdmin(() => {}).admin, + makeFakeVatAdmin(setJig).admin, ); /** @type {ERef} */ const zoe = makeFar(zoeService); - const feeMintAccess = await makeFar(nonFarFeeMintAccess); + const feeMintAccess = makeFar(nonFarFeeMintAccess); return { zoe, feeMintAccess, @@ -55,108 +52,311 @@ export const setUpZoeForTest = async () => { }; harden(setUpZoeForTest); -export const setupAMMBootstrap = async ( - timer = buildManualTimer(console.log), - zoe, -) => { - if (!zoe) { - ({ zoe } = await setUpZoeForTest()); +export const installGovernance = (zoe, produce, bundles = undefined) => { + if (bundles !== undefined) { + produce.committee.resolve(E(zoe).install(bundles.committee)); + produce.contractGovernor.resolve(E(zoe).install(bundles.contractGovernor)); + produce.binaryVoteCounter.resolve(E(zoe).install(bundles.binaryVoteCounter)); } + produce.committee.resolve(E(zoe).install(committeeBundle)); + produce.contractGovernor.resolve(E(zoe).install(contractGovernorBundle)); + produce.binaryVoteCounter.resolve(E(zoe).install(binaryVoteCounterBundle)); +}; - const space = /** @type {any} */ (makePromiseSpace()); - const { produce, consume } = /** @type { EconomyBootstrapPowers } */ (space); +export const setupBootstrap = (t, optTimer = undefined) => { + const space = /** @type {any} */ (makePromiseSpace(t.log)); + const { produce, consume } = + /** @type { import('../src/proposals/econ-behaviors.js').EconomyBootstrapPowers & BootstrapPowers } */ ( + space + ); + const timer = optTimer || buildManualTimer(t.log); produce.chainTimerService.resolve(timer); + + const { + zoe, + compareCurrencyKit: { brand: usdBrand, issuer: usdIssuer }, + } = t.context; produce.zoe.resolve(zoe); - const { agoricNames, spaces } = makeAgoricNamesAccess(); + const { agoricNames, agoricNamesAdmin, spaces } = makeAgoricNamesAccess(); produce.agoricNames.resolve(agoricNames); + produce.agoricNamesAdmin.resolve(agoricNamesAdmin); + + const { brand, issuer } = spaces; + brand.produce.USD.resolve(usdBrand); + issuer.produce.USD.resolve(usdIssuer); - /** @type {Record>} */ - const governanceBundlePs = { - contractGovernor: contractGovernorBundleP, - committee: committeeBundleP, - binaryVoteCounter: voteCounterBundleP, + return { produce, consume, modules: { utils: { ...utils } }, ...spaces }; +}; + +export const startEconomicCommittee = async ( + { + consume: { zoe }, + produce: { economicCommitteeCreatorFacet }, + installation: { + consume: { committee }, + }, + instance: { + produce: { economicCommittee }, + }, + }, + { options: { econCommitteeOptions = {} } = {} }, +) => { + const { + committeeName = 'Initial Economic Committee', + committeeSize = 3, + ...rest + } = econCommitteeOptions; + const { creatorFacet, instance } = await E(zoe).startInstance( + committee, + {}, + { committeeName, committeeSize, ...rest }, + ); + + economicCommitteeCreatorFacet.resolve(creatorFacet); + economicCommittee.resolve(instance); +}; +harden(startEconomicCommittee); + +/** @param { EconomyBootstrapPowers } powers */ +export const setupAmm = async ( + { + consume: { + chainTimerService, + zoe, + economicCommitteeCreatorFacet: committeeCreator, + }, + produce: { ammCreatorFacet, ammGovernorCreatorFacet }, + issuer: { + consume: { [COMPARE_CURRENCY_ISSUER_NAME]: centralIssuer }, + }, + instance: { + consume: { economicCommittee: electorateInstance }, + produce: { amm: ammInstanceProducer, ammGovernor }, + }, + installation: { + consume: { contractGovernor: governorInstallation, amm: ammInstallation }, + }, + }) => { + const poserInvitationP = E(committeeCreator).getPoserInvitation(); + const [poserInvitation, poserInvitationAmount] = await Promise.all([ + poserInvitationP, + E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), + ]); + + const timer = await chainTimerService; // avoid promise for legibility + + const ammTerms = makeAmmTerms(timer, poserInvitationAmount); + + const ammGovernorTerms = { + timer, + electorateInstance, + governedContractInstallation: ammInstallation, + governed: { + terms: ammTerms, + issuerKeywordRecord: { Central: centralIssuer }, + privateArgs: { initialPoserInvitation: poserInvitation }, + }, }; - const bundles = await Collect.allValues(governanceBundlePs); - produce.governanceBundles.resolve(bundles); + const g = await E(zoe).startInstance( + governorInstallation, + {}, + ammGovernorTerms, + { electorateCreatorFacet: committeeCreator }, + ); + + const [creatorFacet, ammPublicFacet, instance] = await Promise.all([ + E(g.creatorFacet).getCreatorFacet(), + E(g.creatorFacet).getPublicFacet(), + E(g.publicFacet).getGovernedContract(), + ]); + ammGovernorCreatorFacet.resolve(g.creatorFacet); + ammCreatorFacet.resolve(creatorFacet); - return { produce, consume, ...spaces }; + // Confirm that the amm was indeed setup + assert(ammPublicFacet, X`ammPublicFacet broken ${ammPublicFacet}`); + + ammInstanceProducer.resolve(instance); + ammGovernor.resolve(g.instance); + return ammInstallation; }; -/** - * NOTE: called separately by each test so AMM/zoe/priceAuthority don't interfere - * - * @param {{ committeeName: string, committeeSize: number}} electorateTerms - * @param {{ brand: Brand, issuer: Issuer }} centralR - * @param {ManualTimer | undefined=} timer - * @param {ERef | undefined=} zoe - */ -export const setupAmmServices = async ( - electorateTerms, - centralR, - timer = buildManualTimer(console.log), - zoe, +harden(setupAmm); + +export const startLendingPool = async ( + { + consume: { + chainTimerService, + priceManager: priceManagerP, + zoe, + economicCommitteeCreatorFacet: electorateCreatorFacet, + }, + produce, // { vaultFactoryCreator } + brand: { + consume: { [COMPARE_CURRENCY_ISSUER_NAME]: compareBrandP }, + }, + instance, + installation: { + consume: { LendingPool, liquidate, contractGovernor }, + }, + }, + { + loanParams = { + chargingPeriod: SECONDS_PER_HOUR, + recordingPeriod: SECONDS_PER_DAY, + priceCheckPeriod: SECONDS_PER_DAY, + } = {}, + }, ) => { - if (!zoe) { - ({ zoe } = await setUpZoeForTest()); - } - // XS doesn't like top-level await, so do it here. this should be quick - const ammBundle = await ammBundleP; - const space = await setupAMMBootstrap(timer, zoe); - const { produce, consume, brand, issuer, installation, instance } = space; - - produce.ammBundle.resolve(ammBundle); - brand.produce.RUN.resolve(centralR.brand); - issuer.produce.RUN.resolve(centralR.issuer); - - await Promise.all([ - startEconomicCommittee(space, electorateTerms), - setupAmm(space), + + const installations = await Collect.allValues({ + LendingPool, + liquidate, + }); + + const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); + const [initialPoserInvitation, invitationAmount] = await Promise.all([ + poserInvitationP, + E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), ]); - const installs = await Collect.allValues({ - amm: installation.consume.amm, - governor: installation.consume.contractGovernor, - electorate: installation.consume.committee, - counter: installation.consume.binaryVoteCounter, + console.log("compareBrandP", compareBrandP) + const compareBrand = await compareBrandP; + + /** + * Types for the governed params for the vaultFactory; addVaultType() sets actual values + * + * @type {VaultManagerParamValues} + */ + const poolManagerParams = { + // XXX the values aren't used. May be addressed by https://github.com/Agoric/agoric-sdk/issues/4861 + liquidationMargin: makeRatio(0n, compareBrand), + interestRate: makeRatio(0n, compareBrand, BASIS_POINTS), + loanFee: makeRatio(0n, compareBrand, BASIS_POINTS), + initialExchangeRate: makeRatio(0n, compareBrand, BASIS_POINTS), + baseRate: makeRatio(0n, compareBrand, BASIS_POINTS), + multiplierRate: makeRatio(0n, compareBrand, BASIS_POINTS), + }; + + const [ + ammInstance, + electorateInstance, + contractGovernorInstall, + ] = await Promise.all([ + instance.consume.amm, + instance.consume.economicCommittee, + contractGovernor, + ]); + const ammPublicFacet = await E(zoe).getPublicFacet(ammInstance); + const priceManager = await priceManagerP; + const timer = await chainTimerService; + + const vaultFactoryTerms = makeGovernedTerms( + priceManager, // priceMan here + loanParams, + installations.liquidate, + timer, + invitationAmount, + poolManagerParams, + ammPublicFacet, + // liquidationTerms: liquidationDetailTerms(centralBrand), + undefined, + compareBrand + ); + // console.log("vaultFactoryTerms", vaultFactoryTerms) + const governorTerms = harden({ + timer, + electorateInstance, + governedContractInstallation: installations.LendingPool, + governed: { + terms: vaultFactoryTerms, + issuerKeywordRecord: {}, + privateArgs: harden({ initialPoserInvitation }), + }, + }); + const { creatorFacet: governorCreatorFacet, instance: governorInstance } = + await E(zoe).startInstance( + contractGovernorInstall, + undefined, + governorTerms, + harden({ electorateCreatorFacet }), + ); + + const [lendingPoolInstance, lendingPoolCreator] = await Promise.all([ + E(governorCreatorFacet).getInstance(), + E(governorCreatorFacet).getCreatorFacet(), + ]); + + const voteCreator = Far('lendingPool vote creator', { + voteOnParamChanges: E(governorCreatorFacet).voteOnParamChanges, }); + produce.lendingPoolCreator.resolve(lendingPoolCreator); + produce.lendingPoolGovernorCreator.resolve(governorCreatorFacet); + produce.lendingPoolVoteCreator.resolve(voteCreator); + + // Advertise the installations, instances in agoricNames. + instance.produce.lendingPool.resolve(lendingPoolInstance); + instance.produce.lendingPoolGovernor.resolve(governorInstance); +}; + +harden(startLendingPool) + +export const setupAmmAndElectorate = async (t, vanLiquidity, compLiquidity) => { + const { + zoe, + vanKit: { issuer: vanIssuer }, + electorateTerms = { committeeName: 'The Cabal', committeeSize: 1 }, + timer, + } = t.context; + + const space = setupBootstrap(t, timer); + const { consume, instance } = space; + installGovernance(zoe, space.installation.produce); + space.installation.produce.amm.resolve(t.context.installation.amm); + await startEconomicCommittee(space, electorateTerms); + await setupAmm(space); + const governorCreatorFacet = consume.ammGovernorCreatorFacet; const governorInstance = await instance.consume.ammGovernor; const governorPublicFacet = await E(zoe).getPublicFacet(governorInstance); - const g = { - governorInstance, - governorPublicFacet, - governorCreatorFacet, - }; const governedInstance = E(governorPublicFacet).getGovernedContract(); - /** @type { XYKAMMPublicFacet & GovernedPublicFacet } */ + /** @type { GovernedPublicFacet } */ + // @ts-expect-error cast from unknown const ammPublicFacet = await E(governorCreatorFacet).getPublicFacet(); - const amm = { + + const liquidityIssuer = E(ammPublicFacet).addPool(vanIssuer, 'VAN'); + const liquidityBrand = await E(liquidityIssuer).getBrand(); + + const liqProposal = harden({ + give: { + Secondary: vanLiquidity.proposal, + Central: compLiquidity.proposal, + }, + want: { Liquidity: AmountMath.makeEmpty(liquidityBrand) }, + }); + const liqInvitation = await E(ammPublicFacet).makeAddLiquidityInvitation(); + + const ammLiquiditySeat = await E(zoe).offer( + liqInvitation, + liqProposal, + harden({ + Secondary: vanLiquidity.payment, + Central: compLiquidity.payment, + }), + ); + + // TODO get the creator directly + const newAmm = { ammCreatorFacet: await consume.ammCreatorFacet, ammPublicFacet, instance: governedInstance, + ammLiquidity: E(ammLiquiditySeat).getPayout('Liquidity'), }; - const committeeCreator = await consume.economicCommitteeCreatorFacet; - const electorateInstance = await instance.consume.economicCommittee; - - const poserInvitationP = E(committeeCreator).getPoserInvitation(); - const poserInvitationAmount = await E( - E(zoe).getInvitationIssuer(), - ).getAmountOf(poserInvitationP); - return { - zoe, - installs, - electorate: installs.electorate, - committeeCreator, - electorateInstance, - governor: g, - amm, - invitationAmount: poserInvitationAmount, - space, - }; + return { amm: newAmm, space }; }; -harden(setupAmmServices); + +harden(setupAmmAndElectorate); diff --git a/contract/test/lendingPool/test-lendingPool.js b/contract/test/lendingPool/test-lendingPool.js index fe9282b..339d228 100644 --- a/contract/test/lendingPool/test-lendingPool.js +++ b/contract/test/lendingPool/test-lendingPool.js @@ -2,13 +2,11 @@ import { makeTracer } from '../../src/makeTracer.js'; const trace = makeTracer('TestST'); -import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; +import { test as unknownTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; // swingset-vat to zoe import '@agoric/zoe/exported.js'; - -import { resolve as importMetaResolve } from 'import-meta-resolve'; +import { deeplyFulfilled } from '@endo/marshal'; import { E } from '@agoric/eventual-send'; -import bundleSource from '@endo/bundle-source'; import { makeIssuerKit, AssetKind, AmountMath } from '@agoric/ertp'; import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; import { @@ -21,46 +19,38 @@ import { getAmountOut } from '@agoric/zoe/src/contractSupport/index.js'; import { makePromiseKit } from '@endo/promise-kit'; -import { observeNotifier } from '@agoric/notifier'; import { makeScriptedPriceAuthority } from '@agoric/zoe/tools/scriptedPriceAuthority.js'; -import { makeGovernedTerms } from '../../src/lendingPool/params.js'; -import { assertAmountsEqual } from '@agoric/zoe/test/zoeTestHelpers.js'; -import { makeParamManagerBuilder } from '@agoric/governance'; import { makePriceManager } from '../../src/lendingPool/priceManager.js'; import { natSafeMath } from '@agoric/zoe/src/contractSupport/safeMath.js'; import { Nat } from '@agoric/nat'; import { makeInnerVault } from '../../src/lendingPool/vault.js'; -import { depositMoney, addPool, makeRates, setupAssets, makeBundle,startLendingPool } from './helpers.js'; +import { depositMoney, addPool, makeRates, setupAssets, makeBundle } from './helpers.js'; import { setUpZoeForTest, - setupAmmServices, + getPath, + startLendingPool, + setupAmmAndElectorate } from './setup.js'; - - import { SECONDS_PER_YEAR } from '../../src/interest.js'; -import { - CHARGING_PERIOD_KEY, - RECORDING_PERIOD_KEY, -} from '../../src/lendingPool/params.js'; import '../../src/lendingPool/types.js'; import * as Collect from '@agoric/run-protocol/src/collect.js'; -import { calculateCurrentDebt } from '../../src/interest-math.js'; -// import { har } from '../../../.yarn/releases/yarn-1.22.4'; +import { unsafeMakeBundleCache } from '@agoric/run-protocol/test/bundleTool.js'; +import { makeManualPriceAuthority } from '@agoric/zoe/tools/manualPriceAuthority.js'; -// #region Support +const test = unknownTest; const contractRoots = { faucet: './faucet.js', liquidate: '../../src/lendingPool/liquidateMinimum.js', LendingPool: '../../src/lendingPool/lendingPool.js', + amm: '@agoric/run-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js', }; /** @typedef {import('../../src/vaultFactory/vaultFactory.js').VaultFactoryPublicFacet} VaultFactoryPublicFacet */ -// const trace = makeTracer('TestST'); - const BASIS_POINTS = 10000n; +const secondsPerDay = SECONDS_PER_YEAR / 365n; // Define locally to test that vaultFactory uses these values export const Phase = /** @type {const} */ ({ @@ -71,13 +61,6 @@ export const Phase = /** @type {const} */ ({ TRANSFER: 'transfer', }); -// makeBundle is a slow step, so we do it once for all the tests. -const bundlePs = { - faucet: makeBundle(bundleSource, contractRoots.faucet), - liquidate: makeBundle(bundleSource, contractRoots.liquidate), - LendingPool: makeBundle(bundleSource, contractRoots.LendingPool), -}; - // Some notifier updates aren't propagating sufficiently quickly for the tests. // This invocation (thanks to Warner) waits for all promises that can fire to // have all their callbacks run @@ -101,368 +84,245 @@ function calculateUnderlyingFromProtocol(protocolAmount, exchangeRate) { ) } -async function setupAmmAndElectorate( - timer, - zoe, - aethLiquidity, - runLiquidity, - runIssuer, - aethIssuer, - electorateTerms, -) { - const runBrand = await E(runIssuer).getBrand(); - const centralR = { issuer: runIssuer, brand: runBrand }; - - const { - amm, - committeeCreator, - governor, - invitationAmount, - electorateInstance, - space, - } = await setupAmmServices(electorateTerms, centralR, timer, zoe); - - const liquidityIssuer = E(amm.ammPublicFacet).addPool(aethIssuer, 'Aeth'); - const liquidityBrand = await E(liquidityIssuer).getBrand(); - - const liqProposal = harden({ - give: { - Secondary: aethLiquidity.proposal, - Central: runLiquidity.proposal, - }, - want: { Liquidity: AmountMath.makeEmpty(liquidityBrand) }, - }); - const liqInvitation = await E( - amm.ammPublicFacet, - ).makeAddLiquidityInvitation(); - - const ammLiquiditySeat = await E(zoe).offer( - liqInvitation, - liqProposal, - harden({ - Secondary: aethLiquidity.payment, - Central: runLiquidity.payment, - }), - ); - - const newAmm = { - ammCreatorFacet: amm.ammCreatorFacet, - ammPublicFacet: amm.ammPublicFacet, - instance: amm.governedInstance, - ammLiquidity: E(ammLiquiditySeat).getPayout('Liquidity'), - }; - - return { - governor, - amm: newAmm, - committeeCreator, - electorateInstance, - invitationAmount, - space, - }; -} - -/** - * @param {ERef} zoe - * @param {ERef} feeMintAccess - * @param {Brand} runBrand - * @param {bigint} runInitialLiquidity - */ -async function getRunFromFaucet( - zoe, - feeMintAccess, - runBrand, - runInitialLiquidity, -) { - const bundle = await bundlePs.faucet; - // On-chain, there will be pre-existing RUN. The faucet replicates that - const { creatorFacet: faucetCreator } = await E(zoe).startInstance( - E(zoe).install(bundle), - {}, - {}, - harden({ feeMintAccess }), - ); - const faucetSeat = E(zoe).offer( - await E(faucetCreator).makeFaucetInvitation(), - harden({ - give: {}, - want: { RUN: AmountMath.make(runBrand, runInitialLiquidity) }, - }), - harden({}), - { feeMintAccess }, - ); - - const runPayment = await E(faucetSeat).getPayout('RUN'); - return runPayment; -} - /** * NOTE: called separately by each test so AMM/zoe/priceAuthority don't interfere - * - * @param {LoanTiming} loanTiming - * @param {unknown} priceList - * @param {Amount} unitAmountIn - * @param {Brand} aethBrand - * @param {unknown} electorateTerms - * @param {TimerService} timer - * @param {unknown} quoteInterval - * @param {unknown} aethLiquidity - * @param {bigint} runInitialLiquidity - * @param {Issuer} aethIssuer - * @param {[]} bootstrappedAssets - * @param {Brand} compareCurrencyBrand */ async function setupServices( - loanTiming, - priceList, + t, + priceOrList, unitAmountIn, - aethBrand, - electorateTerms, - timer = buildManualTimer(console.log), + timer = buildManualTimer(t.log), quoteInterval, - aethLiquidity, - runInitialLiquidity, - aethIssuer, - bootstrappedAssets, - compareCurrencyBrand + compareInitialLiquidity, ) { - const { zoe, feeMintAccess } = await setUpZoeForTest(); - const runIssuer = await E(zoe).getFeeIssuer(); - const runBrand = await E(runIssuer).getBrand(); - const runPayment = await getRunFromFaucet( + const { zoe, - feeMintAccess, - runBrand, - runInitialLiquidity, - ); + compareCurrencyKit: { issuer: compCurrencyIssuer, brand: compCurrencyBrand, mint: compCurrencyMint }, + vanKit: { brand: vanBrand, issuer: vanIssuer, mint: vanMint }, + loanTiming, + minInitialDebt, + rates, + vanInitialLiquidity, + } = t.context; + t.context.timer = timer; - const runLiquidity = { - proposal: harden(AmountMath.make(runBrand, runInitialLiquidity)), - payment: runPayment, + const comparePayment = compCurrencyMint.mintPayment(AmountMath.make(compCurrencyBrand, compareInitialLiquidity)); + + const compLiquidity = { + proposal: harden(AmountMath.make(compCurrencyBrand, compareInitialLiquidity)), + payment: comparePayment, }; + const vanLiquidity = { + proposal: vanInitialLiquidity, + payment: vanMint.mintPayment(vanInitialLiquidity), + }; const { amm: ammFacets, space } = await setupAmmAndElectorate( - timer, - zoe, - aethLiquidity, - runLiquidity, - runIssuer, - aethIssuer, - electorateTerms, + t, + vanLiquidity, + compLiquidity, ); - const { consume, produce } = space; + const { consume, produce, instance } = space; + // trace(t, 'amm', { ammFacets }); const quoteMint = makeIssuerKit('quote', AssetKind.SET).mint; - // const priceAuthority = makeScriptedPriceAuthority({ - // actualBrandIn: aethBrand, - // actualBrandOut: runBrand, - // priceList, - // timer, - // quoteMint, - // unitAmountIn, - // quoteInterval, - // }); - // produce.priceAuthority.resolve(priceAuthority); + // Cheesy hack for easy use of manual price authority + const pa = Array.isArray(priceOrList) + ? makeScriptedPriceAuthority({ + actualBrandIn: vanBrand, + actualBrandOut: compCurrencyBrand, + priceList: priceOrList, + timer, + quoteMint, + unitAmountIn, + quoteInterval, + }) + : makeManualPriceAuthority({ + actualBrandIn: vanBrand, + actualBrandOut: compCurrencyBrand, + initialPrice: priceOrList, + timer, + quoteMint, + }); + produce.priceAuthority.resolve(pa); + + const { + installation: { produce: iProduce }, + } = space; + iProduce.LendingPool.resolve(t.context.installation.LendingPool); + iProduce.liquidate.resolve(t.context.installation.liquidate); const priceManager = makePriceManager({}); produce.priceManager.resolve(priceManager); - // produce.feeMintAccess.resolve(feeMintAccess); - const vaultBundles = await Collect.allValues({ - LendingPool: bundlePs.LendingPool, - liquidate: bundlePs.liquidate, - }); - produce.vaultBundles.resolve(vaultBundles); - produce.bootstrappedAssets.resolve({}); - produce.compareCurrencyBrand.resolve(compareCurrencyBrand); - const { - lendingPoolCreatorFacet, - lendingPoolPublicFacet, - lendingPoolInstance, - } = await startLendingPool(space, { loanParams: loanTiming }); - // const agoricNames = consume.agoricNames; - // const installs = await Collect.allValues({ - // vaultFactory: E(agoricNames).lookup('installation', 'VaultFactory'), - // liquidate: E(agoricNames).lookup('installation', 'liquidate'), - // }); - // - // const governorCreatorFacet = consume.vaultFactoryGovernorCreator; - // /** @type {Promise} */ - // const vaultFactoryCreatorFacet = /** @type { any } */ ( - // E(governorCreatorFacet).getCreatorFacet() - // ); - // /** @type {[any, VaultFactory, VaultFactoryPublicFacet]} */ - // const [governorInstance, vaultFactory, lender] = await Promise.all([ - // E(agoricNames).lookup('instance', 'VaultFactoryGovernor'), - // vaultFactoryCreatorFacet, - // E(governorCreatorFacet).getPublicFacet(), - // ]); - // - // const { g, v } = { - // g: { - // governorInstance, - // governorPublicFacet: E(zoe).getPublicFacet(governorInstance), - // governorCreatorFacet, - // }, - // v: { - // vaultFactory, - // lender, - // }, - // }; + console.log("daaa") + await startLendingPool(space, { loanParams: loanTiming } ); + + const governorCreatorFacet = consume.lendingPoolGovernorCreator; + /** @type {Promise>} */ + const lendingPoolCreatorFacetP = /** @type { any } */ ( + E(governorCreatorFacet).getCreatorFacet() + ); + + /** @type {[any, VaultFactory, VFC['publicFacet'], VaultManager, PriceAuthority]} */ + // @ts-expect-error cast + const [ + governorInstance, + lendingPoolCreatorFacet, + lendingPoolPublicFacet, + ] = await Promise.all([ + instance.consume.lendingPoolGovernor, + lendingPoolCreatorFacetP, + E(governorCreatorFacet).getPublicFacet(), + ]); + // trace(t, { governorInstance, lendingPoolCreatorFacet, lendingPoolPublicFacet }); + + const { g, l } = { + g: { + governorInstance, + governorPublicFacet: E(zoe).getPublicFacet(governorInstance), + governorCreatorFacet, + }, + l: { + lendingPoolCreatorFacet, + lendingPoolPublicFacet, + }, + }; return { zoe, - // installs, - lendingPoolCreatorFacet, - lendingPoolPublicFacet, - lendingPoolInstance, + governor: g, + lendingPool: l, ammFacets, - runKit: { issuer: runIssuer, brand: runBrand }, - quoteMint, - timer, - priceManager + timer }; } -// #endregion test('dummy', t => { t.is('dummy', 'dummy'); }) +test.before(async t => { + const { zoe } = setUpZoeForTest(); -test('initial', async t => { - const { - vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - panKit: {mint: panMint, issuer: panIssuer, brand: panBrand}, - sowKit: {mint: sowMint, issuer: sowIssuer, brand: sowBrand}, - } = setupAssets(); - const loanTiming = { - chargingPeriod: 2n, - recordingPeriod: 10n, - priceCheckPeriod: 50n - }; + const bundleCache = await unsafeMakeBundleCache('./bundles/'); // package-relative + // note that the liquidation might be a different bundle name + const bundles = await Collect.allValues({ + faucet: bundleCache.load(await getPath(contractRoots.faucet), 'faucet'), + liquidate: bundleCache.load(await getPath(contractRoots.liquidate), 'liquidateMinimum'), + LendingPool: bundleCache.load(await getPath(contractRoots.LendingPool), 'lendingPool'), + amm: bundleCache.load(await getPath(contractRoots.amm), 'amm'), + }); + const installation = Collect.mapValues(bundles, bundle => + E(zoe).install(bundle), + ); - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; + const { vanKit, usdKit, panKit, } = setupAssets(); - const bootstrappedAssets = [vanBrand, panBrand, sowBrand]; + const contextPs = { + zoe, + bundles, + installation, + electorateTerms: undefined, + vanKit, + compareCurrencyKit: usdKit, + panKit, + loanTiming: { + chargingPeriod: 2n, + recordingPeriod: 6n, + priceCheckPeriod: 6n , + }, + minInitialDebt: 50n, + vanRates: makeRates(vanKit.brand, usdKit.brand), + panRates: makeRates(panKit.brand, usdKit.brand), + vanInitialLiquidity: AmountMath.make(vanKit.brand, 300n), + panInitialLiquidity: AmountMath.make(panKit.brand, 300n), + }; + const frozenCtx = await deeplyFulfilled(harden(contextPs)); + t.context = { ...frozenCtx, bundleCache }; + // trace(t, 'CONTEXT'); +}); - const { lendingPoolCreatorFacet, lendingPoolPublicFacet } = await setupServices( - loanTiming, +test('initial', async t => { + const { + vanKit: {brand: vanBrand} + } = t.context; + + const services = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, - buildManualTimer(console.log), undefined, - vanLiquidity, + undefined, 500n, - vanIssuer, - bootstrappedAssets ); - - t.is(await E(await E(lendingPoolCreatorFacet).getLimitedCreatorFacet()).helloFromCreator(), 'Hello From the creator'); - t.is(await E(lendingPoolPublicFacet).helloWorld(), 'Hello World'); + console.log("services", services); + t.is("is", "is"); }); test('add-pool', async t => { const { - vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, - } = setupAssets(); - const loanTiming = { - chargingPeriod: 2n, - recordingPeriod: 10n, - priceCheckPeriod: 50n - }; - - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - - const bootstrappedAssets = [vanBrand]; + vanKit: {brand: vanBrand, issuer: vanIssuer}, + compareCurrencyKit: {brand: usdBrand, issuer: usdIssuer}, + vanRates + } = t.context; - const { lendingPoolCreatorFacet, lendingPoolPublicFacet } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, - buildManualTimer(console.log), undefined, - vanLiquidity, + undefined, 500n, - vanIssuer, - bootstrappedAssets ); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); + const vanUsdPriceAuthority = makeScriptedPriceAuthority({ + actualBrandIn: vanBrand, + actualBrandOut: usdBrand, + priceList: [110n, 110n, 101n], + timer, + undefined, + unitAmountIn: AmountMath.make(vanBrand, 100n), + quoteInterval: secondsPerDay * 7n + }); - const rates = makeRates(vanBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(vanIssuer, 'VAN', rates, undefined); + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); t.is(await E(lendingPoolPublicFacet).hasPool(vanBrand), true); - await t.throwsAsync(E(lendingPoolPublicFacet).hasKeyword('AgVAN')); - t.deepEqual(await E(lendingPoolPublicFacet).getPool(vanBrand), pm); + t.deepEqual(await E(lendingPoolPublicFacet).getPool(vanBrand), vanPoolMan); }); test('deposit', async t => { const { - vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, - } = setupAssets(); - const loanTiming = { - chargingPeriod: 2n, - recordingPeriod: 10n, - priceCheckPeriod: 50n - }; - - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - - const bootstrappedAssets = [vanBrand]; + vanKit: {brand: vanBrand, issuer: vanIssuer, mint: vanMint}, + compareCurrencyKit: {brand: usdBrand, issuer: usdIssuer}, + vanRates + } = t.context; - const { lendingPoolCreatorFacet, lendingPoolPublicFacet, zoe, quoteMint, timer } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, - buildManualTimer(console.log), undefined, - vanLiquidity, + undefined, 500n, - vanIssuer, - bootstrappedAssets, - usdBrand ); const vanUsdPriceAuthority = makeScriptedPriceAuthority({ actualBrandIn: vanBrand, actualBrandOut: usdBrand, - priceList: [105n, 15n], + priceList: [110n, 110n, 101n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(vanBrand, 100n), - quoteInterval: 10n + quoteInterval: secondsPerDay * 7n }); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); - - const rates = makeRates(vanBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(vanIssuer, 'VAN', rates, vanUsdPriceAuthority); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); + const protocolBrand = await E(vanPoolMan).getProtocolBrand(); + const protocolIssuer = await E(vanPoolMan).getProtocolIssuer(); console.log('[BRAND]:', protocolBrand); console.log('[ISSUER]:', protocolIssuer); const underlyingAmountIn = AmountMath.make(vanBrand, 111111111n); - const protocolAmountOut = await E(pm).getProtocolAmountOut(underlyingAmountIn); + const protocolAmountOut = await E(vanPoolMan).getProtocolAmountOut(underlyingAmountIn); const proposal = harden({ give: { Underlying: underlyingAmountIn }, want: { Protocol: protocolAmountOut }, @@ -472,7 +332,7 @@ test('deposit', async t => { Underlying: vanMint.mintPayment(underlyingAmountIn), }); - const invitation = await E(pm).makeDepositInvitation(); + const invitation = await E(vanPoolMan).makeDepositInvitation(); const seat = await E(zoe).offer( invitation, proposal, @@ -490,51 +350,40 @@ test('deposit', async t => { ), ); - - t.is(await E(pm).getProtocolLiquidity(), 5555555550n); - t.is(await E(pm).getUnderlyingLiquidity(), 111111111n); + t.is(await E(vanPoolMan).getProtocolLiquidity(), 5555555550n); + t.is(await E(vanPoolMan).getUnderlyingLiquidity(), 111111111n); t.is(message, "Finished"); }); test('deposit - false protocolAmountOut', async t => { const { - vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, - } = setupAssets(); - const loanTiming = { - chargingPeriod: 2n, - recordingPeriod: 10n, - priceCheckPeriod: 50n - }; + vanKit: {brand: vanBrand, issuer: vanIssuer, mint: vanMint}, + compareCurrencyKit: {brand: usdBrand, issuer: usdIssuer}, + vanRates + } = t.context; - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - - const bootstrappedAssets = [vanBrand]; - - const { lendingPoolCreatorFacet, lendingPoolPublicFacet, zoe } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, - buildManualTimer(console.log), undefined, - vanLiquidity, + undefined, 500n, - vanIssuer, - bootstrappedAssets ); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); + const vanUsdPriceAuthority = makeScriptedPriceAuthority({ + actualBrandIn: vanBrand, + actualBrandOut: usdBrand, + priceList: [110n, 110n, 101n], + timer, + undefined, + unitAmountIn: AmountMath.make(vanBrand, 100n), + quoteInterval: secondsPerDay * 7n + }); - const rates = makeRates(vanBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(vanIssuer, 'VAN', rates); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); + const protocolBrand = await E(vanPoolMan).getProtocolBrand(); + const protocolIssuer = await E(vanPoolMan).getProtocolIssuer(); console.log('[BRAND]:', protocolBrand); console.log('[ISSUER]:', protocolIssuer); const underlyingAmountIn = AmountMath.make(vanBrand, 111111111n); @@ -548,7 +397,7 @@ test('deposit - false protocolAmountOut', async t => { Underlying: vanMint.mintPayment(underlyingAmountIn), }); - const invitation = await E(pm).makeDepositInvitation(); + const invitation = await E(vanPoolMan).makeDepositInvitation(); const seat = E(zoe).offer( invitation, proposal, @@ -562,39 +411,25 @@ test('deposit - false protocolAmountOut', async t => { test('borrow', async t => { const { vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, + compareCurrencyKit: { brand: usdBrand }, panKit: { mint: panMint, issuer: panIssuer, brand: panBrand }, - } = setupAssets(); - const secondsPerDay = SECONDS_PER_YEAR / 365n; - const loanTiming = { + vanRates, + panRates, + } = t.context; + + t.context.loanTiming = { chargingPeriod: secondsPerDay, recordingPeriod: secondsPerDay * 5n, priceCheckPeriod: secondsPerDay * 5n * 2n }; - // charge interest on every tick - // const manualTimer = buildManualTimer(console.log, 0n, secondsPerDay * 7n); - - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - const bootstrappedAssets = [vanBrand]; - - const { lendingPoolCreatorFacet, lendingPoolPublicFacet, zoe, timer, quoteMint } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, buildManualTimer(console.log, 0n, secondsPerDay * 5n), - undefined, - vanLiquidity, + secondsPerDay * 5n, 500n, - vanIssuer, - bootstrappedAssets, - usdBrand ); const vanUsdPriceAuthority = makeScriptedPriceAuthority({ @@ -602,7 +437,7 @@ test('borrow', async t => { actualBrandOut: usdBrand, priceList: [105n, 103n, 101n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(vanBrand, 100n), quoteInterval: secondsPerDay * 5n }); @@ -612,117 +447,29 @@ test('borrow', async t => { actualBrandOut: usdBrand, priceList: [500n, 490n, 470n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(panBrand, 100n), quoteInterval: secondsPerDay * 5n, }); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); - - const addVanPool = async () => { - const rates = makeRates(vanBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(vanIssuer, 'VAN', rates, vanUsdPriceAuthority); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); - console.log('[BRAND]:', protocolBrand); - console.log('[ISSUER]:', protocolIssuer); - const underlyingAmountIn = AmountMath.make(vanBrand, 111111111n); - const protocolAmountOut = await E(pm).getProtocolAmountOut(underlyingAmountIn); - const proposal = harden({ - give: { Underlying: underlyingAmountIn }, - want: { Protocol: protocolAmountOut }, - }); - - const paymentKeywordRecord = harden({ - Underlying: vanMint.mintPayment(underlyingAmountIn), - }); - - const invitation = await E(pm).makeDepositInvitation(); - const seat = await E(zoe).offer( - invitation, - proposal, - paymentKeywordRecord - ); - - const { - Protocol: protocolReceived - } = await E(seat).getPayouts(); - const protocolReceivedAmount = await E(protocolIssuer).getAmountOf(protocolReceived); - t.truthy( - AmountMath.isEqual( - protocolReceivedAmount, - AmountMath.make(protocolBrand, 5555555550n), - ), - ); - - - t.is(await E(pm).getProtocolLiquidity(), 5555555550n); - t.is(await E(pm).getUnderlyingLiquidity(), 111111111n); - // t.deepEqual(await E(pm).getPriceAuthorityForBrand(vanBrand), vanUsdPriceAuthority); - - return { pm, protocolReceived }; - } - - const addPanPool = async () => { - const rates = makeRates(panBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(panIssuer, 'PAN', rates, panUsdPriceAuthority); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); - console.log('[BRAND]:', protocolBrand); - console.log('[ISSUER]:', protocolIssuer); - const underlyingAmountIn = AmountMath.make(panBrand, 211111111n); - const protocolAmountOut = await E(pm).getProtocolAmountOut(underlyingAmountIn); - const proposal = harden({ - give: { Underlying: underlyingAmountIn }, - want: { Protocol: protocolAmountOut }, - }); - - const paymentKeywordRecord = harden({ - Underlying: panMint.mintPayment(underlyingAmountIn), - }); - - const invitation = await E(pm).makeDepositInvitation(); - const seat = await E(zoe).offer( - invitation, - proposal, - paymentKeywordRecord - ); - - const { - Protocol: protocolReceived - } = await E(seat).getPayouts(); - t.truthy( - AmountMath.isEqual( - await E(protocolIssuer).getAmountOf(protocolReceived), - AmountMath.make(protocolBrand, 10555555550n), - ), - ); - - - t.is(await E(pm).getProtocolLiquidity(), 10555555550n); - t.is(await E(pm).getUnderlyingLiquidity(), 211111111n); - t.deepEqual((await E(pm).getPriceAuthorityForBrand(panBrand)).priceAuthority, panUsdPriceAuthority); - t.deepEqual((await E(pm).getPriceAuthorityForBrand(vanBrand)).priceAuthority, vanUsdPriceAuthority); - - return { pm, protocolReceived }; - }; - - const vanPoolMan = await addVanPool(); - const panPoolMan = await addPanPool(); + // Add the pools + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); + const panPoolMan = await addPool(zoe, panRates, lendingPoolCreatorFacet, panIssuer, "PAN", panUsdPriceAuthority); - await t.notThrowsAsync(E(panPoolMan.pm).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 15555555n))); - await t.throwsAsync(E(panPoolMan.pm).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 211111112n))); + // Put money inside the pools + let vanPoolDepositedMoney = await depositMoney(zoe, vanPoolMan, vanMint, 1n); + await depositMoney(zoe, panPoolMan, panMint, 10n); - // build the proppsal - const vanPoolProtocolIssuer = await E(vanPoolMan.pm).getProtocolIssuer(); + await t.notThrowsAsync(E(panPoolMan).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 10n * 10n ** 8n - 1n))); + await t.throwsAsync(E(panPoolMan).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 10n * 10n ** 8n + 1n))); const debtProposal = { - give: { Collateral: await E(vanPoolProtocolIssuer).getAmountOf(vanPoolMan.protocolReceived) }, - want: { Debt: AmountMath.make(panBrand, 15555554n) } + give: { Collateral: vanPoolDepositedMoney.amount}, + want: { Debt: AmountMath.make(panBrand, 4n * 10n ** 6n) } }; const debtPaymentKeywordRecord = { - Collateral: await vanPoolMan.protocolReceived + Collateral: vanPoolDepositedMoney.payment }; const borrowInvitation = await E(lendingPoolPublicFacet).makeBorrowInvitation(); @@ -737,49 +484,39 @@ test('borrow', async t => { const vaultKit = await E(borrowerUserSeat).getOfferResult(); const vault = vaultKit.vault; + let vaultCurrentDebt = await E(vault).getCurrentDebt(); + + t.deepEqual(vaultCurrentDebt, debtProposal.want.Debt); + + // accrue interest await timer.tick(); await timer.tick(); - const vaultCurrentDebt = await E(vault).getCurrentDebt(); - - t.notDeepEqual(vaultCurrentDebt, AmountMath.make(panBrand, 15555554n)); + vaultCurrentDebt = await E(vault).getCurrentDebt(); + t.notDeepEqual(vaultCurrentDebt, debtProposal.want.Debt); }); test('borrow-rate-fluctuate', async t => { const { vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, + compareCurrencyKit: { brand: usdBrand }, panKit: { mint: panMint, issuer: panIssuer, brand: panBrand }, - } = setupAssets(); - const secondsPerDay = SECONDS_PER_YEAR / 365n; - const loanTiming = { + vanRates, + panRates, + } = t.context; + + t.context.loanTiming = { chargingPeriod: secondsPerDay, recordingPeriod: secondsPerDay * 7n, priceCheckPeriod: secondsPerDay * 7n * 2n }; - // charge interest on every tick - // const manualTimer = buildManualTimer(console.log, 0n, secondsPerDay * 7n); - - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - const bootstrappedAssets = [vanBrand]; - - const { lendingPoolCreatorFacet, lendingPoolPublicFacet, zoe, timer, quoteMint } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, buildManualTimer(console.log, 0n, secondsPerDay * 7n), - undefined, - vanLiquidity, + secondsPerDay * 7n, 500n, - vanIssuer, - bootstrappedAssets, - usdBrand ); const vanUsdPriceAuthority = makeScriptedPriceAuthority({ @@ -787,7 +524,7 @@ test('borrow-rate-fluctuate', async t => { actualBrandOut: usdBrand, priceList: [105n, 103n, 101n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(vanBrand, 100n), quoteInterval: secondsPerDay * 7n }); @@ -797,121 +534,29 @@ test('borrow-rate-fluctuate', async t => { actualBrandOut: usdBrand, priceList: [500n, 490n, 470n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(panBrand, 100n), quoteInterval: secondsPerDay * 7n, }); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); - - const addVanPool = async () => { - const rates = makeRates(vanBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(vanIssuer, 'VAN', rates, vanUsdPriceAuthority); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); - console.log('[BRAND]:', protocolBrand); - console.log('[ISSUER]:', protocolIssuer); - const displayInfo = vanBrand.getDisplayInfo(); - const decimalPlaces = displayInfo?.decimalPlaces || 0n; - const underlyingAmountIn = AmountMath.make(vanBrand, 4n * 10n ** Nat(decimalPlaces)); - const protocolAmountOut = await E(pm).getProtocolAmountOut(underlyingAmountIn); - const proposal = harden({ - give: { Underlying: underlyingAmountIn }, - want: { Protocol: protocolAmountOut }, - }); - - const paymentKeywordRecord = harden({ - Underlying: vanMint.mintPayment(underlyingAmountIn), - }); - - const invitation = await E(pm).makeDepositInvitation(); - const seat = await E(zoe).offer( - invitation, - proposal, - paymentKeywordRecord - ); - - const { - Protocol: protocolReceived - } = await E(seat).getPayouts(); - const protocolReceivedAmount = await E(protocolIssuer).getAmountOf(protocolReceived); - t.truthy( - AmountMath.isEqual( - protocolReceivedAmount, - AmountMath.make(protocolBrand, 20000000000n), - ), - ); - - - t.is(await E(pm).getProtocolLiquidity(), 20000000000n); - t.is(await E(pm).getUnderlyingLiquidity(), 4n * 10n ** Nat(decimalPlaces)); - // t.deepEqual(await E(pm).getPriceAuthorityForBrand(vanBrand), vanUsdPriceAuthority); - - return { pm, protocolReceived }; - } - - const addPanPool = async () => { - const rates = makeRates(panBrand, usdBrand); - const pm = await E(lendingPool).addPoolType(panIssuer, 'PAN', rates, panUsdPriceAuthority); - const protocolBrand = await E(pm).getProtocolBrand(); - const protocolIssuer = await E(pm).getProtocolIssuer(); - console.log('[BRAND]:', protocolBrand); - console.log('[ISSUER]:', protocolIssuer); - const displayInfo = vanBrand.getDisplayInfo(); - const decimalPlaces = displayInfo?.decimalPlaces || 0n; - const underlyingAmountIn = AmountMath.make(panBrand, 4n * 10n ** Nat(decimalPlaces)); - const protocolAmountOut = await E(pm).getProtocolAmountOut(underlyingAmountIn); - const proposal = harden({ - give: { Underlying: underlyingAmountIn }, - want: { Protocol: protocolAmountOut }, - }); - - const paymentKeywordRecord = harden({ - Underlying: panMint.mintPayment(underlyingAmountIn), - }); - - const invitation = await E(pm).makeDepositInvitation(); - const seat = await E(zoe).offer( - invitation, - proposal, - paymentKeywordRecord - ); - - const { - Protocol: protocolReceived - } = await E(seat).getPayouts(); - t.truthy( - AmountMath.isEqual( - await E(protocolIssuer).getAmountOf(protocolReceived), - AmountMath.make(protocolBrand, 20000000000n), - ), - ); - - - t.is(await E(pm).getProtocolLiquidity(), 20000000000n); - t.is(await E(pm).getUnderlyingLiquidity(), 4n * 10n ** Nat(decimalPlaces)); - t.deepEqual((await E(pm).getPriceAuthorityForBrand(panBrand)).priceAuthority, panUsdPriceAuthority); - t.deepEqual((await E(pm).getPriceAuthorityForBrand(vanBrand)).priceAuthority, vanUsdPriceAuthority); - - return { pm, protocolReceived }; - }; - - const vanPoolMan = await addVanPool(); - const panPoolMan = await addPanPool(); + // Add the pools + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); + const panPoolMan = await addPool(zoe, panRates, lendingPoolCreatorFacet, panIssuer, "PAN", panUsdPriceAuthority); - await t.notThrowsAsync(E(panPoolMan.pm).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 399999999n))); - await t.throwsAsync(E(panPoolMan.pm).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 400000001n))); + // Put money inside the pools + let vanPoolDepositedMoney = await depositMoney(zoe, vanPoolMan, vanMint, 1n); + await depositMoney(zoe, panPoolMan, panMint, 4n); - // build the proppsal - const vanPoolProtocolIssuer = await E(vanPoolMan.pm).getProtocolIssuer(); + await t.notThrowsAsync(E(panPoolMan).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 4n * 10n ** 8n - 1n))); + await t.throwsAsync(E(panPoolMan).enoughLiquidityForProposedDebt(AmountMath.make(panBrand, 4n * 10n ** 8n + 1n))); let debtProposal = { - give: { Collateral: await E(vanPoolProtocolIssuer).getAmountOf(vanPoolMan.protocolReceived) }, - want: { Debt: AmountMath.make(panBrand, 4000000n) } + give: { Collateral: vanPoolDepositedMoney.amount }, + want: { Debt: AmountMath.make(panBrand, 4n * 10n ** 6n) } }; let debtPaymentKeywordRecord = { - Collateral: await vanPoolMan.protocolReceived + Collateral: vanPoolDepositedMoney.payment }; let borrowInvitation = await E(lendingPoolPublicFacet).makeBorrowInvitation(); @@ -928,10 +573,10 @@ test('borrow-rate-fluctuate', async t => { const vaultCurrentDebt4B = await E(vault4B).getCurrentDebt(); - t.deepEqual(vaultCurrentDebt4B, AmountMath.make(panBrand, 4000000n)); - t.deepEqual(await E(panPoolMan.pm).getCurrentBorrowingRate(), makeRatio(270n, panBrand, BASIS_POINTS)); + t.deepEqual(vaultCurrentDebt4B, AmountMath.make(panBrand, 4n * 10n ** 6n)); + t.deepEqual(await E(panPoolMan).getCurrentBorrowingRate(), makeRatio(270n, panBrand, BASIS_POINTS)); - const collateral = await depositMoney(zoe, vanPoolMan.pm, vanMint,4n); + const collateral = await depositMoney(zoe, vanPoolMan, vanMint,4n); debtProposal = { give: { Collateral: collateral.amount }, @@ -957,13 +602,13 @@ test('borrow-rate-fluctuate', async t => { const vaultCurrentDebt1B = await E(vault1B).getCurrentDebt(); t.deepEqual(vaultCurrentDebt1B, AmountMath.make(panBrand, 1000000n)); - t.deepEqual(await E(panPoolMan.pm).getCurrentBorrowingRate(), makeRatio(275n, panBrand, BASIS_POINTS)); + t.deepEqual(await E(panPoolMan).getCurrentBorrowingRate(), makeRatio(275n, panBrand, BASIS_POINTS)); await timer.tick(); await waitForPromisesToSettle(); - t.deepEqual(await E(panPoolMan.pm).getTotalDebt(), AmountMath.make(panBrand, 5000000n + 372n * 7n)) - t.deepEqual(await E(panPoolMan.pm).getCurrentBorrowingRate(), makeRatio(276n, panBrand, BASIS_POINTS)); - t.deepEqual((await E(panPoolMan.pm).getExchangeRate()).numerator, AmountMath.make(panBrand, 201n)); + t.deepEqual(await E(panPoolMan).getTotalDebt(), AmountMath.make(panBrand, 5000000n + 372n * 7n)) + t.deepEqual(await E(panPoolMan).getCurrentBorrowingRate(), makeRatio(275n, panBrand, BASIS_POINTS)); // adopt banker's rounding + t.deepEqual((await E(panPoolMan).getExchangeRate()).numerator, AmountMath.make(panBrand, 200n)); // adopt banker's rounding }); /** @@ -974,37 +619,25 @@ test('borrow-rate-fluctuate', async t => { test('adjust-balances-no-interest', async t => { const { vanKit: { mint: vanMint, issuer: vanIssuer, brand: vanBrand }, - usdKit: { mint: usdMint, issuer: usdIssuer, brand: usdBrand }, + compareCurrencyKit: { brand: usdBrand }, panKit: { mint: panMint, issuer: panIssuer, brand: panBrand }, - } = setupAssets(); - const secondsPerDay = SECONDS_PER_YEAR / 365n; - const loanTiming = { + vanRates, + panRates, + } = t.context; + + t.context.loanTiming = { chargingPeriod: secondsPerDay, recordingPeriod: secondsPerDay * 7n, priceCheckPeriod: secondsPerDay * 7n * 2n }; - const vanInitialLiquidity = AmountMath.make(vanBrand, 300n); - const vanLiquidity = { - proposal: vanInitialLiquidity, - payment: vanMint.mintPayment(vanInitialLiquidity), - }; - - const bootstrappedAssets = [vanBrand]; - - const { lendingPoolCreatorFacet, lendingPoolPublicFacet, zoe, timer, quoteMint } = await setupServices( - loanTiming, + const { zoe, lendingPool: { lendingPoolCreatorFacet, lendingPoolPublicFacet }, timer } = await setupServices( + t, [500n, 15n], AmountMath.make(vanBrand, 900n), - vanBrand, - { committeeName: 'TheCabal', committeeSize: 5 }, buildManualTimer(console.log, 0n, secondsPerDay * 7n), - undefined, - vanLiquidity, + secondsPerDay * 7n, 500n, - vanIssuer, - bootstrappedAssets, - usdBrand ); const vanUsdPriceAuthority = makeScriptedPriceAuthority({ @@ -1012,7 +645,7 @@ test('adjust-balances-no-interest', async t => { actualBrandOut: usdBrand, priceList: [110n, 110n, 101n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(vanBrand, 100n), quoteInterval: secondsPerDay * 7n }); @@ -1022,20 +655,14 @@ test('adjust-balances-no-interest', async t => { actualBrandOut: usdBrand, priceList: [200n, 200n, 470n], timer, - quoteMint, + undefined, unitAmountIn: AmountMath.make(panBrand, 100n), quoteInterval: secondsPerDay * 7n, }); - const lendingPool = await E(lendingPoolCreatorFacet).getLimitedCreatorFacet(); - - // Make the rates for the pools - const vanPoolRates = makeRates(vanBrand, usdBrand); - const panPoolRates = makeRates(panBrand, usdBrand); - // Add the pools - const vanPoolMan = await addPool(zoe, vanPoolRates, lendingPool, vanIssuer, "VAN", vanUsdPriceAuthority); - const panPoolMan = await addPool(zoe, panPoolRates, lendingPool, panIssuer, "PAN", panUsdPriceAuthority); + const vanPoolMan = await addPool(zoe, vanRates, lendingPoolCreatorFacet, vanIssuer, "VAN", vanUsdPriceAuthority); + const panPoolMan = await addPool(zoe, panRates, lendingPoolCreatorFacet, panIssuer, "PAN", panUsdPriceAuthority); // Put money inside the pools let vanPoolDepositedMoney = await depositMoney(zoe, vanPoolMan, vanMint, 6n);