Skip to content

Commit 86ebd2c

Browse files
committed
feat(dips): get agreements cli command
1 parent 7b605bd commit 86ebd2c

12 files changed

Lines changed: 636 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { cliTest, connect } from '../util'
2+
import path from 'path'
3+
4+
const baseDir = path.join(__dirname, '..')
5+
describe('Indexer dips tests', () => {
6+
describe('Dips help', () => {
7+
beforeAll(connect)
8+
cliTest('Indexer dips', ['indexer', 'dips'], 'references/indexer-dips', {
9+
expectedExitCode: 1,
10+
cwd: baseDir,
11+
timeout: 10000,
12+
})
13+
cliTest(
14+
'Indexer dips agreements',
15+
['indexer', 'dips', 'agreements'],
16+
'references/indexer-dips-agreements',
17+
{
18+
expectedExitCode: 1,
19+
cwd: baseDir,
20+
timeout: 10000,
21+
},
22+
)
23+
cliTest(
24+
'Indexer dips agreements get help',
25+
['indexer', 'dips', 'agreements', 'get', '--help'],
26+
'references/indexer-dips-agreements-get',
27+
{
28+
expectedExitCode: 0,
29+
cwd: baseDir,
30+
timeout: 10000,
31+
},
32+
)
33+
})
34+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
- Processing inputs
2+
💁
3+
graph indexer dips agreements get [options]
4+
graph indexer dips agreements get [options] <agreement-id>
5+
graph indexer dips agreements get [options] all
6+
7+
Options:
8+
9+
-h, --help Show usage information
10+
-n, --network <network> Filter agreements by their protocol network (mainnet, arbitrum-one, sepolia, arbitrum-sepolia)
11+
--status Accepted|CanceledByPayer|CanceledByServiceProvider|NotAccepted Filter by agreement state
12+
--deployment <id> Fetch only agreements for a specific subgraph deployment
13+
-o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML
14+
-w, --wrap [N] Wrap the output to a specific width (default: 0, no wrapping)
15+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Manage DIPs indexing agreements
2+
3+
indexer dips agreements get List one or more DIPs indexing agreements
4+
indexer dips agreements Manage DIPs indexing agreements
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Manage DIPs (Direct Indexer Payments)
2+
3+
indexer dips agreements get List one or more DIPs indexing agreements
4+
indexer dips agreements Manage DIPs indexing agreements
5+
indexer dips Manage DIPs (Direct Indexer Payments)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { GluegunToolbox } from 'gluegun'
2+
3+
module.exports = {
4+
name: 'dips',
5+
alias: [],
6+
description: 'Manage DIPs (Direct Indexer Payments)',
7+
hidden: false,
8+
dashed: false,
9+
run: async (toolbox: GluegunToolbox) => {
10+
const { print } = toolbox
11+
print.info(toolbox.command?.description)
12+
print.printCommands(toolbox, ['indexer', 'dips'])
13+
process.exitCode = 1
14+
},
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { GluegunToolbox } from 'gluegun'
2+
3+
module.exports = {
4+
name: 'agreements',
5+
alias: [],
6+
description: 'Manage DIPs indexing agreements',
7+
hidden: false,
8+
dashed: false,
9+
run: async (toolbox: GluegunToolbox) => {
10+
const { print } = toolbox
11+
print.info(toolbox.command?.description)
12+
print.printCommands(toolbox, ['indexer', 'dips', 'agreements'])
13+
process.exitCode = 1
14+
},
15+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import { GluegunToolbox } from 'gluegun'
2+
import chalk from 'chalk'
3+
4+
import { loadValidatedConfig } from '../../../../config'
5+
import { createIndexerManagementClient } from '../../../../client'
6+
import {
7+
extractProtocolNetworkOption,
8+
fixParameters,
9+
} from '../../../../command-helpers'
10+
import gql from 'graphql-tag'
11+
import { SubgraphDeploymentID } from '@graphprotocol/common-ts'
12+
import { processIdentifier, SubgraphIdentifierType } from '@graphprotocol/indexer-common'
13+
import { IndexingAgreement, printIndexingAgreements } from '../../../../dips'
14+
import { isHexString } from 'ethers'
15+
16+
const HELP = `
17+
${chalk.bold('graph indexer dips agreements get')} [options]
18+
${chalk.bold('graph indexer dips agreements get')} [options] <agreement-id>
19+
${chalk.bold('graph indexer dips agreements get')} [options] all
20+
21+
${chalk.dim('Options:')}
22+
23+
-h, --help Show usage information
24+
-n, --network <network> Filter agreements by their protocol network (mainnet, arbitrum-one, sepolia, arbitrum-sepolia)
25+
--status Accepted|CanceledByPayer|CanceledByServiceProvider|NotAccepted Filter by agreement state
26+
--deployment <id> Fetch only agreements for a specific subgraph deployment
27+
-o, --output table|json|yaml Choose the output format: table (default), JSON, or YAML
28+
-w, --wrap [N] Wrap the output to a specific width (default: 0, no wrapping)
29+
`
30+
31+
const AGREEMENT_STATES = [
32+
'Accepted',
33+
'CanceledByPayer',
34+
'CanceledByServiceProvider',
35+
'NotAccepted',
36+
]
37+
38+
module.exports = {
39+
name: 'get',
40+
alias: [],
41+
description: 'List one or more DIPs indexing agreements',
42+
run: async (toolbox: GluegunToolbox) => {
43+
const { print, parameters } = toolbox
44+
45+
const spinner = toolbox.print.spin('Processing inputs')
46+
47+
const { status, deployment, h, help, o, output, w, wrap } = parameters.options
48+
49+
const [agreementId] = fixParameters(parameters, { h, help }) || []
50+
const outputFormat = o || output || 'table'
51+
const wrapWidth = w || wrap || 0
52+
53+
if (help || h) {
54+
spinner.stopAndPersist({ symbol: '💁', text: HELP })
55+
return
56+
}
57+
58+
try {
59+
const protocolNetwork = extractProtocolNetworkOption(parameters.options, true)
60+
61+
if (!['json', 'yaml', 'table'].includes(outputFormat)) {
62+
throw Error(
63+
`Invalid output format "${outputFormat}" must be one of 'json', 'yaml' or 'table'`,
64+
)
65+
}
66+
67+
if (status && !AGREEMENT_STATES.includes(status)) {
68+
throw Error(
69+
`Invalid '--status' provided, must be one of ${AGREEMENT_STATES.join(', ')}`,
70+
)
71+
}
72+
73+
if (agreementId) {
74+
if (agreementId !== 'all' && !isHexString(agreementId, 16)) {
75+
throw Error(
76+
`Invalid 'agreement-id' provided ('${agreementId}'), must be a bytes16 string or 'all'`,
77+
)
78+
}
79+
80+
if (agreementId == 'all') {
81+
if (status || deployment) {
82+
throw Error(
83+
`Invalid query, cannot specify '--status' or '--deployment' filters in addition to 'agreement-id = all'`,
84+
)
85+
}
86+
}
87+
}
88+
89+
let deploymentString: string | undefined = undefined
90+
let type: SubgraphIdentifierType
91+
92+
if (deployment) {
93+
;[deploymentString, type] = await processIdentifier(deployment, {
94+
all: true,
95+
global: false,
96+
})
97+
if (type !== SubgraphIdentifierType.DEPLOYMENT) {
98+
throw Error(
99+
`Invalid '--deployment' must be a valid deployment ID (bytes32 or base58 formatted)`,
100+
)
101+
}
102+
}
103+
104+
spinner.text = 'Querying indexer management server'
105+
const config = loadValidatedConfig()
106+
const client = await createIndexerManagementClient({ url: config.api })
107+
108+
const result = await client
109+
.query(
110+
gql`
111+
query indexingAgreements($filter: IndexingAgreementFilter!) {
112+
indexingAgreements(filter: $filter) {
113+
id
114+
payer
115+
indexer
116+
allocationId
117+
subgraphDeploymentId
118+
state
119+
acceptedAt
120+
lastCollectionAt
121+
endsAt
122+
tokensPerSecond
123+
tokensCollected
124+
canceledAt
125+
canceledBy
126+
protocolNetwork
127+
}
128+
}
129+
`,
130+
{
131+
filter: {
132+
status: status ? status : null,
133+
agreementId:
134+
agreementId && agreementId !== 'all' ? agreementId : null,
135+
protocolNetwork,
136+
},
137+
},
138+
)
139+
.toPromise()
140+
141+
if (result.error) {
142+
throw result.error
143+
}
144+
145+
const agreements = deploymentString
146+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
147+
result.data.indexingAgreements.filter((agreement: any) => {
148+
return (
149+
new SubgraphDeploymentID(agreement.subgraphDeploymentId).toString() ===
150+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
151+
new SubgraphDeploymentID(deploymentString!).toString()
152+
)
153+
})
154+
: result.data.indexingAgreements
155+
156+
let displayProperties: (keyof IndexingAgreement)[] = [
157+
'id',
158+
'subgraphDeploymentId',
159+
'allocationId',
160+
'payer',
161+
'state',
162+
'acceptedAt',
163+
'endsAt',
164+
'tokensCollected',
165+
'protocolNetwork',
166+
]
167+
if (agreementId && agreementId !== 'all') {
168+
displayProperties = [
169+
'id',
170+
'payer',
171+
'indexer',
172+
'allocationId',
173+
'subgraphDeploymentId',
174+
'state',
175+
'acceptedAt',
176+
'lastCollectionAt',
177+
'endsAt',
178+
'tokensPerSecond',
179+
'tokensCollected',
180+
'canceledAt',
181+
'canceledBy',
182+
'protocolNetwork',
183+
]
184+
}
185+
186+
spinner.succeed('Agreements')
187+
printIndexingAgreements(
188+
print,
189+
outputFormat,
190+
agreements,
191+
displayProperties,
192+
wrapWidth,
193+
)
194+
} catch (error) {
195+
spinner.fail(error.toString())
196+
process.exitCode = 1
197+
return
198+
}
199+
},
200+
}

0 commit comments

Comments
 (0)