Skip to content

Commit f3ff02c

Browse files
Add NEAR support for substreams init
- Create near-hello-world package with conversation handler - Add NEAR-specific templates for substreams.yaml, protobuf, and Rust code - Register NEAR hello world in server handler - Use substreams-near v0.10 dependency - Support NEAR block processing with transactions and receipts extraction
1 parent 2f86802 commit f3ff02c

File tree

10 files changed

+298
-0
lines changed

10 files changed

+298
-0
lines changed

near-hello-world/convo.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package nearhelloworld
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"regexp"
7+
8+
registry "github.com/pinax-network/graph-networks-libs/packages/golang/lib"
9+
networks "github.com/streamingfast/firehose-networks"
10+
codegen "github.com/streamingfast/substreams-codegen"
11+
"github.com/streamingfast/substreams-codegen/base"
12+
"github.com/streamingfast/substreams-codegen/loop"
13+
)
14+
15+
type Convo struct {
16+
*codegen.Conversation[*Project]
17+
}
18+
19+
func New() codegen.Converser {
20+
return &Convo{&codegen.Conversation[*Project]{
21+
State: &Project{
22+
ConversationState: base.ConversationState{
23+
ChainName: "near",
24+
},
25+
},
26+
}}
27+
}
28+
29+
func init() {
30+
codegen.RegisterConversation(
31+
"near-hello-world",
32+
"Example Substreams that reads NEAR blocks and extract data from transactions and receipts.",
33+
"Use this example as a starting point to create your own custom Substreams, which indexes the data you need from the NEAR blockchain.",
34+
New,
35+
60,
36+
"NEAR",
37+
)
38+
}
39+
40+
func (c *Convo) NextStep() loop.Cmd {
41+
p := c.State
42+
if p.Name == "" {
43+
return cmd(codegen.AskProjectName{})
44+
}
45+
46+
return cmd(codegen.RunGenerate{})
47+
}
48+
49+
func (c *Convo) Update(msg loop.Msg) loop.Cmd {
50+
switch msg := msg.(type) {
51+
case codegen.MsgStart:
52+
c.SetClientVersion(msg.Version)
53+
var msgCmd loop.Cmd
54+
if msg.Hydrate != nil {
55+
if err := json.Unmarshal([]byte(msg.Hydrate.SavedState), &c.State); err != nil {
56+
return loop.Quit(fmt.Errorf(`something went wrong, here's an error message to share with our devs (%s); we've notified them already`, err))
57+
}
58+
59+
msgCmd = c.Msg().Message("Ok, I reloaded your state.").Cmd()
60+
} else {
61+
msgCmd = c.Msg().Message("Ok, let's start a new package.").Cmd()
62+
}
63+
return loop.Seq(msgCmd, c.NextStep())
64+
65+
case codegen.InputSubstreamsConsumptionChoice:
66+
return c.HandleSubstreamsConsumptionChoice(msg.Value)
67+
68+
case codegen.InputSourceDownloaded:
69+
return c.HandleDownloaded(msg.Value)
70+
71+
case codegen.AskProjectName:
72+
return c.CmdAskProjectName()
73+
74+
case codegen.InputProjectName:
75+
c.State.Name = msg.Value
76+
return c.NextStep()
77+
78+
case codegen.RunGenerate:
79+
return c.CmdGenerate(c.State.Generate)
80+
81+
case codegen.ReturnGenerate:
82+
return c.CmdDownloadFiles(msg)
83+
}
84+
85+
return loop.Quit(fmt.Errorf("invalid loop message: %T", msg))
86+
}
87+
88+
var cmd = codegen.Cmd
89+
90+
var nearNetworkRegexp = regexp.MustCompile(`^near`)
91+
92+
func nearNetworks() []*registry.Network {
93+
return networks.GetSubstreamsRegistry().Search(nearNetworkRegexp)
94+
}
95+

near-hello-world/state.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package nearhelloworld
2+
3+
import (
4+
"embed"
5+
6+
codegen "github.com/streamingfast/substreams-codegen"
7+
"github.com/streamingfast/substreams-codegen/base"
8+
)
9+
10+
//go:embed templates/*
11+
var templatesFS embed.FS
12+
13+
type Project struct {
14+
base.ConversationState
15+
}
16+
17+
func (p *Project) Generate() codegen.ReturnGenerate {
18+
return codegen.GenerateTemplateTree(p, templatesFS, map[string]string{
19+
"proto/mydata.proto.gotmpl": "proto/mydata.proto",
20+
"src/pb/mod.rs.gotmpl": "src/pb/mod.rs",
21+
"src/lib.rs.gotmpl": "src/lib.rs",
22+
"Cargo.toml.gotmpl": "Cargo.toml",
23+
".gitignore.gotmpl": ".gitignore",
24+
"substreams.yaml.gotmpl": "substreams.yaml",
25+
"README.md.gotmpl": "README.md",
26+
"common-templates/buf.gen.yaml": "buf.gen.yaml",
27+
})
28+
}
29+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock
4+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "{{ .Name }}"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
crate-type = ["cdylib"]
8+
9+
[dependencies]
10+
substreams = "0.5"
11+
substreams-near = "0.10"
12+
prost = "0.11"
13+
prost-types = "0.11"
14+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# {{ .Name }}
2+
3+
This Substreams extracts data from NEAR blockchain transactions and receipts.
4+
5+
## Quick Start
6+
7+
```bash
8+
# Build the substreams
9+
substreams build
10+
11+
# Run the substreams
12+
substreams run substreams.yaml map_my_data -e near.streamingfast.io:443
13+
```
14+
15+
## Generated Files
16+
17+
- `substreams.yaml`: Substreams manifest
18+
- `proto/mydata.proto`: Protocol buffer definitions
19+
- `src/lib.rs`: Main Rust source code
20+
- `Cargo.toml`: Rust dependencies
21+
22+
## Customization
23+
24+
Modify the `map_my_data` function in `src/lib.rs` to extract the specific data you need from NEAR blocks.
25+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
syntax = "proto3";
2+
3+
package mydata.v1;
4+
5+
import "google/protobuf/any.proto";
6+
import "google/protobuf/timestamp.proto";
7+
import "sf/near/type/v1/type.proto";
8+
9+
message MyData {
10+
uint64 block_number = 1;
11+
google.protobuf.Timestamp timestamp = 2;
12+
repeated NearTransaction transactions = 3;
13+
}
14+
15+
message NearTransaction {
16+
string signer_id = 1;
17+
string receiver_id = 2;
18+
repeated NearAction actions = 3;
19+
string transaction_hash = 4;
20+
}
21+
22+
message NearAction {
23+
string action_type = 1;
24+
google.protobuf.Any action_data = 2;
25+
}
26+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
mod pb;
2+
3+
use crate::pb::{
4+
mydata::v1::{MyData, NearTransaction, NearAction},
5+
sf::near::r#type::v1::Block,
6+
};
7+
use pb::mydata::v1 as mydata;
8+
use prost::Message;
9+
use prost_types::Timestamp;
10+
use substreams::Hex;
11+
12+
#[substreams::handlers::map]
13+
fn map_my_data(block: Block) -> Result<mydata::MyData, substreams::errors::Error> {
14+
let header = block.header.as_ref().unwrap();
15+
16+
let mut transactions: Vec<NearTransaction> = Vec::new();
17+
18+
// Process transactions from all shards
19+
for shard in block.shards {
20+
if let Some(chunk) = shard.chunk {
21+
for tx_with_outcome in chunk.transactions {
22+
let tx = tx_with_outcome.transaction.unwrap();
23+
24+
let mut actions: Vec<NearAction> = Vec::new();
25+
for action in tx.actions {
26+
// Convert action to our custom format
27+
// Note: This is a simplified example - you would need to handle
28+
// different action types (CreateAccount, DeployContract, FunctionCall, etc.)
29+
let action_data = NearAction {
30+
action_type: "Unknown".to_string(), // You would determine the actual action type
31+
action_data: None, // You would serialize the actual action data
32+
};
33+
actions.push(action_data);
34+
}
35+
36+
let near_tx = NearTransaction {
37+
signer_id: tx.signer_id,
38+
receiver_id: tx.receiver_id,
39+
actions,
40+
transaction_hash: Hex::encode(&tx.hash.unwrap().bytes),
41+
};
42+
43+
transactions.push(near_tx);
44+
}
45+
}
46+
}
47+
48+
Ok(MyData {
49+
block_number: header.height,
50+
timestamp: Some(Timestamp {
51+
seconds: (header.timestamp / 1_000_000_000) as i64,
52+
nanos: (header.timestamp % 1_000_000_000) as i32,
53+
}),
54+
transactions,
55+
})
56+
}
57+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @generated
2+
pub mod mydata {
3+
pub mod v1 {
4+
include!(concat!(env!("OUT_DIR"), "/mydata.v1.rs"));
5+
}
6+
}
7+
8+
pub mod sf {
9+
pub mod near {
10+
pub mod r#type {
11+
pub mod v1 {
12+
include!(concat!(env!("OUT_DIR"), "/sf.near.type.v1.rs"));
13+
}
14+
}
15+
}
16+
}
17+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
specVersion: v0.1.0
2+
package:
3+
name: my_project
4+
version: v0.1.0
5+
6+
protobuf:
7+
files:
8+
- mydata.proto
9+
importPaths:
10+
- ./proto
11+
excludePaths:
12+
- sf/substreams
13+
- google
14+
15+
binaries:
16+
default:
17+
type: wasm/rust-v1
18+
file: ./target/wasm32-unknown-unknown/release/substreams.wasm
19+
20+
modules:
21+
- name: map_my_data
22+
kind: map
23+
initialBlock: 9820210
24+
inputs:
25+
- source: sf.near.type.v1.Block
26+
output:
27+
type: proto:mydata.v1.MyData
28+
29+
network: {{ .ChainName }}
30+

server/handler_convo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
_ "github.com/streamingfast/substreams-codegen/injective-hello-world"
2929
_ "github.com/streamingfast/substreams-codegen/mantra-events"
3030
_ "github.com/streamingfast/substreams-codegen/mantra-hello-world"
31+
_ "github.com/streamingfast/substreams-codegen/near-hello-world"
3132
_ "github.com/streamingfast/substreams-codegen/sol-anchor"
3233
_ "github.com/streamingfast/substreams-codegen/sol-hello-world"
3334
_ "github.com/streamingfast/substreams-codegen/sol-transactions"

0 commit comments

Comments
 (0)