Status: Deployed January 30, 2026
This guide covers deploying the Base ↔ Stacks bridge to mainnet.
- Contract:
0x0EdF28403D027Be0917625C751c78236407dD4E0 - USDC:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 - Verified: ✅ BaseScan
- Contract:
SP1MTYHV6K2FNH3QNF4P5QXS9VJ3XZ0GBB5T1SJPK.wrapped-usdc-v5 - Deploy TX:
0x44c62212aa019260add71e59bea6bc0de16298efa19d730ff4d1c9645e785d0f - Block Height: 6202949
- Signers TX:
0x82f87b9599b7d04515af772c41b8d0b064e0156474f8257c1e8887960df0d7d3
- Base Deployer Wallet - New wallet with ~0.01 ETH for gas
- Stacks Deployer Wallet - New wallet with ~10 STX for contract fees
- Multi-Sig Signer 1 - Separate wallet for relayer 1
- Multi-Sig Signer 2 - Separate wallet for relayer 2
- Multi-Sig Signer 3 - Separate wallet for relayer 3
⚠️ Security: Each signer wallet should be controlled by a different party or hardware wallet.
- Hiro API Key - Get from platform.hiro.so
- BaseScan API Key - Get from basescan.org
- Base RPC - Public or use Alchemy/Infura for reliability
# Stacks
cp stacks/settings/Mainnet.toml.example stacks/settings/Mainnet.toml
# Add your mainnet mnemonic
# EVM
cp evm/.env.mainnet.example evm/.env
# Add private key and signer addresses
# Relayer
cp relayer/.env.mainnet.example relayer/.env
# Add all configurationcd stacks
# Check contract compiles
clarinet check
# Deploy to mainnet
clarinet deployments apply -p deployments/default.mainnet-plan.yamlExpected output: Contract address like SP...xxx.wrapped-usdc
cd evm
# Deploy BridgeBase
npx hardhat run scripts/deploy.js --network baseMainnet
# Verify on BaseScan
npx hardhat verify --network baseMainnet <CONTRACT_ADDRESS> <USDC_ADDRESS> [SIGNER1, SIGNER2, SIGNER3]Expected output: Contract address like 0x...xxx
cd relayer
# Initialize the 3 signers on Stacks contract
node scripts/initialize-signers.jsEdit frontend/src/lib/config.ts:
export const config = {
network: 'mainnet',
contracts: {
base: {
bridge: '0x...MAINNET_ADDRESS...',
usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // Base mainnet USDC
},
stacks: {
address: 'SP...xxx',
name: 'wrapped-usdc',
},
},
chains: {
base: {
chainId: 8453, // Base Mainnet
rpc: 'https://mainnet.base.org',
explorer: 'https://basescan.org',
},
stacks: {
network: 'mainnet',
api: 'https://api.hiro.so',
explorer: 'https://explorer.hiro.so',
},
},
};Each relayer should run on a separate server:
# Relayer 1 (Server A)
SIGNER_INDEX=1 node src/index.js
# Relayer 2 (Server B)
SIGNER_INDEX=2 node src/index.js
# Relayer 3 (Server C)
SIGNER_INDEX=3 node src/index.js- BridgeBase verified on BaseScan
- wrapped-usdc visible on Stacks Explorer
- All 3 signers registered on both chains
- Small deposit (10 USDC) - Base → Stacks
- Check xUSDC minted on Stacks
- Small withdraw (5 xUSDC) - Stacks → Base
- Check USDC released on Base
- Multi-sig requires 2-of-3 approvals
- Rate limits active (10K/tx, 50K/hr, 200K/day)
- Timelock working for large transactions
- Emergency pause tested
| Chain | Contract | Address |
|---|---|---|
| Base Mainnet | BridgeBase | 0x0EdF28403D027Be0917625C751c78236407dD4E0 |
| Base Mainnet | USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Stacks Mainnet | wrapped-usdc-v5 | SP1MTYHV6K2FNH3QNF4P5QXS9VJ3XZ0GBB5T1SJPK.wrapped-usdc-v5 |
| Stacks Mainnet | sip-010-trait | SP1MTYHV6K2FNH3QNF4P5QXS9VJ3XZ0GBB5T1SJPK.sip-010-trait-ft-standard |
| Stacks Mainnet | USDCx (Circle) | SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE.usdcx |
| Service | URL |
|---|---|
| Base RPC | https://mainnet.base.org |
| Base Explorer | https://basescan.org |
| Stacks API | https://api.hiro.so |
| Stacks Explorer | https://explorer.hiro.so |
// From owner wallet
await bridgeBase.pause();// Only owner can call
await bridgeBase.emergencyWithdraw(safeAddress, amount);// Remove and add new signer
await bridgeBase.removeSigner(compromisedAddress);
await bridgeBase.addSigner(newSignerAddress);