@graphprotocol/graph-cli 0.94.0-alpha-20250109043757-c53ef95047eaf4833353c30dc2d1f6cbb9fc13e4 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -6
- package/dist/codegen/util.d.ts +4 -1
- package/dist/command-helpers/contracts.d.ts +0 -1
- package/dist/command-helpers/contracts.js +1 -43
- package/dist/commands/add.js +15 -21
- package/dist/commands/auth.js +4 -2
- package/dist/commands/deploy.js +8 -4
- package/dist/commands/init.js +19 -22
- package/dist/commands/publish.js +4 -2
- package/dist/protocols/ethereum/codegen/abi.js +1 -1
- package/dist/protocols/ethereum/scaffold/mapping.js +1 -1
- package/dist/scaffold/ethereum.test.js +25 -1
- package/dist/scaffold/mapping.d.ts +17 -4
- package/dist/scaffold/mapping.js +59 -17
- package/dist/scaffold/schema.d.ts +1 -1
- package/dist/scaffold/tests.js +7 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/command-helpers/proxy.d.ts +0 -7
- package/dist/command-helpers/proxy.js +0 -25
- package/dist/command-helpers/proxy.test.d.ts +0 -1
- package/dist/command-helpers/proxy.test.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
# @graphprotocol/graph-cli
|
|
2
2
|
|
|
3
|
-
## 0.94.0
|
|
3
|
+
## 0.94.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
- [#
|
|
8
|
-
[`
|
|
9
|
-
Thanks [@YaroShkvorets](https://github.com/YaroShkvorets)! -
|
|
10
|
-
|
|
11
|
-
implementation contract.
|
|
7
|
+
- [#1892](https://github.com/graphprotocol/graph-tooling/pull/1892)
|
|
8
|
+
[`b4564a7`](https://github.com/graphprotocol/graph-tooling/commit/b4564a71134c310dee983f6eff613b953bf481be)
|
|
9
|
+
Thanks [@YaroShkvorets](https://github.com/YaroShkvorets)! - handle tuple[] and address[] for
|
|
10
|
+
event parameters - #949
|
|
12
11
|
|
|
13
12
|
### Patch Changes
|
|
14
13
|
|
|
14
|
+
- [#1891](https://github.com/graphprotocol/graph-tooling/pull/1891)
|
|
15
|
+
[`d1fa5e0`](https://github.com/graphprotocol/graph-tooling/commit/d1fa5e0e34a92f24db29d3def26e133e87e7bdde)
|
|
16
|
+
Thanks [@YaroShkvorets](https://github.com/YaroShkvorets)! - fix bug with duplicate IDs - #1604
|
|
17
|
+
|
|
18
|
+
- [#1889](https://github.com/graphprotocol/graph-tooling/pull/1889)
|
|
19
|
+
[`e085e39`](https://github.com/graphprotocol/graph-tooling/commit/e085e39e48cd0241277ede246aa2b7833e6a91f3)
|
|
20
|
+
Thanks [@YaroShkvorets](https://github.com/YaroShkvorets)! - fix confusing "no error" message when
|
|
21
|
+
pressing Escape on prompts
|
|
22
|
+
|
|
15
23
|
- [#1879](https://github.com/graphprotocol/graph-tooling/pull/1879)
|
|
16
24
|
[`f6079a6`](https://github.com/graphprotocol/graph-tooling/commit/f6079a68c016e8d9864f2fc94526a60d4b21eb69)
|
|
17
25
|
Thanks [@McAronDev](https://github.com/McAronDev)! - fix bug with missing Graft type in SpS
|
|
18
26
|
manifest definition
|
|
19
27
|
|
|
28
|
+
- [#1884](https://github.com/graphprotocol/graph-tooling/pull/1884)
|
|
29
|
+
[`93d87a8`](https://github.com/graphprotocol/graph-tooling/commit/93d87a8c6912b87ed8dbdce0e1b4d2765bc1df39)
|
|
30
|
+
Thanks [@YaroShkvorets](https://github.com/YaroShkvorets)! - `graph init`: fix tests generation
|
|
31
|
+
for events with [][] params #1878
|
|
32
|
+
|
|
20
33
|
## 0.93.3
|
|
21
34
|
|
|
22
35
|
### Patch Changes
|
package/dist/codegen/util.d.ts
CHANGED
|
@@ -10,6 +10,5 @@ export declare class ContractService {
|
|
|
10
10
|
getABI(ABICtor: typeof ABI, networkId: string, address: string): Promise<ABI>;
|
|
11
11
|
getStartBlock(networkId: string, address: string): Promise<string>;
|
|
12
12
|
getContractName(networkId: string, address: string): Promise<string>;
|
|
13
|
-
getProxyImplementation(networkId: string, address: string): Promise<string>;
|
|
14
13
|
private fetchTransactionByHash;
|
|
15
14
|
}
|
|
@@ -122,48 +122,6 @@ export class ContractService {
|
|
|
122
122
|
}
|
|
123
123
|
throw new Error(`Failed to fetch contract name for ${address}`);
|
|
124
124
|
}
|
|
125
|
-
async getProxyImplementation(networkId, address) {
|
|
126
|
-
const urls = this.getRpcUrls(networkId);
|
|
127
|
-
if (!urls.length) {
|
|
128
|
-
throw new Error(`No JSON-RPC available for ${networkId} in the registry`);
|
|
129
|
-
}
|
|
130
|
-
const EIP_1967_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
|
|
131
|
-
const OPEN_ZEPPELIN_SLOT = '0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3';
|
|
132
|
-
const getStorageAt = async (url, slot) => {
|
|
133
|
-
const response = await fetch(url, {
|
|
134
|
-
method: 'POST',
|
|
135
|
-
headers: { 'Content-Type': 'application/json' },
|
|
136
|
-
body: JSON.stringify({
|
|
137
|
-
jsonrpc: '2.0',
|
|
138
|
-
method: 'eth_getStorageAt',
|
|
139
|
-
params: [address, slot, 'latest'],
|
|
140
|
-
id: 1,
|
|
141
|
-
}),
|
|
142
|
-
});
|
|
143
|
-
const json = await response.json();
|
|
144
|
-
if (json?.result) {
|
|
145
|
-
const impl = '0x' + json.result.slice(-40);
|
|
146
|
-
if (impl !== '0x0000000000000000000000000000000000000000') {
|
|
147
|
-
return impl;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
151
|
-
};
|
|
152
|
-
for (const url of urls) {
|
|
153
|
-
for (const slot of [EIP_1967_SLOT, OPEN_ZEPPELIN_SLOT]) {
|
|
154
|
-
try {
|
|
155
|
-
const impl = await getStorageAt(url, slot);
|
|
156
|
-
if (impl) {
|
|
157
|
-
return impl;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
logger(`Failed to fetch proxy implementation from ${url}: ${error}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
throw new Error(`No implementation address found`);
|
|
166
|
-
}
|
|
167
125
|
async fetchTransactionByHash(networkId, txHash) {
|
|
168
126
|
const urls = this.getRpcUrls(networkId);
|
|
169
127
|
if (!urls.length) {
|
|
@@ -191,6 +149,6 @@ export class ContractService {
|
|
|
191
149
|
logger(`Failed to fetch tx ${txHash} from ${url}: ${error}`);
|
|
192
150
|
}
|
|
193
151
|
}
|
|
194
|
-
throw new Error(`
|
|
152
|
+
throw new Error(`JSON-RPC is unreachable`);
|
|
195
153
|
}
|
|
196
154
|
}
|
package/dist/commands/add.js
CHANGED
|
@@ -4,7 +4,6 @@ import { Args, Command, Errors, Flags } from '@oclif/core';
|
|
|
4
4
|
import { ContractService } from '../command-helpers/contracts.js';
|
|
5
5
|
import * as DataSourcesExtractor from '../command-helpers/data-sources.js';
|
|
6
6
|
import { updateNetworksFile } from '../command-helpers/network.js';
|
|
7
|
-
import { checkForProxy } from '../command-helpers/proxy.js';
|
|
8
7
|
import { loadRegistry } from '../command-helpers/registry.js';
|
|
9
8
|
import { retryWithPrompt } from '../command-helpers/retry.js';
|
|
10
9
|
import { generateDataSource, writeABI, writeMapping, writeSchema, writeTestsFiles, } from '../command-helpers/scaffold.js';
|
|
@@ -62,7 +61,6 @@ export default class AddCommand extends Command {
|
|
|
62
61
|
let startBlock = startBlockFlag ? parseInt(startBlockFlag).toString() : startBlockFlag;
|
|
63
62
|
let contractName = contractNameFlag || DEFAULT_CONTRACT_NAME;
|
|
64
63
|
let ethabi = null;
|
|
65
|
-
let implAddress = null;
|
|
66
64
|
if (abi) {
|
|
67
65
|
ethabi = EthereumABI.load(contractName, abi);
|
|
68
66
|
}
|
|
@@ -73,18 +71,12 @@ export default class AddCommand extends Command {
|
|
|
73
71
|
ethabi = await retryWithPrompt(() => withSpinner('Fetching ABI from contract API...', 'Failed to fetch ABI', 'Warning fetching ABI', () => contractService.getABI(EthereumABI, network, address)));
|
|
74
72
|
if (!ethabi)
|
|
75
73
|
throw Error;
|
|
76
|
-
const { implementationAbi, implementationAddress } = await checkForProxy(contractService, network, address, ethabi);
|
|
77
|
-
if (implementationAddress) {
|
|
78
|
-
implAddress = implementationAddress;
|
|
79
|
-
ethabi = implementationAbi;
|
|
80
|
-
}
|
|
81
|
-
if (!ethabi)
|
|
82
|
-
throw Error;
|
|
83
74
|
}
|
|
84
75
|
catch (error) {
|
|
85
76
|
// we cannot ask user to do prompt in test environment
|
|
86
77
|
if (process.env.NODE_ENV !== 'test') {
|
|
87
|
-
const { abi: abiFile } = await prompt
|
|
78
|
+
const { abi: abiFile } = await prompt
|
|
79
|
+
.ask([
|
|
88
80
|
{
|
|
89
81
|
type: 'input',
|
|
90
82
|
name: 'abi',
|
|
@@ -99,24 +91,23 @@ export default class AddCommand extends Command {
|
|
|
99
91
|
}
|
|
100
92
|
},
|
|
101
93
|
},
|
|
102
|
-
])
|
|
94
|
+
])
|
|
95
|
+
.catch(() => this.exit(1)); // properly handle ESC
|
|
103
96
|
ethabi = EthereumABI.load(contractName, abiFile);
|
|
104
97
|
}
|
|
105
98
|
}
|
|
106
99
|
}
|
|
107
|
-
if (!ethabi) {
|
|
108
|
-
this.error('Failed to load ABI', { exit: 1 });
|
|
109
|
-
}
|
|
110
100
|
try {
|
|
111
101
|
if (isLocalHost)
|
|
112
102
|
throw Error; // Triggers user prompting without waiting for Etherscan lookup to fail
|
|
113
|
-
startBlock ||= Number(await contractService.getStartBlock(network,
|
|
103
|
+
startBlock ||= Number(await contractService.getStartBlock(network, address)).toString();
|
|
114
104
|
}
|
|
115
105
|
catch (error) {
|
|
116
106
|
// we cannot ask user to do prompt in test environment
|
|
117
107
|
if (process.env.NODE_ENV !== 'test') {
|
|
118
108
|
// If we can't get the start block, we'll just leave it out of the manifest
|
|
119
|
-
const { startBlock: userInputStartBlock } = await prompt
|
|
109
|
+
const { startBlock: userInputStartBlock } = await prompt
|
|
110
|
+
.ask([
|
|
120
111
|
{
|
|
121
112
|
type: 'input',
|
|
122
113
|
name: 'startBlock',
|
|
@@ -127,7 +118,8 @@ export default class AddCommand extends Command {
|
|
|
127
118
|
return value;
|
|
128
119
|
},
|
|
129
120
|
},
|
|
130
|
-
])
|
|
121
|
+
])
|
|
122
|
+
.catch(() => this.exit(1));
|
|
131
123
|
startBlock = userInputStartBlock;
|
|
132
124
|
}
|
|
133
125
|
}
|
|
@@ -136,14 +128,14 @@ export default class AddCommand extends Command {
|
|
|
136
128
|
throw Error; // Triggers user prompting without waiting for Etherscan lookup to fail
|
|
137
129
|
if (contractName === DEFAULT_CONTRACT_NAME) {
|
|
138
130
|
contractName =
|
|
139
|
-
(await contractService.getContractName(network,
|
|
140
|
-
DEFAULT_CONTRACT_NAME;
|
|
131
|
+
(await contractService.getContractName(network, address)) ?? DEFAULT_CONTRACT_NAME;
|
|
141
132
|
}
|
|
142
133
|
}
|
|
143
134
|
catch (error) {
|
|
144
135
|
// not asking user to do prompt in test environment
|
|
145
136
|
if (process.env.NODE_ENV !== 'test') {
|
|
146
|
-
const { contractName: userInputContractName } = await prompt
|
|
137
|
+
const { contractName: userInputContractName } = await prompt
|
|
138
|
+
.ask([
|
|
147
139
|
{
|
|
148
140
|
type: 'input',
|
|
149
141
|
name: 'contractName',
|
|
@@ -154,7 +146,8 @@ export default class AddCommand extends Command {
|
|
|
154
146
|
return value;
|
|
155
147
|
},
|
|
156
148
|
},
|
|
157
|
-
])
|
|
149
|
+
])
|
|
150
|
+
.catch(() => this.exit(1));
|
|
158
151
|
contractName = userInputContractName;
|
|
159
152
|
}
|
|
160
153
|
}
|
|
@@ -196,6 +189,7 @@ export default class AddCommand extends Command {
|
|
|
196
189
|
});
|
|
197
190
|
}
|
|
198
191
|
await withSpinner('Running codegen', 'Failed to run codegen', 'Warning during codegen', async () => await system.run(yarn ? 'yarn codegen' : 'npm run codegen'));
|
|
192
|
+
this.exit(0);
|
|
199
193
|
}
|
|
200
194
|
}
|
|
201
195
|
const getEntities = (manifest) => {
|
package/dist/commands/auth.js
CHANGED
|
@@ -20,7 +20,8 @@ export default class AuthCommand extends Command {
|
|
|
20
20
|
async run() {
|
|
21
21
|
const { args: { 'deploy-key': initialDeployKey }, } = await this.parse(AuthCommand);
|
|
22
22
|
const { node } = chooseNodeUrl({});
|
|
23
|
-
const { deployKey } = await prompt
|
|
23
|
+
const { deployKey } = await prompt
|
|
24
|
+
.ask([
|
|
24
25
|
{
|
|
25
26
|
type: 'input',
|
|
26
27
|
name: 'deployKey',
|
|
@@ -30,7 +31,8 @@ export default class AuthCommand extends Command {
|
|
|
30
31
|
skip: this.validateStudioDeployKey(initialDeployKey),
|
|
31
32
|
validate: value => this.validateStudioDeployKey(value) || `Invalid Subgraph Studio deploy key: ${value}`,
|
|
32
33
|
},
|
|
33
|
-
])
|
|
34
|
+
])
|
|
35
|
+
.catch(() => this.exit(1));
|
|
34
36
|
try {
|
|
35
37
|
await saveDeployKey(node, deployKey);
|
|
36
38
|
print.success(`Deploy key set for ${node}`);
|
package/dist/commands/deploy.js
CHANGED
|
@@ -87,7 +87,8 @@ export default class DeployCommand extends Command {
|
|
|
87
87
|
};
|
|
88
88
|
async run() {
|
|
89
89
|
const { args: { 'subgraph-name': subgraphNameArg, 'subgraph-manifest': manifest }, flags: { 'deploy-key': deployKeyFlag, 'access-token': accessToken, 'version-label': versionLabelFlag, ipfs, headers, node: nodeFlag, 'output-dir': outputDir, 'skip-migrations': skipMigrations, watch, 'debug-fork': debugFork, network, 'network-file': networkFile, 'ipfs-hash': ipfsHash, }, } = await this.parse(DeployCommand);
|
|
90
|
-
const { subgraphName } = await prompt
|
|
90
|
+
const { subgraphName } = await prompt
|
|
91
|
+
.ask([
|
|
91
92
|
{
|
|
92
93
|
type: 'input',
|
|
93
94
|
name: 'subgraphName',
|
|
@@ -96,7 +97,8 @@ export default class DeployCommand extends Command {
|
|
|
96
97
|
initial: subgraphNameArg,
|
|
97
98
|
required: true,
|
|
98
99
|
},
|
|
99
|
-
])
|
|
100
|
+
])
|
|
101
|
+
.catch(() => this.exit(1));
|
|
100
102
|
const { node } = chooseNodeUrl({
|
|
101
103
|
node: nodeFlag,
|
|
102
104
|
});
|
|
@@ -124,7 +126,8 @@ export default class DeployCommand extends Command {
|
|
|
124
126
|
};
|
|
125
127
|
}
|
|
126
128
|
// Ask for label if not on hosted service
|
|
127
|
-
const { versionLabel } = await prompt
|
|
129
|
+
const { versionLabel } = await prompt
|
|
130
|
+
.ask([
|
|
128
131
|
{
|
|
129
132
|
type: 'input',
|
|
130
133
|
name: 'versionLabel',
|
|
@@ -133,7 +136,8 @@ export default class DeployCommand extends Command {
|
|
|
133
136
|
skip: () => !!versionLabelFlag,
|
|
134
137
|
required: true,
|
|
135
138
|
},
|
|
136
|
-
])
|
|
139
|
+
])
|
|
140
|
+
.catch(() => this.exit(1));
|
|
137
141
|
const deploySubgraph = async (ipfsHash) => {
|
|
138
142
|
const spinner = print.spin(`Deploying to Graph node ${requestUrl}`);
|
|
139
143
|
client.request('subgraph_deploy', {
|
package/dist/commands/init.js
CHANGED
|
@@ -10,7 +10,6 @@ import { DEFAULT_IPFS_URL } from '../command-helpers/ipfs.js';
|
|
|
10
10
|
import { initNetworksConfig } from '../command-helpers/network.js';
|
|
11
11
|
import { chooseNodeUrl } from '../command-helpers/node.js';
|
|
12
12
|
import { PromptManager } from '../command-helpers/prompt-manager.js';
|
|
13
|
-
import { checkForProxy } from '../command-helpers/proxy.js';
|
|
14
13
|
import { loadRegistry } from '../command-helpers/registry.js';
|
|
15
14
|
import { retryWithPrompt } from '../command-helpers/retry.js';
|
|
16
15
|
import { generateScaffold, writeScaffold } from '../command-helpers/scaffold.js';
|
|
@@ -464,27 +463,18 @@ async function processInitForm({ abi: initAbi, abiPath: initAbiPath, directory:
|
|
|
464
463
|
return address;
|
|
465
464
|
}
|
|
466
465
|
// If ABI is not provided, try to fetch it from Etherscan API
|
|
467
|
-
let implAddress = undefined;
|
|
468
466
|
if (protocolInstance.hasABIs() && !initAbi) {
|
|
469
467
|
abiFromApi = await retryWithPrompt(() => withSpinner('Fetching ABI from contract API...', 'Failed to fetch ABI', 'Warning fetching ABI', () => contractService.getABI(protocolInstance.getABI(), network.id, address)));
|
|
470
|
-
initDebugger.extend('processInitForm')("
|
|
471
|
-
if (abiFromApi) {
|
|
472
|
-
const { implementationAbi, implementationAddress } = await checkForProxy(contractService, network.id, address, abiFromApi);
|
|
473
|
-
if (implementationAddress) {
|
|
474
|
-
implAddress = implementationAddress;
|
|
475
|
-
abiFromApi = implementationAbi;
|
|
476
|
-
initDebugger.extend('processInitForm')("Impl ABI: '%s', Impl Address: '%s'", abiFromApi?.name, implAddress);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
468
|
+
initDebugger.extend('processInitForm')("abiFromEtherscan len: '%s'", abiFromApi?.name);
|
|
479
469
|
}
|
|
480
470
|
// If startBlock is not provided, try to fetch it from Etherscan API
|
|
481
471
|
if (!initStartBlock) {
|
|
482
|
-
startBlock = await retryWithPrompt(() => withSpinner('Fetching start block from contract API...', 'Failed to fetch start block', 'Warning fetching start block', () => contractService.getStartBlock(network.id,
|
|
472
|
+
startBlock = await retryWithPrompt(() => withSpinner('Fetching start block from contract API...', 'Failed to fetch start block', 'Warning fetching start block', () => contractService.getStartBlock(network.id, address)));
|
|
483
473
|
initDebugger.extend('processInitForm')("startBlockFromEtherscan: '%s'", startBlock);
|
|
484
474
|
}
|
|
485
475
|
// If contract name is not provided, try to fetch it from Etherscan API
|
|
486
476
|
if (!initContractName) {
|
|
487
|
-
contractName = await retryWithPrompt(() => withSpinner('Fetching contract name from contract API...', 'Failed to fetch contract name', 'Warning fetching contract name', () => contractService.getContractName(network.id,
|
|
477
|
+
contractName = await retryWithPrompt(() => withSpinner('Fetching contract name from contract API...', 'Failed to fetch contract name', 'Warning fetching contract name', () => contractService.getContractName(network.id, address)));
|
|
488
478
|
initDebugger.extend('processInitForm')("contractNameFromEtherscan: '%s'", contractName);
|
|
489
479
|
}
|
|
490
480
|
source = address;
|
|
@@ -720,9 +710,10 @@ Subgraph ${subgraphName} created in ${relativeDir}
|
|
|
720
710
|
Make sure to visit the documentation on https://thegraph.com/docs/ for further information.`);
|
|
721
711
|
}
|
|
722
712
|
async function initSubgraphFromExample({ fromExample, subgraphName, directory, skipInstall, skipGit, }, { commands, }) {
|
|
723
|
-
let overwrite = false;
|
|
724
713
|
if (filesystem.exists(directory)) {
|
|
725
|
-
overwrite = await prompt
|
|
714
|
+
const overwrite = await prompt
|
|
715
|
+
.confirm('Directory already exists, do you want to initialize the subgraph here (files will be overwritten) ?', false)
|
|
716
|
+
.catch(() => false);
|
|
726
717
|
if (!overwrite) {
|
|
727
718
|
this.exit(1);
|
|
728
719
|
}
|
|
@@ -746,7 +737,7 @@ async function initSubgraphFromExample({ fromExample, subgraphName, directory, s
|
|
|
746
737
|
if (!filesystem.exists(exampleSubgraphPath)) {
|
|
747
738
|
return { result: false, error: `Example not found: ${fromExample}` };
|
|
748
739
|
}
|
|
749
|
-
filesystem.copy(exampleSubgraphPath, directory, { overwrite });
|
|
740
|
+
filesystem.copy(exampleSubgraphPath, directory, { overwrite: true });
|
|
750
741
|
return true;
|
|
751
742
|
}
|
|
752
743
|
finally {
|
|
@@ -811,9 +802,13 @@ async function initSubgraphFromExample({ fromExample, subgraphName, directory, s
|
|
|
811
802
|
}
|
|
812
803
|
async function initSubgraphFromContract({ protocolInstance, subgraphName, directory, abi, network, source, indexEvents, contractName, node, startBlock, spkgPath, skipInstall, skipGit, ipfsUrl, }, { commands, addContract, }) {
|
|
813
804
|
const isComposedSubgraph = protocolInstance.isComposedSubgraph();
|
|
814
|
-
if (filesystem.exists(directory)
|
|
815
|
-
|
|
816
|
-
|
|
805
|
+
if (filesystem.exists(directory)) {
|
|
806
|
+
const overwrite = await prompt
|
|
807
|
+
.confirm('Directory already exists, do you want to initialize the subgraph here (files will be overwritten) ?', false)
|
|
808
|
+
.catch(() => false);
|
|
809
|
+
if (!overwrite) {
|
|
810
|
+
this.exit(1);
|
|
811
|
+
}
|
|
817
812
|
}
|
|
818
813
|
let entities;
|
|
819
814
|
if (isComposedSubgraph) {
|
|
@@ -890,10 +885,12 @@ async function initSubgraphFromContract({ protocolInstance, subgraphName, direct
|
|
|
890
885
|
this.exit(1);
|
|
891
886
|
}
|
|
892
887
|
while (addContract) {
|
|
893
|
-
addContract = await addAnotherContract
|
|
888
|
+
addContract = await addAnotherContract
|
|
889
|
+
.bind(this)({
|
|
894
890
|
protocolInstance,
|
|
895
891
|
directory,
|
|
896
|
-
})
|
|
892
|
+
})
|
|
893
|
+
.catch(() => false);
|
|
897
894
|
}
|
|
898
895
|
}
|
|
899
896
|
printNextSteps.bind(this)({ subgraphName, directory }, { commands });
|
|
@@ -917,7 +914,7 @@ async function addAnotherContract({ protocolInstance, directory, }) {
|
|
|
917
914
|
name: 'contract',
|
|
918
915
|
initial: ProtocolContract.identifierName(),
|
|
919
916
|
required: true,
|
|
920
|
-
message: () =>
|
|
917
|
+
message: () => `\nContract ${ProtocolContract.identifierName()}`,
|
|
921
918
|
validate: value => {
|
|
922
919
|
const { valid, error } = validateContract(value, ProtocolContract);
|
|
923
920
|
return valid ? true : error;
|
package/dist/commands/publish.js
CHANGED
|
@@ -51,7 +51,8 @@ export default class PublishCommand extends Command {
|
|
|
51
51
|
* Prompt the user to open up the browser to continue publishing the subgraph
|
|
52
52
|
*/
|
|
53
53
|
async publishWithBrowser({ ipfsHash, webapp, subgraphId, protocolNetwork, apiKey, }) {
|
|
54
|
-
const { openBrowser } = await prompt
|
|
54
|
+
const { openBrowser } = await prompt
|
|
55
|
+
.ask([
|
|
55
56
|
{
|
|
56
57
|
type: 'confirm',
|
|
57
58
|
name: 'openBrowser',
|
|
@@ -59,7 +60,8 @@ export default class PublishCommand extends Command {
|
|
|
59
60
|
initial: true,
|
|
60
61
|
required: true,
|
|
61
62
|
},
|
|
62
|
-
])
|
|
63
|
+
])
|
|
64
|
+
.catch(() => this.exit(1));
|
|
63
65
|
if (!openBrowser) {
|
|
64
66
|
this.exit(0);
|
|
65
67
|
}
|
|
@@ -220,7 +220,7 @@ export default class AbiCodeGenerator {
|
|
|
220
220
|
name = parentType === 'event' ? `param${index}` : `value${index}`;
|
|
221
221
|
}
|
|
222
222
|
const tupleIdentifier = parentClass + tsCodegen.namedType(name).capitalize();
|
|
223
|
-
const tupleClassName = tupleIdentifier + 'Struct';
|
|
223
|
+
const tupleClassName = tupleIdentifier + (parentField === 'outputValues' ? 'Output' : '') + 'Struct';
|
|
224
224
|
let tupleClasses = [];
|
|
225
225
|
const isTupleType = util.isTupleType(type);
|
|
226
226
|
const returnValue = typesCodegen.ethereumToAsc(parentType === 'tuple'
|
|
@@ -27,7 +27,7 @@ export const generatePlaceholderHandlers = ({ abi, events, contractName, }) => `
|
|
|
27
27
|
entity.count = entity.count + BigInt.fromI32(1)
|
|
28
28
|
|
|
29
29
|
// Entity fields can be set based on event parameters
|
|
30
|
-
${generateEventFieldAssignments(event, contractName).slice(0, 2).join('\n')}
|
|
30
|
+
${generateEventFieldAssignments(event, contractName).assignments.slice(0, 2).join('\n')}
|
|
31
31
|
|
|
32
32
|
// Entities can be written to the store with \`.save()\`
|
|
33
33
|
entity.save()
|
|
@@ -51,7 +51,28 @@ const TEST_CALLABLE_FUNCTIONS = [
|
|
|
51
51
|
outputs: [{ type: 'tuple', components: [{ type: 'uint256' }] }],
|
|
52
52
|
},
|
|
53
53
|
];
|
|
54
|
-
const
|
|
54
|
+
const TEST_TUPLE_ARRAY_EVENT = {
|
|
55
|
+
name: 'TupleArrayEvent',
|
|
56
|
+
type: 'event',
|
|
57
|
+
inputs: [
|
|
58
|
+
{
|
|
59
|
+
name: 'tupleArray',
|
|
60
|
+
type: 'tuple[]',
|
|
61
|
+
components: [
|
|
62
|
+
{ name: 'field1', type: 'uint256' },
|
|
63
|
+
{ name: 'field2', type: 'address' },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{ name: 'addressArray', type: 'address[]' },
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
const TEST_ABI = new ABI('Contract', undefined, immutable.fromJS([
|
|
70
|
+
TEST_EVENT,
|
|
71
|
+
OVERLOADED_EVENT,
|
|
72
|
+
TEST_TUPLE_ARRAY_EVENT,
|
|
73
|
+
TEST_CONTRACT,
|
|
74
|
+
...TEST_CALLABLE_FUNCTIONS,
|
|
75
|
+
]));
|
|
55
76
|
const protocol = new Protocol('ethereum');
|
|
56
77
|
const scaffoldOptions = {
|
|
57
78
|
protocol,
|
|
@@ -82,6 +103,9 @@ describe('Ethereum subgraph scaffolding', () => {
|
|
|
82
103
|
test('Mapping (for indexing events)', async () => {
|
|
83
104
|
expect(await scaffoldWithIndexEvents.generateMapping()).toMatchSnapshot();
|
|
84
105
|
});
|
|
106
|
+
test('Mapping handles tuple array type conversion', async () => {
|
|
107
|
+
expect(await scaffold.generateMapping()).toMatchSnapshot();
|
|
108
|
+
});
|
|
85
109
|
test('Test Files (default)', async () => {
|
|
86
110
|
const files = await scaffoldWithIndexEvents.generateTests();
|
|
87
111
|
const testFile = files?.['contract.test.ts'];
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Map of value types that need to be changetype'd to their corresponding AssemblyScript type
|
|
3
|
+
*/
|
|
4
|
+
export declare const VALUE_TYPECAST_MAP: Record<string, string>;
|
|
5
|
+
export declare const generateFieldAssignment: (key: string[], value: string[], type: string) => {
|
|
6
|
+
assignment: string;
|
|
7
|
+
imports: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare const generateFieldAssignments: ({ index, input, }: {
|
|
3
10
|
index: number;
|
|
4
11
|
input: any;
|
|
5
|
-
}) =>
|
|
12
|
+
}) => {
|
|
13
|
+
assignments: string[];
|
|
14
|
+
imports: string[];
|
|
15
|
+
};
|
|
6
16
|
export declare const renameNameIfNeeded: (name: string) => string;
|
|
7
|
-
export declare const generateEventFieldAssignments: (event: any, _contractName: string) =>
|
|
17
|
+
export declare const generateEventFieldAssignments: (event: any, _contractName: string) => {
|
|
18
|
+
assignments: string[];
|
|
19
|
+
imports: string[];
|
|
20
|
+
};
|
|
8
21
|
export declare const generateEventIndexingHandlers: (events: any[], contractName: string) => string;
|
package/dist/scaffold/mapping.js
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import * as util from '../codegen/util.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
:
|
|
2
|
+
/**
|
|
3
|
+
* Map of value types that need to be changetype'd to their corresponding AssemblyScript type
|
|
4
|
+
*/
|
|
5
|
+
export const VALUE_TYPECAST_MAP = {
|
|
6
|
+
'address[]': 'Bytes[]',
|
|
7
|
+
'tuple[]': 'Bytes[]',
|
|
8
|
+
};
|
|
9
|
+
export const generateFieldAssignment = (key, value, type) => {
|
|
10
|
+
let rightSide = `event.params.${value.join('.')}`;
|
|
11
|
+
const imports = [];
|
|
12
|
+
if (type in VALUE_TYPECAST_MAP) {
|
|
13
|
+
const castTo = VALUE_TYPECAST_MAP[type];
|
|
14
|
+
rightSide = `changetype<${castTo}>(${rightSide})`;
|
|
15
|
+
imports.push(castTo.replace('[]', ''));
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
assignment: `entity.${key.join('_')} = ${rightSide}`,
|
|
19
|
+
imports,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export const generateFieldAssignments = ({ index, input, }) => {
|
|
23
|
+
const fields = input.type === 'tuple'
|
|
24
|
+
? util
|
|
25
|
+
.unrollTuple({ value: input, index, path: [input.name || `param${index}`] })
|
|
26
|
+
.map(({ path, type }) => generateFieldAssignment(path, path, type))
|
|
27
|
+
: [
|
|
28
|
+
generateFieldAssignment([(input.mappedName ?? input.name) || `param${index}`], [input.name || `param${index}`], input.type),
|
|
29
|
+
];
|
|
30
|
+
return {
|
|
31
|
+
assignments: fields.map(a => a.assignment),
|
|
32
|
+
imports: fields.map(a => a.imports).flat(),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
8
35
|
/**
|
|
9
36
|
* Map of input names that are reserved so we do not use them as field names to avoid conflicts
|
|
10
37
|
* see https://github.com/graphprotocol/graph-tooling/issues/710
|
|
@@ -19,17 +46,23 @@ export const renameNameIfNeeded = (name) => {
|
|
|
19
46
|
};
|
|
20
47
|
export const generateEventFieldAssignments = (event, _contractName) => event.inputs.reduce((acc, input, index) => {
|
|
21
48
|
input.mappedName = renameNameIfNeeded(input.name);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.map(event =>
|
|
49
|
+
const { assignments, imports } = generateFieldAssignments({ input, index });
|
|
50
|
+
return {
|
|
51
|
+
assignments: acc.assignments.concat(assignments),
|
|
52
|
+
imports: acc.imports.concat(imports),
|
|
53
|
+
};
|
|
54
|
+
}, { assignments: [], imports: [] });
|
|
55
|
+
export const generateEventIndexingHandlers = (events, contractName) => {
|
|
56
|
+
const eventFieldAssignments = events.map(event => ({
|
|
57
|
+
event,
|
|
58
|
+
...generateEventFieldAssignments(event, contractName),
|
|
59
|
+
}));
|
|
60
|
+
const allImports = [...new Set(eventFieldAssignments.map(({ imports }) => imports).flat())];
|
|
61
|
+
const eventHandlers = eventFieldAssignments.map(({ event, assignments }) => {
|
|
62
|
+
return `
|
|
30
63
|
export function handle${event._alias}(event: ${event._alias}Event): void {
|
|
31
64
|
let entity = new ${event._alias}(event.transaction.hash.concatI32(event.logIndex.toI32()))
|
|
32
|
-
${
|
|
65
|
+
${assignments.join('\n')}
|
|
33
66
|
|
|
34
67
|
entity.blockNumber = event.block.number
|
|
35
68
|
entity.blockTimestamp = event.block.timestamp
|
|
@@ -37,6 +70,15 @@ export const generateEventIndexingHandlers = (events, contractName) => `
|
|
|
37
70
|
|
|
38
71
|
entity.save()
|
|
39
72
|
}
|
|
40
|
-
|
|
41
|
-
|
|
73
|
+
`;
|
|
74
|
+
});
|
|
75
|
+
return `
|
|
76
|
+
import { ${events.map(event => `${event._alias} as ${event._alias}Event`)}} from '../generated/${contractName}/${contractName}'
|
|
77
|
+
import { ${events.map(event => event._alias)} } from '../generated/schema'
|
|
78
|
+
${allImports.length > 0
|
|
79
|
+
? `import { ${allImports.join(', ')} } from '@graphprotocol/graph-ts'`
|
|
80
|
+
: ''}
|
|
81
|
+
|
|
82
|
+
${eventHandlers.join('\n')}
|
|
42
83
|
`;
|
|
84
|
+
};
|
|
@@ -13,6 +13,6 @@ export declare const generateEventFields: ({ index, input, protocolName, }: {
|
|
|
13
13
|
index: number;
|
|
14
14
|
input: any;
|
|
15
15
|
protocolName: string;
|
|
16
|
-
}) =>
|
|
16
|
+
}) => string[];
|
|
17
17
|
export declare const generateEventType: (event: any, protocolName: string, contractName: string | undefined) => string;
|
|
18
18
|
export declare const generateExampleEntityType: (protocol: Protocol, events: any[]) => string;
|
package/dist/scaffold/tests.js
CHANGED
|
@@ -97,7 +97,13 @@ const isNativeType = (type) => {
|
|
|
97
97
|
const natives = [/i32/, /string/, /boolean/];
|
|
98
98
|
return natives.some(rx => rx.test(type));
|
|
99
99
|
};
|
|
100
|
-
|
|
100
|
+
// get inner type: Array<T> -> T, Array<Array<T>> -> T
|
|
101
|
+
const fetchArrayInnerType = (type) => {
|
|
102
|
+
const match = type.match(/Array<(.+)>/);
|
|
103
|
+
if (!match)
|
|
104
|
+
return null;
|
|
105
|
+
return fetchArrayInnerType(match[1]) || match;
|
|
106
|
+
};
|
|
101
107
|
// Generates the example test.ts file
|
|
102
108
|
const generateExampleTest = (contract, event, indexEvents, importTypes) => {
|
|
103
109
|
const entity = indexEvents ? String(event._alias) : 'ExampleEntity';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/constants.ts","../src/debug.ts","../src/fetch.ts","../src/index.ts","../src/migrations.ts","../src/schema.ts","../src/subgraph.ts","../src/type-generator.ts","../src/utils.ts","../src/version.ts","../src/watcher.ts","../src/codegen/schema.test.ts","../src/codegen/schema.ts","../src/codegen/template.ts","../src/codegen/typescript.ts","../src/codegen/util.test.ts","../src/codegen/util.ts","../src/codegen/types/conversions.ts","../src/codegen/types/index.test.ts","../src/codegen/types/index.ts","../src/command-helpers/auth.ts","../src/command-helpers/compiler.test.ts","../src/command-helpers/compiler.ts","../src/command-helpers/contracts.test.ts","../src/command-helpers/contracts.ts","../src/command-helpers/data-sources.ts","../src/command-helpers/file-resolver.test.ts","../src/command-helpers/file-resolver.ts","../src/command-helpers/fs.ts","../src/command-helpers/ipfs.ts","../src/command-helpers/jsonrpc.ts","../src/command-helpers/network.test.ts","../src/command-helpers/network.ts","../src/command-helpers/node.ts","../src/command-helpers/prompt-manager.ts","../src/command-helpers/
|
|
1
|
+
{"root":["../src/constants.ts","../src/debug.ts","../src/fetch.ts","../src/index.ts","../src/migrations.ts","../src/schema.ts","../src/subgraph.ts","../src/type-generator.ts","../src/utils.ts","../src/version.ts","../src/watcher.ts","../src/codegen/schema.test.ts","../src/codegen/schema.ts","../src/codegen/template.ts","../src/codegen/typescript.ts","../src/codegen/util.test.ts","../src/codegen/util.ts","../src/codegen/types/conversions.ts","../src/codegen/types/index.test.ts","../src/codegen/types/index.ts","../src/command-helpers/auth.ts","../src/command-helpers/compiler.test.ts","../src/command-helpers/compiler.ts","../src/command-helpers/contracts.test.ts","../src/command-helpers/contracts.ts","../src/command-helpers/data-sources.ts","../src/command-helpers/file-resolver.test.ts","../src/command-helpers/file-resolver.ts","../src/command-helpers/fs.ts","../src/command-helpers/ipfs.ts","../src/command-helpers/jsonrpc.ts","../src/command-helpers/network.test.ts","../src/command-helpers/network.ts","../src/command-helpers/node.ts","../src/command-helpers/prompt-manager.ts","../src/command-helpers/registry.ts","../src/command-helpers/retry.ts","../src/command-helpers/scaffold.ts","../src/command-helpers/sort.test.ts","../src/command-helpers/sort.ts","../src/command-helpers/spinner.ts","../src/command-helpers/subgraph.ts","../src/command-helpers/version.test.ts","../src/command-helpers/version.ts","../src/commands/add.ts","../src/commands/auth.ts","../src/commands/binary-install.d.ts","../src/commands/build.ts","../src/commands/clean.ts","../src/commands/codegen.ts","../src/commands/create.ts","../src/commands/deploy.ts","../src/commands/init.ts","../src/commands/local.ts","../src/commands/publish.ts","../src/commands/remove.ts","../src/commands/test.ts","../src/compiler/asc.ts","../src/compiler/index.ts","../src/migrations/mapping_api_version_0_0_1.ts","../src/migrations/mapping_api_version_0_0_2.ts","../src/migrations/mapping_api_version_0_0_3.ts","../src/migrations/mapping_api_version_0_0_4.ts","../src/migrations/mapping_api_version_0_0_5.ts","../src/migrations/spec_version_0_0_2.ts","../src/migrations/spec_version_0_0_3.ts","../src/migrations/util/load-manifest.ts","../src/migrations/util/versions.ts","../src/protocols/contract.ts","../src/protocols/file_template.ts","../src/protocols/index.ts","../src/protocols/subgraph.ts","../src/protocols/arweave/subgraph.ts","../src/protocols/arweave/scaffold/manifest.ts","../src/protocols/arweave/scaffold/mapping.ts","../src/protocols/cosmos/subgraph.ts","../src/protocols/cosmos/scaffold/manifest.ts","../src/protocols/cosmos/scaffold/mapping.ts","../src/protocols/ethereum/abi.ts","../src/protocols/ethereum/contract.ts","../src/protocols/ethereum/subgraph.ts","../src/protocols/ethereum/type-generator.ts","../src/protocols/ethereum/codegen/abi.test.ts","../src/protocols/ethereum/codegen/abi.ts","../src/protocols/ethereum/codegen/template.ts","../src/protocols/ethereum/scaffold/manifest.ts","../src/protocols/ethereum/scaffold/mapping.ts","../src/protocols/near/contract.ts","../src/protocols/near/subgraph.ts","../src/protocols/near/scaffold/manifest.ts","../src/protocols/near/scaffold/mapping.ts","../src/protocols/subgraph/subgraph.ts","../src/protocols/subgraph/scaffold/manifest.ts","../src/protocols/subgraph/scaffold/mapping.ts","../src/protocols/substreams/subgraph.ts","../src/protocols/substreams/scaffold/manifest.ts","../src/scaffold/cosmos.test.ts","../src/scaffold/ethereum.test.ts","../src/scaffold/get-docker-file.ts","../src/scaffold/get-git-ignore.ts","../src/scaffold/index.ts","../src/scaffold/mapping.ts","../src/scaffold/near.test.ts","../src/scaffold/schema.ts","../src/scaffold/tests.ts","../src/validation/contract.ts","../src/validation/index.ts","../src/validation/manifest.ts","../src/validation/schema.test.ts","../src/validation/schema.ts"],"version":"5.7.2"}
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphprotocol/graph-cli",
|
|
3
|
-
"version": "0.94.0
|
|
3
|
+
"version": "0.94.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for building for and deploying to The Graph",
|
|
6
6
|
"license": "(Apache-2.0 OR MIT)",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import EthereumABI from '../protocols/ethereum/abi.js';
|
|
2
|
-
import { ContractService } from './contracts.js';
|
|
3
|
-
export interface CheckForProxyResult {
|
|
4
|
-
implementationAbi: EthereumABI | null;
|
|
5
|
-
implementationAddress: string | null;
|
|
6
|
-
}
|
|
7
|
-
export declare function checkForProxy(contractService: ContractService, network: string, address: string, abi: EthereumABI): Promise<CheckForProxyResult>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { prompt } from 'gluegun';
|
|
2
|
-
import EthereumABI from '../protocols/ethereum/abi.js';
|
|
3
|
-
import { retryWithPrompt } from './retry.js';
|
|
4
|
-
import { withSpinner } from './spinner.js';
|
|
5
|
-
export async function checkForProxy(contractService, network, address, abi) {
|
|
6
|
-
let implementationAddress = null;
|
|
7
|
-
let implementationAbi = null;
|
|
8
|
-
const maybeProxy = abi.callFunctionSignatures()?.includes('upgradeTo(address)');
|
|
9
|
-
if (maybeProxy) {
|
|
10
|
-
const impl = await retryWithPrompt(() => withSpinner('Fetching proxy implementation address...', 'Failed to fetch proxy implementation address', 'Warning fetching proxy implementation address', () => contractService.getProxyImplementation(network, address)));
|
|
11
|
-
if (impl) {
|
|
12
|
-
const useImplementation = await prompt
|
|
13
|
-
.confirm(`Proxy contract detected. Use implementation contract ABI at ${impl}?`, true)
|
|
14
|
-
.catch(() => false);
|
|
15
|
-
if (useImplementation) {
|
|
16
|
-
implementationAddress = impl;
|
|
17
|
-
implementationAbi = await retryWithPrompt(() => withSpinner('Fetching implementation contract ABI...', 'Failed to fetch implementation ABI', 'Warning fetching implementation ABI', () => contractService.getABI(EthereumABI, network, implementationAddress)));
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
implementationAbi,
|
|
23
|
-
implementationAddress,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { prompt } from 'gluegun';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import EthereumABI from '../protocols/ethereum/abi.js';
|
|
4
|
-
import { ContractService } from './contracts.js';
|
|
5
|
-
import { checkForProxy } from './proxy.js';
|
|
6
|
-
import { loadRegistry } from './registry.js';
|
|
7
|
-
// Mock gluegun's prompt
|
|
8
|
-
vi.mock('gluegun', async () => {
|
|
9
|
-
const actual = await vi.importActual('gluegun');
|
|
10
|
-
return {
|
|
11
|
-
...actual,
|
|
12
|
-
prompt: {
|
|
13
|
-
confirm: vi.fn().mockResolvedValue(true),
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
});
|
|
17
|
-
describe('Proxy detection', async () => {
|
|
18
|
-
const NETWORK = 'mainnet';
|
|
19
|
-
const registry = await loadRegistry();
|
|
20
|
-
const contractService = new ContractService(registry);
|
|
21
|
-
const testCases = [
|
|
22
|
-
{
|
|
23
|
-
name: 'USDC',
|
|
24
|
-
type: 'EIP-1967 Upgradeable',
|
|
25
|
-
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
26
|
-
implementationAddress: '0x43506849d7c04f9138d1a2050bbf3a0c054402dd',
|
|
27
|
-
expectedFunctions: ['mint(address,uint256)', 'configureMinter(address,uint256)'],
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'BUSD',
|
|
31
|
-
type: 'OpenZeppelin Unstructured Storage',
|
|
32
|
-
address: '0x4Fabb145d64652a948d72533023f6E7A623C7C53',
|
|
33
|
-
implementationAddress: '0x2A3F1A37C04F82aA274f5353834B2d002Db91015',
|
|
34
|
-
expectedFunctions: ['reclaimBUSD()', 'claimOwnership()'],
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'Gelato',
|
|
38
|
-
type: 'EIP-2535 Diamond Pattern (not supported)',
|
|
39
|
-
address: '0x3caca7b48d0573d793d3b0279b5f0029180e83b6',
|
|
40
|
-
implementationAddress: null,
|
|
41
|
-
expectedFunctions: [],
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
for (const testCase of testCases) {
|
|
45
|
-
it(`should handle ${testCase.name} ${testCase.type} Proxy`, async () => {
|
|
46
|
-
const abi = await contractService.getABI(EthereumABI, NETWORK, testCase.address);
|
|
47
|
-
expect(abi).toBeDefined();
|
|
48
|
-
const { implementationAddress, implementationAbi } = await checkForProxy(contractService, NETWORK, testCase.address, abi);
|
|
49
|
-
expect(implementationAddress === testCase.implementationAddress);
|
|
50
|
-
const implFunctions = implementationAbi?.callFunctionSignatures();
|
|
51
|
-
for (const expectedFunction of testCase.expectedFunctions) {
|
|
52
|
-
expect(implFunctions).toContain(expectedFunction);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
it('should handle when user declines to use implementation', async () => {
|
|
57
|
-
vi.mocked(prompt.confirm).mockResolvedValueOnce(false);
|
|
58
|
-
const abi = await contractService.getABI(EthereumABI, NETWORK, testCases[0].address);
|
|
59
|
-
expect(abi).toBeDefined();
|
|
60
|
-
const { implementationAddress, implementationAbi } = await checkForProxy(contractService, NETWORK, testCases[0].address, abi);
|
|
61
|
-
expect(implementationAddress).toBeNull();
|
|
62
|
-
expect(implementationAbi).toBeNull();
|
|
63
|
-
});
|
|
64
|
-
});
|