🎓 A1Learner
🔍
0 of 45 lessons completed

Solidity Bridge Concepts

A bridge is a pair of contracts that locks value on one side and mints a matching token on the other side. The locked value backs the minted token, so the two sides must always match.

Locking records value on the first side

The lock contract records how much each holder has locked. Nothing is minted yet in this step.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LockBox {
    address public relayer;
    mapping(address => uint256) public locked;
    constructor(address relayerAddress) {
        relayer = relayerAddress;
    }
    function lock(uint256 amount) public {
        require(amount > 0, "amount must be above zero");
        locked[msg.sender] += amount;
    }
    function release(address user, uint256 amount) public {
        require(msg.sender == relayer, "only relayer");
        require(locked[user] >= amount, "locked balance too low");
        locked[user] -= amount;
    }
}

Minting creates the matching token

The mint contract creates wrapped tokens only when the relayer calls it. The relayer is the link between the two sides.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract WrappedMint {
    address public relayer;
    mapping(address => uint256) public balances;
    uint256 public totalSupply;
    constructor(address relayerAddress) {
        relayer = relayerAddress;
    }
    function mint(address to, uint256 amount) public {
        require(msg.sender == relayer, "only relayer");
        balances[to] += amount;
        totalSupply += amount;
    }
    function burn(uint256 amount) public {
        require(balances[msg.sender] >= amount, "balance too low");
        balances[msg.sender] -= amount;
        totalSupply -= amount;
    }
}

Only the relayer can mint

A stranger calling mint must fail, because an open mint could create unbacked tokens. The relayer call right after it succeeds.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract WrappedMint {
    address public relayer;
    mapping(address => uint256) public balances;
    uint256 public totalSupply;
    constructor(address relayerAddress) {
        relayer = relayerAddress;
    }
    function mint(address to, uint256 amount) public {
        require(msg.sender == relayer, "only relayer");
        balances[to] += amount;
        totalSupply += amount;
    }
    function burn(uint256 amount) public {
        require(balances[msg.sender] >= amount, "balance too low");
        balances[msg.sender] -= amount;
        totalSupply -= amount;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract LockBox {
    address public relayer;
    mapping(address => uint256) public locked;
    constructor(address relayerAddress) {
        relayer = relayerAddress;
    }
    function lock(uint256 amount) public {
        require(amount > 0, "amount must be above zero");
        locked[msg.sender] += amount;
    }
    function release(address user, uint256 amount) public {
        require(msg.sender == relayer, "only relayer");
        require(locked[user] >= amount, "locked balance too low");
        locked[user] -= amount;
    }
}

contract WrappedMint {
    address public relayer;
    mapping(address => uint256) public balances;
    uint256 public totalSupply;
    constructor(address relayerAddress) {
        relayer = relayerAddress;
    }
    function mint(address to, uint256 amount) public {
        require(msg.sender == relayer, "only relayer");
        balances[to] += amount;
        totalSupply += amount;
    }
    function burn(uint256 amount) public {
        require(balances[msg.sender] >= amount, "balance too low");
        balances[msg.sender] -= amount;
        totalSupply -= amount;
    }
}

Key points

  • Bridge: A bridge is a pair of contracts that locks value on one side and mints a matching token on the other side.
  • Lock: Locking records value held on the first side until it is released.
  • Relayer: A relayer is a trusted account that carries a message from one side to the other.
  • Mint: Minting creates the matching token on the second side after a lock.
  • Burn: Burning destroys the matching token when the holder wants the locked value back.
  • Restatement: A bridge locks on one side, mints the same amount on the other side, and burns to unlock.

In this example, the holder locks 100 in the lock contract, the relayer mints 100 wrapped tokens, the holder burns all 100, and the relayer releases the 100 locked value. Each step checks the exact balance, so the loop ends where it started.

Note

Real bridges span two chains, and this lesson simulates the two sides with two contracts on the local chain only.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

A holder locks 100 and the relayer mints wrapped tokens. How many wrapped tokens should the relayer mint?