Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/covenant-gov/pacto-app/llms.txt

Use this file to discover all available pages before exploring further.

Pacto’s WalletBar embeds two structured message types directly inside 1:1 DM threads. wallet_tx_request lets one peer ask another to send a payment; wallet_tx_announcement is posted only after a successful on-chain receipt and confirms that a transfer has settled. Both types are carried as plain DM content — a single JSON object — and rendered as interactive cards by the DM message router. They are distinct from squad and network announcement messages and share no schema with those flows.

Versioning

Every wallet DM payload includes a top-level version integer. The current specification is version 1.
  • Clients must reject (and treat as plain text) any payload with a version value greater than the highest version they implement.
  • A missing version, or a version that is not 1 for these types, means the message is not rendered as a wallet card.

Shared enums and field types

network

Must match a SupportedChainId value from src/lib/wallet/chains.ts. The wire value anvil is accepted as an alias for local.
ValueMeaning
mainnetEthereum mainnet
arbitrumArbitrum One
optimismOP Mainnet
gnosisGnosis Chain
sepoliaEthereum Sepolia testnet
localLocal Anvil devnet (anvil is also accepted)

asset

Must match a WalletAssetCode value from src/lib/wallet/assets.ts.
ValueMeaning
ETHNative ETH
USDCERC-20 USDC
USDTERC-20 USDT

amount

The amount field is always a decimal string in human units (e.g. "0.1", "12.345678"), never a JSON number. This avoids floating-point drift and matches the WalletBar backend’s decimal string input.
  • Non-empty after trimming whitespace.
  • Must match the regex: ^(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$ — no sign character, no scientific notation; a leading-dot fraction such as ".00001" is valid.
  • Maximum 32 characters to bound message size.

request_id

An opaque string, unique per payment request. Implementations should generate a UUID (RFC 4122 string). The request_id links a wallet_tx_announcement back to the wallet_tx_request that triggered it.

tx_hash

0x followed by exactly 64 lowercase hexadecimal characters (EIP-119 format).

wallet_tx_request (v1)

A wallet_tx_request is posted by the user who creates the payment request. The counterparty — the intended payer — is inferred from the DM thread context; parsers should not rely on a duplicate identity field.
FieldTypeRequiredDescription
versionnumberyesMust be 1.
typestringyesMust be "wallet_tx_request".
request_idstringyesUUID string. Unique per request; used to link announcements.
networkstringyesOne of the network enum values above.
assetstringyesOne of the asset enum values above.
amountstringyesHuman decimal amount string (see amount).
from_evm_addressstringyesPosting user’s active EVM signer address: 0x + 40 hex characters.
created_at_msnumbernoUnix milliseconds when the client created the request. Used for display ordering.

Example

{
  "version": 1,
  "type": "wallet_tx_request",
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "network": "sepolia",
  "asset": "ETH",
  "amount": "0.05",
  "from_evm_address": "0x1111111111111111111111111111111111111111",
  "created_at_ms": 1710000000000
}

wallet_tx_announcement (v1)

A wallet_tx_announcement is posted only after a successful on-chain transaction receipt. The presence of a valid announcement in the thread implies confirmed success — there is no separate status field in v1. See TRANSACTION_LIFECYCLE.md for the full confirmation flow.
FieldTypeRequiredDescription
versionnumberyesMust be 1.
typestringyesMust be "wallet_tx_announcement".
networkstringyesOne of the network enum values above.
assetstringyesOne of the asset enum values above.
amountstringyesHuman decimal amount string (should match the amount sent on-chain).
tx_hashstringyes0x + 64 hex characters.
from_npubstringyesSender’s bech32 npub1… string.
to_npubstringyesRecipient’s bech32 npub1… string.
from_evm_addressstringyesEVM account that signed the transfer: 0x + 40 hex (should match the on-chain from).
request_idstringnoIf present, ties this transfer to a prior wallet_tx_request. Announcements without request_id do not mark any request card as paid.
block_numberstringnoDecimal string block number from the transaction receipt. Stored as a string to avoid JavaScript integer precision issues on very large block numbers.

Example

{
  "version": 1,
  "type": "wallet_tx_announcement",
  "network": "sepolia",
  "asset": "USDC",
  "amount": "10.00",
  "tx_hash": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
  "from_npub": "npub1sender...",
  "to_npub": "npub1recipient...",
  "from_evm_address": "0x1111111111111111111111111111111111111111",
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "block_number": "12345678"
}

Request card states

A wallet_tx_request card in the DM thread can be in one of several states. Accepted/declined state is stored locally; fulfilled state is derived from the thread.
StateHow it is determined
PendingNo accepted, declined, or matching announcement found yet.
AcceptedPersisted in a per-account (npub-scoped) list of accepted DM message ids.
DeclinedPersisted in a per-account list of declined DM message ids. Decline is local-only — no DM is sent to the requester. The requester sees only the pending state until they pay or decline on their own side.
PaidDerived from the thread: any wallet_tx_announcement in the same thread whose optional request_id matches this request’s request_id causes the request card to show “Paid” for all participants. Updates automatically as new messages arrive.
A message id cannot be simultaneously in the accepted and declined lists. Toggling from one state to the other removes the id from the previous list. Appending is a no-op if the id is already present (idempotent).

Wire format rules

  1. content is a single JSON object — not an array, not a double-encoded string.
  2. Parsing: trim leading and trailing whitespace → JSON.parse the whole string → validate type, version, and all required fields.
  3. Unknown or invalid type / version: treat the message as plain text. Do not render a wallet card.
  4. Size: keep payloads compact (no unnecessary whitespace in production). Typical v1 payloads are hundreds of bytes UTF-8. Stay well under 2 KiB to remain safe for Nostr relay limits and the Pacto backend.
Do not embed large memos, blobs, or base64-encoded data in v1 wallet DM payloads. If extended metadata is needed in a future version, add a new field with a strict maximum length and increment the version number.

TypeScript implementations

All parsing, formatting, and fulfillment-detection logic lives in src/lib/wallet/dm-messages.ts:
// Parse inbound messages
parseWalletTxRequest(content: string): WalletTxRequest | null
parseWalletTxAnnouncement(content: string): WalletTxAnnouncement | null

// Format outbound messages
formatWalletTxRequest(params: WalletTxRequestParams): string
formatWalletTxAnnouncement(params: WalletTxAnnouncementParams): string

// Derive fulfilled request ids from a thread's messages
getFulfilledWalletRequestIdsFromMessages(messages: ChatMessage[]): Set<string>
Tests are in src/lib/wallet/dm-messages.test.ts (run with npm run test). UI components rendering these cards are WalletTxRequestCard and WalletTxAnnouncementCard in src/components/wallet/, mounted via DmMessageRouter.svelte.

Build docs developers (and LLMs) love