Skip to content
Payvol

Payvol Observe

See your own money arrive. Nobody else has to.

Observe turns updates from your own participant into records you can match. No relay, no public index, and no third party watching your payments in order to tell you about them.

Turn an update into a record Read the guide

@payvol/core/draft02your nodeno relay

What it does

On Canton, the answer is not sitting where you would look for it.

Other networks let you scan a public index for a reference and find your payment. Canton has no such index, by design. And once a transfer is accepted, the identifier is no longer on an active contract at all: it survives in the participant's update history, which is a different thing to read and a different thing to keep.

Read the stream, not the contracts

Where it actually is

Before the accept, the identifier travels with the transfer. After it, the contract is gone and only the update history still carries it. Reading active contracts finds nothing, which looks exactly like a payment that never arrived.

Evidence has a lifetime

Write before the window closes

Update history is pruned, so a record has to be written while the window is open. An adapter that starts after the relevant updates were pruned reports missing evidence, not an unmatched payment. Those mean opposite things to an operator: one says nobody has looked, the other says nothing arrived.

Three parties can read it, and no more

Who sees a settlement

For Canton Coin, measured: the payer, the payee and the instrument registry can read the identifier, and a party that took no part reads nothing. The registry is a signatory of the template, so this is structural rather than a choice of ours. Other registries can make further parties of their own able to see a transfer; the reader set belongs to the instrument, not to Payvol.

Nobody stands between you and your ledger

No middle

A relay would have to see every payment it forwards. An index would have to be told about yours. On Canton you already have an authorised view of everything you are party to, so Observe reads that and adds nothing in between.

How it works

Read, resolve, keep.

01

Read

  • Your code fetches updates from your participant's Ledger API
  • Pass each one to recordFromUpdate with your trust configuration
  • A response missing a required field comes back naming it, not dropped

02

Resolve

  • A transfer offer is recorded as offered, never as paid
  • resolveOffer reads the accept, reject or withdraw that follows
  • Only an accepted transfer becomes settled, with its effective time

03

Keep

  • Store each record before update history is pruned
  • Check with windowCovers that your history reaches the deadline
  • Hand settled records to Reconcile with toSettlementObservation

Scope

What Observe leaves to you.

It does not run a node for you Observe works on updates from a participant you already operate or already have access to. Running Canton infrastructure stays with you and your validator.
It does not index the network Only what your participant is party to. There is no crawler, no aggregate view, and no way to ask it about somebody else's payment.
It does not decide what a sighting means Observe records what arrived. Whether that satisfies an invoice is the next module's question, and a deliberately separate one.
It does not produce portable proof A record says this participant saw this settlement at this time. It is evidence for you and your auditor, not a certificate a third party can verify on its own.

Get started

Turn an update into a record.

Install the package, fetch updates from your participant, and pass each one in with your trust configuration. A settled record is what Reconcile weighs.

npm i @payvol/core version 0.4.0 TypeScript
import { recordFromUpdate, resolveOffer, windowCovers } from '@payvol/core/draft02';

// an update your participant returned; `input.trust` is your configuration
const offer = recordFromUpdate(offerUpdate, input);

if (!offer.ok) {
  offer.missing; // the fields it could not read, rather than a dropped sighting
} else {
  offer.record.lifecycle; // 'offered': creating a transfer offer is not a payment

  const outcome = resolveOffer(offer.record, acceptUpdate, input);
  if (outcome.ok) outcome.record.lifecycle; // 'settled', 'rejected' or 'withdrawn'
}

// false when your history no longer reaches back to the deadline
windowCovers(coverage, deadline);