Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added DataEdge contract with OracleConfiguration #2

Merged
merged 4 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
16 changes: 16 additions & 0 deletions abis/SAODataEdge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "Log",
"type": "event"
},
{ "stateMutability": "payable", "type": "fallback" }
]
4 changes: 4 additions & 0 deletions networks.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"SubgraphAvailabilityManager": {
"address": "0x9bDC3264596850E7F5d141A8D898dFA7001355CC",
"startBlock": 22552633
},
"SAODataEdge": {
"address": "0xB61AF143c79Cbdd68f179B657AaC86665CC2B469",
"startBlock": 25463619
}
}
}
61 changes: 26 additions & 35 deletions schema.graphql
Original file line number Diff line number Diff line change
@@ -1,45 +1,36 @@
type NewOwnership @entity(immutable: true) {
id: Bytes!
from: Bytes! # address
to: Bytes! # address
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
type GlobalState @entity {
id: ID!
oracles: [Oracle!]! @derivedFrom(field: "state")
}

type NewPendingOwnership @entity(immutable: true) {
id: Bytes!
from: Bytes! # address
to: Bytes! # address
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type OracleSet @entity(immutable: true) {
id: Bytes!
index: BigInt! # uint256
oracle: Bytes! # address
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
type Oracle @entity {
id: String! # oracle_index
Maikol marked this conversation as resolved.
Show resolved Hide resolved
state: GlobalState!
address: Bytes!
latestConfig: OracleConfiguration!
configurations: [OracleConfiguration!]! @derivedFrom(field: "oracle")
votes: [OracleVote!]! @derivedFrom(field: "oracle")
}

type OracleVote @entity(immutable: true) {
id: Bytes!
id: ID!
oracle: Oracle!
subgraphDeploymentID: Bytes! # bytes32
deny: Boolean! # bool
oracleIndex: BigInt! # uint256
timestamp: BigInt! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type VoteTimeLimitSet @entity(immutable: true) {
id: Bytes!
voteTimeLimit: BigInt! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
type OracleConfiguration @entity {
id: ID!
oracle: Oracle!
commitHash: String!
ipfsConcurrency: String!
ipfsTimeout: String!
minSignal: String!
period: String!
gracePeriod: String!
supportedDataSourceKinds: String!
subgraph: String!
subgraphAvailabilityManagerContract: String!
oracleIndex: String!
}
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export let ORACLE_CONFIGURATION_ABI =
"(string,(string,string,string,string,string,string,string,string,string))"
export let TUPLE_OFFSET_HEX = "0x0000000000000000000000000000000000000000000000000000000000000020"
28 changes: 28 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { log, BigInt, Bytes } from "@graphprotocol/graph-ts";
import { StoreCache } from "./store-cache";
import { TUPLE_OFFSET_HEX } from './constants';

export function parseCalldata(calldata: Bytes): Bytes {
// Remove function signature
let dataWithoutSignature = calldata.toHexString().slice(10);
let result = TUPLE_OFFSET_HEX + dataWithoutSignature;
Maikol marked this conversation as resolved.
Show resolved Hide resolved
return Bytes.fromHexString(result);
}

export function isSubmitterAllowed(
cache: StoreCache,
oracleIndex: String,
submitter: String,
): boolean {
let oracle = cache.getOracle(oracleIndex);
return oracle.address.toHexString() == submitter;
}


export function getOracleVoteId(
subgraphDeploymentID: String,
oracleIndex: String,
timestamp: String
): string {
return [subgraphDeploymentID, oracleIndex, timestamp].join("-") as string;
}
47 changes: 47 additions & 0 deletions src/sao-data-edge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { log, ethereum, Bytes } from "@graphprotocol/graph-ts";
import { Log as LogEvent } from "../generated/SAODataEdge/SAODataEdge"
import { parseCalldata, isSubmitterAllowed } from "./helpers"
import { StoreCache } from "./store-cache";
import { ORACLE_CONFIGURATION_ABI } from "./constants";

export function handleLog(event: LogEvent): void {
let submitter = event.transaction.from.toHexString();
event.logIndex.toI32();
Maikol marked this conversation as resolved.
Show resolved Hide resolved
processPayload(submitter, event.params.data, event.transaction.hash.toHexString());
}

export function processPayload(submitter: string, payload: Bytes, txHash: string): void {
log.warning("Processing payload. Submitter: {}", [submitter]);

let cache = new StoreCache();

let parsedCalldata = parseCalldata(payload);
let decoded = ethereum.decode(ORACLE_CONFIGURATION_ABI, parsedCalldata)!.toTuple();
let decodedConfig = decoded[1].toTuple();
let decodedOracleIndex = decodedConfig[8].toString();

if (!isSubmitterAllowed(cache, decodedOracleIndex, submitter)) {
log.error("Submitter not allowed: {}", [submitter]);
return;
}

log.info("Submitter allowed", []);

let oracle = cache.getOracle(decodedOracleIndex);
let config = cache.getOracleConfiguration(txHash);
config.oracle = oracle.id;
config.commitHash = decoded[0].toString();
config.ipfsConcurrency = decodedConfig[0].toString();
config.ipfsTimeout = decodedConfig[1].toString();
config.minSignal = decodedConfig[2].toString();
config.period = decodedConfig[3].toString();
config.gracePeriod = decodedConfig[4].toString();
config.supportedDataSourceKinds = decodedConfig[5].toString();
config.subgraph = decodedConfig[6].toString();
config.subgraphAvailabilityManagerContract = decodedConfig[7].toString();
config.oracleIndex = decodedOracleIndex;

oracle.latestConfig = config.id;

cache.commitChanges();
}
86 changes: 86 additions & 0 deletions src/store-cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { GlobalState, Oracle, OracleConfiguration, OracleVote } from "../generated/schema"
import { log, Bytes } from "@graphprotocol/graph-ts"

export class SafeMap<K, V> extends Map<K, V> {
safeGet(id: K): V | null {
return this.has(id) ? this.get(id) : null;
}
}

export class StoreCache {
state: GlobalState;
oracles: SafeMap<String, Oracle>;
oraclesConfigs: SafeMap<String, OracleConfiguration>;
oracleVotes: SafeMap<String, OracleVote>;

constructor() {
let state = GlobalState.load("0");
if (state == null) {
state = new GlobalState("0");
state.save();
}

this.state = state;
this.oracles = new SafeMap<String, Oracle>();
this.oraclesConfigs = new SafeMap<String, OracleConfiguration>();
this.oracleVotes = new SafeMap<String, OracleVote>();
}

getGlobalState(): GlobalState {
return this.state;
}

getOracle(id: String): Oracle {
if (this.oracles.safeGet(id) == null) {
let oracle = Oracle.load(id);
if (oracle == null) {
oracle = new Oracle(id);
oracle.state = this.state.id;
oracle.address = Bytes.empty();
}
this.oracles.set(id, oracle);
}
return this.oracles.safeGet(id)!;
}

getOracleConfiguration(id: String): OracleConfiguration {
if (this.oraclesConfigs.safeGet(id) == null) {
let config = OracleConfiguration.load(id);
if (config == null) {
config = new OracleConfiguration(id);
}
this.oraclesConfigs.set(id, config);
}
return this.oraclesConfigs.safeGet(id)!;
}

getOracleVote(id: String): OracleVote {
if (this.oracleVotes.safeGet(id) == null) {
let vote = OracleVote.load(id);
if (vote == null) {
vote = new OracleVote(id);
}
this.oracleVotes.set(id, vote);
}
return this.oracleVotes.safeGet(id)!;
}

commitChanges(): void {
this.state.save();

let oracles = this.oracles.values();
for (let i = 0; i < oracles.length; i++) {
oracles[i].save();
}

let configs = this.oraclesConfigs.values();
for (let i = 0; i < configs.length; i++) {
configs[i].save();
}

let votes = this.oracleVotes.values();
for (let i = 0; i < votes.length; i++) {
votes[i].save();
}
}
}
103 changes: 27 additions & 76 deletions src/subgraph-availability-manager.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,38 @@
import {
NewOwnership as NewOwnershipEvent,
NewPendingOwnership as NewPendingOwnershipEvent,
OracleSet as OracleSetEvent,
OracleVote as OracleVoteEvent,
VoteTimeLimitSet as VoteTimeLimitSetEvent
} from "../generated/SubgraphAvailabilityManager/SubgraphAvailabilityManager"
import {
NewOwnership,
NewPendingOwnership,
OracleSet,
OracleVote,
VoteTimeLimitSet
} from "../generated/schema"

export function handleNewOwnership(event: NewOwnershipEvent): void {
let entity = new NewOwnership(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.from = event.params.from
entity.to = event.params.to

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

export function handleNewPendingOwnership(
event: NewPendingOwnershipEvent
): void {
let entity = new NewPendingOwnership(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.from = event.params.from
entity.to = event.params.to

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}
import { StoreCache } from "./store-cache";
import { getOracleVoteId } from "./helpers";

export function handleOracleSet(event: OracleSetEvent): void {
let entity = new OracleSet(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.index = event.params.index
entity.oracle = event.params.oracle

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
let oracleIndex = event.params.index.toString();
let oracleAddress = event.params.oracle;

let cache = new StoreCache();
let state = cache.getGlobalState();
let oracle = cache.getOracle(oracleIndex);
oracle.state = state.id;
oracle.address = oracleAddress;
oracle.latestConfig = "";
cache.commitChanges();
}

export function handleOracleVote(event: OracleVoteEvent): void {
let entity = new OracleVote(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.subgraphDeploymentID = event.params.subgraphDeploymentID
entity.deny = event.params.deny
entity.oracleIndex = event.params.oracleIndex
entity.timestamp = event.params.timestamp

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}

export function handleVoteTimeLimitSet(event: VoteTimeLimitSetEvent): void {
let entity = new VoteTimeLimitSet(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.voteTimeLimit = event.params.voteTimeLimit

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
let subgraphDeploymentID = event.params.subgraphDeploymentID.toHexString();
let oracleIndex = event.params.oracleIndex.toString();
let timestamp = event.params.timestamp.toString();
let voteId = getOracleVoteId(subgraphDeploymentID, oracleIndex, timestamp);

let cache = new StoreCache();
let oracle = cache.getOracle(oracleIndex);
let oracleVote = cache.getOracleVote(voteId);
oracleVote.subgraphDeploymentID = event.params.subgraphDeploymentID
oracleVote.deny = event.params.deny
oracleVote.oracle = oracle.id
oracleVote.timestamp = event.params.timestamp

cache.commitChanges();
}
Loading
Loading