Skip to content

Commit 9f29a73

Browse files
Move Etherscan utils to lib-sourcify (#2297)
* move etherscan utils from server to lib-sourcify * enhance error handling for Etherscan and update related tests * move stringToBase64 utility function from etherscan-util to etherscan.session.handlers * refactor: reorganize Etherscan utilities and error handling for improved clarity and maintainability * streamline Etherscan error handling by removing unnecessary parameters and enhancing error message generation * simplify Etherscan error handling by removing URL parameters and enhancing error messages * update dependencies and enhance test setup with chai-http * test failing tests on circleci * fix linter * try disabling parallel mode on circleci * disable concurrency in circleci test * test fix circleci tests * enhance error handling in fetchFromEtherscan and update test to focus on this function * fix circleci tests by forcing the mocked api key to '' * allow empty userApiKey in mockEtherscanApi * Use etherscanResponseMock from server in lib-sourcify's tests * remove AGENTS.md file * small fixes * add documentation and refactor code in server * Rename `url` to `maskedUrl` following 0a417e6 * fix PR comments * improve types in etherscan-util.ts * fix conflicts on package-lock.json --------- Co-authored-by: Kaan Uzdoğan <[email protected]>
1 parent 1b3c083 commit 9f29a73

25 files changed

+1213
-1015
lines changed

.circleci/continue_config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ commands:
1313
gcp-oidc-generate-cred-config-file:
1414
description: "Authenticate with GCP using a CircleCI OIDC token."
1515
parameters:
16-
gcp_cred_config_file_path:
16+
gcp_cred_config_file_path:
1717
type: string
1818
default: /home/circleci/gcp_cred_config.json
19-
oidc_token_file_path:
19+
oidc_token_file_path:
2020
type: string
2121
default: /home/circleci/oidc_token.json
2222
steps:
@@ -35,7 +35,7 @@ commands:
3535
gcp-oidc-authenticate:
3636
description: "Authenticate with GCP using a GCP credentials file."
3737
parameters:
38-
gcp_cred_config_file_path:
38+
gcp_cred_config_file_path:
3939
type: string
4040
default: /home/circleci/gcp_cred_config.json
4141
steps:
@@ -248,7 +248,7 @@ jobs:
248248
- gcp-cli/install
249249
- gcp-oidc-generate-cred-config-file
250250
- gcp-oidc-authenticate
251-
- run:
251+
- run:
252252
name: Deploy to GCP
253253
no_output_timeout: 10m
254254
command: |

.circleci/new_branch.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ parameters:
77
run-validate-database-schema:
88
type: boolean
99
default: false
10-
10+
1111
workflows:
1212
# Always run
1313
node-build:
@@ -139,7 +139,7 @@ jobs:
139139
npm run migrate:up
140140
# Force dump in case problems were ignored
141141
npx dbmate dump
142-
142+
143143
# Check if the schema file has changed
144144
if git diff --exit-code sourcify-database.sql; then
145145
echo "✅ Schema validation passed - sourcify-database.sql is in sync with migrations"
@@ -175,4 +175,4 @@ jobs:
175175
done;
176176
177177
# Check if migrations can be run again
178-
npm run migrate:up
178+
npm run migrate:up

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/lib-sourcify/README.md

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ import {
2626
ISolidityCompiler,
2727
SolidityJsonInput,
2828
SolidityOutput,
29-
} from "@ethereum-sourcify/lib-sourcify";
30-
import { useSolidityCompiler } from "@ethereum-sourcify/compilers";
31-
import * as fs from "fs";
29+
} from '@ethereum-sourcify/lib-sourcify';
30+
import { useSolidityCompiler } from '@ethereum-sourcify/compilers';
31+
import * as fs from 'fs';
3232

3333
// Step 1: Setup your compiler
3434
class Solc implements ISolidityCompiler {
@@ -43,26 +43,26 @@ class Solc implements ISolidityCompiler {
4343
async compile(
4444
version: string,
4545
solcJsonInput: SolidityJsonInput,
46-
forceEmscripten: boolean = false
46+
forceEmscripten: boolean = false,
4747
): Promise<SolidityOutput> {
4848
return await useSolidityCompiler(
4949
this.solcRepoPath, // useSolidityCompiler will automatically download and store solc here
5050
this.solJsonRepoPath, // useSolidityCompiler will automatically download and store solcjs here
5151
version,
5252
solcJsonInput,
53-
forceEmscripten
53+
forceEmscripten,
5454
);
5555
}
5656
}
5757

58-
const solc = new Solc("/path/to/solc", "/path/to/solcjs");
58+
const solc = new Solc('/path/to/solc', '/path/to/solcjs');
5959

6060
// Step 2: Prepare your standard JSON input
6161
const jsonInput = {
62-
language: "Solidity",
62+
language: 'Solidity',
6363
sources: {
64-
"Contract.sol": {
65-
content: "contract MyContract { function foo() public {} }",
64+
'Contract.sol': {
65+
content: 'contract MyContract { function foo() public {} }',
6666
},
6767
},
6868
settings: {
@@ -71,35 +71,35 @@ const jsonInput = {
7171
runs: 200,
7272
},
7373
outputSelection: {
74-
"*": [],
74+
'*': [],
7575
},
7676
},
7777
} as SolidityJsonInput;
7878

7979
// Step 3: Create a compilation
8080
const compilation = new SolidityCompilation(
8181
solc,
82-
"0.8.20", // compiler version
82+
'0.8.20', // compiler version
8383
jsonInput,
8484
{
85-
path: "Contract.sol",
86-
name: "MyContract", // The name of your contract
87-
}
85+
path: 'Contract.sol',
86+
name: 'MyContract', // The name of your contract
87+
},
8888
);
8989

9090
// Step 4: Set up a SourcifyChain instance
9191
const myChain = new SourcifyChain({
92-
name: "My EVM Chain",
92+
name: 'My EVM Chain',
9393
chainId: 1337,
94-
rpc: ["http://localhost:8545"],
94+
rpc: ['http://localhost:8545'],
9595
supported: true,
9696
});
9797

9898
// Step 5: Verify the contract
9999
const verification = new Verification(
100100
compilation,
101101
myChain,
102-
"0xc0ffee254729296a45a3885639AC7E10F9d54979"
102+
'0xc0ffee254729296a45a3885639AC7E10F9d54979',
103103
);
104104

105105
await verification.verify();
@@ -115,13 +115,16 @@ This example shows the complete verification flow for a Solidity contract using
115115
For browser usage, we recommend using [web-solc](https://github.com/gnidan/web-solc) instead of `@ethereum-sourcify/compilers`. Here's an example of how to implement the `ISolidityCompiler` interface with `web-solc`:
116116

117117
```typescript
118-
import { ISolidityCompiler, SolidityJsonInput } from "@ethereum-sourcify/lib-sourcify";
119-
import { fetchSolc } from "web-solc";
118+
import {
119+
ISolidityCompiler,
120+
SolidityJsonInput,
121+
} from '@ethereum-sourcify/lib-sourcify';
122+
import { fetchSolc } from 'web-solc';
120123

121124
class Solc implements ISolidityCompiler {
122125
async compile(
123126
version: string,
124-
solcJsonInput: SolidityJsonInput
127+
solcJsonInput: SolidityJsonInput,
125128
): Promise<any> {
126129
const { compile } = await fetchSolc(version);
127130
return await compile(solcJsonInput);
@@ -136,12 +139,10 @@ const solc = new Solc();
136139
lib-sourcify v2 consists of several key components:
137140

138141
- **Validation**: Based on a Solidity metadata.json file that describes a contract build, checks if all source files are present and valid. Fetches missing sources from IPFS if necessary.
139-
140142
- `SolidityMetadataContract`: Represents a Solidity contract with its metadata and source files
141143
- `processFiles.ts`: Utility functions for creating `SolidityMetadataContract` instances from files
142144

143145
- **Compilation**: Handles contract compilation
144-
145146
- `AbstractCompilation`: Base class for compilation
146147
- `SolidityCompilation`: Handles Solidity compilation
147148
- `VyperCompilation`: Handles Vyper compilation
@@ -177,7 +178,7 @@ import { SolidityMetadataContract } from '@ethereum-sourcify/lib-sourcify';
177178
// Set a custom IPFS gateway
178179
SolidityMetadataContract.setGlobalIpfsGateway({
179180
url: 'https://my-ipfs-gateway.com/ipfs/',
180-
headers: { 'Authorization': 'Bearer my-token' }
181+
headers: { Authorization: 'Bearer my-token' },
181182
});
182183

183184
// Get the current IPFS gateway configuration
@@ -189,7 +190,11 @@ const gateway = SolidityMetadataContract.getGlobalIpfsGateway();
189190
You can set the global log level and provide a custom logger:
190191

191192
```typescript
192-
import { setLibSourcifyLoggerLevel, setLibSourcifyLogger, getLibSourcifyLoggerLevel } from '@ethereum-sourcify/lib-sourcify';
193+
import {
194+
setLibSourcifyLoggerLevel,
195+
setLibSourcifyLogger,
196+
getLibSourcifyLoggerLevel,
197+
} from '@ethereum-sourcify/lib-sourcify';
193198

194199
// Set the log level
195200
setLibSourcifyLoggerLevel(5); // 0=errors, 1=warnings, 2=info, 5=debug, 6=silly
@@ -205,7 +210,7 @@ setLibSourcifyLogger({
205210
},
206211
log(level, msg) {
207212
// Custom logging implementation
208-
}
213+
},
209214
});
210215
```
211216

@@ -222,28 +227,31 @@ import {
222227
SolidityOutput,
223228
ISolidityCompiler,
224229
SolidityJsonInput,
225-
} from "@ethereum-sourcify/lib-sourcify";
226-
import { useSolidityCompiler } from "@ethereum-sourcify/compilers";
230+
} from '@ethereum-sourcify/lib-sourcify';
231+
import { useSolidityCompiler } from '@ethereum-sourcify/compilers';
227232

228233
class Solc implements ISolidityCompiler {
229-
constructor(private solcRepoPath: string, private solJsonRepoPath: string) {}
234+
constructor(
235+
private solcRepoPath: string,
236+
private solJsonRepoPath: string,
237+
) {}
230238

231239
async compile(
232240
version: string,
233241
solcJsonInput: SolidityJsonInput,
234-
forceEmscripten: boolean = false
242+
forceEmscripten: boolean = false,
235243
): Promise<SolidityOutput> {
236244
return await useSolidityCompiler(
237245
this.solcRepoPath,
238246
this.solJsonRepoPath,
239247
version,
240248
solcJsonInput,
241-
forceEmscripten
249+
forceEmscripten,
242250
);
243251
}
244252
}
245253

246-
const solc = new Solc("/path/to/solc/repo", "/path/to/solcjs/repo");
254+
const solc = new Solc('/path/to/solc/repo', '/path/to/solcjs/repo');
247255
```
248256

249257
### Vyper Compiler Example
@@ -359,7 +367,10 @@ const compilation = await metadataContract.createCompilation(solidityCompiler);
359367
For file-based validation:
360368

361369
```typescript
362-
import { createMetadataContractsFromFiles, PathBuffer } from '@ethereum-sourcify/lib-sourcify';
370+
import {
371+
createMetadataContractsFromFiles,
372+
PathBuffer,
373+
} from '@ethereum-sourcify/lib-sourcify';
363374

364375
const pathBuffers: PathBuffer[] = [];
365376
pathBuffers.push({
@@ -488,14 +499,45 @@ setLibSourcifyLogger({
488499
});
489500
```
490501

502+
## EtherscanUtils
503+
504+
EtherscanUtils provides utilities for importing and processing verified contracts from Etherscan APIs.
505+
506+
### Fetching Contract Data from Etherscan
507+
508+
```typescript
509+
import { EtherscanUtils } from '@ethereum-sourcify/lib-sourcify';
510+
511+
// Fetch contract source code and metadata from Etherscan
512+
const etherscanResult = await EtherscanUtils.fetchFromEtherscan(
513+
1, // chainId
514+
'0x6B175474E89094C44Da98b954EedeAC495271d0F', // contract address
515+
'YOUR_API_KEY', // API key
516+
);
517+
```
518+
519+
### Creating Compilations from Etherscan Results
520+
521+
```typescript
522+
// Create a compilation object directly from Etherscan data
523+
const compilation = await EtherscanUtils.getCompilationFromEtherscanResult(
524+
etherscanResult,
525+
solidityCompiler, // ISolidityCompiler instance
526+
vyperCompiler, // IVyperCompiler instance (for Vyper contracts)
527+
);
528+
529+
// Use the compilation for verification
530+
const verification = new Verification(compilation, chain, contractAddress);
531+
await verification.verify();
532+
```
533+
491534
## Migration Guide from v1 to v2
492535

493536
Version 2 of lib-sourcify brings a significant redesign of the library's architecture. Here's how to migrate from v1 to v2:
494537

495538
### Key Changes
496539

497540
1. **Class Structure Changes**:
498-
499541
- `AbstractCheckedContract` is replaced by language-specific compilation classes
500542
- `SolidityCheckedContract` functionality is now split between `SolidityMetadataContract` and `SolidityCompilation`
501543
- `VyperCheckedContract` is now represented by `VyperCompilation`

packages/lib-sourcify/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@ethersproject/bignumber": "5.8.0",
4545
"@ethersproject/bytes": "5.8.0",
4646
"@fairdatasociety/bmt-js": "2.1.0",
47+
"@solidity-parser/parser": "0.20.2",
4748
"bs58": "5.0.0",
4849
"ethers": "6.15.0",
4950
"jszip": "3.10.1",
@@ -62,6 +63,7 @@
6263
"c8": "10.1.3",
6364
"chai": "4.5.0",
6465
"chai-as-promised": "7.1.2",
66+
"chai-http": "4.4.0",
6567
"cspell": "8.19.4",
6668
"cz-conventional-changelog": "3.3.0",
6769
"gh-pages": "6.3.0",

packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
getVariationsByContentHash,
3131
} from './variationsUtils';
3232
import { logDebug } from '../logger';
33-
import { splitFullyQualifiedName } from '../utils';
33+
import { splitFullyQualifiedName } from '../utils/utils';
3434

3535
export class SolidityMetadataContract {
3636
metadata: Metadata;

packages/lib-sourcify/src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ export * from './Compilation/CompilationTypes';
1616
export * from './Verification/Verification';
1717
export * from './Verification/VerificationTypes';
1818

19+
// Etherscan utils exports
20+
export * from './utils/etherscan/EtherscanTypes';
21+
import * as etherscanUtils from './utils/etherscan/etherscan-util';
22+
export const EtherscanUtils = {
23+
...etherscanUtils,
24+
};
25+
1926
// Validation exports
2027
export * from './Validation/SolidityMetadataContract';
2128
export * from './Validation/ValidationTypes';
@@ -32,7 +39,7 @@ export * from './SourcifyChain/SourcifyChainTypes';
3239
export * from './SourcifyLibError';
3340

3441
// Utils exports
35-
export * from './utils';
42+
export * from './utils/utils';
3643

3744
// Export all compilers types
3845
export * from '@ethereum-sourcify/compilers-types';

0 commit comments

Comments
 (0)