Skip to content

Latest commit

History

595 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Canton Middleware

MetaMask-compatible middleware for Canton Network, enabling ERC-20 style interactions with Splice-compliant CIP-56 tokens.

Overview

This project provides:

  • API Server -- Ethereum JSON-RPC facade that translates MetaMask transactions to Canton CIP-56 operations
  • Relayer -- Bridges PROMPT tokens between Ethereum and Canton
  • Interoperability -- Native Canton users (e.g. Canton Loop) and MetaMask users can seamlessly transfer tokens to each other
  • Splice Token Standard (CIP-0056) -- All tokens implement the Splice HoldingV1, TransferFactory, and Metadata interfaces
  • Splice Registry API -- External wallets discover TransferFactory contracts for explicit disclosure during transfers
  • External Parties -- All users are allocated as external parties using the Interactive Submission API (no ~200 internal party limit)

Quick Start (Local Development)

# 1. Bootstrap: starts Docker, registers users, mints tokens
./scripts/testing/bootstrap-local.sh --clean
# 2. Test: runs all 8 interop + bridge test steps
go run scripts/testing/interop-demo.go

Both scripts auto-detect all dynamic configuration (domain IDs, party IDs, contract addresses) from the running Docker containers. See the Local Interop Testing Guide for full details.

Prerequisites

ToolVersionInstall
Go1.23+https://go.dev/dl/
DockerLatesthttps://docs.docker.com/get-docker/
Docker Composev2+(included with Docker Desktop)
FoundryLatestcurl -L https://foundry.paradigm.xyz | bash && foundryup

MetaMask Configuration

After bootstrap completes, configure MetaMask:

SettingValue
Network NameCanton Local
RPC URLhttp://localhost:8081/eth
Chain ID31337
Currency SymbolETH

Test Accounts (Anvil defaults):

UserAddressPrivate Key
User 10xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
User 20x70997970C51812dc3A010C7d01b50e0d17dc79C859c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

Token Addresses:

TokenAddressType
DEMO0xDE30000000000000000000000000000000000001Native Canton token
PROMPT0x5FbDB2315678afecb367f032d93F642f64180aa3Bridged ERC-20

Service Endpoints

ServiceURL
API Server (MetaMask RPC)http://localhost:8081/eth
User Registrationhttp://localhost:8081/register
Splice Registry APIhttp://localhost:8081/registry/transfer-instruction/v1/transfer-factory
API Server Healthhttp://localhost:8081/health
Canton gRPClocalhost:5011
Canton HTTPhttp://localhost:5013
Anvil (Ethereum)http://localhost:8545
PostgreSQLlocalhost:5432
Relayer Metricshttp://localhost:9090/metrics
Prometheus UIhttp://localhost:9091
Grafana UIhttp://localhost:3001

Monitoring

docker compose up brings up Prometheus and Grafana alongside the application services.

  • Prometheus scrapes the relayer's /metrics endpoint on relayer:9090 every 15 s. The Prometheus UI is available at http://localhost:9091.
  • Grafana is pre-provisioned with Prometheus as the default data source (no manual setup required). Open http://localhost:3001 — login with admin / admin.

Configuration files:

FilePurpose
deployments/prometheus/prometheus.ymlPrometheus scrape config
deployments/grafana/provisioning/datasources/prometheus.yamlGrafana auto-provisioned Prometheus datasource

Architecture

┌─────────────────┐ ┌─────────────────┐
│ MetaMask │ │ Canton Loop / │
│ (EVM Wallet) │ │ Native Canton │
└────────┬────────┘ └────────┬────────┘
│ JSON-RPC │ gRPC / Registry API
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ API SERVER │
│ • /eth - JSON-RPC facade (MetaMask-compatible) │
│ • /register - User registration (EVM + Canton native) │
│ • /registry/… - Splice Registry API (TransferFactory) │
│ • Custodial Canton key management (AES-256-GCM) │
│ • Balance reconciliation with Canton │
└────────────────────────┬─────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Canton │ │ Relayer │
│ (Users/ │ │ (CIP-56 │ │ (ERC-20 │
│ Balances) │ │ Ledger) │ │ Bridge) │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────▼──────┐
│ Ethereum │
│ (Anvil / │
│ Mainnet) │
└─────────────┘

How Transfers Work

All users are external parties on Canton. They hold their own signing keys and use the Interactive Submission API (prepare/sign/execute) instead of the standard CommandService.

MetaMask / cast send / Canton Loop
│
▼
/eth endpoint (JSON-RPC) or Interactive Submission (gRPC)
│
▼
Resolve Canton signing key from encrypted DB store
│
▼
PrepareSubmission → sign with user's key → ExecuteSubmission
│
▼
Canton Ledger: TransferFactory_Transfer (Splice-compliant CIP-56)

Tokens

TokenTypeDescription
DEMONative Canton (CIP-56)Minted directly on Canton via CIP56Manager, implements Splice HoldingV1
PROMPTBridged ERC-20Bridged from Ethereum via the Wayfinder bridge, also uses Splice HoldingV1

Both tokens carry Splice-standard metadata (TextMap Text) with DNS-prefixed keys (e.g. splice.chainsafe.io/symbol). Metadata is propagated through all transfers via the TransferFactory.


Project Structure

canton-middleware/
├── cmd/
│ ├── api-server/ # API server entry point
│ └── relayer/ # Relayer entry point
├── pkg/
│ ├── apidb/ # API database operations (users, balances, whitelist)
│ ├── app/ # Application wiring (api server, http server)
│ ├── auth/ # JWT and EVM authentication
│ ├── cantonsdk/ # Canton SDK
│ │ ├── bridge/ # ERC-20 ↔ Canton bridge operations
│ │ ├── client/ # High-level SDK facade
│ │ ├── identity/ # Party allocation, fingerprint mappings
│ │ ├── lapi/v2/ # Generated Ledger API v2 protobufs
│ │ ├── ledger/ # Low-level gRPC client (state, commands, auth)
│ │ ├── token/ # CIP-56 token operations (mint, burn, transfer)
│ │ └── values/ # Daml value encoding/decoding helpers
│ ├── config/ # Configuration loading
│ ├── db/ # Relayer database schema
│ ├── ethereum/ # Ethereum client and ABI
│ ├── ethrpc/ # Ethereum JSON-RPC server
│ ├── keys/ # Custodial key management (secp256k1, AES-256-GCM)
│ ├── registration/ # User registration handler
│ ├── registry/ # Splice Registry API handler
│ ├── relayer/ # Bridge relayer logic
│ └── service/ # Token service layer
├── contracts/
│ ├── canton-erc20/ # DAML contracts (CIP-56, Splice-compliant)
│ └── ethereum-wayfinder/ # Solidity bridge contracts
├── scripts/
│ ├── setup/ # Bootstrap and infrastructure scripts
│ ├── testing/ # Test and demo scripts
│ ├── bridge/ # Bridge operation scripts
│ ├── demo/ # Demo scripts
│ ├── remote/ # Remote deployment scripts
│ ├── utils/ # Diagnostic utilities
│ ├── lib/ # Shared bash functions
│ └── archive/ # Archived migration scripts
├── docs/ # Documentation
├── deployments/ # Docker, Canton, Prometheus configs
└── proto/ # Protobuf definitions

Scripts

Testing (scripts/testing/)

ScriptDescription
bootstrap-local.shFull local bootstrap (Docker, users, tokens)
interop-demo.go8-step interop + bridge test suite
demo-activity.goDisplay Canton token activity (holdings, configs, events)
canton-transfer-demo.goDemo transfers and reconciliation
register-native-user.goRegister a native Canton user
register-user.goRegister an EVM user
e2e-local.goEnd-to-end local tests
test-reconcile.goTest balance reconciliation
test-whitelist.goTest whitelist functionality

Setup (scripts/setup/)

ScriptDescription
bootstrap-all.shFull automated setup (alternative to bootstrap-local.sh)
bootstrap-bridge.goBootstrap bridge contracts on Canton
bootstrap-demo.goMint DEMO tokens to users
docker-bootstrap.shDocker-specific bootstrap
setup-local.shLocal environment setup
setup-devnet.shDevNet setup
build-dars.shBuild DAML archives
generate-protos.shRegenerate protobuf Go code

Bridge (scripts/bridge/)

ScriptDescription
bridge-activity.goDisplay recent bridge activity
bridge-deposit.goDeposit ERC-20 to Canton
get-holding-cid.goGet holding contract ID for withdrawals
initiate-withdrawal.goInitiate Canton → Ethereum withdrawal
cleanup-withdrawals.goClean up processed withdrawals

Utilities (scripts/utils/)

ScriptDescription
check-user-holdings.goCheck user holdings on Canton
verify-canton-holdings.goVerify/compare holdings (DB vs Canton)
list-parties.goList known parties
list-users.goList registered users
check-mappings.goCheck fingerprint mappings
reconcile.goManual balance reconciliation
reset-demo-state.goReset demo state
query-canton-holdings.shQuery Canton holdings (bash)
metamask-info.shShow MetaMask config
mock-oauth2-server.goLocal OAuth2 mock server

Development

Build

go mod download
go build ./...

Lint

golangci-lint run ./...

Manual Setup

export CANTON_MASTER_KEY=$(openssl rand -base64 32)export SKIP_CANTON_SIG_VERIFY=true
docker compose up -d
# Wait for healthy status, then bootstrap
go run scripts/setup/bootstrap-bridge.go -config config.e2e-local.yaml
go run scripts/setup/bootstrap-demo.go -config config.e2e-local.yaml

Verify Setup

curl http://localhost:8081/health
curl -X POST http://localhost:8081/registry/transfer-instruction/v1/transfer-factory
go run scripts/utils/check-user-holdings.go -config config.e2e-local.yaml

Configuration

EnvironmentConfig File
Local testingconfig.e2e-local.yaml
Dockerconfig.docker.yaml
API server (Docker)config.api-server.docker.yaml
Local devnetconfig.local-devnet.yaml
Exampleconfig.example.yaml

Key Canton-specific fields (auto-detected by bootstrap):

FieldDescription
domain_idCanton synchronizer domain ID
relayer_partyBridge relayer / token issuer party
instrument_adminParty administering token instruments
instrument_idToken instrument identifier (e.g. PROMPT)
cip56_package_idcip56-token DAR package hash
splice_holding_package_idSplice HoldingV1 interface DAR hash
splice_transfer_package_idSplice TransferFactory interface DAR hash
bridge_package_idbridge-wayfinder DAR package hash

Documentation

DocumentDescription
dApp & Snap TestingTesting with the Wayfinder dApp and MetaMask Snap
Local Interop TestingFull local bootstrap and 8-step interop test guide
API DocumentationEndpoint reference (JSON-RPC, Registration, Splice Registry)
ArchitectureSystem design and data flows
DevNet Interop TestingDEMO token testing on ChainSafe DevNet
CIP-0086 OverviewCIP-0086 compliance
Deployment RequirementsProduction deployment checklist

License

This project is licensed under the Apache License, Version 2.0. See LICENSE for the full license text and NOTICE for attributions of third-party software included in or depended on by this project.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages