Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
e4536f9e
Unverified
Commit
e4536f9e
authored
Nov 16, 2023
by
Wyatt Barnes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BindGen - local contracts refactor
parent
5fe703da
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
604 additions
and
301 deletions
+604
-301
Makefile
op-bindings/Makefile
+17
-9
artifacts.json
op-bindings/artifacts.json
+50
-48
generator_local.go
op-bindings/bindgen/generator_local.go
+253
-0
main.go
op-bindings/bindgen/main.go
+150
-0
utils.go
op-bindings/bindgen/utils.go
+131
-0
main.go
op-bindings/gen/main.go
+0
-241
init.go
op-e2e/config/init.go
+3
-3
No files found.
op-bindings/Makefile
View file @
e4536f9e
...
...
@@ -3,6 +3,8 @@ SHELL := /usr/bin/env bash
pkg
:=
bindings
monorepo-base
:=
$(
shell
dirname
$
(
realpath
.
))
contracts-dir
:=
$
(
monorepo-base
)
/packages/contracts-bedrock
contracts-list
:=
./artifacts.json
log-level
:=
info
all
:
version mkdir bindings
...
...
@@ -15,16 +17,22 @@ compile:
forge clean
&&
\
pnpm build
bindings
:
compile bindings-build
bindings
:
bindgen-local
bindings-build
:
go run ./gen/main.go
\
-forge-artifacts
$
(
contracts-dir
)
/forge-artifacts
\
-out
./bindings
\
-contracts
./artifacts.json
\
-source-maps
MIPS,PreimageOracle
\
-package
$(pkg)
\
-monorepo-base
$
(
monorepo-base
)
bindings-build
:
bindgen-generate-local
bindgen-local
:
compile bindgen-generate-local
bindgen-generate-local
:
go run ./bindgen/
\
generate
\
--metadata-out
./
$(pkg)
\
--bindings-package
$(pkg)
\
--contracts-list
$
(
contracts-list
)
\
--log
.level
$
(
log-level
)
\
local
\
--source-maps-list
MIPS,PreimageOracle
\
--forge-artifacts
$
(
contracts-dir
)
/forge-artifacts
mkdir
:
mkdir
-p
$(pkg)
...
...
op-bindings/artifacts.json
View file @
e4536f9e
[
"SystemConfig"
,
"L1CrossDomainMessenger"
,
"L1StandardBridge"
,
"OptimismPortal"
,
"L2OutputOracle"
,
"AddressManager"
,
"L1Block"
,
"L2ToL1MessagePasser"
,
"GasPriceOracle"
,
"L2CrossDomainMessenger"
,
"L2StandardBridge"
,
"L2ERC721Bridge"
,
"L1ERC721Bridge"
,
"OptimismMintableERC721Factory"
,
"SequencerFeeVault"
,
"BaseFeeVault"
,
"L1FeeVault"
,
"OptimismMintableERC20Factory"
,
"OptimismMintableERC20"
,
"LegacyERC20ETH"
,
"Proxy"
,
"ProxyAdmin"
,
"LegacyMessagePasser"
,
"ERC20"
,
"WETH9"
,
"DeployerWhitelist"
,
"L1BlockNumber"
,
"DisputeGameFactory"
,
"FaultDisputeGame"
,
"OutputBisectionGame"
,
"AlphabetVM"
,
"AlphabetVM2"
,
"StandardBridge"
,
"CrossDomainMessenger"
,
"MIPS"
,
"PreimageOracle"
,
"BlockOracle"
,
"EAS"
,
"SchemaRegistry"
,
"ProtocolVersions"
,
"Safe"
,
"SafeProxyFactory"
,
"DelayedVetoable"
,
"ISemver"
,
"StorageSetter"
,
"SuperchainConfig"
]
{
"local"
:
[
"SystemConfig"
,
"L1CrossDomainMessenger"
,
"L1StandardBridge"
,
"OptimismPortal"
,
"L2OutputOracle"
,
"AddressManager"
,
"L1Block"
,
"L2ToL1MessagePasser"
,
"GasPriceOracle"
,
"L2CrossDomainMessenger"
,
"L2StandardBridge"
,
"L2ERC721Bridge"
,
"L1ERC721Bridge"
,
"OptimismMintableERC721Factory"
,
"SequencerFeeVault"
,
"BaseFeeVault"
,
"L1FeeVault"
,
"OptimismMintableERC20Factory"
,
"OptimismMintableERC20"
,
"LegacyERC20ETH"
,
"Proxy"
,
"ProxyAdmin"
,
"LegacyMessagePasser"
,
"ERC20"
,
"WETH9"
,
"DeployerWhitelist"
,
"L1BlockNumber"
,
"DisputeGameFactory"
,
"FaultDisputeGame"
,
"OutputBisectionGame"
,
"AlphabetVM"
,
"AlphabetVM2"
,
"StandardBridge"
,
"CrossDomainMessenger"
,
"MIPS"
,
"PreimageOracle"
,
"BlockOracle"
,
"EAS"
,
"SchemaRegistry"
,
"ProtocolVersions"
,
"Safe"
,
"SafeProxyFactory"
,
"DelayedVetoable"
,
"ISemver"
,
"StorageSetter"
,
"SuperchainConfig"
]
}
op-bindings/bindgen/generator_local.go
0 → 100644
View file @
e4536f9e
package
main
import
(
"encoding/json"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
"github.com/ethereum-optimism/optimism/op-bindings/ast"
"github.com/ethereum-optimism/optimism/op-bindings/foundry"
)
type
bindGenGeneratorLocal
struct
{
bindGenGeneratorBase
sourceMapsList
string
forgeArtifactsPath
string
}
type
localContractMetadata
struct
{
Name
string
StorageLayout
string
DeployedBin
string
Package
string
DeployedSourceMap
string
HasImmutableReferences
bool
}
func
(
generator
*
bindGenGeneratorLocal
)
generateBindings
()
error
{
contracts
,
err
:=
readContractList
(
generator
.
logger
,
generator
.
contractsListPath
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error reading contract list %s: %w"
,
generator
.
contractsListPath
,
err
)
}
if
len
(
contracts
.
Local
)
==
0
{
return
fmt
.
Errorf
(
"no contracts parsed from given contract list: %s"
,
generator
.
contractsListPath
)
}
return
generator
.
processContracts
(
contracts
.
Local
)
}
func
(
generator
*
bindGenGeneratorLocal
)
processContracts
(
contracts
[]
string
)
error
{
tempArtifactsDir
,
err
:=
mkTempArtifactsDir
(
generator
.
logger
)
if
err
!=
nil
{
return
err
}
defer
func
()
{
err
:=
os
.
RemoveAll
(
tempArtifactsDir
)
if
err
!=
nil
{
generator
.
logger
.
Error
(
"Error removing temporary artifact directory"
,
"path"
,
tempArtifactsDir
,
"err"
,
err
.
Error
())
}
else
{
generator
.
logger
.
Debug
(
"Successfully removed temporary artifact directory"
)
}
}()
sourceMapsList
:=
strings
.
Split
(
generator
.
sourceMapsList
,
","
)
sourceMapsSet
:=
make
(
map
[
string
]
struct
{})
for
_
,
k
:=
range
sourceMapsList
{
sourceMapsSet
[
k
]
=
struct
{}{}
}
contractArtifactPaths
,
err
:=
generator
.
getContractArtifactPaths
()
if
err
!=
nil
{
return
err
}
contractMetadataFileTemplate
:=
template
.
Must
(
template
.
New
(
"localContractMetadata"
)
.
Parse
(
localContractMetadataTemplate
))
for
_
,
contractName
:=
range
contracts
{
generator
.
logger
.
Info
(
"Generating bindings and metadata for local contract"
,
"contract"
,
contractName
)
forgeArtifact
,
err
:=
generator
.
readForgeArtifact
(
contractName
,
contractArtifactPaths
)
if
err
!=
nil
{
return
err
}
abiFilePath
,
bytecodeFilePath
,
err
:=
writeContractArtifacts
(
generator
.
logger
,
tempArtifactsDir
,
contractName
,
forgeArtifact
.
Abi
,
[]
byte
(
forgeArtifact
.
Bytecode
.
Object
.
String
()))
if
err
!=
nil
{
return
err
}
err
=
genContractBindings
(
generator
.
logger
,
abiFilePath
,
bytecodeFilePath
,
generator
.
bindingsPackageName
,
contractName
)
if
err
!=
nil
{
return
err
}
deployedSourceMap
,
canonicalStorageStr
,
err
:=
generator
.
canonicalizeStorageLayout
(
forgeArtifact
,
sourceMapsSet
,
contractName
)
if
err
!=
nil
{
return
err
}
re
:=
regexp
.
MustCompile
(
`\s+`
)
immutableRefs
,
err
:=
json
.
Marshal
(
re
.
ReplaceAllString
(
string
(
forgeArtifact
.
DeployedBytecode
.
ImmutableReferences
),
""
))
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error marshaling immutable references: %w"
,
err
)
}
hasImmutables
:=
string
(
immutableRefs
)
!=
`""`
contractMetaData
:=
localContractMetadata
{
Name
:
contractName
,
StorageLayout
:
canonicalStorageStr
,
DeployedBin
:
forgeArtifact
.
DeployedBytecode
.
Object
.
String
(),
Package
:
generator
.
bindingsPackageName
,
DeployedSourceMap
:
deployedSourceMap
,
HasImmutableReferences
:
hasImmutables
,
}
if
err
:=
generator
.
writeContractMetadata
(
contractMetaData
,
contractName
,
contractMetadataFileTemplate
);
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
generator
*
bindGenGeneratorLocal
)
getContractArtifactPaths
()
(
map
[
string
]
string
,
error
)
{
// If some contracts have the same name then the path to their
// artifact depends on their full import path. Scan over all artifacts
// and hold a mapping from the contract name to the contract path.
// Walk walks the directory deterministically, so the earliest instance
// of the contract with the same name will be used
artifactPaths
:=
make
(
map
[
string
]
string
)
if
err
:=
filepath
.
Walk
(
generator
.
forgeArtifactsPath
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
if
err
!=
nil
{
return
err
}
if
strings
.
HasSuffix
(
path
,
".json"
)
{
base
:=
filepath
.
Base
(
path
)
name
:=
strings
.
TrimSuffix
(
base
,
".json"
)
// remove the compiler version from the name
re
:=
regexp
.
MustCompile
(
`\.\d+\.\d+\.\d+`
)
sanitized
:=
re
.
ReplaceAllString
(
name
,
""
)
_
,
ok
:=
artifactPaths
[
sanitized
]
if
!
ok
{
artifactPaths
[
sanitized
]
=
path
}
else
{
generator
.
logger
.
Warn
(
"Multiple versions of forge artifacts exist, using lesser version"
,
"contract"
,
sanitized
)
}
}
return
nil
});
err
!=
nil
{
return
artifactPaths
,
err
}
return
artifactPaths
,
nil
}
func
(
generator
*
bindGenGeneratorLocal
)
readForgeArtifact
(
contractName
string
,
contractArtifactPaths
map
[
string
]
string
)
(
foundry
.
Artifact
,
error
)
{
var
forgeArtifact
foundry
.
Artifact
contractArtifactPath
:=
path
.
Join
(
generator
.
forgeArtifactsPath
,
contractName
+
".sol"
,
contractName
+
".json"
)
forgeArtifactRaw
,
err
:=
os
.
ReadFile
(
contractArtifactPath
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
generator
.
logger
.
Debug
(
"Cannot find forge-artifact at standard path, trying provided path"
,
"contract"
,
contractName
,
"standardPath"
,
contractArtifactPath
,
"providedPath"
,
contractArtifactPaths
[
contractName
])
contractArtifactPath
=
contractArtifactPaths
[
contractName
]
forgeArtifactRaw
,
err
=
os
.
ReadFile
(
contractArtifactPath
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
return
forgeArtifact
,
fmt
.
Errorf
(
"cannot find forge-artifact of %q"
,
contractName
)
}
}
generator
.
logger
.
Debug
(
"Using forge-artifact"
,
"path"
,
contractArtifactPath
)
if
err
:=
json
.
Unmarshal
(
forgeArtifactRaw
,
&
forgeArtifact
);
err
!=
nil
{
return
forgeArtifact
,
fmt
.
Errorf
(
"failed to parse forge artifact of %q: %w"
,
contractName
,
err
)
}
return
forgeArtifact
,
nil
}
func
(
generator
*
bindGenGeneratorLocal
)
canonicalizeStorageLayout
(
forgeArtifact
foundry
.
Artifact
,
sourceMapsSet
map
[
string
]
struct
{},
contractName
string
)
(
string
,
string
,
error
)
{
artifactStorageStruct
:=
forgeArtifact
.
StorageLayout
canonicalStorageStruct
:=
ast
.
CanonicalizeASTIDs
(
&
artifactStorageStruct
,
generator
.
monorepoBasePath
)
canonicalStorageJson
,
err
:=
json
.
Marshal
(
canonicalStorageStruct
)
if
err
!=
nil
{
return
""
,
""
,
fmt
.
Errorf
(
"error marshaling canonical storage: %w"
,
err
)
}
canonicalStorageStr
:=
strings
.
Replace
(
string
(
canonicalStorageJson
),
"
\"
"
,
"
\\\"
"
,
-
1
)
deployedSourceMap
:=
""
if
_
,
ok
:=
sourceMapsSet
[
contractName
];
ok
{
deployedSourceMap
=
forgeArtifact
.
DeployedBytecode
.
SourceMap
}
return
deployedSourceMap
,
canonicalStorageStr
,
nil
}
func
(
generator
*
bindGenGeneratorLocal
)
writeContractMetadata
(
contractMetaData
localContractMetadata
,
contractName
string
,
fileTemplate
*
template
.
Template
)
error
{
metadataFilePath
:=
filepath
.
Join
(
generator
.
metadataOut
,
strings
.
ToLower
(
contractName
)
+
"_more.go"
)
metadataFile
,
err
:=
os
.
OpenFile
(
metadataFilePath
,
os
.
O_RDWR
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
0
o600
,
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error opening %s's metadata file at %s: %w"
,
contractName
,
metadataFilePath
,
err
)
}
defer
metadataFile
.
Close
()
if
err
:=
fileTemplate
.
Execute
(
metadataFile
,
contractMetaData
);
err
!=
nil
{
return
fmt
.
Errorf
(
"error writing %s's contract metadata at %s: %w"
,
contractName
,
metadataFilePath
,
err
)
}
generator
.
logger
.
Debug
(
"Successfully wrote contract metadata"
,
"contract"
,
contractName
,
"path"
,
metadataFilePath
)
return
nil
}
// associated with a local Ethereum contract. This template is used to produce
// Go code containing necessary constants and initialization logic for the contract's
// storage layout, deployed bytecode, and optionally its deployed source map.
//
// The template expects the following fields to be provided:
// - Package: The name of the Go package for the generated bindings.
// - Name: The name of the contract.
// - StorageLayout: Canonicalized storage layout of the contract as a JSON string.
// - DeployedBin: The deployed bytecode of the contract.
// - DeployedSourceMap (optional): The source map of the deployed contract.
var
localContractMetadataTemplate
=
`// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
import (
"encoding/json"
"github.com/ethereum-optimism/optimism/op-bindings/solc"
)
const {{.Name}}StorageLayoutJSON = "{{.StorageLayout}}"
var {{.Name}}StorageLayout = new(solc.StorageLayout)
var {{.Name}}DeployedBin = "{{.DeployedBin}}"
{{if .DeployedSourceMap}}
var {{.Name}}DeployedSourceMap = "{{.DeployedSourceMap}}"
{{end}}
func init() {
if err := json.Unmarshal([]byte({{.Name}}StorageLayoutJSON), {{.Name}}StorageLayout); err != nil {
panic(err)
}
layouts["{{.Name}}"] = {{.Name}}StorageLayout
deployedBytecodes["{{.Name}}"] = {{.Name}}DeployedBin
immutableReferences["{{.Name}}"] = {{.HasImmutableReferences}}
}
`
op-bindings/bindgen/main.go
0 → 100644
View file @
e4536f9e
package
main
import
(
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-e2e/config"
oplog
"github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
)
type
bindGenGeneratorBase
struct
{
metadataOut
string
bindingsPackageName
string
monorepoBasePath
string
contractsListPath
string
logger
log
.
Logger
}
const
(
// Base Flags
MetadataOutFlagName
=
"metadata-out"
BindingsPackageNameFlagName
=
"bindings-package"
ContractsListFlagName
=
"contracts-list"
// Local Contracts Flags
SourceMapsListFlagName
=
"source-maps-list"
ForgeArtifactsFlagName
=
"forge-artifacts"
)
func
main
()
{
oplog
.
SetupDefaults
()
app
:=
&
cli
.
App
{
Name
:
"BindGen"
,
Usage
:
"Generate contract bindings using Foundry artifacts and/or remotely sourced contract data"
,
Commands
:
[]
*
cli
.
Command
{
{
Name
:
"generate"
,
Usage
:
"Generate contract bindings"
,
Flags
:
baseFlags
(),
Subcommands
:
[]
*
cli
.
Command
{
{
Name
:
"local"
,
Usage
:
"Generate bindings for locally sourced contracts"
,
Flags
:
localFlags
(),
Action
:
generateBindings
,
},
},
},
},
}
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
log
.
Crit
(
"BindGen error"
,
"error"
,
err
.
Error
())
}
}
func
setupLogger
(
c
*
cli
.
Context
)
log
.
Logger
{
logger
:=
oplog
.
NewLogger
(
oplog
.
AppOut
(
c
),
oplog
.
ReadCLIConfig
(
c
))
oplog
.
SetGlobalLogHandler
(
logger
.
GetHandler
())
return
logger
}
func
generateBindings
(
c
*
cli
.
Context
)
error
{
logger
:=
setupLogger
(
c
)
switch
c
.
Command
.
Name
{
case
"local"
:
localBindingsGenerator
,
err
:=
parseConfigLocal
(
logger
,
c
)
if
err
!=
nil
{
return
err
}
if
err
:=
localBindingsGenerator
.
generateBindings
();
err
!=
nil
{
return
fmt
.
Errorf
(
"error generating local bindings: %w"
,
err
)
}
return
nil
default
:
return
fmt
.
Errorf
(
"unknown command: %s"
,
c
.
Command
.
Name
)
}
}
func
parseConfigBase
(
logger
log
.
Logger
,
c
*
cli
.
Context
)
(
bindGenGeneratorBase
,
error
)
{
cwd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
return
bindGenGeneratorBase
{},
err
}
monoRepoPath
,
err
:=
config
.
FindMonorepoRoot
(
cwd
)
if
err
!=
nil
{
return
bindGenGeneratorBase
{},
err
}
return
bindGenGeneratorBase
{
metadataOut
:
c
.
String
(
MetadataOutFlagName
),
bindingsPackageName
:
c
.
String
(
BindingsPackageNameFlagName
),
monorepoBasePath
:
monoRepoPath
,
contractsListPath
:
c
.
String
(
ContractsListFlagName
),
logger
:
logger
,
},
nil
}
func
parseConfigLocal
(
logger
log
.
Logger
,
c
*
cli
.
Context
)
(
bindGenGeneratorLocal
,
error
)
{
baseConfig
,
err
:=
parseConfigBase
(
logger
,
c
)
if
err
!=
nil
{
return
bindGenGeneratorLocal
{},
err
}
return
bindGenGeneratorLocal
{
bindGenGeneratorBase
:
baseConfig
,
sourceMapsList
:
c
.
String
(
SourceMapsListFlagName
),
forgeArtifactsPath
:
c
.
String
(
ForgeArtifactsFlagName
),
},
nil
}
func
baseFlags
()
[]
cli
.
Flag
{
baseFlags
:=
[]
cli
.
Flag
{
&
cli
.
StringFlag
{
Name
:
MetadataOutFlagName
,
Usage
:
"Output directory to put contract metadata files in"
,
Required
:
true
,
},
&
cli
.
StringFlag
{
Name
:
BindingsPackageNameFlagName
,
Usage
:
"Go package name given to generated bindings"
,
Required
:
true
,
},
&
cli
.
StringFlag
{
Name
:
ContractsListFlagName
,
Usage
:
"Path to file containing list of contract names to generate bindings for"
,
Required
:
true
,
},
}
return
append
(
baseFlags
,
oplog
.
CLIFlags
(
"bindgen"
)
...
)
}
func
localFlags
()
[]
cli
.
Flag
{
return
[]
cli
.
Flag
{
&
cli
.
StringFlag
{
Name
:
SourceMapsListFlagName
,
Usage
:
"Comma-separated list of contracts to generate source-maps for"
,
},
&
cli
.
StringFlag
{
Name
:
ForgeArtifactsFlagName
,
Usage
:
"Path to forge-artifacts directory, containing compiled contract artifacts"
,
Required
:
true
,
},
}
}
op-bindings/bindgen/utils.go
0 → 100644
View file @
e4536f9e
package
main
import
(
"encoding/json"
"fmt"
"os"
"os/exec"
"path"
"strings"
"github.com/ethereum/go-ethereum/log"
)
type
contractsList
struct
{
Local
[]
string
`json:"local"`
}
// readContractList reads a JSON file from the given `filePath` and unmarshals
// its content into the provided result interface. It logs the path of the file
// being read.
//
// Parameters:
// - logger: An instance of go-ethereum/log
// - filePath: The path to the JSON file to be read.
// - result: A pointer to the structure where the JSON data will be unmarshaled.
//
// Returns:
// - An error if reading the file or unmarshaling fails, nil otherwise.
func
readContractList
(
logger
log
.
Logger
,
filePath
string
)
(
contractsList
,
error
)
{
logger
.
Debug
(
"Reading contract list"
,
"filePath"
,
filePath
)
var
contracts
contractsList
contractData
,
err
:=
os
.
ReadFile
(
filePath
)
if
err
!=
nil
{
return
contracts
,
err
}
return
contracts
,
json
.
Unmarshal
(
contractData
,
&
contracts
)
}
// mkTempArtifactsDir creates a temporary directory with a "op-bindings" prefix
// for holding contract artifacts. The path to the created directory is logged.
//
// Parameters:
// - logger: An instance of go-ethereum/log
//
// Returns:
// - The path to the created temporary directory.
// - An error if the directory creation fails, nil otherwise.
func
mkTempArtifactsDir
(
logger
log
.
Logger
)
(
string
,
error
)
{
dir
,
err
:=
os
.
MkdirTemp
(
""
,
"op-bindings"
)
if
err
!=
nil
{
return
""
,
err
}
logger
.
Debug
(
"Created temporary artifacts directory"
,
"dir"
,
dir
)
return
dir
,
nil
}
// writeContractArtifacts writes the provided ABI and bytecode data to respective
// files in the specified temporary directory. The naming convention for these
// files is based on the provided contract name. The ABI data is written to a file
// with a ".abi" extension, and the bytecode data is written to a file with a ".bin"
// extension.
//
// Parameters:
// - logger: An instance of go-ethereum/log
// - tempDirPath: The directory path where the ABI and bytecode files will be written.
// - contractName: The name of the contract, used to create the filenames.
// - abi: The ABI data of the contract.
// - bytecode: The bytecode of the contract.
//
// Returns:
// - The full path to the written ABI file.
// - The full path to the written bytecode file.
// - An error if writing either file fails, nil otherwise.
func
writeContractArtifacts
(
logger
log
.
Logger
,
tempDirPath
,
contractName
string
,
abi
,
bytecode
[]
byte
)
(
string
,
string
,
error
)
{
logger
.
Debug
(
"Writing ABI and bytecode to temporary artifacts directory"
,
"contractName"
,
contractName
,
"tempDirPath"
,
tempDirPath
)
abiFilePath
:=
path
.
Join
(
tempDirPath
,
contractName
+
".abi"
)
if
err
:=
os
.
WriteFile
(
abiFilePath
,
abi
,
0
o600
);
err
!=
nil
{
return
""
,
""
,
fmt
.
Errorf
(
"error writing %s's ABI file: %w"
,
contractName
,
err
)
}
bytecodeFilePath
:=
path
.
Join
(
tempDirPath
,
contractName
+
".bin"
)
if
err
:=
os
.
WriteFile
(
bytecodeFilePath
,
bytecode
,
0
o600
);
err
!=
nil
{
return
""
,
""
,
fmt
.
Errorf
(
"error writing %s's bytecode file: %w"
,
contractName
,
err
)
}
return
abiFilePath
,
bytecodeFilePath
,
nil
}
// genContractBindings generates Go bindings for an Ethereum contract using
// the provided ABI and bytecode files. The bindings are generated using the
// `abigen` tool and are written to the specified Go package directory. The
// generated file's name is based on the provided contract name and will have
// a ".go" extension. The generated bindings will be part of the provided Go
// package.
//
// Parameters:
// - logger: An instance of go-ethereum/log
// - abiFilePath: The path to the ABI file for the contract.
// - bytecodeFilePath: The path to the bytecode file for the contract.
// - goPackageName: The name of the Go package where the bindings will be written.
// - contractName: The name of the contract, used for naming the output file and
// defining the type in the generated bindings.
//
// Returns:
// - An error if there's an issue during any step of the binding generation process,
// nil otherwise.
//
// Note: This function relies on the external `abigen` tool, which should be
// installed and available in the system's PATH.
func
genContractBindings
(
logger
log
.
Logger
,
abiFilePath
,
bytecodeFilePath
,
goPackageName
,
contractName
string
)
error
{
cwd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
return
fmt
.
Errorf
(
"error getting cwd: %w"
,
err
)
}
outFilePath
:=
path
.
Join
(
cwd
,
goPackageName
,
strings
.
ToLower
(
contractName
)
+
".go"
)
logger
.
Debug
(
"Generating contract bindings"
,
"contractName"
,
contractName
,
"outFilePath"
,
outFilePath
)
cmd
:=
exec
.
Command
(
"abigen"
,
"--abi"
,
abiFilePath
,
"--bin"
,
bytecodeFilePath
,
"--pkg"
,
goPackageName
,
"--type"
,
contractName
,
"--out"
,
outFilePath
)
cmd
.
Stdout
=
os
.
Stdout
if
err
:=
cmd
.
Run
();
err
!=
nil
{
return
fmt
.
Errorf
(
"error running abigen for %s: %w"
,
contractName
,
err
)
}
return
nil
}
op-bindings/gen/main.go
deleted
100644 → 0
View file @
5fe703da
package
main
import
(
"encoding/json"
"errors"
"flag"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
"github.com/ethereum-optimism/optimism/op-bindings/ast"
"github.com/ethereum-optimism/optimism/op-bindings/foundry"
)
type
flags
struct
{
ForgeArtifacts
string
Contracts
string
SourceMaps
string
OutDir
string
Package
string
MonorepoBase
string
}
type
data
struct
{
Name
string
StorageLayout
string
DeployedBin
string
Package
string
DeployedSourceMap
string
HasImmutableReferences
bool
}
func
main
()
{
var
f
flags
flag
.
StringVar
(
&
f
.
ForgeArtifacts
,
"forge-artifacts"
,
""
,
"Forge artifacts directory, to load sourcemaps from, if available"
)
flag
.
StringVar
(
&
f
.
OutDir
,
"out"
,
""
,
"Output directory to put code in"
)
flag
.
StringVar
(
&
f
.
Contracts
,
"contracts"
,
"artifacts.json"
,
"Path to file containing list of contracts to generate bindings for"
)
flag
.
StringVar
(
&
f
.
SourceMaps
,
"source-maps"
,
""
,
"Comma-separated list of contracts to generate source-maps for"
)
flag
.
StringVar
(
&
f
.
Package
,
"package"
,
"artifacts"
,
"Go package name"
)
flag
.
StringVar
(
&
f
.
MonorepoBase
,
"monorepo-base"
,
""
,
"Base of the monorepo"
)
flag
.
Parse
()
if
f
.
MonorepoBase
==
""
{
log
.
Fatal
(
"must provide -monorepo-base"
)
}
log
.
Printf
(
"Using monorepo base %s
\n
"
,
f
.
MonorepoBase
)
contractData
,
err
:=
os
.
ReadFile
(
f
.
Contracts
)
if
err
!=
nil
{
log
.
Fatal
(
"error reading contract list: %w
\n
"
,
err
)
}
contracts
:=
[]
string
{}
if
err
:=
json
.
Unmarshal
(
contractData
,
&
contracts
);
err
!=
nil
{
log
.
Fatal
(
"error parsing contract list: %w
\n
"
,
err
)
}
sourceMaps
:=
strings
.
Split
(
f
.
SourceMaps
,
","
)
sourceMapsSet
:=
make
(
map
[
string
]
struct
{})
for
_
,
k
:=
range
sourceMaps
{
sourceMapsSet
[
k
]
=
struct
{}{}
}
if
len
(
contracts
)
==
0
{
log
.
Fatalf
(
"must define a list of contracts"
)
}
t
:=
template
.
Must
(
template
.
New
(
"artifact"
)
.
Parse
(
tmpl
))
// Make a temp dir to hold all the inputs for abigen
dir
,
err
:=
os
.
MkdirTemp
(
""
,
"op-bindings"
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
log
.
Printf
(
"Using package %s
\n
"
,
f
.
Package
)
defer
os
.
RemoveAll
(
dir
)
log
.
Printf
(
"created temp dir %s
\n
"
,
dir
)
// If some contracts have the same name then the path to their
// artifact depends on their full import path. Scan over all artifacts
// and hold a mapping from the contract name to the contract path.
// Walk walks the directory deterministically, so the later instance
// of the contract with the same name will be used
re
:=
regexp
.
MustCompile
(
`\.\d+\.\d+\.\d+`
)
artifactPaths
:=
make
(
map
[
string
]
string
)
if
err
:=
filepath
.
Walk
(
f
.
ForgeArtifacts
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
if
err
!=
nil
{
return
err
}
if
strings
.
HasSuffix
(
path
,
".json"
)
{
base
:=
filepath
.
Base
(
path
)
name
:=
strings
.
TrimSuffix
(
base
,
".json"
)
// remove the compiler version from the name
sanitized
:=
re
.
ReplaceAllString
(
name
,
""
)
if
_
,
ok
:=
artifactPaths
[
sanitized
];
!
ok
{
artifactPaths
[
sanitized
]
=
path
}
}
return
nil
});
err
!=
nil
{
log
.
Fatal
(
err
)
}
for
_
,
name
:=
range
contracts
{
log
.
Printf
(
"generating code for %s
\n
"
,
name
)
artifactPath
:=
path
.
Join
(
f
.
ForgeArtifacts
,
name
+
".sol"
,
name
+
".json"
)
forgeArtifactData
,
err
:=
os
.
ReadFile
(
artifactPath
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
log
.
Printf
(
"cannot find forge-artifact for %s at standard path %s, trying %s
\n
"
,
name
,
artifactPath
,
artifactPaths
[
name
])
artifactPath
=
artifactPaths
[
name
]
forgeArtifactData
,
err
=
os
.
ReadFile
(
artifactPath
)
if
errors
.
Is
(
err
,
os
.
ErrNotExist
)
{
log
.
Fatalf
(
"cannot find forge-artifact of %q
\n
"
,
name
)
}
}
log
.
Printf
(
"using forge-artifact %s
\n
"
,
artifactPath
)
var
artifact
foundry
.
Artifact
if
err
:=
json
.
Unmarshal
(
forgeArtifactData
,
&
artifact
);
err
!=
nil
{
log
.
Fatalf
(
"failed to parse forge artifact of %q: %v
\n
"
,
name
,
err
)
}
rawAbi
:=
artifact
.
Abi
if
err
!=
nil
{
log
.
Fatalf
(
"error marshaling abi: %v
\n
"
,
err
)
}
abiFile
:=
path
.
Join
(
dir
,
name
+
".abi"
)
if
err
:=
os
.
WriteFile
(
abiFile
,
rawAbi
,
0
o600
);
err
!=
nil
{
log
.
Fatalf
(
"error writing file: %v
\n
"
,
err
)
}
rawBytecode
:=
artifact
.
Bytecode
.
Object
.
String
()
if
err
!=
nil
{
log
.
Fatalf
(
"error marshaling bytecode: %v
\n
"
,
err
)
}
bytecodeFile
:=
path
.
Join
(
dir
,
name
+
".bin"
)
if
err
:=
os
.
WriteFile
(
bytecodeFile
,
[]
byte
(
rawBytecode
),
0
o600
);
err
!=
nil
{
log
.
Fatalf
(
"error writing file: %v
\n
"
,
err
)
}
cwd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
log
.
Fatalf
(
"error getting cwd: %v
\n
"
,
err
)
}
lowerName
:=
strings
.
ToLower
(
name
)
outFile
:=
path
.
Join
(
cwd
,
f
.
Package
,
lowerName
+
".go"
)
cmd
:=
exec
.
Command
(
"abigen"
,
"--abi"
,
abiFile
,
"--bin"
,
bytecodeFile
,
"--pkg"
,
f
.
Package
,
"--type"
,
name
,
"--out"
,
outFile
)
cmd
.
Stdout
=
os
.
Stdout
if
err
:=
cmd
.
Run
();
err
!=
nil
{
log
.
Fatalf
(
"error running abigen: %v
\n
"
,
err
)
}
storage
:=
artifact
.
StorageLayout
canonicalStorage
:=
ast
.
CanonicalizeASTIDs
(
&
storage
,
f
.
MonorepoBase
)
ser
,
err
:=
json
.
Marshal
(
canonicalStorage
)
if
err
!=
nil
{
log
.
Fatalf
(
"error marshaling storage: %v
\n
"
,
err
)
}
serStr
:=
strings
.
Replace
(
string
(
ser
),
"
\"
"
,
"
\\\"
"
,
-
1
)
deployedSourceMap
:=
""
if
_
,
ok
:=
sourceMapsSet
[
name
];
ok
{
deployedSourceMap
=
artifact
.
DeployedBytecode
.
SourceMap
}
re
:=
regexp
.
MustCompile
(
`\s+`
)
immutableRefs
,
err
:=
json
.
Marshal
(
re
.
ReplaceAllString
(
string
(
artifact
.
DeployedBytecode
.
ImmutableReferences
),
""
))
if
err
!=
nil
{
log
.
Fatalf
(
"error marshaling immutable references: %v
\n
"
,
err
)
}
hasImmutables
:=
string
(
immutableRefs
)
!=
`""`
d
:=
data
{
Name
:
name
,
StorageLayout
:
serStr
,
DeployedBin
:
artifact
.
DeployedBytecode
.
Object
.
String
(),
Package
:
f
.
Package
,
DeployedSourceMap
:
deployedSourceMap
,
HasImmutableReferences
:
hasImmutables
,
}
fname
:=
filepath
.
Join
(
f
.
OutDir
,
strings
.
ToLower
(
name
)
+
"_more.go"
)
outfile
,
err
:=
os
.
OpenFile
(
fname
,
os
.
O_RDWR
|
os
.
O_CREATE
|
os
.
O_TRUNC
,
0
o600
,
)
if
err
!=
nil
{
log
.
Fatalf
(
"error opening %s: %v
\n
"
,
fname
,
err
)
}
if
err
:=
t
.
Execute
(
outfile
,
d
);
err
!=
nil
{
log
.
Fatalf
(
"error writing template %s: %v"
,
outfile
.
Name
(),
err
)
}
outfile
.
Close
()
log
.
Printf
(
"wrote file %s
\n
"
,
outfile
.
Name
())
}
}
var
tmpl
=
`// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
import (
"encoding/json"
"github.com/ethereum-optimism/optimism/op-bindings/solc"
)
const {{.Name}}StorageLayoutJSON = "{{.StorageLayout}}"
var {{.Name}}StorageLayout = new(solc.StorageLayout)
var {{.Name}}DeployedBin = "{{.DeployedBin}}"
{{if .DeployedSourceMap}}
var {{.Name}}DeployedSourceMap = "{{.DeployedSourceMap}}"
{{end}}
func init() {
if err := json.Unmarshal([]byte({{.Name}}StorageLayoutJSON), {{.Name}}StorageLayout); err != nil {
panic(err)
}
layouts["{{.Name}}"] = {{.Name}}StorageLayout
deployedBytecodes["{{.Name}}"] = {{.Name}}DeployedBin
immutableReferences["{{.Name}}"] = {{.HasImmutableReferences}}
}
`
op-e2e/config/init.go
View file @
e4536f9e
...
...
@@ -49,7 +49,7 @@ func init() {
if
err
!=
nil
{
panic
(
err
)
}
root
,
err
:=
f
indMonorepoRoot
(
cwd
)
root
,
err
:=
F
indMonorepoRoot
(
cwd
)
if
err
!=
nil
{
panic
(
err
)
}
...
...
@@ -159,9 +159,9 @@ func allExist(filenames ...string) error {
return
nil
}
//
f
indMonorepoRoot will recursively search upwards for a go.mod file.
//
F
indMonorepoRoot will recursively search upwards for a go.mod file.
// This depends on the structure of the monorepo having a go.mod file at the root.
func
f
indMonorepoRoot
(
startDir
string
)
(
string
,
error
)
{
func
F
indMonorepoRoot
(
startDir
string
)
(
string
,
error
)
{
dir
,
err
:=
filepath
.
Abs
(
startDir
)
if
err
!=
nil
{
return
""
,
err
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment