Event Catcher Usage
Event catcher is still under development, and will got update frequently.
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)",
})