Skip to content

Commit 1ba6e82

Browse files
authored
feat: aggregated IndexingAgreement entity (#5)
* feat: aggregated IndexingAgreement entity * fix: bump subgraph spec version
1 parent 537fcfe commit 1ba6e82

11 files changed

Lines changed: 441 additions & 276 deletions

abis/RecurringCollector.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
[
2+
{
3+
"anonymous": false,
4+
"inputs": [
5+
{ "indexed": true, "name": "dataService", "type": "address" },
6+
{ "indexed": true, "name": "payer", "type": "address" },
7+
{ "indexed": true, "name": "serviceProvider", "type": "address" },
8+
{ "indexed": false, "name": "agreementId", "type": "bytes16" },
9+
{ "indexed": false, "name": "acceptedAt", "type": "uint64" },
10+
{ "indexed": false, "name": "endsAt", "type": "uint64" },
11+
{ "indexed": false, "name": "maxInitialTokens", "type": "uint256" },
12+
{ "indexed": false, "name": "maxOngoingTokensPerSecond", "type": "uint256" },
13+
{ "indexed": false, "name": "minSecondsPerCollection", "type": "uint32" },
14+
{ "indexed": false, "name": "maxSecondsPerCollection", "type": "uint32" }
15+
],
16+
"name": "AgreementAccepted",
17+
"type": "event"
18+
},
19+
{
20+
"anonymous": false,
21+
"inputs": [
22+
{ "indexed": true, "name": "dataService", "type": "address" },
23+
{ "indexed": true, "name": "payer", "type": "address" },
24+
{ "indexed": true, "name": "serviceProvider", "type": "address" },
25+
{ "indexed": false, "name": "agreementId", "type": "bytes16" },
26+
{ "indexed": false, "name": "canceledAt", "type": "uint64" },
27+
{ "indexed": false, "name": "canceledBy", "type": "uint8" }
28+
],
29+
"name": "AgreementCanceled",
30+
"type": "event"
31+
},
32+
{
33+
"anonymous": false,
34+
"inputs": [
35+
{ "indexed": true, "name": "dataService", "type": "address" },
36+
{ "indexed": true, "name": "payer", "type": "address" },
37+
{ "indexed": true, "name": "serviceProvider", "type": "address" },
38+
{ "indexed": false, "name": "agreementId", "type": "bytes16" },
39+
{ "indexed": false, "name": "updatedAt", "type": "uint64" },
40+
{ "indexed": false, "name": "endsAt", "type": "uint64" },
41+
{ "indexed": false, "name": "maxInitialTokens", "type": "uint256" },
42+
{ "indexed": false, "name": "maxOngoingTokensPerSecond", "type": "uint256" },
43+
{ "indexed": false, "name": "minSecondsPerCollection", "type": "uint32" },
44+
{ "indexed": false, "name": "maxSecondsPerCollection", "type": "uint32" }
45+
],
46+
"name": "AgreementUpdated",
47+
"type": "event"
48+
},
49+
{
50+
"anonymous": false,
51+
"inputs": [
52+
{ "indexed": true, "name": "dataService", "type": "address" },
53+
{ "indexed": true, "name": "payer", "type": "address" },
54+
{ "indexed": true, "name": "serviceProvider", "type": "address" },
55+
{ "indexed": false, "name": "agreementId", "type": "bytes16" },
56+
{ "indexed": false, "name": "collectionId", "type": "bytes32" },
57+
{ "indexed": false, "name": "tokens", "type": "uint256" },
58+
{ "indexed": false, "name": "dataServiceCut", "type": "uint256" }
59+
],
60+
"name": "RCACollected",
61+
"type": "event"
62+
}
63+
]

config/arbitrum-one.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"network": "arbitrum-one",
3-
"address": "0x0000000000000000000000000000000000000000",
3+
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000",
4+
"recurringCollectorAddress": "0x0000000000000000000000000000000000000000",
45
"startBlock": 0
56
}

config/arbitrum-sepolia.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"network": "arbitrum-sepolia",
3-
"address": "0x0000000000000000000000000000000000000000",
3+
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000",
4+
"recurringCollectorAddress": "0x0000000000000000000000000000000000000000",
45
"startBlock": 0
56
}

config/hardhat.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"network": "hardhat",
3-
"address": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
3+
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000",
4+
"recurringCollectorAddress": "0x0000000000000000000000000000000000000000",
45
"startBlock": 0
56
}

schema.graphql

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,63 @@
1-
type IndexingAgreementAccepted @entity(immutable: true) {
2-
id: ID!
3-
indexer: Bytes!
4-
payer: Bytes!
5-
agreementId: Bytes!
6-
allocationId: Bytes!
7-
blockNumber: BigInt!
8-
blockTimestamp: BigInt!
9-
transactionHash: Bytes!
1+
"Agreement lifecycle state"
2+
enum AgreementState {
3+
"Proposed but not yet accepted (reserved for future use — entities are currently only created on accept)"
4+
NotAccepted
5+
Accepted
6+
CanceledByServiceProvider
7+
CanceledByPayer
108
}
119

12-
type IndexingAgreementCanceled @entity(immutable: true) {
13-
id: ID!
14-
indexer: Bytes!
10+
type IndexingAgreement @entity(immutable: false) {
11+
"The agreement ID (bytes16)"
12+
id: Bytes!
13+
"The payer address"
1514
payer: Bytes!
16-
agreementId: Bytes!
17-
canceledBy: Bytes!
18-
blockNumber: BigInt!
19-
blockTimestamp: BigInt!
20-
transactionHash: Bytes!
21-
}
22-
23-
type IndexingAgreementUpdated @entity(immutable: true) {
24-
id: ID!
15+
"The indexer address"
2516
indexer: Bytes!
26-
payer: Bytes!
27-
agreementId: Bytes!
17+
"The allocation linked to this agreement"
2818
allocationId: Bytes!
29-
version: Int!
30-
blockNumber: BigInt!
31-
blockTimestamp: BigInt!
32-
transactionHash: Bytes!
19+
"The subgraph deployment ID"
20+
subgraphDeploymentId: Bytes!
21+
"Agreement lifecycle state"
22+
state: AgreementState!
23+
"Timestamp when the agreement was accepted"
24+
acceptedAt: BigInt!
25+
"Timestamp of last collection"
26+
lastCollectionAt: BigInt!
27+
"Timestamp when the agreement ends"
28+
endsAt: BigInt!
29+
"Maximum tokens for first collection"
30+
maxInitialTokens: BigInt!
31+
"Maximum tokens per second for ongoing collections"
32+
maxOngoingTokensPerSecond: BigInt!
33+
"Tokens per second from agreement terms"
34+
tokensPerSecond: BigInt!
35+
"Tokens per entity per second from agreement terms"
36+
tokensPerEntityPerSecond: BigInt!
37+
"Minimum seconds between collections"
38+
minSecondsPerCollection: Int!
39+
"Maximum seconds between collections"
40+
maxSecondsPerCollection: Int!
41+
"Timestamp when agreement was last updated (0 if never updated)"
42+
lastUpdatedAt: BigInt!
43+
"Timestamp when agreement was canceled (0 if not canceled)"
44+
canceledAt: BigInt!
45+
"Total tokens collected over lifetime"
46+
tokensCollected: BigInt!
47+
"Fee collection history"
48+
collections: [IndexingFeeCollection!]! @derivedFrom(field: "agreement")
3349
}
3450

35-
type IndexingFeesCollected @entity(immutable: true) {
36-
id: ID!
37-
indexer: Bytes!
38-
payer: Bytes!
39-
agreementId: Bytes!
40-
allocationId: Bytes!
41-
subgraphDeploymentId: Bytes!
51+
type IndexingFeeCollection @entity(immutable: true) {
52+
id: Bytes!
53+
agreement: IndexingAgreement!
4254
currentEpoch: BigInt!
4355
tokensCollected: BigInt!
4456
entities: BigInt!
4557
poi: Bytes!
4658
poiBlockNumber: BigInt!
4759
blockNumber: BigInt!
4860
blockTimestamp: BigInt!
49-
transactionHash: Bytes!
5061
}
5162

5263
type IndexerDeploymentLatest @entity(immutable: false) {

src/helpers.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
2+
import { IndexingAgreement } from '../generated/schema'
3+
4+
export const BIGINT_ZERO = BigInt.fromI32(0)
5+
6+
export function createOrLoadIndexingAgreement(agreementId: Bytes): IndexingAgreement {
7+
let agreement = IndexingAgreement.load(agreementId)
8+
if (agreement == null) {
9+
agreement = new IndexingAgreement(agreementId)
10+
agreement.payer = Bytes.empty()
11+
agreement.indexer = Bytes.empty()
12+
agreement.allocationId = Bytes.empty()
13+
agreement.subgraphDeploymentId = Bytes.empty()
14+
agreement.state = 'NotAccepted'
15+
agreement.acceptedAt = BIGINT_ZERO
16+
agreement.lastCollectionAt = BIGINT_ZERO
17+
agreement.endsAt = BIGINT_ZERO
18+
agreement.maxInitialTokens = BIGINT_ZERO
19+
agreement.maxOngoingTokensPerSecond = BIGINT_ZERO
20+
agreement.tokensPerSecond = BIGINT_ZERO
21+
agreement.tokensPerEntityPerSecond = BIGINT_ZERO
22+
agreement.minSecondsPerCollection = 0
23+
agreement.maxSecondsPerCollection = 0
24+
agreement.lastUpdatedAt = BIGINT_ZERO
25+
agreement.canceledAt = BIGINT_ZERO
26+
agreement.tokensCollected = BIGINT_ZERO
27+
}
28+
return agreement
29+
}

src/mapping.ts

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/recurringCollector.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { IndexingAgreement } from '../generated/schema'
2+
import {
3+
AgreementAccepted,
4+
AgreementCanceled,
5+
AgreementUpdated,
6+
RCACollected,
7+
} from '../generated/RecurringCollector/RecurringCollector'
8+
import { createOrLoadIndexingAgreement, BIGINT_ZERO } from './helpers'
9+
10+
export function handleAgreementAccepted(event: AgreementAccepted): void {
11+
let agreement = createOrLoadIndexingAgreement(event.params.agreementId)
12+
13+
agreement.payer = event.params.payer
14+
agreement.indexer = event.params.serviceProvider
15+
agreement.state = 'Accepted'
16+
agreement.acceptedAt = event.params.acceptedAt
17+
agreement.lastCollectionAt = event.params.acceptedAt
18+
agreement.endsAt = event.params.endsAt
19+
agreement.maxInitialTokens = event.params.maxInitialTokens
20+
agreement.maxOngoingTokensPerSecond = event.params.maxOngoingTokensPerSecond
21+
agreement.minSecondsPerCollection = event.params.minSecondsPerCollection.toI32()
22+
agreement.maxSecondsPerCollection = event.params.maxSecondsPerCollection.toI32()
23+
agreement.canceledAt = BIGINT_ZERO
24+
agreement.tokensCollected = BIGINT_ZERO
25+
26+
agreement.save()
27+
}
28+
29+
export function handleAgreementCanceled(event: AgreementCanceled): void {
30+
let agreement = IndexingAgreement.load(event.params.agreementId)
31+
if (agreement == null) return
32+
33+
// canceledBy enum: 0=ServiceProvider, 1=Payer
34+
if (event.params.canceledBy == 0) {
35+
agreement.state = 'CanceledByServiceProvider'
36+
} else {
37+
agreement.state = 'CanceledByPayer'
38+
}
39+
agreement.canceledAt = event.params.canceledAt
40+
agreement.save()
41+
}
42+
43+
export function handleAgreementUpdated(event: AgreementUpdated): void {
44+
let agreement = IndexingAgreement.load(event.params.agreementId)
45+
if (agreement == null) return
46+
47+
agreement.lastUpdatedAt = event.params.updatedAt
48+
agreement.endsAt = event.params.endsAt
49+
agreement.maxInitialTokens = event.params.maxInitialTokens
50+
agreement.maxOngoingTokensPerSecond = event.params.maxOngoingTokensPerSecond
51+
agreement.minSecondsPerCollection = event.params.minSecondsPerCollection.toI32()
52+
agreement.maxSecondsPerCollection = event.params.maxSecondsPerCollection.toI32()
53+
agreement.save()
54+
}
55+
56+
export function handleRCACollected(event: RCACollected): void {
57+
let agreement = IndexingAgreement.load(event.params.agreementId)
58+
if (agreement == null) return
59+
60+
agreement.lastCollectionAt = event.block.timestamp
61+
agreement.tokensCollected = agreement.tokensCollected.plus(event.params.tokens)
62+
agreement.save()
63+
}

0 commit comments

Comments
 (0)