# Far(), Remotable, and Marshaling

Let's look more closely at an eventual send between vats:

counter remote presence

In the exporting vat, we'll take the makeCounter Hardened JavaScript example and refine it to make remotable counters by marking them with Far:

const makeCounter = () => {
  let count = 0;
  return Far('counter', {
    incr: () => (count += 1),
    decr: () => (count -= 1),
  });
};

const publicFacet = Far('makeCounter', { makeCounter });
assert(passStyleOf(publicFacet) === 'remotable');

# Marshaling by Copy or by Presence

Recall that the first step in an eventual send is to marshal the method name and arguments. Marshalling (opens new window) is transforming a data structure into a format suitable for storage or transmission. The @endo/marshal (opens new window) package uses JSON (opens new window), but it can handle Javascript values that cannot be expressed directly as JSON, such as undefined and BigInts.

const m = makeMarshal(undefined, undefined, smallCaps);

const stuff = harden([1, 2, 3n, undefined, NaN]);
const capData = m.toCapData(stuff);
t.deepEqual(m.fromCapData(capData), stuff);

Also, while many forms of data are copied between vats, remotables are marshalled so that they become remote presences when unmarshaled. Another vat can then make and use the exported counters:

const counter = E(publicFacet).makeCounter();
const n = await E(counter).incr();
assert(n === 1);

# Pass Styles and harden

Calls to remote presences must only contain passable arguments and return passable results. There are three kinds of passables:

  • Remotables: objects with methods that can be called using E() eventual send notation.
  • Pass-by-copy data, such as numbers or hardened structures.
  • Promises for passables.

Every object exported from a smart contract, such as publicFacet or creatorFacet, must be passable. All objects used in your contract's external API must be passable. All passables must be hardened.

Consider what might happen if we had a remote item and we did not harden some pass-by-copy data that we passed to it:

let amount1 = { brand: brand1, value: 10n };
await E(item).setPrice(amount1); // Throws an error, but let's imagine it doesn't.
amount1.value = 20n;

Now amount1 is supposedly both in the local and the remote vat, but the value is 20n in the local vat but 10n in the remote vat. (Worse: the remote vat might be the same as the local vat.) Requiring harden() for pass-by-copy data leads to behavior across vats that is straightforward to reason about.

# passStyleOf API

import { passStyleOf } from '@endo/pass-style';

passStyleOf(passable)

  • passable {Passable}
  • Returns: {PassStyle}

A Passable is a hardened value that may be marshalled. It is classified as one of the following PassStyle values:

  • Atomic pass-by-copy primitives ("undefined" | "null" | "boolean" | "number" | "bigint" | "string" | "symbol").
  • Pass-by-copy containers that contain other Passables ("copyArray" | "copyRecord").
  • Special cases, which also contain other Passables ("error").
  • So-called PassableCap leafs ("remotable" | "promise").

Check `passStyleOf` when handling untrusted structured data

Just as you would use typeof to check that an argument is a string or number, use passStyleOf when you expect, say, a copyRecord; this prevents malicious clients from playing tricks with cyclic data etc.

# Far() API

import { Far } from '@endo/far';

Far(farName, object-with-methods)

  • farName { String }
  • object-with-methods { Object } [remotable={}]
  • Returns: A Remotable object.

The farName parameter gives the Remotable an interface name for debugging purposes, which only shows up when logged through the console, for example with console.log.

The object-with-methods parameter should be an object whose properties are the functions serving as the object's methods.

The Far() function marks an object as remotable. Far() also:

  • Hardens the object.
  • Checks that all property values are functions and throws an error otherwise.
    • Accessors (i.e., get() and set()) are not allowed.
  • Records the object's interface name.

Avoid accidental exports

If an object should never be exposed to other vats, you should make it a point not to use Far() on it. If an object is not marked as a remotable but is accidentally exposed, an error is thrown. This prevents any vulnerability from such accidental exposure.