Skip to content

Commit 57c0110

Browse files
committed
feat(SOLNENG-28): initial commit
0 parents  commit 57c0110

File tree

4 files changed

+239
-0
lines changed

4 files changed

+239
-0
lines changed

README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
2+
# Blockdaemon Chain Watch - streaming on-chain events in real-time
3+
- In this example we'll use the Event Streaming service to monitor any on-chain transfers to the PEPE smart contract
4+
- Requirements:
5+
- Go lang
6+
- Blockdaemon REST API key
7+
- Reverse proxy service
8+
- [HTTPie](https://httpie.io/cli) & [jq](https://jqlang.github.io/jq/download/) CLI tools
9+
- Docs: https://docs.blockdaemon.com/reference/events-introduction
10+
11+
## Step 1. Run webhook receiver (includes a Challenge Response Check)
12+
- the sample webhook server will respond to CRC token checks with the secret "mysecret123"
13+
- all webhook posts will be printed to the console
14+
- the service will listen on port 8082
15+
```shell
16+
go run webhookserver.go
17+
```
18+
19+
## Step 2. Publish the webhook server socket
20+
- [ngrok reverse proxy](https://ngrok.com/docs/getting-started/) is used as an example to publish the server
21+
- register for ngrok account or publish through your own methods
22+
```shell
23+
# set the Webhook Server FQDN
24+
export WEBHOOKSERVERFQDN=XXXX.ngrok-free.app
25+
26+
# start the reverse proxy
27+
ngrok http --domain=$WEBHOOKSERVERFQDN http://localhost:8082
28+
```
29+
30+
## Step 3.
31+
32+
```shell
33+
# set Blockdaemon API key variable
34+
export XAPIKey=XXXXX
35+
36+
# note Event Streaming protocol capabilities
37+
http GET https://svc.blockdaemon.com/streaming/v2/ \
38+
X-Api-Key:$XAPIKey
39+
40+
# create webhook target - set the ngrok domain and note secret matches webhook server
41+
TARGET_ID=$(http POST https://svc.blockdaemon.com/streaming/v2/targets \
42+
X-Api-Key:$XAPIKey \
43+
name='smartcontract webhook target' \
44+
description='Sample Go Webhookserver target' \
45+
max_buffer_count:=100 \
46+
settings:='{"destination": "https://'"$WEBHOOKSERVERFQDN"'", "method": "POST", "secret": "mysecret123"}' \
47+
type='webhook' \
48+
| jq -r .id)
49+
50+
# get the webhook target status - should be "connected"
51+
http GET https://svc.blockdaemon.com/streaming/v2/targets \
52+
X-Api-Key:$XAPIKey
53+
```
54+
## Step 4.
55+
```shell
56+
# create a smart contract address variable key
57+
VARIABLE_ID=$(http POST https://svc.blockdaemon.com/streaming/v2/variables \
58+
X-Api-Key:$XAPIKey \
59+
name='smart contract addresses' \
60+
description='smart contract addresses' \
61+
type='string' \
62+
| jq -r .id)
63+
64+
# set the variable key value to the PEPE token contract address 0x6982508145454Ce325dDbE47a25d4ec3d2311933
65+
http POST https://svc.blockdaemon.com/streaming/v2/variables/$VARIABLE_ID/values \
66+
X-Api-Key:$XAPIKey \
67+
value='0x6982508145454Ce325dDbE47a25d4ec3d2311933'
68+
```
69+
## Step 5.
70+
```shell
71+
# create rule combing the webhook target, the blockchain (Ethereum Mainnet), and the target address
72+
RULE_ID=$(http POST https://svc.blockdaemon.com/streaming/v2/rules \
73+
X-Api-Key:$XAPIKey \
74+
name='Ethereum mainnet rule' \
75+
description='Ethereum mainnet rule' \
76+
network='mainnet' \
77+
protocol='ethereum' \
78+
condition:='[{"variable_type": "address", "variable_id": "'"$VARIABLE_ID"'"}]' \
79+
target="$TARGET_ID" \
80+
condition_type='match_var' \
81+
is_active:=true \
82+
template='ALL_DATA')
83+
84+
# validate the rule
85+
http GET https://svc.blockdaemon.com/streaming/v2/rules \
86+
X-Api-Key:$XAPIKey
87+
88+
```
89+
90+
## Step 6.
91+
Observe the webhook events in the webhookserver output. Example below:
92+
```json
93+
{
94+
"data": {
95+
"accessList": [],
96+
"additionalFields": {
97+
"yParity": "0x0"
98+
},
99+
"blockHash": "0xfc57c0ce8f94d133863f21dda0943b553e792e5fd1b9e687c680aa1b286bfd1f",
100+
"blockNumber": "0x12b845b",
101+
"chainId": "0x1",
102+
"contractAddress": null,
103+
"creates": null,
104+
"cumulativeGasUsed": "0x1bfc64d",
105+
"effectiveGasPrice": "0x2bad348ab",
106+
"from": "0xcAFb5420CE411476ef43CCeEa50e71A95b6Ad5B0",
107+
"gas": "0x163ed",
108+
"gasPrice": "0x2bad348ab",
109+
"gasUsed": "0xd986",
110+
"hash": "0x06424f8c03c746ea1ccf3167d454a4ca54351bb4b179c9b3803455872cc31248",
111+
"input": "0xa9059cbb00000000000000000000000031da1b45d2570b5722618b8b43b0c805114048ee00000000000000000000000000000000000000000031c7ea725f3bca431e6000",
112+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000008000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000010000004000000000000000000000000000000000000000000000000000000000200000000000040000000000000000000000000000000002000000000000000000000000000000002000000000400000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000008000",
113+
"maxFeePerGas": "0x40c933ae2",
114+
"maxPriorityFeePerGas": "0xecd10",
115+
"nonce": "0x3d6",
116+
"publicKey": null,
117+
"r": "0x2b12ee7984f96db6cf16a190e257caa0392fa8961243488ff07caa4931adaffe",
118+
"raw": null,
119+
"root": null,
120+
"s": "0x6ed8f306cdfa24c4aea4f30ec5cfb853dfe90adc6d32db37d60195e36e3b1422",
121+
"status": "0x1",
122+
"timestamp": 1712799383,
123+
"to": "0x6982508145454Ce325dDbE47a25d4ec3d2311933",
124+
"transactionIndex": "0xd0",
125+
"type": "0x2",
126+
"uuid": "8e695b82-7ed3-4ab0-894d-bce7a91077a6",
127+
"v": "0x0",
128+
"value": "0x0"
129+
},
130+
"event_type": "confirmed_tx",
131+
"network": "mainnet",
132+
"protocol": "ethereum",
133+
"rule_id": "34b775e7-4425-4487-9181-0436022588fc",
134+
"target_id": "9c6b6a09-dd23-40b7-bb57-7aa0673f2a20"
135+
}
136+
```
137+
where:
138+
```json
139+
data.input: 0xa9059cbb00000000000000000000000031da1b45d2570b5722618b8b43b0c805114048ee00000000000000000000000000000000000000000031c7ea725f3bca431e6000
140+
```
141+
ABI decodes to:
142+
```json
143+
"definition": "transfer(address,uint256)",
144+
"decodedInputs": [
145+
"0x31DA1B45d2570B5722618B8b43b0C805114048EE",
146+
"60181440870692720000000000"
147+
]
148+
```
149+
meaning `60181440870692720000000000` PEPE was transferred from `0xcAFb5420CE411476ef43CCeEa50e71A95b6Ad5B0` to `0x31DA1B45d2570B5722618B8b43b0C805114048EE`
150+
151+
152+
153+
## Step 7.
154+
```shell
155+
# delete the rule to halt further notifitions
156+
http DELETE https://svc.blockdaemon.com/streaming/v2/rules/$RULE_ID \
157+
X-Api-Key:$XAPIKey
158+
```

go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module eventstreamingdemo
2+
3+
go 1.21.0
4+
5+
require github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
6+
7+
require (
8+
github.com/fatih/color v1.16.0 // indirect
9+
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect
10+
github.com/mattn/go-colorable v0.1.13 // indirect
11+
github.com/mattn/go-isatty v0.0.20 // indirect
12+
golang.org/x/sys v0.14.0 // indirect
13+
)

go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
2+
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
3+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
4+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
5+
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=
6+
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
7+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
8+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
9+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
10+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
11+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
12+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
13+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14+
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
15+
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

webhookserver.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha256"
6+
"encoding/base64"
7+
"encoding/json"
8+
"io"
9+
"log"
10+
11+
"fmt"
12+
"net/http"
13+
14+
"github.com/TylerBrock/colorjson"
15+
)
16+
17+
func main() {
18+
http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
19+
token := req.URL.Query().Get("token")
20+
if token == "" {
21+
body, err := io.ReadAll(req.Body)
22+
if err != nil {
23+
panic(err)
24+
}
25+
var obj map[string]interface{}
26+
err = json.Unmarshal(body, &obj)
27+
28+
if err != nil {
29+
log.Println(err)
30+
return
31+
}
32+
33+
f := colorjson.NewFormatter()
34+
f.Indent = 2
35+
s, _ := f.Marshal(obj)
36+
37+
fmt.Println(string(s))
38+
return
39+
}
40+
41+
key := []byte("mysecret123")
42+
h := hmac.New(sha256.New, key)
43+
h.Write([]byte(token))
44+
45+
hash := h.Sum(nil)
46+
encodedHash := base64.StdEncoding.EncodeToString(hash)
47+
response := "sha256=" + encodedHash
48+
_, _ = rw.Write([]byte(fmt.Sprintf(`{ "response_token": "%s"}`, response)))
49+
})
50+
51+
http.ListenAndServe(":8082", http.DefaultServeMux)
52+
53+
}

0 commit comments

Comments
 (0)