-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: interchainMints contract subsumes mintHolder
and publishInterchainAssetFromBank() from addAssetToVault.js
- Loading branch information
Showing
3 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// @ts-check | ||
import { E } from '@endo/far'; | ||
import { allValues } from './objectTools.js'; | ||
import { installContract, startContract } from './platform-goals/start-contract.js'; | ||
|
||
const { Fail } = assert; | ||
|
||
const contractName = 'interchainMints'; | ||
|
||
/** | ||
* @param {BootstrapPowers} powers | ||
* @param {{ options: { interchainMints: { bundleID: string }}}} config | ||
*/ | ||
export const installMintsContract = async (powers, config) => { | ||
const { | ||
// must be supplied by caller or template-replaced | ||
bundleID = Fail`no bundleID`, | ||
} = config?.options?.[contractName] ?? {}; | ||
|
||
return installContract(powers, { | ||
name: contractName, | ||
bundleID, | ||
}); | ||
}; | ||
|
||
/** | ||
* @param {BootstrapPowers} powers | ||
*/ | ||
export const deployInterchainMints = async (powers) => { | ||
/** @type {Installation<typeof import('./interchainMints').prepare>} */ | ||
const installation = powers.installation.consume.interchainMints; | ||
const { agoricNamesAdmin, bankManager } = powers.consume; | ||
const { interchainMintsKit } = powers.produce; | ||
|
||
const privateArgs = await allValues({ | ||
nameAdmins: allValues({ | ||
brand: E(agoricNamesAdmin).lookupAdmin('brand'), | ||
issuer: E(agoricNamesAdmin).lookupAdmin('issuer'), | ||
}), | ||
bankManager, | ||
}); | ||
|
||
const kit = await startContract(powers, { | ||
name: contractName, | ||
startArgs: { installation, privateArgs }, | ||
}); | ||
interchainMintsKit.reset(); | ||
interchainMintsKit.resolve(kit); | ||
}; | ||
|
||
export const permit = { | ||
consume: { agoricNamesAdmin: true, bankManager: true, zoe: true, startUpgradable:true }, | ||
produce: { interchainMintsKit: true }, | ||
installation: { | ||
produce: { interchainMints: true }, | ||
consume: { interchainMints: true }, | ||
}, | ||
instance: { | ||
produce: { interchainMints: true }, | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// @jessie-check | ||
// @ts-check | ||
|
||
import { E } from '@endo/far'; | ||
import { M } from '@endo/patterns'; | ||
import { makeDurableIssuerKit, AssetKind, BrandShape } from '@agoric/ertp'; | ||
import { makeDurableZone } from '@agoric/zone/durable.js'; | ||
|
||
/** @import { Baggage } from '@agoric/vat-data' */ | ||
/** @import { ContractMeta } from './@types/zoe-contract-facet'; */ | ||
/** @import { NameAdmin } from '@agoric/vats'; */ | ||
/** @import { InterchainAssetOptions } from './@types/inter-types'; */ | ||
|
||
const InterchainAssetOptionsShape = M.splitRecord( | ||
{ | ||
// issuerBoardId not supported | ||
denom: M.string(), | ||
decimalPlaces: M.number(), | ||
// TODO: time to fix the keyword goofiness? | ||
keyword: M.string(), | ||
}, | ||
{ | ||
issuerName: M.string(), | ||
proposedName: M.string(), | ||
oracleBrand: M.string(), | ||
}, | ||
); | ||
|
||
/** | ||
* NOTE: "keyword" connotes initial caps constraint, which doesn't apply here. | ||
* | ||
* @template {AssetKind} K | ||
* @typedef {{ | ||
* keyword: string; | ||
* assetKind: K; | ||
* displayInfo: DisplayInfo; | ||
* }} IssuerInfo<K> | ||
*/ | ||
|
||
/** @type {ContractMeta} */ | ||
export const meta = { | ||
upgradability: 'canUpgrade', | ||
privateArgsShape: { | ||
nameAdmins: { | ||
brand: M.remotable('Brand NameAdmin'), | ||
issuer: M.remotable('Issuer NameAdmin'), | ||
}, | ||
bankManager: M.remotable('BankManager'), | ||
}, | ||
}; | ||
harden(meta); | ||
export const privateArgsShape = meta.privateArgsShape; | ||
|
||
/** | ||
* Create and register IssuerKits for interchain / vbank assets. | ||
* | ||
* @param {ZCF} _zcf | ||
* @param {{ | ||
* nameAdmins: { | ||
* brand: NameAdmin, | ||
* issuer: NameAdmin, | ||
* }, | ||
* bankManager: BankManager, | ||
* }} _privateArgs | ||
* @param {Baggage} baggage | ||
*/ | ||
export const prepare = (_zcf, { nameAdmins, bankManager }, baggage) => { | ||
const zone = makeDurableZone(baggage); | ||
|
||
const incapable = zone.exo('Incapable', undefined, {}); | ||
|
||
/** @type {MapStore<Brand, Baggage>} */ | ||
const brandToBaggage = zone.mapStore('brandToBaggage', { | ||
keyShape: BrandShape, | ||
}); | ||
|
||
const creatorFacet = zone.exo( | ||
'InterchainMints', | ||
M.interface('InterchainMints', { | ||
addAsset: M.callWhen(InterchainAssetOptionsShape).returns(), | ||
}), | ||
{ | ||
/** | ||
* @param {InterchainAssetOptions} interchainAssetOptions | ||
*/ | ||
async addAsset(interchainAssetOptions) { | ||
const issuerBaggage = zone.detached().mapStore('issuerBaggage'); | ||
const { | ||
denom, | ||
decimalPlaces, | ||
keyword, | ||
issuerName = keyword, | ||
proposedName = keyword, | ||
} = interchainAssetOptions; | ||
const kit = makeDurableIssuerKit( | ||
issuerBaggage, | ||
issuerName, | ||
AssetKind.NAT, | ||
{ decimalPlaces }, | ||
); | ||
brandToBaggage.init(kit.brand, issuerBaggage); | ||
|
||
await Promise.all([ | ||
E(nameAdmins.issuer).update(issuerName, kit.issuer), | ||
E(nameAdmins.brand).update(issuerName, kit.brand), | ||
E(bankManager).addAsset(denom, issuerName, proposedName, kit), | ||
]); | ||
}, | ||
}, | ||
); | ||
|
||
return { | ||
publicFacet: incapable, | ||
creatorFacet, | ||
}; | ||
}; | ||
harden(prepare); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// @ts-check | ||
|
||
// eslint-disable-next-line import/order | ||
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; | ||
|
||
import { createRequire } from 'module'; | ||
|
||
import { E } from '@endo/far'; | ||
import { buildRootObject as buildBankVatRoot } from '@agoric/vats/src/vat-bank.js'; | ||
import { extractPowers } from '@agoric/vats/src/core/utils.js'; | ||
import { makeScalarMapStore } from '@agoric/store'; | ||
import { | ||
bootAndInstallBundles, | ||
getBundleId, | ||
makeBundleCacheContext, | ||
} from './boot-tools.js'; | ||
import { deployInterchainMints, installMintsContract, permit } from '../src/interchainMints.deploy.js'; | ||
|
||
/** @import { StartedInstanceKit } from '@agoric/zoe/src/zoeService/utils.js'; */ | ||
|
||
/** @type {import('ava').TestFn<Awaited<ReturnType<makeBundleCacheContext>>>} */ | ||
const test = anyTest; | ||
|
||
const nodeRequire = createRequire(import.meta.url); | ||
|
||
const bundleRoots = { | ||
interchainMints: nodeRequire.resolve('../src/interchainMints.js'), | ||
}; | ||
|
||
test.before(async t => (t.context = await makeBundleCacheContext(t))); | ||
|
||
test('add IBC token to vbank', async t => { | ||
const { powers, bundles } = await bootAndInstallBundles(t, bundleRoots); | ||
|
||
const noBridge = undefined; | ||
const baggage = makeScalarMapStore('baggage'); | ||
const bankManager = E( | ||
buildBankVatRoot(undefined, undefined, baggage), | ||
).makeBankManager(noBridge); | ||
powers.produce.bankManager.resolve(bankManager); | ||
|
||
const bundleID = getBundleId(bundles.interchainMints); | ||
const permittedPowers = extractPowers(permit, powers); | ||
await Promise.all([ | ||
installMintsContract(permittedPowers, { | ||
options: { interchainMints: { bundleID } }, | ||
}), | ||
deployInterchainMints(permittedPowers), | ||
]); | ||
|
||
/** @type {StartedInstanceKit<typeof import('../src/interchainMints').prepare>} */ | ||
const kit = await powers.consume.interchainMintsKit; | ||
t.log('interchainMintsKit', kit); | ||
t.is(kit.instance, await powers.instance.consume.interchainMints); | ||
t.truthy(kit.adminFacet); | ||
|
||
const tokens = [ | ||
{ issuerName: 'Asset1', issuerKeyword: 'Asset1', denom: 'ibc/1234567890123456789012345678901234567890123456789012345678901234' }, | ||
{ issuerName: 'Asset2', issuerKeyword: 'Asset2', denom: 'ibc/2234567890123456789012345678901234567890123456789012345678901234' }, | ||
] | ||
|
||
const { agoricNames } = powers.consume; | ||
const { creatorFacet: mintsAdmin } = kit; | ||
await Promise.all(tokens.map(async token => { | ||
await E(mintsAdmin).addAsset({...token, keyword: token.issuerName, decimalPlaces: 6 }); | ||
|
||
const issuerP = E(agoricNames).lookup('issuer', token.issuerName); | ||
const brand = await E(agoricNames).lookup('brand', token.issuerName); | ||
const p1 = await E(issuerP).makeEmptyPurse() | ||
const balance = await E(p1).getCurrentAmount(); | ||
t.log(balance) | ||
t.deepEqual(balance, { brand, value: 0n }); | ||
})); | ||
}); |