UNPKG

cloutcontracts

Version:

CCS Agoric DAPP Implementation

177 lines (140 loc) 6.09 kB
// @ts-check /* global process */ // Agoric Dapp api deployment script import fs from 'fs'; import { E } from '@agoric/eventual-send'; import '@agoric/zoe/exported'; import installationConstants from '../ui/public/conf/installationConstants'; // deploy.js runs in an ephemeral Node.js outside of swingset. The // spawner runs within ag-solo, so is persistent. Once the deploy.js // script ends, connections to any of its objects are severed. /** * @typedef {Object} DeployPowers The special powers that `agoric deploy` gives us * @property {(path: string) => Promise<{ moduleFormat: string, source: string }>} bundleSource * @property {(path: string) => string} pathResolve * @property {(path: string, opts?: any) => Promise<any>} installUnsafePlugin * * @typedef {Object} Board * @property {(id: string) => any} getValue * @property {(value: any) => string} getId * @property {(value: any) => boolean} has * @property {() => [string]} ids */ const API_PORT = process.env.API_PORT || '7210'; /** * @typedef {{ zoe: ZoeService, board: Board, spawner, wallet, uploads, http }} Home * @param {Promise<Home>} homePromise * A promise for the references available from REPL home * @param {DeployPowers} powers */ export default async function deployApi( homePromise, { bundleSource, pathResolve }, ) { // Let's wait for the promise to resolve. const home = await homePromise; // Unpack the references. const { // *** LOCAL REFERENCES *** // The spawner persistently runs scripts within ag-solo, off-chain. spawner, // *** ON-CHAIN REFERENCES *** // Zoe lives on-chain and is shared by everyone who has access to // the chain. In this demo, that's just you, but on our testnet, // everyone has access to the same Zoe. zoe, // The http service allows registered handlers that are executed as part of // the ag-solo web server. http, // This is a scratch pad specific to the current ag-solo and inaccessible // from the chain. uploads: scratch, // The board is an on-chain object that is used to make private // on-chain objects public to everyone else on-chain. These // objects get assigned a unique string id. Given the id, other // people can access the object through the board. Ids and values // have a one-to-one bidirectional mapping. If a value is added a // second time, the original id is just returned. board, } = home; // To get the backend of our dapp up and running, first we need to // grab the installation that our contract deploy script put // in the public board. const { INSTALLATION_BOARD_ID, CONTRACT_NAME } = installationConstants; const installation = await E(board).getValue(INSTALLATION_BOARD_ID); // Second, we can use the installation to create a new instance of // our contract code on Zoe. A contract instance is a running // program that can take offers through Zoe. Making an instance will // give us a `creatorFacet` that will let us make invitations we can // send to users. const { creatorFacet, instance, publicFacet } = await E(zoe).startInstance( installation, ); console.log('- SUCCESS! contract instance is running on Zoe'); console.log('Retrieving Board IDs for issuers and brands'); const invitationIssuerP = E(zoe).getInvitationIssuer(); const invitationBrandP = E(invitationIssuerP).getBrand(); const tokenIssuer = await E(publicFacet).getTokenIssuer(); // Set up our token to be used by other dapps (like our card store). E(scratch).set('faucetTokenIssuer', tokenIssuer); const tokenBrand = await E(tokenIssuer).getBrand(); const invitationIssuer = await invitationIssuerP; const [ INSTANCE_BOARD_ID, TOKEN_BRAND_BOARD_ID, TOKEN_ISSUER_BOARD_ID, ] = await Promise.all([ E(board).getId(instance), E(board).getId(tokenBrand), E(board).getId(tokenIssuer), ]); console.log(`-- Contract Name: ${CONTRACT_NAME}`); console.log(`-- INSTANCE_BOARD_ID: ${INSTANCE_BOARD_ID}`); console.log(`-- TOKEN_ISSUER_BOARD_ID: ${TOKEN_ISSUER_BOARD_ID}`); console.log(`-- TOKEN_BRAND_BOARD_ID: ${TOKEN_BRAND_BOARD_ID}`); // We want the handler to run persistently. (Scripts such as this // deploy.js script are ephemeral and all connections to objects // within this script are severed when the script is done running.) const installURLHandler = async () => { // To run the URL handler persistently, we must use the spawner to run // the code on our ag-solo even after the deploy script exits. // Bundle up the handler code const bundle = await bundleSource(pathResolve('./src/handler.js')); // Install it on the spawner const handlerInstall = E(spawner).install(bundle); // Spawn the installed code to create an URL handler. const handler = E(handlerInstall).spawn({ creatorFacet, board, http, invitationIssuer, }); // Have our ag-solo wait on ws://localhost:8000/api/fungible-faucet for // websocket connections. await E(http).registerURLHandler(handler, '/api/fungible-faucet'); }; await installURLHandler(); const invitationBrand = await invitationBrandP; const INVITE_BRAND_BOARD_ID = await E(board).getId(invitationBrand); const API_URL = process.env.API_URL || `http://127.0.0.1:${API_PORT || 7210}`; // Re-save the constants somewhere where the UI and api can find it. const dappConstants = { INSTANCE_BOARD_ID, INSTALLATION_BOARD_ID, INVITE_BRAND_BOARD_ID, // BRIDGE_URL: 'agoric-lookup:https://local.agoric.com?append=/bridge', brandBoardIds: { Token: TOKEN_BRAND_BOARD_ID, }, issuerBoardIds: { Token: TOKEN_ISSUER_BOARD_ID }, BRIDGE_URL: 'http://127.0.0.1:7210', API_URL, }; const defaultsFile = pathResolve(`../ui/public/conf/defaults.js`); console.log('writing', defaultsFile); const defaultsContents = `\ // GENERATED FROM ${pathResolve('./deploy.js')} export default ${JSON.stringify(dappConstants, undefined, 2)}; `; await fs.promises.writeFile(defaultsFile, defaultsContents); }