lib/eth/solidity.rb
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require "open3"
# Provides the {Eth} module.
module Eth
# Class to create {Solidity} compiler bingings for Ruby.
class Solidity
# Provides a Compiler Error in case the contract does not compile.
class CompilerError < StandardError; end
# Solidity compiler binary path.
attr_reader :compiler
# Instantiates a Solidity `solc` system compiler binding that can be
# used to compile Solidity contracts.
#
# @param path [String] optional override of the solidity compiler path.
def initialize(path = nil)
# Currently only supports `solc`. Try to override with `path`.
solc = path || get_compiler_path
raise SystemCallError, "Unable to find the solc compiler path!" if solc.nil?
@compiler = solc
end
# Use the bound Solidity executable to compile the given contract.
#
# @param contract [String] path of the contract to compile.
# @return [Array] JSON containing the compiled contract and ABI for all contracts.
def compile(contract)
raise Errno::ENOENT, "Contract file not found: #{contract}" unless File.exist? contract
flag_opt = "--optimize"
flag_ir = "--via-ir"
flag_json = "--combined-json=bin,abi"
path = File.realpath contract
output, error, status = Open3.capture3 @compiler, flag_opt, flag_ir, flag_json, path
raise SystemCallError, "Unable to run solc compiler!" if status.exitstatus === 127
raise CompilerError, error unless status.success?
json = JSON.parse output
result = {}
json["contracts"].each do |key, value|
_file, name = key.split ":"
result[name] = {}
result[name]["abi"] = value["abi"]
result[name]["bin"] = value["bin"]
end
return result
end
private
# Tries to find a system executable path for the given compiler binary name.
def get_compiler_path(name = "solc")
extensions = [""]
extensions = ENV["PATHEXT"].split(";") unless ENV["PATHEXT"].nil?
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
extensions.each do |ext|
executable = File.join path, "#{name}#{ext}"
return executable if File.executable? executable and !File.directory? executable
end
end
return nil
end
end
end