Skip to main content

Event Catcher Usage

warning

Event catcher is still under development, and will got update frequently.

warning

Currently, event catcher is only available as single node. But soon will be available as a cluster of nodes for high availability and scalability.

API

For registering a new contract/event, call this API:

POST https://event-catcher-dev.layerg.xyz/api/v1/contracts
BODY sample:
{
"chainId": 2484,
"contractAddress": "0x176581dbb0a5d0ccbb7395f2aafd63e52e3936d7",
"eventSignature": "TransferSingle(address indexed, address indexed, address indexed, uint256, uint256)",
"eventAbi": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"TransferSingle\",\"type\":\"event\"}]",
"startBlock": 44445660
}

For querying events in the past, call this API:

Required headers:

  • Authorization: Bearer token
GET https://event-catcher-dev.layerg.xyz/api/v1/events?contractAddress={address}&chainId={chainId}&eventSignature={eventName}&txHash={txHash}&take={take}&skip={skip}

Query Parameters:

chainId (required): Chain ID of the event

contractAddress (required): Contract address of the event

eventSignature (optional): Event signature (e.g., Transfer(address,address,uint256))

txHash (optional): Transaction hash of the event

take (optional): Number of events to return (pagination)

skip (optional): Number of events to skip (pagination)

To monitor an event’s status after registration, call this API:

GET https://event-catcher-dev.layerg.xyz/api/v1/status/{chainId}/{contractAddress}/{eventName}

Runtime

As you use LayerG runtime, you can use it with Event Catcher Go SDK to create a real-time stream of events.

Installation

go get github.com/u2u-labs/event-catcher-sdk

View the full source on Github

// 1. Initialize Client
import (
"context"
"time"
"github.com/u2u-labs/event-catcher-sdk/client"
)

gw := client.NewClient(&client.GatewayOpts{})
defer gw.Disconnect()

// 2. Get Available Nodes & Authenticate
// Request available nodes
nodes, err := gw.RequestNodeFromGateway(context.Background(), "2484")
if err != nil {
panic(err)
}

nodeURL := nodes.Nodes[0].Domain
nodeRpcURL := nodes.Nodes[0].DomainRpc
nodeAddress := "0x01857E2BCFcb8B4eF76Df6590F8dCd3bf736C9E9"

// Authenticate
timestamp := time.Now().UTC().Format(time.RFC3339)
signature, err := gw.SignLoginMessage("your_private_key_hex", timestamp)
if err != nil {
panic(err)
}

auth, err := gw.ValidateClient(
context.Background(),
nodeAddress,
signature,
timestamp,
)
if err != nil || !auth.Success {
panic(err)
}

authToken := auth.ConnectionToken

// 3. Register Contract for Monitoring
contractID, err := gw.RegisterNodeMonitorContract(
context.Background(),
nodeURL,
client.RegisterContract{
ChainId: 2484,
ContractAddress: "0x8B0b7E0c9C5a6B48F5bA0352713B85c2C4973B78",
EventSignature: "Transfer(address indexed from, address indexed to, uint256 value)",
EventAbi: "[{\\\"anonymous\\\":false,\\\"inputs\\\":[{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"operator\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"from\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"to\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":false,\\\"internalType\\\":\\\"uint256\\\",\\\"name\\\":\\\"id\\\",\\\"type\\\":\\\"uint256\\\"},{\\\"indexed\\\":false,\\\"internalType\\\":\\\"uint256\\\",\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"uint256\\\"}],\\\"name\\\":\\\"TransferSingle\\\",\\\"type\\\":\\\"event\\\"}]",
StartBlock: 50035775,
},
)
if err != nil {
panic(err)
}

// 4. Subscribe to Real-time Events

filter := &node.StreamEventsRequest{
ChainId: 2484,
ContractAddress: "0x8B0b7E0c9C5a6B48F5bA0352713B85c2C4973B78",
EventSignature: "Transfer",
}

// Create a handler function to handle incoming events
handler := func(event *client.Event) {
log.Printf("New event - Block: %d, TxHash: %s, Data: %s",
event.BlockNumber, event.TxHash, event.Data)
}

subscription, err := gw.SubscribeEvent(
context.Background(),
nil, // TLS config (nil for no TLS)
nodeRpcURL,
authToken,
filter,
handler,
)
if err != nil {
panic(err)
}

log.Printf("Subscribed to events: %s", subscription.ID)

// 5. Stop Stream
response, err := gw.StopStream(subscription.ID)
if err != nil {
log.Printf("Error stopping stream: %v", err)
} else {
log.Printf("Stream stopped successfully: %v", response)
}
Full Example
package main

import (
"context"
"log"
"time"

"github.com/u2u-labs/event-catcher-sdk/client"
"github.com/u2u-labs/event-catcher-sdk/proto/node"
)

func main() {
gw := client.NewClient(&client.GatewayOpts{})
defer gw.Disconnect()

// Get nodes and authenticate
nodes, _ := gw.RequestNodeFromGateway(context.Background(), "2484")
nodeURL := nodes.Nodes[0].Domain
nodeRpcURL := nodes.Nodes[0].DomainRpc
nodeAddress := "0x01857E2BCFcb8B4eF76Df6590F8dCd3bf736C9E9"

timestamp := time.Now().UTC().Format(time.RFC3339)
signature, _ := client.SignLoginMessage("your_private_key_here", timestamp)
auth, _ := client.ValidateClient(context.Background(), nodeAddress, signature, timestamp)

// Register contract
contractID, _ := gw.RegisterNodeMonitorContract(
context.Background(),
nodeURL,
client.RegisterContract{
ChainId: 2484,
ContractAddress: "0x8B0b7E0c9C5a6B48F5bA0352713B85c2C4973B78",
EventSignature: "Transfer(address indexed from, address indexed to, uint256 value)",
EventAbi: "[{\\\"anonymous\\\":false,\\\"inputs\\\":[{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"operator\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"from\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":true,\\\"internalType\\\":\\\"address\\\",\\\"name\\\":\\\"to\\\",\\\"type\\\":\\\"address\\\"},{\\\"indexed\\\":false,\\\"internalType\\\":\\\"uint256\\\",\\\"name\\\":\\\"id\\\",\\\"type\\\":\\\"uint256\\\"},{\\\"indexed\\\":false,\\\"internalType\\\":\\\"uint256\\\",\\\"name\\\":\\\"value\\\",\\\"type\\\":\\\"uint256\\\"}],\\\"name\\\":\\\"TransferSingle\\\",\\\"type\\\":\\\"event\\\"}]",
StartBlock: 50035775,
},
)
log.Printf("Contract registered: %s", contractID)

// Subscribe to events
filter := &node.StreamEventsRequest{
ChainId: 2484,
ContractAddress: "0x8B0b7E0c9C5a6B48F5bA0352713B85c2C4973B78",
EventSignature: "Transfer",
}

subscription, _ := gw.SubscribeEvent(
context.Background(),
nil,
nodeRpcURL,
auth.ConnectionToken,
filter,
func(event *client.Event) {
log.Printf("Event: Block %d, Tx %s", event.BlockNumber, event.TxHash)
},
)

log.Printf("Subscribed to events: %s", subscription.ID)

// Stop after 60 seconds
go func() {
time.Sleep(60 * time.Second)
client.StopStream(subscription.ID)
}()

select {}
}

Other SDK methods:

Get current event's status after registration with SDK:

client := newTestClient()
nodeUrl := "https://event-catcher-dev.layerg.xyz"
rs, err := client.GetEventCurrentSyncStatus(context.Background(), nodeUrl, SyncStatusParams{
ChainId: 2484,
ContractAddress: "0x8B0b7E0c9C5a6B48F5bA0352713B85c2C4973B78",
EventName: "NodeAdded(address indexed node)",
})