Skip to main content

Broadcasting Transactions

Sending Transactions via Echo

Fiber's users can leverage Echo a standalone product, developed by Chainbound, to easily broadcast transactions directly to block builders and validators.

info

Here Echo's official documentation!

Echo has the following unique features:

  • UX & Convenience: Unified API for quick bundle submissions.
  • Front-running Protection: Direct transmissions to block builders, skipping the mempool to defend against MEV.
  • Maintenance & Upgrades: Automated system upgrades and updates with the latest builder API specifications.
  • Real-time Notifications: Immediate alerts once your bundle is included in a block via Fiber's execution_payloads stream.
  • Mempool Add-on Integration:
    • Use the SendTransactionSequence method from Fiber.
    • Allows global dispatch to both mempools and builders.
    • Ideal for rapid inclusion tasks like hack-prevention and Oracle updates.

You can obtain a free API key by reaching out to us at admin@chainbound.io or by joining our Discord and opening a ticket.

Sending Transactions via Fiber

info

For users only interested in fast-inclusion, Fiber allows to bypass Echo's methods and inject transactions directly to the p2p network, guaranteeing low latency and reliable propagation globally.

We currently support 4 methods for sending transactions & bundles directly via Fiber:

MethodDescription
SendTransactionMethod for sending signed transactions (with the v, r, s fields populated) to the Fiber Network. Fiber propagates these transactions to all other Fiber nodes, as well as all full nodes it's connected to.
SendRawTransactionMethod for sending a signed RLP encoded transaction. This is useful when you’re using libraries like ethers or web3.js with JavaScript, or ethers-rs with Rust.
SendTransactionSequenceMethod for sending a sequence of transactions to be propagated across the network, to ensure that they arrive at the block producer in the correct order.
SendRawTransactionSequenceSame as above, but for RLP encoded signed transactions.
info

The SendTransactionSequence and SendRawTransactionSequence methods don't guarantee that the transactions will land in order on-chain. They will be sent in sequence, but the final ordering will be determined by the block producer for the block in which they're included.

Here you can find the endpoints implementation in the different packages:

SendTransaction

In Go, the method takes a go-ethereum.types.Transaction as a parameter. This plays along nicely with any code that's already using go-ethereum as a library.

import (
"context"
"log"
"math/big"
"time"

fiber "github.com/chainbound/fiber-go"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)


func main() {
// Connection process omitted
...

// Example transaction
tx := types.NewTx(&types.DynamicFeeTx{
Nonce: nonce,
To: common.HexToAddress("0x...."),
Value: big.NewInt(100),
Gas: 21000,
GasFeeCap: big.NewInt(x),
GasTipCap: big.NewInt(y),
Data: nil,
})

// Get your private key
pk, _ := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
// Create a signer
signer := types.NewLondonSigner(common.Big1)

// Sign the transaction. Note that this doesn't RLP encode the transaction
signed, err := types.SignTx(tx, signer, pk)
if err != nil {
log.Fatal(err)
}

// Create a timeout for sending
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

// Returns:
// * Transaction hash
// * Unix timestamp in microseconds at which the node saw the transaction (useful for debugging latency issues)
// * Any potential errors
hash, timestamp, err := client.SendTransaction(ctx, signed)
if err != nil {
log.Fatal(err)
}

}

SendRawTransaction

import (
"context"
"log"
"math/big"
"time"

fiber "github.com/chainbound/fiber-go"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)


func main() {
// Connection process omitted
...

// Example transaction
tx := types.NewTx(&types.DynamicFeeTx{
Nonce: nonce,
To: common.HexToAddress("0x...."),
Value: big.NewInt(100),
Gas: 21000,
GasFeeCap: big.NewInt(x),
GasTipCap: big.NewInt(y),
Data: nil,
})

// Get your private key
pk, _ := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
// Create a signer
signer := types.NewLondonSigner(common.Big1)

// Sign the transaction. Note that this doesn't RLP encode the transaction
signed, err := types.SignTx(tx, signer, pk)
if err != nil {
log.Fatal(err)
}

// Create a timeout for sending
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

raw, err := signed.MarshalBinary()
if err != nil {
log.Fatal(err)
}


// Returns:
// * Transaction hash
// * Unix timestamp in microseconds at which the node saw the transaction (useful for debugging latency issues)
// * Any potential errors
hash, timestamp, err := client.SendRawTransaction(ctx, raw)
if err != nil {
log.Fatal(err)
}

}

SendTransactionSequence

Sends multiple signed transactions in a sequence at the p2p level.

caution

This is not a bundle like in Flashbots.
The final transaction ordering is determined by the block producer.

import (
"context"
"log"
"math/big"
"time"

fiber "github.com/chainbound/fiber-go"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

func main() {

...

// Example transaction
tx := types.NewTx(&types.DynamicFeeTx{
Nonce: nonce,
To: common.HexToAddress("0x...."),
Value: big.NewInt(100),
Gas: 21000,
GasFeeCap: big.NewInt(x),
GasTipCap: big.NewInt(y),
Data: nil,
})

pk, _ := crypto.HexToECDSA("PRIVATE_KEY")
signer := types.NewLondonSigner(common.Big1)

signed, err := types.SignTx(tx, signer, pk)
if err != nil {
log.Fatal(err)
}

// type should be *types.Transaction (but signed, e.g. v,r,s fields filled in)
// (this is the transaction that will be sent first)
target := someTargetTransaction

hashes, timestamp, err := client.SendTransactionSequence(ctx, target, signed)
if err != nil {
log.Fatal(err)
}

doSomething(hashes, timestamp)
}

SendRawTransactionSequence

Exactly the same as above, but this time our transactions have to be RLP encoded. Check the SendRawTransaction section for full examples of signing and RLP encoding transactions.

import (
"context"
"log"
"math/big"
"time"

fiber "github.com/chainbound/fiber-go"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)


func main() {
// Connection process, signing transaction omitted
...

bytes, err := signed.MarshalBinary()
if err != nil {
log.Fatal(err)
}

// Type should be []byte
targetTransaction := someTargetTransaction

hashes, timestamp, err := client.SendRawTransactionSequence(ctx, targetTransaction, bytes)
if err != nil {
log.Fatal(err)
}

doSomething(hashes, timestamp)
}