Note: This document was written for the Wayfinder showcase and may be outdated. Key changes since this was written:
- All users are now external parties using the Interactive Submission API (no ~200 internal party limit)
- Bootstrap flow has changed -- see Local Interop Testing and DevNet Interop Testing
- Canton signing keys use secp256k1 (not ed25519)
This document outlines what Wayfinder needs to prepare for:
- Single Transfer Showcase - using ChainSafe infrastructure
- Full Production Deployment - as participant node owner and issuer
For the single transfer showcase, Wayfinder needs to prepare nothing technical.
| Component | Who Provides | What's Needed |
|---|---|---|
| Canton Participant Node | ChainSafe | Already set up |
| Ethereum Node (Sepolia) | ChainSafe | Already set up |
| DAML Contracts (DARs) | ChainSafe | Upload to participant |
| Solidity Contracts | ChainSafe | Deploy to Sepolia |
| Relayer Middleware | ChainSafe | Run and configure |
| PROMPT Token on Sepolia | Wayfinder/ChainSafe | Deploy or use existing |
- Provide the Canton recipient fingerprint - a
bytes32representing the Canton party where tokens should be minted - Optionally observe the transaction on both chains
- No operational responsibility during showcase
When Wayfinder deploys as the participant node owner and issuer, the complete stack must be deployed and operated.
┌─────────────────────────────────────────────────────────────────┐
│ WAYFINDER INFRASTRUCTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ Canton │ │ API Server │ │ PostgreSQL │ │
│ │ Participant │ │ (Port 8081) │ │ Database │ │
│ │ Node │ │ │ │ │ │
│ │ │ │ • /eth JSON-RPC │ │ • Users │ │
│ │ Ports: │ │ • /register │ │ • Balances │ │
│ │ • 5011 gRPC │ │ • MetaMask │ │ • Transfers │ │
│ │ • 5013 HTTP │ │ compatible │ │ │ │
│ └────────┬─────────┘ └────────┬─────────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ ┌────────┴─────────┐ │ │
│ │ │ Relayer │ │ │
│ │ │ (Bridge Events) │ │ │
│ │ └────────┬─────────┘ │ │
│ └──────────────────────┼────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────────────┤
│ │ ETHEREUM │
│ │ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ CantonBridge │ │ PROMPT Token │ │
│ │ │ Smart Contract │ │ (existing ERC20)│ │
│ │ └─────────────────┘ └─────────────────┘ │
│ └──────────────────────────────────────────────────────────────┤
└─────────────────────────────────────────────────────────────────┘
Two Go services:
- API Server - Handles MetaMask connections, user registration, ERC-20 translation
- Relayer - Bridges PROMPT token between Ethereum ↔ Canton
Required Software:
- Canton Distribution - Use
chainsafe/canton:3.4.8Docker image (or newer) - Minimum 8GB RAM, SSD storage
Configuration File (similar to simple-topology.conf):
canton.participants.participant1 {
storage.type = postgres // Use postgres for production, not memory
storage.config {
url = "jdbc:postgresql://localhost:5432/canton"
user = "canton"
password = "${CANTON_DB_PASSWORD}"
}
admin-api {
address = "0.0.0.0"
port = 5012
}
ledger-api {
address = "0.0.0.0"
port = 5011
// Wildcard auth since Wayfinder IS the operator
auth-services = [{ type = wildcard }]
}
http-ledger-api {
address = "0.0.0.0"
port = 5013
}
}Checklist:
- Deploy Canton participant node
- Connect to Canton Network Testnet/Mainnet Synchronizer (endpoint provided by Canton Network)
- Configure firewall: Ledger API (5011) should NOT be publicly exposed - only the relayer connects
- Set up PostgreSQL storage for production durability
Build and upload these packages:
cd contracts/canton-erc20/daml
# Build all packages
./scripts/setup/build-dars.sh
# Upload to participant (order matters):
# 1. common
# 2. cip56-token
# 3. bridge-core
# 4. bridge-wayfinderKey Package IDs to record:
| Package | Purpose |
|---|---|
cip56-token |
CIP56Manager for token minting/burning |
bridge-wayfinder |
WayfinderBridgeConfig for bridge operations |
Deploy to Mainnet (production) or Sepolia (testnet):
cd contracts/ethereum
export PRIVATE_KEY="<deployer-private-key>"
export RELAYER_ADDRESS="<wayfinder-relayer-address>"
export RPC_URL="https://mainnet.infura.io/v3/<YOUR_KEY>"
forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast --verifyRecord deployed addresses:
CantonBridgecontract address- Token mapping configuration (PROMPT token → Canton token ID)
Bridge Setup Transactions:
# Add PROMPT token mapping
cast send $BRIDGE "addTokenMapping(address,bytes32,bool)" \
$PROMPT_TOKEN $CANTON_TOKEN_ID false \
--rpc-url $RPC_URL --private-key $RELAYER_KEY
# Grant MINTER_ROLE to bridge (if using wrapped token pattern)
cast send $TOKEN "grantRole(bytes32,address)" $MINTER_ROLE $RELAYER \
--rpc-url $RPC_URL --private-key $RELAYER_KEYBuild the Go binaries:
make build
# Creates: bin/api-server, bin/relayerProduction Configuration (config.production.yaml):
server:
host: "0.0.0.0"
port: 8080
database:
host: "localhost"
port: 5432
user: "relayer"
password: "${DB_PASSWORD}"
database: "canton_bridge"
ssl_mode: "require"
ethereum:
rpc_url: "https://mainnet.infura.io/v3/${INFURA_API_KEY}"
ws_url: "wss://mainnet.infura.io/ws/v3/${INFURA_API_KEY}"
chain_id: 1 # Mainnet
bridge_contract: "0x<DEPLOYED_BRIDGE>"
token_contract: "0x28d38df637db75533bd3f71426f3410a82041544" # PROMPT
relayer_private_key: "${RELAYER_PRIVATE_KEY}"
confirmation_blocks: 12 # Mainnet finality
gas_limit: 300000
polling_interval: "15s"
canton:
rpc_url: "localhost:5011" # Your participant
domain_id: "<SYNCHRONIZER_ID>"
application_id: "wayfinder-bridge"
relayer_party: "<WAYFINDER_ISSUER_PARTY>"
bridge_package_id: "<BRIDGE_WAYFINDER_PACKAGE_ID>"
bridge_module: "Wayfinder.Bridge"
bridge_contract: "WayfinderBridgeConfig"
tls:
enabled: false # Self-hosted, no TLS needed for local connection
auth:
type: "oauth2"
token_url: "https://your-oauth-provider/token"
client_id: "${OAUTH_CLIENT_ID}"
client_secret: "${OAUTH_CLIENT_SECRET}"
polling_interval: "1s"
bridge:
max_transfer_amount: "1000000000000000000000000" # 1M tokens
min_transfer_amount: "1000000000000000" # 0.001 tokens
rate_limit_per_hour: 1000
monitoring:
enabled: true
server:
host: "0.0.0.0"
port: 9090
read_timeout: "5s"
write_timeout: "10s"
idle_timeout: "30s"
shutdown_timeout: "5s"Environment Variables Required:
# Encryption key for custodial Canton keys (generate securely)
export CANTON_MASTER_KEY=$(openssl rand -base64 32)
# Database credentials
export DB_PASSWORD="<secure-password>"
# Ethereum relayer private key
export RELAYER_PRIVATE_KEY="<hex-encoded-key>"
# OAuth2 credentials for Canton authentication
export OAUTH_CLIENT_ID="<client-id>"
export OAUTH_CLIENT_SECRET="<client-secret>"One-time setup after deployment:
# 1. Allocate the Wayfinder Issuer party
curl -X POST http://localhost:5013/v2/parties \
-H 'Content-Type: application/json' \
-d '{"partyIdHint": "WayfinderIssuer"}'
# Record: Party ID like "WayfinderIssuer::1220abc...def"
# 2. Run bootstrap script
go run scripts/setup/bootstrap-bridge.go \
-config config.production.yaml \
-issuer "WayfinderIssuer::1220..." \
-package "<BRIDGE_WAYFINDER_PACKAGE_ID>"Bootstrap creates:
CIP56Managercontract (for PROMPT token minting/burning)WayfinderBridgeConfigcontract (bridge configuration)
| Aspect | Requirement |
|---|---|
| Uptime | API Server and Relayer must run 24/7 |
| Private Keys | Securely store: Ethereum relayer key, CANTON_MASTER_KEY (HSM recommended) |
| ETH Balance | Keep relayer address funded for gas fees |
| Monitoring | Prometheus metrics at :9090/metrics |
| Database Backup | Regular PostgreSQL backups (users, balances, transfer state) |
| Logs | Structured JSON logging, shipped to observability stack |
| TLS Termination | HTTPS for API Server public endpoint |
- Never expose Ledger API (5011) publicly - only relayer connects
- Use HTTPS/TLS for Ethereum RPC endpoints
- Store private keys in secrets manager (Vault, AWS Secrets, etc.)
- Enable PostgreSQL SSL
- Set up alerting for:
- Failed transfers
- Stream disconnections
- Low ETH balance
- High transaction latency
- Rate limiting on bridge API endpoints
- Audit logging for all admin operations
| Scenario | What Wayfinder Provides |
|---|---|
| Showcase (ChainSafe infra) | Just the recipient fingerprint; observe the transfer |
| Production Deployment | Full infrastructure: Canton node, relayer, contracts, monitoring |
Wayfinder needs to provide:
- A Canton party fingerprint (the
bytes32recipient address) - Optionally, the amount of PROMPT to transfer
Wayfinder becomes the participant node operator and bridge issuer, requiring:
- ✅ Canton participant node infrastructure
- ✅ PostgreSQL database
- ✅ API Server deployment (MetaMask compatibility layer)
- ✅ Relayer deployment (bridge event processing)
- ✅ Ethereum contract deployment and configuration
- ✅ Monitoring and operational procedures
- ✅ Private key management (
CANTON_MASTER_KEY, relayer key)
- Architecture - System design and data flows
- Local Interop Testing - Full local E2E testing
- API Documentation - Endpoint reference