Skip to content

Commit 84b0d21

Browse files
graph init: invalid characters in contract name (#1966)
* remove invalid characters from contract name changeset * add tests * remove hardcoded default name
1 parent 5ffce65 commit 84b0d21

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

.changeset/red-roses-speak.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphprotocol/graph-cli': patch
3+
---
4+
5+
handle invalid characters in contract name #1883
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { formatContractName, formatSubgraphName, getSubgraphBasename } from './subgraph';
3+
4+
describe('getSubgraphBasename', () => {
5+
it('returns the last segment of a subgraph name', () => {
6+
expect(getSubgraphBasename('user/my-subgraph')).toBe('my-subgraph');
7+
expect(getSubgraphBasename('org/project')).toBe('project');
8+
});
9+
10+
it('returns the full name if no slash is present', () => {
11+
expect(getSubgraphBasename('single-name')).toBe('single-name');
12+
});
13+
});
14+
15+
describe('formatSubgraphName', () => {
16+
it('converts to lowercase', () => {
17+
expect(formatSubgraphName('MySubGraph')).toBe('mysubgraph');
18+
});
19+
20+
it('replaces spaces with hyphens', () => {
21+
expect(formatSubgraphName('my subgraph name')).toBe('my-subgraph-name');
22+
expect(formatSubgraphName('multiple spaces')).toBe('multiple-spaces');
23+
});
24+
25+
it('removes special characters', () => {
26+
expect(formatSubgraphName('my$special@subgraph!')).toBe('myspecialsubgraph');
27+
});
28+
29+
it('keeps alphanumeric characters, hyphens and underscores', () => {
30+
expect(formatSubgraphName('my-subgraph_123')).toBe('my-subgraph_123');
31+
});
32+
});
33+
34+
describe('formatContractName', () => {
35+
it('replaces spaces and dots with underscores', () => {
36+
expect(formatContractName('My Contract')).toBe('My_Contract');
37+
expect(formatContractName('contract.name')).toBe('contract_name');
38+
expect(formatContractName('multiple...dots')).toBe('multiple_dots');
39+
});
40+
41+
it('removes special characters but keeps alphanumeric, hyphens and underscores', () => {
42+
expect(formatContractName('Contract$$$Name')).toBe('ContractName');
43+
expect(formatContractName('My-Contract_123')).toBe('My-Contract_123');
44+
expect(formatContractName('My Contract $$$$')).toBe('My_Contract_');
45+
});
46+
47+
it('preserves case', () => {
48+
expect(formatContractName('MyContractName')).toBe('MyContractName');
49+
});
50+
});

packages/cli/src/command-helpers/subgraph.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ export const formatSubgraphName = (slug: string) => {
99
.replace(/\s+/g, '-')
1010
.replace(/[^a-z0-9_-]/g, '');
1111
};
12+
13+
export const formatContractName = (contractName: string) => {
14+
return contractName.replace(/[\s.]+/g, '_').replace(/[^a-zA-Z0-9_-]/g, '');
15+
};

packages/cli/src/commands/init.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ import { retryWithPrompt } from '../command-helpers/retry.js';
1616
import { generateScaffold, writeScaffold } from '../command-helpers/scaffold.js';
1717
import { sortWithPriority } from '../command-helpers/sort.js';
1818
import { withSpinner } from '../command-helpers/spinner.js';
19-
import { formatSubgraphName, getSubgraphBasename } from '../command-helpers/subgraph.js';
19+
import {
20+
formatContractName,
21+
formatSubgraphName,
22+
getSubgraphBasename,
23+
} from '../command-helpers/subgraph.js';
2024
import { GRAPH_CLI_SHARED_HEADERS } from '../constants.js';
2125
import debugFactory from '../debug.js';
2226
import EthereumABI from '../protocols/ethereum/abi.js';
@@ -289,7 +293,7 @@ export default class InitCommand extends Command {
289293
indexEvents,
290294
network,
291295
subgraphName,
292-
contractName: contractName || DEFAULT_CONTRACT_NAME,
296+
contractName,
293297
node,
294298
startBlock,
295299
spkgPath,
@@ -351,7 +355,7 @@ export default class InitCommand extends Command {
351355
network: answers.network,
352356
source: answers.source,
353357
indexEvents: answers.indexEvents,
354-
contractName: answers.contractName || DEFAULT_CONTRACT_NAME,
358+
contractName: answers.contractName,
355359
node,
356360
startBlock: answers.startBlock,
357361
spkgPath: answers.spkgPath,
@@ -873,7 +877,7 @@ async function processInitForm(
873877
type: 'input',
874878
name: 'contractName',
875879
message: 'Contract name',
876-
initial: () => initContractName || contractName || 'Contract',
880+
initial: () => initContractName || contractName || DEFAULT_CONTRACT_NAME,
877881
skip: () => initFromExample !== undefined || !protocolInstance.hasContract() || isSubstreams,
878882
validate: value =>
879883
initFromExample !== undefined ||
@@ -1308,7 +1312,7 @@ async function initSubgraphFromContract(
13081312
network,
13091313
source,
13101314
indexEvents,
1311-
contractName,
1315+
contractName: formatContractName(contractName || DEFAULT_CONTRACT_NAME),
13121316
startBlock,
13131317
node,
13141318
spkgPath,

0 commit comments

Comments
 (0)