block_given 0.1.0
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 +7 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +463 -0
- data/lib/block_given/abi/coder.rb +134 -0
- data/lib/block_given/abi/custom_error.rb +29 -0
- data/lib/block_given/abi/event.rb +87 -0
- data/lib/block_given/abi/function.rb +72 -0
- data/lib/block_given/abi/interface.rb +109 -0
- data/lib/block_given/abi/parameter.rb +57 -0
- data/lib/block_given/chain.rb +112 -0
- data/lib/block_given/client.rb +234 -0
- data/lib/block_given/configuration.rb +48 -0
- data/lib/block_given/connectors/alchemy.rb +36 -0
- data/lib/block_given/connectors/base.rb +26 -0
- data/lib/block_given/connectors/http.rb +150 -0
- data/lib/block_given/connectors/stub.rb +66 -0
- data/lib/block_given/contract.rb +287 -0
- data/lib/block_given/errors.rb +157 -0
- data/lib/block_given/event.rb +36 -0
- data/lib/block_given/normalizer.rb +37 -0
- data/lib/block_given/poller.rb +226 -0
- data/lib/block_given/railtie.rb +17 -0
- data/lib/block_given/receipt.rb +44 -0
- data/lib/block_given/signed_transaction.rb +145 -0
- data/lib/block_given/transaction.rb +85 -0
- data/lib/block_given/utils.rb +134 -0
- data/lib/block_given/version.rb +5 -0
- data/lib/block_given/wallet.rb +137 -0
- data/lib/block_given.rb +68 -0
- metadata +130 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BlockGiven
|
|
4
|
+
# A private key + (optional) client, able to sign and send transactions
|
|
5
|
+
# (viem's WalletClient + Account).
|
|
6
|
+
#
|
|
7
|
+
# wallet = BlockGiven::Wallet.new(private_key: ENV["PRIVATE_KEY"])
|
|
8
|
+
# wallet.address
|
|
9
|
+
# wallet.send_transaction(to: "0x...", value: BlockGiven::Utils.parse_ether("0.01")).wait
|
|
10
|
+
class Wallet
|
|
11
|
+
attr_reader :address
|
|
12
|
+
|
|
13
|
+
def self.generate(client: nil)
|
|
14
|
+
new(private_key: Eth::Key.new.private_hex, client: client)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(private_key:, client: nil)
|
|
18
|
+
hex = Utils.strip_hex(private_key.to_s.strip)
|
|
19
|
+
raise InvalidArgumentError, "private key must be 32 bytes hex" unless hex.match?(/\A[0-9a-fA-F]{64}\z/)
|
|
20
|
+
|
|
21
|
+
@key = Eth::Key.new(priv: hex)
|
|
22
|
+
@address = @key.address.checksummed
|
|
23
|
+
@client = client
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def private_key = Utils.prefix_hex(@key.private_hex)
|
|
27
|
+
def public_key = Utils.prefix_hex(@key.public_hex)
|
|
28
|
+
|
|
29
|
+
def client = @client || BlockGiven.client
|
|
30
|
+
def with_client(client) = self.class.new(private_key: private_key, client: client)
|
|
31
|
+
def chain = client.chain
|
|
32
|
+
|
|
33
|
+
def balance(block: :latest) = client.get_balance(address, block: block)
|
|
34
|
+
def nonce(block: :pending) = client.get_transaction_count(address, block: block)
|
|
35
|
+
|
|
36
|
+
# EIP-191 personal_sign. Returns the 65-byte signature as hex.
|
|
37
|
+
def sign_message(message)
|
|
38
|
+
Utils.prefix_hex(@key.personal_sign(message))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# EIP-712 typed data (Hash with :types, :primaryType, :domain, :message).
|
|
42
|
+
def sign_typed_data(typed_data)
|
|
43
|
+
Utils.prefix_hex(@key.sign_typed_data(typed_data))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Fills in nonce / gas / fees from the client and signs, without broadcasting.
|
|
47
|
+
# Returns a BlockGiven::SignedTransaction: its #hash and #nonce are known before
|
|
48
|
+
# any network call (persist them, then #broadcast). Same keywords as prepare_transaction;
|
|
49
|
+
# pass gas_price: to build a legacy (type 0) transaction instead of EIP-1559.
|
|
50
|
+
def signed_transaction(**params)
|
|
51
|
+
prepared = prepare_transaction(**params)
|
|
52
|
+
tx = Eth::Tx.new(to_eth_tx_params(prepared))
|
|
53
|
+
tx.sign(@key)
|
|
54
|
+
SignedTransaction.new(raw: Utils.prefix_hex(tx.hex), params: prepared, wallet: self)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Signs and returns the raw tx hex.
|
|
58
|
+
def sign_transaction(**params) = signed_transaction(**params).raw
|
|
59
|
+
|
|
60
|
+
# Signs and broadcasts. Returns a BlockGiven::Transaction.
|
|
61
|
+
def send_transaction(**params) = signed_transaction(**params).broadcast
|
|
62
|
+
|
|
63
|
+
# Resolves every missing field (nonce, gas, fees, chain id) without signing.
|
|
64
|
+
def prepare_transaction(to: nil, value: 0, data: nil, gas: nil, nonce: nil, max_fee_per_gas: nil,
|
|
65
|
+
max_priority_fee_per_gas: nil, gas_price: nil, chain_id: nil, access_list: nil)
|
|
66
|
+
to = Utils.checksum_address(to) if to
|
|
67
|
+
value = coerce_wei(value)
|
|
68
|
+
data = data.nil? || data.empty? ? "" : Utils.prefix_hex(data)
|
|
69
|
+
|
|
70
|
+
params = {
|
|
71
|
+
from: address, to: to, value: value, data: data,
|
|
72
|
+
chain_id: chain_id || client.chain.id,
|
|
73
|
+
nonce: nonce || self.nonce,
|
|
74
|
+
gas: gas || estimate_gas(to: to, data: data, value: value),
|
|
75
|
+
access_list: access_list
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if gas_price
|
|
79
|
+
params[:gas_price] = gas_price
|
|
80
|
+
else
|
|
81
|
+
fees = if max_fee_per_gas && max_priority_fee_per_gas
|
|
82
|
+
{ max_fee_per_gas: max_fee_per_gas, max_priority_fee_per_gas: max_priority_fee_per_gas }
|
|
83
|
+
else
|
|
84
|
+
estimated = client.estimate_fees_per_gas
|
|
85
|
+
{ max_fee_per_gas: max_fee_per_gas || estimated[:max_fee_per_gas],
|
|
86
|
+
max_priority_fee_per_gas: max_priority_fee_per_gas || estimated[:max_priority_fee_per_gas] }
|
|
87
|
+
end
|
|
88
|
+
params.merge!(fees)
|
|
89
|
+
end
|
|
90
|
+
params
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ==(other) = other.is_a?(Wallet) && other.address == address
|
|
94
|
+
alias eql? ==
|
|
95
|
+
def hash = address.hash
|
|
96
|
+
def to_s = address
|
|
97
|
+
def inspect = "#<BlockGiven::Wallet #{address}>"
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def estimate_gas(to:, data:, value:)
|
|
102
|
+
raise InvalidArgumentError, "contract creation requires an explicit gas: value" if to.nil?
|
|
103
|
+
|
|
104
|
+
estimate = client.estimate_gas(to: to, data: data, from: address, value: value)
|
|
105
|
+
(estimate * BlockGiven.config.gas_multiplier).ceil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def coerce_wei(value)
|
|
109
|
+
case value
|
|
110
|
+
when nil then 0
|
|
111
|
+
when Integer then value
|
|
112
|
+
when Float, BigDecimal, Rational
|
|
113
|
+
unless value == value.to_i
|
|
114
|
+
raise InvalidArgumentError,
|
|
115
|
+
"value must be an integer amount of wei (use BlockGiven::Utils.parse_ether)"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
value.to_i
|
|
119
|
+
when String then Utils.hex?(value) ? Utils.hex_to_int(value) : Integer(value, 10)
|
|
120
|
+
else raise InvalidArgumentError, "invalid value: #{value.inspect}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def to_eth_tx_params(params)
|
|
125
|
+
base = {
|
|
126
|
+
chain_id: params[:chain_id], nonce: params[:nonce], gas_limit: params[:gas],
|
|
127
|
+
to: params[:to], value: params[:value], data: params[:data]
|
|
128
|
+
}
|
|
129
|
+
if params[:gas_price]
|
|
130
|
+
base.merge(gas_price: params[:gas_price])
|
|
131
|
+
else
|
|
132
|
+
base[:access_list] = params[:access_list] || []
|
|
133
|
+
base.merge(priority_fee: params[:max_priority_fee_per_gas], max_gas_fee: params[:max_fee_per_gas])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
data/lib/block_given.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "eth"
|
|
4
|
+
|
|
5
|
+
require_relative "block_given/version"
|
|
6
|
+
require_relative "block_given/errors"
|
|
7
|
+
require_relative "block_given/utils"
|
|
8
|
+
require_relative "block_given/chain"
|
|
9
|
+
require_relative "block_given/configuration"
|
|
10
|
+
require_relative "block_given/connectors/base"
|
|
11
|
+
require_relative "block_given/connectors/http"
|
|
12
|
+
require_relative "block_given/connectors/alchemy"
|
|
13
|
+
require_relative "block_given/connectors/stub"
|
|
14
|
+
require_relative "block_given/normalizer"
|
|
15
|
+
require_relative "block_given/poller"
|
|
16
|
+
require_relative "block_given/receipt"
|
|
17
|
+
require_relative "block_given/transaction"
|
|
18
|
+
require_relative "block_given/signed_transaction"
|
|
19
|
+
require_relative "block_given/client"
|
|
20
|
+
require_relative "block_given/wallet"
|
|
21
|
+
require_relative "block_given/abi/parameter"
|
|
22
|
+
require_relative "block_given/abi/coder"
|
|
23
|
+
require_relative "block_given/abi/function"
|
|
24
|
+
require_relative "block_given/abi/event"
|
|
25
|
+
require_relative "block_given/abi/custom_error"
|
|
26
|
+
require_relative "block_given/abi/interface"
|
|
27
|
+
require_relative "block_given/event"
|
|
28
|
+
require_relative "block_given/contract"
|
|
29
|
+
require_relative "block_given/railtie" if defined?(Rails::Railtie)
|
|
30
|
+
|
|
31
|
+
# viem-inspired toolkit to read from and write to EVM smart contracts.
|
|
32
|
+
#
|
|
33
|
+
# BlockGiven.configure do |c|
|
|
34
|
+
# c.connector = BlockGiven::Connectors::Alchemy.new(api_key: ENV["ALCHEMY_API_KEY"])
|
|
35
|
+
# c.chain = :base
|
|
36
|
+
# end
|
|
37
|
+
module BlockGiven
|
|
38
|
+
class << self
|
|
39
|
+
def config
|
|
40
|
+
@config ||= Configuration.new
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def configure
|
|
44
|
+
yield config
|
|
45
|
+
@client = nil
|
|
46
|
+
config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Default client built from the global configuration.
|
|
50
|
+
def client
|
|
51
|
+
@client ||= Client.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_writer :client
|
|
55
|
+
|
|
56
|
+
# Running background watchers (see BlockGiven::Watcher.find / stop / stop_all).
|
|
57
|
+
def watchers = Watcher.all
|
|
58
|
+
|
|
59
|
+
# Forget configuration and default client, stop every watcher (useful in tests).
|
|
60
|
+
def reset!
|
|
61
|
+
Watcher.stop_all(join: 1)
|
|
62
|
+
@config = nil
|
|
63
|
+
@client = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def logger = config.logger
|
|
67
|
+
end
|
|
68
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: block_given
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Remi Wallaere
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-11 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bigdecimal
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: eth
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.5'
|
|
34
|
+
- - ">="
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: 0.5.17
|
|
37
|
+
type: :runtime
|
|
38
|
+
prerelease: false
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - "~>"
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0.5'
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.5.17
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: logger
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.5'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.5'
|
|
61
|
+
description: |
|
|
62
|
+
BlockGiven lets you talk to EVM smart contracts from Ruby with a small, explicit API
|
|
63
|
+
inspired by viem: typed contract classes generated from an ABI, wallets that sign
|
|
64
|
+
EIP-1559 transactions, pluggable JSON-RPC connectors (Alchemy first) and
|
|
65
|
+
polling helpers for receipts, blocks and events.
|
|
66
|
+
email:
|
|
67
|
+
- remi@boleromusic.com
|
|
68
|
+
executables: []
|
|
69
|
+
extensions: []
|
|
70
|
+
extra_rdoc_files: []
|
|
71
|
+
files:
|
|
72
|
+
- CHANGELOG.md
|
|
73
|
+
- LICENSE.txt
|
|
74
|
+
- README.md
|
|
75
|
+
- lib/block_given.rb
|
|
76
|
+
- lib/block_given/abi/coder.rb
|
|
77
|
+
- lib/block_given/abi/custom_error.rb
|
|
78
|
+
- lib/block_given/abi/event.rb
|
|
79
|
+
- lib/block_given/abi/function.rb
|
|
80
|
+
- lib/block_given/abi/interface.rb
|
|
81
|
+
- lib/block_given/abi/parameter.rb
|
|
82
|
+
- lib/block_given/chain.rb
|
|
83
|
+
- lib/block_given/client.rb
|
|
84
|
+
- lib/block_given/configuration.rb
|
|
85
|
+
- lib/block_given/connectors/alchemy.rb
|
|
86
|
+
- lib/block_given/connectors/base.rb
|
|
87
|
+
- lib/block_given/connectors/http.rb
|
|
88
|
+
- lib/block_given/connectors/stub.rb
|
|
89
|
+
- lib/block_given/contract.rb
|
|
90
|
+
- lib/block_given/errors.rb
|
|
91
|
+
- lib/block_given/event.rb
|
|
92
|
+
- lib/block_given/normalizer.rb
|
|
93
|
+
- lib/block_given/poller.rb
|
|
94
|
+
- lib/block_given/railtie.rb
|
|
95
|
+
- lib/block_given/receipt.rb
|
|
96
|
+
- lib/block_given/signed_transaction.rb
|
|
97
|
+
- lib/block_given/transaction.rb
|
|
98
|
+
- lib/block_given/utils.rb
|
|
99
|
+
- lib/block_given/version.rb
|
|
100
|
+
- lib/block_given/wallet.rb
|
|
101
|
+
homepage: https://github.com/Bolero-Music/block_given
|
|
102
|
+
licenses:
|
|
103
|
+
- MIT
|
|
104
|
+
metadata:
|
|
105
|
+
homepage_uri: https://github.com/Bolero-Music/block_given
|
|
106
|
+
source_code_uri: https://github.com/Bolero-Music/block_given
|
|
107
|
+
changelog_uri: https://github.com/Bolero-Music/block_given/blob/main/CHANGELOG.md
|
|
108
|
+
bug_tracker_uri: https://github.com/Bolero-Music/block_given/issues
|
|
109
|
+
documentation_uri: https://rubydoc.info/gems/block_given
|
|
110
|
+
rubygems_mfa_required: 'true'
|
|
111
|
+
post_install_message:
|
|
112
|
+
rdoc_options: []
|
|
113
|
+
require_paths:
|
|
114
|
+
- lib
|
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
|
+
requirements:
|
|
117
|
+
- - ">="
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: 3.1.0
|
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
requirements: []
|
|
126
|
+
rubygems_version: 3.3.7
|
|
127
|
+
signing_key:
|
|
128
|
+
specification_version: 4
|
|
129
|
+
summary: viem-inspired Ruby client for EVM smart contracts.
|
|
130
|
+
test_files: []
|