Showing preview only (7,928K chars total). Download the full file or copy to clipboard to get everything.
Repository: q9f/eth.rb
Branch: main
Commit: 87cafff8254b
Files: 136
Total size: 7.4 MB
Directory structure:
gitextract_urvyeq_7/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── codeql.yml
│ ├── docs.yml
│ └── spec.yml
├── .gitignore
├── .gitmodules
├── .yardopts
├── AUTHORS.txt
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── SECURITY.md
├── abi/
│ ├── ens_registry.json
│ └── ens_resolver.json
├── bin/
│ ├── console
│ └── setup
├── codecov.yml
├── eth.gemspec
├── lib/
│ ├── eth/
│ │ ├── abi/
│ │ │ ├── decoder.rb
│ │ │ ├── encoder.rb
│ │ │ ├── event.rb
│ │ │ ├── function.rb
│ │ │ ├── packed/
│ │ │ │ └── encoder.rb
│ │ │ └── type.rb
│ │ ├── abi.rb
│ │ ├── address.rb
│ │ ├── api.rb
│ │ ├── bls.rb
│ │ ├── chain.rb
│ │ ├── client/
│ │ │ ├── http.rb
│ │ │ ├── ipc.rb
│ │ │ └── ws.rb
│ │ ├── client.rb
│ │ ├── constant.rb
│ │ ├── contract/
│ │ │ ├── error.rb
│ │ │ ├── event.rb
│ │ │ ├── function.rb
│ │ │ ├── function_input.rb
│ │ │ ├── function_output.rb
│ │ │ └── initializer.rb
│ │ ├── contract.rb
│ │ ├── eip712.rb
│ │ ├── ens/
│ │ │ ├── coin_type.rb
│ │ │ └── resolver.rb
│ │ ├── ens.rb
│ │ ├── key/
│ │ │ ├── decrypter.rb
│ │ │ └── encrypter.rb
│ │ ├── key.rb
│ │ ├── rlp/
│ │ │ ├── decoder.rb
│ │ │ ├── encoder.rb
│ │ │ ├── sedes/
│ │ │ │ ├── big_endian_int.rb
│ │ │ │ ├── binary.rb
│ │ │ │ └── list.rb
│ │ │ └── sedes.rb
│ │ ├── rlp.rb
│ │ ├── signature.rb
│ │ ├── solidity.rb
│ │ ├── tx/
│ │ │ ├── eip1559.rb
│ │ │ ├── eip2930.rb
│ │ │ ├── eip4844.rb
│ │ │ ├── eip7702.rb
│ │ │ └── legacy.rb
│ │ ├── tx.rb
│ │ ├── unit.rb
│ │ ├── util.rb
│ │ └── version.rb
│ └── eth.rb
└── spec/
├── eth/
│ ├── abi/
│ │ ├── decoder_spec.rb
│ │ ├── encoder_spec.rb
│ │ ├── event_spec.rb
│ │ ├── function_spec.rb
│ │ ├── packed/
│ │ │ └── encoder_spec.rb
│ │ └── type_spec.rb
│ ├── abi_spec.rb
│ ├── address_spec.rb
│ ├── bls_spec.rb
│ ├── chain_spec.rb
│ ├── client/
│ │ └── ws_spec.rb
│ ├── client_spec.rb
│ ├── constant_spec.rb
│ ├── contract/
│ │ ├── error_spec.rb
│ │ ├── event_spec.rb
│ │ ├── function_input_spec.rb
│ │ ├── function_output_spec.rb
│ │ ├── function_spec.rb
│ │ └── initializer_spec.rb
│ ├── contract_spec.rb
│ ├── eip712_spec.rb
│ ├── ens/
│ │ ├── coin_type_spec.rb
│ │ └── resolver_spec.rb
│ ├── ens_spec.rb
│ ├── key/
│ │ ├── decrypter_spec.rb
│ │ └── encrypter_spec.rb
│ ├── key_spec.rb
│ ├── rlp/
│ │ ├── sedes/
│ │ │ ├── big_endian_int_spec.rb
│ │ │ ├── binary_spec.rb
│ │ │ └── list_spec.rb
│ │ └── sedes_spec.rb
│ ├── rlp_spec.rb
│ ├── signature_spec.rb
│ ├── solidity_spec.rb
│ ├── tx/
│ │ ├── eip1559_spec.rb
│ │ ├── eip2930_spec.rb
│ │ ├── eip4844_spec.rb
│ │ ├── eip7702_spec.rb
│ │ └── legacy_spec.rb
│ ├── tx_spec.rb
│ ├── unit_spec.rb
│ └── util_spec.rb
├── eth_spec.rb
├── fixtures/
│ ├── abi/
│ │ ├── ENSRegistryWithFallback.json
│ │ ├── ERC1155.json
│ │ ├── ERC20.json
│ │ ├── ERC721.json
│ │ ├── Tuple.json
│ │ ├── Tuple2.json
│ │ └── ethers.json
│ ├── contracts/
│ │ ├── address_storage.sol
│ │ ├── deposit.sol
│ │ ├── dummy.sol
│ │ ├── erc20.sol
│ │ ├── error.sol
│ │ ├── greeter.sol
│ │ ├── signer.sol
│ │ ├── simple_registry.sol
│ │ ├── test_contract.sol
│ │ ├── tuple.sol
│ │ └── tuple2.sol
│ └── keys/
│ ├── testingtesting.json
│ ├── testpassword.json
│ └── testunknownkdf.json
└── spec_helper.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
---
updates:
-
directory: /
labels:
- dependencies
package-ecosystem: bundler
schedule:
interval: weekly
versioning-strategy: increase
-
directory: /
labels:
- operations
package-ecosystem: github-actions
schedule:
interval: monthly
version: 2
================================================
FILE: .github/workflows/codeql.yml
================================================
---
name: CodeQL
on:
pull_request:
branches:
- main
push:
branches:
- main
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language:
- ruby
steps:
- name: "Checkout repository"
uses: actions/checkout@v6
- name: "Initialize CodeQL"
uses: github/codeql-action/init@v4
with:
languages: "${{ matrix.language }}"
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: "Perform CodeQL Analysis"
uses: github/codeql-action/analyze@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true
- name: "Run rufo code formatting checks"
run: |
gem install rufo
rufo --check ./lib
rufo --check ./spec
- name: "Run yard documentation checks"
run: |
gem install yard
yard doc --fail-on-warning
================================================
FILE: .github/workflows/docs.yml
================================================
---
name: Docs
on:
push:
branches:
- main
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true
- name: Run Yard Doc
run: |
gem install yard
yard doc
- name: Deploy GH Pages
uses: JamesIves/github-pages-deploy-action@v4.8.0
with:
branch: gh-pages
folder: doc/
================================================
FILE: .github/workflows/spec.yml
================================================
---
name: Spec
on:
pull_request:
branches:
- main
push:
branches:
- main
schedule:
-
cron: "45 3 * * *"
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
ruby: ['3.4', '4.0']
steps:
- uses: actions/checkout@v6
- name: MacOs Dependencies
if: matrix.os == 'macos-latest'
run: |
brew update
brew tap ethereum/ethereum
brew install --verbose autoconf automake libtool pkg-config autogen geth solidity
- name: Ubuntu Dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install -y autoconf automake libtool pkg-config geth solc
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run Geth
run: |
geth --dev \
--http \
--ws \
--ipcpath /tmp/geth.ipc \
>/tmp/geth.log 2>&1 &
echo $! > /tmp/geth.pid
sleep 10
- name: Gem Dependencies
run: |
git submodule update --init --recursive
- name: Run Tests
run: |
bundle exec rspec
- name: Stop Geth
if: always()
run: |
if [ -f /tmp/geth.pid ]; then
kill "$(cat /tmp/geth.pid)" 2>/dev/null || true
fi
- name: Geth Logs (on failure)
if: failure()
run: |
if [ -f /tmp/geth.log ]; then
echo "===== geth log ====="
tail -n 200 /tmp/geth.log
fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
================================================
FILE: .gitignore
================================================
*.DS_Store
*.o
*.so
*.gem
*.log
*.rbc
.config
.rake_tasks~
coverage/
InstalledFiles
pkg/
tmp/
# RSpec configuration and generated files:
.rspec
spec/examples.txt
# Documentation cache and generated files:
.yardoc/
_yardoc/
doc/
rdoc/
# Environment normalization:
.bundle/
vendor/bundle/*
!vendor/bundle/.keep
lib/bundler/man/
# For a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
Gemfile.lock
.ruby-version
.ruby-gemset
# Unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
# NodeJS stuff
node_modules
package.json
package-lock.json
================================================
FILE: .gitmodules
================================================
[submodule "spec/fixtures/ethereum/tests"]
path = spec/fixtures/ethereum/tests
url = https://github.com/ethereum/tests.git
================================================
FILE: .yardopts
================================================
--verbose --fail-on-warning --markup markdown --embed-mixins
================================================
FILE: AUTHORS.txt
================================================
The Ruby-Eth Contributors are:
* Steve Ellis @se3000
* Afri Schoedon @q9f
* John Omar @chainoperator
* Joshua Peek @josh
* Yuta Kurotaki @kurotaky
See also:
* https://github.com/q9f/eth.rb/graphs/contributors
The Ruby-Eth project was maintained 2016-2020 in Steve Ellis's (@se3000)
repository licensed under MIT conditions:
* https://github.com/se3000/ruby-eth
The latest Ruby-Eth gem is not only a rewrite of the aforementioned gem
but also a partial merge of the Ethereum.rb Ruby Ethereum library by
Marek Kirejczyk (@marekkirejczyk) and Yuta Kurotaki (@kurotaky) licensed
under MIT conditions:
* https://github.com/EthWorks/ethereum.rb
The latest version of the Ruby-Eth gem includes a revised version of the
ABI gem by Jan Xie (@janx) and Zhang Yaning (@u2) licensed under MIT
conditions:
* https://github.com/cryptape/ruby-ethereum-abi
The latest version of the Ruby-Eth gem contains a condensed version of the
RLP gem by Jan Xie (@janx) and Zhang Yaning (@u2) licensed under MIT
conditions:
* https://github.com/cryptape/ruby-rlp
================================================
FILE: CHANGELOG.md
================================================
# Change Log
All notable changes to this project will be documented in this file.
## [0.5.15]
### Added
* Implement EIP712 array encoding [#361](https://github.com/q9f/eth.rb/pull/361)
* Support nested dynamic arrays in ABI [#356](https://github.com/q9f/eth.rb/pull/356)
* Allow signing transactions with external signatures [#349](https://github.com/q9f/eth.rb/pull/349)
* Feat: add eip-4844 transactions [#345](https://github.com/q9f/eth.rb/pull/345)
* Support Solidity custom errors per ERC-6093 [#344](https://github.com/q9f/eth.rb/pull/344)
* Allow to use chains with id > 4294967295 [#337](https://github.com/q9f/eth.rb/pull/337)
### Changed
* Harden ABI type parsing [#358](https://github.com/q9f/eth.rb/pull/358)
* Ensure ABI decoder rejects ZST offsets [#359](https://github.com/q9f/eth.rb/pull/359)
* Test: decode eip4844 blobs [#360](https://github.com/q9f/eth.rb/pull/360)
* Abi: decode transaction input [#354](https://github.com/q9f/eth.rb/pull/354)
* Fix tuple output decoding for contract calls [#353](https://github.com/q9f/eth.rb/pull/353)
* Add comprehensive Tx module tests [#352](https://github.com/q9f/eth.rb/pull/352)
* Move error decoding to contract module [#350](https://github.com/q9f/eth.rb/pull/350)
* Enforce minimal RLP integer decoding [#351](https://github.com/q9f/eth.rb/pull/351)
* Fix tuple size calculation without components [#348](https://github.com/q9f/eth.rb/pull/348)
* Docs: update readme [#347](https://github.com/q9f/eth.rb/pull/347)
* Chore: update development dependencies [#346](https://github.com/q9f/eth.rb/pull/346)
* Handle hex string inputs in big-endian conversion [#343](https://github.com/q9f/eth.rb/pull/343)
* Handle uppercase hex prefixes [#339](https://github.com/q9f/eth.rb/pull/339)
* Add methods to encode function call and decode its result [#334](https://github.com/q9f/eth.rb/pull/334)
* Docs(util): fix hex? return type [#342](https://github.com/q9f/eth.rb/pull/342)
* Handle hex input consistently in int_to_big_endian [#341](https://github.com/q9f/eth.rb/pull/341)
* Fix receiver option spelling [#340](https://github.com/q9f/eth.rb/pull/340)
* Chore: bump version to 0.5.15 [#333](https://github.com/q9f/eth.rb/pull/333)
## [0.5.14]
### Added
* Add ability to decode event parameters using ABI reference. [#328](https://github.com/q9f/eth.rb/pull/328)
* Add support for EIP-7702 transactions [#320](https://github.com/q9f/eth.rb/pull/320)
* Eth/abi: implement packed encoder [#310](https://github.com/q9f/eth.rb/pull/310)
### Changed
* Chore: run rufo, add docs [#332](https://github.com/q9f/eth.rb/pull/332)
* Spec/client: fix nonce too low error handling in spec [#331](https://github.com/q9f/eth.rb/pull/331)
* Move the tests that are failing due to a geth upgrade to pending [#330](https://github.com/q9f/eth.rb/pull/330)
* Spec/client: don't require any rpc api token for tests [#326](https://github.com/q9f/eth.rb/pull/326)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.7.2 to 4.7.3 [#327](https://github.com/q9f/eth.rb/pull/327)
* Eth/eip712: prepare tests for packed encoding [#216](https://github.com/q9f/eth.rb/pull/216)
* Spec/solidity: mute system call output [#319](https://github.com/q9f/eth.rb/pull/319)
* Updated nesting of describe blocks in the EIP-1559 spec. [#318](https://github.com/q9f/eth.rb/pull/318)
* Update README.md [#317](https://github.com/q9f/eth.rb/pull/317)
* Docs: update README.md [#314](https://github.com/q9f/eth.rb/pull/314)
* Gem: update copyright headers [#312](https://github.com/q9f/eth.rb/pull/312)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.7.1 to 4.7.2 [#309](https://github.com/q9f/eth.rb/pull/309)
* Spec: switch from infura to drpc [#308](https://github.com/q9f/eth.rb/pull/308)
* Ci: update ruby version [#307](https://github.com/q9f/eth.rb/pull/307)
* Gem: bump version to 0.5.14 [#305](https://github.com/q9f/eth.rb/pull/305)
* Docs: update changelog [#304](https://github.com/q9f/eth.rb/pull/304)
## [0.5.13]
### Changed
* Eth/api: update to latest available go-ethereum apis [#301](https://github.com/q9f/eth.rb/pull/301)
* Eth/chain: update ids [#300](https://github.com/q9f/eth.rb/pull/300)
* Spec: update ethereum/tests fixtures [#303](https://github.com/q9f/eth.rb/pull/303)
* Ci: fix codecov uploader [#302](https://github.com/q9f/eth.rb/pull/302)
* Eth/tx: only enforce block gas limit on mainnet [#299](https://github.com/q9f/eth.rb/pull/299)
* Eth/util: fix single-byte hex-string nibbles [#298](https://github.com/q9f/eth.rb/pull/298)
* Eth/address: rename null address to zero address [#297](https://github.com/q9f/eth.rb/pull/297)
* Eth/address: add support to check for the ethereum "null address" [#296](https://github.com/q9f/eth.rb/pull/296)
* Build(deps): bump codecov/codecov-action from 4 to 5 [#295](https://github.com/q9f/eth.rb/pull/295)
* Build(deps): bump JamesIves/github-pages-deploy-action [#294](https://github.com/q9f/eth.rb/pull/294)
* Build(deps): bump JamesIves/github-pages-deploy-action [#288](https://github.com/q9f/eth.rb/pull/288)
* Eth/client: always return hash even if transaction didn't succeed [#284](https://github.com/q9f/eth.rb/pull/284)
* Eth/chain: update list of chains [#283](https://github.com/q9f/eth.rb/pull/283)
* Fix undefined method `raise_error' for an instance of Eth::Tx::Eip1559 (NoMethodError) [#282](https://github.com/q9f/eth.rb/pull/282)
* Gem: bump version to 0.5.13 [#281](https://github.com/q9f/eth.rb/pull/281)
## [0.5.12]
### Added
* Allow to call JSON RPC with custom block number [#268](https://github.com/q9f/eth.rb/pull/268)
* Support tuple params in EventLog [#276](https://github.com/q9f/eth.rb/pull/276)
### Changed
* Eth: update version [#280](https://github.com/q9f/eth.rb/pull/280)
* Eth/abi: fix negative integer coding [#279](https://github.com/q9f/eth.rb/pull/279)
* Support negative number from JSON RPC [#267](https://github.com/q9f/eth.rb/pull/267)
* Abi/event: confirm decoding tuples works [#278](https://github.com/q9f/eth.rb/pull/278)
* Allow to call JSON RPC with custom block number [#268](https://github.com/q9f/eth.rb/pull/268)
* Gem: run rufo [#277](https://github.com/q9f/eth.rb/pull/277)
* Fix event signature [#250](https://github.com/q9f/eth.rb/pull/250)
* Support tuple params in EventLog [#276](https://github.com/q9f/eth.rb/pull/276)
* Ci: update ruby version [#271](https://github.com/q9f/eth.rb/pull/271)
* Eth/api: remove coinbase as default account [#269](https://github.com/q9f/eth.rb/pull/269)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.5.0 to 4.6.1 [#275](https://github.com/q9f/eth.rb/pull/275)
* Build(deps): bump github/codeql-action from 2 to 3 [#257](https://github.com/q9f/eth.rb/pull/257)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.3 to 4.5.0 [#256](https://github.com/q9f/eth.rb/pull/256)
* Fix typo in contract_spec.rb [#253](https://github.com/q9f/eth.rb/pull/253)
* Eth/eip721: fix data type bug for bytes, fix #251 [#252](https://github.com/q9f/eth.rb/pull/252)
* Ci: unpatch geth [#248](https://github.com/q9f/eth.rb/pull/248)
* Build(deps): bump actions/checkout from 3 to 4 [#246](https://github.com/q9f/eth.rb/pull/246)
## [0.5.11]
### Added
* Eth/abi: allow encoding address types [#242](https://github.com/q9f/eth.rb/pull/242)
* Eth/solidity: enable --via-ir [#232](https://github.com/q9f/eth.rb/pull/232)
* Checking userinfo with the uri method [#233](https://github.com/q9f/eth.rb/pull/233)
* Eth/abi: add abicoder gem tests collection [#218](https://github.com/q9f/eth.rb/pull/218)
* Manual default_account [#215](https://github.com/q9f/eth.rb/pull/215)
* Add moonbeam networks in [#209](https://github.com/q9f/eth.rb/pull/209)
### Changed
* Spec: run rufo [#245](https://github.com/q9f/eth.rb/pull/245)
* Fix the decoding of unsigned transactions [#243](https://github.com/q9f/eth.rb/pull/243)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.2 to 4.4.3 [#244](https://github.com/q9f/eth.rb/pull/244)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.1 to 4.4.2 [#240](https://github.com/q9f/eth.rb/pull/240)
* Eth/tx: update tx initcode cost for shanghai [#237](https://github.com/q9f/eth.rb/pull/237)
* Eth/client: remove default gas limit attribute [#235](https://github.com/q9f/eth.rb/pull/235)
* Docs: minor fixups [#229](https://github.com/q9f/eth.rb/pull/229)
* Eth/contract: ensure contract name is title case [#228](https://github.com/q9f/eth.rb/pull/228)
* Deps: require forwardable for contracts [#227](https://github.com/q9f/eth.rb/pull/227)
* Ens/resolver: remove pending for etc coin type [#219](https://github.com/q9f/eth.rb/pull/219)
* Deps: update secp256k1 to 6 [#214](https://github.com/q9f/eth.rb/pull/214)
* Eth/solidity: add docs for solc path override [#213](https://github.com/q9f/eth.rb/pull/213)
* Manually overwrite solc path [#212](https://github.com/q9f/eth.rb/pull/212)
* Abi.decoder handles arrays of string and bytes [#207](https://github.com/q9f/eth.rb/pull/207)
* Eth/util: fix compressed public key to address in [#206](https://github.com/q9f/eth.rb/pull/206)
* Eth/api: update execution apis to latest spec [#204](https://github.com/q9f/eth.rb/pull/204)
* Eth/abi: split abi class into encoder and decoder [#203](https://github.com/q9f/eth.rb/pull/203)
* Eth/client: deduplicate code [#202](https://github.com/q9f/eth.rb/pull/202)
* Eth/client: rewrite send to send_request [#201](https://github.com/q9f/eth.rb/pull/201)
* Docs: update changelog for 0.5.10 [#200](https://github.com/q9f/eth.rb/pull/200)
* Tested with Ruby 3.2 [#199](https://github.com/q9f/eth.rb/pull/199)
## [0.5.10]
### Added
* Eth/client: add transfer_erc20 function [#197](https://github.com/q9f/eth.rb/pull/197)
* Eth/client: add resolve_ens function [#192](https://github.com/q9f/eth.rb/pull/192)
### Changed
* Eth/ens: restore docs for normalize [#198](https://github.com/q9f/eth.rb/pull/198)
* Docs: update readme [#195](https://github.com/q9f/eth.rb/pull/195)
* Eth/contract: ensure address arrays support [#194](https://github.com/q9f/eth.rb/pull/194)
* Eth/client: do not allow accessing local accounts on remote connections [#193](https://github.com/q9f/eth.rb/pull/193)
* Eth/client: correctly select functions [#191](https://github.com/q9f/eth.rb/pull/191)
* Docs: create security policy [#190](https://github.com/q9f/eth.rb/pull/190)
* Docs: add contribution guidelines [#189](https://github.com/q9f/eth.rb/pull/189)
* Docs: add coc [#188](https://github.com/q9f/eth.rb/pull/188)
* Docs: update changelog for 0.5.9 [#187](https://github.com/q9f/eth.rb/pull/187)
## [0.5.9]
### Added
* Eth/abi: dynamic struct encoding [#135](https://github.com/q9f/eth.rb/pull/135) [#185](https://github.com/q9f/eth.rb/pull/185)
* Eth/client: support camel case (convert before sending the tx) [#172](https://github.com/q9f/eth.rb/pull/172)
* Eth/client: add `tx_succeeded?` [#173](https://github.com/q9f/eth.rb/pull/173)
### Changed
* Eth/client: raise an error if a contract interaction reverts [#186](https://github.com/q9f/eth.rb/pull/186)
* Eth/client: dup params to prevent marshalling on client obj [#184](https://github.com/q9f/eth.rb/pull/184)
* Eth/client: add test for tx_succeeded? [#183](https://github.com/q9f/eth.rb/pull/183)
* Eth: rename functions prefixed with is_ [#182](https://github.com/q9f/eth.rb/pull/182)
* Eth/chain: update available chains [#181](https://github.com/q9f/eth.rb/pull/181)
* Docs: update changelog for 0.5.8 [#180](https://github.com/q9f/eth.rb/pull/180)
* Eth: happy new 2023 [#179](https://github.com/q9f/eth.rb/pull/179)
* Docs: fix readme workflow badge [#178](https://github.com/q9f/eth.rb/pull/178)
* Solidity: sanitize the contract path before compiling [#176](https://github.com/q9f/eth.rb/pull/176)
* Ci: add libyaml on ubuntu [#175](https://github.com/q9f/eth.rb/pull/175)
## [0.5.8]
### Added
* Client: ability to manual set nonce of tx for transfer, deploy, transact methods was added. [#169](https://github.com/q9f/eth.rb/pull/169)
* Client: ability for call contract methods with specific transaction value was added [#168](https://github.com/q9f/eth.rb/pull/168)
* Client: add ENS resolve support [#150](https://github.com/q9f/eth.rb/pull/150)
### Changed
* Client: satisfy yard docs for transfer kwargs [#170](https://github.com/q9f/eth.rb/pull/170)
* Client: remove invalid parameters from call_raw method [#166](https://github.com/q9f/eth.rb/pull/166)
* Gem: bump required ruby version to 3 [#165](https://github.com/q9f/eth.rb/pull/165)
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.0 to 4.4.1 [#162](https://github.com/q9f/eth.rb/pull/162)
* Gem: bump version to 0.5.8 [#161](https://github.com/q9f/eth.rb/pull/161)
* Docs: update changelog [#160](https://github.com/q9f/eth.rb/pull/160)
## [0.5.7]
### Added
* Eth/client: add http basic support auth ([#151](https://github.com/q9f/eth.rb/pull/151))
* Chore: add polygon chain test case ([#146](https://github.com/q9f/eth.rb/pull/146))
### Changed
* Docs: add readme header for yard ([#159](https://github.com/q9f/eth.rb/pull/159))
* Eth/client: fix api documentation ([#158](https://github.com/q9f/eth.rb/pull/158))
* Eth/client: update default fees ([#157](https://github.com/q9f/eth.rb/pull/157))
* Docs: move readme usage to wiki ([#156](https://github.com/q9f/eth.rb/pull/156))
* Eth/signature: fix allowing ledger v values of 0 ([#155](https://github.com/q9f/eth.rb/pull/155))
* Eth/client: rename http basic to http auth ([#154](https://github.com/q9f/eth.rb/pull/154))
* Fix Eth:Tx.decode for transaction with s length < 64 chars ([#148](https://github.com/q9f/eth.rb/pull/148))
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.3.4 to 4.4.0 ([#140](https://github.com/q9f/eth.rb/pull/140))
* Fixed to return uint256[] correctly when passed as type ([#147](https://github.com/q9f/eth.rb/pull/147))
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.3.3 to 4.3.4 ([#133](https://github.com/q9f/eth.rb/pull/133))
* Docs: update CHANGELOG ([#132](https://github.com/q9f/eth.rb/pull/132))
* Gem: bump version to 0.5.7 ([#131](https://github.com/q9f/eth.rb/pull/131))
## [0.5.6]
### Added
- Eth/client: Add gas limit override option for contract deployments ([#128](https://github.com/q9f/eth.rb/pull/128))
- Eth/abi: support dynamic array encoding ([#122](https://github.com/q9f/eth.rb/pull/122))
### Changed
- Eth/client: Include contract constructor args when estimating intrinsic gas ([#111](https://github.com/q9f/eth.rb/pull/111))
- Eth/abi: allow parsing numerics from string inputs ([#112](https://github.com/q9f/eth.rb/pull/112))
- Eth/signature: fix prefix_message for multibyte characters ([#120](https://github.com/q9f/eth.rb/pull/120))
- Eth/abi: raise error if numeric comes as string ([#114](https://github.com/q9f/eth.rb/pull/114))
- Gem: bump version to 0.5.6 ([#130](https://github.com/q9f/eth.rb/pull/130))
## [0.5.5]
### Added
- Eth/contract: Add missing def_delegator for constructor_inputs ([#96](https://github.com/q9f/eth.rb/pull/96))
- Eth/client: Enable passing in constructor params to deploy ([#106](https://github.com/q9f/eth.rb/pull/106))
- Eth/chain: add matic/mumbai ([#107](https://github.com/q9f/eth.rb/pull/107))
### Changed
- Gem: bump version to 0.5.5 ([#89](https://github.com/q9f/eth.rb/pull/89))
- Docs: update changelog for 0.5.4 ([#90](https://github.com/q9f/eth.rb/pull/90))
- Ci: add weekly dependency checks ([#91](https://github.com/q9f/eth.rb/pull/91))
- Build(deps): bump github/codeql-action from 1 to 2 ([#92](https://github.com/q9f/eth.rb/pull/92))
- Build(deps): bump actions/checkout from 2 to 3 ([#93](https://github.com/q9f/eth.rb/pull/93))
- Build(deps): bump JamesIves/github-pages-deploy-action from 4.1.7 to 4.3.3 ([#94](https://github.com/q9f/eth.rb/pull/94))
- Eth/abi: fix handling of hex values for byte strings ([#100](https://github.com/q9f/eth.rb/pull/100))
- Eth/abi: add a testcase for handling hex and bin strings ([#101](https://github.com/q9f/eth.rb/pull/101))
- Eth/abi: Fix Eth::Abi::DecodingError in call method ([#105](https://github.com/q9f/eth.rb/pull/105))
- Eth: some docs and cleanups ([#108](https://github.com/q9f/eth.rb/pull/108))
## [0.5.4]
### Added
- Eth/client: method for eip-1271 ([#80](https://github.com/q9f/eth.rb/pull/80))
### Changed
- Docs: update changelog ([#77](https://github.com/q9f/eth.rb/pull/77))
- Gem: bump version to 0.5.4 ([#78](https://github.com/q9f/eth.rb/pull/78))
- Ci: bump ruby version to 3.1 on ci ([#79](https://github.com/q9f/eth.rb/pull/79))
- Fix typos ([#81](https://github.com/q9f/eth.rb/pull/81))
- Eth/contract: allow creating from file, abi, bin ([#83](https://github.com/q9f/eth.rb/pull/83))
- Eth/client: fix account requirement for client.call() ([#85](https://github.com/q9f/eth.rb/pull/85))
- Add dependency support for openssl 2.2 and greater, including 3.x ([#88](https://github.com/q9f/eth.rb/pull/88))
## [0.5.3]
### Added
- Smart contract support ([#68](https://github.com/q9f/eth.rb/pull/68))
### Changed
- Eth/abi: decode event log ([#69](https://github.com/q9f/eth.rb/pull/69))
- Gem: bump version ([#70](https://github.com/q9f/eth.rb/pull/70))
- Eth/abi/event: batch log decoder ([#71](https://github.com/q9f/eth.rb/pull/71))
## [0.5.2]
### Added
- Eth/solidity: add solidity compiler bindings ([#66](https://github.com/q9f/eth.rb/pull/66))
### Changed
- Eth: remove duplicated code ([#62](https://github.com/q9f/eth.rb/pull/62))
- Ci: allow coverage to drop to 99% without failing ([#63](https://github.com/q9f/eth.rb/pull/63))
- Docs: update readme ([#64](https://github.com/q9f/eth.rb/pull/64))
- Docs: add wiki to readme ([#65](https://github.com/q9f/eth.rb/pull/65))
## [0.5.1]
### Added
- Add eth::rlp module ([#52](https://github.com/q9f/eth.rb/pull/52))
- Eth/client: implement http/ipc ([#37](https://github.com/q9f/eth.rb/pull/37))
### Changed
- Docs: update changelog ([#61](https://github.com/q9f/eth.rb/pull/61))
- Eth/chain: add sepolia chain id; docs ([#60](https://github.com/q9f/eth.rb/pull/60))
- Eth/rlp: cleanup ([#59](https://github.com/q9f/eth.rb/pull/59))
- Eth/tx: properly serialize signatures ([#58](https://github.com/q9f/eth.rb/pull/58))
- Eth/client: fix legacy transfer ([#57](https://github.com/q9f/eth.rb/pull/57))
- Gem: relax openssl requirement ([#56](https://github.com/q9f/eth.rb/pull/56))
- Docs: update changelog ([#53](https://github.com/q9f/eth.rb/pull/53))
- Spec: add upstream test fixtures for keystore ([#50](https://github.com/q9f/eth.rb/pull/50))
## [0.5.0]
### Added
- Eth/tx: create legacy, type-1, and type-2 transactions [#33](https://github.com/q9f/eth.rb/pull/33)
- Signature: implement eip 712 typed structured data signing [#27](https://github.com/q9f/eth.rb/pull/27)
- Lib: import ABI to eth/abi [#29](https://github.com/q9f/eth.rb/pull/29)
- Eth/chains: implement eip 155 for replay protection [#20](https://github.com/q9f/eth.rb/pull/20)
### Changed
- Docs: update readme with features [#49](https://github.com/q9f/eth.rb/pull/49)
- Eth/tx: add method to estimate intrinsic gas costs [#48](https://github.com/q9f/eth.rb/pull/48)
- Eth/key: allow chain_id empty for signing messages/data [#47](https://github.com/q9f/eth.rb/pull/47)
- Gem: prepare for release [#46](https://github.com/q9f/eth.rb/pull/46)
- Eth/sig: allow v values > 0xff, fix #30 [#43](https://github.com/q9f/eth.rb/pull/43)
- Eth/abi: refactor for maintainability [#42](https://github.com/q9f/eth.rb/pull/42)
- Docs: improve readme [#41](https://github.com/q9f/eth.rb/pull/41)
- Lib: improve error handling [#39](https://github.com/q9f/eth.rb/pull/39)
- Docs: update readme for tx and keys [#40](https://github.com/q9f/eth.rb/pull/40)
- Implement encrypt/decrypt [#22](https://github.com/q9f/eth.rb/pull/22)
- Gem: clean up some docs and scripts [#32](https://github.com/q9f/eth.rb/pull/32)
- Rename util and chain to singular [#26](https://github.com/q9f/eth.rb/pull/26)
- Docs: add some examples to readme [#25](https://github.com/q9f/eth.rb/pull/25)
- Key/signature: personal sign and verify [#24](https://github.com/q9f/eth.rb/pull/24)
- Ci: only run coverage on CI [#23](https://github.com/q9f/eth.rb/pull/23)
- Lib/signature: implement personal_recover (eip 191 [#21](https://github.com/q9f/eth.rb/pull/21)
- Eth/util: public_key_to_address should return an eth::address [#19](https://github.com/q9f/eth.rb/pull/19)
- Ci: add docs workflow [#18](https://github.com/q9f/eth.rb/pull/18)
- Address class implementation and tests [#13](https://github.com/q9f/eth.rb/pull/13)
- Spec: improve util tests [#12](https://github.com/q9f/eth.rb/pull/12)
- Spec: improve key tests [#11](https://github.com/q9f/eth.rb/pull/11)
- Gems: bump keccak and secp256k1 [#10](https://github.com/q9f/eth.rb/pull/10)
- Docs: add code climate badge [#8](https://github.com/q9f/eth.rb/pull/8)
- Ci: enable codecov [#7](https://github.com/q9f/eth.rb/pull/7)
- Docs: add AUTHORS file [#6](https://github.com/q9f/eth.rb/pull/6)
- Lib: implement Eth::Key class [#4](https://github.com/q9f/eth.rb/pull/4)
- Ci: add nightly schedule [#2](https://github.com/q9f/eth.rb/pull/2)
- Reset gem to point blank [#1](https://github.com/q9f/eth.rb/pull/1)
## [0.4.18]
### Changed
- CI: add yard doc and rufo workflows [se3000/ruby-eth#75](https://github.com/se3000/ruby-eth/pull/75)
- Gem: run rufo [se3000/ruby-eth#74](https://github.com/se3000/ruby-eth/pull/74)
- Gem: dependencies [se3000/ruby-eth#73](https://github.com/se3000/ruby-eth/pull/73)
- Lib: fix compatibility with libressl (macos) and openssl 1.1.1k [se3000/ruby-eth#66](https://github.com/se3000/ruby-eth/pull/66)
## [0.4.17]
### Changed
- Gems: bump version to 0.4.17 [se3000/ruby-eth#70](https://github.com/se3000/ruby-eth/pull/70)
- Gems: bump keccak to 1.3.0 [se3000/ruby-eth#69](https://github.com/se3000/ruby-eth/pull/69)
## [0.4.16]
### Changed
- Docs: update changelog [se3000/ruby-eth#65](https://github.com/se3000/ruby-eth/pull/65)
- Gems: bump version to 0.4.16 [se3000/ruby-eth#65](https://github.com/se3000/ruby-eth/pull/65)
- License: update copyright notice [se3000/ruby-eth#64](https://github.com/se3000/ruby-eth/pull/64)
- Docs: add badges to readme [se3000/ruby-eth#64](https://github.com/se3000/ruby-eth/pull/64)
- Git: deprecating master [se3000/ruby-eth#63](https://github.com/se3000/ruby-eth/pull/63)
- CI: replace travis with github actions [se3000/ruby-eth#62](https://github.com/se3000/ruby-eth/pull/62)
- Gems: replace digest-sha3-patched with keccak [se3000/ruby-eth#58](https://github.com/se3000/ruby-eth/pull/58)
## [0.4.13], [0.4.14], [0.4.15]
_Released as [`eth-patched`](https://github.com/q9f/ruby-eth) from a different source tree._
## [0.4.12]
### Changed
- Bump rake version because of security vulnerability
## [0.4.11]
### Added
- Support for recovering signatures with a V value below 27 (like from Ledger hardware wallets)
## [0.4.10]
### Changed
- Use updated sha3 dependency
- Improved OpenSSL support
### Changed
- Changed Eth::Configuration.default_chain_id back to .chain_id for dependent libraries.
## [0.4.9]
### Changed
- [escoffon](https://github.com/escoffon) added support for chain IDs larger than 120.
## [0.4.8]
### Added
- [@buhrmi](https://github.com/buhrmi) added Eth::Key#personal_sign.
- [@buhrmi](https://github.com/buhrmi) added Eth::Key#personal_recover.
## [0.4.7]
### Changed
- Updated MoneyTree dependency.
## [0.4.6]
### Added
- Support scrypt private key decryption
## [0.4.5]
### Changed
- Further improve Open SSL configurability
## [0.4.4]
### Changed
- Support old versions of SSL to help avoid preious breaking changes
## [0.4.3]
### Added
- Eth::Key::Encrypter class to handle encrypting keys.
- Eth::Key.encrypt as a nice wrapper around Encrypter class.
- Eth::Key::Decrypter class to handle encrypting keys.
- Eth::Key.decrypt as a nice wrapper around Decrypter class.
## [0.4.2]
### Added
- Address#valid? to validate EIP55 checksums.
- Address#checksummed to generate EIP55 checksums.
- Utils.valid_address? to easily validate EIP55 checksums.
- Utils.format_address to easily convert an address to EIP55 checksummed.
### Changed
- Dependencies no longer include Ethereum::Base. Eth now implements those helpers directly and includes ffi, digest-sha3, and rlp directly.
## [0.4.1]
### Changed
- Tx#hash includes the '0x' hex prefix.
## [0.4.0]
### Added
- Tx#data_bin returns the data field of a transaction in binary.
- Tx#data_hex returns the data field of a transaction as a hexadecimal string.
- Tx#id is an alias of Tx#hash
### Changed
- Tx#data is configurable to return either hex or binary: `config.tx_data_hex = true`.
- Tx#hex includes the '0x' hex prefix.
- Key#address getter is prepended by '0x'.
- Extract public key to address method into Utils.public_key_to_address.
- Tx#from returns an address instead of a public key.
- Chain ID is updated to the later version of the spec.
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<github@q9f.cc>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
version 2.1, available at [v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Ruby Ethereum
Everyone is welcome to contribute to the Ruby Ethereum gem. It is
more than six years old and had seen many maintainers come and go.
Current maintainers:
* [@q9f](https://github.com/q9f)
* [@kurotaky](https://github.com/kurotaky)
### Workflow
To propose a change, fix, or new feature, create an issue or comment
on an existing issue to see if anyone else has an opinion on it or is
eventually already working on it
The general workflow looks roughly like this:
1. Discuss it
2. Fork it
3. Improve it
4. Test it
5. Document it
6. Submit it
### Linting
We use the Ruby formatter `rufo` to ensure consistent formatting across
the code base.
* <https://github.com/ruby-formatter/rufo>
Simply run `rufo .` before comitting your changes.
### Testing
We use behaviour-driven development and this codebase is at least 100%
unit tested. We use RSpec to run the spec tests.
* <https://rspec.info>
The full Ethereum test-suite is available in `fixtures/ethereum/tests`.
Run `git submodule update --init --recursive` to fetch it.
* <https://github.com/ethereum/tests>
If your tests are failing make sure you pulled the ethereum/tests
submodule and run a local geth node in background with
`geth --dev --http --ipcpath /tmp/geth.ipc` as we are running some tests
against a local live node.
Other static test data is available in `fixtures/`
### Documentation
We use the Ruby documentation tool Yard.
* <https://yardoc.org>
The code base is 100% API documented.
* <https://q9f.github.io/eth.rb>
More involved documentation, tutorials, and usage examples should go
into the wiki.
* <https://github.com/q9f/eth.rb/wiki>
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
group :test, :development do
gem "bundler", ">= 2.4"
gem "pry", "~> 0.15"
gem "rake", "~> 13.2"
gem "rdoc", "~> 6.13"
gem "rspec", "~> 3.13"
gem "rufo", "~> 0.18"
gem "simplecov", "~> 0.22"
gem "simplecov-cobertura", "~> 3.0"
gem "yard", "~> 0.9"
end
gemspec
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2016-2025 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.
================================================
FILE: README.md
================================================
<!--
# @markup markdown
# @title Ethereum for Ruby
# @author Afri Schoedon
-->
# Ethereum for Ruby
[](https://github.com/q9f/eth.rb/actions)
[](https://github.com/q9f/eth.rb/releases)
[](https://rubygems.org/gems/eth)
[](https://rubygems.org/gems/eth)
[](https://codecov.io/gh/q9f/eth.rb)
[](https://github.com/q9f/eth.rb/pulse)
[](https://q9f.github.io/eth.rb)
[](https://github.com/q9f/eth.rb/wiki)
[](LICENSE.txt)
[](https://github.com/q9f/eth.rb/issues)
A straightforward library to build, sign, and broadcast Ethereum transactions. It allows the separation of key and node management. Sign transactions and handle keys anywhere you can run Ruby and broadcast transactions through any local or remote node. Sign messages and recover signatures for authentication.
What you get:
- [x] Secp256k1 Key-Pairs and Encrypted Ethereum Key-Stores (JSON)
- [x] EIP-20 Token Transfers (ERC20)
- [x] EIP-55 Checksummed Ethereum Addresses
- [x] EIP-137 Ethereum Domain Name Service (ENS)
- [x] EIP-155 Replay protection with Chain IDs (with presets)
- [x] EIP-191 Ethereum Signed Messages (with prefix and type)
- [x] EIP-712 Ethereum Signed Type Data
- [x] EIP-1271 Smart-Contract Authentification
- [x] EIP-1559 Ethereum Type-2 Transactions (with priority fee and max gas fee)
- [x] EIP-2028 Call-data intrinsic gas cost estimates (plus access lists)
- [x] EIP-2718 Ethereum Transaction Envelopes (and types)
- [x] EIP-2930 Ethereum Type-1 Transactions (with access lists)
- [x] EIP-4844 Ethereum Type-3 Transactions (with shard blobs, up to 9 blobs per block)
- [x] EIP-7702 Ethereum Type-4 Transactions (with authorization lists)
- [x] ABI-Encoder and Decoder (including type parser)
- [x] Packed ABI-Encoder for Solidity smart contracts
- [x] RLP-Encoder and Decoder (including sedes)
- [x] RPC-Client (IPC/HTTP/WS) for Execution-Layer APIs
- [x] Solidity bindings (compile contracts from Ruby)
- [x] Full smart-contract support (deploy, transact, and call)
- [x] ERC-6093 custom Solidity errors
## Installation
Add this line to your application's Gemfile:
```ruby
gem "eth"
```
Or install it yourself as:
```shell
gem install eth
```
## Usage
Check out the
[](https://q9f.github.io/eth.rb)
and the
[](https://github.com/q9f/eth.rb/wiki)
for all the details and example snippets.
## Documentation
The documentation can be found at: https://q9f.github.io/eth.rb
For any specific version, docs can be generated by `yard`:
```shell
gem install bundler rdoc yard
git checkout $VERSION
yard doc
```
The goal is to have 100% API documentation available.
## Testing
The test suite expects working local HTTP, WS, and IPC endpoints with a prefunded developer account, e.g.:
```shell
geth --dev --http --ws --ipcpath /tmp/geth.ipc &
```
To run tests, simply use `rspec`. Note, that the Ethereum test fixtures are also required.
```shell
git submodule update --init --recursive
bundle install
rspec
```
The goal is to have 100% unit-test coverage for all code inside this gem.
## Contributing
Pull requests are welcome! To contribute, please consider the following:
* Code should be fully documented. Run `yard doc` and make sure it does not yield any warnings or undocumented sets.
* Code should be fully covered by tests. Run `rspec` to make sure all tests pass. The CI has an integration that will assist you to identify uncovered lines of code and get coverage up to 100%.
* Code should be formatted properly. Try to eliminate the most common issues such as trailing white-spaces or duplicate new-lines. Usage of the `rufo` gem is recommended.
* Submit pull requests, questions, or issues to Github: <https://github.com/q9f/eth.rb>
## License and Credits
The `eth` gem is licensed under the conditions of [Apache 2.0](./LICENSE.txt). Please see [AUTHORS](./AUTHORS.txt) for contributors and copyright notices.
This gem is a complete rewrite of the old `eth` gem by Steve Ellis.
* <https://github.com/se3000/ruby-eth> (MIT)
It is not only a rewrite of the `eth` gem but also a partial merge of the `ethereum` gem by Marek Kirejczyk and Yuta Kurotaki.
* <https://github.com/EthWorks/ethereum.rb> (MIT)
This gem also includes a revised version of the ABI gem by Jan Xie and Zhang Yaning.
* <https://github.com/cryptape/ruby-ethereum-abi> (MIT)
It also contains a condensed version of the RLP gem by Jan Xie and Zhang Yaning.
* <https://github.com/cryptape/ruby-rlp> (MIT)
================================================
FILE: Rakefile
================================================
require "bundler/gem_tasks"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Ruby Ethereum 0.5.x is a complete rewrite of the old `eth` 0.4.x gem.
It also contains modules from `abi`, `rlp` and the `ethereum` gem.
None of these gems are maintained anymore except for `eth` 0.5.0 and
later.
| Gem | Version | Supported |
| -------------- | ------- | ------------------ |
| `eth` | >= 0.5 | :white_check_mark: |
| `eth` | < 0.5 | :x: |
| `ethereum` | _any_ | :x: |
| `ethereum-abi` | _any_ | :x: |
| `rlp` | _any_ | :x: |
## Reporting a Vulnerability
Please report your findings to <security@q9f.cc>. Do not create a
Github issue!
You can expect an answer within 48 hours.
================================================
FILE: abi/ens_registry.json
================================================
[
{
"inputs":[
{
"internalType":"contract ENS",
"name":"_old",
"type":"address"
}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"constructor"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"address",
"name":"owner",
"type":"address"
},
{
"indexed":true,
"internalType":"address",
"name":"operator",
"type":"address"
},
{
"indexed":false,
"internalType":"bool",
"name":"approved",
"type":"bool"
}
],
"name":"ApprovalForAll",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":true,
"internalType":"bytes32",
"name":"label",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"address",
"name":"owner",
"type":"address"
}
],
"name":"NewOwner",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"address",
"name":"resolver",
"type":"address"
}
],
"name":"NewResolver",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"uint64",
"name":"ttl",
"type":"uint64"
}
],
"name":"NewTTL",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"address",
"name":"owner",
"type":"address"
}
],
"name":"Transfer",
"type":"event"
},
{
"constant":true,
"inputs":[
{
"internalType":"address",
"name":"owner",
"type":"address"
},
{
"internalType":"address",
"name":"operator",
"type":"address"
}
],
"name":"isApprovedForAll",
"outputs":[
{
"internalType":"bool",
"name":"",
"type":"bool"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
],
"name":"old",
"outputs":[
{
"internalType":"contract ENS",
"name":"",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"owner",
"outputs":[
{
"internalType":"address",
"name":"",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"recordExists",
"outputs":[
{
"internalType":"bool",
"name":"",
"type":"bool"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"resolver",
"outputs":[
{
"internalType":"address",
"name":"",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"address",
"name":"operator",
"type":"address"
},
{
"internalType":"bool",
"name":"approved",
"type":"bool"
}
],
"name":"setApprovalForAll",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"address",
"name":"owner",
"type":"address"
}
],
"name":"setOwner",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"address",
"name":"owner",
"type":"address"
},
{
"internalType":"address",
"name":"resolver",
"type":"address"
},
{
"internalType":"uint64",
"name":"ttl",
"type":"uint64"
}
],
"name":"setRecord",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"address",
"name":"resolver",
"type":"address"
}
],
"name":"setResolver",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"label",
"type":"bytes32"
},
{
"internalType":"address",
"name":"owner",
"type":"address"
}
],
"name":"setSubnodeOwner",
"outputs":[
{
"internalType":"bytes32",
"name":"",
"type":"bytes32"
}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"label",
"type":"bytes32"
},
{
"internalType":"address",
"name":"owner",
"type":"address"
},
{
"internalType":"address",
"name":"resolver",
"type":"address"
},
{
"internalType":"uint64",
"name":"ttl",
"type":"uint64"
}
],
"name":"setSubnodeRecord",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"uint64",
"name":"ttl",
"type":"uint64"
}
],
"name":"setTTL",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"ttl",
"outputs":[
{
"internalType":"uint64",
"name":"",
"type":"uint64"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
}
]
================================================
FILE: abi/ens_resolver.json
================================================
[
{
"inputs":[
{
"internalType":"contract ENS",
"name":"_ens",
"type":"address"
}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"constructor"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":true,
"internalType":"uint256",
"name":"contentType",
"type":"uint256"
}
],
"name":"ABIChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"address",
"name":"a",
"type":"address"
}
],
"name":"AddrChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"uint256",
"name":"coinType",
"type":"uint256"
},
{
"indexed":false,
"internalType":"bytes",
"name":"newAddress",
"type":"bytes"
}
],
"name":"AddressChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":true,
"internalType":"address",
"name":"owner",
"type":"address"
},
{
"indexed":true,
"internalType":"address",
"name":"target",
"type":"address"
},
{
"indexed":false,
"internalType":"bool",
"name":"isAuthorised",
"type":"bool"
}
],
"name":"AuthorisationChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"bytes",
"name":"hash",
"type":"bytes"
}
],
"name":"ContenthashChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"bytes",
"name":"name",
"type":"bytes"
},
{
"indexed":false,
"internalType":"uint16",
"name":"resource",
"type":"uint16"
},
{
"indexed":false,
"internalType":"bytes",
"name":"record",
"type":"bytes"
}
],
"name":"DNSRecordChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"bytes",
"name":"name",
"type":"bytes"
},
{
"indexed":false,
"internalType":"uint16",
"name":"resource",
"type":"uint16"
}
],
"name":"DNSRecordDeleted",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"DNSZoneCleared",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":true,
"internalType":"bytes4",
"name":"interfaceID",
"type":"bytes4"
},
{
"indexed":false,
"internalType":"address",
"name":"implementer",
"type":"address"
}
],
"name":"InterfaceChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"string",
"name":"name",
"type":"string"
}
],
"name":"NameChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"bytes32",
"name":"x",
"type":"bytes32"
},
{
"indexed":false,
"internalType":"bytes32",
"name":"y",
"type":"bytes32"
}
],
"name":"PubkeyChanged",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"indexed":true,
"internalType":"string",
"name":"indexedKey",
"type":"string"
},
{
"indexed":false,
"internalType":"string",
"name":"key",
"type":"string"
}
],
"name":"TextChanged",
"type":"event"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"uint256",
"name":"contentTypes",
"type":"uint256"
}
],
"name":"ABI",
"outputs":[
{
"internalType":"uint256",
"name":"",
"type":"uint256"
},
{
"internalType":"bytes",
"name":"",
"type":"bytes"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"addr",
"outputs":[
{
"internalType":"address payable",
"name":"",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"uint256",
"name":"coinType",
"type":"uint256"
}
],
"name":"addr",
"outputs":[
{
"internalType":"bytes",
"name":"",
"type":"bytes"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"",
"type":"bytes32"
},
{
"internalType":"address",
"name":"",
"type":"address"
},
{
"internalType":"address",
"name":"",
"type":"address"
}
],
"name":"authorisations",
"outputs":[
{
"internalType":"bool",
"name":"",
"type":"bool"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"clearDNSZone",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"contenthash",
"outputs":[
{
"internalType":"bytes",
"name":"",
"type":"bytes"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"name",
"type":"bytes32"
},
{
"internalType":"uint16",
"name":"resource",
"type":"uint16"
}
],
"name":"dnsRecord",
"outputs":[
{
"internalType":"bytes",
"name":"",
"type":"bytes"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"name",
"type":"bytes32"
}
],
"name":"hasDNSRecords",
"outputs":[
{
"internalType":"bool",
"name":"",
"type":"bool"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes4",
"name":"interfaceID",
"type":"bytes4"
}
],
"name":"interfaceImplementer",
"outputs":[
{
"internalType":"address",
"name":"",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes[]",
"name":"data",
"type":"bytes[]"
}
],
"name":"multicall",
"outputs":[
{
"internalType":"bytes[]",
"name":"results",
"type":"bytes[]"
}
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"name",
"outputs":[
{
"internalType":"string",
"name":"",
"type":"string"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
}
],
"name":"pubkey",
"outputs":[
{
"internalType":"bytes32",
"name":"x",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"y",
"type":"bytes32"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"uint256",
"name":"contentType",
"type":"uint256"
},
{
"internalType":"bytes",
"name":"data",
"type":"bytes"
}
],
"name":"setABI",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"uint256",
"name":"coinType",
"type":"uint256"
},
{
"internalType":"bytes",
"name":"a",
"type":"bytes"
}
],
"name":"setAddr",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"address",
"name":"a",
"type":"address"
}
],
"name":"setAddr",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"address",
"name":"target",
"type":"address"
},
{
"internalType":"bool",
"name":"isAuthorised",
"type":"bool"
}
],
"name":"setAuthorisation",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes",
"name":"hash",
"type":"bytes"
}
],
"name":"setContenthash",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes",
"name":"data",
"type":"bytes"
}
],
"name":"setDNSRecords",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes4",
"name":"interfaceID",
"type":"bytes4"
},
{
"internalType":"address",
"name":"implementer",
"type":"address"
}
],
"name":"setInterface",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"string",
"name":"name",
"type":"string"
}
],
"name":"setName",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"x",
"type":"bytes32"
},
{
"internalType":"bytes32",
"name":"y",
"type":"bytes32"
}
],
"name":"setPubkey",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":false,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"string",
"name":"key",
"type":"string"
},
{
"internalType":"string",
"name":"value",
"type":"string"
}
],
"name":"setText",
"outputs":[
],
"payable":false,
"stateMutability":"nonpayable",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes4",
"name":"interfaceID",
"type":"bytes4"
}
],
"name":"supportsInterface",
"outputs":[
{
"internalType":"bool",
"name":"",
"type":"bool"
}
],
"payable":false,
"stateMutability":"pure",
"type":"function"
},
{
"constant":true,
"inputs":[
{
"internalType":"bytes32",
"name":"node",
"type":"bytes32"
},
{
"internalType":"string",
"name":"key",
"type":"string"
}
],
"name":"text",
"outputs":[
{
"internalType":"string",
"name":"",
"type":"string"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
}
]
================================================
FILE: bin/console
================================================
#!/usr/bin/env ruby
# use the local version of the code instead of a globally installed gem
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require "eth"
include Eth
require "pry"
Pry.start
================================================
FILE: bin/setup
================================================
#!/usr/bin/env bash
bundle install
git submodule update --init --recursive
rufo .
yard doc
rspec
echo "Tests fail? Run \`geth --dev --http --ipcpath /tmp/geth.ipc\` in background and try again."
================================================
FILE: codecov.yml
================================================
coverage:
status:
project:
default:
target: 99%
threshold: 1%
================================================
FILE: eth.gemspec
================================================
# frozen_string_literal: true
# coding: utf-8
lib = File.expand_path("lib", __dir__).freeze
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
require "eth/version"
Gem::Specification.new do |spec|
spec.name = "eth"
spec.version = Eth::VERSION
spec.authors = ["Steve Ellis", "Afri Schoedon"]
spec.email = ["email@steveell.is", "ruby@q9f.cc"]
spec.summary = %q{Ruby Ethereum library.}
spec.description = %q{Library to handle Ethereum accounts, messages, and transactions.}
spec.homepage = "https://github.com/q9f/eth.rb"
spec.license = "Apache-2.0"
spec.metadata = {
"bug_tracker_uri" => "https://github.com/q9f/eth.rb/issues",
"changelog_uri" => "https://github.com/q9f/eth.rb/blob/main/CHANGELOG.md",
"documentation_uri" => "https://q9f.github.io/eth.rb/",
"github_repo" => "https://github.com/q9f/eth.rb",
"source_code_uri" => "https://github.com/q9f/eth.rb",
}.freeze
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib", "abis"]
spec.test_files = spec.files.grep %r{^(test|spec|features)/}
spec.platform = Gem::Platform::RUBY
spec.required_ruby_version = ">= 3.0", "< 5.0"
# bigdecimal for big decimals ;)
spec.add_dependency "bigdecimal", "~> 3.1"
# forwardable for contracts meta programming
spec.add_dependency "forwardable", "~> 1.3"
# keccak for hashing everything in ethereum
spec.add_dependency "keccak", "~> 1.3"
# konstructor gem for overloading constructors
spec.add_dependency "konstructor", "~> 1.0"
# rbsecp256k1 for key-pairs and signatures
spec.add_dependency "rbsecp256k1", "~> 6.0"
# openssl for encrypted key derivation
spec.add_dependency "openssl", "~> 3.3"
# base64 is required explicitly in Ruby >= 3.4
spec.add_dependency "base64", "~> 0.1"
# scrypt for encrypted key derivation
spec.add_dependency "scrypt", "~> 3.0"
# bls12-381 for BLS signatures and pairings
spec.add_dependency "bls12-381", "~> 0.3"
# httpx for HTTP/2 and persistent connections
spec.add_dependency "httpx", "~> 1.6"
end
================================================
FILE: lib/eth/abi/decoder.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Provides a utility module to assist decoding ABIs.
module Decoder
extend self
# Decodes a specific value, either static or dynamic.
#
# @param type [Eth::Abi::Type] type to be decoded.
# @param arg [String] encoded type data string.
# @return [String] the decoded data for the type.
# @raise [DecodingError] if decoding fails for type.
def type(type, arg)
if %w(string bytes).include?(type.base_type) and type.sub_type.empty? and type.dimensions.empty?
l = Util.deserialize_big_endian_to_int arg[0, 32]
data = arg[32..-1]
raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)
# decoded strings and bytes
data[0, l]
elsif type.base_type == "tuple" && type.dimensions.empty?
offset = 0
result = []
raise DecodingError, "Cannot decode tuples without known components" if type.components.nil?
head_offset = 0
dynamic_offsets = []
type.components.each_with_index do |component, index|
if component.dynamic?
raise DecodingError, "Offset out of bounds" if head_offset + 32 > arg.size
pointer = Util.deserialize_big_endian_to_int arg[head_offset, 32]
dynamic_offsets << [index, pointer]
head_offset += 32
else
size = component.size
raise DecodingError, "Offset out of bounds" if head_offset + size > arg.size
head_offset += size
end
end
dynamic_ranges = tuple_dynamic_ranges(dynamic_offsets, arg.size, head_offset)
type.components.each_with_index do |component, index|
if component.dynamic?
pointer, next_offset = dynamic_ranges[index]
raise DecodingError, "Offset out of bounds" if pointer > arg.size || next_offset > arg.size
raise DecodingError, "Offset out of bounds" if next_offset < pointer
result << type(component, arg[pointer, next_offset - pointer])
offset += 32
else
size = component.size
raise DecodingError, "Offset out of bounds" if offset + size > arg.size
result << type(component, arg[offset, size])
offset += size
end
end
result
elsif type.dynamic? && !type.dimensions.empty? && type.dimensions.last == 0
l = Util.deserialize_big_endian_to_int arg[0, 32]
nested_sub = type.nested_sub
if nested_sub.dynamic?
raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l
offsets = (0...l).map do |i|
off = Util.deserialize_big_endian_to_int arg[32 + 32 * i, 32]
raise DecodingError, "Offset out of bounds" if off < 32 * l || off > arg.size - 64
off
end
offsets.each_with_index.map do |off, index|
start = 32 + off
stop = index + 1 < offsets.length ? 32 + offsets[index + 1] : arg.size
raise DecodingError, "Offset out of bounds" if stop > arg.size || stop < start
type(nested_sub, arg[start, stop - start])
end
else
raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + nested_sub.size * l
# decoded dynamic-sized arrays with static sub-types
(0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
end
elsif !type.dimensions.empty?
l = type.dimensions.last
nested_sub = type.nested_sub
if nested_sub.dynamic?
raise DecodingError, "Wrong data size for static array" unless arg.size >= 32 * l
offsets = (0...l).map do |i|
off = Util.deserialize_big_endian_to_int arg[32 * i, 32]
raise DecodingError, "Offset out of bounds" if off < 32 * l || off > arg.size - 32
off
end
offsets.each_with_index.map do |off, i|
size = (i + 1 < offsets.length ? offsets[i + 1] : arg.size) - off
type(nested_sub, arg[off, size])
end
else
# decoded static-size arrays with static sub-types
(0...l).map { |i| type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
end
else
# decoded primitive types
primitive_type type, arg
end
end
# Decodes primitive types.
#
# @param type [Eth::Abi::Type] type to be decoded.
# @param data [String] encoded primitive type data string.
# @return [String] the decoded data for the type.
# @raise [DecodingError] if decoding fails for type.
def primitive_type(type, data)
case type.base_type
when "address"
# decoded address with 0x-prefix
Address.new(Util.bin_to_hex data[12..-1]).to_s.downcase
when "string", "bytes"
if type.sub_type.empty?
size = Util.deserialize_big_endian_to_int data[0, 32]
# decoded dynamic-sized array
decoded = data[32..-1][0, size]
decoded.force_encoding(Encoding::UTF_8)
decoded
else
# decoded static-sized array
data[0, type.sub_type.to_i]
end
when "hash"
# decoded hash
data[(32 - type.sub_type.to_i), type.sub_type.to_i]
when "uint"
# decoded unsigned integer
Util.deserialize_big_endian_to_int data
when "int"
u = Util.deserialize_big_endian_to_int data
i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** 256) : u
# decoded integer
i
when "ureal", "ufixed"
high, low = type.sub_type.split("x").map(&:to_i)
# decoded unsigned fixed point numeric
Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
when "real", "fixed"
high, low = type.sub_type.split("x").map(&:to_i)
u = Util.deserialize_big_endian_to_int data
i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u
# decoded fixed point numeric
i * 1.0 / 2 ** low
when "bool"
# decoded boolean
data[-1] == Constant::BYTE_ONE
else
raise DecodingError, "Unknown primitive type: #{type.base_type}"
end
end
private
# Computes the byte ranges for dynamic tuple components.
#
# @param offsets [Array<Array(Integer, Integer)>] list of tuples containing the component index and pointer.
# @param total_size [Integer] total number of bytes available for the tuple.
# @param head_size [Integer] size in bytes of the tuple head.
# @return [Hash{Integer=>Array(Integer, Integer)}] mapping component index to a [start, stop) range.
# @raise [DecodingError] if the encoded offsets overlap or leave the tuple head.
def tuple_dynamic_ranges(offsets, total_size, head_size)
ranges = {}
sorted = offsets.sort_by { |(_, pointer)| pointer }
sorted.each_with_index do |(index, pointer), idx|
raise DecodingError, "Offset out of bounds" if pointer < head_size || pointer > total_size
next_pointer = idx + 1 < sorted.length ? sorted[idx + 1][1] : total_size
raise DecodingError, "Offset out of bounds" if next_pointer < pointer || next_pointer > total_size
ranges[index] = [pointer, next_pointer]
end
ranges
end
end
end
end
================================================
FILE: lib/eth/abi/encoder.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
require "bigdecimal"
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Provides a utility module to assist encoding ABIs.
module Encoder
extend self
# Encodes a specific value, either static or dynamic.
#
# @param type [Eth::Abi::Type] type to be encoded.
# @param arg [String|Number] value to be encoded.
# @return [String] the encoded type.
# @raise [EncodingError] if value does not match type.
def type(type, arg)
if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
raise EncodingError, "Argument must be a String" unless arg.instance_of? String
arg = handle_hex_string arg, type
# encodes strings and bytes
size = type Type.size_type, arg.size
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
"#{size}#{arg}#{padding}"
elsif type.base_type == "tuple" && type.dimensions.empty?
tuple arg, type
elsif !type.dimensions.empty?
encode_array type, arg
else
primitive_type type, arg
end
end
# Encodes primitive types.
#
# @param type [Eth::Abi::Type] type to be encoded.
# @param arg [String|Number] value to be encoded.
# @return [String] the encoded primitive type.
# @raise [EncodingError] if value does not match type.
# @raise [ValueOutOfBounds] if value is out of bounds for type.
# @raise [ArgumentError] if encoding fails for type.
def primitive_type(type, arg)
case type.base_type
when "uint"
uint arg, type
when "int"
int arg, type
when "bool"
bool arg
when "ureal", "ufixed"
ufixed arg, type
when "real", "fixed"
fixed arg, type
when "string", "bytes"
bytes arg, type
when "tuple"
tuple arg, type
when "hash"
hash arg, type
when "address"
address arg
else
raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}"
end
end
private
# Properly encodes unsigned integers.
def uint(arg, type)
arg = coerce_number arg
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
real_size = type.sub_type.to_i
i = arg.to_i
raise ValueOutOfBounds, arg unless i >= 0 and i < 2 ** real_size
Util.zpad_int i
end
# Properly encodes signed integers.
def int(arg, type)
arg = coerce_number arg
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
real_size = type.sub_type.to_i
i = arg.to_i
raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1)
Util.zpad_int(i % 2 ** 256)
end
# Properly encodes booleans.
def bool(arg)
raise EncodingError, "Argument is not bool: #{arg}" unless arg.instance_of? TrueClass or arg.instance_of? FalseClass
Util.zpad_int(arg ? 1 : 0)
end
# Properly encodes unsigned fixed-point numbers.
def ufixed(arg, type)
arg = coerce_number arg
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
high, low = type.sub_type.split("x").map(&:to_i)
raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
Util.zpad_int((arg * 2 ** low).to_i)
end
# Properly encodes signed fixed-point numbers.
def fixed(arg, type)
arg = coerce_number arg
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
high, low = type.sub_type.split("x").map(&:to_i)
raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
i = (arg * 2 ** low).to_i
Util.zpad_int(i % 2 ** (high + low))
end
# Properly encodes byte-strings.
def bytes(arg, type)
raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
arg = handle_hex_string arg, type
if type.sub_type.empty?
size = Util.zpad_int arg.size
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
# variable length string/bytes
"#{size}#{arg}#{padding}"
else
raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i
padding = Constant::BYTE_ZERO * (32 - arg.size)
# fixed length string/bytes
"#{arg}#{padding}"
end
end
# Properly encodes tuples.
def tuple(arg, type)
unless arg.is_a?(Hash) || arg.is_a?(Array)
raise EncodingError, "Expecting Hash or Array: #{arg}"
end
raise EncodingError, "Expecting #{type.components.size} elements: #{arg}" unless arg.size == type.components.size
arg = arg.transform_keys(&:to_s) if arg.is_a?(Hash) # because component_type.name is String
static_size = 0
type.components.each_with_index do |component, i|
if type.components[i].dynamic?
static_size += 32
else
static_size += Util.ceil32(type.components[i].size || 0)
end
end
dynamic_offset = static_size
offsets_and_static_values = []
dynamic_values = []
type.components.each_with_index do |component, i|
component_type = type.components[i]
if component_type.dynamic?
offsets_and_static_values << type(Type.size_type, dynamic_offset)
dynamic_value = type(component_type, arg.is_a?(Array) ? arg[i] : arg[component_type.name])
dynamic_values << dynamic_value
dynamic_offset += dynamic_value.size
else
offsets_and_static_values << type(component_type, arg.is_a?(Array) ? arg[i] : arg.fetch(component_type.name))
end
end
offsets_and_static_values.join + dynamic_values.join
end
def coerce_number(arg)
return arg if arg.is_a? Numeric
return arg.to_i(0) if arg.is_a?(String) && arg.match?(/^-?(0x)?[0-9a-fA-F]+$/)
return BigDecimal(arg) if arg.is_a?(String) && arg.match?(/^-?\d+(\.\d+)?$/)
arg
end
# Encodes array values of any dimensionality.
#
# @param type [Eth::Abi::Type] the type describing the array.
# @param values [Array] the Ruby values to encode.
# @return [String] ABI encoded array payload.
# @raise [EncodingError] if the value cardinality does not match static dimensions.
def encode_array(type, values)
raise EncodingError, "Expecting Array value" unless values.is_a?(Array)
required_length = type.dimensions.last
if required_length != 0 && values.size != required_length
raise EncodingError, "Expecting #{required_length} elements: #{values.size} provided"
end
nested_sub = type.nested_sub
if required_length.zero?
encode_dynamic_array(nested_sub, values)
else
encode_static_array(nested_sub, values)
end
end
# Encodes dynamic-sized arrays, including nested tuples.
#
# @param nested_sub [Eth::Abi::Type] the element type.
# @param values [Array] elements to encode.
# @return [String] ABI encoded dynamic array payload.
def encode_dynamic_array(nested_sub, values)
head = type(Type.size_type, values.size)
element_heads, element_tails = encode_array_elements(nested_sub, values)
head + element_heads + element_tails
end
# Encodes static-sized arrays, including nested tuples.
#
# @param nested_sub [Eth::Abi::Type] the element type.
# @param values [Array] elements to encode.
# @return [String] ABI encoded static array payload.
def encode_static_array(nested_sub, values)
element_heads, element_tails = encode_array_elements(nested_sub, values)
element_heads + element_tails
end
# Encodes the head/tail portions for array elements.
#
# @param nested_sub [Eth::Abi::Type] the element type.
# @param values [Array] elements to encode.
# @return [Array<String, String>] head/tail encoded segments.
def encode_array_elements(nested_sub, values)
if nested_sub.dynamic?
head = ""
tail = ""
offset = values.size * 32
values.each do |value|
encoded = type(nested_sub, value)
head += type(Type.size_type, offset)
tail += encoded
offset += encoded.size
end
[head, tail]
else
[values.map { |value| type(nested_sub, value) }.join, ""]
end
end
# Properly encodes hash-strings.
def hash(arg, type)
size = type.sub_type.to_i
raise EncodingError, "Argument too long: #{arg}" unless size > 0 and size <= 32
if arg.is_a? Integer
# hash from integer
Util.zpad_int arg
elsif arg.size == size
# hash from encoded hash
Util.zpad arg, 32
elsif arg.size == size * 2
# hash from hexadecimal hash
Util.zpad_hex arg
else
raise EncodingError, "Could not parse hash: #{arg}"
end
end
# Properly encodes addresses.
def address(arg)
if arg.is_a? Address
# from checksummed address with 0x prefix
Util.zpad_hex arg.to_s[2..-1]
elsif arg.is_a? Integer
# address from integer
Util.zpad_int arg
elsif arg.size == 20
# address from encoded address
Util.zpad arg, 32
elsif arg.size == 40
# address from hexadecimal address
Util.zpad_hex arg
elsif arg.size == 42 and arg[0, 2] == "0x"
# address from hexadecimal address with 0x prefix
Util.zpad_hex arg[2..-1]
else
raise EncodingError, "Could not parse address: #{arg}"
end
end
# The ABI encoder needs to be able to determine between a hex `"123"`
# and a binary `"123"` string.
def handle_hex_string(arg, type)
if Util.prefixed? arg or
(arg.size === type.sub_type.to_i * 2 and Util.hex? arg)
# There is no way telling whether a string is hex or binary with certainty
# in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
# Additionally, if the string size is exactly the double of the expected
# binary size, we can assume a hex value.
Util.hex_to_bin arg
else
# Everything else will be assumed binary or raw string.
arg.b
end
end
end
end
end
================================================
FILE: lib/eth/abi/event.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Provides a module to decode transaction log events.
module Event
extend self
# Compute topic for ABI event interface.
#
# @param interface [Hash] ABI event interface.
# @return [String] a hex-string topic.
def compute_topic(interface)
sig = signature(interface)
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig)))
end
# Build event signature string from ABI interface.
#
# @param interface [Hash] ABI event interface.
# @return [String] interface signature string.
def signature(interface)
name = interface.fetch("name")
inputs = interface.fetch("inputs", [])
types = inputs.map { |i| type(i) }
"#{name}(#{types.join(",")})"
end
# Gets the input type for events.
#
# @param input [Hash] events input.
# @return [String] input type.
def type(input)
if input["type"] == "tuple"
"(#{input["components"].map { |c| type(c) }.join(",")})"
elsif input["type"] == "enum"
"uint8"
else
input["type"]
end
end
# A decoded event log.
class LogDescription
# The event ABI interface used to decode the log.
attr_accessor :event_interface
# The the input argument of the event.
attr_accessor :args
# The named input argument of the event.
attr_accessor :kwargs
# The topic hash.
attr_accessor :topic
# Decodes event log argument values.
#
# @param event_interface [Hash] event ABI type.
# @param log [Hash] transaction receipt log
def initialize(event_interface, log)
@event_interface = event_interface
inputs = event_interface.fetch("inputs")
data = log.fetch("data")
topics = log.fetch("topics", [])
anonymous = event_interface.fetch("anonymous", false)
@topic = topics[0] if !anonymous
@args, @kwargs = Event.decode_log(inputs, data, topics, anonymous)
end
# The event name. (e.g. Transfer)
def name
@name ||= event_interface.fetch("name")
end
# The event signature. (e.g. Transfer(address,address,uint256))
def signature
@signature ||= Abi::Event.signature(event_interface)
end
end
# Decodes a stream of receipt logs with a set of ABI interfaces.
#
# @param interfaces [Array] event ABI types.
# @param logs [Array] transaction receipt logs
# @return [Hash] an enumerator of LogDescription objects.
def decode_logs(interfaces, logs)
Enumerator.new do |y|
topic_to_interfaces = Hash[interfaces.map { |i| [compute_topic(i), i] }]
logs.each do |log|
topic = log.fetch("topics", [])[0]
if topic && interface = topic_to_interfaces[topic]
y << [log, LogDescription.new(interface, log)]
else
y << [log, nil]
end
end
end
end
# Decodes event log argument values.
#
# @param inputs [Array] event ABI types.
# @param data [String] ABI event data to be decoded.
# @param topics [Array] ABI event topics to be decoded.
# @param anonymous [Boolean] If event signature is excluded from topics.
# @return [[Array, Hash]] decoded positional arguments and decoded keyword arguments.
# @raise [DecodingError] if decoding fails for type.
def decode_log(inputs, data, topics, anonymous = false)
topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] }
topic_types = topic_inputs.map do |i|
if i["type"] == "tuple"
Type.parse(i["type"], i["components"], i["name"])
else
i["type"]
end
end
data_types = data_inputs.map do |i|
if i["type"] == "tuple"
Type.parse(i["type"], i["components"], i["name"])
else
i["type"]
end
end
# If event is anonymous, all topics are arguments. Otherwise, the first
# topic will be the event signature.
if anonymous == false
topics = topics[1..-1]
end
decoded_topics = topics.map.with_index { |t, i| Abi.decode([topic_types[i]], t)[0] }
decoded_data = Abi.decode(data_types, data)
args = []
kwargs = {}
inputs.each_with_index do |input, index|
if input["indexed"]
value = decoded_topics[topic_inputs.index(input)]
else
value = decoded_data[data_inputs.index(input)]
end
args[index] = value
kwargs[input["name"].to_sym] = value
end
return args, kwargs
end
end
end
end
================================================
FILE: lib/eth/abi/function.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Provides a module to decode transaction input data.
module Function
extend self
# Build function signature string from ABI interface.
#
# @param interface [Hash] ABI function interface.
# @return [String] interface signature string.
def signature(interface)
name = interface.fetch("name")
inputs = interface.fetch("inputs", [])
types = inputs.map { |i| type(i) }
"#{name}(#{types.join(",")})"
end
# Compute selector for ABI function interface.
#
# @param interface [Hash] ABI function interface.
# @return [String] a hex-string selector.
def selector(interface)
sig = signature(interface)
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig))[0, 8])
end
# Gets the input type for functions.
#
# @param input [Hash] function input.
# @return [String] input type.
def type(input)
if input["type"] == "tuple"
"(#{input["components"].map { |c| type(c) }.join(",")})"
elsif input["type"] == "enum"
"uint8"
else
input["type"]
end
end
# A decoded function call.
class CallDescription
# The function ABI interface used to decode the call.
attr_accessor :function_interface
# The positional arguments of the call.
attr_accessor :args
# The named arguments of the call.
attr_accessor :kwargs
# The function selector.
attr_accessor :selector
# Creates a description object for a decoded function call.
#
# @param function_interface [Hash] function ABI type.
# @param selector [String] function selector hex-string.
# @param args [Array] decoded positional arguments.
# @param kwargs [Hash] decoded keyword arguments.
def initialize(function_interface, selector, args, kwargs)
@function_interface = function_interface
@selector = selector
@args = args
@kwargs = kwargs
end
# The function name. (e.g. transfer)
def name
@name ||= function_interface.fetch("name")
end
# The function signature. (e.g. transfer(address,uint256))
def signature
@signature ||= Function.signature(function_interface)
end
end
# Decodes a transaction input with a set of ABI interfaces.
#
# @param interfaces [Array] function ABI types.
# @param data [String] transaction input data.
# @return [CallDescription, nil] a CallDescription object or nil if selector unknown.
def decode(interfaces, data)
data = Util.remove_hex_prefix(data)
selector = Util.prefix_hex(data[0, 8])
payload = Util.prefix_hex(data[8..] || "")
selector_to_interfaces = Hash[interfaces.map { |i| [selector(i), i] }]
if (interface = selector_to_interfaces[selector])
inputs = interface.fetch("inputs", [])
types = inputs.map { |i| type(i) }
args = Abi.decode(types, payload)
kwargs = {}
inputs.each_with_index do |input, i|
name = input.fetch("name", "")
kwargs[name.to_sym] = args[i] unless name.empty?
end
CallDescription.new(interface, selector, args, kwargs)
end
end
end
end
end
================================================
FILE: lib/eth/abi/packed/encoder.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Encapsulates the module for non-standard packed encoding used in Solidity.
module Packed
# Provides a utility module to assist encoding ABIs.
module Encoder
extend self
# Encodes a specific value, either static or dynamic in non-standard
# packed encoding mode.
#
# @param type [Eth::Abi::Type] type to be encoded.
# @param arg [String|Number] value to be encoded.
# @return [String] the packed encoded type.
# @raise [EncodingError] if value does not match type.
# @raise [ArgumentError] if encoding fails for type.
def type(type, arg)
case type
when /^uint(\d+)$/
uint(arg, $1.to_i / 8)
when /^int(\d+)$/
int(arg, $1.to_i / 8)
when "bool"
bool(arg)
when /^ureal(\d+)x(\d+)$/, /^ufixed(\d+)x(\d+)$/
ufixed(arg, $1.to_i / 8, $2.to_i)
when /^real(\d+)x(\d+)$/, /^fixed(\d+)x(\d+)$/
fixed(arg, $1.to_i / 8, $2.to_i)
when "string"
string(arg)
when /^bytes(\d+)$/
bytes(arg, $1.to_i)
when "bytes"
string(arg)
when /^tuple\((.+)\)$/
tuple($1.split(","), arg)
when /^hash(\d+)$/
hash(arg, $1.to_i / 8)
when "address"
address(arg)
when /^(.+)\[\]$/
array($1, arg)
when /^(.+)\[(\d+)\]$/
fixed_array($1, arg, $2.to_i)
else
raise EncodingError, "Unhandled type: #{type}"
end
end
private
# Properly encodes signed integers.
def uint(value, byte_size)
raise ArgumentError, "Don't know how to handle this input." unless value.is_a? Numeric
raise ValueOutOfBounds, "Number out of range: #{value}" if value > Constant::UINT_MAX or value < Constant::UINT_MIN
i = value.to_i
Util.zpad_int i, byte_size
end
# Properly encodes signed integers.
def int(value, byte_size)
raise ArgumentError, "Don't know how to handle this input." unless value.is_a? Numeric
raise ValueOutOfBounds, "Number out of range: #{value}" if value > Constant::INT_MAX or value < Constant::INT_MIN
real_size = byte_size * 8
i = value.to_i % 2 ** real_size
Util.zpad_int i, byte_size
end
# Properly encodes booleans.
def bool(value)
raise EncodingError, "Argument is not bool: #{value}" unless value.instance_of? TrueClass or value.instance_of? FalseClass
(value ? "\x01" : "\x00").b
end
# Properly encodes unsigned fixed-point numbers.
def ufixed(value, byte_size, decimals)
raise ArgumentError, "Don't know how to handle this input." unless value.is_a? Numeric
raise ValueOutOfBounds, value unless value >= 0 and value < 2 ** decimals
scaled_value = (value * (10 ** decimals)).to_i
uint(scaled_value, byte_size)
end
# Properly encodes signed fixed-point numbers.
def fixed(value, byte_size, decimals)
raise ArgumentError, "Don't know how to handle this input." unless value.is_a? Numeric
raise ValueOutOfBounds, value unless value >= -2 ** (decimals - 1) and value < 2 ** (decimals - 1)
scaled_value = (value * (10 ** decimals)).to_i
int(scaled_value, byte_size)
end
# Properly encodes byte(-string)s.
def bytes(value, length)
raise EncodingError, "Expecting String: #{value}" unless value.instance_of? String
value = handle_hex_string value, length
raise ArgumentError, "Value must be a string of length #{length}" unless value.is_a?(String) && value.bytesize == length
value.b
end
# Properly encodes (byte-)strings.
def string(value)
raise ArgumentError, "Value must be a string" unless value.is_a?(String)
value.b
end
# Properly encodes tuples.
def tuple(types, values)
Abi.solidity_packed(types, values)
end
# Properly encodes hash-strings.
def hash(value, byte_size)
raise EncodingError, "Argument too long: #{value}" unless byte_size > 0 and byte_size <= 32
hash_bytes = handle_hex_string value, byte_size
hash_bytes.b
end
# Properly encodes addresses.
def address(value)
if value.is_a? Address
# from checksummed address with 0x prefix
Util.zpad_hex value.to_s[2..-1], 20
elsif value.is_a? Integer
# address from integer
Util.zpad_int value, 20
elsif value.size == 20
# address from encoded address
Util.zpad value, 20
elsif value.size == 40
# address from hexadecimal address
Util.zpad_hex value, 20
elsif value.size == 42 and value[0, 2] == "0x"
# address from hexadecimal address with 0x prefix
Util.zpad_hex value[2..-1], 20
else
raise EncodingError, "Could not parse address: #{value}"
end
end
# Properly encodes dynamic-sized arrays.
def array(type, values)
values.map { |value| type(type, value) }.join.b
end
# Properly encodes fixed-size arrays.
def fixed_array(type, values, size)
raise ArgumentError, "Array size does not match" unless values.size == size
array(type, values)
end
# The ABI encoder needs to be able to determine between a hex `"123"`
# and a binary `"123"` string.
def handle_hex_string(val, len)
if Util.prefixed? val or
(len === val.size / 2 and Util.hex? val)
# There is no way telling whether a string is hex or binary with certainty
# in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
# Additionally, if the string size is exactly the double of the expected
# binary size, we can assume a hex value.
Util.hex_to_bin val
else
# Everything else will be assumed binary or raw string.
val.b
end
end
end
end
end
end
================================================
FILE: lib/eth/abi/type.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
module Abi
# Provides a class to handle and parse common ABI types.
class Type
# Provides a specific parser error if type cannot be determined.
class ParseError < StandardError; end
# The base attribute, e.g., `string` or `bytes`.
attr :base_type
# The sub-type attribute, e.g., `256` as size of an uint256.
attr :sub_type
# The dimension attribute, e.g., `[10]` for an array of size 10.
attr :dimensions
# The components of a tuple type.
attr :components
# The name of tuple component.
attr :name
# Create a new Type object for base types, sub types, and dimensions.
# Should not be used; use {Type.parse} instead.
#
# @param base_type [String] the base-type attribute.
# @param sub_type [String] the sub-type attribute.
# @param dimensions [Array] the dimension attribute.
# @param components [Array] the components attribute.
# @param component_name [String] the tuple component's name.
# @return [Eth::Abi::Type] an ABI type object.
def initialize(base_type, sub_type, dimensions, components = nil, component_name = nil)
sub_type = sub_type.to_s
@base_type = base_type
@sub_type = sub_type
@dimensions = dimensions
@components = components
@name = component_name
end
# Converts the self.parse method into a constructor.
konstructor :parse
# Attempts to parse a string containing a common Solidity type.
# Creates a new Type upon success (using konstructor).
#
# @param type [String] a common Solidity type.
# @param components [Array] the components attribute.
# @param component_name [String] the tuple component's name.
# @return [Eth::Abi::Type] a parsed Type object.
# @raise [ParseError] if it fails to parse the type.
def parse(type, components = nil, component_name = nil)
if type.is_a?(Type)
@base_type = type.base_type
@sub_type = type.sub_type
@dimensions = type.dimensions
@components = type.components
@name = type.name
return
end
# ensure the type string is reasonable before attempting to parse
raise ParseError, "Invalid type format" unless type.is_a? String
if type.start_with?("tuple(") || type.start_with?("(")
tuple_str = type.start_with?("tuple(") ? type : "tuple#{type}"
inner, rest = extract_tuple(tuple_str)
inner_types = split_tuple_types(inner)
inner_types.each { |t| Type.parse(t) }
base_type = "tuple"
sub_type = ""
dimension = rest
components ||= inner_types.map { |t| { "type" => t } }
else
match = /\A([a-z]+)([0-9]*x?[0-9]*)((?:\[\d+\]|\[\])*)\z/.match(type)
raise ParseError, "Invalid type format" unless match
_, base_type, sub_type, dimension = match.to_a
sub_type = "256" if %w[uint int].include?(base_type) && sub_type.empty?
end
# type dimension can only be numeric or empty for dynamic arrays
dims = dimension.scan(/\[\d+\]|\[\]/)
raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
# enforce base types
validate_base_type base_type, sub_type
# return a new Type (using konstructor)
sub_type = sub_type.to_s
@base_type = base_type
@sub_type = sub_type
@dimensions = dims.map { |x| x == "[]" ? 0 : x[1...-1].to_i }
@components = components.map { |component| Abi::Type.parse(component["type"], component.dig("components"), component.dig("name")) } if components&.any?
@name = component_name
end
# Creates a new uint256 type used for size.
#
# @return [Eth::Abi::Type] a uint256 size type.
def self.size_type
@size_type ||= new("uint", 256, [])
end
# Compares two types for their attributes.
#
# @param another_type [Eth::Abi::Type] another type to be compared.
# @return [Boolean] true if all attributes match.
def ==(another_type)
base_type == another_type.base_type and
sub_type == another_type.sub_type and
dimensions == another_type.dimensions
end
# Computes the size of a type if possible.
#
# @return [Integer] the size of the type; or nil if not available.
def size
s = nil
if dimensions.empty?
if !(["string", "bytes", "tuple"].include?(base_type) and sub_type.empty?)
s = 32
elsif base_type == "tuple" and components&.none?(&:dynamic?)
s = components.sum(&:size)
end
elsif dimensions.last != 0 and !nested_sub.dynamic?
s = dimensions.last * nested_sub.size
end
@size ||= s
end
# Helpes to determine whether array is of dynamic size.
#
# @return [Boolean] true if array is of dynamic size.
def dynamic?
size.nil?
end
# Types can have nested sub-types in arrays.
#
# @return [Eth::Abi::Type] nested sub-type.
def nested_sub
@nested_sub ||= self.class.new(base_type, sub_type, dimensions[0...-1], components, name)
end
# Allows exporting the type as string.
#
# @return [String] the type string.
def to_s
if base_type == "tuple"
"(" + components.map(&:to_s).join(",") + ")" + (dimensions.size > 0 ? dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join : "")
elsif dimensions.empty?
if %w[string bytes].include?(base_type) and sub_type.empty?
base_type
else
"#{base_type}#{sub_type}"
end
else
"#{base_type}#{sub_type}#{dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join}"
end
end
private
# Validates all known base types and raises if an issue occurs.
def validate_base_type(base_type, sub_type)
case base_type
when "string"
# string can not have any suffix
raise ParseError, "String type must have no suffix or numerical suffix" unless sub_type.empty?
when "bytes"
# bytes can be no longer than 32 bytes
raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub_type.empty? or (sub_type.to_i <= 32 and sub_type.to_i > 0)
when "tuple"
# tuples can not have any suffix
raise ParseError, "Tuple type must have no suffix or numerical suffix" unless sub_type.empty?
when "uint", "int"
# integers must have a numerical suffix
raise ParseError, "Integer type must have numerical suffix" unless sub_type =~ /\A[0-9]+\z/
# integer size must be valid
size = sub_type.to_i
raise ParseError, "Integer size out of bounds" unless size >= 8 and size <= 256
raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
when "ureal", "real", "fixed", "ufixed"
# floats must have valid dimensional suffix
raise ParseError, "Real type must have suffix of form <size>x<decimals>, e.g. 128x128" unless sub_type =~ /\A[0-9]+x[0-9]+\z/
size, decimals = sub_type.split("x").map(&:to_i)
total = size + decimals
raise ParseError, "Real size out of bounds (max 32 bytes)" unless total >= 8 and total <= 256
raise ParseError, "Real size must be multiples of 8" unless size % 8 == 0
when "hash"
# hashs must have numerical suffix
raise ParseError, "Hash type must have numerical suffix" unless sub_type =~ /\A[0-9]+\z/
when "address"
# addresses cannot have any suffix
raise ParseError, "Address cannot have suffix" unless sub_type.empty?
when "bool"
# booleans cannot have any suffix
raise ParseError, "Bool cannot have suffix" unless sub_type.empty?
else
# we cannot parse arbitrary types such as 'decimal' or 'hex'
raise ParseError, "Unknown base type"
end
end
# Extracts the inner type list and trailing dimensions from an inline tuple definition.
def extract_tuple(type)
idx = 6 # skip "tuple("
depth = 1
while idx < type.length && depth > 0
case type[idx]
when "("
depth += 1
when ")"
depth -= 1
end
idx += 1
end
raise ParseError, "Invalid tuple format" unless depth.zero?
inner = type[6...(idx - 1)]
rest = type[idx..] || ""
[inner, rest]
end
# Splits a tuple component list into individual type strings, handling nested tuples.
def split_tuple_types(str)
types = []
depth = 0
current = ""
str.each_char do |ch|
case ch
when "("
depth += 1
current << ch
when ")"
depth -= 1
current << ch
when ","
if depth.zero?
types << current
current = ""
else
current << ch
end
else
current << ch
end
end
types << current unless current.empty?
types
end
end
end
end
================================================
FILE: lib/eth/abi.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
require "konstructor"
# Provides the {Eth} module.
module Eth
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
# ref: https://docs.soliditylang.org/en/develop/abi-spec.html
module Abi
extend self
# Provides a special encoding error if anything fails to encode.
class EncodingError < StandardError; end
# Provides a special decoding error if anything fails to decode.
class DecodingError < StandardError; end
# Provides a special out-of-bounds error for values.
class ValueOutOfBounds < StandardError; end
# Encodes Application Binary Interface (ABI) data. It accepts multiple
# arguments and encodes using the head/tail mechanism.
#
# @param types [Array] types to be ABI-encoded.
# @param args [Array] values to be ABI-encoded.
# @param packed [Bool] set true to return packed encoding (default: `false`).
# @return [String] the encoded ABI data.
def encode(types, args, packed = false)
return solidity_packed(types, args) if packed
types = [types] unless types.instance_of? Array
args = [args] unless args.instance_of? Array
raise ArgumentError, "Types and values must be the same length" if types.length != args.length
# parse all types
parsed_types = types.map { |t| Type === t ? t : Type.parse(t) }
# prepare the "head"
head_size = (0...args.size)
.map { |i| parsed_types[i].size or 32 }
.reduce(0, &:+)
head, tail = "", ""
# encode types and arguments
args.each_with_index do |arg, i|
if parsed_types[i].dynamic?
head += Abi::Encoder.type(Type.size_type, head_size + tail.size)
tail += Abi::Encoder.type(parsed_types[i], arg)
else
head += Abi::Encoder.type(parsed_types[i], arg)
end
end
# return the encoded ABI blob
"#{head}#{tail}"
end
# Encodes Application Binary Interface (ABI) data in non-standard packed mode.
# It accepts multiple arguments and encodes using the head/tail mechanism.
#
# @param types [Array] types to be ABI-encoded.
# @param args [Array] values to be ABI-encoded.
# @return [String] the encoded packed ABI data.
# @raise [ArgumentError] if types and args are of different size.
def solidity_packed(types, args)
raise ArgumentError, "Types and values must be the same length" if types.length != args.length
# We do not use the type system for packed encoding but want to call the parser once
# to enforce the type validation.
_ = types.map { |t| Type === t ? t : Type.parse(t) }
packed = types.zip(args).map do |type, arg|
Abi::Packed::Encoder.type(type, arg)
end.join
packed.force_encoding(Encoding::ASCII_8BIT)
end
# Decodes Application Binary Interface (ABI) data. It accepts multiple
# arguments and decodes using the head/tail mechanism.
#
# @param types [Array] the ABI to be decoded.
# @param data [String] ABI data to be decoded.
# @return [Array] the decoded ABI data.
def decode(types, data)
# accept hex abi but decode it first
data = Util.hex_to_bin data if Util.hex? data
# parse all types
parsed_types = types.map { |t| Type.parse(t) }
# prepare output data
outputs = [nil] * types.size
start_positions = [nil] * types.size + [data.size]
pos = 0
parsed_types.each_with_index do |t, i|
if t.dynamic?
# record start position for dynamic type
start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32])
j = i - 1
while j >= 0 and start_positions[j].nil?
start_positions[j] = start_positions[i]
j -= 1
end
pos += 32
else
# get data directly for static types
outputs[i] = data[pos, t.size]
pos += t.size
end
end
# add start position equal the length of the entire data
j = types.size - 1
while j >= 0 and start_positions[j].nil?
start_positions[j] = start_positions[types.size]
j -= 1
end
raise DecodingError, "Not enough data for head" unless pos <= data.size
# add dynamic types
parsed_types.each_with_index do |t, i|
if t.dynamic?
offset, next_offset = start_positions[i, 2]
outputs[i] = data[offset...next_offset]
end
end
# return the decoded ABI types and data
parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out) }
end
end
end
require "eth/abi/packed/encoder"
require "eth/abi/decoder"
require "eth/abi/encoder"
require "eth/abi/event"
require "eth/abi/function"
require "eth/abi/type"
================================================
FILE: lib/eth/address.rb
================================================
# Copyright (c) 2016-2025 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.
# Provides the {Eth} module.
module Eth
# The {Eth::Address} class to handle checksummed Ethereum addresses.
class Address
# The literal zero address 0x0.
ZERO = "0x0000000000000000000000000000000000000000"
# Provides a special checksum error if EIP-55 is violated.
class CheckSumError < StandardError; end
# The prefixed and checksummed Ethereum address.
attr_reader :address
# Constructor of the {Eth::Address} class. Creates a new hex
# prefixed address.
#
# @param address [String] hex string representing an ethereum address.
def initialize(address)
unless Util.hex? address
raise CheckSumError, "Unknown address type #{address}!"
end
@address = Util.prefix_hex address
unless self.valid?
raise CheckSumError, "Invalid address provided #{address}"
end
end
# Checks that the address is valid.
#
# @return [Boolean] true if valid address.
def valid?
if !matches_any_format?
false
elsif not_checksummed?
true
else
checksum_matches?
end
end
# Checks that the address is the zero address.
#
# @return [Boolean] true if the address is the zero address.
def zero?
address == ZERO
end
# Generate a checksummed address.
#
# @return [String] prefixed hexstring representing an checksummed address.
def checksummed
raise CheckSumError, "Invalid address: #{address}" unless matches_any_format?
cased = unprefixed.chars.zip(checksum.chars).map do |char, check|
check.match(/[0-7]/) ? char.downcase : char.upcase
end
Util.prefix_hex cased.join
end
alias :to_s :checksummed
private
# Checks whether the address checksum matches.
def checksum_matches?
address == checksummed
end
# Checks whether the address is not checksummed.
def not_checksummed?
all_uppercase? || all_lowercase?
end
# Checks whether the address is all upper-case.
def all_uppercase?
address.match /(?:0[xX])[A-F0-9]{40}/
end
# Checks whether the address is all lower-case.
def all_lowercase?
address.match /(?:0[xX])[a-f0-9]{40}/
end
# Checks whether the address matches any known format.
def matches_any_format?
address.match /\A(?:0[xX])[a-fA-F0-9]{40}\z/
end
# Computes the checksum of the address.
def checksum
Util.bin_to_hex Util.keccak256 unprefixed.downcase
end
# Removes the hex prefix.
def unprefixed
Util.remove_hex_prefix address
end
end
end
================================================
FILE: lib/eth/api.rb
================================================
# Copyright (c) 2016-2025 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.
# Provides the {Eth} module.
module Eth
# Provides the `Eth::Api` module grouping known RPC commands.
module Api
# Implements the available RPC-APIs provided by Geth version 1.10.26.
COMMANDS = [
"account_ecRecover",
"account_new",
"account_signData",
"account_signTransaction",
"account_signTypedData",
"admin_addPeer",
"admin_addTrustedPeer",
"admin_clearHistory",
"admin_datadir",
"admin_exportChain",
"admin_getDatadir",
"admin_getNodeInfo",
"admin_getPeers",
"admin_importChain",
"admin_nodeInfo",
"admin_peerEvents",
"admin_peers",
"admin_removePeer",
"admin_removeTrustedPeer",
"admin_sleep",
"admin_sleepBlocks",
"admin_startHTTP",
"admin_startRPC",
"admin_startWS",
"admin_stopHTTP",
"admin_stopRPC",
"admin_stopWS",
"clef_deriveAccount",
"clef_listWallets",
"clef_openWallet",
"clique_discard",
"clique_getSigner",
"clique_getSigners",
"clique_getSignersAtHash",
"clique_getSnapshot",
"clique_getSnapshotAtHash",
"clique_proposals",
"clique_propose",
"clique_status",
"db_getHex",
"db_getString",
"db_putHex",
"db_putString",
"debug_accountRange",
"debug_backtraceAt",
"debug_blockProfile",
"debug_chaindbCompact",
"debug_chaindbProperty",
"debug_cpuProfile",
"debug_dbAncient",
"debug_dbAncients",
"debug_dbGet",
"debug_dumpBlock",
"debug_freeOSMemory",
"debug_freezeClient",
"debug_gcStats",
"debug_getAccessibleState",
"debug_getBadBlocks",
"debug_getBlockRlp",
"debug_getHeaderRlp",
"debug_getModifiedAccountsByHash",
"debug_getModifiedAccountsByNumber",
"debug_getRawBlock",
"debug_getRawHeader",
"debug_getRawReceipts",
"debug_getRawTransaction",
"debug_getTrieFlushInterval",
"debug_goTrace",
"debug_intermediateRoots",
"debug_memStats",
"debug_mutexProfile",
"debug_preimage",
"debug_printBlock",
"debug_seedHash",
"debug_setBlockProfileRate",
"debug_setGCPercent",
"debug_setHead",
"debug_setMutexProfileFraction",
"debug_setTrieFlushInterval",
"debug_stacks",
"debug_standardTraceBadBlockToFile",
"debug_standardTraceBlockToFile",
"debug_startCPUProfile",
"debug_startGoTrace",
"debug_stopCPUProfile",
"debug_stopGoTrace",
"debug_storageRangeAt",
"debug_subscribe",
"debug_traceBadBlock",
"debug_traceBlock",
"debug_traceBlockByHash",
"debug_traceBlockByNumber",
"debug_traceBlockFromFile",
"debug_traceCall",
"debug_traceChain",
"debug_traceTransaction",
"debug_verbosity",
"debug_vmodule",
"debug_writeBlockProfile",
"debug_writeMemProfile",
"debug_writeMutexProfile",
"dev_addWithdrawal",
"dev_setFeeRecipient",
"eth_accounts",
"eth_blobBaseFee",
"eth_blockNumber",
"eth_call",
"eth_chainId",
"eth_coinbase",
"eth_compile",
"eth_compileLLL",
"eth_compileSerpent",
"eth_compileSolidity",
"eth_contract",
"eth_createAccessList",
"eth_defaultAccount",
"eth_defaultBlock",
"eth_estimateGas",
"eth_feeHistory",
"eth_fillTransaction",
"eth_filter",
"eth_gasPrice",
"eth_getAccounts",
"eth_getBalance",
"eth_getBlobBaseFee",
"eth_getBlock",
"eth_getBlockByHash",
"eth_getBlockByNumber",
"eth_getBlockNumber",
"eth_getBlockReceipts",
"eth_getBlockTransactionCount",
"eth_getBlockTransactionCountByHash",
"eth_getBlockTransactionCountByNumber",
"eth_getBlockUncleCount",
"eth_getCode",
"eth_getCoinbase",
"eth_getCompilers",
"eth_getFilterChanges",
"eth_getFilterLogs",
"eth_getGasPrice",
"eth_getHashrate",
"eth_getHeaderByHash",
"eth_getHeaderByNumber",
"eth_getLogs",
"eth_getMaxPriorityFeePerGas",
"eth_getMining",
"eth_getPendingTransactions",
"eth_getProof",
"eth_getProtocolVersion",
"eth_getRawTransaction",
"eth_getRawTransactionFromBlock",
"eth_getStorageAt",
"eth_getSyncing",
"eth_getTransaction",
"eth_getTransactionByBlockHashAndIndex",
"eth_getTransactionByBlockNumberAndIndex",
"eth_getTransactionByHash",
"eth_getTransactionCount",
"eth_getTransactionFromBlock",
"eth_getTransactionReceipt",
"eth_getUncle",
"eth_getUncleByBlockHashAndIndex",
"eth_getUncleByBlockNumberAndIndex",
"eth_getUncleCountByBlockHash",
"eth_getUncleCountByBlockNumber",
"eth_getWork",
"eth_hashrate",
"eth_iban",
"eth_icapNamereg",
"eth_isSyncing",
"eth_maxPriorityFeePerGas",
"eth_mining",
"eth_namereg",
"eth_newBlockFilter",
"eth_newFilter",
"eth_newPendingTransactionFilter",
"eth_pendingTransactions",
"eth_protocolVersion",
"eth_resend",
"eth_sendIBANTransaction",
"eth_sendRawTransaction",
"eth_sendTransaction",
"eth_sign",
"eth_signTransaction",
"eth_simulateV1",
"eth_submitHashrate",
"eth_submitTransaction",
"eth_submitWork",
"eth_syncing",
"eth_uninstallFilter",
"eth_unsubscribe",
"les_addBalance",
"les_clientInfo",
"les_getCheckpoint",
"les_getCheckpointContractAddress",
"les_latestCheckpoint",
"les_priorityClientInfo",
"les_serverInfo",
"les_setClientParams",
"les_setDefaultParams",
"miner_getHashrate",
"miner_setEtherbase",
"miner_setExtra",
"miner_setGasLimit",
"miner_setGasPrice",
"miner_setRecommitInterval",
"miner_start",
"miner_stop",
"net_getListening",
"net_getPeerCount",
"net_getVersion",
"net_listening",
"net_peerCount",
"net_version",
"personal_deriveAccount",
"personal_ecRecover",
"personal_importRawKey",
"personal_initializeWallet",
"personal_initializeWallets",
"personal_listAccounts",
"personal_listWallets",
"personal_lockAccount",
"personal_newAccount",
"personal_openWallet",
"personal_sendTransaction",
"personal_sign",
"personal_signTransaction",
"personal_unlockAccount",
"personal_unpair",
"rpc_getModules",
"rpc_modules",
"shh_addToGroup",
"shh_getFilterChanges",
"shh_getMessages",
"shh_hasIdentity",
"shh_newFilter",
"shh_newGroup",
"shh_newIdentity",
"shh_post",
"shh_uninstallFilter",
"shh_version",
"txpool_content",
"txpool_contentFrom",
"txpool_getContent",
"txpool_getInspect",
"txpool_getStatus",
"txpool_inspect",
"txpool_status",
"web3_admin",
"web3_bzz",
"web3_clientVersion",
"web3_createBatch",
"web3_currentProvider",
"web3_db",
"web3_debug",
"web3_dev",
"web3_eth",
"web3_fromAscii",
"web3_fromDecimal",
"web3_fromICAP",
"web3_fromUtf8",
"web3_fromWei",
"web3_isAddress",
"web3_isChecksumAddress",
"web3_isConnected",
"web3_isIBAN",
"web3_miner",
"web3_net",
"web3_padLeft",
"web3_padRight",
"web3_personal",
"web3_providers",
"web3_reset",
"web3_rpc",
"web3_setProvider",
"web3_settings",
"web3_sha3",
"web3_shh",
"web3_toAscii",
"web3_toChecksumAddress",
"web3_toDecimal",
"web3_toHex",
"web3_toUtf8",
"web3_toWei",
"web3_txpool",
"web3_version",
]
end
end
================================================
FILE: lib/eth/bls.rb
================================================
# frozen_string_literal: true
require "bls"
module Eth
# Helper methods for interacting with BLS12-381 points and signatures
module Bls
module_function
# Decode a compressed G1 public key from hex.
# @param [String] hex a compressed G1 point
# @return [BLS::PointG1]
def decode_public_key(hex)
BLS::PointG1.from_hex Util.remove_hex_prefix(hex)
end
# Encode a G1 public key to compressed hex.
# @param [BLS::PointG1] point
# @return [String] hex string prefixed with 0x
def encode_public_key(point)
Util.prefix_hex point.to_hex(compressed: true)
end
# Decode a compressed G2 signature from hex.
# @param [String] hex a compressed G2 point
# @return [BLS::PointG2]
def decode_signature(hex)
BLS::PointG2.from_hex Util.remove_hex_prefix(hex)
end
# Encode a G2 signature to compressed hex.
# @param [BLS::PointG2] point
# @return [String] hex string prefixed with 0x
def encode_signature(point)
Util.prefix_hex point.to_hex(compressed: true)
end
# Derive a compressed public key from a private key.
# @param [String] priv_hex private key as hex
# @return [String] compressed G1 public key (hex)
def get_public_key(priv_hex)
key = BLS.get_public_key Util.remove_hex_prefix(priv_hex)
encode_public_key key
end
# Sign a message digest with the given private key.
# @param [String] message message digest (hex)
# @param [String] priv_hex private key as hex
# @return [String] compressed G2 signature (hex)
def sign(message, priv_hex)
sig = BLS.sign Util.remove_hex_prefix(message),
Util.remove_hex_prefix(priv_hex)
encode_signature sig
end
# Verify a BLS signature using pairings. This mirrors the behaviour of
# the BLS12-381 pairing precompile.
# @param [String] message message digest (hex)
# @param [String] signature_hex compressed G2 signature (hex)
# @param [String] pubkey_hex compressed G1 public key (hex)
# @return [Boolean] verification result
def verify(message, signature_hex, pubkey_hex)
signature = decode_signature(signature_hex)
pubkey = decode_public_key(pubkey_hex)
BLS.verify(signature, Util.remove_hex_prefix(message), pubkey)
end
end
end
================================================
FILE: lib/eth/chain.rb
================================================
# Copyright (c) 2016-2025 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.
# Provides the {Eth} module.
module Eth
# Encapsulates {Eth::Chain} IDs and utilities for EIP-155 compatibility.
# Ref: https://eips.ethereum.org/EIPS/eip-155
module Chain
extend self
# Provides a special replay protection error if EIP-155 is violated.
class ReplayProtectionError < StandardError; end
# Chain ID for Ethereum mainnet.
ETHEREUM = 1.freeze
# Chain ID for Expanse mainnet.
EXPANSE = 2.freeze
# Chain ID for Optimistic Ethereum mainnet.
OPTIMISM = 10.freeze
# Chain ID for Cronos mainnet.
CRONOS = 25.freeze
# Chain ID for Rootstock mainnet.
RSK = 30.freeze
# Chain ID for BNB Smart Chain mainnet.
BNB = 56.freeze
# Chain ID for Ethereum Classic mainnet.
CLASSIC = 61.freeze
# Chain ID for POA Network mainnet.
POA_NET = 99.freeze
# Chain ID for xDAI mainnet (now Gnosis Chain).
XDAI = 100.freeze
# Chain ID for Gnosis mainnet (formerly xDAI).
GNOSIS = XDAI.freeze
# Chain ID for the Matic mainnet (now Polygon).
MATIC = 137.freeze
# Chain ID for the Polygon mainnet (formerly Matic).
POLYGON = MATIC.freeze
# Chain ID for Filecoin mainnet.
FILECOIN = 314.freeze
# Chain ID for the Cronos zkEVM chain.
CRONOS_ZK = 388.freeze
# Chain ID for Redstone Optimistic Rollup.
REDSTONE = 690.freeze
# Chain ID for the Polygon zkEVM.
POLYGON_ZK = 1101.freeze
# Chain ID for the Lisk layer 2.
LISK = 1135.freeze
# Chain ID for Moonbeam
MOONBEAM = 1284.freeze
# Chain ID for Base mainnet.
BASE = 8453.freeze
# Chain ID for the EVMOS mainnet.
EVMOS = 9001.freeze
# Chain ID for the Celo layer 2.
CELO = 42220.freeze
# Chain ID for Arbitrum One mainnet.
ARBITRUM = 42161.freeze
# Chain ID for Avalance C-Chain mainnet.
AVALANCHE = 43114.freeze
# Chain ID for Linea mainnet.
LINEA = 59144.freeze
# Chain ID for the Scroll layer 2.
SCROLL = 534352.freeze
# Chain ID for Morden (Ethereum) testnet.
MORDEN = 2.freeze
# Chain ID for Ropsten testnet.
ROPSTEN = 3.freeze
# Chain ID for Rinkeby testnet.
RINKEBY = 4.freeze
# Chain ID for Goerli testnet.
GOERLI = 5.freeze
# Chain ID for Kotti testnet.
KOTTI = 6.freeze
# Chain ID for Kovan testnet.
KOVAN = 42.freeze
# Chain ID for Morden (Classic) testnet.
MORDEN_CLASSIC = 62.freeze
# Chain ID for Mordor testnet.
MORDOR = 63.freeze
# Chain ID for Optimistm Kovan testnet.
KOVAN_OPTIMISM = 69.freeze
# Chain ID for Arbitrum xDAI testnet.
XDAI_ARBITRUM = 200.freeze
# Chain ID for Optimistic Goerli testnet.
GOERLI_OPTIMISM = 420.freeze
# Chain ID for Moonriver testnet
MOONRIVER = 1285.freeze
# Chain ID for Moonbase alpha
MOONBASE = 1287.freeze
# Chain ID for the Garnet Holesky testnet
GARNET = 17069.freeze
# Chain ID for the Polygon Mumbai testnet.
MUMBAI = 80001.freeze
# Chain ID for Arbitrum Rinkeby testnet.
RINKEBY_ARBITRUM = 421611.freeze
# Chain ID for Arbitrum Goerli testnet.
GOERLI_ARBITRUM = 421613.freeze
# Chain ID for Hoodi testnet.
HOODI = 560048.freeze
# Chain ID for Sepolia testnet.
SEPOLIA = 11155111.freeze
# Chain ID for Holesovice testnet.
HOLESOVICE = 11166111.freeze
# Chain ID for Holesovice testnet.
HOLESKY = HOLESOVICE
# Chain ID for Basecamp testnet.
BASECAMP = 123420001114.freeze
# Chain ID for the geth private network preset.
PRIVATE_GETH = 1337.freeze
# Indicates wether the given `v` indicates a legacy chain value
# used by ledger wallets without EIP-155 replay protection.
#
# @param v [Integer] the signature's `v` value.
# @return [Boolean] true if ledger's legacy value.
def ledger?(v)
[0, 1].include? v
end
# Indicates wether the given `v` indicates a legacy chain value
# without EIP-155 replay protection.
#
# @param v [Integer] the signature's `v` value.
# @return [Boolean] true if legacy value.
def legacy?(v)
[27, 28].include? v
end
# Convert a given `v` value to an ECDSA recovery id for the given
# EIP-155 chain ID.
#
# @param v [Integer] the signature's `v` value.
# @param chain_id [Integer] the chain id the signature was generated on.
# @return [Integer] the recovery id corresponding to `v`.
# @raise [ReplayProtectionError] if the given `v` is invalid.
def to_recovery_id(v, chain_id = ETHEREUM)
e = 0 + 2 * chain_id + 35
i = 1 + 2 * chain_id + 35
if ledger? v
# some wallets are using a `v` of 0 or 1 (ledger)
return v
elsif legacy? v
# this is the pre-EIP-155 legacy case
return v - 27
elsif [e, i].include? v
# this is the EIP-155 case
return v - 35 - 2 * chain_id
else
raise ReplayProtectionError, "Invalid v #{v} value for chain ID #{chain_id}. Invalid chain ID?"
end
end
# Converts a recovery ID into the expected `v` on a given chain.
#
# @param recovery_id [Integer] signature recovery id.
# @param chain_id [Integer] the chain id the signature was generated on.
# @return [Integer] the signature's `v` value.
def to_v(recovery_id, chain_id = nil)
if chain_id.nil? or chain_id < 1
v = 27 + recovery_id
else
v = 2 * chain_id + 35 + recovery_id
end
return v
end
# Converts a `v` value into a chain ID. This does not work for legacy signatures
# with `v < 36` that do not conform with EIP-155.
#
# @param v [Integer] the signature's `v` value.
# @return [Integer] the chain id as per EIP-155 or `nil` if there is no replay protection.
def to_chain_id(v)
return nil if v < 36
chain_id = (v - 35) / 2
return nil if chain_id < 1
return chain_id
end
end
end
================================================
FILE: lib/eth/client/http.rb
================================================
# Copyright (c) 2016-2025 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 "uri"
require "httpx"
# Provides the {Eth} module.
module Eth
# Provides an HTTP/S-RPC client with basic authentication.
class Client::Http < Client
# The host of the HTTP endpoint.
attr_reader :host
# The port of the HTTP endpoint.
attr_reader :port
# The full URI of the HTTP endpoint, including path.
attr_reader :uri
# Attribute indicator for SSL.
attr_reader :ssl
# Attribute for user.
attr_reader :user
# Constructor for the HTTP Client. Should not be used; use
# {Client.create} instead.
#
# @param host [String] an URI pointing to an HTTP RPC-API.
def initialize(host)
super
uri = URI.parse(host)
raise ArgumentError, "Unable to parse the HTTP-URI!" unless ["http", "https"].include? uri.scheme
@host = uri.host
@port = uri.port
@ssl = uri.scheme == "https"
if !(uri.user.nil? && uri.password.nil?)
@user = uri.user
@password = uri.password
if uri.query
@uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}?#{uri.query}")
else
@uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}")
end
else
@uri = uri
end
@client = HTTPX.plugin(:persistent).with(headers: { "Content-Type" => "application/json" })
end
# Sends an RPC request to the connected HTTP client.
#
# @param payload [Hash] the RPC request parameters.
# @return [String] a JSON-encoded response.
def send_request(payload)
response = @client.post(@uri, body: payload)
response.body.to_s
end
end
private
# Attribute for password.
attr_reader :password
end
================================================
FILE: lib/eth/client/ipc.rb
================================================
# Copyright (c) 2016-2025 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 "socket"
# Provides the {Eth} module.
module Eth
# Provides an IPC-RPC client.
class Client::Ipc < Client
# The path of the IPC socket.
attr_accessor :path
# Constructor for the IPC Client. Should not be used; use
# {Client.create} instead.
#
# @param path [String] an URI pointing to an IPC RPC-API.
def initialize(path)
super
@path = path
end
# Sends an RPC request to the connected IPC socket.
#
# @param payload [Hash] the RPC request parameters.
# @return [String] a JSON-encoded response.
def send_request(payload)
socket = UNIXSocket.new(@path)
socket.puts(payload)
read = socket.recvmsg(nil)[0]
until read.end_with?("\n")
read = read << socket.recvmsg(nil)[0]
end
socket.close
return read
end
end
end
================================================
FILE: lib/eth/client/ws.rb
================================================
# Copyright (c) 2016-2025 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 "socket"
require "openssl"
require "uri"
require "base64"
require "securerandom"
require "digest/sha1"
require "thread"
require "ipaddr"
# Provides the {Eth} module.
module Eth
# Provides a WS/S-RPC client with automatic reconnection support.
class Client::Ws < Client
# The host of the WebSocket endpoint.
attr_reader :host
# The port of the WebSocket endpoint.
attr_reader :port
# The full URI of the WebSocket endpoint, including path.
attr_reader :uri
# Attribute indicator for SSL.
attr_reader :ssl
# Constructor for the WebSocket client. Should not be used; use
# {Client.create} instead.
#
# @param host [String] a URI pointing to a WebSocket RPC-API.
def initialize(host)
super
@uri = URI.parse(host)
raise ArgumentError, "Unable to parse the WebSocket-URI!" unless %w[ws wss].include?(@uri.scheme)
@host = @uri.host
@port = @uri.port
@ssl = @uri.scheme == "wss"
@path = build_path(@uri)
@mutex = Mutex.new
@socket = nil
@fragments = nil
end
# Sends an RPC request to the connected WebSocket endpoint.
#
# @param payload [Hash] the RPC request parameters.
# @return [String] a JSON-encoded response.
def send_request(payload)
attempts = 0
begin
attempts += 1
@mutex.synchronize do
ensure_socket
write_frame(@socket, payload)
return read_message(@socket)
end
rescue IOError, SystemCallError => e
@mutex.synchronize { close_socket }
retry if attempts < 2
raise e
end
end
# Closes the underlying WebSocket connection.
#
# @return [void]
def close
@mutex.synchronize { close_socket }
end
private
def ensure_socket
return if @socket && !@socket.closed?
socket = open_socket
begin
perform_handshake(socket)
@socket = socket
@fragments = nil
rescue StandardError
begin
socket.close unless socket.closed?
rescue IOError, SystemCallError
nil
end
@socket = nil
raise
end
end
# Establishes the TCP socket for the RPC connection and upgrades it to TLS
# when a secure endpoint is requested. TLS sessions enforce peer
# verification, load the default system trust store, and enable hostname
# verification when the current OpenSSL bindings support it.
#
# @return [TCPSocket, OpenSSL::SSL::SSLSocket] the established socket.
# @raise [IOError, SystemCallError, OpenSSL::SSL::SSLError] if the socket
# cannot be opened or the TLS handshake fails.
def open_socket
tcp = TCPSocket.new(@host, @port)
return tcp unless @ssl
context = OpenSSL::SSL::SSLContext.new
params = { verify_mode: OpenSSL::SSL::VERIFY_PEER }
params[:verify_hostname] = true if context.respond_to?(:verify_hostname=)
context.set_params(params)
context.cert_store = OpenSSL::X509::Store.new.tap(&:set_default_paths)
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp, context)
ssl_socket.hostname = @host
ssl_socket.sync_close = true
ssl_socket.connect
ssl_socket
end
def perform_handshake(socket)
key = Base64.strict_encode64(SecureRandom.random_bytes(16))
request = build_handshake_request(key)
socket.write(request)
response = read_handshake_response(socket)
verify_handshake!(response, key)
end
def build_handshake_request(key)
origin = build_origin_header
host_header = build_host_header
"GET #{@path} HTTP/1.1\r\n" \
"Host: #{host_header}\r\n" \
"Upgrade: websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Version: 13\r\n" \
"Sec-WebSocket-Key: #{key}\r\n" \
"Origin: #{origin}\r\n\r\n"
end
def read_handshake_response(socket)
response = +""
until response.end_with?("\r\n\r\n")
chunk = socket.readpartial(1024)
raise IOError, "Incomplete WebSocket handshake" if chunk.nil?
response << chunk
end
response
rescue EOFError
raise IOError, "Incomplete WebSocket handshake"
end
def verify_handshake!(response, key)
status_line = response.lines.first&.strip
unless status_line&.start_with?("HTTP/1.1 101")
raise IOError, "WebSocket handshake failed (status: #{status_line || "unknown"})"
end
accept = response[/Sec-WebSocket-Accept:\s*(.+)\r/i, 1]&.strip
expected = Base64.strict_encode64(Digest::SHA1.digest("#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
raise IOError, "WebSocket handshake failed (missing accept header)" unless accept
raise IOError, "WebSocket handshake failed (invalid accept header)" unless accept == expected
end
def write_frame(socket, payload, opcode = 0x1)
frame_payload = payload.is_a?(String) ? payload.dup : payload.to_s
mask_key = SecureRandom.random_bytes(4)
header = [0x80 | opcode]
length = frame_payload.bytesize
if length <= 125
header << (0x80 | length)
elsif length <= 0xFFFF
header << (0x80 | 126)
header.concat([length].pack("n").bytes)
else
header << (0x80 | 127)
header.concat([length].pack("Q>").bytes)
end
masked_payload = apply_mask(frame_payload, mask_key)
socket.write(header.pack("C*") + mask_key + masked_payload)
end
def read_message(socket)
loop do
frame = read_frame(socket)
return frame if frame
end
end
def read_frame(socket)
header = read_bytes(socket, 2)
byte1, byte2 = header.bytes
opcode = byte1 & 0x0F
masked = (byte2 & 0x80) == 0x80
length = byte2 & 0x7F
length = read_bytes(socket, 2).unpack1("n") if length == 126
length = read_bytes(socket, 8).unpack1("Q>") if length == 127
mask_key = masked ? read_bytes(socket, 4).bytes : nil
payload = read_bytes(socket, length)
payload_bytes = payload.bytes
if mask_key
payload_bytes.map!.with_index { |byte, index| byte ^ mask_key[index % 4] }
end
data = payload_bytes.pack("C*")
case opcode
when 0x0
(@fragments ||= +"") << data
if (byte1 & 0x80) == 0x80
message = @fragments.dup
@fragments = nil
message
else
nil
end
when 0x1, 0x2
if (byte1 & 0x80) == 0x80
data
else
@fragments = data
nil
end
when 0x8
close_socket
raise IOError, "WebSocket closed"
when 0x9
write_frame(socket, data, 0xA)
nil
when 0xA
nil
else
nil
end
end
def read_bytes(socket, length)
data = +""
while data.bytesize < length
chunk = socket.read(length - data.bytesize)
raise IOError, "Unexpected end of WebSocket stream" if chunk.nil? || chunk.empty?
data << chunk
end
data
end
def apply_mask(payload, mask_key)
mask_bytes = mask_key.bytes
payload.bytes.map.with_index { |byte, index| byte ^ mask_bytes[index % 4] }.pack("C*")
end
def build_origin_header
scheme = @ssl ? "https" : "http"
host = format_origin_host(@uri.host)
default_port = @ssl ? 443 : 80
port = @uri.port
port_suffix = port == default_port ? "" : ":#{port}"
"#{scheme}://#{host}#{port_suffix}"
end
def build_host_header
"#{format_host(@uri.host)}:#{@uri.port}"
end
def format_origin_host(host)
return "localhost" if loopback_host?(host)
format_host(host)
end
def format_host(host)
return host unless host&.include?(":")
host.start_with?("[") ? host : "[#{host}]"
end
def loopback_host?(host)
return false if host.nil?
return true if host == "localhost"
IPAddr.new(host).loopback?
rescue IPAddr::InvalidAddressError
false
end
def close_socket
return unless @socket
begin
write_frame(@socket, [1000].pack("n"), 0x8)
rescue IOError, SystemCallError
# ignore errors while closing
ensure
begin
@socket.close unless @socket.closed?
rescue IOError, SystemCallError
nil
end
@socket = nil
@fragments = nil
end
end
def build_path(uri)
path = uri.path
path = "/" if path.nil? || path.empty?
query = uri.query
path += "?#{query}" if query
path
end
end
end
================================================
FILE: lib/eth/client.rb
================================================
# Copyright (c) 2016-2025 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.
# Provides the {Eth} module.
module Eth
# Provides the {Eth::Client} super-class to connect to Ethereum
# network's RPC-API endpoints (IPC, HTTP/S, or WS/S).
class Client
# The client's RPC-request ID starting at 0.
attr_reader :id
# The connected network's chain ID.
attr_reader :chain_id
# The connected network's client default account.
attr_accessor :default_account
# The default transaction max priority fee per gas in Wei, defaults to {Tx::DEFAULT_PRIORITY_FEE}.
attr_accessor :max_priority_fee_per_gas
# The default transaction max fee per gas in Wei, defaults to {Tx::DEFAULT_GAS_PRICE}.
attr_accessor :max_fee_per_gas
# The block number used for archive calls.
attr_accessor :block_number
# A custom error type if a contract interaction fails.
class ContractExecutionError < StandardError; end
# Raised when an RPC call returns an error. Carries the error code and the optional
# hex-encoded error data to support custom error decoding.
class RpcError < IOError
attr_reader :data
attr_reader :code
# Constructor for the {RpcError} class.
#
# @param message [String] the error message returned by the RPC.
# @param data [String] optional hex encoded error data.
# @param code [String] optional error code returned by the RPC.
def initialize(message, data = nil, code = nil)
super(message)
@data = data
@code = code
end
end
# Creates a new RPC-Client, either by providing an HTTP/S host, WS/S host,
# or an IPC path. Supports basic authentication with username and password.
#
# **Note**, this sets the following gas defaults: {Tx::DEFAULT_PRIORITY_FEE}
# and {Tx::DEFAULT_GAS_PRICE. Use {#max_priority_fee_per_gas} and
# {#max_fee_per_gas} to set custom values prior to submitting transactions.
#
# @param host [String] either an HTTP/S host, WS/S host, or an IPC path.
# @return [Eth::Client::Ipc] an IPC client.
# @return [Eth::Client::Http] an HTTP client.
# @return [Eth::Client::Ws] a WebSocket client.
# @raise [ArgumentError] in case it cannot determine the client type.
def self.create(host)
return Client::Ipc.new host if host.end_with? ".ipc"
return Client::Http.new host if host.start_with? "http"
return Client::Ws.new host if host.start_with? "ws"
raise ArgumentError, "Unable to detect client type!"
end
# Constructor for the {Eth::Client} super-class. Should not be used;
# use {Client.create} intead.
def initialize(_)
@id = 0
@max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE
@max_fee_per_gas = Tx::DEFAULT_GAS_PRICE
end
# Gets the default account (first account) of the connected client.
#
# **Note**, that many remote providers (e.g., Infura) do not provide
# any accounts.
#
# @return [Eth::Address] the default account address.
def default_account
raise ArgumentError, "The default account is not available on remote connections!" unless local? || @default_account
@default_account ||= Address.new eth_accounts["result"].first
end
# Gets the chain ID of the connected network.
#
# @return [Integer] the chain ID.
def chain_id
@chain_id ||= eth_chain_id["result"].to_i 16
end
# Gets the balance for an address.
#
# @param address [Eth::Address] the address to get the balance for.
# @return [Integer] the balance in Wei.
def get_balance(address)
eth_get_balance(address)["result"].to_i 16
end
# Gets the next nonce for an address used to draft new transactions.
#
# @param address [Eth::Address] the address to get the nonce for.
# @return [Integer] the next nonce to be used.
def get_nonce(address)
eth_get_transaction_count(address, "pending")["result"].to_i 16
end
# Resolves an ENS name to an Ethereum address on the connected chain.
#
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
# @param registry [String] the address for the ENS registry.
# @param coin_type [Integer] the coin type as per EIP-2304.
# @return [Eth::Address] the Ethereum address resolved from the ENS record.
def resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::CoinType::ETHEREUM)
ens = Ens::Resolver.new(self, registry)
ens.resolve(ens_name, coin_type)
end
# Simply transfer Ether to an account and waits for it to be mined.
# Uses `eth_accounts` and external signer if no sender key is
# provided.
#
# See {#transfer} for params and overloads.
#
# @return [String] the transaction hash once it is mined.
def transfer_and_wait(destination, amount, **kwargs)
wait_for_tx(transfer(destination, amount, **kwargs))
end
# Simply transfer Ether to an account without any call data or
# access lists attached. Uses `eth_accounts` and external signer
# if no sender key is provided.
#
# **Note**, that many remote providers (e.g., Infura) do not provide
# any accounts. Provide a `sender_key:` if you experience issues.
#
# @overload transfer(destination, amount)
# @param destination [Eth::Address] the destination address.
# @param amount [Integer] the transfer amount in Wei.
# @overload transfer(destination, amount, **kwargs)
# @param destination [Eth::Address] the destination address.
# @param amount [Integer] the transfer amount in Wei.
# @param **sender_key [Eth::Key] the sender private key.
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
# @param **nonce [Integer] optional specific nonce for transaction.
# @return [String] the local transaction hash.
def transfer(destination, amount, **kwargs)
params = {
value: amount,
to: destination,
gas_limit: Tx::DEFAULT_GAS_LIMIT,
chain_id: chain_id,
}
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
end
# Transfers a token that implements the ERC20 `transfer()` interface.
#
# See {#transfer_erc20} for params and overloads.
#
# @return [Object] returns the result of the transaction.
def transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs)
transact_and_wait(erc20_contract, "transfer", destination, amount, **kwargs)
end
# Transfers a token that implements the ERC20 `transfer()` interface.
#
# **Note**, that many remote providers (e.g., Infura) do not provide
# any accounts. Provide a `sender_key:` if you experience issues.
#
# @overload transfer_erc20(erc20_contract, destination, amount)
# @param erc20_contract [Eth::Contract] the ERC20 contract to write to.
# @param destination [Eth::Address] the destination address.
# @param amount [Integer] the transfer amount (mind the `decimals()`).
# @overload transfer_erc20(erc20_contract, destination, amount, **kwargs)
# @param erc20_contract [Eth::Contract] the ERC20 contract to write to.
# @param destination [Eth::Address] the destination address.
# @param amount [Integer] the transfer amount (mind the `decimals()`).
# @param **sender_key [Eth::Key] the sender private key.
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
# @param **gas_limit [Integer] optional gas limit override for the transfer.
# @param **nonce [Integer] optional specific nonce for transaction.
# @param **tx_value [Integer] optional transaction value field filling.
# @return [Object] returns the result of the transaction.
def transfer_erc20(erc20_contract, destination, amount, **kwargs)
destination = destination.to_s if destination.instance_of? Eth::Address
transact(erc20_contract, "transfer", destination, amount, **kwargs)
end
# Deploys a contract and waits for it to be mined. Uses
# `eth_accounts` or external signer if no sender key is provided.
#
# See {#deploy} for params and overloads.
#
# @return [String] the contract address once it's mined.
def deploy_and_wait(contract, *args, **kwargs)
hash = wait_for_tx(deploy(contract, *args, **kwargs))
addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
contract.address = Address.new(addr).to_s
end
# Deploys a contract. Uses `eth_accounts` or external signer
# if no sender key is provided.
#
# **Note**, that many remote providers (e.g., Infura) do not provide
# any accounts. Provide a `sender_key:` if you experience issues.
#
# @overload deploy(contract)
# @param contract [Eth::Contract] contracts to deploy.
# @overload deploy(contract, *args)
# @param contract [Eth::Contract] the contracts to deploy.
# @param *args (optional) variable constructor parameter list.
# @overload deploy(contract, *args, **kwargs)
# @param contract [Eth::Contract] the contracts to deploy.
# @param *args (optional) variable constructor parameter list.
# @param **sender_key [Eth::Key] the sender private key.
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
# @param **nonce [Integer] optional specific nonce for transaction.
# @return [String] the transaction hash.
# @raise [ArgumentError] in case the contract does not have any source.
def deploy(contract, *args, **kwargs)
raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
raise ArgumentError, "Missing contract constructor params!" if contract.constructor_inputs.length != args.length
data = contract.bin
unless args.empty?
data += encode_constructor_params(contract, args)
end
gas_limit = if kwargs[:gas_limit]
kwargs[:gas_limit]
else
Tx.estimate_intrinsic_gas(data) + Tx::CREATE_GAS
end
params = {
value: 0,
gas_limit: gas_limit,
chain_id: chain_id,
data: data,
}
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
end
# Calls a contract function without executing it
# (non-transactional contract read).
#
# @overload call(contract, function)
# @param contract [Eth::Contract] the subject contract to call.
# @param function [String] method name to be called.
# @overload call(contract, function, *args)
# @param contract [Eth::Contract] the subject contract to call.
# @param function [String] method name to be called.
# @param *args optional function arguments.
# @overload call(contract, function, *args, **kwargs)
# @param contract [Eth::Contract] the subject contract to call.
# @param function [String] method name to be called.
# @param *args optional function arguments.
# @param **sender_key [Eth::Key] the sender private key.
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
# @return [Object] returns the result of the call.
# @see https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call
def call(contract, function, *args, **kwargs)
function = contract.function(function, args: args.size)
output = function.decode_call_result(
eth_call(
{
data: function.encode_call(*args),
to: kwargs[:address] || contract.address,
from: kwargs[:from],
gas: kwargs[:gas],
gasPrice: kwargs[:gas_price],
value: kwargs[:value],
}.compact
)["result"]
)
if output&.length == 1
output[0]
else
output
end
rescue RpcError => e
raise ContractExecutionError, contract.decode_error(e)
end
# Executes a contract function with a transaction (transactional
# contract read/write).
#
# **Note**, that many remote providers (e.g., Infura) do not provide
# any accounts. Provide a `sender_key:` if you experience issues.
#
# @overload transact(contract, function)
# @param contract [Eth::Contract] the subject contract to write to.
# @param function [String] method name to be executed.
# @overload transact(contract, function, *args)
# @param contract [Eth::Contract] the subject contract to write to.
# @param function [String] method name to be executed.
# @param *args optional function arguments.
# @overload transact(contract, function, *args, **kwargs)
# @param contract [Eth::Contract] the subject contract to write to.
# @param function_name [String] method name to be executed.
# @param *args optional function arguments.
# @param **sender_key [Eth::Key] the sender private key.
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
# @param **address [Eth::Address] contract address.
# @param **gas_limit [Integer] optional gas limit override for transacting with the contract.
# @param **nonce [Integer] optional specific nonce for transaction.
# @param **tx_value [Integer] optional transaction value field filling.
# @return [Object] returns the result of the transaction.
def transact(contract, function, *args, **kwargs)
gas_limit = if kwargs[:gas_limit]
kwargs[:gas_limit]
else
Tx.estimate_intrinsic_gas(contract.bin)
end
params = {
value: kwargs[:tx_value] || 0,
gas_limit: gas_limit,
chain_id: chain_id,
to: kwargs[:address] || contract.address,
data: contract.function(function, args: args.size).encode_call(*args),
}
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
end
# Executes a contract function with a transaction and waits for it
# to be mined (transactional contract read/write).
#
# See {#transact} for params and overloads.
#
# @raise [Client::ContractExecutionError] if the execution fails.
# @return [Object, Boolean] returns the result of the transaction (hash and execution status).
def transact_and_wait(contract, function, *args, **kwargs)
begin
hash = wait_for_tx(transact(contract, function, *args, **kwargs))
return hash, tx_succeeded?(hash)
rescue RpcError => e
raise ContractExecutionError, contract.decode_error(e)
end
end
# Provides an interface to call `isValidSignature` as per EIP-1271 on a given
# smart contract to verify the given hash and signature matching the magic
# value.
#
# @param contract [Eth::Contract] a deployed contract implementing EIP-1271.
# @param hash [String] the message hash to be checked against the signature.
# @param signature [String] the signature to be recovered by the contract.
# @param magic [String] the expected magic value (defaults to `1626ba7e`).
# @return [Boolean] true if magic matches and signature is valid.
# @raise [ArgumentError] in case the contract cannot be called yet.
def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
hash = Util.hex_to_bin hash if Util.hex? hash
signature = Util.hex_to_bin signature if Util.hex? signature
magic = Util.hex_to_bin magic if Util.hex? magic
result = call(contract, "isValidSignature", hash, signature)
result === magic
end
# Gives control over resetting the RPC request ID back to zero.
# Usually not needed.
#
# @return [Integer] 0
def reset_id
@id = 0
end
# Checks whether a transaction is mined or not.
#
# @param hash [String] the transaction hash.
# @return [Boolean] true if included in a block.
def tx_mined?(hash)
mined_tx = eth_get_transaction_by_hash hash
!mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil?
end
# Checks whether a contract transaction succeeded or not.
#
# @param hash [String] the transaction hash.
# @return [Boolean] true if status is success.
def tx_succeeded?(hash)
tx_receipt = eth_get_transaction_receipt(hash)
!tx_receipt.nil? && !tx_receipt["result"].nil? && tx_receipt["result"]["status"] == "0x1"
end
# Waits for an transaction to be mined by the connected chain.
#
# @param hash [String] the transaction hash.
# @return [String] the transaction hash once the transaction is mined.
# @raise [Timeout::Error] if it's not mined within 5 minutes.
def wait_for_tx(hash)
start_time = Time.now
timeout = 300
retry_rate = 0.1
loop do
raise Timeout::Error if ((Time.now - start_time) > timeout)
return hash if tx_mined? hash
sleep retry_rate
end
end
# Metafunction to provide all known RPC commands defined in
# {Eth::Api} as snake_case methods to the {Eth::Client} classes.
Api::COMMANDS.each do |cmd|
method_name = cmd.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
define_method method_name do |*args|
send_command cmd, args
end
end
private
# Allows to determine if we work with a local connectoin
def local?
if self.instance_of? Eth::Client::Ipc
true
elsif self.host === "127.0.0.1" || self.host === "localhost"
true
else
false
end
end
# Prepares a transaction to be send for the given params.
def send_transaction(params, legacy, key, nonce)
if legacy
params.merge!({ gas_price: max_fee_per_gas })
else
params.merge!({
priority_fee: max_priority_fee_per_gas,
max_gas_fee: max_fee_per_gas,
})
end
unless key.nil?
# use the provided key as sender and signer
params.merge!({
from: key.address,
nonce: nonce || get_nonce(key.address),
})
tx = Eth::Tx.new(params)
tx.sign key
eth_send_raw_transaction(tx.hex)["result"]
else
# do not allow accessing accounts on remote connections
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
# use the default account as sender and external signer
params.merge!({
from: default_account,
nonce: nonce || get_nonce(default_account),
})
eth_send_transaction(params)["result"]
end
end
# Encodes constructor params
def encode_constructor_params(contract, args)
types = contract.constructor_inputs.map { |input| input.type }
Util.bin_to_hex(Eth::Abi.encode(types, args))
end
# Prepares parameters and sends the command to the client.
def send_command(command, args)
@block_number ||= "latest"
args << block_number if ["eth_getBalance", "eth_call"].include? command
payload = {
jsonrpc: "2.0",
method: command,
params: marshal(args),
id: next_id,
}
output = JSON.parse(send_request(payload.to_json))
if (err = output["error"])
raise RpcError.new(err["message"], err["data"], err["code"])
end
output
end
# Increments the request id.
def next_id
@id += 1
end
# expects Hash object
def camelize!(params)
params.transform_keys! do |k|
k = k.to_s.split(/_/).map(&:capitalize).join
k[0] = k[0].downcase
k.to_sym
end
end
# Recursively marshals all request parameters.
def marshal(params)
params = params.dup
if params.is_a? Array
params.map! { |param| marshal(param) }
elsif params.is_a? Hash
params = camelize!(params)
params.transform_values! { |param| marshal(param) }
elsif params.is_a? Numeric
Util.prefix_hex "#{params.to_i.to_s(16)}"
elsif params.is_a? Address
params.to_s
elsif Util.hex? params
Util.prefix_hex params
else
params
end
end
end
end
# Load the client/* libraries
require "eth/client/http"
require "eth/client/ipc"
require "eth/client/ws"
================================================
FILE: lib/eth/constant.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provides commonly used constants, such as zero bytes or zero keys.
module Constant
# The empty byte is defined as "".
BYTE_EMPTY = "".freeze
# The zero byte is 0x00.
BYTE_ZERO = "\x00".freeze
# The byte one is 0x01.
BYTE_ONE = "\x01".freeze
# The size of a 32-bit number.
TT32 = (2 ** 32).freeze
# The size of a 256-bit number.
TT256 = (2 ** 256).freeze
# The maximum possible value of an UInt256.
UINT_MAX = (2 ** 256 - 1).freeze
# The minimum possible value of an UInt256.
UINT_MIN = 0.freeze
# The maximum possible value of an Int256.
INT_MAX = (2 ** 255 - 1).freeze
# The minimum possible value of an Int256.
INT_MIN = (-2 ** 255).freeze
# A hash containing only zeros.
HASH_ZERO = ("\x00" * 32).freeze
# The RLP short length limit.
SHORT_LENGTH_LIMIT = 56.freeze
# The RLP long length limit.
LONG_LENGTH_LIMIT = (256 ** 8).freeze
# The RLP primitive type offset.
PRIMITIVE_PREFIX_OFFSET = 0x80.freeze
# The RLP array type offset.
LIST_PREFIX_OFFSET = 0xc0.freeze
# The binary encoding is ASCII (8-bit).
BINARY_ENCODING = "ASCII-8BIT".freeze
# Infinity as constant for convenience.
INFINITY = (1.0 / 0.0).freeze
end
end
================================================
FILE: lib/eth/contract/error.rb
================================================
# Copyright (c) 2016-2025 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.
# -*- encoding : ascii-8bit -*-
# Provides the {Eth} module.
module Eth
# Provide classes for contract custom errors.
class Contract::Error
attr_accessor :name, :inputs, :signature, :error_string
# Constructor of the {Eth::Contract::Error} class.
#
# @param data [Hash] contract abi data for the error.
def initialize(data)
@name = data["name"]
@inputs = data.fetch("inputs", []).map do |input|
Eth::Contract::FunctionInput.new(input)
end
@error_string = self.class.calc_signature(@name, @inputs)
@signature = self.class.encoded_error_signature(@error_string)
end
# Creates error strings.
#
# @param name [String] error name.
# @param inputs [Array<Eth::Contract::FunctionInput>] error input class list.
# @return [String] error string.
def self.calc_signature(name, inputs)
"#{name}(#{inputs.map { |x| x.parsed_type.to_s }.join(",")})"
end
# Encodes an error signature.
#
# @param signature [String] error signature.
# @return [String] encoded error signature string.
def self.encoded_error_signature(signature)
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(signature)[0..3]))
end
# Decodes a revert error payload.
#
# @param data [String] the hex-encoded revert data including selector.
# @return [Array] decoded error arguments.
def decode(data)
types = inputs.map(&:type)
payload = "0x" + data[10..]
Eth::Abi.decode(types, payload)
end
end
end
================================================
FILE: lib/eth/contract/event.rb
================================================
# Copyright (c) 2016-2025 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 o
gitextract_urvyeq_7/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── codeql.yml
│ ├── docs.yml
│ └── spec.yml
├── .gitignore
├── .gitmodules
├── .yardopts
├── AUTHORS.txt
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── SECURITY.md
├── abi/
│ ├── ens_registry.json
│ └── ens_resolver.json
├── bin/
│ ├── console
│ └── setup
├── codecov.yml
├── eth.gemspec
├── lib/
│ ├── eth/
│ │ ├── abi/
│ │ │ ├── decoder.rb
│ │ │ ├── encoder.rb
│ │ │ ├── event.rb
│ │ │ ├── function.rb
│ │ │ ├── packed/
│ │ │ │ └── encoder.rb
│ │ │ └── type.rb
│ │ ├── abi.rb
│ │ ├── address.rb
│ │ ├── api.rb
│ │ ├── bls.rb
│ │ ├── chain.rb
│ │ ├── client/
│ │ │ ├── http.rb
│ │ │ ├── ipc.rb
│ │ │ └── ws.rb
│ │ ├── client.rb
│ │ ├── constant.rb
│ │ ├── contract/
│ │ │ ├── error.rb
│ │ │ ├── event.rb
│ │ │ ├── function.rb
│ │ │ ├── function_input.rb
│ │ │ ├── function_output.rb
│ │ │ └── initializer.rb
│ │ ├── contract.rb
│ │ ├── eip712.rb
│ │ ├── ens/
│ │ │ ├── coin_type.rb
│ │ │ └── resolver.rb
│ │ ├── ens.rb
│ │ ├── key/
│ │ │ ├── decrypter.rb
│ │ │ └── encrypter.rb
│ │ ├── key.rb
│ │ ├── rlp/
│ │ │ ├── decoder.rb
│ │ │ ├── encoder.rb
│ │ │ ├── sedes/
│ │ │ │ ├── big_endian_int.rb
│ │ │ │ ├── binary.rb
│ │ │ │ └── list.rb
│ │ │ └── sedes.rb
│ │ ├── rlp.rb
│ │ ├── signature.rb
│ │ ├── solidity.rb
│ │ ├── tx/
│ │ │ ├── eip1559.rb
│ │ │ ├── eip2930.rb
│ │ │ ├── eip4844.rb
│ │ │ ├── eip7702.rb
│ │ │ └── legacy.rb
│ │ ├── tx.rb
│ │ ├── unit.rb
│ │ ├── util.rb
│ │ └── version.rb
│ └── eth.rb
└── spec/
├── eth/
│ ├── abi/
│ │ ├── decoder_spec.rb
│ │ ├── encoder_spec.rb
│ │ ├── event_spec.rb
│ │ ├── function_spec.rb
│ │ ├── packed/
│ │ │ └── encoder_spec.rb
│ │ └── type_spec.rb
│ ├── abi_spec.rb
│ ├── address_spec.rb
│ ├── bls_spec.rb
│ ├── chain_spec.rb
│ ├── client/
│ │ └── ws_spec.rb
│ ├── client_spec.rb
│ ├── constant_spec.rb
│ ├── contract/
│ │ ├── error_spec.rb
│ │ ├── event_spec.rb
│ │ ├── function_input_spec.rb
│ │ ├── function_output_spec.rb
│ │ ├── function_spec.rb
│ │ └── initializer_spec.rb
│ ├── contract_spec.rb
│ ├── eip712_spec.rb
│ ├── ens/
│ │ ├── coin_type_spec.rb
│ │ └── resolver_spec.rb
│ ├── ens_spec.rb
│ ├── key/
│ │ ├── decrypter_spec.rb
│ │ └── encrypter_spec.rb
│ ├── key_spec.rb
│ ├── rlp/
│ │ ├── sedes/
│ │ │ ├── big_endian_int_spec.rb
│ │ │ ├── binary_spec.rb
│ │ │ └── list_spec.rb
│ │ └── sedes_spec.rb
│ ├── rlp_spec.rb
│ ├── signature_spec.rb
│ ├── solidity_spec.rb
│ ├── tx/
│ │ ├── eip1559_spec.rb
│ │ ├── eip2930_spec.rb
│ │ ├── eip4844_spec.rb
│ │ ├── eip7702_spec.rb
│ │ └── legacy_spec.rb
│ ├── tx_spec.rb
│ ├── unit_spec.rb
│ └── util_spec.rb
├── eth_spec.rb
├── fixtures/
│ ├── abi/
│ │ ├── ENSRegistryWithFallback.json
│ │ ├── ERC1155.json
│ │ ├── ERC20.json
│ │ ├── ERC721.json
│ │ ├── Tuple.json
│ │ ├── Tuple2.json
│ │ └── ethers.json
│ ├── contracts/
│ │ ├── address_storage.sol
│ │ ├── deposit.sol
│ │ ├── dummy.sol
│ │ ├── erc20.sol
│ │ ├── error.sol
│ │ ├── greeter.sol
│ │ ├── signer.sol
│ │ ├── simple_registry.sol
│ │ ├── test_contract.sol
│ │ ├── tuple.sol
│ │ └── tuple2.sol
│ └── keys/
│ ├── testingtesting.json
│ ├── testpassword.json
│ └── testunknownkdf.json
└── spec_helper.rb
SYMBOL INDEX (550 symbols across 52 files)
FILE: lib/eth.rb
type Eth (line 16) | module Eth
FILE: lib/eth/abi.rb
type Eth (line 20) | module Eth
type Abi (line 24) | module Abi
class EncodingError (line 28) | class EncodingError < StandardError; end
class DecodingError (line 31) | class DecodingError < StandardError; end
class ValueOutOfBounds (line 34) | class ValueOutOfBounds < StandardError; end
function encode (line 43) | def encode(types, args, packed = false)
function solidity_packed (line 79) | def solidity_packed(types, args)
function decode (line 98) | def decode(types, data)
FILE: lib/eth/abi/decoder.rb
type Eth (line 18) | module Eth
type Abi (line 21) | module Abi
type Decoder (line 24) | module Decoder
function type (line 33) | def type(type, arg)
function primitive_type (line 132) | def primitive_type(type, data)
function tuple_dynamic_ranges (line 195) | def tuple_dynamic_ranges(offsets, total_size, head_size)
FILE: lib/eth/abi/encoder.rb
type Eth (line 19) | module Eth
type Abi (line 22) | module Abi
type Encoder (line 25) | module Encoder
function type (line 34) | def type(type, arg)
function primitive_type (line 60) | def primitive_type(type, arg)
function uint (line 88) | def uint(arg, type)
function int (line 99) | def int(arg, type)
function bool (line 110) | def bool(arg)
function ufixed (line 116) | def ufixed(arg, type)
function fixed (line 125) | def fixed(arg, type)
function bytes (line 135) | def bytes(arg, type)
function tuple (line 155) | def tuple(arg, type)
function coerce_number (line 190) | def coerce_number(arg)
function encode_array (line 203) | def encode_array(type, values)
function encode_dynamic_array (line 225) | def encode_dynamic_array(nested_sub, values)
function encode_static_array (line 236) | def encode_static_array(nested_sub, values)
function encode_array_elements (line 246) | def encode_array_elements(nested_sub, values)
function hash (line 264) | def hash(arg, type)
function address (line 285) | def address(arg)
function handle_hex_string (line 313) | def handle_hex_string(arg, type)
FILE: lib/eth/abi/event.rb
type Eth (line 18) | module Eth
type Abi (line 21) | module Abi
type Event (line 24) | module Event
function compute_topic (line 31) | def compute_topic(interface)
function signature (line 40) | def signature(interface)
function type (line 51) | def type(input)
class LogDescription (line 62) | class LogDescription
method initialize (line 79) | def initialize(event_interface, log)
method name (line 92) | def name
method signature (line 97) | def signature
function decode_logs (line 107) | def decode_logs(interfaces, logs)
function decode_log (line 130) | def decode_log(inputs, data, topics, anonymous = false)
FILE: lib/eth/abi/function.rb
type Eth (line 18) | module Eth
type Abi (line 21) | module Abi
type Function (line 24) | module Function
function signature (line 31) | def signature(interface)
function selector (line 42) | def selector(interface)
function type (line 51) | def type(input)
class CallDescription (line 62) | class CallDescription
method initialize (line 81) | def initialize(function_interface, selector, args, kwargs)
method name (line 89) | def name
method signature (line 94) | def signature
function decode (line 104) | def decode(interfaces, data)
FILE: lib/eth/abi/packed/encoder.rb
type Eth (line 18) | module Eth
type Abi (line 21) | module Abi
type Packed (line 24) | module Packed
type Encoder (line 27) | module Encoder
function type (line 38) | def type(type, arg)
function uint (line 74) | def uint(value, byte_size)
function int (line 82) | def int(value, byte_size)
function bool (line 91) | def bool(value)
function ufixed (line 97) | def ufixed(value, byte_size, decimals)
function fixed (line 105) | def fixed(value, byte_size, decimals)
function bytes (line 113) | def bytes(value, length)
function string (line 121) | def string(value)
function tuple (line 127) | def tuple(types, values)
function hash (line 132) | def hash(value, byte_size)
function address (line 139) | def address(value)
function array (line 166) | def array(type, values)
function fixed_array (line 171) | def fixed_array(type, values, size)
function handle_hex_string (line 178) | def handle_hex_string(val, len)
FILE: lib/eth/abi/type.rb
type Eth (line 18) | module Eth
type Abi (line 21) | module Abi
class Type (line 24) | class Type
class ParseError (line 27) | class ParseError < StandardError; end
method initialize (line 53) | def initialize(base_type, sub_type, dimensions, components = nil, ...
method parse (line 73) | def parse(type, components = nil, component_name = nil)
method size_type (line 121) | def self.size_type
method == (line 129) | def ==(another_type)
method size (line 138) | def size
method dynamic? (line 155) | def dynamic?
method nested_sub (line 162) | def nested_sub
method to_s (line 169) | def to_s
method validate_base_type (line 186) | def validate_base_type(base_type, sub_type)
method extract_tuple (line 237) | def extract_tuple(type)
method split_tuple_types (line 256) | def split_tuple_types(str)
FILE: lib/eth/address.rb
type Eth (line 16) | module Eth
class Address (line 19) | class Address
class CheckSumError (line 25) | class CheckSumError < StandardError; end
method initialize (line 34) | def initialize(address)
method valid? (line 47) | def valid?
method zero? (line 60) | def zero?
method checksummed (line 67) | def checksummed
method checksum_matches? (line 82) | def checksum_matches?
method not_checksummed? (line 87) | def not_checksummed?
method all_uppercase? (line 92) | def all_uppercase?
method all_lowercase? (line 97) | def all_lowercase?
method matches_any_format? (line 102) | def matches_any_format?
method checksum (line 107) | def checksum
method unprefixed (line 112) | def unprefixed
FILE: lib/eth/api.rb
type Eth (line 16) | module Eth
type Api (line 19) | module Api
FILE: lib/eth/bls.rb
type Eth (line 5) | module Eth
type Bls (line 7) | module Bls
function decode_public_key (line 13) | def decode_public_key(hex)
function encode_public_key (line 20) | def encode_public_key(point)
function decode_signature (line 27) | def decode_signature(hex)
function encode_signature (line 34) | def encode_signature(point)
function get_public_key (line 41) | def get_public_key(priv_hex)
function sign (line 50) | def sign(message, priv_hex)
function verify (line 62) | def verify(message, signature_hex, pubkey_hex)
FILE: lib/eth/chain.rb
type Eth (line 16) | module Eth
type Chain (line 20) | module Chain
class ReplayProtectionError (line 24) | class ReplayProtectionError < StandardError; end
function ledger? (line 175) | def ledger?(v)
function legacy? (line 184) | def legacy?(v)
function to_recovery_id (line 195) | def to_recovery_id(v, chain_id = ETHEREUM)
function to_v (line 220) | def to_v(recovery_id, chain_id = nil)
function to_chain_id (line 234) | def to_chain_id(v)
FILE: lib/eth/client.rb
type Eth (line 16) | module Eth
class Client (line 20) | class Client
class ContractExecutionError (line 41) | class ContractExecutionError < StandardError; end
class RpcError (line 45) | class RpcError < IOError
method initialize (line 54) | def initialize(message, data = nil, code = nil)
method create (line 73) | def self.create(host)
method initialize (line 82) | def initialize(_)
method default_account (line 94) | def default_account
method chain_id (line 102) | def chain_id
method get_balance (line 110) | def get_balance(address)
method get_nonce (line 118) | def get_nonce(address)
method resolve_ens (line 128) | def resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type...
method transfer_and_wait (line 140) | def transfer_and_wait(destination, amount, **kwargs)
method transfer (line 161) | def transfer(destination, amount, **kwargs)
method transfer_erc20_and_wait (line 176) | def transfer_erc20_and_wait(erc20_contract, destination, amount, **k...
method transfer_erc20 (line 199) | def transfer_erc20(erc20_contract, destination, amount, **kwargs)
method deploy_and_wait (line 210) | def deploy_and_wait(contract, *args, **kwargs)
method deploy (line 236) | def deploy(contract, *args, **kwargs)
method call (line 275) | def call(contract, function, *args, **kwargs)
method transact (line 322) | def transact(contract, function, *args, **kwargs)
method transact_and_wait (line 345) | def transact_and_wait(contract, function, *args, **kwargs)
method is_valid_signature (line 364) | def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
method reset_id (line 377) | def reset_id
method tx_mined? (line 385) | def tx_mined?(hash)
method tx_succeeded? (line 394) | def tx_succeeded?(hash)
method wait_for_tx (line 404) | def wait_for_tx(hash)
method local? (line 427) | def local?
method send_transaction (line 438) | def send_transaction(params, legacy, key, nonce)
method encode_constructor_params (line 472) | def encode_constructor_params(contract, args)
method send_command (line 478) | def send_command(command, args)
method next_id (line 495) | def next_id
method camelize! (line 500) | def camelize!(params)
method marshal (line 509) | def marshal(params)
FILE: lib/eth/client/http.rb
type Eth (line 19) | module Eth
class Client::Http (line 22) | class Client::Http < Client
method initialize (line 43) | def initialize(host)
method send_request (line 68) | def send_request(payload)
FILE: lib/eth/client/ipc.rb
type Eth (line 18) | module Eth
class Client::Ipc (line 21) | class Client::Ipc < Client
method initialize (line 30) | def initialize(path)
method send_request (line 39) | def send_request(payload)
FILE: lib/eth/client/ws.rb
type Eth (line 25) | module Eth
class Client::Ws (line 28) | class Client::Ws < Client
method initialize (line 46) | def initialize(host)
method send_request (line 63) | def send_request(payload)
method close (line 82) | def close
method ensure_socket (line 88) | def ensure_socket
method open_socket (line 115) | def open_socket
method perform_handshake (line 132) | def perform_handshake(socket)
method build_handshake_request (line 140) | def build_handshake_request(key)
method read_handshake_response (line 152) | def read_handshake_response(socket)
method verify_handshake! (line 164) | def verify_handshake!(response, key)
method write_frame (line 176) | def write_frame(socket, payload, opcode = 0x1)
method read_message (line 196) | def read_message(socket)
method read_frame (line 203) | def read_frame(socket)
method read_bytes (line 251) | def read_bytes(socket, length)
method apply_mask (line 261) | def apply_mask(payload, mask_key)
method build_origin_header (line 266) | def build_origin_header
method build_host_header (line 275) | def build_host_header
method format_origin_host (line 279) | def format_origin_host(host)
method format_host (line 284) | def format_host(host)
method loopback_host? (line 289) | def loopback_host?(host)
method close_socket (line 297) | def close_socket
method build_path (line 315) | def build_path(uri)
FILE: lib/eth/constant.rb
type Eth (line 18) | module Eth
type Constant (line 21) | module Constant
FILE: lib/eth/contract.rb
type Eth (line 20) | module Eth
class Contract (line 23) | class Contract
method initialize (line 38) | def initialize(name, bin, abi)
method from_file (line 56) | def self.from_file(file:, contract_index: 0)
method from_abi (line 71) | def self.from_abi(abi:, address:, name:)
method from_bin (line 88) | def self.from_bin(bin:, abi:, name:)
method address= (line 98) | def address=(addr)
method function (line 115) | def function(name, args: nil)
method error (line 127) | def error(name, args: nil)
method decode_error (line 137) | def decode_error(rpc_error)
method build (line 156) | def build
method parse_abi (line 183) | def parse_abi(abi)
FILE: lib/eth/contract/error.rb
type Eth (line 18) | module Eth
class Contract::Error (line 20) | class Contract::Error
method initialize (line 26) | def initialize(data)
method calc_signature (line 40) | def self.calc_signature(name, inputs)
method encoded_error_signature (line 48) | def self.encoded_error_signature(signature)
method decode (line 56) | def decode(data)
FILE: lib/eth/contract/event.rb
type Eth (line 18) | module Eth
class Contract::Event (line 20) | class Contract::Event
method initialize (line 24) | def initialize(data)
method name (line 31) | def name
method input_types (line 38) | def input_types
method inputs (line 45) | def inputs
method event_string (line 52) | def event_string
method signature (line 59) | def signature
method address (line 66) | def address
method set_address (line 73) | def set_address(address)
method decode_params (line 82) | def decode_params(topics, data = "0x")
method type_name (line 100) | def type_name(x)
FILE: lib/eth/contract/function.rb
type Eth (line 18) | module Eth
class Contract::Function (line 21) | class Contract::Function
method initialize (line 27) | def initialize(data)
method calc_signature (line 45) | def self.calc_signature(name, inputs)
method encoded_function_signature (line 53) | def self.encoded_function_signature(signature)
method encode_call (line 61) | def encode_call(*args)
method decode_call_result (line 71) | def decode_call_result(data)
FILE: lib/eth/contract/function_input.rb
type Eth (line 18) | module Eth
class Contract::FunctionInput (line 21) | class Contract::FunctionInput
method initialize (line 27) | def initialize(data)
method type (line 34) | def type
method parsed_type (line 39) | def parsed_type
FILE: lib/eth/contract/function_output.rb
type Eth (line 18) | module Eth
class Contract::FunctionOutput (line 21) | class Contract::FunctionOutput
method initialize (line 27) | def initialize(data)
method type (line 34) | def type
method parsed_type (line 41) | def parsed_type
FILE: lib/eth/contract/initializer.rb
type Eth (line 18) | module Eth
class Contract::Initializer (line 21) | class Contract::Initializer
method initialize (line 27) | def initialize(file)
method build_all (line 41) | def build_all
FILE: lib/eth/eip712.rb
type Eth (line 16) | module Eth
type Eip712 (line 20) | module Eip712
class TypedDataError (line 25) | class TypedDataError < StandardError; end
function type_dependencies (line 34) | def type_dependencies(primary_type, types, result = [])
function encode_type (line 68) | def encode_type(primary_type, types)
function hash_type (line 100) | def hash_type(primary_type, types)
function encode_data (line 111) | def encode_data(primary_type, data, types)
function encode_value (line 139) | def encode_value(type, value, types)
function encode_array (line 156) | def encode_array(type, value, types)
function hash_data (line 180) | def hash_data(primary_type, data, types)
function enforce_typed_data (line 191) | def enforce_typed_data(data)
function hash (line 207) | def hash(data)
FILE: lib/eth/ens.rb
type Eth (line 19) | module Eth
type Ens (line 22) | module Ens
FILE: lib/eth/ens/coin_type.rb
type Eth (line 16) | module Eth
type Ens (line 19) | module Ens
type CoinType (line 22) | module CoinType
FILE: lib/eth/ens/resolver.rb
type Eth (line 18) | module Eth
type Ens (line 22) | module Ens
class Resolver (line 25) | class Resolver
method initialize (line 37) | def initialize(client, address = DEFAULT_ADDRESS)
method owner (line 50) | def owner(ens_name)
method resolver (line 59) | def resolver(ens_name)
method resolve (line 72) | def resolve(ens_name, coin_type = Ens::CoinType::ETHEREUM)
method text (line 88) | def text(ens_name, key = "description")
method namehash (line 97) | def namehash(ens_name)
method normalize (line 111) | def normalize(input)
FILE: lib/eth/key.rb
type Eth (line 22) | module Eth
class Key (line 25) | class Key
method initialize (line 43) | def initialize(priv: nil)
method sign (line 72) | def sign(blob, chain_id = nil)
method personal_sign (line 94) | def personal_sign(message, chain_id = nil)
method sign_typed_data (line 109) | def sign_typed_data(typed_data, chain_id = nil)
method private_hex (line 117) | def private_hex
method private_bytes (line 125) | def private_bytes
method public_hex (line 133) | def public_hex
method public_hex_compressed (line 141) | def public_hex_compressed
method public_bytes (line 149) | def public_bytes
method public_bytes_compressed (line 156) | def public_bytes_compressed
method address (line 163) | def address
FILE: lib/eth/key/decrypter.rb
type Eth (line 16) | module Eth
class Key::Decrypter (line 19) | class Key::Decrypter
class DecrypterError (line 22) | class DecrypterError < StandardError; end
method perform (line 30) | def self.perform(data, password)
method initialize (line 40) | def initialize(data, password)
method perform (line 49) | def perform
method derive_key (line 62) | def derive_key(password)
method check_macs (line 73) | def check_macs
method decrypted_data (line 82) | def decrypted_data
method crypto_data (line 86) | def crypto_data
method ciphertext (line 90) | def ciphertext
method cipher_name (line 94) | def cipher_name
method cipher (line 98) | def cipher
method iv (line 106) | def iv
method salt (line 110) | def salt
method iterations (line 114) | def iterations
method kdf (line 118) | def kdf
method key_length (line 122) | def key_length
method n (line 126) | def n
method r (line 130) | def r
method p (line 134) | def p
method digest (line 138) | def digest
method digest_name (line 142) | def digest_name
FILE: lib/eth/key/encrypter.rb
type Eth (line 16) | module Eth
class Key::Encrypter (line 19) | class Key::Encrypter
class EncrypterError (line 22) | class EncrypterError < StandardError; end
method perform (line 37) | def self.perform(key, password, options = {})
method initialize (line 54) | def initialize(key, options = {})
method perform (line 70) | def perform(password)
method data (line 80) | def data
method cipher (line 119) | def cipher
method digest (line 127) | def digest
method derive_key (line 131) | def derive_key(password)
method encrypt (line 139) | def encrypt
method mac (line 143) | def mac
method kdf (line 147) | def kdf
method cipher_name (line 151) | def cipher_name
method digest_name (line 155) | def digest_name
method prf (line 159) | def prf
method key_length (line 163) | def key_length
method salt_length (line 167) | def salt_length
method iv_length (line 171) | def iv_length
method id (line 175) | def id
method iterations (line 179) | def iterations
method salt (line 183) | def salt
method iv (line 191) | def iv
method parallelization (line 199) | def parallelization
method block_size (line 203) | def block_size
FILE: lib/eth/rlp.rb
type Eth (line 23) | module Eth
type Rlp (line 26) | module Rlp
class RlpException (line 30) | class RlpException < StandardError; end
class EncodingError (line 33) | class EncodingError < RlpException; end
class DecodingError (line 36) | class DecodingError < RlpException; end
class SerializationError (line 39) | class SerializationError < RlpException; end
class DeserializationError (line 42) | class DeserializationError < RlpException; end
class Data (line 45) | class Data < String; end
function encode (line 51) | def encode(obj)
function decode (line 59) | def decode(rlp)
FILE: lib/eth/rlp/decoder.rb
type Eth (line 18) | module Eth
type Rlp (line 21) | module Rlp
type Decoder (line 24) | module Decoder
function perform (line 33) | def perform(rlp)
function consume_item (line 48) | def consume_item(rlp, start)
function consume_length_prefix (line 54) | def consume_length_prefix(rlp, start)
function enforce_no_zero_bytes (line 89) | def enforce_no_zero_bytes(rlp, start)
function consume_payload (line 94) | def consume_payload(rlp, start, type, length)
FILE: lib/eth/rlp/encoder.rb
type Eth (line 18) | module Eth
type Rlp (line 21) | module Rlp
type Encoder (line 24) | module Encoder
function perform (line 34) | def perform(obj)
function encode_raw (line 42) | def encode_raw(item)
function encode_primitive (line 50) | def encode_primitive(item)
function encode_list (line 58) | def encode_list(list)
function length_prefix (line 65) | def length_prefix(length, offset)
FILE: lib/eth/rlp/sedes.rb
type Eth (line 22) | module Eth
type Rlp (line 25) | module Rlp
type Sedes (line 28) | module Sedes
function infer (line 41) | def infer(obj)
function sedes? (line 53) | def sedes?(obj)
function big_endian_int (line 61) | def big_endian_int
function binary (line 68) | def binary
FILE: lib/eth/rlp/sedes/big_endian_int.rb
type Eth (line 18) | module Eth
type Rlp (line 21) | module Rlp
type Sedes (line 24) | module Sedes
class BigEndianInt (line 27) | class BigEndianInt
method initialize (line 32) | def initialize(size = nil)
method serialize (line 43) | def serialize(obj)
method deserialize (line 57) | def deserialize(serial)
FILE: lib/eth/rlp/sedes/binary.rb
type Eth (line 18) | module Eth
type Rlp (line 21) | module Rlp
type Sedes (line 24) | module Sedes
class Binary (line 27) | class Binary
method fixed_length (line 37) | def fixed_length(l, allow_empty: false)
method valid_type? (line 45) | def valid_type?(obj)
method initialize (line 55) | def initialize(min_length: 0, max_length: Constant::INFINITY, al...
method serialize (line 67) | def serialize(obj)
method deserialize (line 80) | def deserialize(serial)
method valid_length? (line 91) | def valid_length?(length)
FILE: lib/eth/rlp/sedes/list.rb
type Eth (line 18) | module Eth
type Rlp (line 21) | module Rlp
type Sedes (line 24) | module Sedes
class List (line 27) | class List < Array
method initialize (line 33) | def initialize(elements: [], strict: true)
method serialize (line 53) | def serialize(obj)
method deserialize (line 69) | def deserialize(serial)
FILE: lib/eth/signature.rb
type Eth (line 18) | module Eth
type Signature (line 21) | module Signature
class SignatureError (line 25) | class SignatureError < StandardError; end
function prefix_message (line 40) | def prefix_message(message)
function dissect (line 50) | def dissect(signature)
function recover (line 69) | def recover(blob, signature, chain_id = Chain::ETHEREUM)
function personal_recover (line 92) | def personal_recover(message, signature, chain_id = Chain::ETHEREUM)
function recover_typed_data (line 106) | def recover_typed_data(typed_data, signature, chain_id = Chain::ETHE...
function verify (line 119) | def verify(blob, signature, public_key, chain_id = Chain::ETHEREUM)
FILE: lib/eth/solidity.rb
type Eth (line 18) | module Eth
class Solidity (line 21) | class Solidity
class CompilerError (line 24) | class CompilerError < StandardError; end
method initialize (line 33) | def initialize(path = nil)
method compile (line 45) | def compile(contract)
method get_compiler_path (line 68) | def get_compiler_path(name = "solc")
FILE: lib/eth/tx.rb
type Eth (line 26) | module Eth
type Tx (line 29) | module Tx
class TransactionTypeError (line 33) | class TransactionTypeError < TypeError; end
class DecoderError (line 36) | class DecoderError < StandardError; end
class ParameterError (line 39) | class ParameterError < TypeError; end
function new (line 103) | def new(params, chain_id = Chain::ETHEREUM)
function decode (line 139) | def decode(hex)
function unsigned_copy (line 176) | def unsigned_copy(tx)
function estimate_intrinsic_gas (line 208) | def estimate_intrinsic_gas(data = "", list = [])
function validate_params (line 250) | def validate_params(fields)
function validate_eip1559_params (line 275) | def validate_eip1559_params(fields)
function validate_eip4844_params (line 290) | def validate_eip4844_params(fields)
function validate_eip7702_params (line 311) | def validate_eip7702_params(fields)
function validate_legacy_params (line 323) | def validate_legacy_params(fields)
function sanitize_chain (line 335) | def sanitize_chain(id)
function sanitize_address (line 346) | def sanitize_address(addr)
function sanitize_amount (line 360) | def sanitize_amount(val)
function sanitize_data (line 370) | def sanitize_data(data)
function sanitize_list (line 384) | def sanitize_list(list)
function sanitize_hashes (line 406) | def sanitize_hashes(list)
function signed? (line 419) | def signed?(tx)
FILE: lib/eth/tx/eip1559.rb
type Eth (line 16) | module Eth
type Tx (line 19) | module Tx
class Eip1559 (line 24) | class Eip1559
method initialize (line 86) | def initialize(params)
method decode (line 139) | def decode(hex)
method unsigned_copy (line 206) | def unsigned_copy(tx)
method sign (line 238) | def sign(key)
method sign_with (line 266) | def sign_with(signature)
method encoded (line 290) | def encoded
method hex (line 317) | def hex
method hash (line 324) | def hash
method unsigned_encoded (line 332) | def unsigned_encoded
method unsigned_hash (line 353) | def unsigned_hash
method _set_signature (line 360) | def _set_signature(recovery_id, r, s)
FILE: lib/eth/tx/eip2930.rb
type Eth (line 16) | module Eth
type Tx (line 19) | module Tx
class Eip2930 (line 24) | class Eip2930
method initialize (line 84) | def initialize(params)
method decode (line 136) | def decode(hex)
method unsigned_copy (line 201) | def unsigned_copy(tx)
method sign (line 232) | def sign(key)
method sign_with (line 260) | def sign_with(signature)
method encoded (line 284) | def encoded
method hex (line 310) | def hex
method hash (line 317) | def hash
method unsigned_encoded (line 325) | def unsigned_encoded
method unsigned_hash (line 345) | def unsigned_hash
method _set_signature (line 352) | def _set_signature(recovery_id, r, s)
FILE: lib/eth/tx/eip4844.rb
type Eth (line 16) | module Eth
type Tx (line 19) | module Tx
class Eip4844 (line 24) | class Eip4844
method initialize (line 106) | def initialize(params)
method decode (line 163) | def decode(hex)
method unsigned_copy (line 234) | def unsigned_copy(tx)
method sign (line 268) | def sign(key)
method sign_with (line 296) | def sign_with(signature)
method encoded (line 320) | def encoded
method hex (line 349) | def hex
method hash (line 356) | def hash
method unsigned_encoded (line 364) | def unsigned_encoded
method unsigned_hash (line 387) | def unsigned_hash
method _set_signature (line 394) | def _set_signature(recovery_id, r, s)
FILE: lib/eth/tx/eip7702.rb
type Eth (line 16) | module Eth
type Tx (line 19) | module Tx
class Eip7702 (line 24) | class Eip7702
class Authorization (line 28) | class Authorization
method initialize (line 57) | def initialize(fields)
method sign (line 72) | def sign(key)
method unsigned_encoded (line 97) | def unsigned_encoded
method unsigned_hash (line 108) | def unsigned_hash
method raw (line 115) | def raw
method == (line 130) | def ==(o)
method state (line 136) | def state
method initialize (line 205) | def initialize(params)
method decode (line 260) | def decode(hex)
method unsigned_copy (line 330) | def unsigned_copy(tx)
method sign (line 370) | def sign(key)
method sign_with (line 398) | def sign_with(signature)
method encoded (line 422) | def encoded
method hex (line 453) | def hex
method hash (line 460) | def hash
method unsigned_encoded (line 468) | def unsigned_encoded
method unsigned_hash (line 494) | def unsigned_hash
method deserialize_authorizations (line 500) | def deserialize_authorizations(authorization_list)
method _set_signature (line 513) | def _set_signature(recovery_id, r, s)
FILE: lib/eth/tx/legacy.rb
type Eth (line 16) | module Eth
type Tx (line 19) | module Tx
class Legacy (line 23) | class Legacy
method initialize (line 76) | def initialize(params, chain_id = Chain::ETHEREUM)
method decode (line 123) | def decode(hex)
method unsigned_copy (line 175) | def unsigned_copy(tx)
method sign (line 205) | def sign(key)
method sign_with (line 232) | def sign_with(signature)
method encoded (line 254) | def encoded
method hex (line 274) | def hex
method hash (line 281) | def hash
method unsigned_encoded (line 288) | def unsigned_encoded
method unsigned_hash (line 305) | def unsigned_hash
method _set_signature (line 312) | def _set_signature(v, r, s)
FILE: lib/eth/unit.rb
type Eth (line 18) | module Eth
type Unit (line 21) | module Unit
FILE: lib/eth/util.rb
type Eth (line 20) | module Eth
type Util (line 23) | module Util
function public_key_to_address (line 31) | def public_key_to_address(str)
function keccak256 (line 42) | def keccak256(str)
function bin_to_hex (line 51) | def bin_to_hex(bin)
function hex_to_bin (line 62) | def hex_to_bin(hex)
function prefix_hex (line 74) | def prefix_hex(hex)
function remove_hex_prefix (line 82) | def remove_hex_prefix(hex)
function bin_to_prefixed_hex (line 91) | def bin_to_prefixed_hex(bin)
function hex? (line 99) | def hex?(str)
function prefixed? (line 109) | def prefixed?(hex)
function serialize_int_to_big_endian (line 118) | def serialize_int_to_big_endian(num)
function int_to_big_endian (line 130) | def int_to_big_endian(num)
function deserialize_big_endian_to_int (line 144) | def deserialize_big_endian_to_int(str)
function deserialize_rlp_int (line 153) | def deserialize_rlp_int(str)
function big_endian_to_int (line 161) | def big_endian_to_int(str)
function str_to_bytes (line 169) | def str_to_bytes(str)
function bytes_to_str (line 177) | def bytes_to_str(bin)
function bytes? (line 185) | def bytes?(str)
function primitive? (line 193) | def primitive?(item)
function list? (line 201) | def list?(item)
function ceil32 (line 209) | def ceil32(num)
function lpad (line 219) | def lpad(str, sym, len)
function zpad (line 229) | def zpad(str, len)
function zpad_hex (line 238) | def zpad_hex(hex, len = 32)
function zpad_int (line 247) | def zpad_int(num, len = 32)
FILE: lib/eth/version.rb
type Eth (line 16) | module Eth
FILE: spec/eth/abi_spec.rb
function assert (line 363) | def assert(data, types, args)
FILE: spec/eth/client/ws_spec.rb
class DummyWebsocketServer (line 25) | class DummyWebsocketServer
method initialize (line 30) | def initialize
method start (line 45) | def start
method stop (line 50) | def stop
method serve (line 62) | def serve
method handle_client (line 73) | def handle_client(socket)
method perform_handshake (line 101) | def perform_handshake(socket)
method rpc_response_for (line 125) | def rpc_response_for(message)
method read_frame (line 156) | def read_frame(socket)
method send_frame (line 180) | def send_frame(socket, payload, opcode = 0x1)
method send_close (line 196) | def send_close(socket)
method success (line 200) | def success(id, result)
method error (line 208) | def error(id, message)
method record_transaction (line 216) | def record_transaction(tx)
method build_tx_hash (line 240) | def build_tx_hash(from, to, nonce, value)
method normalize_address (line 245) | def normalize_address(address)
method hex_to_int (line 250) | def hex_to_int(value)
method to_hex (line 257) | def to_hex(number)
FILE: spec/eth/key/decrypter_spec.rb
function read_key_fixture (line 4) | def read_key_fixture(path)
Condensed preview — 136 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,267K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 308,
"preview": "---\nupdates:\n -\n directory: /\n labels:\n - dependencies\n package-ecosystem: bundler\n schedule:\n in"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 1093,
"preview": "---\nname: CodeQL\n\non:\n pull_request:\n branches:\n - main\n push:\n branches:\n - main\n\njobs:\n analyze:\n "
},
{
"path": ".github/workflows/docs.yml",
"chars": 465,
"preview": "---\nname: Docs\n\non:\n push:\n branches:\n - main\n\njobs:\n docs:\n runs-on: ubuntu-latest\n steps:\n - uses: "
},
{
"path": ".github/workflows/spec.yml",
"chars": 1825,
"preview": "---\nname: Spec\n\non:\n pull_request:\n branches:\n - main\n push:\n branches:\n - main\n schedule:\n -\n "
},
{
"path": ".gitignore",
"chars": 660,
"preview": "*.DS_Store\n\n*.o\n*.so\n*.gem\n*.log\n*.rbc\n.config\n.rake_tasks~\ncoverage/\nInstalledFiles\npkg/\ntmp/\n\n# RSpec configuration an"
},
{
"path": ".gitmodules",
"chars": 125,
"preview": "[submodule \"spec/fixtures/ethereum/tests\"]\n\tpath = spec/fixtures/ethereum/tests\n\turl = https://github.com/ethereum/tests"
},
{
"path": ".yardopts",
"chars": 61,
"preview": "--verbose --fail-on-warning --markup markdown --embed-mixins\n"
},
{
"path": "AUTHORS.txt",
"chars": 1043,
"preview": "The Ruby-Eth Contributors are:\n* Steve Ellis @se3000\n* Afri Schoedon @q9f\n* John Omar @chainoperator\n* Joshua Peek @josh"
},
{
"path": "CHANGELOG.md",
"chars": 24847,
"preview": "# Change Log\nAll notable changes to this project will be documented in this file.\n\n## [0.5.15]\n### Added\n* Implement EIP"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 4956,
"preview": "\n# Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunit"
},
{
"path": "CONTRIBUTING.md",
"chars": 1656,
"preview": "# Contributing to Ruby Ethereum\n\nEveryone is welcome to contribute to the Ruby Ethereum gem. It is\nmore than six years o"
},
{
"path": "Gemfile",
"chars": 340,
"preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngroup :test, :development do\n gem \"bundler\", \">= 2.4\"\n g"
},
{
"path": "LICENSE.txt",
"chars": 11365,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 5262,
"preview": "<!--\n# @markup markdown\n# @title Ethereum for Ruby\n# @author Afri Schoedon\n-->\n\n# Ethereum for Ruby\n\n[![GitHub Workflow "
},
{
"path": "Rakefile",
"chars": 117,
"preview": "require \"bundler/gem_tasks\"\nrequire \"rspec/core/rake_task\"\n\nRSpec::Core::RakeTask.new(:spec)\n\ntask :default => :spec\n"
},
{
"path": "SECURITY.md",
"chars": 761,
"preview": "# Security Policy\n\n## Supported Versions\n\nRuby Ethereum 0.5.x is a complete rewrite of the old `eth` 0.4.x gem.\nIt also "
},
{
"path": "abi/ens_registry.json",
"chars": 7720,
"preview": "[\n {\n \"inputs\":[\n {\n \"internalType\":\"contract ENS\",\n \"name\":\"_old\",\n \"type\":\"address\"\n "
},
{
"path": "abi/ens_resolver.json",
"chars": 17595,
"preview": "[\n {\n \"inputs\":[\n {\n \"internalType\":\"contract ENS\",\n \"name\":\"_ens\",\n \"type\":\"a"
},
{
"path": "bin/console",
"chars": 204,
"preview": "#!/usr/bin/env ruby\n\n# use the local version of the code instead of a globally installed gem\n$LOAD_PATH.unshift File.exp"
},
{
"path": "bin/setup",
"chars": 197,
"preview": "#!/usr/bin/env bash\n\nbundle install\ngit submodule update --init --recursive\nrufo .\nyard doc\nrspec\n\necho \"Tests fail? Run"
},
{
"path": "codecov.yml",
"chars": 90,
"preview": "coverage:\n status:\n project:\n default:\n target: 99%\n threshold: 1%\n"
},
{
"path": "eth.gemspec",
"chars": 2194,
"preview": "# frozen_string_literal: true\n# coding: utf-8\n\nlib = File.expand_path(\"lib\", __dir__).freeze\n$LOAD_PATH.unshift lib unle"
},
{
"path": "lib/eth/abi/decoder.rb",
"chars": 8483,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi/encoder.rb",
"chars": 11845,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi/event.rb",
"chars": 5617,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi/function.rb",
"chars": 4179,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi/packed/encoder.rb",
"chars": 7149,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi/type.rb",
"chars": 10202,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/abi.rb",
"chars": 5410,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/address.rb",
"chars": 3211,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/api.rb",
"chars": 8568,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/bls.rb",
"chars": 2315,
"preview": "# frozen_string_literal: true\n\nrequire \"bls\"\n\nmodule Eth\n # Helper methods for interacting with BLS12-381 points and si"
},
{
"path": "lib/eth/chain.rb",
"chars": 6545,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/client/http.rb",
"chars": 2341,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/client/ipc.rb",
"chars": 1445,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/client/ws.rb",
"chars": 9254,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/client.rb",
"chars": 21035,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/constant.rb",
"chars": 1950,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/error.rb",
"chars": 2127,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/event.rb",
"chars": 3270,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/function.rb",
"chars": 2642,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/function_input.rb",
"chars": 1357,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/function_output.rb",
"chars": 1376,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract/initializer.rb",
"chars": 1401,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/contract.rb",
"chars": 7603,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/eip712.rb",
"chars": 8592,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/ens/coin_type.rb",
"chars": 1432,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/ens/resolver.rb",
"chars": 4503,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/ens.rb",
"chars": 955,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/key/decrypter.rb",
"chars": 3697,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/key/encrypter.rb",
"chars": 6170,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/key.rb",
"chars": 5945,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/decoder.rb",
"chars": 4327,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/encoder.rb",
"chars": 2702,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/sedes/big_endian_int.rb",
"chars": 2742,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/sedes/binary.rb",
"chars": 3824,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/sedes/list.rb",
"chars": 3074,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp/sedes.rb",
"chars": 2627,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/rlp.rb",
"chars": 1962,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/signature.rb",
"chars": 6977,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/solidity.rb",
"chars": 2897,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx/eip1559.rb",
"chars": 14243,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx/eip2930.rb",
"chars": 13734,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx/eip4844.rb",
"chars": 15997,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx/eip7702.rb",
"chars": 20560,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx/legacy.rb",
"chars": 11632,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/tx.rb",
"chars": 14804,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/unit.rb",
"chars": 1668,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/util.rb",
"chars": 8285,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth/version.rb",
"chars": 961,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "lib/eth.rb",
"chars": 1040,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "spec/eth/abi/decoder_spec.rb",
"chars": 8908,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi::Decoder do\n subject(:t_bool) { Abi::Type.parse \"b"
},
{
"path": "spec/eth/abi/encoder_spec.rb",
"chars": 10056,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi::Encoder do\n subject(:t_bool) { Abi::Type.parse \"b"
},
{
"path": "spec/eth/abi/event_spec.rb",
"chars": 15931,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi::Event do\n let(:erc20_abi_file) { File.read \"spec/"
},
{
"path": "spec/eth/abi/function_spec.rb",
"chars": 1383,
"preview": "require \"spec_helper\"\n\ndescribe Abi::Function do\n let(:erc20_abi_file) { File.read \"spec/fixtures/abi/ERC20.json\" }\n s"
},
{
"path": "spec/eth/abi/packed/encoder_spec.rb",
"chars": 12152,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi::Packed::Encoder do\n it \"encodes packed types\" do\n"
},
{
"path": "spec/eth/abi/type_spec.rb",
"chars": 8115,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi::Type do\n describe \".initialize\" do\n it \"can in"
},
{
"path": "spec/eth/abi_spec.rb",
"chars": 31400,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Abi do\n describe \".encode .decode\" do\n\n # load offi"
},
{
"path": "spec/eth/address_spec.rb",
"chars": 6380,
"preview": "require \"spec_helper\"\n\ndescribe Address do\n describe \".initialize\" do\n # alice is initialized with an unprefixed add"
},
{
"path": "spec/eth/bls_spec.rb",
"chars": 1187,
"preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Eth::Bls do\n let(:priv_key) { \"0x01\" }\n let(:mess"
},
{
"path": "spec/eth/chain_spec.rb",
"chars": 5905,
"preview": "require \"spec_helper\"\n\ndescribe Chain do\n context \"#CHAIN_ID\" do\n it \"has EIP155 chain ids for mainnets, testnets, a"
},
{
"path": "spec/eth/client/ws_spec.rb",
"chars": 10734,
"preview": "# Copyright (c) 2016-2025 The Ruby-Eth Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
},
{
"path": "spec/eth/client_spec.rb",
"chars": 43005,
"preview": "require \"spec_helper\"\n\ndescribe Client do\n\n # run `geth --dev --http --wc --ipcpath /tmp/geth.ipc`\n # to provide http,"
},
{
"path": "spec/eth/constant_spec.rb",
"chars": 1301,
"preview": "# # -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Eth::Constant do\n describe \"#SYMBOLS\" do\n it \"has"
},
{
"path": "spec/eth/contract/error_spec.rb",
"chars": 1272,
"preview": "require \"spec_helper\"\n\ndescribe Contract do\n let(:abi) do\n [\n { \"type\" => \"function\", \"name\" => \"foo\", \"inputs\""
},
{
"path": "spec/eth/contract/event_spec.rb",
"chars": 5382,
"preview": "require \"spec_helper\"\n\ndescribe Contract::Event do\n let(:path) { \"spec/fixtures/contracts/test_contract.sol\" }\n subjec"
},
{
"path": "spec/eth/contract/function_input_spec.rb",
"chars": 770,
"preview": "require \"spec_helper\"\n\ndescribe Contract::FunctionInput do\n subject(:function_input) { Contract::FunctionInput.new({ \"n"
},
{
"path": "spec/eth/contract/function_output_spec.rb",
"chars": 780,
"preview": "require \"spec_helper\"\n\ndescribe Contract::FunctionOutput do\n subject(:function_output) { Contract::FunctionOutput.new({"
},
{
"path": "spec/eth/contract/function_spec.rb",
"chars": 3215,
"preview": "require \"spec_helper\"\n\ndescribe Contract::Function do\n let(:erc20_abi_file) { File.read \"spec/fixtures/abi/ERC20.json\" "
},
{
"path": "spec/eth/contract/initializer_spec.rb",
"chars": 703,
"preview": "require \"spec_helper\"\n\ndescribe Contract::Initializer do\n subject(:file) { \"spec/fixtures/contracts/greeter.sol\" }\n su"
},
{
"path": "spec/eth/contract_spec.rb",
"chars": 6897,
"preview": "require \"spec_helper\"\n\ndescribe Contract do\n subject(:solc) { Eth::Solidity.new }\n subject(:contract) { solc.compile(f"
},
{
"path": "spec/eth/eip712_spec.rb",
"chars": 12327,
"preview": "require \"spec_helper\"\n\ndescribe Eip712 do\n\n # The EIP-712 domain specification descriptor.\n subject(:eip712_domain) {\n"
},
{
"path": "spec/eth/ens/coin_type_spec.rb",
"chars": 470,
"preview": "require \"spec_helper\"\n\ndescribe Ens::CoinType do\n it \"knows some coin slip-44 types\" do\n expect(Ens::CoinType::BITCO"
},
{
"path": "spec/eth/ens/resolver_spec.rb",
"chars": 2564,
"preview": "require \"spec_helper\"\n\ndescribe Ens::Resolver do\n\n # public rpc\n let(:drpc_api) { \"https://eth.drpc.org\" }\n subject(:"
},
{
"path": "spec/eth/ens_spec.rb",
"chars": 243,
"preview": "require \"spec_helper\"\n\ndescribe Ens do\n it \"has EIP155 chain ids for mainnets, testnets, and devnets\" do\n # Chain ID"
},
{
"path": "spec/eth/key/decrypter_spec.rb",
"chars": 2216,
"preview": "require \"spec_helper\"\n\ndescribe Key::Decrypter do\n def read_key_fixture(path)\n File.read \"./spec/fixtures/keys/#{pat"
},
{
"path": "spec/eth/key/encrypter_spec.rb",
"chars": 4585,
"preview": "require \"spec_helper\"\n\n# These test vectors are specified in the ethereum wiki:\n# https://github.com/ethereum/wiki/wiki/"
},
{
"path": "spec/eth/key_spec.rb",
"chars": 8951,
"preview": "require \"spec_helper\"\n\ndescribe Key do\n describe \".initialize\" do\n subject(:alice) { Key.new }\n subject(:bob) { K"
},
{
"path": "spec/eth/rlp/sedes/big_endian_int_spec.rb",
"chars": 2132,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Rlp::Sedes::BigEndianInt do\n subject(:integers) {\n "
},
{
"path": "spec/eth/rlp/sedes/binary_spec.rb",
"chars": 482,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Rlp::Sedes::Binary do\n it \"does serialize fixed length"
},
{
"path": "spec/eth/rlp/sedes/list_spec.rb",
"chars": 1101,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Rlp::Sedes::List do\n subject(:l1) { Rlp::Sedes::List.n"
},
{
"path": "spec/eth/rlp/sedes_spec.rb",
"chars": 885,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Rlp::Sedes do\n describe \"inference\" do\n subject(:pa"
},
{
"path": "spec/eth/rlp_spec.rb",
"chars": 3303,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Rlp do\n describe \".encode .decode\" do\n\n # load offi"
},
{
"path": "spec/eth/signature_spec.rb",
"chars": 16180,
"preview": "require \"spec_helper\"\n\ndescribe Signature do\n describe \".prefix_message\" do\n it \"can properly prefix messages\" do\n "
},
{
"path": "spec/eth/solidity_spec.rb",
"chars": 3322,
"preview": "require \"spec_helper\"\n\ndescribe Solidity do\n it \"finds a solc compiler\" do\n # This fails if no `solc` is in the $PAT"
},
{
"path": "spec/eth/tx/eip1559_spec.rb",
"chars": 12787,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Tx::Eip1559 do\n\n # ref https://goerli.etherscan.io/tx/"
},
{
"path": "spec/eth/tx/eip2930_spec.rb",
"chars": 19636,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Tx::Eip2930 do\n # ref https://goerli.etherscan.io/tx/0"
},
{
"path": "spec/eth/tx/eip4844_spec.rb",
"chars": 9924,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Tx::Eip4844 do\n subject(:blob_hashes) { [\"0x\" + \"11\" *"
},
{
"path": "spec/eth/tx/eip7702_spec.rb",
"chars": 22066,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Tx::Eip7702 do\n subject(:anvil) {\n 31337.freeze\n }"
},
{
"path": "spec/eth/tx/legacy_spec.rb",
"chars": 18406,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Tx::Legacy do\n subject(:tx) {\n Tx.new({\n nonce"
},
{
"path": "spec/eth/tx_spec.rb",
"chars": 14482,
"preview": "require \"spec_helper\"\n\ndescribe Tx do\n context \"#GAS\" do\n it \"defines gas limits\" do\n expect(Tx::DEFAULT_GAS_LI"
},
{
"path": "spec/eth/unit_spec.rb",
"chars": 506,
"preview": "require \"spec_helper\"\n\ndescribe Unit do\n context \"#ETHER\" do\n it \"has constants for all common Ether units\" do\n "
},
{
"path": "spec/eth/util_spec.rb",
"chars": 11263,
"preview": "# -*- encoding : ascii-8bit -*-\n\nrequire \"spec_helper\"\n\ndescribe Util do\n describe \".public_key_to_address\" do\n it \""
},
{
"path": "spec/eth_spec.rb",
"chars": 156,
"preview": "require \"spec_helper\"\n\ndescribe Eth do\n it \"0.5.17 works\" do\n\n # placeholder to set up spec in future\n expect(Eth"
},
{
"path": "spec/fixtures/abi/ENSRegistryWithFallback.json",
"chars": 10136,
"preview": "[\n {\n \"inputs\":[\n {\n \"internalType\":\"contract ENS\",\n \"name\":\"_old\",\n "
},
{
"path": "spec/fixtures/abi/ERC1155.json",
"chars": 5666,
"preview": "[\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n "
},
{
"path": "spec/fixtures/abi/ERC20.json",
"chars": 3385,
"preview": "[\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n "
},
{
"path": "spec/fixtures/abi/ERC721.json",
"chars": 5398,
"preview": "[\n {\n \"anonymous\": false,\n \"inputs\": [\n {\n \"indexed\": true,\n \"internalType\": \"address\",\n "
},
{
"path": "spec/fixtures/abi/Tuple.json",
"chars": 2326,
"preview": "[\n {\n \"inputs\": [\n {\n \"components\": [\n {\n \"internalType\": \"string\",\n \"nam"
},
{
"path": "spec/fixtures/abi/Tuple2.json",
"chars": 2990,
"preview": "[\n {\n \"inputs\": [\n {\n \"components\": [\n {\n \"components\": [\n {\n "
},
{
"path": "spec/fixtures/abi/ethers.json",
"chars": 7014464,
"preview": "[\n {\n \"name\": \"random-((string))\",\n \"type\": \"((string))\",\n \"value\": [\n [\n \"Moo é🚀 éooM🚀🚀ooMoo🚀M🚀ooooMé🚀éé🚀é 🚀M 🚀"
},
{
"path": "spec/fixtures/contracts/address_storage.sol",
"chars": 487,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract AddressStorage {\n\n address myAddress;\n address"
},
{
"path": "spec/fixtures/contracts/deposit.sol",
"chars": 8067,
"preview": "// SPDX-License-Identifier: CC0-1.0\n\npragma solidity ^0.8;\n\n// Do not use this contract in production! It's untested and"
},
{
"path": "spec/fixtures/contracts/dummy.sol",
"chars": 241,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract Dummy {\n uint number;\n function set(uint x) pu"
},
{
"path": "spec/fixtures/contracts/erc20.sol",
"chars": 4969,
"preview": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (v4.8.0 - simplified)\n\npragma solidity ^0.8;\n\n// Do not use th"
},
{
"path": "spec/fixtures/contracts/error.sol",
"chars": 98,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract Error {\n noint number;\n}\n"
},
{
"path": "spec/fixtures/contracts/greeter.sol",
"chars": 831,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract Mortal {\n\n // defines the owner of type payable"
},
{
"path": "spec/fixtures/contracts/signer.sol",
"chars": 1132,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\nlibrary LibBytes {\n function readBytes32(\n bytes memo"
},
{
"path": "spec/fixtures/contracts/simple_registry.sol",
"chars": 290,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract SimpleRegistry {\n uint number1;\n uint number2;"
},
{
"path": "spec/fixtures/contracts/test_contract.sol",
"chars": 835,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract TestContract {\n\n address payable public owner"
},
{
"path": "spec/fixtures/contracts/tuple.sol",
"chars": 473,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract Tuple {\n struct Tuple1 {\n string var1;"
},
{
"path": "spec/fixtures/contracts/tuple2.sol",
"chars": 460,
"preview": "// SPDX-License-Identifier: Apache-2.0\n\npragma solidity ^0.8;\n\ncontract Tuple2 {\n struct Nar {\n Nuu nuu;\n "
},
{
"path": "spec/fixtures/keys/testingtesting.json",
"chars": 491,
"preview": "{\"version\":3,\"id\":\"a8c1108e-033d-4d41-b5ea-e26f620b9eda\",\"address\":\"65bcb68d4c73e163c69eea056d63bb09faacdd8e\",\"Crypto\":{"
},
{
"path": "spec/fixtures/keys/testpassword.json",
"chars": 494,
"preview": "{ \"crypto\" : { \"cipher\" : \"aes-128-ctr\", \"cipherparams\" : { \"iv\" : \"6087dab2f9fdbbfaddc31a909735c1e6\" }, \"ciphertext\" : "
},
{
"path": "spec/fixtures/keys/testunknownkdf.json",
"chars": 500,
"preview": "{\"version\":3,\"id\":\"a8c1108e-033d-4d41-b5ea-e26f620b9eda\",\"address\":\"65bcb68d4c73e163c69eea056d63bb09faacdd8e\",\"Crypto\":{"
},
{
"path": "spec/spec_helper.rb",
"chars": 380,
"preview": "# use the local version of the code instead of a globally installed gem\n$LOAD_PATH.unshift File.expand_path(\"../../lib\","
}
]
About this extraction
This page contains the full source code of the q9f/eth.rb GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 136 files (7.4 MB), approximately 2.0M tokens, and a symbol index with 550 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.