TESTNET ONLINE: PECORINO PECORINO

Passage Integration Guide

The Passage contract helps move assets from Ethereum to Signet instantly. Signet nodes listen for Passage events, and mint tokens on Signet in the same block.

Consult the Pecorino Quickstart guide for contract addresses and RPC endpoints.

Setup

  • The Passage ABI can be downloaded here: Passage ABI.
    • $ curl -O https://signet.sh/docs/abis/Passage.abi.json
  • The Passage Contract is also open source.
    • $ forge install https://github.com/init4tech/zenith
  • Rust alloy bindings are available via the signet-bindings crate.
    • $ cargo add signet-bindings

Overview

Passage.sol exposes a simple interface:

  • enter(address rollupRecipient)
    • move ETH to Signet.
    • fallback and receive - also move ETH to Signet, with msg.sender as recipient.
  • enterToken(address rollupRecipient, address token, uint256 amount)
    • move ERC-20 tokens to Signet
    • requires prior approve of Passage to spend tokens

Passage can be observed using the Enter and EnterToken events. The Enter event is fired whenever ETH is bridging to Signet, and EnterToken is fired when ERC-20 tokens are bridged. The Signet node listens for these events directly, so if the event fired, the tokens were bridged.

Supported Assets

Signet currently supports bridging the following assets from Ethereum:

  • ETH
  • USDC
  • USDT
  • WBTC

Custom assets can be created on Signet but cannot be bridged from Ethereum. Contact the Signet team to add support for additional assets.

Code Examples

Solidity

Passage is available as a Solidity contract or ABI. See the Setup section above.

 1contract YourContract {
 2    Passage public passage;
 3    IERC20 public token;
 4
 5    constructor(address _passage, IERC20 _token) {
 6        passage = Passage(_passage);
 7        token = _token;
 8        token.approve(_passage, type(uint256).max);
 9    }
10
11    // Enter on behalf of a user
12    function bridgeETH() external payable {
13        passage.enter{value: msg.value}(msg.sender);
14    }
15
16    // Move ERC-20 tokens to Signet
17    function bridgeToken(uint256 amount) external {
18        token.transferFrom(msg.sender, address(this), amount);
19        passage.enterToken(address(token), msg.sender);
20    }
21}

Cast

To enter the rollup, Send ETH directly to the Passage contract:

1# Using cast
2cast send $PASSAGE_ADDRESS \
3  --value 1ether \
4  --rpc-url $HOST_CHAIN_RPC \
5  --private-key $PRIVATE_KEY

Entering with tokens requires multiple transactions:

 1# 1. Approve Passage
 2cast send $TOKEN_ADDRESS \
 3  "approve(address,uint256)" \
 4  $PASSAGE_ADDRESS, $AMOUNT \
 5  --rpc-url $HOST_CHAIN_RPC \
 6  --private-key $PRIVATE_KEY
 7
 8# 2. Call Passage to bridge tokens
 9# See Passage.sol for the exact function name and parameters
10cast send $PASSAGE_ADDRESS \
11  "enterTokens(address,address,uint256)" \
12  $SIGNET_RECIPIENT, $TOKEN_ADDRESS, $AMOUNT \
13  --rpc-url $HOST_CHAIN_RPC \
14  --private-key $PRIVATE_KEY

Typescript

1// Using ethers.js
2const tx = await signer.sendTransaction({
3  to: PASSAGE_ADDRESS,
4  value: ethers.parseEther("1.0"),
5});
6await tx.wait();

Listen for events:

 1import { ethers } from "ethers";
 2
 3const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
 4const passage = new ethers.Contract(
 5  PASSAGE_ADDRESS,
 6  PASSAGE_ABI, // Get ABI from contract source
 7  provider
 8);
 9
10// Listen for events
11// Event names and signatures from Passage.sol
12passage.on("Enter", (...args) => {
13  console.log("ETH bridged:", args);
14});
15
16passage.on("EnterToken", (...args) => {
17  console.log("Token bridged:", args);
18});

Rust

1let passage = PassageContract::new(
2    signet_constants::pecorino::HOST_PASSAGE,
3    provider.clone(),
4);
5
6// Enter the rollup from your EOA
7passage.enter().value(eth_in_wei).send(rollup_recipient).await?;

Check Passage.sol for the exact event signatures.

Next Steps

  1. Send a rollup transaction using the Transactor contract.