Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions veascan-subgraph-inbox/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "@kleros/veascan-subgraph-inbox",
"version": "0.2.1",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
Expand All @@ -9,6 +10,7 @@
"yarn": "4.6.0"
},
"scripts": {
"update:arbitrum-sepolia": "./scripts/update.sh arbitrumSepolia arbitrum-sepolia",
"codegen": "graph codegen",
"build": "graph build",
"deploy": "graph deploy --studio veascan-inbox-arb-sep-devnet",
Expand Down
8 changes: 8 additions & 0 deletions veascan-subgraph-inbox/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ type _Schema_
include: [{ entity: "Snapshot", fields: [{ name: "epochString" }, { name: "stateRootString" }] }]
)

type Inbox @entity(immutable: true) {
id: Bytes! # address
messages: [Snapshot!]! @derivedFrom(field: "inbox")
}

type Message @entity {
id: ID!
txHash: Bytes!
Expand All @@ -16,8 +21,10 @@ type Message @entity {
data: Bytes!
}


type Snapshot @entity {
id: ID!
inbox: Inbox! # address
epoch: BigInt
epochString: String
caller: Bytes
Expand All @@ -43,6 +50,7 @@ type Fallback @entity {

type Ref @entity {
id: ID!
inbox: Inbox! # address
currentSnapshotIndex: BigInt!
nextMessageIndex: BigInt!
}
11 changes: 5 additions & 6 deletions veascan-subgraph-inbox/scripts/update.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,28 @@

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

function update() #file #dataSourceIndex #graphNetwork
function update() # file # dataSourceIndex # graphNetwork
{
local f="$1"
local dataSourceIndex="$2"

graphNetwork=$3 yq -i ".dataSources[$dataSourceIndex].network=env(graphNetwork)" "$SCRIPT_DIR"/../subgraph.yaml
graphNetwork=$3 yq -i ".dataSources[$dataSourceIndex].network=env(graphNetwork)" "$SCRIPT_DIR"/../subgraph.yaml

address=$(cat "$f" | jq '.address')
yq -i ".dataSources[$dataSourceIndex].source.address=$address" "$SCRIPT_DIR"/../subgraph.yaml
yq -i ".dataSources[$dataSourceIndex].source.address=$address" "$SCRIPT_DIR"/../subgraph.yaml

blockNumber="$(cat "$f" | jq '.receipt.blockNumber')"
yq -i ".dataSources[$dataSourceIndex].source.startBlock=$blockNumber" "$SCRIPT_DIR"/../subgraph.yaml
yq -i ".dataSources[$dataSourceIndex].source.startBlock=$blockNumber" "$SCRIPT_DIR"/../subgraph.yaml
}

hardhatNetwork=${1:-arbitrumSepolia}
graphNetwork=${2:-arbitrum\-sepolia}
i=0

# backup
cp "$SCRIPT_DIR"/../subgraph.yaml "$SCRIPT_DIR"/../subgraph.yaml.bak.$(date +%s)

for contract in $(yq .dataSources[].name "$SCRIPT_DIR"/../subgraph.yaml)
do
update "$SCRIPT_DIR/../../contracts/deployments/$hardhatNetwork/$contract.json" $i $graphNetwork
(( ++i ))
done
done
197 changes: 158 additions & 39 deletions veascan-subgraph-inbox/src/VeaInbox.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { BigInt, ByteArray, Bytes } from "@graphprotocol/graph-ts";
import { Snapshot, Message, Ref, Fallback } from "../generated/schema";
import {
Address,
BigInt,
ByteArray,
Bytes,
log,
} from "@graphprotocol/graph-ts";
import { Snapshot, Message, Ref, Fallback, Inbox } from "../generated/schema";
import {
MessageSent,
SnapshotSaved,
SnapshotSent,
VeaInbox,
} from "../generated/VeaInbox/VeaInbox";
VeaInboxArbToEthDevnet,
} from "../generated/VeaInboxArbToEthDevnet/VeaInboxArbToEthDevnet";

export function handleMessageSent(event: MessageSent): void {
const snapshot = getCurrentSnapshot();
let inbox = Inbox.load(event.address);
if (!inbox) {
inbox = new Inbox(event.address);
inbox.save();
}
const snapshot = getCurrentSnapshot(event.address);
snapshot.numberMessages = snapshot.numberMessages.plus(BigInt.fromI32(1));
snapshot.save();

const messageIndex = useNextMessageIndex();
const message = new Message(messageIndex.toString());
const messageIndex = useNextMessageIndex(event.address);
const messageId = event.address.toHexString() + "-" + messageIndex.toString();
const message = new Message(messageId);
message.snapshot = snapshot.id;
message.txHash = event.transaction.hash;
message.timestamp = event.block.timestamp;
Expand All @@ -34,27 +46,65 @@ export function handleMessageSent(event: MessageSent): void {
message.save();
}

function getCurrentSnapshot(): Snapshot {
let ref = Ref.load("0");
function getCurrentSnapshot(inboxAddress: Address): Snapshot {
let id = inboxAddress.toHexString();
let ref = Ref.load(id);
if (!ref) {
ref = new Ref("0");
ref = new Ref(id);
ref.inbox = inboxAddress;
ref.currentSnapshotIndex = BigInt.fromI32(0);
ref.nextMessageIndex = BigInt.fromI32(0);
ref.save();
const snapshot = new Snapshot("0");

// Use a composite ID for the initial snapshot.
const snapshotId = inboxAddress.toHexString() + "-0";
const snapshot = new Snapshot(snapshotId);
snapshot.inbox = inboxAddress;
snapshot.numberMessages = BigInt.fromI32(0);
snapshot.taken = false;
snapshot.resolving = false;
snapshot.epoch = BigInt.fromI32(0);
snapshot.epochString = "0";
snapshot.stateRoot = Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
snapshot.stateRootString =
"0x0000000000000000000000000000000000000000000000000000000000000000";
snapshot.timestamp = BigInt.fromI32(0);
snapshot.save();
return snapshot;
}
return Snapshot.load(ref.currentSnapshotIndex.toString())!;
let snapshot = Snapshot.load(
inboxAddress.toHexString() + "-" + ref.currentSnapshotIndex.toString()
);
if (!snapshot) {
// If it doesn't exist, creating one with default values.
snapshot = new Snapshot(
inboxAddress.toHexString() + "-" + ref.currentSnapshotIndex.toString()
);
snapshot.inbox = inboxAddress;
snapshot.numberMessages = BigInt.fromI32(0);
snapshot.taken = false;
snapshot.resolving = false;
snapshot.epoch = BigInt.fromI32(0);
snapshot.epochString = "0";
snapshot.stateRoot = Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
snapshot.stateRootString =
"0x0000000000000000000000000000000000000000000000000000000000000000";
snapshot.timestamp = BigInt.fromI32(0);
snapshot.save();
}
return snapshot;
}

function useNextMessageIndex(): BigInt {
let ref = Ref.load("0");
function useNextMessageIndex(inboxAddress: Address): BigInt {
let id = inboxAddress.toHexString();
let ref = Ref.load(id);
if (!ref) {
ref = new Ref("0");
ref = new Ref(id);
ref.inbox = inboxAddress;
ref.currentSnapshotIndex = BigInt.fromI32(0);
ref.nextMessageIndex = BigInt.fromI32(1);
ref.save();
Expand All @@ -67,13 +117,16 @@ function useNextMessageIndex(): BigInt {
}

export function handleSnapshotSaved(event: SnapshotSaved): void {
const inbox = VeaInbox.bind(event.address);
// Get the epochPeriod from the public variable of the deployed contract
const epochPeriod = inbox.epochPeriod();
let inbox = Inbox.load(event.address);
if (!inbox) {
inbox = new Inbox(event.address);
inbox.save();
}
const contract = VeaInboxArbToEthDevnet.bind(event.address);
const epochPeriod = contract.epochPeriod();
const epoch = event.block.timestamp.div(epochPeriod);
// Get stateRoot from contract
const stateRoot = inbox.snapshots(epoch);
const currentSnapshot = getCurrentSnapshot();
const stateRoot = contract.snapshots(epoch);
const currentSnapshot = getCurrentSnapshot(event.address);
currentSnapshot.taken = true;
currentSnapshot.caller = event.transaction.from;
currentSnapshot.stateRoot = stateRoot;
Expand All @@ -84,11 +137,15 @@ export function handleSnapshotSaved(event: SnapshotSaved): void {
currentSnapshot.epochString = epoch.toString();
currentSnapshot.save();

// Create a new snapshot entity to be the current snapshot.
const ref = Ref.load("0")!;
const newSnapshot = new Snapshot(
ref.currentSnapshotIndex.plus(BigInt.fromI32(1)).toString()
);
// Creating a new snapshot entity to be the current snapshot.
const refId = event.address.toHexString();
const ref = Ref.load(refId)!;
const snapshotId =
ref.inbox.toHexString() +
"-" +
ref.currentSnapshotIndex.plus(BigInt.fromI32(1)).toString();
const newSnapshot = new Snapshot(snapshotId);
newSnapshot.inbox = event.address;
newSnapshot.numberMessages = BigInt.fromI32(0);
newSnapshot.taken = false;
newSnapshot.resolving = false;
Expand All @@ -101,27 +158,89 @@ export function handleSnapshotSaved(event: SnapshotSaved): void {

export function handleSnapshotSent(event: SnapshotSent): void {
const epochSent = event.params._epochSent;
const fallback = new Fallback(
epochSent.plus(event.block.timestamp).toString()
);
let snapshot: Snapshot | null;
const ref = Ref.load("0")!;

// Create a unique fallback id based on epochSent and block.timestamp.
const fallbackId = epochSent.plus(event.block.timestamp).toString();
const fallback = new Fallback(fallbackId);

// Load or initialize Ref.
let id = event.address.toHexString(); // Unique ID per inbox contract
let ref = Ref.load(id);
if (!ref) {
ref = new Ref(id);
ref.inbox = event.address;
ref.currentSnapshotIndex = BigInt.fromI32(0);
ref.nextMessageIndex = BigInt.fromI32(0);
ref.save();
}

fallback.timestamp = event.block.timestamp;
fallback.txHash = event.transaction.hash;
fallback.executor = event.transaction.from;
fallback.ticketId = event.params._ticketId;

let snapshotFound = false;
let snapshot: Snapshot | null = null;

// Iterate from the current snapshot index downward to search for a snapshot with a matching epoch.
for (let i = ref.currentSnapshotIndex.toI32(); i >= 0; i--) {
const snapshotId = BigInt.fromI32(i).toString();
const snapshotId =
event.address.toHexString() + "-" + BigInt.fromI32(i).toString();
snapshot = Snapshot.load(snapshotId);

if (snapshot && snapshot.epoch === epochSent) {
// Snapshot found, update resolving field and save
snapshot.resolving = true;
snapshot.save();
fallback.snapshot = snapshotId;
break;
if (snapshot && snapshot.epoch) {
if (BigInt.compare(snapshot.epoch as BigInt, epochSent) == 0) {
// Matching snapshot found: update and mark as resolving.
snapshot.resolving = true;
snapshot.save();
fallback.snapshot = snapshotId;
snapshotFound = true;
break;
}
}
}

// If no snapshot was found, update the current snapshot and create a new one.
if (!snapshotFound) {
const inbox = VeaInboxArbToEthDevnet.bind(event.address);

let currentSnapshot = getCurrentSnapshot(event.address);
currentSnapshot.taken = false;
currentSnapshot.resolving = true;
currentSnapshot.timestamp = epochSent.times(inbox.epochPeriod());
currentSnapshot.stateRoot = Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
currentSnapshot.stateRootString =
"0x0000000000000000000000000000000000000000000000000000000000000000";
currentSnapshot.epoch = epochSent;
currentSnapshot.epochString = epochSent.toString();
currentSnapshot.save();

fallback.snapshot = currentSnapshot.id;

// Create a new snapshot with an incremented index.
const newIndex = ref.currentSnapshotIndex.plus(BigInt.fromI32(1));
const newSnapshotId =
event.address.toHexString() + "-" + newIndex.toString();
const newSnapshot = new Snapshot(newSnapshotId);
newSnapshot.inbox = event.address;
newSnapshot.numberMessages = BigInt.fromI32(0);
newSnapshot.taken = false;
newSnapshot.resolving = false;
// Initialize new snapshot fields with defaults.
newSnapshot.epoch = BigInt.fromI32(0);
newSnapshot.epochString = "0";
newSnapshot.stateRoot = Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
newSnapshot.stateRootString =
"0x0000000000000000000000000000000000000000000000000000000000000000";
newSnapshot.timestamp = BigInt.fromI32(0);
newSnapshot.save();

// Update Ref with the new snapshot index.
ref.currentSnapshotIndex = ref.currentSnapshotIndex.plus(BigInt.fromI32(1));
ref.save();
}
fallback.save();
}
38 changes: 33 additions & 5 deletions veascan-subgraph-inbox/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ features:
- fullTextSearch
dataSources:
- kind: ethereum
name: VeaInbox
name: VeaInboxArbToEthTestnet
network: arbitrum-sepolia
source:
address: "0x0B5851fE2a931F619F73E739E5435C43976f1D68"
abi: VeaInbox
startBlock: 69673433
address: "0xE12daFE59Bc3A996362d54b37DFd2BA9279cAd06"
abi: VeaInboxArbToEthTestnet
startBlock: 77452741
mapping:
kind: ethereum/events
apiVersion: 0.0.7
Expand All @@ -20,7 +20,35 @@ dataSources:
- Message
- Refs
abis:
- name: VeaInbox
- name: VeaInboxArbToEthTestnet
file: ../contracts/deployments/arbitrumSepolia/VeaInboxArbToEthTestnet.json
- name: VeaInboxArbToEthDevnet
file: ../contracts/deployments/arbitrumSepolia/VeaInboxArbToEthDevnet.json
eventHandlers:
- event: MessageSent(bytes)
handler: handleMessageSent
- event: SnapshotSaved(bytes32,uint256,uint64)
handler: handleSnapshotSaved
- event: SnapshotSent(indexed uint256,bytes32)
handler: handleSnapshotSent
file: ./src/VeaInbox.ts
- kind: ethereum
name: VeaInboxArbToEthDevnet
network: arbitrum-sepolia
source:
address: "0xF6C5640de593fEf76129F1F1A863F7ddc65776C9"
abi: VeaInboxArbToEthDevnet
startBlock: 129093493
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- Snapshot
- Message
- Refs
abis:
- name: VeaInboxArbToEthDevnet
file: ../contracts/deployments/arbitrumSepolia/VeaInboxArbToEthDevnet.json
eventHandlers:
- event: MessageSent(bytes)
Expand Down
Loading
Loading