ChainSafe / ChainSafe/canton-middleware

feat(relayer): TokenBridge adapter foundation (interface, registry, driver)

Open
#356 0 comments 0 reactions 0 assignees View on GitHub
CIP-0086: Phase 2 Deliverable: D3 Type: Feature
Dominant language
Go
Stars
1
Forks
1
Avg merge
40m
Merged PRs (30d)
1

Description

# Goal

Introduce the multi-token `TokenBridge` adapter foundation in `pkg/relayer` — interface, registry, driver loop, schema, per-token config. **Scope decision (2026-07-11): the existing PROMPT pipeline is NOT ported in this issue.** It keeps running unchanged (processor + reconcile loop as today); the new driver runs alongside it and serves adapter-registered tokens only (first consumer: xreserve/USDCx, #357). Porting PROMPT to a `wayfinder` adapter is deferred to a follow-up issue.

# Design

Full design: `docs/bridging-design.md` (see epic #361 for the condensed version).

Core abstraction — don't abstract over lock/mint verbs; abstract over the one thing every mechanism shares:

> A transfer is a durable record advanced by an idempotent step function until terminal.

```go
type TokenBridge interface {
Key() string // "xreserve", later "wayfinder"
Sources(ctx context.Context) ([]Source, error) // event streams (empty for observer mechanisms)
Step(ctx context.Context, t *relayer.Transfer) (StepResult, error) // advance one stage, never block
}

type StepResult struct {
Status relayer.TransferStatus // pending | in_progress | completed | failed
Stage string // mechanism-defined
DestTxHash *string
Metadata map[string]any // merged into transfers.metadata
RetryAfter time.Duration
}
```

Engine additions (new code, not a rewrite of the existing pipeline):
- `runIngest` (one per Source): event -> `CreateTransfer` (idempotent) -> save offset. Detection only. Exercised only by tests until a sourced (executor) adapter registers.
- `runDriver` (one for all mechanisms): tick -> load non-terminal adapter transfers with `next_step_at <= now` -> dispatch `Step` via registry by `t.BridgeKey` -> persist `StepResult`. Retry/backoff/stuck-detection written once.

# Work items

- [ ] `pkg/relayer/bridge.go` — `TokenBridge`, `Source` (same shape as today's), `StepResult`, `Registry`
- [ ] `pkg/relayer/config.go` — add `Tokens map[string]TokenConfig{mechanism, evm_address, decimals, ...}`; existing single-token fields stay for the legacy PROMPT pipeline
- [ ] `pkg/relayer/engine/` — add `driver.go` + `ingest.go`; **do not** delete/modify `processor.go`, `destination.go`, `source.go` (they serve PROMPT until the port)
- [ ] `pkg/migrations/relayerdb/3_multi_token.go` — `transfers` += `bridge_key` (backfill existing rows to `wayfinder`), `token_symbol`, `stage`, `metadata jsonb`, `next_step_at`; new columns nullable/defaulted so the legacy write path is untouched. `chain_state` composite key `(bridge_key, chain_id)` with existing rows keyed to `wayfinder`
- [ ] `pkg/relayer/store/` — `GetSteppableTransfers` (filters to registry-known bridge keys), `ApplyStep`, `RecordStepError`
- [ ] `pkg/app/relayer/server.go` — build registry from config (mechanism -> constructor); legacy pipeline wiring unchanged
- [ ] Mocks for `TokenBridge`/`Source`; driver unit tests with a fake adapter (stage progression, backoff, RetryAfter, unknown-key handling)

# Acceptance

- PROMPT deposits and withdrawals behave identically on devstack (existing e2e green, legacy pipeline untouched)
- A fake adapter registered in config gets driven through its stages by `runDriver`
- Driver ignores legacy `wayfinder`-keyed rows (owned by the old pipeline until the port)

# Follow-up (deferred)

- Port PROMPT to `bridges/wayfinder/` and delete the legacy pipeline — separate issue.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with docs/bridging-design.md, then read pkg/relayer/bridge.go and the existing engine, store, migration, and server entry points named in the work items. Add the fake-adapter driver tests for stage progression, backoff, RetryAfter, and unknown keys, and run the existing devstack e2e checks. Done means the configured fake adapter advances while legacy PROMPT behavior and wayfinder-keyed rows remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.