1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package foundry
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"github.com/ethereum-optimism/optimism/op-chain-ops/solc"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// Artifact represents a foundry compilation artifact.
// JSON marshaling logic is implemented to maintain the ability
// to roundtrip serialize an artifact
type Artifact struct {
ABI abi.ABI
abi json.RawMessage
StorageLayout solc.StorageLayout
DeployedBytecode DeployedBytecode
Bytecode Bytecode
Metadata Metadata
}
func (a *Artifact) UnmarshalJSON(data []byte) error {
artifact := artifactMarshaling{}
if err := json.Unmarshal(data, &artifact); err != nil {
return err
}
parsed, err := abi.JSON(strings.NewReader(string(artifact.ABI)))
if err != nil {
return err
}
a.ABI = parsed
a.abi = artifact.ABI
a.StorageLayout = artifact.StorageLayout
a.DeployedBytecode = artifact.DeployedBytecode
a.Bytecode = artifact.Bytecode
a.Metadata = artifact.Metadata
return nil
}
func (a Artifact) MarshalJSON() ([]byte, error) {
artifact := artifactMarshaling{
ABI: a.abi,
StorageLayout: a.StorageLayout,
DeployedBytecode: a.DeployedBytecode,
Bytecode: a.Bytecode,
Metadata: a.Metadata,
}
return json.Marshal(artifact)
}
// artifactMarshaling is a helper struct for marshaling and unmarshalling
// foundry artifacts.
type artifactMarshaling struct {
ABI json.RawMessage `json:"abi"`
StorageLayout solc.StorageLayout `json:"storageLayout"`
DeployedBytecode DeployedBytecode `json:"deployedBytecode"`
Bytecode Bytecode `json:"bytecode"`
Metadata Metadata `json:"metadata"`
}
// Metadata is the subset of metadata in a foundry contract artifact that we use in OP-Stack tooling.
type Metadata struct {
Compiler struct {
Version string `json:"version"`
} `json:"compiler"`
Language string `json:"language"`
Output json.RawMessage `json:"output"`
Settings struct {
// Remappings of the contract imports
Remappings json.RawMessage `json:"remappings"`
// Optimizer settings affect the compiler output, but can be arbitrary.
// We load them opaquely, to include it in the hash of what we run.
Optimizer json.RawMessage `json:"optimizer"`
// Metadata is loaded opaquely, similar to the Optimizer, to include in hashing.
// E.g. the bytecode-hash contract suffix as setting is enabled/disabled in here.
Metadata json.RawMessage `json:"metadata"`
// Map of full contract path to compiled contract name.
CompilationTarget map[string]string `json:"compilationTarget"`
// EVM version affects output, and hence included.
EVMVersion string `json:"evmVersion"`
// Libraries data
Libraries json.RawMessage `json:"libraries"`
} `json:"settings"`
Sources map[string]ContractSource `json:"sources"`
Version int `json:"version"`
}
// ContractSource represents a JSON value in the "sources" map of a contract metadata dump.
// This uniquely identifies the source code of the contract.
type ContractSource struct {
Keccak256 common.Hash `json:"keccak256"`
URLs []string `json:"urls"`
License string `json:"license"`
}
var ErrLinkingUnsupported = errors.New("cannot load bytecode with linking placeholders")
// LinkableBytecode is not purely hex, it returns an ErrLinkingUnsupported error when
// input contains __$aaaaaaa$__ style linking placeholders.
// See https://docs.soliditylang.org/en/latest/using-the-compiler.html#library-linking
// In practice this is only used by test contracts to link in large test libraries.
type LinkableBytecode []byte
func (lb *LinkableBytecode) UnmarshalJSON(data []byte) error {
if bytes.Contains(data, []byte("__$")) {
return ErrLinkingUnsupported
}
return (*hexutil.Bytes)(lb).UnmarshalJSON(data)
}
func (lb LinkableBytecode) MarshalText() ([]byte, error) {
return (hexutil.Bytes)(lb).MarshalText()
}
// DeployedBytecode represents the deployed bytecode section of the solc compiler output.
type DeployedBytecode struct {
SourceMap string `json:"sourceMap"`
Object LinkableBytecode `json:"object"`
LinkReferences json.RawMessage `json:"linkReferences"`
ImmutableReferences json.RawMessage `json:"immutableReferences,omitempty"`
}
// Bytecode represents the bytecode section of the solc compiler output.
type Bytecode struct {
SourceMap string `json:"sourceMap"`
// not purely hex, can contain __$aaaaaaa$__ style linking placeholders
Object LinkableBytecode `json:"object"`
LinkReferences json.RawMessage `json:"linkReferences"`
ImmutableReferences json.RawMessage `json:"immutableReferences,omitempty"`
}
// ReadArtifact will read an artifact from disk given a path.
func ReadArtifact(path string) (*Artifact, error) {
file, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("artifact at %s not found: %w", path, err)
}
artifact := Artifact{}
if err := json.Unmarshal(file, &artifact); err != nil {
return nil, err
}
return &artifact, nil
}