viem_rb 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -2
- data/README.md +2 -2
- data/lib/viem/accounts/private_key_account.rb +1 -1
- data/lib/viem/actions/public/ens.rb +23 -23
- data/lib/viem/actions/public/get_block.rb +7 -7
- data/lib/viem/actions/public/get_code.rb +1 -1
- data/lib/viem/actions/public/get_gas.rb +5 -6
- data/lib/viem/actions/public/get_logs.rb +108 -9
- data/lib/viem/actions/public/get_transaction.rb +5 -1
- data/lib/viem/actions/public/read_contract.rb +2 -2
- data/lib/viem/actions/wallet/send_transaction.rb +24 -29
- data/lib/viem/actions/wallet/write_contract.rb +2 -2
- data/lib/viem/chains/base.rb +1 -1
- data/lib/viem/chains/definitions.rb +18 -18
- data/lib/viem/clients/test_client.rb +3 -3
- data/lib/viem/transports/http.rb +1 -3
- data/lib/viem/utils/address.rb +1 -1
- data/lib/viem/utils/hex.rb +2 -2
- data/lib/viem/utils/units.rb +2 -2
- data/lib/viem/version.rb +1 -1
- metadata +13 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b484347f34f49daa07a62c0de9470faefab1b9ea6eb95573fc92b8785fc2e228
|
|
4
|
+
data.tar.gz: 431aadd5602eebd269ebea09884d1614355b57c3979e5e301c1dbd0451d60bf3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e6e9137eee7e6840dd5fd62702d7689d2f8dde28e74338e0cc4fbcfa5ab160633d64d616e8c992e3111b8a401424d3762753f72d24494b2adc4402af7805514
|
|
7
|
+
data.tar.gz: 62a543abdc1014f1e750a2ec7158a90ebc9dea761cabe06a99da25d6efacc2ae4cb83fe2f33c0e1d3369ae5532736d7c88c28a4732db7228e7d92ed2d2a3a76f
|
data/CHANGELOG.md
CHANGED
|
@@ -5,16 +5,37 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.3] - 2026-03-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `get_logs` fully completed:
|
|
12
|
+
- `block_hash:` param — filter logs from a specific block hash (mutually exclusive with `from_block`/`to_block`)
|
|
13
|
+
- Multi-address support — `address:` now accepts an array of contract addresses
|
|
14
|
+
- Address validation on `address:` param (raises `InvalidAddressError` for invalid inputs)
|
|
15
|
+
- Full indexed args encoding — `args:` hash now encodes topic1/2/3 by type (`address`, `bool`, `uint`, `int`, `bytes32`, `bytes`, `string`)
|
|
16
|
+
- OR filtering — pass an array as an arg value to generate a multi-value OR topic filter
|
|
17
|
+
- Raw `topics:` override — bypass event/args encoding and pass topics directly
|
|
18
|
+
- Tuple event support — canonical type resolution for events with `tuple` inputs
|
|
19
|
+
- Trailing nil wildcard stripping — topics array is trimmed to the last meaningful filter
|
|
20
|
+
- 31 new specs for `get_logs` (192 total, 0 failures)
|
|
21
|
+
|
|
22
|
+
## [0.1.2] - 2026-03-19
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Bumped minimum required Ruby version to `>= 3.0.0` (transitive dependency `rbsecp256k1` via `eth` is incompatible with Ruby < 3.0)
|
|
26
|
+
- CI matrix updated to Ruby 3.0–3.3, RuboCop on 3.3
|
|
27
|
+
- Fixed all RuboCop offenses across lib and spec files
|
|
28
|
+
- Fixed invalid cop name `Style/TrailingCommaInArgList` → `Style/TrailingCommaInArguments`
|
|
29
|
+
|
|
8
30
|
## [0.1.1] - 2026-03-19
|
|
9
31
|
|
|
10
32
|
### Added
|
|
11
33
|
- Extended test suite: ABI decoder, ABI parser, Hash utils, error hierarchy, chain definitions, and Fallback transport specs (161 examples total)
|
|
12
|
-
- GitHub Actions CI workflow — tests on Ruby
|
|
34
|
+
- GitHub Actions CI workflow — tests on Ruby 3.0 through 3.3, RuboCop on 3.3
|
|
13
35
|
- GitHub Actions publish workflow — auto-publishes to RubyGems on GitHub release
|
|
14
36
|
|
|
15
37
|
### Fixed
|
|
16
38
|
- Corrected author name from `Noryk` to `Nyrok` in gemspec, README and LICENSE
|
|
17
|
-
- Lowered required Ruby version to `>= 2.6.0`
|
|
18
39
|
|
|
19
40
|
## [0.1.0] - 2025-01-01
|
|
20
41
|
|
data/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
[](https://rubygems.org/gems/viem_rb)
|
|
4
4
|
[](https://rubygems.org/gems/viem_rb)
|
|
5
5
|
[](./LICENSE)
|
|
6
|
-
[](https://www.ruby-lang.org)
|
|
7
7
|
|
|
8
8
|
📦 **[https://rubygems.org/gems/viem_rb](https://rubygems.org/gems/viem_rb)**
|
|
9
9
|
|
|
10
|
-
A Ruby/Rails adaptation of [viem](https://viem.sh) — the TypeScript Ethereum library. Provides Ethereum clients, ABI encoding/decoding, account management, and utilities for Ruby
|
|
10
|
+
A Ruby/Rails adaptation of [viem](https://viem.sh) — the TypeScript Ethereum library. Provides Ethereum clients, ABI encoding/decoding, account management, and utilities for Ruby 3.0+ and Rails 7+ applications.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -8,44 +8,44 @@ module Viem
|
|
|
8
8
|
|
|
9
9
|
ENS_PUBLIC_RESOLVER_ABI = [
|
|
10
10
|
{
|
|
11
|
-
"name"
|
|
12
|
-
"type"
|
|
13
|
-
"inputs"
|
|
14
|
-
"outputs"
|
|
15
|
-
"stateMutability" => "view"
|
|
11
|
+
"name" => "addr",
|
|
12
|
+
"type" => "function",
|
|
13
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
14
|
+
"outputs" => [{ "type" => "address", "name" => "" }],
|
|
15
|
+
"stateMutability" => "view",
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
"name"
|
|
19
|
-
"type"
|
|
20
|
-
"inputs"
|
|
21
|
-
"outputs"
|
|
22
|
-
"stateMutability" => "view"
|
|
23
|
-
}
|
|
18
|
+
"name" => "name",
|
|
19
|
+
"type" => "function",
|
|
20
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
21
|
+
"outputs" => [{ "type" => "string", "name" => "" }],
|
|
22
|
+
"stateMutability" => "view",
|
|
23
|
+
},
|
|
24
24
|
].freeze
|
|
25
25
|
|
|
26
26
|
REGISTRY_ABI = [
|
|
27
27
|
{
|
|
28
|
-
"name"
|
|
29
|
-
"type"
|
|
30
|
-
"inputs"
|
|
31
|
-
"outputs"
|
|
32
|
-
"stateMutability" => "view"
|
|
33
|
-
}
|
|
28
|
+
"name" => "resolver",
|
|
29
|
+
"type" => "function",
|
|
30
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
31
|
+
"outputs" => [{ "type" => "address", "name" => "" }],
|
|
32
|
+
"stateMutability" => "view",
|
|
33
|
+
},
|
|
34
34
|
].freeze
|
|
35
35
|
|
|
36
36
|
def get_ens_address(name:)
|
|
37
37
|
node = namehash(name)
|
|
38
38
|
resolver_addr = read_contract(
|
|
39
39
|
address: ENS_REGISTRY, abi: REGISTRY_ABI,
|
|
40
|
-
function_name: "resolver", args: [node]
|
|
40
|
+
function_name: "resolver", args: [node],
|
|
41
41
|
)
|
|
42
42
|
return nil if Utils::Address.is_zero_address?(resolver_addr)
|
|
43
43
|
|
|
44
44
|
read_contract(
|
|
45
45
|
address: resolver_addr, abi: ENS_PUBLIC_RESOLVER_ABI,
|
|
46
|
-
function_name: "addr", args: [node]
|
|
46
|
+
function_name: "addr", args: [node],
|
|
47
47
|
)
|
|
48
|
-
rescue
|
|
48
|
+
rescue => e
|
|
49
49
|
raise Error, "ENS resolution failed for #{name}: #{e.message}"
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -55,15 +55,15 @@ module Viem
|
|
|
55
55
|
node = namehash(reversed)
|
|
56
56
|
resolver_addr = read_contract(
|
|
57
57
|
address: ENS_REGISTRY, abi: REGISTRY_ABI,
|
|
58
|
-
function_name: "resolver", args: [node]
|
|
58
|
+
function_name: "resolver", args: [node],
|
|
59
59
|
)
|
|
60
60
|
return nil if Utils::Address.is_zero_address?(resolver_addr)
|
|
61
61
|
|
|
62
62
|
read_contract(
|
|
63
63
|
address: resolver_addr, abi: ENS_PUBLIC_RESOLVER_ABI,
|
|
64
|
-
function_name: "name", args: [node]
|
|
64
|
+
function_name: "name", args: [node],
|
|
65
65
|
)
|
|
66
|
-
rescue
|
|
66
|
+
rescue
|
|
67
67
|
nil
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -32,13 +32,13 @@ module Viem
|
|
|
32
32
|
def format_block(raw)
|
|
33
33
|
raw.each_with_object({}) do |(k, v), h|
|
|
34
34
|
key = k.to_sym
|
|
35
|
-
if BLOCK_STRING_FIELDS.include?(k.to_s)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
h[key] = if BLOCK_STRING_FIELDS.include?(k.to_s)
|
|
36
|
+
v
|
|
37
|
+
elsif v.is_a?(String) && v.start_with?("0x") && v.match?(/\A0x[0-9a-f]+\z/)
|
|
38
|
+
Utils::Hex.hex_to_number(v)
|
|
39
|
+
else
|
|
40
|
+
v
|
|
41
|
+
end
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
end
|
|
@@ -12,7 +12,7 @@ module Viem
|
|
|
12
12
|
|
|
13
13
|
def get_storage_at(address:, slot:, block_tag: "latest")
|
|
14
14
|
address = Utils::Address.get_address(address)
|
|
15
|
-
slot =
|
|
15
|
+
slot = Utils::Hex.number_to_hex(slot, size: 32) if slot.is_a?(Integer)
|
|
16
16
|
@transport.request("eth_getStorageAt", [address, slot, block_tag.to_s])
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -10,12 +10,11 @@ module Viem
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def get_fee_history(block_count:, newest_block: "latest", reward_percentiles: [])
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
result
|
|
13
|
+
@transport.request("eth_feeHistory", [
|
|
14
|
+
Utils::Hex.number_to_hex(block_count),
|
|
15
|
+
newest_block.to_s,
|
|
16
|
+
reward_percentiles,
|
|
17
|
+
],)
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def get_max_priority_fee_per_gas
|
|
@@ -4,28 +4,127 @@ module Viem
|
|
|
4
4
|
module Actions
|
|
5
5
|
module Public
|
|
6
6
|
module GetLogs
|
|
7
|
-
|
|
7
|
+
# Fetch event logs from the chain.
|
|
8
|
+
#
|
|
9
|
+
# @param address [String, Array<String>, nil] Contract address(es) to filter
|
|
10
|
+
# @param event [Hash, nil] ABI item for the event (generates topic0)
|
|
11
|
+
# @param args [Hash] Indexed argument values to filter (topic1+).
|
|
12
|
+
# Values can be arrays for OR filtering.
|
|
13
|
+
# @param from_block [Integer, String, nil] Start block (number, "latest", "earliest", "pending")
|
|
14
|
+
# @param to_block [Integer, String, nil] End block
|
|
15
|
+
# @param block_hash [String, nil] Filter by specific block hash (exclusive with from/to)
|
|
16
|
+
# @param topics [Array, nil] Raw topics override (skips event+args encoding)
|
|
17
|
+
#
|
|
18
|
+
# @return [Array<Hash>] Formatted log objects
|
|
19
|
+
def get_logs(
|
|
20
|
+
address: nil,
|
|
21
|
+
event: nil,
|
|
22
|
+
args: {},
|
|
23
|
+
from_block: nil,
|
|
24
|
+
to_block: nil,
|
|
25
|
+
block_hash: nil,
|
|
26
|
+
topics: nil
|
|
27
|
+
)
|
|
28
|
+
raise ArgumentError, "block_hash is mutually exclusive with from_block/to_block" \
|
|
29
|
+
if block_hash && (from_block || to_block)
|
|
30
|
+
|
|
8
31
|
params = {}
|
|
9
|
-
|
|
10
|
-
params[:
|
|
11
|
-
|
|
12
|
-
|
|
32
|
+
|
|
33
|
+
params[:address] = encode_address_filter(address) if address
|
|
34
|
+
|
|
35
|
+
if block_hash
|
|
36
|
+
params[:blockHash] = block_hash
|
|
37
|
+
else
|
|
38
|
+
params[:fromBlock] = encode_block_param(from_block) if from_block
|
|
39
|
+
params[:toBlock] = encode_block_param(to_block) if to_block
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
encoded_topics = topics || (event ? encode_event_topics(event, args) : nil)
|
|
43
|
+
params[:topics] = encoded_topics if encoded_topics
|
|
44
|
+
|
|
13
45
|
results = @transport.request("eth_getLogs", [stringify_keys(params)])
|
|
14
46
|
results.map { |l| format_log(l) }
|
|
15
47
|
end
|
|
16
48
|
|
|
17
49
|
private
|
|
18
50
|
|
|
51
|
+
# Encode address or array of addresses, validating each one.
|
|
52
|
+
def encode_address_filter(address)
|
|
53
|
+
if address.is_a?(Array)
|
|
54
|
+
address.map { |a| Utils::Address.get_address(a) }
|
|
55
|
+
else
|
|
56
|
+
Utils::Address.get_address(address)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Convert block param to hex string or pass through string tags.
|
|
61
|
+
def encode_block_param(value)
|
|
62
|
+
return value if value.is_a?(String)
|
|
63
|
+
|
|
64
|
+
Utils::Hex.number_to_hex(value)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Build topics array from event ABI + args.
|
|
68
|
+
# topic0 = keccak256(event signature)
|
|
69
|
+
# topic1+ = encoded indexed argument values (nil = wildcard)
|
|
19
70
|
def encode_event_topics(event_abi, args)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
71
|
+
topic0 = Utils::Hash.keccak256(event_signature(event_abi))
|
|
72
|
+
topics = [topic0]
|
|
73
|
+
|
|
74
|
+
indexed_inputs = (event_abi["inputs"] || []).select { |i| i["indexed"] }
|
|
75
|
+
indexed_inputs.each do |input|
|
|
76
|
+
name = input["name"]
|
|
77
|
+
value = if args.is_a?(Hash)
|
|
78
|
+
args.key?(name) ? args[name] : args[name.to_sym]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
topics << if value.nil?
|
|
82
|
+
nil # wildcard — match any value for this topic position
|
|
83
|
+
elsif value.is_a?(Array)
|
|
84
|
+
value.map { |v| encode_topic_value(input["type"], v) }
|
|
85
|
+
else
|
|
86
|
+
encode_topic_value(input["type"], value)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Strip trailing wildcards to keep the request minimal
|
|
91
|
+
topics.reverse.drop_while(&:nil?).reverse
|
|
23
92
|
end
|
|
24
93
|
|
|
94
|
+
# ABI-encode a single indexed value to a 32-byte hex topic.
|
|
95
|
+
def encode_topic_value(type, value)
|
|
96
|
+
case type
|
|
97
|
+
when "address"
|
|
98
|
+
addr = Utils::Address.get_address(value).delete_prefix("0x").downcase
|
|
99
|
+
"0x#{"0" * 24}#{addr}"
|
|
100
|
+
when "bool"
|
|
101
|
+
value ? "0x#{"0" * 63}1" : "0x#{"0" * 64}"
|
|
102
|
+
when /\Auint\d*\z/, /\Aint\d*\z/
|
|
103
|
+
Abi::Encoder.encode_abi_parameters([type], [value])
|
|
104
|
+
when /\Abytes(\d+)\z/
|
|
105
|
+
raw = value.delete_prefix("0x")
|
|
106
|
+
"0x#{raw.ljust(64, "0")}"
|
|
107
|
+
when "bytes", "string"
|
|
108
|
+
# Dynamic types are stored as keccak256 of their content
|
|
109
|
+
Utils::Hash.keccak256(value)
|
|
110
|
+
else
|
|
111
|
+
value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Build canonical event signature string for keccak256 hashing.
|
|
25
116
|
def event_signature(abi_item)
|
|
26
|
-
inputs = (abi_item["inputs"] || []).map { |i| i
|
|
117
|
+
inputs = (abi_item["inputs"] || []).map { |i| canonical_type(i) }.join(",")
|
|
27
118
|
"#{abi_item["name"]}(#{inputs})"
|
|
28
119
|
end
|
|
120
|
+
|
|
121
|
+
# Resolve tuple types to their canonical form for signature hashing.
|
|
122
|
+
def canonical_type(input)
|
|
123
|
+
return input["type"] unless input["type"] == "tuple"
|
|
124
|
+
|
|
125
|
+
inner = (input["components"] || []).map { |c| canonical_type(c) }.join(",")
|
|
126
|
+
"(#{inner})"
|
|
127
|
+
end
|
|
29
128
|
end
|
|
30
129
|
end
|
|
31
130
|
end
|
|
@@ -29,7 +29,11 @@ module Viem
|
|
|
29
29
|
loop do
|
|
30
30
|
receipt = @transport.request("eth_getTransactionReceipt", [hash])
|
|
31
31
|
return format_receipt(receipt) if receipt
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
if Time.now > deadline
|
|
34
|
+
raise WaitForTransactionReceiptTimeoutError,
|
|
35
|
+
"Timeout after #{timeout}s waiting for #{hash}"
|
|
36
|
+
end
|
|
33
37
|
|
|
34
38
|
sleep poll_interval
|
|
35
39
|
end
|
|
@@ -11,7 +11,7 @@ module Viem
|
|
|
11
11
|
Abi::Decoder.decode_function_result(abi_item, result)
|
|
12
12
|
rescue RpcError => e
|
|
13
13
|
raise ContractFunctionExecutionError.new(e,
|
|
14
|
-
|
|
14
|
+
contract_address: address, function_name: function_name, args: args,)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def simulate_contract(address:, abi:, function_name:, args: [], account: nil, value: nil, block_tag: "latest")
|
|
@@ -22,7 +22,7 @@ module Viem
|
|
|
22
22
|
Abi::Decoder.decode_function_result(abi_item, result)
|
|
23
23
|
rescue RpcError => e
|
|
24
24
|
raise ContractFunctionExecutionError.new(e,
|
|
25
|
-
|
|
25
|
+
contract_address: address, function_name: function_name, args: args,)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
@@ -21,28 +21,23 @@ module Viem
|
|
|
21
21
|
nonce ||= get_transaction_count(address: acct.address)
|
|
22
22
|
chain_id = @chain&.id || get_chain_id
|
|
23
23
|
|
|
24
|
+
gas ||= begin
|
|
25
|
+
estimate_gas(to: to, from: acct.address, data: data, value: value)
|
|
26
|
+
rescue
|
|
27
|
+
21_000
|
|
28
|
+
end
|
|
24
29
|
if max_fee_per_gas
|
|
25
|
-
gas ||= begin
|
|
26
|
-
estimate_gas(to: to, from: acct.address, data: data, value: value)
|
|
27
|
-
rescue StandardError
|
|
28
|
-
21_000
|
|
29
|
-
end
|
|
30
30
|
tx = build_eip1559_tx(
|
|
31
31
|
to: to, value: value, data: data, gas: gas,
|
|
32
32
|
max_fee_per_gas: max_fee_per_gas,
|
|
33
33
|
max_priority_fee_per_gas: max_priority_fee_per_gas,
|
|
34
|
-
nonce: nonce, chain_id: chain_id
|
|
34
|
+
nonce: nonce, chain_id: chain_id,
|
|
35
35
|
)
|
|
36
36
|
else
|
|
37
|
-
gas ||= begin
|
|
38
|
-
estimate_gas(to: to, from: acct.address, data: data, value: value)
|
|
39
|
-
rescue StandardError
|
|
40
|
-
21_000
|
|
41
|
-
end
|
|
42
37
|
gas_price ||= get_gas_price
|
|
43
38
|
tx = build_legacy_tx(
|
|
44
39
|
to: to, value: value, data: data, gas: gas,
|
|
45
|
-
gas_price: gas_price, nonce: nonce, chain_id: chain_id
|
|
40
|
+
gas_price: gas_price, nonce: nonce, chain_id: chain_id,
|
|
46
41
|
)
|
|
47
42
|
end
|
|
48
43
|
|
|
@@ -59,27 +54,27 @@ module Viem
|
|
|
59
54
|
|
|
60
55
|
def build_eip1559_tx(to:, value:, data:, gas:, max_fee_per_gas:, max_priority_fee_per_gas:, nonce:, chain_id:)
|
|
61
56
|
Eth::Tx::Eip1559.new({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
chain_id: chain_id,
|
|
58
|
+
nonce: nonce,
|
|
59
|
+
max_priority_fee_per_gas: max_priority_fee_per_gas || Utils::Units.parse_gwei("1.5"),
|
|
60
|
+
max_fee_per_gas: max_fee_per_gas,
|
|
61
|
+
gas_limit: gas,
|
|
62
|
+
to: to,
|
|
63
|
+
value: value,
|
|
64
|
+
data: data || "",
|
|
65
|
+
})
|
|
71
66
|
end
|
|
72
67
|
|
|
73
68
|
def build_legacy_tx(to:, value:, data:, gas:, gas_price:, nonce:, chain_id:)
|
|
74
69
|
Eth::Tx::Legacy.new({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
chain_id: chain_id,
|
|
71
|
+
nonce: nonce,
|
|
72
|
+
gas_price: gas_price,
|
|
73
|
+
gas_limit: gas,
|
|
74
|
+
to: to,
|
|
75
|
+
value: value,
|
|
76
|
+
data: data || "",
|
|
77
|
+
})
|
|
83
78
|
end
|
|
84
79
|
|
|
85
80
|
def sign_tx(account, tx)
|
|
@@ -27,11 +27,11 @@ module Viem
|
|
|
27
27
|
gas: gas, gas_price: gas_price,
|
|
28
28
|
max_fee_per_gas: max_fee_per_gas,
|
|
29
29
|
max_priority_fee_per_gas: max_priority_fee_per_gas,
|
|
30
|
-
account: acct
|
|
30
|
+
account: acct,
|
|
31
31
|
)
|
|
32
32
|
rescue RpcError => e
|
|
33
33
|
raise ContractFunctionExecutionError.new(e,
|
|
34
|
-
|
|
34
|
+
contract_address: address, function_name: function_name, args: args,)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def deploy_contract(abi:, bytecode:, args: [], value: nil, gas: nil, account: nil)
|
data/lib/viem/chains/base.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Viem
|
|
|
9
9
|
native_currency: NativeCurrency.new(name: "Ether", symbol: "ETH", decimals: 18),
|
|
10
10
|
rpc_urls: { default: { http: ["https://cloudflare-eth.com"] } },
|
|
11
11
|
block_explorers: { default: { name: "Etherscan", url: "https://etherscan.io" } },
|
|
12
|
-
testnet: false
|
|
12
|
+
testnet: false,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
SEPOLIA = Chain.new(
|
|
@@ -19,7 +19,7 @@ module Viem
|
|
|
19
19
|
native_currency: NativeCurrency.new(name: "Sepolia Ether", symbol: "ETH", decimals: 18),
|
|
20
20
|
rpc_urls: { default: { http: ["https://rpc.sepolia.org"] } },
|
|
21
21
|
block_explorers: { default: { name: "Etherscan", url: "https://sepolia.etherscan.io" } },
|
|
22
|
-
testnet: true
|
|
22
|
+
testnet: true,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
GOERLI = Chain.new(
|
|
@@ -29,7 +29,7 @@ module Viem
|
|
|
29
29
|
native_currency: NativeCurrency.new(name: "Goerli Ether", symbol: "ETH", decimals: 18),
|
|
30
30
|
rpc_urls: { default: { http: ["https://rpc.ankr.com/eth_goerli"] } },
|
|
31
31
|
block_explorers: { default: { name: "Etherscan", url: "https://goerli.etherscan.io" } },
|
|
32
|
-
testnet: true
|
|
32
|
+
testnet: true,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
POLYGON = Chain.new(
|
|
@@ -39,7 +39,7 @@ module Viem
|
|
|
39
39
|
native_currency: NativeCurrency.new(name: "MATIC", symbol: "MATIC", decimals: 18),
|
|
40
40
|
rpc_urls: { default: { http: ["https://polygon-rpc.com"] } },
|
|
41
41
|
block_explorers: { default: { name: "PolygonScan", url: "https://polygonscan.com" } },
|
|
42
|
-
testnet: false
|
|
42
|
+
testnet: false,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
POLYGON_MUMBAI = Chain.new(
|
|
@@ -49,7 +49,7 @@ module Viem
|
|
|
49
49
|
native_currency: NativeCurrency.new(name: "MATIC", symbol: "MATIC", decimals: 18),
|
|
50
50
|
rpc_urls: { default: { http: ["https://rpc-mumbai.maticvigil.com"] } },
|
|
51
51
|
block_explorers: { default: { name: "PolygonScan", url: "https://mumbai.polygonscan.com" } },
|
|
52
|
-
testnet: true
|
|
52
|
+
testnet: true,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
OPTIMISM = Chain.new(
|
|
@@ -59,7 +59,7 @@ module Viem
|
|
|
59
59
|
native_currency: NativeCurrency.new(name: "Ether", symbol: "ETH", decimals: 18),
|
|
60
60
|
rpc_urls: { default: { http: ["https://mainnet.optimism.io"] } },
|
|
61
61
|
block_explorers: { default: { name: "Optimism Explorer", url: "https://optimistic.etherscan.io" } },
|
|
62
|
-
testnet: false
|
|
62
|
+
testnet: false,
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
OPTIMISM_GOERLI = Chain.new(
|
|
@@ -69,7 +69,7 @@ module Viem
|
|
|
69
69
|
native_currency: NativeCurrency.new(name: "Goerli Ether", symbol: "ETH", decimals: 18),
|
|
70
70
|
rpc_urls: { default: { http: ["https://goerli.optimism.io"] } },
|
|
71
71
|
block_explorers: { default: { name: "Optimism Explorer", url: "https://goerli-optimism.etherscan.io" } },
|
|
72
|
-
testnet: true
|
|
72
|
+
testnet: true,
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
ARBITRUM = Chain.new(
|
|
@@ -79,7 +79,7 @@ module Viem
|
|
|
79
79
|
native_currency: NativeCurrency.new(name: "Ether", symbol: "ETH", decimals: 18),
|
|
80
80
|
rpc_urls: { default: { http: ["https://arb1.arbitrum.io/rpc"] } },
|
|
81
81
|
block_explorers: { default: { name: "Arbiscan", url: "https://arbiscan.io" } },
|
|
82
|
-
testnet: false
|
|
82
|
+
testnet: false,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
ARBITRUM_GOERLI = Chain.new(
|
|
@@ -89,7 +89,7 @@ module Viem
|
|
|
89
89
|
native_currency: NativeCurrency.new(name: "Arbitrum Goerli Ether", symbol: "AGOR", decimals: 18),
|
|
90
90
|
rpc_urls: { default: { http: ["https://goerli-rollup.arbitrum.io/rpc"] } },
|
|
91
91
|
block_explorers: { default: { name: "Arbiscan", url: "https://goerli.arbiscan.io" } },
|
|
92
|
-
testnet: true
|
|
92
|
+
testnet: true,
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
BASE = Chain.new(
|
|
@@ -99,7 +99,7 @@ module Viem
|
|
|
99
99
|
native_currency: NativeCurrency.new(name: "Ether", symbol: "ETH", decimals: 18),
|
|
100
100
|
rpc_urls: { default: { http: ["https://mainnet.base.org"] } },
|
|
101
101
|
block_explorers: { default: { name: "Basescan", url: "https://basescan.org" } },
|
|
102
|
-
testnet: false
|
|
102
|
+
testnet: false,
|
|
103
103
|
)
|
|
104
104
|
|
|
105
105
|
BASE_GOERLI = Chain.new(
|
|
@@ -109,7 +109,7 @@ module Viem
|
|
|
109
109
|
native_currency: NativeCurrency.new(name: "Goerli Ether", symbol: "ETH", decimals: 18),
|
|
110
110
|
rpc_urls: { default: { http: ["https://goerli.base.org"] } },
|
|
111
111
|
block_explorers: { default: { name: "Basescan", url: "https://goerli.basescan.org" } },
|
|
112
|
-
testnet: true
|
|
112
|
+
testnet: true,
|
|
113
113
|
)
|
|
114
114
|
|
|
115
115
|
AVALANCHE = Chain.new(
|
|
@@ -119,7 +119,7 @@ module Viem
|
|
|
119
119
|
native_currency: NativeCurrency.new(name: "Avalanche", symbol: "AVAX", decimals: 18),
|
|
120
120
|
rpc_urls: { default: { http: ["https://api.avax.network/ext/bc/C/rpc"] } },
|
|
121
121
|
block_explorers: { default: { name: "SnowTrace", url: "https://snowtrace.io" } },
|
|
122
|
-
testnet: false
|
|
122
|
+
testnet: false,
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
AVALANCHE_FUJI = Chain.new(
|
|
@@ -129,7 +129,7 @@ module Viem
|
|
|
129
129
|
native_currency: NativeCurrency.new(name: "Avalanche", symbol: "AVAX", decimals: 18),
|
|
130
130
|
rpc_urls: { default: { http: ["https://api.avax-test.network/ext/bc/C/rpc"] } },
|
|
131
131
|
block_explorers: { default: { name: "SnowTrace", url: "https://testnet.snowtrace.io" } },
|
|
132
|
-
testnet: true
|
|
132
|
+
testnet: true,
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
BSC = Chain.new(
|
|
@@ -139,7 +139,7 @@ module Viem
|
|
|
139
139
|
native_currency: NativeCurrency.new(name: "BNB", symbol: "BNB", decimals: 18),
|
|
140
140
|
rpc_urls: { default: { http: ["https://bsc-dataseed.binance.org"] } },
|
|
141
141
|
block_explorers: { default: { name: "BscScan", url: "https://bscscan.com" } },
|
|
142
|
-
testnet: false
|
|
142
|
+
testnet: false,
|
|
143
143
|
)
|
|
144
144
|
|
|
145
145
|
BSC_TESTNET = Chain.new(
|
|
@@ -149,7 +149,7 @@ module Viem
|
|
|
149
149
|
native_currency: NativeCurrency.new(name: "BNB", symbol: "tBNB", decimals: 18),
|
|
150
150
|
rpc_urls: { default: { http: ["https://data-seed-prebsc-1-s1.binance.org:8545"] } },
|
|
151
151
|
block_explorers: { default: { name: "BscScan", url: "https://testnet.bscscan.com" } },
|
|
152
|
-
testnet: true
|
|
152
|
+
testnet: true,
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
GNOSIS = Chain.new(
|
|
@@ -159,7 +159,7 @@ module Viem
|
|
|
159
159
|
native_currency: NativeCurrency.new(name: "xDAI", symbol: "xDAI", decimals: 18),
|
|
160
160
|
rpc_urls: { default: { http: ["https://rpc.gnosischain.com"] } },
|
|
161
161
|
block_explorers: { default: { name: "Gnosis Scan", url: "https://gnosisscan.io" } },
|
|
162
|
-
testnet: false
|
|
162
|
+
testnet: false,
|
|
163
163
|
)
|
|
164
164
|
|
|
165
165
|
FANTOM = Chain.new(
|
|
@@ -169,7 +169,7 @@ module Viem
|
|
|
169
169
|
native_currency: NativeCurrency.new(name: "Fantom", symbol: "FTM", decimals: 18),
|
|
170
170
|
rpc_urls: { default: { http: ["https://rpc.ftm.tools"] } },
|
|
171
171
|
block_explorers: { default: { name: "FtmScan", url: "https://ftmscan.com" } },
|
|
172
|
-
testnet: false
|
|
172
|
+
testnet: false,
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
CELO = Chain.new(
|
|
@@ -179,7 +179,7 @@ module Viem
|
|
|
179
179
|
native_currency: NativeCurrency.new(name: "CELO", symbol: "CELO", decimals: 18),
|
|
180
180
|
rpc_urls: { default: { http: ["https://forno.celo.org"] } },
|
|
181
181
|
block_explorers: { default: { name: "Celo Explorer", url: "https://explorer.celo.org" } },
|
|
182
|
-
testnet: false
|
|
182
|
+
testnet: false,
|
|
183
183
|
)
|
|
184
184
|
end
|
|
185
185
|
end
|
|
@@ -5,9 +5,9 @@ module Viem
|
|
|
5
5
|
class TestClient < PublicClient
|
|
6
6
|
def mine(blocks: 1, interval: 0)
|
|
7
7
|
@transport.request("anvil_mine", [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Utils::Hex.number_to_hex(blocks),
|
|
9
|
+
Utils::Hex.number_to_hex(interval),
|
|
10
|
+
],)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def set_balance(address:, value:)
|
data/lib/viem/transports/http.rb
CHANGED
|
@@ -19,9 +19,7 @@ module Viem
|
|
|
19
19
|
def request(method, params = [])
|
|
20
20
|
body = build_rpc_body(method, params)
|
|
21
21
|
resp = @mutex.synchronize { @conn.post("/", body.to_json) }
|
|
22
|
-
unless resp.success?
|
|
23
|
-
raise HttpRequestError.new("HTTP #{resp.status}", status: resp.status, body: resp.body)
|
|
24
|
-
end
|
|
22
|
+
raise HttpRequestError.new("HTTP #{resp.status}", status: resp.status, body: resp.body) unless resp.success?
|
|
25
23
|
|
|
26
24
|
parse_response(resp.body)
|
|
27
25
|
rescue Faraday::Error => e
|
data/lib/viem/utils/address.rb
CHANGED
data/lib/viem/utils/hex.rb
CHANGED
|
@@ -58,8 +58,8 @@ module Viem
|
|
|
58
58
|
|
|
59
59
|
private_class_method def self.twos_complement(value)
|
|
60
60
|
bits = value.bit_length + 1
|
|
61
|
-
bits += (8 - bits % 8) if (bits % 8) != 0
|
|
62
|
-
(2**bits + value).to_s(16)
|
|
61
|
+
bits += (8 - (bits % 8)) if (bits % 8) != 0
|
|
62
|
+
((2**bits) + value).to_s(16)
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
data/lib/viem/utils/units.rb
CHANGED
|
@@ -26,11 +26,11 @@ module Viem
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def self.parse_units(value, decimals)
|
|
29
|
-
(BigDecimal(value.to_s) * 10**decimals).to_i
|
|
29
|
+
(BigDecimal(value.to_s) * (10**decimals)).to_i
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.format_units(value, decimals)
|
|
33
|
-
(BigDecimal(value.to_s) / 10**decimals).to_s("F")
|
|
33
|
+
(BigDecimal(value.to_s) / (10**decimals)).to_s("F")
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
end
|
data/lib/viem/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: viem_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nyrok
|
|
@@ -67,36 +67,36 @@ dependencies:
|
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '3.12'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
70
|
+
name: rubocop
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - "~>"
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '
|
|
75
|
+
version: '1.50'
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
82
|
+
version: '1.50'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
84
|
+
name: webmock
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
89
|
+
version: '3.18'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
97
|
-
description: A Ruby/Rails adaptation of the viem TypeScript library. Provides
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
version: '3.18'
|
|
97
|
+
description: A Ruby/Rails adaptation of the viem TypeScript library. Provides Ethereum
|
|
98
|
+
clients, ABI encoding/decoding, account management, and utilities for Ruby on Rails
|
|
99
|
+
7+ applications.
|
|
100
100
|
email:
|
|
101
101
|
- nyrokgaming1@gmail.com
|
|
102
102
|
executables: []
|
|
@@ -143,7 +143,8 @@ files:
|
|
|
143
143
|
homepage: https://github.com/Nyrok/viem_rb
|
|
144
144
|
licenses:
|
|
145
145
|
- MIT
|
|
146
|
-
metadata:
|
|
146
|
+
metadata:
|
|
147
|
+
rubygems_mfa_required: 'true'
|
|
147
148
|
post_install_message:
|
|
148
149
|
rdoc_options: []
|
|
149
150
|
require_paths:
|
|
@@ -152,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
152
153
|
requirements:
|
|
153
154
|
- - ">="
|
|
154
155
|
- !ruby/object:Gem::Version
|
|
155
|
-
version:
|
|
156
|
+
version: 3.0.0
|
|
156
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
158
|
requirements:
|
|
158
159
|
- - ">="
|