EstimateMessageFee
Estimates the L2 fee of a message sent from Ethereum L1 to Starknet L2. This method is specifically for cross-layer messaging where an L1 contract sends a message to an L2 contract with an l1_handler function. The estimation helps determine the gas costs before sending an actual L1->L2 message.
Method Signature
func (provider *Provider) EstimateMessageFee(
ctx context.Context,
msg MsgFromL1,
blockID BlockID,
) (MessageFeeEstimation, error)Source: contract.go
Parameters
ctx(context.Context): Context for request cancellation and timeoutmsg(MsgFromL1): The L1->L2 message to estimate fees forblockID(BlockID): Block identifier specifying the state to estimate against
Returns
MessageFeeEstimation: Fee estimation for the L1->L2 message in WEIerror: Error if the request fails
Type Definitions
- Input Types (
MsgFromL1,BlockID) define the cross-layer message (L1 sender, L2 recipient, payload) and the block state to estimate against. - Result Type (
MessageFeeEstimation) returned fee estimate that embedsFeeEstimationCommonand specifies the unit as WEI (Ethereum L1 fees). - Common Fee Fields (
FeeEstimationCommon) shared structure containing gas consumption and pricing details (L1 gas, L2 gas, data gas, and overall fee).
The method accepts a MsgFromL1 (with Ethereum L1 sender address and Starknet L2 recipient) and returns a MessageFeeEstimation in WEI. Unlike EstimateFee which returns FRI for L2 transactions, this returns WEI because L1→L2 messages are initiated on Ethereum and require L1 gas payment.
MsgFromL1
type MsgFromL1 struct {
// FromAddress The address of the L1 contract sending the message
FromAddress string `json:"from_address"`
// ToAddress The target L2 address the message is sent to
ToAddress *felt.Felt `json:"to_address"`
// EntryPointSelector The selector of the l1_handler in invoke in the target contract
Selector *felt.Felt `json:"entry_point_selector"`
// Payload The payload of the message
Payload []*felt.Felt `json:"payload"`
}Source: types_transaction_receipt.go
MessageFeeEstimation
type MessageFeeEstimation struct {
FeeEstimationCommon
// Units in which the fee is given, can only be WEI
Unit PriceUnitWei `json:"unit"`
}Source: types.go
FeeEstimationCommon
type FeeEstimationCommon struct {
// The Ethereum gas consumption of the transaction, charged for L1->L2
// messages and, depending on the block's DA_MODE, state diffs
L1GasConsumed *felt.Felt `json:"l1_gas_consumed"`
// The gas price (in wei or fri, depending on the tx version) that was
// used in the cost estimation.
L1GasPrice *felt.Felt `json:"l1_gas_price"`
// The L2 gas consumption of the transaction
L2GasConsumed *felt.Felt `json:"l2_gas_consumed"`
// The L2 gas price (in wei or fri, depending on the tx version) that
// was used in the cost estimation.
L2GasPrice *felt.Felt `json:"l2_gas_price"`
// The Ethereum data gas consumption of the transaction.
L1DataGasConsumed *felt.Felt `json:"l1_data_gas_consumed"`
// The data gas price (in wei or fri, depending on the tx version) that
// was used in the cost estimation.
L1DataGasPrice *felt.Felt `json:"l1_data_gas_price"`
// The estimated fee for the transaction (in wei or fri, depending on the
// tx version), equals to gas_consumed*gas_price + data_gas_consumed*data_gas_price.
OverallFee *felt.Felt `json:"overall_fee"`
}Source: types.go
BlockID
The blockID parameter specifies which block state to query. You can identify a block in three ways:
type BlockID struct {
Number *uint64 `json:"block_number,omitempty"`
Hash *felt.Felt `json:"block_hash,omitempty"`
Tag BlockTag `json:"tag,omitempty"`
}Source: types_block.go
There are helper functions for creating BlockID. So Instead of manually creating the BlockID struct, use these convenience functions.
-
By Block Number - Get a specific block by its height
blockID := rpc.WithBlockNumber(123456)Source: block.go
-
By Block Hash - Get a specific block by its hash
hash, _ := new(felt.Felt).SetString("0x1234...") blockID := rpc.WithBlockHash(hash)Source: block.go
-
By Block Tag - Get a dynamic block reference
blockID := rpc.WithBlockTag("latest") // Latest accepted block blockID := rpc.WithBlockTag("pending") // Block currently being builtSource: block.go
Usage Example
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/NethermindEth/starknet.go/utils"
"github.com/joho/godotenv"
)
func main() {
// Load environment variables from .env file
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
// Get RPC URL from environment variable
rpcURL := os.Getenv("STARKNET_RPC_URL")
if rpcURL == "" {
log.Fatal("STARKNET_RPC_URL not found in .env file")
}
// Initialize provider
provider, err := rpc.NewProvider(context.Background(), rpcURL)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// L1 contract address (Ethereum address)
fromAddress := "0x8453fc6cd1bcfe8d4dfc069c400b433054d47bdc"
// L2 contract address (Starknet address)
toAddress, err := utils.HexToFelt("0x04c5772d1914fe6ce891b64eb35bf3522aeae1315647314aac58b01137607f3f")
if err != nil {
log.Fatal(err)
}
// L1 handler selector
selector, err := utils.HexToFelt("0x1b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb19")
if err != nil {
log.Fatal(err)
}
// Message payload
payload, err := utils.HexArrToFelt([]string{
"0x455448",
"0x2f14d277fc49e0e2d2967d019aea8d6bd9cb3998",
"0x02000e6213e24b84012b1f4b1cbd2d7a723fb06950aeab37bedb6f098c7e051a",
"0x01a055690d9db80000",
"0x00",
})
if err != nil {
log.Fatal(err)
}
// Create L1->L2 message
l1Handler := rpc.MsgFromL1{
FromAddress: fromAddress,
ToAddress: toAddress,
Selector: selector,
Payload: payload,
}
// Estimate message fee
blockNumber := uint64(523066)
result, err := provider.EstimateMessageFee(ctx, l1Handler, rpc.WithBlockNumber(blockNumber))
if err != nil {
log.Fatal(err)
}
resultJSON, _ := json.MarshalIndent(result, "", " ")
fmt.Printf("Estimate Message Fee:\n%s\n", resultJSON)
}Error Handling
result, err := provider.EstimateMessageFee(ctx, l1Handler, rpc.WithBlockNumber(blockNumber))
if err != nil {
if errors.Is(err, rpc.ErrContractError) {
log.Printf("Contract error: L1 handler not found or execution failed")
return
}
if errors.Is(err, rpc.ErrBlockNotFound) {
log.Printf("Block not found")
return
}
log.Printf("Error estimating message fee: %v", err)
return
}
fmt.Printf("Overall fee: %s WEI\n", result.OverallFee)Common Use Cases
- Estimating gas costs before sending L1->L2 messages from Ethereum to Starknet.
- Validating that L1 handler functions exist and can be invoked with the given payload.
- Comparing message fees across different block states.

