Channels & Setup Requirements

Notes travel over channels. Before you can transfer token X to Bob, three things must exist: Bob's registration, a channel from you to Bob, and a subchannel for token X inside that channel. discoverRequirement tells you which of these is still missing.

Snippets assume transfers, account and provider from Getting Started.

import { SetupRequirement } from "@starkware-libs/starknet-privacy-sdk"

// recipient is a hex string; token must be a bigint.
const requirement = await transfers.discoverRequirement(bob, BigInt(tokenAddress))
SetupRequirement Meaning Fix
Register Recipient has no viewing key on-chain They must register - you cannot do it for them
SetupChannel No channel from you to the recipient builder.setup(recipient)
SetupToken Channel exists, this token's subchannel does not t.setup(recipient)
Ready Transfer will go through Nothing

Opening channels explicitly

setup() on the main builder opens the channel; setup() on the token builder opens the token subchannel. Bundle them with the transfer they unblock:

const provingBlockId = (await provider.getBlockNumber()) - 10
const builder = transfers.build({ autoDiscover: { notes: "refresh" } })
builder.surplusTo(account.address)

if (requirement === SetupRequirement.SetupChannel) {
  builder.setup(bob) // channel
  builder.with(tokenAddress, (t) => t.setup(bob)) // + token subchannel
} else if (requirement === SetupRequirement.SetupToken) {
  builder.with(tokenAddress, (t) => t.setup(bob)) // subchannel only
}

builder.with(tokenAddress, (t) => t.transfer({ recipient: bob, amount: 50n }))
const { callAndProof } = await builder.execute({ provingBlockId })
// ... submission tail as usual

autoSetup

build({ autoSetup: true }) adds the missing setup actions automatically - convenient for single-recipient flows. For batches, prefer the explicit pattern above: autoSetup decides from the local registry, and stale registry data makes it re-open already-open channels, which fails on-chain.

Things to notice

  • The caller must be registered before calling discoverRequirement - it derives your channel keys from your on-chain viewing key. If you are not registered it throws, with an unhelpful message; match on "not registered" / "viewing key" substrings to distinguish it from RPC errors.
  • SetupRequirement.Register is a hard stop: only the recipient can publish their own viewing key. Show "ask them to register" UX.
  • Opening a channel is a proved pool action like any other - it rides the same build/execute/submit cycle, alone or alongside transfers.

Next: Discovering Notes - reading your private balance.