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

# API Reference

> gRPC service definition, messages, and client examples

## Service definition

```protobuf theme={null}
service ParserService {
  rpc Parse(ParseRequest) returns (ParseResponse);
}
```

## Messages

### ParseRequest

The main request for parsing transactions.

```protobuf theme={null}
message ParseRequest {
  string unsigned_payload = 1;    // Raw transaction (base64 or hex)
  Chain chain = 2;                // Blockchain network
  ChainMetadata chain_metadata = 3; // Optional chain-specific data
}
```

**Fields:**

| Field | Type | Description |
| - | - | - |
| `unsigned_payload` | string | Raw unsigned transaction bytes, base64 or hex encoded |
| `chain` | Chain enum | The blockchain network (CHAIN\_ETHEREUM, CHAIN\_SOLANA, etc.) |
| `chain_metadata` | ChainMetadata | Optional metadata like ABI or IDL |

### Chain enum

Supported blockchain networks:

```protobuf theme={null}
enum Chain {
  CHAIN_UNSPECIFIED = 0;
  CHAIN_BITCOIN = 1;
  CHAIN_ETHEREUM = 2;
  CHAIN_SOLANA = 3;
  CHAIN_SUI = 4;
  CHAIN_TRON = 5;
  CHAIN_CUSTOM = 999;  // For extensibility
}
```

### ChainMetadata

Chain-specific metadata for enhanced parsing:

```protobuf theme={null}
message ChainMetadata {
  oneof metadata {
    EthereumMetadata ethereum = 1;
    SolanaMetadata solana = 2;
  }
}

message EthereumMetadata {
  reserved 1;                              // Removed: was singular `abi` field.
  reserved "abi";                          //
  optional string network_id = 2;          // Network identifier (e.g., "ETHEREUM_MAINNET", "POLYGON_MAINNET")
  map<string, Abi> abi_mappings = 3;       // Contract address (0x-prefixed) to ABI definition
}

message SolanaMetadata {
  optional string network_id = 2;       // Network identifier (e.g., "SOLANA_MAINNET", "SOLANA_DEVNET")
  optional Idl idl = 1;                 // Anchor IDL for decoding (single program)
  map<string, Idl> idl_mappings = 3;    // Map of program_id to IDL (multiple programs)
  optional string simulated_transaction_result = 5; // Base64 simulateTransaction result, for inner (CPI) instructions
  optional uint64 max_fee_lamports = 6; // Max total fee allowed, in lamports; default 0.1 SOL. "Max Fee" row ("Max Fee (default)" without it), rejected when below the committed fee
}
```

### ParseResponse

The parsed transaction response:

```protobuf theme={null}
message ParseResponse {
  ParsedTransaction parsed_transaction = 1;
}

message ParsedTransaction {
  ParsedTransactionPayload payload = 1;
  Signature signature = 2;
}

message ParsedTransactionPayload {
  string parsed_payload = 1;         // VisualSign JSON string (primary)
  string input_payload_digest = 2;   // SHA-256 of the unsigned payload
  string metadata_digest = 3;        // SHA-256 (Borsh) of chain metadata
  string signable_payload = 4;       // Legacy. Will be removed; use parsed_payload.
}
```

### Signature

Cryptographic signature for attestation:

```protobuf theme={null}
message Signature {
  SignatureScheme scheme = 1;  // Signature algorithm
  string public_key = 2;       // Public key (hex)
  string message = 3;          // Message that was signed
  string signature = 4;        // Signature bytes (hex)
}

enum SignatureScheme {
  SIGNATURE_SCHEME_UNSPECIFIED = 0;
  SIGNATURE_SCHEME_TURNKEY_P256_EPHEMERAL_KEY = 1;
}
```

## Response structure

The `parsed_payload` field contains a JSON string with the following structure:

```typescript theme={null}
interface TransactionData {
  Version: string;           // Format version
  Title: string;             // Transaction title
  Subtitle?: string;         // Optional subtitle
  Fields: Field[];           // Array of field objects
  ReplayProtection?: string; // Optional replay protection
  VaultSubID?: string;       // Optional vault sub-ID
  EndorsedParamsDigest?: string; // Optional params digest
}
```

See [Field Types](https://visualsign.dev/field-types.md) for details on the available field types.

## Usage examples

### Basic transaction parsing

```bash theme={null}
# Ethereum transaction
grpcurl -plaintext -d '{
  "unsigned_payload": "0xec808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080",
  "chain": "CHAIN_ETHEREUM"
}' localhost:44020 parser.ParserService/Parse
```

### With contract ABI

```bash theme={null}
# Ethereum with ABI for smart contract decoding.
# abi_mappings is keyed by 0x-prefixed contract address; pick a consistent
# casing convention (all lowercase or EIP-55 checksummed) across requests.
grpcurl -plaintext -d '{
  "unsigned_payload": "0x...",
  "chain": "CHAIN_ETHEREUM",
  "chain_metadata": {
    "ethereum": {
      "network_id": "ETHEREUM_MAINNET",
      "abi_mappings": {
        "0xdac17f958d2ee523a2206206994597c13d831ec7": {
          "value": "[{\"inputs\":[],\"name\":\"transfer\",\"type\":\"function\"}]"
        }
      }
    }
  }
}' localhost:44020 parser.ParserService/Parse
```

### Solana with Anchor IDL

```bash theme={null}
# Solana with IDL for program decoding
grpcurl -plaintext -d '{
  "unsigned_payload": "AgAAAAAAAA...",
  "chain": "CHAIN_SOLANA",
  "chain_metadata": {
    "solana": {
      "idl": {
        "value": "{\"version\":\"0.30.0\",\"name\":\"my_program\",...}",
        "idl_type": "SOLANA_IDL_TYPE_ANCHOR",
        "idl_version": "0.30.0"
      }
    }
  }
}' localhost:44020 parser.ParserService/Parse
```

### Solana with a max fee

Every Solana payload carries a top-level fee cap row right after the header
rows: the caller's `max_fee_lamports` when the request sets one, labelled "Max
Fee", otherwise the parser's default cap of 0.1 SOL (100000000 lamports),
labelled "Max Fee (default)". The parser rejects the request when the
transaction commits to more than the cap in force: 5000 lamports per signature,
counting the ones precompile instructions verify, plus the compute budget's
priority fee. That fee is the unit price times the requested unit limit, or,
with no limit, times the runtime's default limit for the message (3000 units for
each builtin instruction, 200000 for any other, at most 1400000). Compute budget
requests are decoded as the runtime decodes them, so trailing bytes are ignored.
A request the runtime rejects is rejected here too: one that does not decode, a
repeated price, limit, heap frame, or loaded accounts data size, a heap frame
size out of range, a zero loaded accounts data size, or the unused variant. With
`include_intermediate_output`, the cap in force is also the trailing
`max_fee_lamports` field of the Solana intermediate output. That output is at
schema version 5, which also adds a `native_instruction_decoder` tag right after
the version. A consumer that rewrites the compute budget instructions after
parsing, for example to re-price at signing time, changes the fee the cap bounds
and must re-check the final fee against `max_fee_lamports` itself.

```bash theme={null}
# Solana with a caller-supplied cap, in lamports. Over the HTTP gateway the same
# field is the camelCase key `maxFeeLamports`.
grpcurl -plaintext -d '{
  "unsigned_payload": "AgAAAAAAAA...",
  "chain": "CHAIN_SOLANA",
  "chain_metadata": {
    "solana": {
      "max_fee_lamports": 5105000
    }
  }
}' localhost:44020 parser.ParserService/Parse
```

## Response format

### Successful response

```json theme={null}
{
  "parsedTransaction": {
    "payload": {
      "parsedPayload": "{\"Version\":\"0\",\"Title\":\"Ethereum Transfer\",\"Fields\":[{\"Type\":\"text_v2\",\"Label\":\"To\",\"TextV2\":{\"Text\":\"0x3535...\"}}]}",
      "inputPayloadDigest": "a1b2c3...",
      "metadataDigest": "e3b0c4..."
    },
    "signature": {
      "scheme": "SIGNATURE_SCHEME_TURNKEY_P256_EPHEMERAL_KEY",
      "publicKey": "04abc123...",
      "message": "sha256_hash_of_payload",
      "signature": "304502..."
    }
  }
}
```

### Error response

Errors follow the standard gRPC status codes:

```json theme={null}
{
  "code": 3,
  "message": "Invalid transaction format",
  "details": []
}
```

Common error codes:

| Code | Name | Description |
| - | - | - |
| `3` | INVALID\_ARGUMENT | Invalid transaction or chain |
| `5` | NOT\_FOUND | Chain not supported |
| `13` | INTERNAL | Parser error |
| `14` | UNAVAILABLE | Service unavailable |

## Health check API

The service also implements the standard gRPC health check:

```protobuf theme={null}
service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}
```

Check service health:

```bash theme={null}
grpcurl -plaintext -d '{"service":"parser.ParserService"}' \
    localhost:44020 grpc.health.v1.Health/Check
```

Response:

```json theme={null}
{
  "status": "SERVING"
}
```

## Client libraries

### Go

```go theme={null}
import pb "github.com/anchorageoss/visualsign-parser/proto"

conn, _ := grpc.Dial("localhost:44020", grpc.WithInsecure())
client := pb.NewParserServiceClient(conn)
```

### Python

```python theme={null}
import grpc
import json
import parser_pb2
import parser_pb2_grpc

# Connect to parser service
channel = grpc.insecure_channel('localhost:44020')
stub = parser_pb2_grpc.ParserServiceStub(channel)

# Parse transaction
request = parser_pb2.ParseRequest(
    unsigned_payload=raw_tx_bytes,
    chain=parser_pb2.CHAIN_ETHEREUM
)
response = stub.Parse(request)

# Get structured data
transaction_data = json.loads(response.parsed_transaction.payload.parsed_payload)
```

### JavaScript/TypeScript

```typescript theme={null}
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const packageDefinition = protoLoader.loadSync('parser.proto');
const parser = grpc.loadPackageDefinition(packageDefinition).parser;

const client = new parser.ParserService(
    'localhost:44020',
    grpc.credentials.createInsecure()
);

// Parse transaction
client.Parse({
    unsigned_payload: rawTxHex,
    chain: 'CHAIN_ETHEREUM'
}, (err, response) => {
    if (err) {
        console.error('Parse error:', err);
        return;
    }
    const transactionData = JSON.parse(response.parsedTransaction.payload.parsedPayload);
    console.log('Parsed:', transactionData);
});
```

## Rate limits

The parser service enforces the following limits:

* **Request size**: Max 1MB per transaction
* **Timeout**: 30 seconds per request
* **Concurrent requests**: 100 per client

## Security

### TLS configuration

In production, always use TLS:

```go theme={null}
creds := credentials.NewTLS(&tls.Config{
    ServerName: "parser.example.com",
})
conn, _ := grpc.Dial("parser.example.com:44020", grpc.WithTransportCredentials(creds))
```

### Authentication

The service supports mTLS for authentication:

```go theme={null}
cert, _ := tls.LoadX509KeyPair("client.crt", "client.key")
creds := credentials.NewTLS(&tls.Config{
    Certificates: []tls.Certificate{cert},
})
```


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