@0xsequence/catapult 1.4.0 → 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 +27 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +22 -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 +173 -0
- package/dist/lib/core/engine.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/parsers/__tests__/source.spec.js +37 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
- package/dist/lib/parsers/source.js +1 -1
- package/dist/lib/parsers/source.js.map +1 -1
- package/dist/lib/provenance.js +51 -2
- package/dist/lib/provenance.js.map +1 -1
- package/dist/lib/types/actions.d.ts +26 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +3 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/source.d.ts +2 -0
- package/dist/lib/types/source.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 -18
- 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 -6
- package/src/commands/list.ts +0 -262
- package/src/commands/provenance.ts +0 -120
- 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 -2269
- 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/__tests__/provenance.spec.ts +0 -208
- 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 -612
- package/src/lib/contracts/repository.ts +0 -411
- package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -2005
- 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 -367
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
- 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 -1834
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -253
- package/src/lib/core/resolver.ts +0 -873
- package/src/lib/deployer.ts +0 -1005
- 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 -17
- 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 -439
- package/src/lib/parsers/__tests__/source.spec.ts +0 -134
- 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 -6
- package/src/lib/parsers/job.ts +0 -160
- package/src/lib/parsers/source.ts +0 -129
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/provenance.ts +0 -785
- 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 -148
- 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 -26
- package/src/lib/types/definitions.ts +0 -77
- package/src/lib/types/index.ts +0 -9
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/source.ts +0 -26
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -221
- 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,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "NestedContract",
|
|
3
|
-
"sourceName": "contracts/nested/NestedContract.sol",
|
|
4
|
-
"abi": [
|
|
5
|
-
{
|
|
6
|
-
"type": "function",
|
|
7
|
-
"name": "nestedFunction",
|
|
8
|
-
"inputs": [],
|
|
9
|
-
"outputs": [
|
|
10
|
-
{
|
|
11
|
-
"name": "",
|
|
12
|
-
"type": "bool"
|
|
13
|
-
}
|
|
14
|
-
]
|
|
15
|
-
}
|
|
16
|
-
],
|
|
17
|
-
"bytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806320965255146100365780636d4ce63c14610040575b600080fd5b610040610058565b005b610048610062565b6040516100579190610078565b60405180910390f35b0000005b6000806000fefe"
|
|
18
|
-
}
|
|
@@ -1,612 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs/promises'
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
import { ContractRepository } from '../repository'
|
|
4
|
-
|
|
5
|
-
function buildInfoContent(): string {
|
|
6
|
-
return JSON.stringify({
|
|
7
|
-
_format: 'hh-sol-build-info-1',
|
|
8
|
-
id: 'test-build-id',
|
|
9
|
-
solcVersion: '0.8.0',
|
|
10
|
-
solcLongVersion: '0.8.0+commit.c7dfd78e',
|
|
11
|
-
input: {
|
|
12
|
-
language: 'Solidity',
|
|
13
|
-
sources: {
|
|
14
|
-
'src/Stage1Module.sol': {
|
|
15
|
-
content: 'contract Stage1Module {}'
|
|
16
|
-
},
|
|
17
|
-
'src/Stage2Module.sol': {
|
|
18
|
-
content: 'contract Stage2Module {}'
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
settings: {
|
|
22
|
-
outputSelection: {
|
|
23
|
-
'*': {
|
|
24
|
-
'*': ['abi', 'evm.bytecode', 'evm.deployedBytecode']
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
output: {
|
|
30
|
-
contracts: {
|
|
31
|
-
'src/Stage1Module.sol': {
|
|
32
|
-
Stage1Module: {
|
|
33
|
-
abi: [],
|
|
34
|
-
evm: {
|
|
35
|
-
bytecode: {
|
|
36
|
-
object: '0x608060405234801561001057600080fd5b50111111'
|
|
37
|
-
},
|
|
38
|
-
deployedBytecode: {
|
|
39
|
-
object: '0x608060405234801561001057600080fd5b50111112'
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
'src/Stage2Module.sol': {
|
|
45
|
-
Stage2Module: {
|
|
46
|
-
abi: [],
|
|
47
|
-
evm: {
|
|
48
|
-
bytecode: {
|
|
49
|
-
object: '0x608060405234801561001057600080fd5b50222222'
|
|
50
|
-
},
|
|
51
|
-
deployedBytecode: {
|
|
52
|
-
object: '0x608060405234801561001057600080fd5b50222223'
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
sources: {}
|
|
59
|
-
}
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
describe('ContractRepository', () => {
|
|
64
|
-
let repository: ContractRepository
|
|
65
|
-
let tempDir: string
|
|
66
|
-
|
|
67
|
-
beforeEach(async () => {
|
|
68
|
-
repository = new ContractRepository()
|
|
69
|
-
|
|
70
|
-
// Create a temporary directory for test files
|
|
71
|
-
tempDir = path.join(__dirname, 'temp-test-files')
|
|
72
|
-
await fs.mkdir(tempDir, { recursive: true })
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
afterEach(async () => {
|
|
76
|
-
// Clean up temporary files
|
|
77
|
-
try {
|
|
78
|
-
await fs.rm(tempDir, { recursive: true, force: true })
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// Ignore cleanup errors
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe('loadFrom method', () => {
|
|
85
|
-
it('should find and load standard artifact files', async () => {
|
|
86
|
-
const artifactContent = JSON.stringify({
|
|
87
|
-
contractName: 'TestContract',
|
|
88
|
-
abi: [{ type: 'function', name: 'test' }],
|
|
89
|
-
bytecode: '0x608060405234801561001057600080fd5b50',
|
|
90
|
-
deployedBytecode: '0x608060405234801561001057600080fd5b506004361061003957600080fd5b50'
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const artifactPath = path.join(tempDir, 'TestContract.json')
|
|
94
|
-
await fs.writeFile(artifactPath, artifactContent)
|
|
95
|
-
|
|
96
|
-
await repository.loadFrom(tempDir)
|
|
97
|
-
|
|
98
|
-
const contracts = repository.getAll()
|
|
99
|
-
expect(contracts).toHaveLength(1)
|
|
100
|
-
|
|
101
|
-
const contract = contracts[0]
|
|
102
|
-
expect(contract.contractName).toBe('TestContract')
|
|
103
|
-
expect(contract.creationCode).toBe('0x608060405234801561001057600080fd5b50')
|
|
104
|
-
expect(contract.runtimeBytecode).toBe('0x608060405234801561001057600080fd5b506004361061003957600080fd5b50')
|
|
105
|
-
expect(contract.abi).toEqual([{ type: 'function', name: 'test' }])
|
|
106
|
-
expect(contract._sources.has(artifactPath)).toBe(true)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('should find and load build-info files', async () => {
|
|
110
|
-
const buildInfoContent = JSON.stringify({
|
|
111
|
-
_format: 'hh-sol-build-info-1',
|
|
112
|
-
id: 'test-build-id',
|
|
113
|
-
solcVersion: '0.8.0',
|
|
114
|
-
solcLongVersion: '0.8.0+commit.c7dfd78e',
|
|
115
|
-
input: {
|
|
116
|
-
language: 'Solidity',
|
|
117
|
-
sources: {
|
|
118
|
-
'src/TestContract.sol': {
|
|
119
|
-
content: 'contract TestContract { function test() public {} }'
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
settings: {
|
|
123
|
-
outputSelection: {
|
|
124
|
-
'*': {
|
|
125
|
-
'*': ['abi', 'evm.bytecode', 'evm.deployedBytecode']
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
output: {
|
|
131
|
-
contracts: {
|
|
132
|
-
'src/TestContract.sol': {
|
|
133
|
-
TestContract: {
|
|
134
|
-
abi: [{ type: 'function', name: 'test' }],
|
|
135
|
-
evm: {
|
|
136
|
-
bytecode: {
|
|
137
|
-
object: '0x608060405234801561001057600080fd5b50'
|
|
138
|
-
},
|
|
139
|
-
deployedBytecode: {
|
|
140
|
-
object: '0x608060405234801561001057600080fd5b506004361061003939600080fd5b50'
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
sources: {}
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
const buildInfoDir = path.join(tempDir, 'build-info')
|
|
151
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
152
|
-
const buildInfoPath = path.join(buildInfoDir, 'test-buildinfo.json')
|
|
153
|
-
await fs.writeFile(buildInfoPath, buildInfoContent)
|
|
154
|
-
|
|
155
|
-
await repository.loadFrom(tempDir)
|
|
156
|
-
|
|
157
|
-
const contracts = repository.getAll()
|
|
158
|
-
expect(contracts).toHaveLength(1)
|
|
159
|
-
|
|
160
|
-
const contract = contracts[0]
|
|
161
|
-
expect(contract.contractName).toBe('TestContract')
|
|
162
|
-
expect(contract.sourceName).toBe('src/TestContract.sol')
|
|
163
|
-
expect(contract.creationCode).toBe('0x608060405234801561001057600080fd5b50')
|
|
164
|
-
expect(contract.runtimeBytecode).toBe('0x608060405234801561001057600080fd5b506004361061003939600080fd5b50')
|
|
165
|
-
expect(contract.abi).toEqual([{ type: 'function', name: 'test' }])
|
|
166
|
-
expect(contract.buildInfoId).toBe('test-build-id')
|
|
167
|
-
expect(contract.source).toBe('contract TestContract { function test() public {} }')
|
|
168
|
-
expect(contract._sources.has(buildInfoPath)).toBe(true)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('should attach source provenance from build-info sidecars', async () => {
|
|
172
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
173
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
174
|
-
const buildInfoPath = path.join(buildInfoDir, 'stage1.json')
|
|
175
|
-
const sourcePath = path.join(buildInfoDir, 'source.yaml')
|
|
176
|
-
|
|
177
|
-
await fs.writeFile(buildInfoPath, buildInfoContent())
|
|
178
|
-
await fs.writeFile(sourcePath, `
|
|
179
|
-
type: source
|
|
180
|
-
build_info:
|
|
181
|
-
"./stage1.json":
|
|
182
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3"
|
|
183
|
-
ref: "v3.0.0-rc.5"
|
|
184
|
-
commit: "0d9061f229da73edae890e6fdd1fbf753028df6d"
|
|
185
|
-
build: "forge build --build-info"
|
|
186
|
-
contracts:
|
|
187
|
-
"src/Stage1Module.sol:Stage1Module":
|
|
188
|
-
ref: "stage1-special"
|
|
189
|
-
`)
|
|
190
|
-
|
|
191
|
-
await repository.loadFrom(tempDir)
|
|
192
|
-
|
|
193
|
-
const stage1 = repository.lookup(`${buildInfoPath}:Stage1Module`)
|
|
194
|
-
const stage2 = repository.lookup(`${buildInfoPath}:Stage2Module`)
|
|
195
|
-
|
|
196
|
-
expect(stage1).not.toBeNull()
|
|
197
|
-
expect(stage2).not.toBeNull()
|
|
198
|
-
|
|
199
|
-
expect(stage1!.sourceProvenance).toMatchObject({
|
|
200
|
-
repo: 'https://github.com/0xsequence/wallet-contracts-v3',
|
|
201
|
-
ref: 'stage1-special',
|
|
202
|
-
commit: '0d9061f229da73edae890e6fdd1fbf753028df6d',
|
|
203
|
-
build: 'forge build --build-info',
|
|
204
|
-
sourceDocumentPath: sourcePath,
|
|
205
|
-
buildInfoPath
|
|
206
|
-
})
|
|
207
|
-
expect(stage2!.sourceProvenance).toMatchObject({
|
|
208
|
-
repo: 'https://github.com/0xsequence/wallet-contracts-v3',
|
|
209
|
-
ref: 'v3.0.0-rc.5',
|
|
210
|
-
commit: '0d9061f229da73edae890e6fdd1fbf753028df6d'
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
expect(stage1!._sourceProvenance?.get(buildInfoPath)?.ref).toBe('stage1-special')
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it('should skip source sidecars that point to missing build-info files', async () => {
|
|
217
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
218
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
219
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
220
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yaml'), `
|
|
221
|
-
type: source
|
|
222
|
-
build_info:
|
|
223
|
-
"./missing.json":
|
|
224
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3"
|
|
225
|
-
`)
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
await expect(repository.loadFrom(tempDir)).resolves.toBeUndefined()
|
|
229
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('does not exist'))
|
|
230
|
-
} finally {
|
|
231
|
-
warnSpy.mockRestore()
|
|
232
|
-
}
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('should skip malformed source sidecars without blocking build-info loading', async () => {
|
|
236
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
237
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
238
|
-
const buildInfoPath = path.join(buildInfoDir, 'stage1.json')
|
|
239
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
240
|
-
|
|
241
|
-
await fs.writeFile(buildInfoPath, buildInfoContent())
|
|
242
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yaml'), `
|
|
243
|
-
type: source
|
|
244
|
-
build_info:
|
|
245
|
-
"./stage1.json":
|
|
246
|
-
ref: "v3.0.0-rc.5"
|
|
247
|
-
`)
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
await repository.loadFrom(tempDir)
|
|
251
|
-
} finally {
|
|
252
|
-
warnSpy.mockRestore()
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const stage1 = repository.lookup(`${buildInfoPath}:Stage1Module`)
|
|
256
|
-
expect(stage1).not.toBeNull()
|
|
257
|
-
expect(stage1!.sourceProvenance).toBeUndefined()
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
it('should keep valid source sidecar entries when sibling entries are invalid', async () => {
|
|
261
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
262
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
263
|
-
const buildInfoPath = path.join(buildInfoDir, 'stage1.json')
|
|
264
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
265
|
-
|
|
266
|
-
await fs.writeFile(buildInfoPath, buildInfoContent())
|
|
267
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yaml'), `
|
|
268
|
-
type: source
|
|
269
|
-
build_info:
|
|
270
|
-
"./stage1.json":
|
|
271
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3"
|
|
272
|
-
commit: "0d9061f229da73edae890e6fdd1fbf753028df6d"
|
|
273
|
-
"./bad.json":
|
|
274
|
-
typo_field: true
|
|
275
|
-
`)
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
await repository.loadFrom(tempDir)
|
|
279
|
-
|
|
280
|
-
const stage1 = repository.lookup(`${buildInfoPath}:Stage1Module`)
|
|
281
|
-
expect(stage1).not.toBeNull()
|
|
282
|
-
expect(stage1!.sourceProvenance).toMatchObject({
|
|
283
|
-
repo: 'https://github.com/0xsequence/wallet-contracts-v3',
|
|
284
|
-
commit: '0d9061f229da73edae890e6fdd1fbf753028df6d'
|
|
285
|
-
})
|
|
286
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('typo_field is not supported'))
|
|
287
|
-
} finally {
|
|
288
|
-
warnSpy.mockRestore()
|
|
289
|
-
}
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
it('should skip source sidecar entries that do not point to build-info JSON', async () => {
|
|
293
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
294
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
295
|
-
const buildInfoPath = path.join(buildInfoDir, 'stage1.json')
|
|
296
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
297
|
-
|
|
298
|
-
await fs.writeFile(buildInfoPath, buildInfoContent())
|
|
299
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yaml'), `
|
|
300
|
-
type: source
|
|
301
|
-
build_info:
|
|
302
|
-
"./stage1.txt":
|
|
303
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3"
|
|
304
|
-
`)
|
|
305
|
-
|
|
306
|
-
try {
|
|
307
|
-
await repository.loadFrom(tempDir)
|
|
308
|
-
|
|
309
|
-
const stage1 = repository.lookup(`${buildInfoPath}:Stage1Module`)
|
|
310
|
-
expect(stage1).not.toBeNull()
|
|
311
|
-
expect(stage1!.sourceProvenance).toBeUndefined()
|
|
312
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('does not point to a build-info JSON file'))
|
|
313
|
-
} finally {
|
|
314
|
-
warnSpy.mockRestore()
|
|
315
|
-
}
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
it('should skip duplicate source provenance entries for the same build-info file', async () => {
|
|
319
|
-
const buildInfoDir = path.join(tempDir, 'build-info', 'rc-5')
|
|
320
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
321
|
-
const buildInfoPath = path.join(buildInfoDir, 'stage1.json')
|
|
322
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
|
|
323
|
-
|
|
324
|
-
await fs.writeFile(buildInfoPath, buildInfoContent())
|
|
325
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yaml'), `
|
|
326
|
-
type: source
|
|
327
|
-
build_info:
|
|
328
|
-
"./stage1.json":
|
|
329
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3-a"
|
|
330
|
-
`)
|
|
331
|
-
await fs.writeFile(path.join(buildInfoDir, 'source.yml'), `
|
|
332
|
-
type: source
|
|
333
|
-
build_info:
|
|
334
|
-
"./stage1.json":
|
|
335
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3-b"
|
|
336
|
-
`)
|
|
337
|
-
|
|
338
|
-
try {
|
|
339
|
-
await repository.loadFrom(tempDir)
|
|
340
|
-
|
|
341
|
-
const stage1 = repository.lookup(`${buildInfoPath}:Stage1Module`)
|
|
342
|
-
expect(stage1).not.toBeNull()
|
|
343
|
-
expect(stage1!._sourceProvenance?.size).toBe(1)
|
|
344
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('duplicate provenance'))
|
|
345
|
-
} finally {
|
|
346
|
-
warnSpy.mockRestore()
|
|
347
|
-
}
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
it('should select preferred source provenance deterministically for duplicate bytecode', async () => {
|
|
351
|
-
const olderBuildInfoDir = path.join(tempDir, 'build-info', 'b-release')
|
|
352
|
-
const newerBuildInfoDir = path.join(tempDir, 'build-info', 'a-release')
|
|
353
|
-
await fs.mkdir(olderBuildInfoDir, { recursive: true })
|
|
354
|
-
await fs.mkdir(newerBuildInfoDir, { recursive: true })
|
|
355
|
-
|
|
356
|
-
const olderBuildInfoPath = path.join(olderBuildInfoDir, 'stage1.json')
|
|
357
|
-
const newerBuildInfoPath = path.join(newerBuildInfoDir, 'stage1.json')
|
|
358
|
-
await fs.writeFile(olderBuildInfoPath, buildInfoContent())
|
|
359
|
-
await fs.writeFile(newerBuildInfoPath, buildInfoContent())
|
|
360
|
-
|
|
361
|
-
await fs.writeFile(path.join(olderBuildInfoDir, 'source.yaml'), `
|
|
362
|
-
type: source
|
|
363
|
-
build_info:
|
|
364
|
-
"./stage1.json":
|
|
365
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3-old"
|
|
366
|
-
`)
|
|
367
|
-
await fs.writeFile(path.join(newerBuildInfoDir, 'source.yaml'), `
|
|
368
|
-
type: source
|
|
369
|
-
build_info:
|
|
370
|
-
"./stage1.json":
|
|
371
|
-
repo: "https://github.com/0xsequence/wallet-contracts-v3-new"
|
|
372
|
-
`)
|
|
373
|
-
|
|
374
|
-
await repository.loadFrom(tempDir)
|
|
375
|
-
|
|
376
|
-
const stage1 = repository.lookup(`${newerBuildInfoPath}:Stage1Module`)
|
|
377
|
-
expect(stage1).not.toBeNull()
|
|
378
|
-
expect(stage1!._sourceProvenance?.size).toBe(2)
|
|
379
|
-
expect(stage1!.sourceProvenance?.repo).toBe('https://github.com/0xsequence/wallet-contracts-v3-new')
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
it('should hydrate contracts from multiple source files', async () => {
|
|
383
|
-
// Create a basic artifact file (minimal info, will be hydrated by build-info)
|
|
384
|
-
const artifactContent = JSON.stringify({
|
|
385
|
-
contractName: 'TestContract',
|
|
386
|
-
abi: [],
|
|
387
|
-
bytecode: '0x608060405234801561001057600080fd5b50'
|
|
388
|
-
})
|
|
389
|
-
const artifactPath = path.join(tempDir, 'TestContract.json')
|
|
390
|
-
await fs.writeFile(artifactPath, artifactContent)
|
|
391
|
-
|
|
392
|
-
// Create a build-info file with the same bytecode but more complete information
|
|
393
|
-
const buildInfoContent = JSON.stringify({
|
|
394
|
-
_format: 'hh-sol-build-info-1',
|
|
395
|
-
id: 'test-build-id',
|
|
396
|
-
solcVersion: '0.8.0',
|
|
397
|
-
solcLongVersion: '0.8.0+commit.c7dfd78e',
|
|
398
|
-
input: {
|
|
399
|
-
language: 'Solidity',
|
|
400
|
-
sources: {
|
|
401
|
-
'src/TestContract.sol': {
|
|
402
|
-
content: 'contract TestContract { function test() public {} }'
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
settings: {
|
|
406
|
-
outputSelection: {
|
|
407
|
-
'*': {
|
|
408
|
-
'*': ['abi', 'evm.bytecode', 'evm.deployedBytecode']
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
output: {
|
|
414
|
-
contracts: {
|
|
415
|
-
'src/TestContract.sol': {
|
|
416
|
-
TestContract: {
|
|
417
|
-
abi: [{ type: 'function', name: 'test' }],
|
|
418
|
-
evm: {
|
|
419
|
-
bytecode: {
|
|
420
|
-
object: '0x608060405234801561001057600080fd5b50'
|
|
421
|
-
},
|
|
422
|
-
deployedBytecode: {
|
|
423
|
-
object: '0x608060405234801561001057600080fd5b506004361061003939600080fd5b50'
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
sources: {}
|
|
430
|
-
}
|
|
431
|
-
})
|
|
432
|
-
|
|
433
|
-
const buildInfoDir = path.join(tempDir, 'build-info')
|
|
434
|
-
await fs.mkdir(buildInfoDir, { recursive: true })
|
|
435
|
-
const buildInfoPath = path.join(buildInfoDir, 'test-buildinfo.json')
|
|
436
|
-
await fs.writeFile(buildInfoPath, buildInfoContent)
|
|
437
|
-
|
|
438
|
-
await repository.loadFrom(tempDir)
|
|
439
|
-
|
|
440
|
-
const contracts = repository.getAll()
|
|
441
|
-
expect(contracts).toHaveLength(1)
|
|
442
|
-
|
|
443
|
-
const contract = contracts[0]
|
|
444
|
-
// Should have information from both sources
|
|
445
|
-
expect(contract.contractName).toBe('TestContract')
|
|
446
|
-
expect(contract.sourceName).toBe('src/TestContract.sol')
|
|
447
|
-
expect(contract.abi).toEqual([{ type: 'function', name: 'test' }])
|
|
448
|
-
expect(contract.buildInfoId).toBe('test-build-id')
|
|
449
|
-
expect(contract.source).toBe('contract TestContract { function test() public {} }')
|
|
450
|
-
expect(contract._sources.has(artifactPath)).toBe(true)
|
|
451
|
-
expect(contract._sources.has(buildInfoPath)).toBe(true)
|
|
452
|
-
})
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
describe('disambiguateReferences method', () => {
|
|
456
|
-
beforeEach(async () => {
|
|
457
|
-
// Create test files with deliberate name collisions
|
|
458
|
-
const artifact1Content = JSON.stringify({
|
|
459
|
-
contractName: 'MyToken',
|
|
460
|
-
abi: [],
|
|
461
|
-
bytecode: '0x608060405234801561001057600080fd5b50111111'
|
|
462
|
-
})
|
|
463
|
-
const artifact1Path = path.join(tempDir, 'contracts', 'MyToken.json')
|
|
464
|
-
await fs.mkdir(path.dirname(artifact1Path), { recursive: true })
|
|
465
|
-
await fs.writeFile(artifact1Path, artifact1Content)
|
|
466
|
-
|
|
467
|
-
const artifact2Content = JSON.stringify({
|
|
468
|
-
contractName: 'MyToken',
|
|
469
|
-
abi: [],
|
|
470
|
-
bytecode: '0x608060405234801561001057600080fd5b50222222'
|
|
471
|
-
})
|
|
472
|
-
const artifact2Path = path.join(tempDir, 'legacy', 'MyToken.json')
|
|
473
|
-
await fs.mkdir(path.dirname(artifact2Path), { recursive: true })
|
|
474
|
-
await fs.writeFile(artifact2Path, artifact2Content)
|
|
475
|
-
|
|
476
|
-
await repository.loadFrom(tempDir)
|
|
477
|
-
})
|
|
478
|
-
|
|
479
|
-
it('should identify ambiguous references correctly', () => {
|
|
480
|
-
const ambiguousRefs = repository.getAmbiguousReferences()
|
|
481
|
-
expect(ambiguousRefs).toContain('MyToken')
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
it('should allow lookup by unique hash', () => {
|
|
485
|
-
const contracts = repository.getAll()
|
|
486
|
-
const contract1 = contracts.find(c => c.creationCode.includes('111111'))
|
|
487
|
-
const contract2 = contracts.find(c => c.creationCode.includes('222222'))
|
|
488
|
-
|
|
489
|
-
expect(contract1).toBeDefined()
|
|
490
|
-
expect(contract2).toBeDefined()
|
|
491
|
-
|
|
492
|
-
const lookupResult1 = repository.lookup(contract1!.uniqueHash)
|
|
493
|
-
const lookupResult2 = repository.lookup(contract2!.uniqueHash)
|
|
494
|
-
|
|
495
|
-
expect(lookupResult1).toBe(contract1)
|
|
496
|
-
expect(lookupResult2).toBe(contract2)
|
|
497
|
-
})
|
|
498
|
-
|
|
499
|
-
it('should allow lookup by unambiguous path', () => {
|
|
500
|
-
const contracts = repository.getAll()
|
|
501
|
-
const contract1 = contracts.find(c => c.creationCode.includes('111111'))
|
|
502
|
-
|
|
503
|
-
const lookupResult = repository.lookup(path.join(tempDir, 'contracts', 'MyToken.json'))
|
|
504
|
-
expect(lookupResult).toBe(contract1)
|
|
505
|
-
})
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
describe('lookup method', () => {
|
|
509
|
-
beforeEach(async () => {
|
|
510
|
-
const artifactContent = JSON.stringify({
|
|
511
|
-
contractName: 'UniqueContract',
|
|
512
|
-
abi: [{ type: 'function', name: 'test' }],
|
|
513
|
-
bytecode: '0x608060405234801561001057600080fd5b50'
|
|
514
|
-
})
|
|
515
|
-
const artifactPath = path.join(tempDir, 'UniqueContract.json')
|
|
516
|
-
await fs.writeFile(artifactPath, artifactContent)
|
|
517
|
-
|
|
518
|
-
await repository.loadFrom(tempDir)
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
it('should successfully lookup by contract name', () => {
|
|
522
|
-
const contract = repository.lookup('UniqueContract')
|
|
523
|
-
expect(contract).not.toBeNull()
|
|
524
|
-
expect(contract!.contractName).toBe('UniqueContract')
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
it('should successfully lookup by unique hash', () => {
|
|
528
|
-
const contracts = repository.getAll()
|
|
529
|
-
const testContract = contracts[0]
|
|
530
|
-
|
|
531
|
-
const contract = repository.lookup(testContract.uniqueHash)
|
|
532
|
-
expect(contract).toBe(testContract)
|
|
533
|
-
})
|
|
534
|
-
|
|
535
|
-
it('should successfully lookup by file path', () => {
|
|
536
|
-
const artifactPath = path.join(tempDir, 'UniqueContract.json')
|
|
537
|
-
const contract = repository.lookup(artifactPath)
|
|
538
|
-
expect(contract).not.toBeNull()
|
|
539
|
-
expect(contract!.contractName).toBe('UniqueContract')
|
|
540
|
-
})
|
|
541
|
-
|
|
542
|
-
it('should return null for non-existent references', () => {
|
|
543
|
-
const contract = repository.lookup('NonExistentContract')
|
|
544
|
-
expect(contract).toBeNull()
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
it('should throw error for ambiguous references', async () => {
|
|
548
|
-
// Add another contract with the same name
|
|
549
|
-
const artifact2Content = JSON.stringify({
|
|
550
|
-
contractName: 'UniqueContract',
|
|
551
|
-
abi: [],
|
|
552
|
-
bytecode: '0x608060405234801561001057600080fd5b50222222'
|
|
553
|
-
})
|
|
554
|
-
const artifact2Path = path.join(tempDir, 'other', 'UniqueContract.json')
|
|
555
|
-
await fs.mkdir(path.dirname(artifact2Path), { recursive: true })
|
|
556
|
-
await fs.writeFile(artifact2Path, artifact2Content)
|
|
557
|
-
|
|
558
|
-
// Reload to pick up the new file
|
|
559
|
-
repository = new ContractRepository()
|
|
560
|
-
await repository.loadFrom(tempDir)
|
|
561
|
-
|
|
562
|
-
expect(() => {
|
|
563
|
-
repository.lookup('UniqueContract')
|
|
564
|
-
}).toThrow(/Ambiguous contract reference/)
|
|
565
|
-
})
|
|
566
|
-
})
|
|
567
|
-
|
|
568
|
-
describe('edge cases', () => {
|
|
569
|
-
it('should handle directories with no contract files', async () => {
|
|
570
|
-
const emptyDir = path.join(tempDir, 'empty')
|
|
571
|
-
await fs.mkdir(emptyDir, { recursive: true })
|
|
572
|
-
|
|
573
|
-
await repository.loadFrom(emptyDir)
|
|
574
|
-
|
|
575
|
-
const contracts = repository.getAll()
|
|
576
|
-
expect(contracts).toHaveLength(0)
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
it('should ignore invalid JSON files', async () => {
|
|
580
|
-
const invalidJsonPath = path.join(tempDir, 'invalid.json')
|
|
581
|
-
await fs.writeFile(invalidJsonPath, 'invalid json content')
|
|
582
|
-
|
|
583
|
-
const validArtifactContent = JSON.stringify({
|
|
584
|
-
contractName: 'ValidContract',
|
|
585
|
-
abi: [],
|
|
586
|
-
bytecode: '0x608060405234801561001057600080fd5b50'
|
|
587
|
-
})
|
|
588
|
-
const validArtifactPath = path.join(tempDir, 'valid.json')
|
|
589
|
-
await fs.writeFile(validArtifactPath, validArtifactContent)
|
|
590
|
-
|
|
591
|
-
await repository.loadFrom(tempDir)
|
|
592
|
-
|
|
593
|
-
const contracts = repository.getAll()
|
|
594
|
-
expect(contracts).toHaveLength(1)
|
|
595
|
-
expect(contracts[0].contractName).toBe('ValidContract')
|
|
596
|
-
})
|
|
597
|
-
|
|
598
|
-
it('should ignore files that are not artifacts or build-info', async () => {
|
|
599
|
-
const nonArtifactContent = JSON.stringify({
|
|
600
|
-
name: 'not an artifact',
|
|
601
|
-
value: 'some data'
|
|
602
|
-
})
|
|
603
|
-
const nonArtifactPath = path.join(tempDir, 'notartifact.json')
|
|
604
|
-
await fs.writeFile(nonArtifactPath, nonArtifactContent)
|
|
605
|
-
|
|
606
|
-
await repository.loadFrom(tempDir)
|
|
607
|
-
|
|
608
|
-
const contracts = repository.getAll()
|
|
609
|
-
expect(contracts).toHaveLength(0)
|
|
610
|
-
})
|
|
611
|
-
})
|
|
612
|
-
})
|