Skip to main content

Getting started

Install dependencies and configure clients for Signet development.

New to Signet? Start with Learn about Signet for background on how the system works.

Select your language to see the relevant setup instructions. Get testnet tokens from the Parmigiana quickstart.

These docs use Viem as the Ethereum interface. Any library that can send transactions and read contract state will work (ethers.js, web3.js, etc.), but the SDK is built on Viem and the examples use it throughout. You should be comfortable with Ethereum basics: wallets, transactions, ABIs, and ERC-20 tokens.

All examples target the Parmigiana testnet. To follow along, grab testnet ETH and USD from the faucet.

Install the SDK

bash
npm install @signet-sh/sdk viem

Create clients

Viem uses separate clients for reads and writes. Signet spans two chains, so you need a pair for each:

ClientChainPurpose
signetPublicClientSignetRead balances, events, contract state
signetWalletClientSignetCreate orders, approve tokens
hostPublicClientEthereumRead fill events, check balances
hostWalletClientEthereumEnter Signet via Passage, fill orders

The SDK provides pre-configured Viem chain definitions for both:

typescript
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { parmigianaRollup, parmigianaHost } from '@signet-sh/sdk/constants';

// Signet (rollup) clients
const signetPublicClient = createPublicClient({
  chain: parmigianaRollup,
  transport: http(),
});

const signetWalletClient = createWalletClient({
  account: privateKeyToAccount('0x...'),
  chain: parmigianaRollup,
  transport: http(),
});

// Ethereum (host) clients
const hostPublicClient = createPublicClient({
  chain: parmigianaHost,
  transport: http(),
});

const hostWalletClient = createWalletClient({
  account: privateKeyToAccount('0x...'),
  chain: parmigianaHost,
  transport: http(),
});

When you see hostWalletClient.writeContract(...) in later pages, that’s a transaction on Ethereum. signetWalletClient.writeContract(...) is on Signet.

Without the SDK

Define the chains manually with Viem’s defineChain:

typescript
import { createPublicClient, createWalletClient, defineChain, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const signetParmigiana = defineChain({
  id: 88888,
  name: 'Parmigiana Testnet',
  nativeCurrency: { name: 'USD', symbol: 'USD', decimals: 18 },
  rpcUrls: {
    default: { http: ['https://rpc.parmigiana.signet.sh'] },
  },
  blockExplorers: {
    default: {
      name: 'Signet Explorer',
      url: 'https://explorer.parmigiana.signet.sh',
    },
  },
});

const signetHost = defineChain({
  id: 3151908,
  name: 'Parmigiana Host',
  nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
  rpcUrls: {
    default: { http: ['https://host-rpc.parmigiana.signet.sh'] },
  },
  blockExplorers: {
    default: {
      name: 'Host Explorer',
      url: 'https://explorer-host.parmigiana.signet.sh',
    },
  },
});

const signetPublicClient = createPublicClient({ chain: signetParmigiana, transport: http() });
const signetWalletClient = createWalletClient({
  account: privateKeyToAccount('0x...'),
  chain: signetParmigiana,
  transport: http(),
});
const hostPublicClient = createPublicClient({ chain: signetHost, transport: http() });
const hostWalletClient = createWalletClient({
  account: privateKeyToAccount('0x...'),
  chain: signetHost,
  transport: http(),
});

Reading balances

Use the SDK’s token helpers to resolve addresses:

typescript
import { PARMIGIANA } from '@signet-sh/sdk/constants';
import { getTokenAddress } from '@signet-sh/sdk/tokens';
import { erc20Abi } from 'viem';

const weth = getTokenAddress('WETH', PARMIGIANA.rollupChainId, PARMIGIANA);

const balance = await signetPublicClient.readContract({
  address: weth,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [userAddress],
});
Without the SDK
typescript
import { erc20Abi } from 'viem';

const wethAddress = '0x...'; // WETH contract on Signet

const balance = await signetPublicClient.readContract({
  address: wethAddress,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [userAddress],
});

Working with tokens

List available tokens

Query which tokens exist on each chain:

typescript
import { PARMIGIANA } from '@signet-sh/sdk/constants';
import { getAvailableTokens } from '@signet-sh/sdk/tokens';

const rollupTokens = getAvailableTokens(PARMIGIANA.rollupChainId, PARMIGIANA);
// ['ETH', 'WETH', 'WBTC', 'USDC', 'USDT', 'WUSD', 'USD']

const hostTokens = getAvailableTokens(PARMIGIANA.hostChainId, PARMIGIANA);
// ['ETH', 'WETH', 'WBTC', 'USDC', 'USDT']

Resolve tokens across chains

mapTokenCrossChain returns the equivalent symbol, or undefined if no cross-chain counterpart exists:

typescript
import { mapTokenCrossChain } from '@signet-sh/sdk/tokens';

mapTokenCrossChain('WETH'); // 'WETH'
mapTokenCrossChain('USDC'); // 'USDC'
mapTokenCrossChain('WUSD'); // undefined (no host equivalent)

resolveTokenSymbol turns a contract address back into a symbol:

typescript
import { resolveTokenSymbol } from '@signet-sh/sdk/tokens';
import { PARMIGIANA } from '@signet-sh/sdk/constants';

const symbol = resolveTokenSymbol(tokenAddress, PARMIGIANA.rollupChainId, PARMIGIANA);
// 'WETH', 'USDC', etc.

Check if ETH needs wrapping

needsWethWrap returns true when a flow requires WETH rather than native ETH:

typescript
import { needsWethWrap } from '@signet-sh/sdk/tokens';

needsWethWrap('ETH', 'hostToRollup', 'passage'); // false (Passage accepts native ETH)
needsWethWrap('ETH', 'hostToRollup', 'orders');  // true  (orders need WETH)
needsWethWrap('USDC', 'hostToRollup', 'orders'); // false (already ERC-20)

wrapEth and unwrapEth handle the conversion:

typescript
import { PARMIGIANA } from '@signet-sh/sdk/constants';
import { getTokenAddress } from '@signet-sh/sdk/tokens';
import { wethAbi } from '@signet-sh/sdk/abi';
import { parseEther } from 'viem';

// Wrap ETH to WETH
const weth = getTokenAddress('WETH', PARMIGIANA.rollupChainId, PARMIGIANA);
await signetWalletClient.writeContract({
  address: weth,
  abi: wethAbi,
  functionName: 'deposit',
  value: parseEther('1'),
});

The Signet Rust SDK is a collection of crates built on alloy. Install the core crates:

bash
cargo add signet-constants signet-types

Additional crates for specific features:

CratePurpose
signet-constantsChain configs, contract addresses, token addresses
signet-typesOrder and fill types, EIP-712 signing
signet-tx-cacheSubmit transactions, orders, and bundles to the tx-cache
signet-ordersOrderSender for long-running services
signet-zenithZenith contract bindings
signet-bundleBundle construction (SignetEthBundle)

Create providers

Signet spans two chains, so you need a provider for each:

rust
use alloy::providers::{Provider, ProviderBuilder};

// Signet (rollup) provider
let signet_provider = ProviderBuilder::new()
    .on_http("https://rpc.parmigiana.signet.sh".parse()?);

// Ethereum (host) provider
let host_provider = ProviderBuilder::new()
    .on_http("https://host-rpc.parmigiana.signet.sh".parse()?);

For signing transactions, add a wallet:

rust
use alloy::{
    network::EthereumWallet,
    providers::ProviderBuilder,
    signers::local::PrivateKeySigner,
};

let signer: PrivateKeySigner = "0x...".parse()?;
let wallet = EthereumWallet::from(signer);

let signet_provider = ProviderBuilder::new()
    .wallet(wallet)
    .on_http("https://rpc.parmigiana.signet.sh".parse()?);

Contract addresses and chain config

The signet-constants crate provides all addresses and chain IDs for each network:

rust
use signet_constants::parmigiana;

// Chain IDs
let rollup_chain_id = parmigiana::ROLLUP_CHAIN_ID; // 88888
let host_chain_id = parmigiana::HOST_CHAIN_ID;     // 3151908

// Token addresses
let ru_weth = parmigiana::RU_WETH;
let host_weth = parmigiana::HOST_WETH;

// System constants (for order signing)
let constants = parmigiana::system_constants();

Signet contracts are part of the Zenith repository. Install with Foundry:

bash
forge install https://github.com/init4tech/zenith
forge install https://github.com/init4tech/signet-sol

Contract addresses and ABIs

All contract addresses, ABIs, and RPC endpoints are on the Parmigiana quickstart page.

In Solidity, use the SignetL2 base contract from signet-sol for automatic chain-aware address resolution:

solidity
import {SignetL2} from "./Signet.sol";

contract YourContract is SignetL2 {
    function doSomething() external {
        // Automatically has access to:
        // - rollupOrders: RollupOrders contract address
        // - permit2: Permit2 contract address
    }
}

Source: Signet.sol

These examples use Foundry’s cast and curl. No SDK required.

RPC endpoints

Set up environment variables for both chains:

bash
export SIGNET_RPC=https://rpc.parmigiana.signet.sh
export HOST_RPC=https://host-rpc.parmigiana.signet.sh
export TX_CACHE=https://transactions.parmigiana.signet.sh
export PRIVATE_KEY=<your private key>
export YOUR_ADDRESS=<your wallet address>
ChainRPC URL
Signet (Parmigiana)https://rpc.parmigiana.signet.sh
Ethereum (Host)https://host-rpc.parmigiana.signet.sh

Check RPC health

bash
curl -s -X POST $SIGNET_RPC \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

Check balances

bash
# Native balance on Signet
cast balance 0xYourAddress --rpc-url $SIGNET_RPC

# Native balance on Ethereum host
cast balance 0xYourAddress --rpc-url $HOST_RPC

Read contracts

bash
# Read a contract on Signet
cast call 0xContractAddress "balanceOf(address)(uint256)" 0xYourAddress \
  --rpc-url $SIGNET_RPC

# Read a contract on Ethereum host
cast call 0xContractAddress "functionName()" --rpc-url $HOST_RPC

Contract addresses and ABIs

All contract addresses and ABI downloads are on the Parmigiana quickstart page.

ESC

Start typing to search documentation...

Navigate Select ⌘K Open