Twin Documentation
  • Overview
  • Consumer
  • Developer
  • Operations
Arquitectura
Flujos Técnicos
    Minting & RedeemBridge Cross-chainUniswap V4 Liquidity ManagementOracle Price UpdatesAuto-RebalanceRewardsAuthentication (Platform)Circulation ManagementDisperse (Batch Transfer)Market Data PipelineSupply API (CoinGecko)Contract Data CollectionCron Jobs: Flujos Detallados
Base de Datos
Local SetupConventions
powered by Zudoku
Flujos Técnicos

Bridge Cross-chain

LayerZero V2 OFT bridge for Twin tokens across chains (Base, Polygon, etc.).

Overview

The bridge uses LayerZero V2 OFT (Omnichain Fungible Token) adapters to transfer Twin tokens between chains. The mechanism is burn-on-source, mint-on-destination. Each token has adapter contracts deployed per chain, grouped by a bridge_token_key.


Bridge Flow

Non-blocking Record

The POST /bridge/transaction call is non-blocking by design. If it fails (network error, server down), the bridge transaction is already on-chain and will complete via LayerZero regardless. The frontend logs a warning but does not block the user.

Code
// Frontend pattern try { await recordBridgeTransaction(txData); } catch (err) { console.warn('Failed to record bridge transaction:', err); // TX already on-chain, don't block user }

Entity Relationship Diagram


bridge_token_key Concept

A bridge_token_key (e.g. "ARGT") groups routes for the same conceptual token across chains. This is necessary because ops_token has one row per (address, network) pair -- the bridge thinks in terms of "ARGT" not "ARGT on Base + ARGT on Polygon".

Code
bridge_token_key = "ARGT" ├── Route: ARGT on Base (ops_token_id=..., adapter=0x..., lz_eid=30184) └── Route: ARGT on Polygon (ops_token_id=..., adapter=0x..., lz_eid=30109)

Auto-uppercased in the backend. The frontend groups routes by this key to show available source/destination pairs.

Partial Unique Index

Code
CREATE UNIQUE INDEX bridge_route_token_network_unique ON bridge_route (bridge_token_key, network) WHERE deleted_at IS NULL;

Soft-deleted routes don't block re-creation. Same pattern as ops_token and circulation_wallet.


Frontend Hooks

All hooks are parameterized -- they receive token address, adapter address, and chain ID as arguments rather than using constants.

HookPurposeKey Params
useBalanceRead user's token balancetokenAddress, chainId
useAllowanceCheck ERC20 allowance for adaptertokenAddress, adapterAddress, chainId
useApproveApprove adapter to spend tokenstokenAddress, adapterAddress, amount
useQuoteSendGet LayerZero fee estimateadapterAddress, dstEid, amount
useSendExecute bridge send TXadapterAddress, dstEid, to, amount, nativeFee

Hook Design Pattern

Code
// Parameterized -- no hardcoded addresses or chain IDs function useQuoteSend(adapterAddress: string, dstEid: number, amount: bigint) { return useReadContract({ address: adapterAddress as `0x${string}`, abi: oftAdapterAbi, functionName: 'quoteSend', args: [/* SendParam */], chainId, }); }

Safe Wallet Limitations

ConstraintReason
No useWaitForTransactionReceiptSafe multisig returns safeTxHash (off-chain hash). The on-chain TX hash only exists after threshold signers approve in Safe UI. Polling for safeTxHash on-chain would never resolve.
No useSwitchChain()Safe wallets don't support wallet_switchEthereumChain RPC method. Display an Alert instructing the user to connect their wallet on the correct network.

API Endpoints

MethodPathDescription
GET/api/operations/bridge/tokensList bridgeable tokens (grouped by bridge_token_key)
GET/api/operations/bridge/routesList all bridge routes
POST/api/operations/bridge/routesCreate a bridge route
PUT/api/operations/bridge/routes/:idUpdate a bridge route
DELETE/api/operations/bridge/routes/:idSoft-delete a bridge route
GET/api/operations/bridge/transactionsList bridge transactions (paginated)
POST/api/operations/bridge/transactionRecord a bridge transaction

Dashboard Pages

PagePathAccessPurpose
Bridge (operational)/operations/bridgeAdmin, OpsExecute bridge transfers via connected wallet
Bridge Routes (CRUD)/wallets/bridgeAdminManage bridge routes (adapter addresses, LZ EIDs)
  • Sidebar: operational page under "Operaciones", CRUD under "Configuracion" (formerly "Wallets").
  • BridgeService in shared/src/service/: CRUD admin + getBridgeableTokens() (groupBy + join OpsToken) + recordTransaction() + getTransactions() paginated.
  • UniqueViolationError handler for duplicate (bridge_token_key, network).

LayerZero Details

  • Finality: 1-5 minutes depending on source/destination chain confirmation requirements.
  • Fee: Paid in native token (ETH on Base) via msg.value in the send() call. Quoted via quoteSend() before execution.
  • Endpoint IDs: Base = 30184, Polygon = 30109. Stored per route in lz_eid column.
  • Message format: Standard OFT send() with SendParam struct containing dstEid, to (bytes32), amountLD, minAmountLD.
Last modified on May 12, 2026
Minting & RedeemUniswap V4 Liquidity Management
On this page
  • Overview
  • Bridge Flow
    • Non-blocking Record
  • Entity Relationship Diagram
  • bridge_token_key Concept
    • Partial Unique Index
  • Frontend Hooks
    • Hook Design Pattern
  • Safe Wallet Limitations
  • API Endpoints
  • Dashboard Pages
  • LayerZero Details
TypeScript
TypeScript