Wallet Integration Guide
This guide explains how to integrate VisualSign into your wallet or application.
Architecture Overview
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Wallet │────▶│ Parser Host │────▶│ Enclave │
│ App │ │ (gRPC) │ │ (Parser) │
└─────────────┘ └──────────────┘ └─────────────┘
│ │ │
│ ▼ │
│ ┌──────────────┐ │
└───────────▶│ Verification │◀────────────┘
│ Library │
└──────────────┘
Integration Steps
1. Connect to Parser Service
The parser exposes a gRPC service. You can connect using any gRPC client library.
Proto Definition:
service ParserService {
rpc Parse(ParseRequest) returns (ParseResponse);
}
message ParseRequest {
string unsigned_payload = 1; // Base64 or hex encoded transaction
Chain chain = 2; // CHAIN_ETHEREUM, CHAIN_SOLANA, etc.
ChainMetadata chain_metadata = 3; // Optional chain-specific data
}
2. Parse Transactions
Send raw transactions to the parser:
Go Example:
import (
pb "your-project/parser"
"google.golang.org/grpc"
)
// Connect to parser
conn, err := grpc.Dial("localhost:44020", grpc.WithInsecure())
client := pb.NewParserServiceClient(conn)
// Parse transaction
resp, err := client.Parse(context.Background(), &pb.ParseRequest{
UnsignedPayload: "0xf86c80850...", // Your raw transaction
Chain: pb.Chain_CHAIN_ETHEREUM,
})
// Extract VisualSign JSON
visualSignJSON := resp.ParsedTransaction.Payload.SignablePayload
JavaScript/TypeScript Example:
import { ParserServiceClient } from './generated/parser_grpc_pb';
import { ParseRequest, Chain } from './generated/parser_pb';
const client = new ParserServiceClient('localhost:44020');
const request = new ParseRequest();
request.setUnsignedPayload('0xf86c80850...');
request.setChain(Chain.CHAIN_ETHEREUM);
client.parse(request, (error, response) => {
if (!error) {
const visualSignJSON = response.getParsedTransaction()
.getPayload()
.getSignablePayload();
// Display to user
}
});
3. Verify Attestation
IMPORTANT: Always verify the enclave attestation before trusting the parsed output.
The parser response includes a signature that must be verified:
// Verify the signature
signature := resp.ParsedTransaction.Signature
publicKey := signature.PublicKey
message := signature.Message
sig := signature.Signature
// Verify using P256 ECDSA
valid := verifyP256Signature(publicKey, message, sig)
4. Display to User
Parse the VisualSign JSON and display it in your UI:
const visualSign = JSON.parse(visualSignJSON);
// Display transaction details
console.log(`Transaction: ${visualSign.Title}`);
visualSign.Fields.forEach(field => {
switch(field.Type) {
case 'text_v2':
console.log(`${field.Label}: ${field.TextV2.Text}`);
break;
case 'amount_v2':
console.log(`${field.Label}: ${field.AmountV2.Amount} ${field.AmountV2.Currency}`);
break;
case 'address_v2':
console.log(`${field.Label}: ${field.AddressV2.Address}`);
break;
}
});
Chain-Specific Metadata
Ethereum - ABI Support
For smart contract interactions, provide the ABI:
request := &pb.ParseRequest{
UnsignedPayload: txBytes,
Chain: pb.Chain_CHAIN_ETHEREUM,
ChainMetadata: &pb.ChainMetadata{
Metadata: &pb.ChainMetadata_Ethereum{
Ethereum: &pb.EthereumMetadata{
Abi: &pb.Abi{
Value: contractABI, // JSON ABI string
},
},
},
},
}
Solana - IDL Support
For Anchor programs, provide the IDL:
request := &pb.ParseRequest{
UnsignedPayload: txBytes,
Chain: pb.Chain_CHAIN_SOLANA,
ChainMetadata: &pb.ChainMetadata{
Metadata: &pb.ChainMetadata_Solana{
Solana: &pb.SolanaMetadata{
Idl: &pb.Idl{
Value: anchorIDL, // JSON IDL string
IdlType: pb.SolanaIdlType_SOLANA_IDL_TYPE_ANCHOR,
},
},
},
},
}
Error Handling
The parser may return errors for invalid transactions:
resp, err := client.Parse(ctx, request)
if err != nil {
// Handle gRPC error
status := status.Convert(err)
log.Printf("Parse failed: %v", status.Message())
}
Health Checks
Monitor parser health:
grpcurl -plaintext -d '{"service":""}' \
localhost:44020 grpc.health.v1.Health/Check
Security Considerations
- Always verify attestations - Don't trust parsed output without verification
- Use TLS in production - Enable TLS for gRPC connections
- Validate signatures - Check the P256 signature on all responses
- Monitor PCR values - Keep allowlists updated for enclave measurements
Example: Complete Integration
package main
import (
"context"
"encoding/json"
"log"
pb "your-project/parser"
"google.golang.org/grpc"
)
func parseAndDisplayTransaction(rawTx string) error {
// Connect to parser
conn, err := grpc.Dial("localhost:44020", grpc.WithInsecure())
if err != nil {
return err
}
defer conn.Close()
client := pb.NewParserServiceClient(conn)
// Parse transaction
resp, err := client.Parse(context.Background(), &pb.ParseRequest{
UnsignedPayload: rawTx,
Chain: pb.Chain_CHAIN_ETHEREUM,
})
if err != nil {
return err
}
// Verify signature
sig := resp.ParsedTransaction.Signature
if !verifySignature(sig) {
return fmt.Errorf("invalid signature")
}
// Parse VisualSign JSON
var visualSign map[string]interface{}
err = json.Unmarshal([]byte(resp.ParsedTransaction.Payload.SignablePayload), &visualSign)
if err != nil {
return err
}
// Display to user
displayTransaction(visualSign)
return nil
}
Testing
Use the provided test transactions:
Solana:
cargo run --bin parser_cli -- --chain solana -t 'AgAAAAAAAA...'
Ethereum:
cargo run --bin parser_cli -- --chain ethereum -t '0xf86c808504a817c800...'