Skip to content

Commit 54121df

Browse files
committed
feat: indexing fees / dips (wip)
1 parent 5e72ec7 commit 54121df

28 files changed

+946
-13
lines changed

packages/indexer-agent/jest.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ module.exports = {
1111
preset: 'ts-jest',
1212
testEnvironment: 'node',
1313
testPathIgnorePatterns: ['/node_modules/', '/dist/', '.yalc'],
14+
transformIgnorePatterns: [
15+
// Transform @graphprotocol packages but ignore other node_modules
16+
'/node_modules/(?!(@graphprotocol)/)'
17+
],
18+
transform: {
19+
'^.+\\.ts$': ['ts-jest'] // Only transform .ts files
20+
},
1421
globals: {
1522
__DATABASE__: {
1623
host:

packages/indexer-agent/src/__tests__/indexer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ const setup = async () => {
146146
const network = await Network.create(
147147
logger,
148148
networkSpecification,
149+
models,
149150
queryFeeModels,
150151
graphNode,
151152
metrics,

packages/indexer-agent/src/agent.ts

+10
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ export class Agent {
223223
{ logger, milliseconds: requestIntervalSmall },
224224
async () => {
225225
const { network, operator } = this.networkAndOperator
226+
if (network.specification.indexerOptions.enableDips) {
227+
// There should be a DipsManager in the operator
228+
if (!operator.dipsManager) {
229+
throw new Error('DipsManager is not available')
230+
}
231+
logger.trace('Ensuring indexing rules for DIPS', {
232+
protocolNetwork: network.specification.networkIdentifier,
233+
})
234+
await operator.dipsManager.ensureAgreementRules()
235+
}
226236
logger.trace('Fetching indexing rules', {
227237
protocolNetwork: network.specification.networkIdentifier,
228238
})

packages/indexer-agent/src/commands/start.ts

+28
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,26 @@ export const start = {
307307
default: 1,
308308
group: 'Indexer Infrastructure',
309309
})
310+
.option('enable-dips', {
311+
description: 'Whether to enable Indexing Fees (DIPs)',
312+
type: 'boolean',
313+
default: false,
314+
group: 'Indexing Fees ("DIPs")',
315+
})
316+
.option('dipper-endpoint', {
317+
description: 'Gateway endpoint for DIPs receipts',
318+
type: 'string',
319+
array: false,
320+
required: false,
321+
group: 'Indexing Fees ("DIPs")',
322+
})
323+
.option('dips-allocation-amount', {
324+
description: 'Amount of GRT to allocate for DIPs',
325+
type: 'number',
326+
default: 1,
327+
required: false,
328+
group: 'Indexing Fees ("DIPs")',
329+
})
310330
.check(argv => {
311331
if (
312332
!argv['network-subgraph-endpoint'] &&
@@ -334,6 +354,9 @@ export const start = {
334354
) {
335355
return 'Invalid --rebate-claim-max-batch-size provided. Must be > 0 and an integer.'
336356
}
357+
if (argv['enable-dips'] && !argv['dipper-endpoint']) {
358+
return 'Invalid --dipper-endpoint provided. Must be provided when --enable-dips is true.'
359+
}
337360
return true
338361
})
339362
},
@@ -369,6 +392,10 @@ export async function createNetworkSpecification(
369392
allocateOnNetworkSubgraph: argv.allocateOnNetworkSubgraph,
370393
register: argv.register,
371394
finalityTime: argv.chainFinalizeTime,
395+
enableDips: argv.enableDips,
396+
dipperEndpoint: argv.dipperEndpoint,
397+
dipsAllocationAmount: argv.dipsAllocationAmount,
398+
dipsEpochsMargin: argv.dipsEpochsMargin,
372399
}
373400

374401
const transactionMonitoring = {
@@ -585,6 +612,7 @@ export async function run(
585612
const network = await Network.create(
586613
logger,
587614
networkSpecification,
615+
managementModels,
588616
queryFeeModels,
589617
graphNode,
590618
metrics,

packages/indexer-cli/jest.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ module.exports = {
1313
// The glob patterns Jest uses to detect test files
1414
testMatch: ['**/?(*.)+(spec|test).ts?(x)'],
1515
testPathIgnorePatterns: ['/node_modules/', '/dist/', '.yalc'],
16+
transformIgnorePatterns: [
17+
// Transform @graphprotocol packages but ignore other node_modules
18+
'/node_modules/(?!(@graphprotocol)/)'
19+
],
20+
transform: {
21+
'^.+\\.ts$': ['ts-jest'] // Only transform .ts files
22+
},
1623
testTimeout: 60000,
1724
globals: {
1825
__DATABASE__: {

packages/indexer-cli/src/__tests__/util.ts

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const setup = async () => {
8787
const network = await Network.create(
8888
logger,
8989
testNetworkSpecification,
90+
models,
9091
queryFeeModels,
9192
graphNode,
9293
metrics,

packages/indexer-common/jest.config.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ module.exports = {
1111
forceExit: true,
1212
testEnvironment: 'node',
1313
testPathIgnorePatterns: ['/node_modules/', '/dist/', '/.yalc', 'util.ts'],
14-
transformIgnorePatterns: ['!node_modules/'],
14+
transformIgnorePatterns: [
15+
// Transform @graphprotocol packages but ignore other node_modules
16+
'/node_modules/(?!(@graphprotocol)/)'
17+
],
18+
transform: {
19+
'^.+\\.ts$': ['ts-jest'] // Only transform .ts files
20+
},
1521
globals: {
1622
__DATABASE__: {
1723
host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'),

packages/indexer-common/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
"clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo"
2323
},
2424
"dependencies": {
25+
"@bufbuild/protobuf": "2.2.3",
2526
"@graphprotocol/common-ts": "2.0.11",
2627
"@graphprotocol/cost-model": "0.1.18",
28+
"@graphprotocol/dips-proto": "0.2.1",
29+
"@grpc/grpc-js": "^1.12.6",
2730
"@semiotic-labs/tap-contracts-bindings": "^1.2.1",
2831
"@thi.ng/heaps": "1.2.38",
2932
"@types/lodash.clonedeep": "^4.5.7",

packages/indexer-common/src/allocations/__tests__/tap.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
TapSubgraphResponse,
88
TapCollector,
99
Allocation,
10+
defineIndexerManagementModels,
1011
} from '@graphprotocol/indexer-common'
1112
import {
1213
Address,
@@ -43,6 +44,7 @@ const setup = async () => {
4344
// Clearing the registry prevents duplicate metric registration in the default registry.
4445
metrics.registry.clear()
4546
sequelize = await connectDatabase(__DATABASE__)
47+
const models = defineIndexerManagementModels(sequelize)
4648
queryFeeModels = defineQueryFeeModels(sequelize)
4749
sequelize = await sequelize.sync({ force: true })
4850

@@ -56,6 +58,7 @@ const setup = async () => {
5658
const network = await Network.create(
5759
logger,
5860
testNetworkSpecification,
61+
models,
5962
queryFeeModels,
6063
graphNode,
6164
metrics,

packages/indexer-common/src/allocations/__tests__/validate-queries.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
defineIndexerManagementModels,
23
defineQueryFeeModels,
34
GraphNode,
45
Network,
@@ -36,6 +37,7 @@ const setup = async () => {
3637
// Clearing the registry prevents duplicate metric registration in the default registry.
3738
metrics.registry.clear()
3839
sequelize = await connectDatabase(__DATABASE__)
40+
const models = defineIndexerManagementModels(sequelize)
3941
queryFeeModels = defineQueryFeeModels(sequelize)
4042
sequelize = await sequelize.sync({ force: true })
4143

@@ -49,6 +51,7 @@ const setup = async () => {
4951
const network = await Network.create(
5052
logger,
5153
testNetworkSpecification,
54+
models,
5255
queryFeeModels,
5356
graphNode,
5457
metrics,

packages/indexer-common/src/allocations/escrow-accounts.ts

+31
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ export type EscrowAccountResponse = {
1313
}[]
1414
}
1515

16+
export type EscrowSenderResponse = {
17+
signer: {
18+
sender: {
19+
id: string
20+
}
21+
}
22+
}
23+
1624
export class EscrowAccounts {
1725
constructor(private sendersBalances: Map<Address, U256>) {}
1826

@@ -65,3 +73,26 @@ export const getEscrowAccounts = async (
6573
}
6674
return EscrowAccounts.fromResponse(result.data)
6775
}
76+
77+
export const getEscrowSenderForSigner = async (
78+
tapSubgraph: SubgraphClient,
79+
signer: Address,
80+
): Promise<Address> => {
81+
const signerLower = signer.toLowerCase()
82+
const result = await tapSubgraph.query<EscrowSenderResponse>(
83+
gql`
84+
query EscrowAccountQuery($signer: ID!) {
85+
signer(id: $signer) {
86+
sender {
87+
id
88+
}
89+
}
90+
}
91+
`,
92+
{ signer: signerLower },
93+
)
94+
if (!result.data) {
95+
throw `There was an error while querying Tap Subgraph. Errors: ${result.error}`
96+
}
97+
return toAddress(result.data.signer.sender.id)
98+
}

packages/indexer-common/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './allocations'
33
export * from './async-cache'
44
export * from './errors'
55
export * from './indexer-management'
6+
export * from './indexing-fees'
67
export * from './graph-node'
78
export * from './operator'
89
export * from './network'

packages/indexer-common/src/indexer-management/__tests__/allocations.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const setup = async () => {
6161
const network = await Network.create(
6262
logger,
6363
testNetworkSpecification,
64+
managementModels,
6465
queryFeeModels,
6566
graphNode,
6667
metrics,

packages/indexer-common/src/indexer-management/__tests__/util.ts

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const createTestManagementClient = async (
5656
const network = await Network.create(
5757
logger,
5858
networkSpecification,
59+
managementModels,
5960
queryFeeModels,
6061
graphNode,
6162
metrics,

packages/indexer-common/src/indexer-management/allocations.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
AllocationStatus,
1616
CloseAllocationResult,
1717
CreateAllocationResult,
18+
DipsManager,
1819
fetchIndexingRules,
1920
GraphNode,
2021
indexerError,
@@ -98,12 +99,23 @@ export type TransactionResult =
9899
| ActionFailure[]
99100

100101
export class AllocationManager {
102+
declare dipsManager: DipsManager | null
101103
constructor(
102104
private logger: Logger,
103105
private models: IndexerManagementModels,
104106
private graphNode: GraphNode,
105107
private network: Network,
106-
) {}
108+
) {
109+
if (this.network.specification.indexerOptions.dipperEndpoint) {
110+
this.dipsManager = new DipsManager(
111+
this.logger,
112+
this.models,
113+
this.graphNode,
114+
this.network,
115+
this,
116+
)
117+
}
118+
}
107119

108120
async executeBatch(actions: Action[]): Promise<AllocationResult[]> {
109121
const logger = this.logger.child({ function: 'executeBatch' })
@@ -511,6 +523,14 @@ export class AllocationManager {
511523
await upsertIndexingRule(logger, this.models, indexingRule)
512524
}
513525

526+
if (this.dipsManager) {
527+
await this.dipsManager.tryUpdateAgreementAllocation(
528+
deployment,
529+
null,
530+
createAllocationEventLogs.allocationID,
531+
)
532+
}
533+
514534
return {
515535
actionID,
516536
type: 'allocate',
@@ -667,6 +687,15 @@ export class AllocationManager {
667687

668688
await upsertIndexingRule(logger, this.models, neverIndexingRule)
669689

690+
if (this.dipsManager) {
691+
await this.dipsManager.tryCancelAgreement(allocationID)
692+
await this.dipsManager.tryUpdateAgreementAllocation(
693+
allocation.subgraphDeployment.id.toString(),
694+
allocationID,
695+
null,
696+
)
697+
}
698+
670699
return {
671700
actionID,
672701
type: 'unallocate',
@@ -966,6 +995,14 @@ export class AllocationManager {
966995
await upsertIndexingRule(logger, this.models, indexingRule)
967996
}
968997

998+
if (this.dipsManager) {
999+
await this.dipsManager.tryUpdateAgreementAllocation(
1000+
subgraphDeploymentID.toString(),
1001+
allocationID,
1002+
createAllocationEventLogs.allocationID,
1003+
)
1004+
}
1005+
9691006
return {
9701007
actionID,
9711008
type: 'reallocate',

packages/indexer-common/src/indexer-management/client.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,11 @@ export interface IndexerManagementClientOptions {
454454
graphNode: GraphNode
455455
network: Network
456456
defaults: IndexerManagementDefaults
457+
actionManager?: ActionManager | undefined
457458
}
458459

459460
export class IndexerManagementClient extends Client {
461+
declare actionManager: ActionManager | undefined
460462
private logger?: Logger
461463
private models: IndexerManagementModels
462464

@@ -465,6 +467,7 @@ export class IndexerManagementClient extends Client {
465467

466468
this.logger = options.logger
467469
this.models = options.models
470+
this.actionManager = options.actionManager
468471
}
469472
}
470473

@@ -501,5 +504,8 @@ export const createIndexerManagementClient = async (
501504
context,
502505
})
503506

504-
return new IndexerManagementClient({ url: 'no-op', exchanges: [exchange] }, options)
507+
return new IndexerManagementClient(
508+
{ url: 'no-op', exchanges: [exchange] },
509+
{ ...options, actionManager },
510+
)
505511
}

packages/indexer-common/src/indexer-management/models/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { IndexingRuleModels, defineIndexingRuleModels } from './indexing-rule'
44
import { CostModelModels, defineCostModelModels } from './cost-model'
55
import { POIDisputeModels, definePOIDisputeModels } from './poi-dispute'
66
import { ActionModels, defineActionModels } from './action'
7+
import { defineIndexingFeesModels, IndexingFeesModels } from './indexing-agreement'
78

89
export * from './cost-model'
910
export * from './indexing-rule'
@@ -13,7 +14,8 @@ export * from './action'
1314
export type IndexerManagementModels = IndexingRuleModels &
1415
CostModelModels &
1516
POIDisputeModels &
16-
ActionModels
17+
ActionModels &
18+
IndexingFeesModels
1719

1820
export const defineIndexerManagementModels = (
1921
sequelize: Sequelize,
@@ -24,4 +26,5 @@ export const defineIndexerManagementModels = (
2426
defineIndexingRuleModels(sequelize),
2527
definePOIDisputeModels(sequelize),
2628
defineActionModels(sequelize),
29+
defineIndexingFeesModels(sequelize),
2730
)

0 commit comments

Comments
 (0)