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
npm install @signet-sh/sdk viemCreate clients
Viem uses separate clients for reads and writes. Signet spans two chains, so you need a pair for each:
| Client | Chain | Purpose |
|---|---|---|
signetPublicClient | Signet | Read balances, events, contract state |
signetWalletClient | Signet | Create orders, approve tokens |
hostPublicClient | Ethereum | Read fill events, check balances |
hostWalletClient | Ethereum | Enter Signet via Passage, fill orders |
The SDK provides pre-configured Viem chain definitions for both:
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:
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:
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
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:
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:
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:
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:
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:
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:
cargo add signet-constants signet-typesAdditional crates for specific features:
| Crate | Purpose |
|---|---|
signet-constants | Chain configs, contract addresses, token addresses |
signet-types | Order and fill types, EIP-712 signing |
signet-tx-cache | Submit transactions, orders, and bundles to the tx-cache |
signet-orders | OrderSender for long-running services |
signet-zenith | Zenith contract bindings |
signet-bundle | Bundle construction (SignetEthBundle) |
Create providers
Signet spans two chains, so you need a provider for each:
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:
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:
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:
forge install https://github.com/init4tech/zenith
forge install https://github.com/init4tech/signet-solContract 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:
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:
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>| Chain | RPC URL |
|---|---|
| Signet (Parmigiana) | https://rpc.parmigiana.signet.sh |
| Ethereum (Host) | https://host-rpc.parmigiana.signet.sh |
Check RPC health
curl -s -X POST $SIGNET_RPC \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'Check balances
# Native balance on Signet
cast balance 0xYourAddress --rpc-url $SIGNET_RPC
# Native balance on Ethereum host
cast balance 0xYourAddress --rpc-url $HOST_RPCRead contracts
# 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_RPCContract addresses and ABIs
All contract addresses and ABI downloads are on the Parmigiana quickstart page.