PartyLayerDocs
Try Demo

Send Wallet

Send is a passkey-based Canton wallet that exposes the splice-wallet-kernel OpenRPC contract via window.canton. The dApp connection layer is open-sourced as Sigilry; this PartyLayer adapter wraps that contract with the same surface every other Canton wallet uses.

How Send Differs

PropertySendNotes
AuthenticationPasskey (WebAuthn-PRF)Touch ID / Face ID prompt per signature
Provider injectionwindow.cantonCIP-0103 / splice-wallet-kernel native via Sigilry
Networkscanton:mainnet only

Installation

Send is delivered as a browser extension. Direct your users to the Send wallet homepage at sigilry.org for current installation instructions before they can connect.

bash
npm install @partylayer/sdk @partylayer/react @partylayer/adapter-send

With PartyLayerKit auto-discovery there is no further wiring — Send appears in the wallet picker automatically. If you build a custom adapter set, register SendAdapter alongside the others:

tsx
import { createPartyLayer, getBuiltinAdapters, SendAdapter } from '@partylayer/sdk';

const client = createPartyLayer({
  network: 'mainnet',
  appName: 'My dApp',
  adapters: [...getBuiltinAdapters(), new SendAdapter()],
});
ℹ️ Note
Send is already in getBuiltinAdapters(). The example above is only meaningful if you previously passed a manually-curated adapter list.

Connection Flow

tsx
import { useConnect } from '@partylayer/react';

function ConnectWithSend() {
  const { connect, isConnecting } = useConnect();
  return (
    <button
      onClick={() => connect('send')}
      disabled={isConnecting}
    >
      {isConnecting ? 'Connecting…' : 'Connect with Send'}
    </button>
  );
}

End-to-end user experience:

  • User clicks Connect with Send.
  • Send extension shows its Connect to Site? permission prompt.
  • On approval, the OS surfaces a passkey prompt (Touch ID / Face ID).
  • Once unlocked, the SDK receives a session containing partyId, kernelId, and the wallet's public key.
ℹ️ Note
Every signature operation (signMessage, submitTransaction) prompts a fresh passkey unlock — this is by design. Send does not cache passkey approval across calls.

Reading the Ledger

Send proxies the Canton v2 JSON Ledger API through Sigilry's ledgerApi RPC method. Use useLedgerApi for any read-side query:

tsx
import { useLedgerApi } from '@partylayer/react';

function LedgerEndDisplay() {
  const { ledgerApi } = useLedgerApi();
  const [offset, setOffset] = useState<string | null>(null);

  useEffect(() => {
    ledgerApi({
      requestMethod: 'GET',
      resource: '/v2/state/ledger-end',
    }).then(({ response }) => {
      const parsed = JSON.parse(response) as { offset: string };
      setOffset(parsed.offset);
    });
  }, [ledgerApi]);

  return <div>Ledger end: {offset ?? 'loading…'}</div>;
}

For active-contracts queries with eventFormat, see the Wallet Balances guide — Send accepts the same request shape as the other ledger-API-capable adapters.

Token Standard Transfers (CIP-56)

Send signs and submits transactions in a single step via prepareExecuteAndWait. The wallet handles Scan-side coordination, choice context lookup, and passkey signing internally. Adapter consumers call submitTransaction with a JsPrepareSubmissionRequest:

tsx
import { useSubmitTransaction } from '@partylayer/react';

const { submit } = useSubmitTransaction();

await submit({
  signedTx: {
    commandId: crypto.randomUUID(),
    commands: [
      {
        ExerciseCommand: {
          templateId:
            '#splice-api-token-transfer-instruction-v1:Splice.Api.Token.TransferInstructionV1:TransferFactory',
          contractId: factoryCid,
          choice: 'TransferFactory_Transfer',
          choiceArgument: { /* …Scan-derived shape… */ },
        },
      },
    ],
    actAs: [session.partyId],
  },
});

See Token Transfers for the full CIP-56 flow including the Scan /registry/transfer-instruction/v1/transfer-factory endpoint and the choice-context tagged-union shape.

⚠️ Warning
The Send adapter ships the same templateId migration warning as Loop — passing a legacy Amulet_Transfer exercise on Splice.Amulet:Amulet throws an actionable error pointing at this page.

Capability Matrix

  • connect — supported (Sigilry connect RPC + getPrimaryAccount)
  • disconnect — supported
  • restore — supported (silent status probe; no popup on reload)
  • signMessage — supported (passkey-signed)
  • signTransactionnot supported; fused into prepareExecute. Calling it throws CapabilityNotSupportedError pointing at submitTransaction.
  • submitTransaction — supported via prepareExecuteAndWait; receipt populated from tx.payload.updateId.
  • ledgerApi — supported (full Sigilry passthrough; matches Console / Nightly).
  • events — supported; txChanged bridged to PartyLayer tx:status.
  • injected — supported via window.canton with kernel.id guard.

Network Support

This adapter integrates with Send on canton:mainnet.

ℹ️ Note
Demo-app display caveat: the PartyLayer demo at localhost:3000 defaults its network label to devnet. That label reflects the demo's configuration — not the actual network the connected wallet sits on. Send's adapter reads the live network from window.canton.getActiveNetwork() and reports canton:mainnet when Send is active. dApps that ship to production should configure PartyLayerKit with network="mainnet" when targeting Send.

Troubleshooting

  • "Send not detected" — the extension is missing, or window.canton.kernel.id doesn't match Send. The adapter intentionally refuses to claim foreign providers (e.g. another splice-wallet-kernel-compatible extension); install Send and reload.
  • "Connection cancelled" — the user dismissed the passkey prompt or the extension popup. Triggering connect again is safe.
  • "Authentication Failed: Cannot reach authentication server" — Send's backend at auth.cantonwallet.com is unreachable. Check network and retry.
  • "OAuth state mismatch" — stale Send session. Clear cookies for cantonwallet.com and reconnect.
  • Transaction errors with hint "Execute Unknown on Unknown" — legacy Amulet_Transfer exercise on Splice.Amulet:Amulet. Migrate to CIP-56 TransferFactory_Transfer; see Token Transfers.

Security Notes

  • Private keys never leave the extension. Passkey signing happens on the user's device through WebAuthn-PRF; PartyLayer never touches the underlying key material.
  • Session JWT is held by the extension. The adapter receives an access token in status.session.accessToken for the lifetime of the connection; PartyLayer's session-persistence layer encrypts state at rest in the dApp's configured storage.
  • Registry-driven detection guard. Every Send adapter call verifies the live window.canton provider against the registry's providerDetection rules before forwarding. If a different splice-wallet-kernel-compatible extension grabs the global, Send adapter cleanly returns not installed and yields to the matching adapter rather than acting on a foreign provider.

References

PreviousWallets & AdaptersNextCIP-0103 Provider