block_given 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +34 -1
- data/README.md +92 -55
- data/lib/block_given/abi/codec/decoder.rb +111 -0
- data/lib/block_given/abi/codec.rb +170 -0
- data/lib/block_given/abi/coder.rb +122 -11
- data/lib/block_given/abi/custom_error.rb +33 -1
- data/lib/block_given/abi/event.rb +85 -3
- data/lib/block_given/abi/function.rb +94 -2
- data/lib/block_given/abi/interface.rb +99 -4
- data/lib/block_given/abi/parameter.rb +60 -3
- data/lib/block_given/abi/standards/erc1155.rb +41 -0
- data/lib/block_given/abi/standards/erc20.rb +33 -0
- data/lib/block_given/abi/standards/erc4626.rb +41 -0
- data/lib/block_given/abi/standards/erc721.rb +47 -0
- data/lib/block_given/abi/standards.rb +124 -0
- data/lib/block_given/abi/type.rb +138 -0
- data/lib/block_given/chain.rb +114 -2
- data/lib/block_given/client.rb +322 -20
- data/lib/block_given/configuration.rb +85 -3
- data/lib/block_given/connectors/alchemy.rb +38 -4
- data/lib/block_given/connectors/base.rb +32 -5
- data/lib/block_given/connectors/http.rb +91 -5
- data/lib/block_given/connectors/stub.rb +70 -4
- data/lib/block_given/contract.rb +422 -26
- data/lib/block_given/crypto/keccak.rb +152 -0
- data/lib/block_given/crypto/secp256k1.rb +168 -0
- data/lib/block_given/crypto.rb +22 -0
- data/lib/block_given/eip712.rb +199 -0
- data/lib/block_given/errors.rb +134 -14
- data/lib/block_given/event.rb +51 -1
- data/lib/block_given/normalizer.rb +27 -2
- data/lib/block_given/poller.rb +177 -15
- data/lib/block_given/receipt.rb +63 -3
- data/lib/block_given/rlp.rb +146 -0
- data/lib/block_given/signed_transaction.rb +148 -47
- data/lib/block_given/transaction.rb +103 -12
- data/lib/block_given/transaction_envelope/fields.rb +104 -0
- data/lib/block_given/transaction_envelope.rb +183 -0
- data/lib/block_given/utils.rb +143 -9
- data/lib/block_given/version.rb +2 -1
- data/lib/block_given/wallet.rb +216 -35
- data/lib/block_given.rb +67 -4
- metadata +19 -23
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BlockGiven
|
|
4
|
+
module Abi
|
|
5
|
+
# A parsed Solidity ABI type (`uint256`, `bytes32[2][]`, `(uint256,(bool,bytes))[]`), as the codec needs it:
|
|
6
|
+
# its base type, its size, its array dimensions and its tuple components.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class Type
|
|
10
|
+
# @return [String] `uint`, `int`, `address`, `bool`, `bytes`, `string` or `tuple`
|
|
11
|
+
attr_reader :base
|
|
12
|
+
# @return [Integer, nil] bits of an integer, bytes of a `bytesN`, nil otherwise
|
|
13
|
+
attr_reader :size
|
|
14
|
+
# @return [Array<Integer, nil>] array dimensions, innermost first; nil marks a dynamic `[]`
|
|
15
|
+
attr_reader :dimensions
|
|
16
|
+
# @return [Array<Type>] tuple components, empty for other types
|
|
17
|
+
attr_reader :components
|
|
18
|
+
|
|
19
|
+
# Parses a canonical type string, with tuples written `(a,b)` or `tuple(a,b)`.
|
|
20
|
+
#
|
|
21
|
+
# @param type [String]
|
|
22
|
+
# @return [Type]
|
|
23
|
+
# @raise [BlockGiven::AbiError] when the type is malformed or not supported (`fixed`, `function`)
|
|
24
|
+
def self.parse(type)
|
|
25
|
+
type = type.to_s.strip
|
|
26
|
+
type = type.delete_prefix("tuple") if type.start_with?("tuple(")
|
|
27
|
+
return parse_tuple(type) if type.start_with?("(")
|
|
28
|
+
|
|
29
|
+
match = type.match(/\A([a-z]+)(\d*)((?:\[\d*\])*)\z/)
|
|
30
|
+
raise AbiError, "unsupported ABI type #{type.inspect}" unless match
|
|
31
|
+
|
|
32
|
+
new(*elementary(match[1], match[2], type), dimensions(match[3]))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Parses a tuple type and its trailing dimensions.
|
|
36
|
+
#
|
|
37
|
+
# @param type [String] starting with `(`
|
|
38
|
+
# @return [Type]
|
|
39
|
+
# @raise [BlockGiven::AbiError] when the parentheses do not balance
|
|
40
|
+
def self.parse_tuple(type)
|
|
41
|
+
depth = 0
|
|
42
|
+
parts = [+""]
|
|
43
|
+
type.each_char.with_index do |char, index|
|
|
44
|
+
depth += { "(" => 1, ")" => -1 }.fetch(char, 0)
|
|
45
|
+
if depth.zero?
|
|
46
|
+
raise AbiError, "unsupported ABI type #{type.inspect}" unless type[(index + 1)..].match?(/\A(\[\d*\])*\z/)
|
|
47
|
+
|
|
48
|
+
components = parts.first.empty? && parts.size == 1 ? [] : parts.map { |part| parse(part) }
|
|
49
|
+
return new("tuple", nil, dimensions(type[(index + 1)..]), components)
|
|
50
|
+
end
|
|
51
|
+
next if depth == 1 && char == "("
|
|
52
|
+
|
|
53
|
+
depth == 1 && char == "," ? parts << +"" : parts.last << char
|
|
54
|
+
end
|
|
55
|
+
raise AbiError, "unsupported ABI type #{type.inspect}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Validates an elementary base type and its size suffix.
|
|
59
|
+
#
|
|
60
|
+
# @param base [String]
|
|
61
|
+
# @param suffix [String] digits, possibly empty
|
|
62
|
+
# @param type [String] the full type, for error messages
|
|
63
|
+
# @return [Array(String, Integer)] base and size (nil when the type takes none)
|
|
64
|
+
# @raise [BlockGiven::AbiError] for an unknown base or an invalid size
|
|
65
|
+
def self.elementary(base, suffix, type)
|
|
66
|
+
size = suffix.empty? ? nil : suffix.to_i
|
|
67
|
+
valid =
|
|
68
|
+
case base
|
|
69
|
+
when "uint", "int"
|
|
70
|
+
size ||= 256
|
|
71
|
+
size.between?(8, 256) && (size % 8).zero?
|
|
72
|
+
when "bytes" then size.nil? || size.between?(1, 32)
|
|
73
|
+
when "address", "bool", "string" then size.nil?
|
|
74
|
+
else false
|
|
75
|
+
end
|
|
76
|
+
raise AbiError, "unsupported ABI type #{type.inspect}" unless valid
|
|
77
|
+
|
|
78
|
+
[base, size]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @param suffix [String] e.g. `[2][]`
|
|
82
|
+
# @return [Array<Integer, nil>]
|
|
83
|
+
def self.dimensions(suffix) = suffix.scan(/\[(\d*)\]/).map { |(digits)| digits.empty? ? nil : digits.to_i }
|
|
84
|
+
|
|
85
|
+
private_class_method :parse_tuple, :elementary, :dimensions
|
|
86
|
+
|
|
87
|
+
# @param base [String]
|
|
88
|
+
# @param size [Integer, nil]
|
|
89
|
+
# @param dimensions [Array<Integer, nil>]
|
|
90
|
+
# @param components [Array<Type>]
|
|
91
|
+
def initialize(base, size, dimensions, components = [])
|
|
92
|
+
@base = base
|
|
93
|
+
@size = size
|
|
94
|
+
@dimensions = dimensions
|
|
95
|
+
@components = components
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @return [Boolean] whether the type is an array
|
|
99
|
+
def array? = !dimensions.empty?
|
|
100
|
+
|
|
101
|
+
# @return [Type] the element type of an array (outermost dimension removed)
|
|
102
|
+
def element = Type.new(base, size, dimensions[0...-1], components)
|
|
103
|
+
|
|
104
|
+
# @return [Integer, nil] the outermost array length, nil for a dynamic array
|
|
105
|
+
def length = dimensions.last
|
|
106
|
+
|
|
107
|
+
# Whether the encoding is dynamic (stored behind an offset).
|
|
108
|
+
#
|
|
109
|
+
# @return [Boolean]
|
|
110
|
+
def dynamic?
|
|
111
|
+
return @dynamic unless @dynamic.nil?
|
|
112
|
+
|
|
113
|
+
@dynamic =
|
|
114
|
+
if array? then length.nil? || element.dynamic?
|
|
115
|
+
elsif base == "tuple" then components.any?(&:dynamic?)
|
|
116
|
+
else base == "string" || (base == "bytes" && size.nil?)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Bytes taken by a static value in the head of its enclosing sequence (32 for dynamic types: the offset).
|
|
121
|
+
#
|
|
122
|
+
# @return [Integer]
|
|
123
|
+
def head_size
|
|
124
|
+
return 32 if dynamic?
|
|
125
|
+
return length * element.head_size if array?
|
|
126
|
+
return components.sum(&:head_size) if base == "tuple"
|
|
127
|
+
|
|
128
|
+
32
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @return [String] the canonical type, e.g. `(uint256,bytes)[]`
|
|
132
|
+
def to_s
|
|
133
|
+
core = base == "tuple" ? "(#{components.join(',')})" : "#{base}#{size}"
|
|
134
|
+
core + dimensions.map { |d| "[#{d}]" }.join
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/lib/block_given/chain.rb
CHANGED
|
@@ -2,10 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
module BlockGiven
|
|
4
4
|
# Static description of an EVM network, similar to viem/chains.
|
|
5
|
+
#
|
|
6
|
+
# A `Struct` with keyword initialisation; the constants in {Chains} are the instances applications use, but
|
|
7
|
+
# any network can be described by building one directly (e.g. a private fork with its own id).
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# chain = BlockGiven::Chains::BASE
|
|
11
|
+
# chain.id # => 8453
|
|
12
|
+
# chain.testnet? # => false
|
|
13
|
+
# chain.explorer_tx_url("0xabc...") # => "https://basescan.org/tx/0xabc..."
|
|
14
|
+
#
|
|
15
|
+
# BlockGiven::Chain.new(id: 1337, name: "Fork", rpc_urls: "http://localhost:8545", testnet: true)
|
|
16
|
+
#
|
|
17
|
+
# @!attribute [rw] id
|
|
18
|
+
# EIP-155 chain id (`8453` for Base), also used in transaction signatures.
|
|
19
|
+
# @return [Integer]
|
|
20
|
+
# @!attribute [rw] name
|
|
21
|
+
# Human readable name (`"Base Sepolia"`).
|
|
22
|
+
# @return [String]
|
|
23
|
+
# @!attribute [rw] network
|
|
24
|
+
# URL-safe slug matched by {Chains.resolve} (`"base-sepolia"`); derived from `name` when not given.
|
|
25
|
+
# @return [String]
|
|
26
|
+
# @!attribute [rw] native_currency
|
|
27
|
+
# Native coin as `{ name:, symbol:, decimals: }` (defaults to `{ name: "Ether", symbol: "ETH", decimals: 18 }`).
|
|
28
|
+
# @return [Hash{Symbol => String, Integer}]
|
|
29
|
+
# @!attribute [rw] rpc_urls
|
|
30
|
+
# Public JSON-RPC endpoints; {Connectors::Http} falls back to the first one when built without a URL.
|
|
31
|
+
# @return [Array<String>]
|
|
32
|
+
# @!attribute [rw] block_explorer_url
|
|
33
|
+
# Base URL of the block explorer without trailing slash (`"https://basescan.org"`), or nil when there is none.
|
|
34
|
+
# @return [String, nil]
|
|
35
|
+
# @!attribute [rw] alchemy_network
|
|
36
|
+
# Alchemy subdomain for this network (`"base-mainnet"`), used by {Connectors::Alchemy} to build its
|
|
37
|
+
# endpoint; nil when Alchemy does not serve the network.
|
|
38
|
+
# @return [String, nil]
|
|
39
|
+
# @!attribute [rw] testnet
|
|
40
|
+
# Whether the network is a test network (default `false`); see {#testnet?}.
|
|
41
|
+
# @return [Boolean]
|
|
5
42
|
Chain = Struct.new(
|
|
6
43
|
:id, :name, :network, :native_currency, :rpc_urls, :block_explorer_url, :alchemy_network, :testnet,
|
|
7
44
|
keyword_init: true
|
|
8
45
|
) do
|
|
46
|
+
# Build a chain description; only `id` and `name` are required.
|
|
47
|
+
#
|
|
48
|
+
# @param id [Integer] EIP-155 chain id
|
|
49
|
+
# @param name [String] human readable name
|
|
50
|
+
# @param network [String, nil] slug used for lookups; defaults to `name` downcased with runs of
|
|
51
|
+
# non-alphanumeric characters replaced by `-`
|
|
52
|
+
# @param native_currency [Hash{Symbol => String, Integer}, nil] `{ name:, symbol:, decimals: }`, defaults to
|
|
53
|
+
# Ether with 18 decimals
|
|
54
|
+
# @param rpc_urls [Array<String>, String] one or more public JSON-RPC endpoints (a single String is wrapped)
|
|
55
|
+
# @param block_explorer_url [String, nil] explorer base URL without trailing slash
|
|
56
|
+
# @param alchemy_network [String, nil] Alchemy subdomain (`"base-mainnet"`)
|
|
57
|
+
# @param testnet [Boolean] whether this is a test network
|
|
58
|
+
# @return [Chain]
|
|
9
59
|
def initialize(id:, name:, network: nil, native_currency: nil, rpc_urls: [], block_explorer_url: nil,
|
|
10
60
|
alchemy_network: nil, testnet: false)
|
|
11
61
|
super(
|
|
@@ -16,85 +66,137 @@ module BlockGiven
|
|
|
16
66
|
)
|
|
17
67
|
end
|
|
18
68
|
|
|
69
|
+
# Whether this is a test network.
|
|
70
|
+
#
|
|
71
|
+
# @return [Boolean] `testnet` coerced to a strict boolean
|
|
19
72
|
def testnet? = !!testnet
|
|
20
73
|
|
|
74
|
+
# Block explorer page of a transaction.
|
|
75
|
+
#
|
|
76
|
+
# @param hash [String] transaction hash as a `0x` hex string
|
|
77
|
+
# @return [String, nil] `"#{block_explorer_url}/tx/#{hash}"`, or nil when the chain has no explorer
|
|
21
78
|
def explorer_tx_url(hash)
|
|
22
79
|
block_explorer_url && "#{block_explorer_url}/tx/#{hash}"
|
|
23
80
|
end
|
|
24
81
|
|
|
82
|
+
# Block explorer page of an address (account or contract).
|
|
83
|
+
#
|
|
84
|
+
# @param address [String] `0x` hex address
|
|
85
|
+
# @return [String, nil] `"#{block_explorer_url}/address/#{address}"`, or nil when the chain has no explorer
|
|
25
86
|
def explorer_address_url(address)
|
|
26
87
|
block_explorer_url && "#{block_explorer_url}/address/#{address}"
|
|
27
88
|
end
|
|
28
89
|
|
|
90
|
+
# Short human readable form.
|
|
91
|
+
#
|
|
92
|
+
# @return [String] `"Base (8453)"`
|
|
29
93
|
def to_s = "#{name} (#{id})"
|
|
94
|
+
|
|
95
|
+
# Compact inspection string, keeping RPC URLs and currency details out of logs.
|
|
96
|
+
#
|
|
97
|
+
# @return [String] `"#<BlockGiven::Chain Base (8453)>"`
|
|
30
98
|
def inspect = "#<BlockGiven::Chain #{self}>"
|
|
31
99
|
end
|
|
32
100
|
|
|
101
|
+
# Catalogue of known networks and the lookup used by {Configuration#chain=} and {Client#initialize}.
|
|
102
|
+
#
|
|
103
|
+
# Each constant is a {Chain}. {Chains.resolve} accepts a chain, a chain id, or a network slug as a Symbol or
|
|
104
|
+
# String (`:base_sepolia`, `"base-sepolia"`), so applications can keep the chain in an environment variable.
|
|
105
|
+
#
|
|
106
|
+
# @example
|
|
107
|
+
# BlockGiven::Chains.resolve(:base_sepolia) # => #<BlockGiven::Chain Base Sepolia (84532)>
|
|
108
|
+
# BlockGiven::Chains.resolve(8453) # => #<BlockGiven::Chain Base (8453)>
|
|
109
|
+
# BlockGiven::Chains[ENV.fetch("CHAIN")] # same as resolve
|
|
33
110
|
module Chains
|
|
111
|
+
# Ethereum mainnet: id 1, explorer https://etherscan.io.
|
|
34
112
|
MAINNET = Chain.new(
|
|
35
113
|
id: 1, name: "Ethereum", network: "mainnet",
|
|
36
114
|
rpc_urls: ["https://eth.merkle.io"], block_explorer_url: "https://etherscan.io",
|
|
37
115
|
alchemy_network: "eth-mainnet"
|
|
38
116
|
)
|
|
117
|
+
# Sepolia, the Ethereum testnet: id 11155111, explorer https://sepolia.etherscan.io.
|
|
39
118
|
SEPOLIA = Chain.new(
|
|
40
119
|
id: 11_155_111, name: "Sepolia", network: "sepolia",
|
|
41
120
|
rpc_urls: ["https://sepolia.drpc.org"], block_explorer_url: "https://sepolia.etherscan.io",
|
|
42
121
|
alchemy_network: "eth-sepolia", testnet: true
|
|
43
122
|
)
|
|
123
|
+
# Base mainnet (Coinbase L2, where Bolero contracts are deployed): id 8453, explorer https://basescan.org.
|
|
44
124
|
BASE = Chain.new(
|
|
45
125
|
id: 8453, name: "Base", network: "base",
|
|
46
126
|
rpc_urls: ["https://mainnet.base.org"], block_explorer_url: "https://basescan.org",
|
|
47
127
|
alchemy_network: "base-mainnet"
|
|
48
128
|
)
|
|
129
|
+
# Base Sepolia testnet: id 84532, explorer https://sepolia.basescan.org.
|
|
49
130
|
BASE_SEPOLIA = Chain.new(
|
|
50
131
|
id: 84_532, name: "Base Sepolia", network: "base-sepolia",
|
|
51
132
|
rpc_urls: ["https://sepolia.base.org"], block_explorer_url: "https://sepolia.basescan.org",
|
|
52
133
|
alchemy_network: "base-sepolia", testnet: true
|
|
53
134
|
)
|
|
135
|
+
# Polygon PoS mainnet (native currency POL): id 137, explorer https://polygonscan.com.
|
|
54
136
|
POLYGON = Chain.new(
|
|
55
137
|
id: 137, name: "Polygon", network: "polygon",
|
|
56
138
|
native_currency: { name: "POL", symbol: "POL", decimals: 18 },
|
|
57
139
|
rpc_urls: ["https://polygon-rpc.com"], block_explorer_url: "https://polygonscan.com",
|
|
58
140
|
alchemy_network: "polygon-mainnet"
|
|
59
141
|
)
|
|
142
|
+
# Polygon Amoy testnet (native currency POL): id 80002, explorer https://amoy.polygonscan.com.
|
|
60
143
|
POLYGON_AMOY = Chain.new(
|
|
61
144
|
id: 80_002, name: "Polygon Amoy", network: "polygon-amoy",
|
|
62
145
|
native_currency: { name: "POL", symbol: "POL", decimals: 18 },
|
|
63
146
|
rpc_urls: ["https://rpc-amoy.polygon.technology"], block_explorer_url: "https://amoy.polygonscan.com",
|
|
64
147
|
alchemy_network: "polygon-amoy", testnet: true
|
|
65
148
|
)
|
|
149
|
+
# Arbitrum One mainnet: id 42161, explorer https://arbiscan.io.
|
|
66
150
|
ARBITRUM = Chain.new(
|
|
67
151
|
id: 42_161, name: "Arbitrum One", network: "arbitrum",
|
|
68
152
|
rpc_urls: ["https://arb1.arbitrum.io/rpc"], block_explorer_url: "https://arbiscan.io",
|
|
69
153
|
alchemy_network: "arb-mainnet"
|
|
70
154
|
)
|
|
155
|
+
# Arbitrum Sepolia testnet: id 421614, explorer https://sepolia.arbiscan.io.
|
|
71
156
|
ARBITRUM_SEPOLIA = Chain.new(
|
|
72
157
|
id: 421_614, name: "Arbitrum Sepolia", network: "arbitrum-sepolia",
|
|
73
158
|
rpc_urls: ["https://sepolia-rollup.arbitrum.io/rpc"], block_explorer_url: "https://sepolia.arbiscan.io",
|
|
74
159
|
alchemy_network: "arb-sepolia", testnet: true
|
|
75
160
|
)
|
|
161
|
+
# OP Mainnet (Optimism): id 10, explorer https://optimistic.etherscan.io.
|
|
76
162
|
OPTIMISM = Chain.new(
|
|
77
163
|
id: 10, name: "OP Mainnet", network: "optimism",
|
|
78
164
|
rpc_urls: ["https://mainnet.optimism.io"], block_explorer_url: "https://optimistic.etherscan.io",
|
|
79
165
|
alchemy_network: "opt-mainnet"
|
|
80
166
|
)
|
|
167
|
+
# OP Sepolia testnet: id 11155420, explorer https://sepolia-optimism.etherscan.io.
|
|
81
168
|
OPTIMISM_SEPOLIA = Chain.new(
|
|
82
169
|
id: 11_155_420, name: "OP Sepolia", network: "optimism-sepolia",
|
|
83
170
|
rpc_urls: ["https://sepolia.optimism.io"], block_explorer_url: "https://sepolia-optimism.etherscan.io",
|
|
84
171
|
alchemy_network: "opt-sepolia", testnet: true
|
|
85
172
|
)
|
|
86
|
-
# Hardhat / Anvil / Ganache default chain,
|
|
173
|
+
# Hardhat / Anvil / Ganache default chain (id 31337, RPC at http://127.0.0.1:8545, no explorer), handy with
|
|
174
|
+
# a local fork.
|
|
87
175
|
LOCALHOST = Chain.new(
|
|
88
176
|
id: 31_337, name: "Localhost", network: "localhost",
|
|
89
177
|
rpc_urls: ["http://127.0.0.1:8545"], testnet: true
|
|
90
178
|
)
|
|
91
179
|
|
|
180
|
+
# Every chain known to the gem, in declaration order; {.resolve} and {.find_by_id} search this list.
|
|
92
181
|
ALL = [MAINNET, SEPOLIA, BASE, BASE_SEPOLIA, POLYGON, POLYGON_AMOY, ARBITRUM, ARBITRUM_SEPOLIA,
|
|
93
182
|
OPTIMISM, OPTIMISM_SEPOLIA, LOCALHOST].freeze
|
|
94
183
|
|
|
95
184
|
module_function
|
|
96
185
|
|
|
97
|
-
#
|
|
186
|
+
# Resolve a chain from a {Chain}, a chain id, or a network slug given as Symbol or String.
|
|
187
|
+
#
|
|
188
|
+
# Slugs are compared case-insensitively against {Chain#network} after mapping underscores to dashes, so
|
|
189
|
+
# `:base_sepolia`, `"BASE_SEPOLIA"` and `"base-sepolia"` all resolve to {BASE_SEPOLIA}. A {Chain} is
|
|
190
|
+
# returned as-is, which lets custom chains flow through the same code path.
|
|
191
|
+
#
|
|
192
|
+
# @param value [BlockGiven::Chain, Integer, Symbol, String]
|
|
193
|
+
# @return [BlockGiven::Chain]
|
|
194
|
+
# @raise [BlockGiven::ConfigurationError] when the id or slug is unknown, or the value has another type
|
|
195
|
+
# @example
|
|
196
|
+
# BlockGiven::Chains.resolve(:base) # => #<BlockGiven::Chain Base (8453)>
|
|
197
|
+
# BlockGiven::Chains.resolve("base-sepolia") # => #<BlockGiven::Chain Base Sepolia (84532)>
|
|
198
|
+
# BlockGiven::Chains.resolve(8453) # => #<BlockGiven::Chain Base (8453)>
|
|
199
|
+
# BlockGiven::Chains.resolve(:mumbai) # raises BlockGiven::ConfigurationError
|
|
98
200
|
def resolve(value)
|
|
99
201
|
case value
|
|
100
202
|
when Chain then value
|
|
@@ -106,7 +208,17 @@ module BlockGiven
|
|
|
106
208
|
end
|
|
107
209
|
end
|
|
108
210
|
|
|
211
|
+
# Look a known chain up by its chain id.
|
|
212
|
+
#
|
|
213
|
+
# @param id [Integer] EIP-155 chain id
|
|
214
|
+
# @return [BlockGiven::Chain, nil] nil when no chain in {ALL} has this id
|
|
109
215
|
def find_by_id(id) = ALL.find { |c| c.id == id }
|
|
216
|
+
|
|
217
|
+
# Shorthand for {.resolve}, so `Chains[:base]` reads like a lookup table.
|
|
218
|
+
#
|
|
219
|
+
# @param value [BlockGiven::Chain, Integer, Symbol, String]
|
|
220
|
+
# @return [BlockGiven::Chain]
|
|
221
|
+
# @raise [BlockGiven::ConfigurationError] when the value cannot be resolved
|
|
110
222
|
def [](value) = resolve(value)
|
|
111
223
|
end
|
|
112
224
|
end
|