> ## Documentation Index
> Fetch the complete documentation index at: https://visualsign.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Library Integration

> Embed the VisualSign parser directly in your application

Embed VisualSign parsing directly in your application without network calls. The parser is available as Rust crates that can be compiled into your application.

## When to use library integration

Library integration is ideal when:

* You're building a Rust application (CLI tool, backend service)
* Parsing should happen without external service dependencies
* Network latency is unacceptable
* You want the smallest deployment footprint

## Trade-offs

| Advantage | Consideration |
| - | - |
| No network latency | No attestation (trust your process) |
| Works offline | Larger binary size |
| Simpler deployment | Updates require app releases |

## Rust integration

### Dependencies

Add the required crates to your `Cargo.toml`. The `parser_app` crate provides the full parser registry with all chain support:

```toml theme={null}
[dependencies]
parser_app = { git = "https://github.com/anchorageoss/visualsign-parser", package = "parser_app" }
visualsign = { git = "https://github.com/anchorageoss/visualsign-parser", package = "visualsign" }
generated = { git = "https://github.com/anchorageoss/visualsign-parser", package = "generated" }
```

### Basic usage

```rust theme={null}
use parser_app::config::ParserConfig;
use parser_app::registry::create_registry;
use visualsign::registry::Chain;
use visualsign::vsptrait::{VisualSignOptions, DeveloperConfig};
use generated::parser::{ChainMetadata, EthereumMetadata, chain_metadata::Metadata};

fn main() {
    // Create the parser registry (includes all supported chains).
    // The config picks the ABI trust posture: `accept_unsigned()` registers
    // caller-supplied ABI mappings that carry no signature. See "ABI trust
    // posture" below for the stricter alternative.
    let config = ParserConfig::accept_unsigned();
    let registry = create_registry(&config);

    // Raw transaction hex (with or without 0x prefix)
    // This is a complete EIP-1559 transaction
    let raw_tx = "0x02f901..."; // Your full transaction hex

    // Configure parsing options
    let options = VisualSignOptions {
        decode_transfers: true,
        metadata: Some(ChainMetadata {
            metadata: Some(Metadata::Ethereum(EthereumMetadata {
                network_id: Some("ETHEREUM_MAINNET".to_string()), // Ethereum Mainnet
                abi_mappings: Default::default(),
            })),
        }),
        developer_config: Some(DeveloperConfig {
            allow_signed_transactions: true,
        }),
        ..Default::default()
    };

    // Parse the transaction
    match registry.convert_transaction(&Chain::Ethereum, raw_tx, options) {
        Ok(payload) => {
            println!("Title: {}", payload.title);
            println!("Fields: {:?}", payload.fields);
        }
        Err(e) => eprintln!("Parse error: {:?}", e),
    }
}
```

### Working with the SignablePayload

The parser returns a `SignablePayload` containing structured fields:

```rust theme={null}
use visualsign::{SignablePayload, SignablePayloadField};

fn display_payload(payload: &SignablePayload) {
    println!("Transaction: {}", payload.title);

    if let Some(subtitle) = &payload.subtitle {
        println!("  {}", subtitle);
    }

    for field in &payload.fields {
        match field {
            SignablePayloadField::TextV2 { common, text_v2 } => {
                println!("  {}: {}", common.label, text_v2.text);
            }
            SignablePayloadField::AmountV2 { common, amount_v2 } => {
                println!("  {}: {} {}",
                    common.label,
                    amount_v2.amount,
                    amount_v2.abbreviation.as_deref().unwrap_or("")
                );
            }
            SignablePayloadField::AddressV2 { common, address_v2 } => {
                println!("  {}: {}", common.label, address_v2.address);
            }
            _ => {
                // Handle other field types as needed
            }
        }
    }
}
```

### Supported chains

The registry includes parsers for:

* **Ethereum** — Mainnet and L2s (Arbitrum, Optimism, Base, Polygon)
* **Solana** — With IDL support for program parsing
* **Sui** — Move-based transactions
* **Tron** — TRX transfers and Stake 2.0 resource staking

Use the `Chain` enum to specify which parser to use:

```rust theme={null}
use visualsign::registry::Chain;

let chain = Chain::Ethereum;  // or Chain::Solana, Chain::Sui, Chain::Tron
```

### Adding custom ABIs (Ethereum)

For Ethereum transactions, you can provide custom ABIs to improve parsing of unrecognized contracts:

```rust theme={null}
use std::collections::HashMap;
use generated::parser::{Abi, ChainMetadata, EthereumMetadata, chain_metadata::Metadata};

// Load ABI JSON from file
let abi_json = std::fs::read_to_string("/path/to/abi.json")
    .expect("failed to read ABI file");

// Create ABI mapping keyed by contract address
let mut abi_mappings = HashMap::new();
abi_mappings.insert(
    "0xdAC17F958D2ee523a2206206994597C13D831ec7".to_string(), // Replace with your contract address
    Abi {
        value: abi_json,
        signature: None, // Optional: include signature for validation
    },
);

let options = VisualSignOptions {
    metadata: Some(ChainMetadata {
        metadata: Some(Metadata::Ethereum(EthereumMetadata {
            network_id: Some("ETHEREUM_MAINNET".to_string()),
            abi_mappings,
        })),
    }),
    ..Default::default()
};

// Parse using the standard registry; ABIs are extracted from metadata automatically.
// `accept_unsigned()` is required for the `signature: None` mapping above to be registered.
let config = ParserConfig::accept_unsigned();
let registry = create_registry(&config);
let payload = registry.convert_transaction(&Chain::Ethereum, raw_tx, options)
    .expect("failed to parse transaction");
```

### ABI trust posture

Whether the parser registers caller-supplied ABI mappings that carry no signature is a
deploy-time choice, not a per-request one. You pick it when you build the `ParserConfig`:

```rust theme={null}
use parser_app::config::ParserConfig;
use parser_app::payment_verify::PaymentPolicy;
use visualsign::signing::MetadataTrustPolicy;
use visualsign_ethereum::abi_metadata::signer_allowlist_from_hex;

// Register unsigned ABI mappings. A signature that is present must still verify,
// but its signer is not checked against an allowlist.
let permissive = ParserConfig::accept_unsigned();

// Only register mappings signed by an allowlisted secp256k1 key. Unsigned and
// otherwise-signed mappings are dropped. Payment verification is disabled here;
// see `PaymentPolicy` if your deployment requires gateway-signed payment markers.
let allowlist = signer_allowlist_from_hex(&["0x04...".to_string()])
    .expect("invalid signer public key");
let strict = ParserConfig::new(
    MetadataTrustPolicy::RequireAllowlistedSigner(allowlist),
    PaymentPolicy::Disabled,
);
```

Under `RequireAllowlistedSigner`, an empty allowlist rejects every mapping. Note that this
posture governs Ethereum `abi_mappings` only. Solana `idl_mappings` go through a separate
path.

`signer_allowlist_from_hex` comes from the `visualsign-ethereum` crate, so add it to your
dependencies if you use the strict posture.

### Adding custom IDLs (Solana)

For Solana transactions, provide Anchor IDLs to parse program instructions:

```rust theme={null}
use std::collections::HashMap;
use generated::parser::{ChainMetadata, SolanaMetadata, Idl, SolanaIdlType, chain_metadata::Metadata};

// Load IDL JSON from file
let idl_json = std::fs::read_to_string("/path/to/idl.json")
    .expect("failed to read IDL file");

// Create IDL mapping
let mut idl_mappings = HashMap::new();
idl_mappings.insert(
    "YourProgramId1111111111111111111111111111111".to_string(),
    Idl {
        value: idl_json,
        idl_type: Some(SolanaIdlType::Anchor as i32),
        idl_version: None,
        signature: None,
        program_name: Some("MyProgram".to_string()),
    },
);

let options = VisualSignOptions {
    metadata: Some(ChainMetadata {
        metadata: Some(Metadata::Solana(SolanaMetadata {
            network_id: None,
            idl: None,
            idl_mappings,
            simulated_transaction_result: None,
            max_fee_lamports: None,
        })),
    }),
    ..Default::default()
};
```

## Testing your integration

Use the CLI to verify expected output before integrating:

```bash theme={null}
# From the src/ directory
cargo run --bin parser_cli --release -- decode \
  --chain ethereum \
  --network ETHEREUM_MAINNET \
  --output json \
  -t "0x02f901..."
```

Compare the CLI's JSON output with your integration to ensure consistent parsing.

## Complete example

A working example that demonstrates library integration is available in the repository:

* [src/examples/library\_integration\_test.rs](https://github.com/anchorageoss/visualsign-parser/blob/main/src/examples/library_integration_test.rs)

Run it with:

```bash theme={null}
# From the src/ directory
cargo run -p library_integration_test
```

## Next steps

* [Chain Metadata](https://visualsign.dev/wallet-integration/core-concepts/chain-metadata.md): Providing ABIs and IDLs for better parsing
* [Error Handling](https://visualsign.dev/wallet-integration/core-concepts/error-handling.md): Handling parse failures gracefully
* [Field Types](https://visualsign.dev/field-types.md): Understanding the SignablePayload schema


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.