Execute from Ethereum
Execute transactions from Ethereum to Signet with guaranteed inclusion
The Transactor contract provides guaranteed, censorship-resistant execution on Signet from Ethereum. Any Ethereum account can call Transactor to trigger a transaction on Signet that builders cannot censor or reorder.
Overview
Transactor.sol exposes two function signatures for sending transactions to Signet:
Default Chain
function transact(
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) external payableThis version uses the contract’s defaultRollupChainId configured at deployment.
Explicit Chain
function transact(
uint256 rollupChainId,
address to,
bytes calldata data,
uint256 value,
uint256 gas,
uint256 maxFeePerGas
) external payableUse this version when targeting a specific Signet chain in multi-chain deployments.
Calling either transact function results in a system transaction being issued on
Signet from the address of the caller. This allows Ethereum contracts to
own assets on Signet, and to trigger contract execution on Signet from
Ethereum with native authentication.
The value argument is the amount of USD in wei to send on the rollup.
USD is the native asset of Signet. This amount will be attached to the Signet
transaction. The balance of the originating address on Signet must be sufficient to
cover this value.
Transact events are executed at the end of the Signet block. Contract
creation via Transactor is not currently supported.
The Transactor emits a Transact event that can be monitored. The Signet node
listens for these events directly, so if the event fired, the transaction
was included in Signet, although the inner call may revert.
This page assumes you’ve set up your clients.
Sometimes an Ethereum contract needs to trigger execution on Signet: a DAO proposal passes, a governance timelock expires, a multisig approves a parameter change. The Transactor contract handles this. It guarantees transaction inclusion on Signet in the same block, bypassing builder discretion.
How Transactor works
The Transactor contract lives on Ethereum. You call transact with the Signet call you want to execute. The system creates a transaction on Signet from the caller’s address, executing at the end of the Signet block. No builder can exclude it.
Normally, builders choose which transactions to include in Signet blocks. If a builder has a reason to censor yours (MEV extraction, competitive pressure), you have no recourse through the normal path. Transactor bypasses builder discretion entirely.
When you need it
Direct Signet transactions are cheaper and simpler. Transactor exists for three situations:
Your logic lives on Ethereum. The Ethereum contract calls Transactor directly, with no off-chain relayer and no trust assumptions.
You need censorship resistance. The transaction executes unconditionally. This matters for liquidations, time-sensitive governance, or any operation where inclusion is non-negotiable.
You want L1 security for the trigger. The Ethereum transaction is the source of authority. Signet execution follows deterministically.
Contracts
| Contract | Address |
|---|---|
| Transactor (host) | 0x0B4fc18e78c585687E01c172a1087Ea687943db9 |
All contract addresses on this page are for Parmigiana testnet.
Execute a transaction
Encode the Signet call and submit through Transactor on Ethereum:
import { transactorAbi } from '@signet-sh/sdk/abi';
import { PARMIGIANA } from '@signet-sh/sdk/constants';
import { encodeFunctionData, parseGwei } from 'viem';
const innerData = encodeFunctionData({
abi: targetContractAbi,
functionName: 'someFunction',
args: [arg1, arg2],
});
const hash = await hostWalletClient.writeContract({
address: PARMIGIANA.hostTransactor,
abi: transactorAbi,
functionName: 'transact',
args: [
signetTargetAddress,
innerData,
0n, // USD value on Signet
1_000_000n, // gas limit
parseGwei('100'), // max fee per gas
],
});Without the SDK
import { encodeFunctionData, parseGwei } from 'viem';
const transactorAbi = [
{
name: 'transact',
type: 'function',
stateMutability: 'payable',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'data', type: 'bytes' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'maxFeePerGas', type: 'uint256' },
],
outputs: [],
},
] as const;
const innerData = encodeFunctionData({
abi: targetContractAbi,
functionName: 'someFunction',
args: [arg1, arg2],
});
const hash = await hostWalletClient.writeContract({
address: '0x0B4fc18e78c585687E01c172a1087Ea687943db9',
abi: transactorAbi,
functionName: 'transact',
args: [
signetTargetAddress,
innerData,
0n,
1_000_000n,
parseGwei('100'),
],
});Verifying execution
The Transact event on Ethereum confirms your transaction was included on Signet, but the inner call can still revert. Check the Signet transaction receipt for the corresponding block to confirm success.
const receipt = await hostPublicClient.waitForTransactionReceipt({ hash });
// Transact event confirms inclusion on Signet
// Check the Signet transaction receipt for the actual result
When to use something else
Next steps
- Passage moves assets to Signet without guaranteed inclusion
- EVM behavior covers address aliasing details for contract callers
This page assumes you’ve set up your providers.
The Transactor contract lives on Ethereum. You call transact with the Signet call you want to execute, and the system creates a transaction on Signet from the caller’s address. No builder can exclude it.
See Parmigiana Testnet for the Transactor contract address.
Execute a transaction
Encode the calldata for your target Signet contract, then call transact on the Transactor contract via the host chain provider:
use alloy::primitives::{utils::parse_units, Address, Bytes, U256};
use alloy::providers::{Provider, ProviderBuilder};
use signet_constants::parmigiana;
use signet_zenith::Transactor;
// Encode the function call for the target Signet contract
let calldata: Bytes = /* your encoded calldata */;
let transactor = Transactor::new(parmigiana::HOST_TRANSACTOR, &host_provider);
let tx = transactor
.transact_0(
signet_target_address, // target contract on Signet
calldata, // encoded function call
U256::ZERO, // USD value on Signet
U256::from(1_000_000), // gas limit
parse_units("100", "gwei")?.into(), // max fee per gas
)
.send()
.await?;
let receipt = tx.get_receipt().await?;Use transact_1 instead of transact_0 to specify an explicit rollup chain ID for multi-chain deployments.
Verifying execution
The Transact event on Ethereum confirms your transaction was included on Signet, but the inner call can still revert. Check the Signet transaction receipt for the corresponding block to confirm success.
Next steps
- Passage (Rust) – move assets without guaranteed inclusion
- EVM behavior – address aliasing details
This page assumes you’ve completed the getting started setup.
import {Transactor} from "zenith/src/Transactor.sol";
contract YourContract {
Transactor public transactor;
constructor(Transactor _transactor) {
transactor = _transactor;
}
// Execute a function on Signet from Ethereum
function executeOnSignet(
address signetTarget,
bytes calldata functionData
) external {
transactor.transact(
signetTarget,
functionData,
0, // USD value on Signet
1_000_000, // gas limit on Signet
100 gwei // max fee per gas (in USD) on Signet
);
}
// Emergency action on Signet
function emergencyAction(address signetContract) external onlyOwner {
// Encode the emergency function call
bytes memory data = abi.encodeWithSignature("emergencyPause()");
executeOnSignet(signetContract, data);
}
}Source
- Transactor.sol source code
- ABIs available on the Parmigiana quickstart
Call transact (default chain)
The simpler form uses the contract’s defaultRollupChainId:
cast send 0x0B4fc18e78c585687E01c172a1087Ea687943db9 \
"transact(address,bytes,uint256,uint256,uint256)" \
$SIGNET_TARGET \
$CALLDATA \
0 \
1000000 \
100000000000 \
--rpc-url $HOST_RPC \
--private-key $PRIVATE_KEYThe arguments:
- to: target contract address on Signet
- data: ABI-encoded calldata for the Signet function
- value: USD (in wei) to attach on Signet. The caller’s Signet balance must cover this.
- gas: gas limit for the Signet transaction
- maxFeePerGas: max fee per gas in USD (here, 100 gwei)
Call transact (explicit chain ID)
For multi-chain deployments, specify the rollup chain ID directly:
cast send 0x0B4fc18e78c585687E01c172a1087Ea687943db9 \
"transact(uint256,address,bytes,uint256,uint256,uint256)" \
88888 \
$SIGNET_TARGET \
$CALLDATA \
0 \
1000000 \
100000000000 \
--rpc-url $HOST_RPC \
--private-key $PRIVATE_KEY88888 is the Parmigiana rollup chain ID.
Encoding calldata
Use cast calldata to produce the $CALLDATA argument:
CALLDATA=$(cast calldata "someFunction(uint256,address)" 42 0xYourAddress)Then pass it to the transact call above.
Verifying execution
The Transact event on Ethereum confirms inclusion on Signet. Check for it in the transaction receipt:
cast receipt $TX_HASH --rpc-url $HOST_RPCThe inner call on Signet can still revert. Check the corresponding Signet block for the actual result.
All contract addresses on this page are for Parmigiana testnet.