diff --git a/packages/ruby/README.md b/packages/ruby/README.md index cef4b03..1218ce8 100644 --- a/packages/ruby/README.md +++ b/packages/ruby/README.md @@ -1,6 +1,7 @@ # upsideos_evm_rwa_artifacts -ABIs and verification sources for the audited EVM Security Token releases. +ABIs, creation code and verification sources for the audited EVM Security +Token releases. The gem carries data only. It has no dependencies, and it ships the same files as the `@upsideos/evm-rwa-artifacts` npm package, built from the @@ -36,6 +37,17 @@ call surface when you talk to the token address: UpsideosEvmRwaArtifacts.merged_abi('v5', 'RestrictedLockupToken') ``` +## Deploy a contract + +`artifact` returns the ABI together with the creation code, in the shape a +deployment front end reads. `bytecode` returns the creation code alone, as +the 0x-prefixed hex a deployment transaction carries: + +```ruby +UpsideosEvmRwaArtifacts.artifact('v5.1', 'RestrictedLockupToken') +UpsideosEvmRwaArtifacts.bytecode('v5.1', 'RestrictedLockupToken') +``` + ## Verify a deployment `verification_source_codes` returns the solc standard-json bundle that an @@ -69,9 +81,8 @@ Digest::SHA256.hexdigest(path.binread) ## What the gem leaves out -The gem ships no bytecode and no tooling, because a Ruby consumer reads -contracts and verifies them but does not compile or deploy them. Use the -npm package for deployment bytecode, and `@upsideos/evm-explorer-verify` +The gem ships no tooling, because a Ruby consumer reads contracts and +hands them on but does not compile them. Use `@upsideos/evm-explorer-verify` to submit a verification to an explorer. ## Notes diff --git a/packages/ruby/lib/upsideos_evm_rwa_artifacts.rb b/packages/ruby/lib/upsideos_evm_rwa_artifacts.rb index a1c695b..2284504 100644 --- a/packages/ruby/lib/upsideos_evm_rwa_artifacts.rb +++ b/packages/ruby/lib/upsideos_evm_rwa_artifacts.rb @@ -5,7 +5,8 @@ require_relative 'upsideos_evm_rwa_artifacts/version' -# ABIs and verification sources for audited EVM contract releases. +# ABIs, creation code and verification sources for audited EVM contract +# releases. # # The gem carries data only. It has no dependencies and runs no code at # load time beyond reading the release directory. @@ -54,6 +55,12 @@ def abi_path(release, contract) path(release, "abi/#{contract}.json") end + # The ABI together with the creation code, in the shape a deployment + # front end reads: abi, bytecode, deployedBytecode, immutableReferences. + def artifact_path(release, contract) + path(release, "artifacts/#{contract}.json") + end + # The token forwards unknown selectors into its extensions, so a caller # that talks to the token address needs the merged call surface rather # than the token ABI alone. @@ -77,6 +84,16 @@ def merged_abi(release, contract) read_json(merged_abi_path(release, contract)) end + def artifact(release, contract) + read_json(artifact_path(release, contract)) + end + + # The creation code, as the 0x-prefixed hex a deployment transaction + # carries. Constructor arguments append to it. + def bytecode(release, contract) + artifact(release, contract).fetch('bytecode') + end + def verification_source_codes(release) read_json(verification_source_codes_path(release)) end diff --git a/packages/ruby/lib/upsideos_evm_rwa_artifacts/version.rb b/packages/ruby/lib/upsideos_evm_rwa_artifacts/version.rb index 2ab55c5..73c6e40 100644 --- a/packages/ruby/lib/upsideos_evm_rwa_artifacts/version.rb +++ b/packages/ruby/lib/upsideos_evm_rwa_artifacts/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module UpsideosEvmRwaArtifacts - VERSION = '0.1.1' + VERSION = '0.2.0' end diff --git a/packages/ruby/script/prepare_data.rb b/packages/ruby/script/prepare_data.rb index 4c534e3..886df38 100644 --- a/packages/ruby/script/prepare_data.rb +++ b/packages/ruby/script/prepare_data.rb @@ -3,9 +3,9 @@ # Copy the data the gem ships out of the release directories. # -# The gem carries ABIs, manifests and verification sources. It leaves out -# the bytecode in artifacts/, because a Ruby consumer reads contracts and -# verifies them but does not deploy them. +# The gem carries ABIs, artifacts, manifests and verification sources. A +# Ruby consumer hands the creation code to a browser that deploys the set, +# so the artifacts come along with the ABIs. require 'fileutils' require 'pathname' @@ -29,17 +29,17 @@ def copy_release(release) FileUtils.cp(source.join(file), target.join(file)) end - FileUtils.mkdir_p(target.join('abi')) - Dir[source.join('abi', '*.json')].each do |abi| - FileUtils.cp(abi, target.join('abi', File.basename(abi))) - end + copy_json_dir(source.join('abi'), target.join('abi')) + copy_json_dir(source.join('artifacts'), target.join('artifacts')) + copy_json_dir(source.join('abi', 'merged'), target.join('abi', 'merged')) +end - merged = source.join('abi', 'merged') - return unless merged.directory? +def copy_json_dir(source, target) + return unless source.directory? - FileUtils.mkdir_p(target.join('abi', 'merged')) - Dir[merged.join('*.json')].each do |abi| - FileUtils.cp(abi, target.join('abi', 'merged', File.basename(abi))) + FileUtils.mkdir_p(target) + Dir[source.join('*.json')].each do |file| + FileUtils.cp(file, target.join(File.basename(file))) end end diff --git a/packages/ruby/test/upsideos_evm_rwa_artifacts_test.rb b/packages/ruby/test/upsideos_evm_rwa_artifacts_test.rb index 1c7ec43..8b05b34 100644 --- a/packages/ruby/test/upsideos_evm_rwa_artifacts_test.rb +++ b/packages/ruby/test/upsideos_evm_rwa_artifacts_test.rb @@ -85,9 +85,35 @@ def test_unknown_contract assert_raises(UpsideosEvmRwaArtifacts::UnknownFile) { UpsideosEvmRwaArtifacts.abi('v5', 'NoSuchContract') } end - # The gem is data only. Bytecode belongs to the npm package, because a - # Ruby consumer reads and verifies contracts but does not deploy them. - def test_no_bytecode_ships - assert_empty Dir[UpsideosEvmRwaArtifacts::DATA_ROOT.join('**', 'artifacts', '*')] + def test_bytecode_is_prefixed_hex + bytecode = UpsideosEvmRwaArtifacts.bytecode('v5', 'AccessControl') + + assert_match(/\A0x[0-9a-f]+\z/, bytecode) + end + + def test_artifact_carries_the_same_abi_as_the_abi_file + artifact = UpsideosEvmRwaArtifacts.artifact('v5.1', 'RestrictedLockupToken') + + assert_equal UpsideosEvmRwaArtifacts.abi('v5.1', 'RestrictedLockupToken'), artifact.fetch('abi') + end + + # The manifest digests the creation code as raw bytes, so a truncated or + # re-encoded copy in the gem shows up here. + def test_shipped_creation_code_matches_the_manifest_digests + UpsideosEvmRwaArtifacts.releases.each do |release| + UpsideosEvmRwaArtifacts.manifest(release).fetch('artifacts').each do |entry| + name = entry.fetch('name') + creation = entry.fetch('creationCode') + bytes = [UpsideosEvmRwaArtifacts.bytecode(release, name).delete_prefix('0x')].pack('H*') + + assert_equal creation.fetch('length'), bytes.length, "#{release} #{name} creation code length" + assert_equal creation.fetch('sha256'), Digest::SHA256.hexdigest(bytes), + "#{release} #{name} creation code digest" + end + end + end + + def test_unknown_contract_artifact + assert_raises(UpsideosEvmRwaArtifacts::UnknownFile) { UpsideosEvmRwaArtifacts.artifact('v5', 'NoSuchContract') } end end