@0xsequence/catapult 1.3.17 → 1.5.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/README.md +276 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +12 -0
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/provenance.d.ts +3 -0
- package/dist/commands/provenance.d.ts.map +1 -0
- package/dist/commands/provenance.js +138 -0
- package/dist/commands/provenance.js.map +1 -0
- package/dist/lib/__tests__/deployer.spec.js +118 -1
- package/dist/lib/__tests__/deployer.spec.js.map +1 -1
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/__tests__/provenance.spec.d.ts +2 -0
- package/dist/lib/__tests__/provenance.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/provenance.spec.js +205 -0
- package/dist/lib/__tests__/provenance.spec.js.map +1 -0
- package/dist/lib/contracts/__tests__/repository.spec.js +243 -0
- package/dist/lib/contracts/__tests__/repository.spec.js.map +1 -1
- package/dist/lib/contracts/repository.d.ts +9 -1
- package/dist/lib/contracts/repository.d.ts.map +1 -1
- package/dist/lib/contracts/repository.js +93 -7
- package/dist/lib/contracts/repository.js.map +1 -1
- package/dist/lib/core/__tests__/assert-action.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/assert-action.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/assert-action.spec.js +377 -0
- package/dist/lib/core/__tests__/assert-action.spec.js.map +1 -0
- package/dist/lib/core/__tests__/engine.spec.js +80 -0
- package/dist/lib/core/__tests__/engine.spec.js.map +1 -1
- package/dist/lib/core/__tests__/loader.spec.js +29 -0
- package/dist/lib/core/__tests__/loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +405 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.js +40 -0
- package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +3 -2
- package/dist/lib/core/context.d.ts.map +1 -1
- package/dist/lib/core/context.js +3 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/engine.d.ts +4 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +206 -0
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/loader.d.ts +1 -0
- package/dist/lib/core/loader.d.ts.map +1 -1
- package/dist/lib/core/loader.js +6 -1
- package/dist/lib/core/loader.js.map +1 -1
- package/dist/lib/core/resolver.d.ts +2 -0
- package/dist/lib/core/resolver.d.ts.map +1 -1
- package/dist/lib/core/resolver.js +89 -0
- package/dist/lib/core/resolver.js.map +1 -1
- package/dist/lib/core/signer.d.ts +7 -0
- package/dist/lib/core/signer.d.ts.map +1 -0
- package/dist/lib/core/signer.js +60 -0
- package/dist/lib/core/signer.js.map +1 -0
- package/dist/lib/deployer.d.ts.map +1 -1
- package/dist/lib/deployer.js +21 -4
- package/dist/lib/deployer.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/parsers/__tests__/job.spec.js +77 -0
- package/dist/lib/parsers/__tests__/job.spec.js.map +1 -1
- package/dist/lib/parsers/__tests__/source.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/source.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/source.spec.js +158 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -0
- package/dist/lib/parsers/index.d.ts +1 -0
- package/dist/lib/parsers/index.d.ts.map +1 -1
- package/dist/lib/parsers/index.js +1 -0
- package/dist/lib/parsers/index.js.map +1 -1
- package/dist/lib/parsers/job.d.ts.map +1 -1
- package/dist/lib/parsers/job.js +11 -0
- package/dist/lib/parsers/job.js.map +1 -1
- package/dist/lib/parsers/source.d.ts +4 -0
- package/dist/lib/parsers/source.d.ts.map +1 -0
- package/dist/lib/parsers/source.js +107 -0
- package/dist/lib/parsers/source.js.map +1 -0
- package/dist/lib/provenance.d.ts +34 -0
- package/dist/lib/provenance.d.ts.map +1 -0
- package/dist/lib/provenance.js +694 -0
- package/dist/lib/provenance.js.map +1 -0
- package/dist/lib/types/actions.d.ts +42 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +4 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/contracts.d.ts +3 -0
- package/dist/lib/types/contracts.d.ts.map +1 -1
- package/dist/lib/types/definitions.d.ts +1 -0
- package/dist/lib/types/definitions.d.ts.map +1 -1
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/source.d.ts +26 -0
- package/dist/lib/types/source.d.ts.map +1 -0
- package/dist/lib/types/source.js +3 -0
- package/dist/lib/types/source.js.map +1 -0
- package/dist/lib/types/values.d.ts +33 -1
- package/dist/lib/types/values.d.ts.map +1 -1
- package/package.json +4 -1
- package/.eslintrc.json +0 -29
- package/.github/workflows/ci.yml +0 -181
- package/CONCEPT.md +0 -24
- package/contracts/checked-call.huff +0 -65
- package/eslint.config.js +0 -48
- package/examples/jobs/guards-v1.yaml +0 -17
- package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
- package/examples/jobs/sequence-v1.yaml +0 -59
- package/examples/templates/sequence-factory-v1.yaml +0 -56
- package/jest.config.js +0 -25
- package/src/cli.ts +0 -17
- package/src/commands/common.ts +0 -61
- package/src/commands/dry.ts +0 -209
- package/src/commands/etherscan.ts +0 -360
- package/src/commands/index.ts +0 -5
- package/src/commands/list.ts +0 -249
- package/src/commands/run.ts +0 -146
- package/src/commands/utils.ts +0 -215
- package/src/index.ts +0 -67
- package/src/lib/__tests__/deployer-events.spec.ts +0 -338
- package/src/lib/__tests__/deployer.spec.ts +0 -2093
- package/src/lib/__tests__/network-loader.spec.ts +0 -150
- package/src/lib/__tests__/network-selection.spec.ts +0 -41
- package/src/lib/__tests__/network-utils.spec.ts +0 -230
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
- package/src/lib/contracts/__tests__/repository.spec.ts +0 -344
- package/src/lib/contracts/repository.ts +0 -313
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -1889
- package/src/lib/core/__tests__/graph.spec.ts +0 -125
- package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
- package/src/lib/core/__tests__/loader.spec.ts +0 -334
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2053
- package/src/lib/core/__tests__/static-action.spec.ts +0 -172
- package/src/lib/core/context.ts +0 -127
- package/src/lib/core/engine.ts +0 -1782
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -247
- package/src/lib/core/resolver.ts +0 -757
- package/src/lib/deployer.ts +0 -981
- package/src/lib/events/__tests__/event-system.spec.ts +0 -392
- package/src/lib/events/cli-adapter.ts +0 -369
- package/src/lib/events/emitter.ts +0 -62
- package/src/lib/events/index.ts +0 -3
- package/src/lib/events/types.ts +0 -520
- package/src/lib/index.ts +0 -14
- package/src/lib/network-loader.ts +0 -90
- package/src/lib/network-selection.ts +0 -73
- package/src/lib/network-utils.ts +0 -64
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
- package/src/lib/parsers/__tests__/job.spec.ts +0 -358
- package/src/lib/parsers/__tests__/template.spec.ts +0 -111
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
- package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
- package/src/lib/parsers/artifact/index.ts +0 -27
- package/src/lib/parsers/artifact/types.ts +0 -9
- package/src/lib/parsers/buildinfo.ts +0 -127
- package/src/lib/parsers/constants.ts +0 -56
- package/src/lib/parsers/index.ts +0 -5
- package/src/lib/parsers/job.ts +0 -148
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
- package/src/lib/std/templates/assured-deployment.yaml +0 -46
- package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
- package/src/lib/std/templates/erc-2470.yaml +0 -70
- package/src/lib/std/templates/min-balance.yaml +0 -35
- package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
- package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
- package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
- package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
- package/src/lib/types/actions.ts +0 -127
- package/src/lib/types/artifacts.ts +0 -21
- package/src/lib/types/buildinfo.ts +0 -116
- package/src/lib/types/conditions.ts +0 -50
- package/src/lib/types/contracts.ts +0 -23
- package/src/lib/types/definitions.ts +0 -70
- package/src/lib/types/index.ts +0 -8
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -150
- package/src/lib/utils/assertion.ts +0 -24
- package/src/lib/utils/validation.ts +0 -116
- package/src/lib/validation/contract-references.ts +0 -210
- package/src/lib/validation/index.ts +0 -1
- package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
- package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
- package/src/lib/verification/etherscan.ts +0 -547
- package/src/lib/verification/sourcify.ts +0 -248
- package/test_validation/artifacts/TestContract.json +0 -9
- package/test_validation/jobs/test-missing.yaml +0 -16
- package/test_validation/networks.yaml +0 -3
- package/tsconfig.json +0 -36
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
2
|
-
import chalk from 'chalk'
|
|
3
|
-
import { loadNetworks } from '../lib/network-loader'
|
|
4
|
-
import { resolveSingleChainId } from '../lib/network-selection'
|
|
5
|
-
import { projectOption, verbosityOption } from './common'
|
|
6
|
-
import { setVerbosity } from '../index'
|
|
7
|
-
import * as solc from 'solc'
|
|
8
|
-
import { createHash } from 'crypto'
|
|
9
|
-
|
|
10
|
-
type ApiAction = 'getsourcecode' | 'getabi'
|
|
11
|
-
|
|
12
|
-
interface EtherscanCmdBase {
|
|
13
|
-
project: string
|
|
14
|
-
verbose: number
|
|
15
|
-
etherscanApiKey?: string
|
|
16
|
-
network?: string
|
|
17
|
-
address: string
|
|
18
|
-
raw?: boolean
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getEtherscanApiUrl(chainId: number): string {
|
|
22
|
-
return `https://api.etherscan.io/v2/api?chainid=${chainId}`
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type EtherscanSourceEnvelope = {
|
|
26
|
-
rawResult: Record<string, unknown>
|
|
27
|
-
parsedSource: unknown // standard-json object or flattened string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function fetchFromEtherscan(
|
|
31
|
-
chainId: number,
|
|
32
|
-
apiKey: string,
|
|
33
|
-
address: string,
|
|
34
|
-
action: ApiAction
|
|
35
|
-
): Promise<unknown | EtherscanSourceEnvelope> {
|
|
36
|
-
const apiUrl = getEtherscanApiUrl(chainId)
|
|
37
|
-
const params = new URLSearchParams({
|
|
38
|
-
module: 'contract',
|
|
39
|
-
action,
|
|
40
|
-
apikey: apiKey,
|
|
41
|
-
address
|
|
42
|
-
})
|
|
43
|
-
const resp = await fetch(`${apiUrl}&${params.toString()}`, {
|
|
44
|
-
method: 'GET',
|
|
45
|
-
signal: AbortSignal.timeout(20000)
|
|
46
|
-
})
|
|
47
|
-
if (!resp.ok) {
|
|
48
|
-
throw new Error(`HTTP ${resp.status}: ${resp.statusText}`)
|
|
49
|
-
}
|
|
50
|
-
const data = await resp.json() as {
|
|
51
|
-
status: string
|
|
52
|
-
message?: string
|
|
53
|
-
result:
|
|
54
|
-
| string
|
|
55
|
-
| Array<{
|
|
56
|
-
SourceCode?: string
|
|
57
|
-
ABI?: string
|
|
58
|
-
[key: string]: unknown
|
|
59
|
-
}>
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (data.status !== '1') {
|
|
63
|
-
// Etherscan v2 returns status "0" with message in result
|
|
64
|
-
const msg = typeof data.result === 'string' ? data.result : JSON.stringify(data.result)
|
|
65
|
-
throw new Error(msg || 'Unknown Etherscan error')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (action === 'getabi') {
|
|
69
|
-
// data.result is a JSON-encoded string for ABI, parse and return as object
|
|
70
|
-
if (typeof data.result !== 'string') {
|
|
71
|
-
throw new Error('Unexpected ABI result format from Etherscan')
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
return JSON.parse(data.result as string)
|
|
75
|
-
} catch (_e) {
|
|
76
|
-
throw new Error('Failed to parse ABI JSON returned by Etherscan')
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (action === 'getsourcecode') {
|
|
81
|
-
// data.result[0].SourceCode is a string; when standard-json it is wrapped with a leading and trailing character
|
|
82
|
-
if (!Array.isArray(data.result) || data.result.length === 0) {
|
|
83
|
-
throw new Error('Empty result from Etherscan')
|
|
84
|
-
}
|
|
85
|
-
const first = (data.result as Array<{ SourceCode?: string }>)[0] as Record<string, unknown>
|
|
86
|
-
const sourceCodeRaw = first?.SourceCode as string
|
|
87
|
-
if (typeof sourceCodeRaw !== 'string' || sourceCodeRaw.length === 0) {
|
|
88
|
-
throw new Error('No SourceCode found on Etherscan')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// The SourceCode may be:
|
|
92
|
-
// 1) a raw source (flattened) string
|
|
93
|
-
// 2) a JSON string that may be double-wrapped like: "{...}" or "{{...}}"
|
|
94
|
-
// Try to normalize to a parsed JSON object when possible, otherwise return the raw string.
|
|
95
|
-
const trimmed = sourceCodeRaw.trim()
|
|
96
|
-
// Heuristic: if it starts with '{{' and ends with '}}', strip one layer
|
|
97
|
-
const cleaned = trimmed.startsWith('{{') && trimmed.endsWith('}}')
|
|
98
|
-
? trimmed.slice(1, -1)
|
|
99
|
-
: trimmed
|
|
100
|
-
|
|
101
|
-
// Try to parse JSON; if it fails, return string
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(cleaned)
|
|
104
|
-
return { rawResult: first, parsedSource: parsed }
|
|
105
|
-
} catch {
|
|
106
|
-
// Not JSON, return as-is string
|
|
107
|
-
return { rawResult: first, parsedSource: sourceCodeRaw }
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return data.result
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function makeEtherscanCommand(): Command {
|
|
115
|
-
const etherscan = new Command('etherscan')
|
|
116
|
-
.description('Etherscan helper commands (ABI/source fetch)')
|
|
117
|
-
|
|
118
|
-
// Common options builder
|
|
119
|
-
const withCommon = (cmd: Command) => {
|
|
120
|
-
projectOption(cmd)
|
|
121
|
-
verbosityOption(cmd)
|
|
122
|
-
cmd
|
|
123
|
-
.option('--etherscan-api-key <key>', 'Etherscan API key. Can also be set via ETHERSCAN_API_KEY env var.')
|
|
124
|
-
.option('-n, --network <selector>', 'Target network (chain ID or name). When a name matches multiple networks, the first match is used.')
|
|
125
|
-
.option('-a, --address <address>', 'Contract address to query', '')
|
|
126
|
-
.option('--raw', 'Print raw response (no pretty JSON). Useful for piping.', false)
|
|
127
|
-
return cmd
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// etherscan abi
|
|
131
|
-
const abi = new Command('abi')
|
|
132
|
-
.description('Fetch contract ABI from Etherscan and print to stdout')
|
|
133
|
-
withCommon(abi)
|
|
134
|
-
abi.action(async (options: EtherscanCmdBase) => {
|
|
135
|
-
try {
|
|
136
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
137
|
-
|
|
138
|
-
const apiKey = options.etherscanApiKey || process.env.ETHERSCAN_API_KEY
|
|
139
|
-
if (!apiKey) {
|
|
140
|
-
console.error(chalk.red('Etherscan API key is required. Use --etherscan-api-key or set ETHERSCAN_API_KEY.'))
|
|
141
|
-
process.exit(1)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (!options.address) {
|
|
145
|
-
console.error(chalk.red('Missing required --address option'))
|
|
146
|
-
process.exit(1)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Determine chainId
|
|
150
|
-
let chainId: number | undefined
|
|
151
|
-
const networks = await loadNetworks(options.project)
|
|
152
|
-
if (options.network) {
|
|
153
|
-
chainId = resolveSingleChainId(options.network, networks)
|
|
154
|
-
} else if (networks.length === 1) {
|
|
155
|
-
chainId = networks[0].chainId
|
|
156
|
-
}
|
|
157
|
-
if (!chainId) {
|
|
158
|
-
console.error(chalk.red('Please provide --network <selector>. When multiple networks are configured, selection is required.'))
|
|
159
|
-
process.exit(1)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const result = await fetchFromEtherscan(chainId!, apiKey, options.address, 'getabi')
|
|
163
|
-
|
|
164
|
-
if (options.raw) {
|
|
165
|
-
// raw: output minified JSON
|
|
166
|
-
process.stdout.write(JSON.stringify(result))
|
|
167
|
-
} else {
|
|
168
|
-
// pretty
|
|
169
|
-
console.log(JSON.stringify(result, null, 2))
|
|
170
|
-
}
|
|
171
|
-
} catch (error) {
|
|
172
|
-
console.error(chalk.red('Error fetching ABI from Etherscan:'), error instanceof Error ? error.message : String(error))
|
|
173
|
-
process.exit(1)
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
// etherscan source
|
|
178
|
-
const source = new Command('source')
|
|
179
|
-
.description('Fetch contract source and emit a self-contained build-info JSON suitable for verification')
|
|
180
|
-
withCommon(source)
|
|
181
|
-
source.action(async (options: EtherscanCmdBase) => {
|
|
182
|
-
try {
|
|
183
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
184
|
-
|
|
185
|
-
const apiKey = options.etherscanApiKey || process.env.ETHERSCAN_API_KEY
|
|
186
|
-
if (!apiKey) {
|
|
187
|
-
console.error(chalk.red('Etherscan API key is required. Use --etherscan-api-key or set ETHERSCAN_API_KEY.'))
|
|
188
|
-
process.exit(1)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (!options.address) {
|
|
192
|
-
console.error(chalk.red('Missing required --address option'))
|
|
193
|
-
process.exit(1)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Determine chainId
|
|
197
|
-
let chainId: number | undefined
|
|
198
|
-
const networks2 = await loadNetworks(options.project)
|
|
199
|
-
if (options.network) {
|
|
200
|
-
chainId = resolveSingleChainId(options.network, networks2)
|
|
201
|
-
} else if (networks2.length === 1) {
|
|
202
|
-
chainId = networks2[0].chainId
|
|
203
|
-
}
|
|
204
|
-
if (!chainId) {
|
|
205
|
-
console.error(chalk.red('Please provide --network <selector>. When multiple networks are configured, selection is required.'))
|
|
206
|
-
process.exit(1)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const result = await fetchFromEtherscan(chainId!, apiKey, options.address, 'getsourcecode') as EtherscanSourceEnvelope
|
|
210
|
-
|
|
211
|
-
const raw = result.rawResult
|
|
212
|
-
const parsed = result.parsedSource
|
|
213
|
-
|
|
214
|
-
// Extract compiler version with commit from metadata when available
|
|
215
|
-
const compilerVersion = (raw?.CompilerVersion as string | undefined) || ''
|
|
216
|
-
const optimizationUsed = (raw?.OptimizationUsed as string | undefined) || ''
|
|
217
|
-
const runsStr = (raw?.Runs as string | undefined) || ''
|
|
218
|
-
const evmVersionRaw = (raw?.EVMVersion as string | undefined) || ''
|
|
219
|
-
const isStandardJson = !!(parsed && typeof parsed === 'object' && 'language' in parsed && 'sources' in parsed)
|
|
220
|
-
|
|
221
|
-
// If we have a standard JSON input, use it; otherwise build one from flattened source
|
|
222
|
-
type SolcInput = {
|
|
223
|
-
language: string
|
|
224
|
-
sources: Record<string, { content?: string }>
|
|
225
|
-
settings?: {
|
|
226
|
-
optimizer?: { enabled?: boolean; runs?: number }
|
|
227
|
-
evmVersion?: string
|
|
228
|
-
outputSelection?: Record<string, Record<string, string[]>>
|
|
229
|
-
viaIR?: boolean
|
|
230
|
-
libraries?: Record<string, Record<string, string>>
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
let input: SolcInput
|
|
234
|
-
if (isStandardJson) {
|
|
235
|
-
input = parsed as SolcInput
|
|
236
|
-
// Ensure outputSelection includes required entries to get creation bytecode and metadata
|
|
237
|
-
const currentSel = (input.settings?.outputSelection ?? {}) as Record<string, Record<string, string[]>>
|
|
238
|
-
const mergedSel: Record<string, Record<string, string[]>> = {
|
|
239
|
-
'*': {
|
|
240
|
-
'*': Array.from(new Set<string>([
|
|
241
|
-
...((currentSel?.['*']?.['*']) || []),
|
|
242
|
-
'abi',
|
|
243
|
-
'evm.bytecode',
|
|
244
|
-
'evm.deployedBytecode',
|
|
245
|
-
'metadata',
|
|
246
|
-
'userdoc',
|
|
247
|
-
'devdoc',
|
|
248
|
-
'evm.methodIdentifiers'
|
|
249
|
-
]))
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
input.settings = {
|
|
253
|
-
...(input.settings || {}),
|
|
254
|
-
outputSelection: mergedSel
|
|
255
|
-
}
|
|
256
|
-
} else {
|
|
257
|
-
// Build a minimal standard JSON input from flattened source
|
|
258
|
-
const flattened = String(parsed || '')
|
|
259
|
-
input = {
|
|
260
|
-
language: 'Solidity',
|
|
261
|
-
sources: {
|
|
262
|
-
'Flattened.sol': { content: flattened }
|
|
263
|
-
},
|
|
264
|
-
settings: {
|
|
265
|
-
optimizer: {
|
|
266
|
-
enabled: optimizationUsed === '1',
|
|
267
|
-
runs: Number.isFinite(Number(runsStr)) ? Number(runsStr) : 200
|
|
268
|
-
},
|
|
269
|
-
evmVersion: evmVersionRaw && evmVersionRaw !== 'default' ? evmVersionRaw : undefined,
|
|
270
|
-
outputSelection: {
|
|
271
|
-
'*': {
|
|
272
|
-
'*': [
|
|
273
|
-
'abi',
|
|
274
|
-
'evm.bytecode.object',
|
|
275
|
-
'evm.bytecode.sourceMap',
|
|
276
|
-
'evm.bytecode.linkReferences',
|
|
277
|
-
'evm.deployedBytecode.object',
|
|
278
|
-
'evm.deployedBytecode.sourceMap',
|
|
279
|
-
'evm.deployedBytecode.linkReferences',
|
|
280
|
-
'evm.deployedBytecode.immutableReferences',
|
|
281
|
-
'evm.methodIdentifiers',
|
|
282
|
-
'metadata'
|
|
283
|
-
]
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Compile with exact solc version when possible
|
|
291
|
-
const solcInput = JSON.stringify(input)
|
|
292
|
-
const versionTag = compilerVersion && compilerVersion.startsWith('v') ? compilerVersion : (compilerVersion ? `v${compilerVersion}` : '')
|
|
293
|
-
let outputRaw: string
|
|
294
|
-
if (versionTag) {
|
|
295
|
-
outputRaw = await new Promise<string>((resolve, reject) => {
|
|
296
|
-
// @ts-ignore - loadRemoteVersion exists in solc js
|
|
297
|
-
solc.loadRemoteVersion(versionTag, (err: unknown, specificSolc: { compile: (input: string) => string } | undefined) => {
|
|
298
|
-
if (err || !specificSolc) return reject((err as Error) || new Error('Failed to load solc version'))
|
|
299
|
-
try {
|
|
300
|
-
resolve(specificSolc.compile(solcInput))
|
|
301
|
-
} catch (e) {
|
|
302
|
-
reject(e)
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
})
|
|
306
|
-
} else {
|
|
307
|
-
outputRaw = solc.compile(solcInput)
|
|
308
|
-
}
|
|
309
|
-
const output = JSON.parse(outputRaw)
|
|
310
|
-
|
|
311
|
-
// Build build-info id as hex of sha1 of input
|
|
312
|
-
const id = createHash('sha1').update(solcInput).digest('hex')
|
|
313
|
-
|
|
314
|
-
// Determine solc versions
|
|
315
|
-
const solcLongVersion = (output?.compiler?.version as string | undefined) || (compilerVersion ? compilerVersion.replace(/^v/, '') : undefined)
|
|
316
|
-
const solcMaybe = solc as unknown as { version?: () => string }
|
|
317
|
-
const solcVersion = (solcLongVersion || '').split('+')[0] || (typeof solcMaybe.version === 'function' ? solcMaybe.version() : 'unknown')
|
|
318
|
-
|
|
319
|
-
// Augment settings with defaults similar to reference format
|
|
320
|
-
const basePath = process.cwd()
|
|
321
|
-
const includePaths = [basePath]
|
|
322
|
-
const allowPaths = includePaths
|
|
323
|
-
|
|
324
|
-
const buildInfo = {
|
|
325
|
-
id,
|
|
326
|
-
source_id_to_path: Object.fromEntries(Object.keys(input.sources).map((p, i) => [String(i), p])),
|
|
327
|
-
language: input.language,
|
|
328
|
-
_format: 'ethers-rs-sol-build-info-1',
|
|
329
|
-
input: {
|
|
330
|
-
version: solcVersion,
|
|
331
|
-
language: input.language,
|
|
332
|
-
sources: input.sources,
|
|
333
|
-
settings: input.settings,
|
|
334
|
-
evmVersion: input.settings?.evmVersion || 'cancun',
|
|
335
|
-
viaIR: input.settings?.viaIR || false,
|
|
336
|
-
libraries: input.settings?.libraries || {}
|
|
337
|
-
},
|
|
338
|
-
allowPaths,
|
|
339
|
-
basePath,
|
|
340
|
-
includePaths,
|
|
341
|
-
output: {
|
|
342
|
-
contracts: output.contracts || {},
|
|
343
|
-
sources: output.sources || {}
|
|
344
|
-
},
|
|
345
|
-
solcLongVersion: solcLongVersion || solcVersion,
|
|
346
|
-
solcVersion: solcVersion
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Print build-info JSON
|
|
350
|
-
console.log(options.raw ? JSON.stringify(buildInfo) : JSON.stringify(buildInfo, null, 2))
|
|
351
|
-
} catch (error) {
|
|
352
|
-
console.error(chalk.red('Error fetching source from Etherscan:'), error instanceof Error ? error.message : String(error))
|
|
353
|
-
process.exit(1)
|
|
354
|
-
}
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
etherscan.addCommand(abi)
|
|
358
|
-
etherscan.addCommand(source)
|
|
359
|
-
return etherscan
|
|
360
|
-
}
|
package/src/commands/index.ts
DELETED
package/src/commands/list.ts
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
2
|
-
import chalk from 'chalk'
|
|
3
|
-
import * as path from 'path'
|
|
4
|
-
import { loadProject, projectOption, noStdOption, verbosityOption } from './common'
|
|
5
|
-
import { loadNetworks } from '../lib/network-loader'
|
|
6
|
-
import { setVerbosity } from '../index'
|
|
7
|
-
|
|
8
|
-
interface ListOptions {
|
|
9
|
-
project: string
|
|
10
|
-
std: boolean
|
|
11
|
-
verbose: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface NetworksListOptions {
|
|
15
|
-
project: string
|
|
16
|
-
verbose: number
|
|
17
|
-
onlyTestnets?: boolean
|
|
18
|
-
onlyNonTestnets?: boolean
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function makeListCommand(): Command {
|
|
22
|
-
const list = new Command('list')
|
|
23
|
-
.description('List project resources like jobs, contracts, and networks')
|
|
24
|
-
|
|
25
|
-
const listJobs = new Command('jobs')
|
|
26
|
-
.description('List all available jobs in the project')
|
|
27
|
-
projectOption(listJobs)
|
|
28
|
-
noStdOption(listJobs)
|
|
29
|
-
verbosityOption(listJobs)
|
|
30
|
-
listJobs.action(async (options: ListOptions) => {
|
|
31
|
-
try {
|
|
32
|
-
// Set verbosity level for logging
|
|
33
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
34
|
-
const loader = await loadProject(options.project, {
|
|
35
|
-
loadStdTemplates: options.std !== false
|
|
36
|
-
})
|
|
37
|
-
console.log(chalk.bold.underline('Available Jobs:'))
|
|
38
|
-
if (loader.jobs.size === 0) {
|
|
39
|
-
console.log(chalk.yellow('No jobs found in this project.'))
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
for (const job of loader.jobs.values()) {
|
|
43
|
-
const deprecatedMark = (job as { deprecated?: boolean }).deprecated ? ` ${chalk.yellow('(deprecated)')}` : ''
|
|
44
|
-
console.log(`- ${chalk.cyan(job.name)} (v${job.version})${deprecatedMark}`)
|
|
45
|
-
if (job.description) {
|
|
46
|
-
console.log(` ${chalk.gray(job.description)}`)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error(chalk.red('Error listing jobs:'), error instanceof Error ? error.message : String(error))
|
|
51
|
-
process.exit(1)
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
const listContracts = new Command('contracts')
|
|
56
|
-
.description('List all contracts found in the project')
|
|
57
|
-
projectOption(listContracts)
|
|
58
|
-
noStdOption(listContracts)
|
|
59
|
-
verbosityOption(listContracts)
|
|
60
|
-
listContracts.action(async (options: ListOptions) => {
|
|
61
|
-
try {
|
|
62
|
-
// Set verbosity level for logging
|
|
63
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
64
|
-
const loader = await loadProject(options.project, {
|
|
65
|
-
loadStdTemplates: options.std !== false
|
|
66
|
-
})
|
|
67
|
-
const contracts = loader.contractRepository.getAll()
|
|
68
|
-
const ambiguousRefs = loader.contractRepository.getAmbiguousReferences()
|
|
69
|
-
|
|
70
|
-
// Display contracts
|
|
71
|
-
console.log(chalk.bold.underline('Available Contracts:'))
|
|
72
|
-
if (contracts.length === 0) {
|
|
73
|
-
console.log(chalk.yellow('No contracts found in this project.'))
|
|
74
|
-
} else {
|
|
75
|
-
for (const contract of contracts) {
|
|
76
|
-
const name = contract.contractName || 'Unknown'
|
|
77
|
-
const source = contract.sourceName || 'Unknown'
|
|
78
|
-
console.log(`- ${chalk.cyan(name)} (${source})`)
|
|
79
|
-
console.log(` ${chalk.gray('Unique Hash:')} ${contract.uniqueHash}`)
|
|
80
|
-
if (contract.buildInfoId) {
|
|
81
|
-
console.log(` ${chalk.gray('Build Info ID:')} ${contract.buildInfoId}`)
|
|
82
|
-
}
|
|
83
|
-
console.log(` ${chalk.gray('Sources:')} ${Array.from(contract._sources).map(p => path.relative(options.project, p)).join(', ')}`)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Display ambiguous references if any
|
|
88
|
-
if (ambiguousRefs.length > 0) {
|
|
89
|
-
console.log('\n' + chalk.bold.underline(chalk.yellow('Ambiguous References:')))
|
|
90
|
-
console.log(chalk.yellow('The following references point to multiple contracts:'))
|
|
91
|
-
for (const ref of ambiguousRefs) {
|
|
92
|
-
console.log(`- ${chalk.red(ref)}`)
|
|
93
|
-
}
|
|
94
|
-
console.log(chalk.yellow('Use the unique hash or a more specific path to reference these contracts.'))
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (contracts.length === 0) {
|
|
98
|
-
console.log('\n' + chalk.yellow('No contracts found in this project. Make sure you have artifact files or build-info files in your project.'))
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error(chalk.red('Error listing contracts:'), error instanceof Error ? error.message : String(error))
|
|
102
|
-
process.exit(1)
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
const listTemplates = new Command('templates')
|
|
107
|
-
.description('List all available templates')
|
|
108
|
-
projectOption(listTemplates)
|
|
109
|
-
noStdOption(listTemplates)
|
|
110
|
-
verbosityOption(listTemplates)
|
|
111
|
-
listTemplates.action(async (options: ListOptions) => {
|
|
112
|
-
try {
|
|
113
|
-
// Set verbosity level for logging
|
|
114
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
115
|
-
const loader = await loadProject(options.project, {
|
|
116
|
-
loadStdTemplates: options.std !== false
|
|
117
|
-
})
|
|
118
|
-
console.log(chalk.bold.underline('Available Templates:'))
|
|
119
|
-
if (loader.templates.size === 0) {
|
|
120
|
-
console.log(chalk.yellow('No templates found.'))
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
for (const template of loader.templates.values()) {
|
|
124
|
-
console.log(`- ${chalk.cyan(template.name)}`)
|
|
125
|
-
if (template.description) {
|
|
126
|
-
console.log(` ${chalk.gray(template.description)}`)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(chalk.red('Error listing templates:'), error instanceof Error ? error.message : String(error))
|
|
131
|
-
process.exit(1)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
const listNetworks = new Command('networks')
|
|
136
|
-
.description('List all configured networks')
|
|
137
|
-
projectOption(listNetworks)
|
|
138
|
-
verbosityOption(listNetworks)
|
|
139
|
-
listNetworks.option('--only-testnets', 'Show only test networks')
|
|
140
|
-
listNetworks.option('--only-non-testnets', 'Show only non-test networks')
|
|
141
|
-
listNetworks.option('--simple', 'Output only network names, one per line')
|
|
142
|
-
listNetworks.option('--simple-chain-ids', 'Output only chain IDs, one per line')
|
|
143
|
-
listNetworks.action(async (options: NetworksListOptions & { simple?: boolean; simpleChainIds?: boolean }) => {
|
|
144
|
-
try {
|
|
145
|
-
// Set verbosity level for logging
|
|
146
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
147
|
-
const networks = await loadNetworks(options.project)
|
|
148
|
-
|
|
149
|
-
// Apply filtering if specified
|
|
150
|
-
let filteredNetworks = networks
|
|
151
|
-
if (options.onlyTestnets) {
|
|
152
|
-
filteredNetworks = networks.filter(network => network.testnet === true)
|
|
153
|
-
} else if (options.onlyNonTestnets) {
|
|
154
|
-
filteredNetworks = networks.filter(network => network.testnet !== true)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Handle simple output formats
|
|
158
|
-
if (options.simple) {
|
|
159
|
-
if (filteredNetworks.length === 0) {
|
|
160
|
-
console.log('')
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
console.log(filteredNetworks.map(network => network.name).join('\n'))
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (options.simpleChainIds) {
|
|
168
|
-
if (filteredNetworks.length === 0) {
|
|
169
|
-
console.log('')
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
console.log(filteredNetworks.map(network => network.chainId.toString()).join('\n'))
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Default formatted output
|
|
177
|
-
console.log(chalk.bold.underline('Available Networks:'))
|
|
178
|
-
if (filteredNetworks.length === 0) {
|
|
179
|
-
if (options.onlyTestnets) {
|
|
180
|
-
console.log(chalk.yellow('No test networks configured.'))
|
|
181
|
-
} else if (options.onlyNonTestnets) {
|
|
182
|
-
console.log(chalk.yellow('No non-test networks configured.'))
|
|
183
|
-
} else {
|
|
184
|
-
console.log(chalk.yellow('No networks configured. Create a networks.yaml file in your project root.'))
|
|
185
|
-
}
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
for (const network of filteredNetworks) {
|
|
190
|
-
const testnetIndicator = network.testnet ? chalk.green('(testnet)') : ''
|
|
191
|
-
console.log(`- ${chalk.cyan(network.name)} (Chain ID: ${network.chainId}) ${testnetIndicator}`)
|
|
192
|
-
console.log(` ${chalk.gray(`RPC: ${network.rpcUrl}`)}`)
|
|
193
|
-
}
|
|
194
|
-
} catch (error) {
|
|
195
|
-
console.error(chalk.red('Error listing networks:'), error instanceof Error ? error.message : String(error))
|
|
196
|
-
process.exit(1)
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
// New: list constants
|
|
201
|
-
const listConstants = new Command('constants')
|
|
202
|
-
.description('List constants defined at top-level and per job')
|
|
203
|
-
projectOption(listConstants)
|
|
204
|
-
noStdOption(listConstants)
|
|
205
|
-
verbosityOption(listConstants)
|
|
206
|
-
listConstants.action(async (options: ListOptions) => {
|
|
207
|
-
try {
|
|
208
|
-
setVerbosity(options.verbose as 0 | 1 | 2 | 3)
|
|
209
|
-
const loader = await loadProject(options.project, {
|
|
210
|
-
loadStdTemplates: options.std !== false
|
|
211
|
-
})
|
|
212
|
-
console.log(chalk.bold.underline('Top-level Constants:'))
|
|
213
|
-
if (loader.constants.size === 0) {
|
|
214
|
-
console.log(chalk.yellow('No top-level constants found.'))
|
|
215
|
-
} else {
|
|
216
|
-
for (const [key, value] of loader.constants.entries()) {
|
|
217
|
-
// Only display keys to avoid dumping large values
|
|
218
|
-
console.log(`- ${chalk.cyan(key)}${options.verbose ? ` = ${JSON.stringify(value)}` : ''}`)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
console.log(chalk.bold.underline('\nJob-level Constants:'))
|
|
222
|
-
let anyJobConstants = false
|
|
223
|
-
for (const job of loader.jobs.values()) {
|
|
224
|
-
const constants = job.constants
|
|
225
|
-
if (constants && Object.keys(constants).length > 0) {
|
|
226
|
-
anyJobConstants = true
|
|
227
|
-
console.log(`- ${chalk.cyan(job.name)}:`)
|
|
228
|
-
for (const key of Object.keys(constants)) {
|
|
229
|
-
console.log(` • ${key}${options.verbose ? ` = ${JSON.stringify(constants[key])}` : ''}`)
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (!anyJobConstants) {
|
|
234
|
-
console.log(chalk.yellow('No job-level constants found.'))
|
|
235
|
-
}
|
|
236
|
-
} catch (error) {
|
|
237
|
-
console.error(chalk.red('Error listing constants:'), error instanceof Error ? error.message : String(error))
|
|
238
|
-
process.exit(1)
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
list.addCommand(listJobs)
|
|
243
|
-
list.addCommand(listContracts)
|
|
244
|
-
list.addCommand(listTemplates)
|
|
245
|
-
list.addCommand(listNetworks)
|
|
246
|
-
list.addCommand(listConstants)
|
|
247
|
-
|
|
248
|
-
return list
|
|
249
|
-
}
|