From 18befd02ab4a37fe9bc44e3d1d41c8ba5320415a Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 31 Oct 2024 14:09:17 -0700 Subject: [PATCH 1/4] test: contract placeholder --- packages/fast-usdc/package.json | 13 ++++++ packages/fast-usdc/src/fast-usdc.contract.js | 46 ++++++++++++++++++++ packages/fast-usdc/tsconfig.json | 5 +-- 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 packages/fast-usdc/src/fast-usdc.contract.js diff --git a/packages/fast-usdc/package.json b/packages/fast-usdc/package.json index 21b669555f4..70c9e7df559 100644 --- a/packages/fast-usdc/package.json +++ b/packages/fast-usdc/package.json @@ -27,6 +27,19 @@ "ts-blank-space": "^0.4.1" }, "dependencies": { + "@agoric/ertp": "^0.16.2", + "@agoric/internal": "^0.3.2", + "@agoric/orchestration": "^0.1.0", + "@agoric/store": "^0.9.2", + "@agoric/vow": "^0.1.0", + "@endo/common": "^1.2.7", + "@endo/errors": "^1.2.7", + "@endo/eventual-send": "^1.2.7", + "@endo/far": "^1.1.8", + "@endo/marshal": "^1.6.1", + "@endo/pass-style": "^1.4.6", + "@endo/patterns": "^1.4.6", + "@endo/promise-kit": "^1.1.7", "commander": "^12.1.0" }, "ava": { diff --git a/packages/fast-usdc/src/fast-usdc.contract.js b/packages/fast-usdc/src/fast-usdc.contract.js new file mode 100644 index 00000000000..a4dd32c08bf --- /dev/null +++ b/packages/fast-usdc/src/fast-usdc.contract.js @@ -0,0 +1,46 @@ +import { BrandShape } from '@agoric/ertp/src/typeGuards.js'; +import { withOrchestration } from '@agoric/orchestration'; +import { M } from '@endo/patterns'; + +/** + * @import {OrchestrationPowers, OrchestrationTools} from '@agoric/orchestration/src/utils/start-helper.js'; + * @import {Zone} from '@agoric/zone'; + */ + +/** + * @typedef {{ + * poolFee: Amount<'nat'>; + * contractFee: Amount<'nat'>; + * }} FastUsdcTerms + */ +const NatAmountShape = { brand: BrandShape, value: M.nat() }; +export const meta = { + customTermsShape: { + contractFee: NatAmountShape, + poolFee: NatAmountShape, + }, +}; +harden(meta); + +/** + * @param {ZCF} zcf + * @param {OrchestrationPowers & { + * marshaller: Marshaller; + * }} privateArgs + * @param {Zone} zone + * @param {OrchestrationTools} tools + */ +export const contract = async (zcf, privateArgs, zone, tools) => { + assert(tools, 'no tools'); + const terms = zcf.getTerms(); + assert('USDC' in terms.brands, 'no USDC brand'); + + const creatorFacet = zone.exo('Fast USDC Creator', undefined, {}); + + return harden({ creatorFacet }); +}; +harden(contract); + +export const start = withOrchestration(contract); +harden(start); +/** @typedef {typeof start} FastUsdcSF */ diff --git a/packages/fast-usdc/tsconfig.json b/packages/fast-usdc/tsconfig.json index 2bb806097ef..f690da86a72 100644 --- a/packages/fast-usdc/tsconfig.json +++ b/packages/fast-usdc/tsconfig.json @@ -1,9 +1,8 @@ { "extends": "../../tsconfig.json", - "compilerOptions": { - "strict": true, - }, + "compilerOptions": {}, "include": [ + "scripts", "src", "test", ], From 0bc5b16c7e9d39f32d9438380d503944a5422514 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 31 Oct 2024 16:01:32 -0700 Subject: [PATCH 2/4] test: starting the contract --- packages/fast-usdc/package.json | 4 + .../fast-usdc/test/fast-usdc.contract.test.ts | 35 +++ packages/fast-usdc/test/supports.ts | 216 ++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 packages/fast-usdc/test/fast-usdc.contract.test.ts create mode 100644 packages/fast-usdc/test/supports.ts diff --git a/packages/fast-usdc/package.json b/packages/fast-usdc/package.json index 70c9e7df559..cd889223866 100644 --- a/packages/fast-usdc/package.json +++ b/packages/fast-usdc/package.json @@ -22,6 +22,10 @@ "lint:eslint": "eslint ." }, "devDependencies": { + "@agoric/swingset-liveslots": "^0.10.2", + "@agoric/vats": "^0.15.1", + "@agoric/zoe": "^0.26.2", + "@agoric/zone": "^0.2.2", "ava": "^5.3.0", "c8": "^9.1.0", "ts-blank-space": "^0.4.1" diff --git a/packages/fast-usdc/test/fast-usdc.contract.test.ts b/packages/fast-usdc/test/fast-usdc.contract.test.ts new file mode 100644 index 00000000000..485f2851be2 --- /dev/null +++ b/packages/fast-usdc/test/fast-usdc.contract.test.ts @@ -0,0 +1,35 @@ +import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; + +import { setUpZoeForTest } from '@agoric/zoe/tools/setup-zoe.js'; +import { E } from '@endo/far'; +import path from 'path'; +import { commonSetup } from './supports.js'; + +const dirname = path.dirname(new URL(import.meta.url).pathname); + +const contractFile = `${dirname}/../src/fast-usdc.contract.js`; +type StartFn = typeof import('../src/fast-usdc.contract.js').start; + +test('start', async t => { + const { + bootstrap, + brands: { usdc }, + commonPrivateArgs, + utils, + } = await commonSetup(t); + + const { zoe, bundleAndInstall } = await setUpZoeForTest(); + const installation: Installation = + await bundleAndInstall(contractFile); + + const { creatorFacet } = await E(zoe).startInstance( + installation, + { USDC: usdc.issuer }, + { + poolFee: usdc.make(1n), + contractFee: usdc.make(1n), + }, + commonPrivateArgs, + ); + t.truthy(creatorFacet); +}); diff --git a/packages/fast-usdc/test/supports.ts b/packages/fast-usdc/test/supports.ts new file mode 100644 index 00000000000..dd315bdefda --- /dev/null +++ b/packages/fast-usdc/test/supports.ts @@ -0,0 +1,216 @@ +import { makeIssuerKit } from '@agoric/ertp'; +import { VTRANSFER_IBC_EVENT } from '@agoric/internal/src/action-types.js'; +import { makeFakeStorageKit } from '@agoric/internal/src/storage-test-utils.js'; +import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js'; +import { registerKnownChains } from '@agoric/orchestration/src/chain-info.js'; +import { makeChainHub } from '@agoric/orchestration/src/exos/chain-hub.js'; +import { prepareCosmosInterchainService } from '@agoric/orchestration/src/exos/cosmos-interchain-service.js'; +import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js'; +import { setupFakeNetwork } from '@agoric/orchestration/test/network-fakes.js'; +import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js'; +import { reincarnate } from '@agoric/swingset-liveslots/tools/setup-vat-data.js'; +import { makeNameHubKit } from '@agoric/vats'; +import { prepareBridgeTargetModule } from '@agoric/vats/src/bridge-target.js'; +import { makeWellKnownSpaces } from '@agoric/vats/src/core/utils.js'; +import { prepareLocalChainTools } from '@agoric/vats/src/localchain.js'; +import { prepareTransferTools } from '@agoric/vats/src/transfer.js'; +import { makeFakeBankManagerKit } from '@agoric/vats/tools/bank-utils.js'; +import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js'; +import { + makeFakeLocalchainBridge, + makeFakeTransferBridge, +} from '@agoric/vats/tools/fake-bridge.js'; +import { prepareSwingsetVowTools } from '@agoric/vow/vat.js'; +import type { Installation } from '@agoric/zoe/src/zoeService/utils.js'; +import { buildZoeManualTimer } from '@agoric/zoe/tools/manualTimer.js'; +import { withAmountUtils } from '@agoric/zoe/tools/test-utils.js'; +import { makeHeapZone, type Zone } from '@agoric/zone'; +import { makeDurableZone } from '@agoric/zone/durable.js'; +import { E } from '@endo/far'; +import type { ExecutionContext } from 'ava'; + +export { + makeFakeLocalchainBridge, + makeFakeTransferBridge, +} from '@agoric/vats/tools/fake-bridge.js'; + +export const commonSetup = async (t: ExecutionContext) => { + t.log('bootstrap vat dependencies'); + // The common setup cannot support a durable zone because many of the fakes are not durable. + // They were made before we had durable kinds (and thus don't take a zone or baggage). + // To test durability in unit tests, test a particular entity with `relaxDurabilityRules: false`. + // To test durability integrating multiple vats, use a RunUtils/bootstrap test. + const rootZone = makeHeapZone(); + + const { nameHub: agoricNames, nameAdmin: agoricNamesAdmin } = + makeNameHubKit(); + + const usdc = withAmountUtils(makeIssuerKit('USDC')); + const bankBridgeMessages = [] as any[]; + const { bankManager, pourPayment } = await makeFakeBankManagerKit({ + onToBridge: obj => bankBridgeMessages.push(obj), + }); + await E(bankManager).addAsset( + 'uusdc', + 'USDC', + 'USD Circle Stablecoin', + usdc.issuerKit, + ); + // These mints no longer stay in sync with bankManager. + // Use pourPayment() for IST. + const { mint: _i, ...usdcSansMint } = usdc; + // XXX real bankManager does this. fake should too? + // TODO https://github.com/Agoric/agoric-sdk/issues/9966 + await makeWellKnownSpaces(agoricNamesAdmin, t.log, ['vbankAsset']); + await E(E(agoricNamesAdmin).lookupAdmin('vbankAsset')).update( + 'uusdc', + /** @type {AssetInfo} */ harden({ + brand: usdc.brand, + issuer: usdc.issuer, + issuerName: 'IST', + denom: 'uusdc', + proposedName: 'IST', + displayInfo: { IOU: true }, + }), + ); + + const vowTools = prepareSwingsetVowTools(rootZone.subZone('vows')); + + const transferBridge = makeFakeTransferBridge(rootZone); + const { makeBridgeTargetKit } = prepareBridgeTargetModule( + rootZone.subZone('bridge'), + ); + const { makeTransferMiddlewareKit } = prepareTransferTools( + rootZone.subZone('transfer'), + vowTools, + ); + + const { finisher, interceptorFactory, transferMiddleware } = + makeTransferMiddlewareKit(); + const bridgeTargetKit = makeBridgeTargetKit( + transferBridge, + VTRANSFER_IBC_EVENT, + interceptorFactory, + ); + finisher.useRegistry(bridgeTargetKit.targetRegistry); + await E(transferBridge).initHandler(bridgeTargetKit.bridgeHandler); + + const localBridgeMessages = [] as any[]; + const localchainBridge = makeFakeLocalchainBridge(rootZone, obj => + localBridgeMessages.push(obj), + ); + const localchain = prepareLocalChainTools( + rootZone.subZone('localchain'), + vowTools, + ).makeLocalChain({ + bankManager, + system: localchainBridge, + transfer: transferMiddleware, + }); + const timer = buildZoeManualTimer(t.log); + const marshaller = makeFakeBoard().getReadonlyMarshaller(); + const storage = makeFakeStorageKit('mockChainStorageRoot', { + sequence: false, + }); + + const { portAllocator, setupIBCProtocol, ibcBridge } = setupFakeNetwork( + rootZone.subZone('network'), + { vowTools }, + ); + await setupIBCProtocol(); + + const makeCosmosInterchainService = prepareCosmosInterchainService( + rootZone.subZone('orchestration'), + vowTools, + ); + const cosmosInterchainService = makeCosmosInterchainService({ + portAllocator, + }); + + await registerKnownChains(agoricNamesAdmin, () => {}); + + let ibcSequenceNonce = 0n; + /** simulate incoming message as if the transfer completed over IBC */ + const transmitTransferAck = async () => { + // assume this is called after each outgoing IBC transfer + ibcSequenceNonce += 1n; + // let the promise for the transfer start + await eventLoopIteration(); + const lastMsgTransfer = localBridgeMessages.at(-1).messages[0]; + await E(transferBridge).fromBridge( + buildVTransferEvent({ + receiver: lastMsgTransfer.receiver, + sender: lastMsgTransfer.sender, + target: lastMsgTransfer.sender, + sourceChannel: lastMsgTransfer.sourceChannel, + sequence: ibcSequenceNonce, + }), + ); + // let the bridge handler finish + await eventLoopIteration(); + }; + + const chainHub = makeChainHub( + rootZone.subZone('chainHub'), + agoricNames, + vowTools, + ); + + return { + bootstrap: { + agoricNames, + agoricNamesAdmin, + bankManager, + timer, + localchain, + cosmosInterchainService, + // TODO remove; bootstrap doesn't have a zone + rootZone: rootZone.subZone('contract'), + storage, + // TODO remove; bootstrap doesn't have vowTools + vowTools, + }, + brands: { + usdc: usdcSansMint, + }, + mocks: { + ibcBridge, + transferBridge, + }, + commonPrivateArgs: { + agoricNames, + localchain, + orchestrationService: cosmosInterchainService, + storageNode: storage.rootNode, + marshaller, + timerService: timer, + }, + facadeServices: { + agoricNames, + chainHub, + localchain, + orchestrationService: cosmosInterchainService, + timerService: timer, + }, + utils: { + pourPayment, + inspectLocalBridge: () => harden([...localBridgeMessages]), + inspectDibcBridge: () => E(ibcBridge).inspectDibcBridge(), + inspectBankBridge: () => harden([...bankBridgeMessages]), + transmitTransferAck, + }, + }; +}; + +export const makeDefaultContext = (contract: Installation) => {}; + +/** + * Reincarnate without relaxDurabilityRules and provide a durable zone in the incarnation. + * @param key + */ +export const provideDurableZone = (key: string): Zone => { + const { fakeVomKit } = reincarnate({ relaxDurabilityRules: false }); + const root = fakeVomKit.cm.provideBaggage(); + const zone = makeDurableZone(root); + return zone.subZone(key); +}; From 22b2cc5af1e6975f7bddda9ddd7668a5c08c06b7 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 1 Nov 2024 10:01:17 -0700 Subject: [PATCH 3/4] chore: exo stubs --- packages/fast-usdc/src/exos/advancer.js | 19 +++++++++++++++++++ packages/fast-usdc/src/exos/settler.js | 17 +++++++++++++++++ packages/fast-usdc/src/exos/status-manager.js | 13 +++++++++++++ .../fast-usdc/src/exos/transaction-feed.js | 13 +++++++++++++ packages/fast-usdc/src/fast-usdc.contract.js | 11 +++++++++++ 5 files changed, 73 insertions(+) create mode 100644 packages/fast-usdc/src/exos/advancer.js create mode 100644 packages/fast-usdc/src/exos/settler.js create mode 100644 packages/fast-usdc/src/exos/status-manager.js create mode 100644 packages/fast-usdc/src/exos/transaction-feed.js diff --git a/packages/fast-usdc/src/exos/advancer.js b/packages/fast-usdc/src/exos/advancer.js new file mode 100644 index 00000000000..28f146515f1 --- /dev/null +++ b/packages/fast-usdc/src/exos/advancer.js @@ -0,0 +1,19 @@ +/** + * @import {Zone} from '@agoric/zone'; + * @import {TransactionFeed} from './transaction-feed.js'; + * @import {StatusManager} from './status-manager.js'; + */ + +import { assertAllDefined } from '@agoric/internal'; + +/** + * @param {Zone} zone + * @param {object} caps + * @param {TransactionFeed} caps.feed + * @param {StatusManager} caps.statusManager + */ +export const prepareAdvancer = (zone, { feed, statusManager }) => { + assertAllDefined({ feed, statusManager }); + return zone.exo('Fast USDC Advancer', undefined, {}); +}; +harden(prepareAdvancer); diff --git a/packages/fast-usdc/src/exos/settler.js b/packages/fast-usdc/src/exos/settler.js new file mode 100644 index 00000000000..59356b1b795 --- /dev/null +++ b/packages/fast-usdc/src/exos/settler.js @@ -0,0 +1,17 @@ +/** + * @import {Zone} from '@agoric/zone'; + * @import {StatusManager} from './status-manager.js'; + */ + +import { assertAllDefined } from '@agoric/internal'; + +/** + * @param {Zone} zone + * @param {object} caps + * @param {StatusManager} caps.statusManager + */ +export const prepareSettler = (zone, { statusManager }) => { + assertAllDefined({ statusManager }); + return zone.exo('Fast USDC Settler', undefined, {}); +}; +harden(prepareSettler); diff --git a/packages/fast-usdc/src/exos/status-manager.js b/packages/fast-usdc/src/exos/status-manager.js new file mode 100644 index 00000000000..ea1f69b5827 --- /dev/null +++ b/packages/fast-usdc/src/exos/status-manager.js @@ -0,0 +1,13 @@ +/** + * @import {Zone} from '@agoric/zone'; + */ + +/** + * @param {Zone} zone + */ +export const prepareStatusManager = zone => { + return zone.exo('Fast USDC Status Manager', undefined, {}); +}; +harden(prepareStatusManager); + +/** @typedef {ReturnType} StatusManager */ diff --git a/packages/fast-usdc/src/exos/transaction-feed.js b/packages/fast-usdc/src/exos/transaction-feed.js new file mode 100644 index 00000000000..481f33a1eb0 --- /dev/null +++ b/packages/fast-usdc/src/exos/transaction-feed.js @@ -0,0 +1,13 @@ +/** + * @import {Zone} from '@agoric/zone'; + */ + +/** + * @param {Zone} zone + */ +export const prepareTransactionFeed = zone => { + return zone.exo('Fast USDC Feed', undefined, {}); +}; +harden(prepareTransactionFeed); + +/** @typedef {ReturnType} TransactionFeed */ diff --git a/packages/fast-usdc/src/fast-usdc.contract.js b/packages/fast-usdc/src/fast-usdc.contract.js index a4dd32c08bf..b9be820265a 100644 --- a/packages/fast-usdc/src/fast-usdc.contract.js +++ b/packages/fast-usdc/src/fast-usdc.contract.js @@ -1,6 +1,11 @@ import { BrandShape } from '@agoric/ertp/src/typeGuards.js'; import { withOrchestration } from '@agoric/orchestration'; import { M } from '@endo/patterns'; +import { assertAllDefined } from '@agoric/internal'; +import { prepareTransactionFeed } from './exos/transaction-feed.js'; +import { prepareSettler } from './exos/settler.js'; +import { prepareAdvancer } from './exos/advancer.js'; +import { prepareStatusManager } from './exos/status-manager.js'; /** * @import {OrchestrationPowers, OrchestrationTools} from '@agoric/orchestration/src/utils/start-helper.js'; @@ -35,6 +40,12 @@ export const contract = async (zcf, privateArgs, zone, tools) => { const terms = zcf.getTerms(); assert('USDC' in terms.brands, 'no USDC brand'); + const statusManager = prepareStatusManager(zone); + const feed = prepareTransactionFeed(zone); + const settler = prepareSettler(zone, { statusManager }); + const advancer = prepareAdvancer(zone, { feed, statusManager }); + assertAllDefined({ feed, settler, advancer, statusManager }); + const creatorFacet = zone.exo('Fast USDC Creator', undefined, {}); return harden({ creatorFacet }); From 17df411438ae40c101b6283463230aee198177b5 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 1 Nov 2024 11:14:55 -0700 Subject: [PATCH 4/4] test: poolShares issuer --- packages/fast-usdc/src/fast-usdc.contract.js | 1 + packages/fast-usdc/test/fast-usdc.contract.test.ts | 4 ++-- packages/fast-usdc/test/supports.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/fast-usdc/src/fast-usdc.contract.js b/packages/fast-usdc/src/fast-usdc.contract.js index b9be820265a..fb1197e4ae5 100644 --- a/packages/fast-usdc/src/fast-usdc.contract.js +++ b/packages/fast-usdc/src/fast-usdc.contract.js @@ -39,6 +39,7 @@ export const contract = async (zcf, privateArgs, zone, tools) => { assert(tools, 'no tools'); const terms = zcf.getTerms(); assert('USDC' in terms.brands, 'no USDC brand'); + assert('PoolShares' in terms.brands, 'no PoolShares brand'); const statusManager = prepareStatusManager(zone); const feed = prepareTransactionFeed(zone); diff --git a/packages/fast-usdc/test/fast-usdc.contract.test.ts b/packages/fast-usdc/test/fast-usdc.contract.test.ts index 485f2851be2..8d4d3db0ec6 100644 --- a/packages/fast-usdc/test/fast-usdc.contract.test.ts +++ b/packages/fast-usdc/test/fast-usdc.contract.test.ts @@ -13,7 +13,7 @@ type StartFn = typeof import('../src/fast-usdc.contract.js').start; test('start', async t => { const { bootstrap, - brands: { usdc }, + brands: { poolShares, usdc }, commonPrivateArgs, utils, } = await commonSetup(t); @@ -24,7 +24,7 @@ test('start', async t => { const { creatorFacet } = await E(zoe).startInstance( installation, - { USDC: usdc.issuer }, + { PoolShares: poolShares.issuer, USDC: usdc.issuer }, { poolFee: usdc.make(1n), contractFee: usdc.make(1n), diff --git a/packages/fast-usdc/test/supports.ts b/packages/fast-usdc/test/supports.ts index dd315bdefda..aa21002b5f3 100644 --- a/packages/fast-usdc/test/supports.ts +++ b/packages/fast-usdc/test/supports.ts @@ -171,6 +171,7 @@ export const commonSetup = async (t: ExecutionContext) => { vowTools, }, brands: { + poolShares: withAmountUtils(makeIssuerKit('Fast USDC Pool Shares')), usdc: usdcSansMint, }, mocks: {