How to Authenticate to the Operations-Relay
After establishing the connection to the operations-relay, solvers can authenticate themselves, by calling the solver_identify
method. This is needed to receive notifications (user operations) that certain dApps have restricted visibility to a subset of whitelisted solvers only.
info
Authentication is NOT a requirement, and is not necessary to receive standard notifications.
1. Generate and sign the payload
connect.go
package operations_relay
import (
"encoding/binary"
"fmt"
"time"
"github.com/FastLane-Labs/atlas-sdk-go/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
type Auth struct {
Address common.Address `json:"address"`
Timestamp uint64 `json:"timestamp"`
Signature hexutil.Bytes `json:"signature"`
}
func buildAuth() (*Auth, error) {
// Get the solver private key
solverPk, err := crypto.HexToECDSA("0xmySolverPrivateKey")
if err != nil {
return nil, err
}
solverAccount := crypto.PubkeyToAddress(solverPk.PublicKey)
// Convert current timestamp to bytes
timestamp := time.Now().Unix()
timestampBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timestampBytes, uint64(timestamp))
payload := crypto.Keccak256(
solverAccount.Bytes(),
timestampBytes,
)
// Use the utils package to sign Ethereum (EIP 191) message
signature, err := utils.SignEthereumMessage(payload, solverPk)
if err != nil {
return nil, fmt.Errorf("failed to sign message, %w", err)
}
return &Auth{
Address: solverAccount,
Timestamp: uint64(timestamp),
Signature: signature,
}, nil
}
2. Connect and authenticate to the Operations-Relay
connect.go
package operations_relay
import (
"context"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gorilla/websocket"
)
const (
operationsRelayUrl = "wss://relay-fra.fastlane-labs.xyz/ws/solver"
)
func runOperationsRelay() {
// Set bigger buffer sizes for the websocket connection
dialerOption := rpc.WithWebsocketDialer(websocket.Dialer{
ReadBufferSize: 1024 * 1024,
WriteBufferSize: 1024 * 1024,
})
// Dial the operations relay
operationsRelayClient, err := rpc.DialOptions(context.TODO(), operationsRelayUrl, dialerOption)
if err != nil {
panic(err)
}
// Build the auth
auth, err := buildAuth()
if err != nil {
panic(err)
}
// Send the auth to the operations relay
if err = operationsRelayClient.CallContext(context.TODO(), nil, "solver_identify", auth); err != nil {
panic(err)
}
// No error means we are successfully authenticated
}
3. Full Code
connect.go
package operations_relay
import (
"context"
"encoding/binary"
"fmt"
"time"
"github.com/FastLane-Labs/atlas-sdk-go/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gorilla/websocket"
)
const (
operationsRelayUrl = "wss://relay-fra.fastlane-labs.xyz/ws/solver"
)
type Auth struct {
Address common.Address `json:"address"`
Timestamp uint64 `json:"timestamp"`
Signature hexutil.Bytes `json:"signature"`
}
func runOperationsRelay() {
// Set bigger buffer sizes for the websocket connection
dialerOption := rpc.WithWebsocketDialer(websocket.Dialer{
ReadBufferSize: 1024 * 1024,
WriteBufferSize: 1024 * 1024,
})
// Dial the operations relay
operationsRelayClient, err := rpc.DialOptions(context.TODO(), operationsRelayUrl, dialerOption)
if err != nil {
panic(err)
}
// Build the auth
auth, err := buildAuth()
if err != nil {
panic(err)
}
// Send the auth to the operations relay
if err = operationsRelayClient.CallContext(context.TODO(), nil, "solver_identify", auth); err != nil {
panic(err)
}
// No error means we are successfully authenticated
}
func buildAuth() (*Auth, error) {
// Get the solver private key
solverPk, err := crypto.HexToECDSA("0xmySolverPrivateKey")
if err != nil {
return nil, err
}
solverAccount := crypto.PubkeyToAddress(solverPk.PublicKey)
// Convert current timestamp to bytes
timestamp := time.Now().Unix()
timestampBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timestampBytes, uint64(timestamp))
payload := crypto.Keccak256(
solverAccount.Bytes(),
timestampBytes,
)
// Use the utils package to sign Ethereum (EIP 191) message
signature, err := utils.SignEthereumMessage(payload, solverPk)
if err != nil {
return nil, fmt.Errorf("failed to sign message, %w", err)
}
return &Auth{
Address: solverAccount,
Timestamp: uint64(timestamp),
Signature: signature,
}, nil
}