Instructions for deploying a contract to a local agoric testnet and granting storage node powers. Most of the content here derives from Agoric Office hours, Dec. 14

Why

In order for an Agoric contract to publish data without having the user pay tx fees, the use of a chain storage node is required. Chain storage node is an object that can be passed to a contract by submitting a proposal using cosmos-sdk level txs [swingset.CoreEval]. If passed, the proposal runs a script that gets access to the storage node. The pattern described in the following steps uses this script to start an instance of a contract, passing the storage node as a privateArg to the contract

Let’s split the steps in 4 parts:

  1. Code setup: write the proposal code needed for obtaining the storage node
  2. Networking: start a local testnet and use agd to request access using the code
  3. Pushing data: push content to the storage node from an agoric contract
  4. Consuming data: read storageNode data from a web application

Set up contract and proposal

Before we can use the storage node, we have to create a core-eval proposal that will request access to the storage node. While instantiating the smart contract in the proposal isn’t the only way to pass the storageNode to a contract, we recommend doing it this way in order to take advantage of the privateArgs functionality

/**
 * @type {ContractStartFn}
 * @param {ZCF} zcf
 * @param {{storageNode: StorageNode, marshaller: Marshaller}} privateArgs
 */
const start = async (zcf, privateArgs) => {

  const state = {
    privateArgs,
  }

  [...]
}
{
  "consume": {
    "board": true,
    "chainStorage": true,
    "zoe": true
  },
  "produce": {
    "bakeSaleKit": true
  },
  "instance": {
    "produce": {
      "bakeSale": true
    }
  }
}

(note that at this point the bundleID from contractInfo is not yet known)

/**
 * @file
 *
 * This is a script for use with swingset.CoreEval.
 *
 * It's a script, not a module, so we can't use `import`.
 * But E, Far, etc. are in scope, provided by the
 * `new Compartment(globals)` call in
 * `bridgeCoreEval()` in packages/vats/src/core/chain-behaviors.js
 */
// @ts-check
// uncomment the following line to typecheck, for example, in vs-code.
// import { E } from '@endo/far';

const contractInfo = {
  storagePath: 'bakeSales',
  instanceName: 'bakeSaleAgent',

  // see discussion of publish-bundle and bundleID
  // from Dec 14 office hours
  // <https://github.com/Agoric/agoric-sdk/issues/6454#issuecomment-1351949397>
  bundleID:
    'b1-392ae6656ae68bcfd8bd77a2a100ad4076e8b888808274796cce4c148fcb99d1f8b6c2f946bd6bc3e3c26c54c671c788890f3e9780464354fb2d30916e7be896',
};

const fail = reason => {
  throw reason;
};

/**
 * Execute a proposal to start a contract that publishes bake sales.
 *
 * See also:
 * BLDer DAO governance using arbitrary code injection: swingset.CoreEval
 * <https://community.agoric.com/t/blder-dao-governance-using-arbitrary-code-injection-swingset-coreeval/99>
 *
 * @param {BootstrapPowers} powers see the `behavior(powers)` call
 *   in `bridgeCoreEval()`
 */
const executeProposal = async powers => {
  // Destructure the powers that we use.
  // See also bakeSale-permit.json
  const {
    consume: { board, chainStorage, zoe },
    // @ts-expect-error bakeSaleKit isn't declared in vats/src/core/types.js
    produce: { bakeSaleKit },
    instance: {
      // @ts-expect-error bakeSaleKit isn't declared in vats/src/core/types.js
      produce: { [contractInfo.instanceName]: produceInstance },
    },
  } = powers;

  const chainStorageSettled =
    (await chainStorage) || fail(Error('no chainStorage - sim chain?'));

  const storageNode = E(chainStorageSettled).makeChildNode(
    contractInfo.storagePath,
  );
  const marshaller = await E(board).getReadonlyMarshaller();

  const privateArgs = harden({ storageNode, marshaller });

  const installation = await E(zoe).installBundleID(contractInfo.bundleID);
  const noIssuers = harden({});
  const noTerms = harden({});
  const facets = await E(zoe).startInstance(
    installation,
    noIssuers,
    noTerms,
    privateArgs,
  );

  // Share instance widely via E(agoricNames).lookup('instance', 'bakeSaleAgent')
  produceInstance.resolve(facets.instance);

  // Share the publicFacet, creatorFacet, and adminFacet in the bootstrap space
  // for use by other CoreEval behaviors.
  bakeSaleKit.resolve(facets);
};
harden(executeProposal);

// "export" the function as the script completion value
executeProposal;