Visual Sign Protocol Documentation
This document provides specifications for the Visual Sign Protocol (VSP), a structured format for displaying transaction details to users for approval. The VSP is designed to present meaningful, human-readable information about operations requiring signatures.
Important Concepts
Non-Canonical Format
The SignablePayload JSON format is NOT canonical. It should be treated by signers as an opaque string field. While we maintain deterministic ordering (currently alphabetical) for debugging consistency and cross-implementation compatibility, this is an implementation detail that may change. Signers should not parse or depend on the specific JSON structure or field ordering.
Display Requirements for Implementers
Display elements (wallets, signing interfaces) are responsible for:
- Parsing and interpreting the SignablePayload to determine what to show users
- Ensuring all fields are displayed - Every field in the payload MUST be shown to the user
- Minimum display guarantee - At the very least, the
FallbackTextfor each field must be displayed - Making display decisions - The display element decides how to render fields (layout, styling, grouping)
- Respecting user preferences - Honor accessibility settings and display preferences
Notes
- We don't use v1 text field types, but they're around for backwards compatibility for now
- AnnotatedFields are a layer on top of SignablePayload field for our wallet to provide more context, it's not in scope of the SignablePayload, it's still in structs but we'll consider removing in future
- Field ordering is deterministic but not guaranteed to be alphabetical in future versions - see Deterministic Ordering Documentation
SignablePayload
A SignablePayload is the core structure that defines what is displayed to the user during the signing process. It contains metadata about the transaction and a collection of fields representing the transaction details.
Structure
Payload Components
| Field | Type | Description |
|---|---|---|
| Version | String | Protocol version |
| Title | String | Primary title for the operation |
| Subtitle | String (optional) | Secondary descriptive text |
| PayloadType | String | Identifier for the SignablePayload (ex: Withdrawal, Swap, etc) |
| Fields | Array of SignablePayloadField | The fields containing transaction details |
| EndorsedParamsDigest | String (optional) | Digest of endorsed parameters |
Field Types
The Visual Sign Protocol supports various field types to represent different kinds of data.
Common Field Structure
All field types include these common properties:
| Field | Type | Description |
|---|---|---|
| Label | String | Field label shown to the user |
| FallbackText | String | Plain text representation (for limited clients) |
| Type | String | Type identifier for the field |
Specific Field Types
Text Fields
Address Fields
Amount Fields
Amount fields are user friendly ways to display the value being transferred
Number Fields
Divider Fields
Divider fields are UI elements to split the UI on. This is used for clarity and to allow the UI to keep views in separate pages if needed.
Layout Fields
We have additional layout fields for two different use cases - one for creating preview elements, where a condensed view can be optionally expanded by the user.
Endorsed Params
The Endorsed Params feature allows passing additional parameters for the visualizer to interpret and potentially use for transforming the raw transaction to make meaningful display for user in a deterministic way.
Structure
Endorsed parameters are cryptographically bound to the SignablePayload through the EndorsedParamsDigest field, which contains a hash of all endorsed parameters. These are presented as an example - and may be chain or wallet-specific.
Usage
-
Transaction Construction: The visualizer service collects all necessary parameters for constructing a valid transaction.
-
Parameter Separation: Parameters are separated into:
- User-facing fields (included in the
Fieldsarray) - Hidden parameters (included in
EndorsedParams)
- User-facing fields (included in the
-
Digest Creation: The service computes a hash of the endorsed parameters:
EndorsedParamsDigest = sha256(serialize(EndorsedParams)) -
Payload Assembly: The digest is included in the SignablePayload, cryptographically binding the hidden parameters to the displayed information.
Security Considerations
- The signer must verify that the
EndorsedParamsDigestmatches the endorsed parameters used for transaction construction - Parameters that affect user funds or authorization should generally be displayed rather than hidden
- Implementations should document which parameters are endorsed vs. displayed to ensure transparency
Example Use Cases
- Network fees and gas parameters
- Technical identifiers (contract addresses, chain IDs)
- Implementation-specific parameters (nonces, replay protection values)
- Method signatures and serialized call data
Example Fixtures
Below are screenshots corresponding to specific fixture examples:
Bitcoin Withdraw

ERC20 Token Withdraw

Solana Withdraw with Expandable Preview Layouts
Expanding fields, these are expected to be shown when one of the expandable fields is clicked
Implementation Considerations
Field Ordering: Fields should be displayed in the order they appear in the Fields array Version Compatibility: Clients should check the Version field to ensure they can properly render the payload Fallback Rendering: If a client doesn't understand a field type, it should fall back to displaying the FallbackText Security: Implementations should validate the ReplayProtection and EndorsedParamsDigest values
Extending SignablePayloadField Types
The VisualSign Protocol is designed to be extensible, allowing developers to safely add new field types while maintaining backward compatibility and ensuring data integrity.
Architecture Overview
The field serialization system uses a trait-based architecture with compile-time and runtime verification that provides multiple layers of protection against incomplete implementations:
Key Features
- โ๏ธ Compile-Time Enforcement:
DeterministicOrderingtrait ensures types implement deterministic serialization - ๐ Runtime Verification: Automatically verifies all expected fields are present during serialization
- ๐ Deterministic Ordering: Fields are automatically sorted deterministically (currently alphabetically) for consistent output
- ๐จ Error Detection: Missing or unexpected fields cause immediate serialization failure with detailed error messages
- ๐งช Test-Driven: Comprehensive test suite proves the verification system works correctly
- ๐ Extensible: Adding new field types is straightforward and safe
How to Add New Field Types
1. Define the Field Structure
First, create the data structure for your new field type:
2. Add the Enum Variant
Add your new variant to the SignablePayloadField enum:
3. Implement Serialization Logic
Add your field to both required methods in the FieldSerializer implementation:
4. Update Helper Methods
Add your variant to the existing helper methods:
5. Implement DeterministicOrdering Trait
Critical: Your new field type must implement the DeterministicOrdering trait to be usable in contexts requiring deterministic serialization:
// This is already implemented for SignablePayloadField, but if creating a new top-level type:
Without this implementation, the type cannot be used in functions requiring deterministic ordering, and compilation will fail with a clear error message.
Runtime Verification System
The system automatically verifies field completeness during serialization:
// โ
Successful serialization - all fields present
let currency_field = Currency ;
let json = to_string?;
// Result: {"Currency":{"CurrencyCode":"USD","Symbol":"$"},"FallbackText":"USD ($)","Label":"Payment Currency","Type":"currency"}
If you forget to serialize a field or have mismatched expectations:
// โ This would fail with detailed error message:
// "Missing expected field 'Currency'. Expected: ["Currency", "FallbackText", "Label", "Type"], Actual: ["FallbackText", "Label", "Type"]"
Comprehensive Testing
The system includes extensive tests that prove the verification works:
Benefits of This Approach
-
๐ก๏ธ Defense in Depth:
- Compile-time: Exhaustive pattern matching ensures all variants are handled,
DeterministicOrderingtrait enforces proper implementation - Runtime: Field verification catches missing/incorrect fields
- Test-time: Comprehensive tests prove the system works
- Compile-time: Exhaustive pattern matching ensures all variants are handled,
-
๐ Clear Error Messages:
- Missing
DeterministicOrderingtrait causes compile-time error with clear message - Missing fields are immediately identified with specific field names at runtime
- Unexpected fields are caught and reported
- Detailed error context helps debugging
- Missing
-
๐ Consistent Output:
- All fields automatically ordered deterministically (currently alphabetically)
- Consistent JSON structure across all field types
- Backward compatibility maintained
-
๐ Easy Extension:
- Adding new field types requires minimal code changes
- Macro-based approach reduces boilerplate
- Compile-time checking makes it impossible to miss required implementation steps
Migration from Legacy Approach
The new system maintains full backward compatibility while adding safety:
- All existing field types work unchanged
- JSON output format is identical
- No breaking changes to API
- Existing tests continue to pass
Best Practices
- Always test new field types with the provided verification tests
- Use descriptive field names that clearly indicate their purpose
- Follow the naming convention of existing field types
- Document new field types in this README
- Consider backward compatibility when designing new field structures
This extensible architecture transforms field extension from a error-prone manual process into a safe, verified, and automatic system that catches mistakes before they can cause issues in production.

