DEV Community

flat cash
flat cash

Posted on Originally published at flat.cash

BearerSwap: Private Crypto Transfers Without Mixers or ZK Proofs

BearerSwap: Private Crypto Transfers Without Mixers or ZK Proofs

Private transfers on Ethereum have historically relied on either mixers (like Tornado Cash) or zero-knowledge proofs (like Railgun). Both approaches introduce complexity, regulatory scrutiny, or computational overhead. BearerSwap, part of the FLAT Protocol, offers a simpler alternative: private transfers via bearer instruments, similar to physical cash.

Unlike traditional transfers where the chain records a direct movement between addresses, BearerSwap breaks the on-chain link by treating transfers as mint-and-burn operations. The sender creates a one-time token (a "bearer instrument"), and the recipient claims it—without ever revealing the connection between them.

Here’s how it works, why it’s different, and how you can use it today.


How BearerSwap Works

BearerSwap enables private transfers in three steps:

  1. Mint a Bearer Token
    The sender creates a new ERC-20 token (a "bearer instrument") with a unique identifier. This token is minted to a null address (effectively burned on creation), but its metadata is stored off-chain in a registry. The sender retains a private claim to this token.

  2. Transfer the Claim
    The sender shares the token’s claim (e.g., a signed message or secret) with the recipient off-chain. This could be via encrypted chat, email, or even physical transfer—just like handing over cash.

  3. Claim the Token
    The recipient uses the claim to redeem the token on-chain. The contract burns the old token (if it existed) and mints a new one to the recipient’s address. The chain only sees a mint and burn, not a transfer between two known addresses.

This process ensures no direct link between sender and recipient is ever recorded on-chain.


Comparison: BearerSwap vs. Tornado Cash vs. Railgun

Feature BearerSwap Tornado Cash Railgun (ZK)
Privacy Mechanism Bearer tokens (mint+burn) Mixer (pool-based) Zero-knowledge proofs
Regulatory Status Not sanctioned Sanctioned by OFAC Not sanctioned
Complexity Low (simple mint/burn) Medium (pool management) High (ZK circuit setup)
Gas Cost ~50–80k gas per operation ~100–150k gas (deposit/withdraw) ~500k+ gas (ZK proof verification)
Trust Assumptions Trust in protocol registry Trust in mixer operators Trust in ZK circuit
Censorship Risk Low (no mixer contracts) High (sanctioned) Low

Key Advantages of BearerSwap:

  • No mixers: Avoids the regulatory and operational risks of pool-based privacy.
  • No ZK proofs: Eliminates the complexity and gas costs of zero-knowledge systems.
  • Works on mainnet: Fully compatible with Ethereum, no L2 or sidechain required.

Limitations:

  • Off-chain trust: The protocol relies on a registry to track bearer tokens. If the registry is compromised or malicious, privacy could be broken.
  • No formal audit: As of writing, BearerSwap is experimental and not yet audited.

Code Example: Using BearerSwap via MCP

The FLAT Protocol provides a Multi-Chain Protocol (MCP) interface for interacting with BearerSwap. Below is a Python example showing how an agent (e.g., a bot or service) would mint and claim a bearer token.

Prerequisites

  • Python 3.8+
  • web3.py (pip install web3)
  • Ethereum node (e.g., Infura, Alchemy)
  • BearerSwap contract address (see flat.cash/docs)

Example: Mint and Claim a Bearer Token

from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_defunct

# Configuration
RPC_URL = "https://mainnet.infura.io/v3/YOUR_INFURA_KEY"
CONTRACT_ADDRESS = "0x..."  # BearerSwap contract
PRIVATE_KEY = "0x..."       # Sender's private key
RECIPIENT_ADDRESS = "0x..." # Recipient's address

# Initialize Web3
w3 = Web3(Web3.HTTPProvider(RPC_URL))
if not w3.is_connected():
    raise Exception("Failed to connect to Ethereum")

# Load contract ABI (simplified; use full ABI in production)
abi = [
    {
        "inputs": [
            {"internalType": "address", "name": "recipient", "type": "address"},
            {"internalType": "bytes32", "name": "tokenId", "type": "bytes32"}
        ],
        "name": "claim",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {"internalType": "bytes32", "name": "tokenId", "type": "bytes32"}
        ],
        "name": "mint",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
]
contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=abi)

# Step 1: Mint a bearer token (sender)
token_id = w3.keccak(text="unique_token_id_123")  # Generate a unique ID
sender_account = Account.from_key(PRIVATE_KEY)

mint_tx = contract.functions.mint(token_id).build_transaction({
    "from": sender_account.address,
    "nonce": w3.eth.get_transaction_count(sender_account.address),
    "gas": 100000,
    "gasPrice": w3.eth.gas_price
})

signed_tx = sender_account.sign_transaction(mint_tx)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print(f"Minted token {token_id.hex()}: https://etherscan.io/tx/{tx_hash.hex()}")

# Step 2: Off-chain transfer (sender shares `token_id` with recipient)
# In practice, this could be encrypted or sent via a secure channel.
print(f"Share this token ID with recipient: {token_id.hex()}")

# Step 3: Claim the token (recipient)
recipient_account = Account.create()  # In practice, use recipient's private key
claim_tx = contract.functions.claim(RECIPIENT_ADDRESS, token_id).build_transaction({
    "from": recipient_account.address,  # Simplified; recipient signs
    "nonce": w3.eth.get_transaction_count(recipient_account.address),
    "gas": 100000,
    "gasPrice": w3.eth.gas_price
})

# Recipient signs and sends the claim transaction
signed_claim_tx = recipient_account.sign_transaction(claim_tx)
claim_hash = w3.eth.send_raw_transaction(signed_claim_tx.rawTransaction)
print(f"Claimed token for {RECIPIENT_ADDRESS}: https://etherscan.io/tx/{claim_hash.hex()}")
Enter fullscreen mode Exit fullscreen mode

Notes:

  1. Token ID: The token_id should be unique and unpredictable to prevent front-running or replay attacks.
  2. Off-Chain Transfer: The actual transfer of the token_id (step 2) happens off-chain. Use encryption (e.g., PGP, Signal) for security.
  3. Gas Costs: Minting and claiming cost ~50–80k gas each, making it cheaper than ZK-based solutions.

Limitations and Future Work

While BearerSwap offers a novel approach to private transfers, it has key limitations:

  1. Trust in Registry: The protocol relies on a registry to track bearer tokens. If the registry is compromised or malicious, privacy could be broken. A decentralized registry (e.g., IPFS + ENS) could mitigate this.
  2. No Formal Audit: BearerSwap is experimental. A security audit would be required for production use.
  3. Off-Chain Complexity: The off-chain transfer of claims introduces operational overhead (e.g., secure channels, key management).

Future Improvements:

  • Decentralized Registry: Use IPFS or Arweave to store token metadata, reducing reliance on a single registry.
  • Batch Operations: Support batch minting/claiming to reduce gas costs.
  • EIP-712 Signatures: Improve the security of off-chain claims with typed signatures.

Conclusion

BearerSwap demonstrates that private transfers don’t require mixers or ZK proofs. By leveraging bearer instruments—conceptually similar to physical cash—it achieves privacy with minimal on-chain complexity. While not a silver bullet, it offers a pragmatic alternative for users seeking simplicity and regulatory neutrality.

For developers, the MCP interface makes integration


For the full protocol reference, see docs.flat.cash.

Top comments (0)