Skip to content

Commit

Permalink
feat: interchainMints contract subsumes mintHolder
Browse files Browse the repository at this point in the history
and publishInterchainAssetFromBank() from addAssetToVault.js
  • Loading branch information
dckc committed Apr 11, 2024
1 parent 7bec0e0 commit 111afb3
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 0 deletions.
61 changes: 61 additions & 0 deletions contract/src/interchainMints.deploy.js
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 },
}
};
117 changes: 117 additions & 0 deletions contract/src/interchainMints.js
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);
74 changes: 74 additions & 0 deletions contract/test/test-interchainMints.js
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 });
}));
});

0 comments on commit 111afb3

Please sign in to comment.