viem_rb 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 +28 -0
- data/LICENSE +21 -0
- data/README.md +365 -0
- data/lib/viem/abi/decoder.rb +28 -0
- data/lib/viem/abi/encoder.rb +48 -0
- data/lib/viem/abi/parse.rb +59 -0
- data/lib/viem/accounts/mnemonic_account.rb +34 -0
- data/lib/viem/accounts/private_key_account.rb +51 -0
- data/lib/viem/actions/public/call.rb +35 -0
- data/lib/viem/actions/public/ens.rb +85 -0
- data/lib/viem/actions/public/get_balance.rb +15 -0
- data/lib/viem/actions/public/get_block.rb +47 -0
- data/lib/viem/actions/public/get_code.rb +21 -0
- data/lib/viem/actions/public/get_gas.rb +33 -0
- data/lib/viem/actions/public/get_logs.rb +32 -0
- data/lib/viem/actions/public/get_transaction.rb +72 -0
- data/lib/viem/actions/public/read_contract.rb +41 -0
- data/lib/viem/actions/wallet/send_transaction.rb +92 -0
- data/lib/viem/actions/wallet/sign_message.rb +16 -0
- data/lib/viem/actions/wallet/sign_typed_data.rb +16 -0
- data/lib/viem/actions/wallet/write_contract.rb +48 -0
- data/lib/viem/chains/base.rb +21 -0
- data/lib/viem/chains/definitions.rb +185 -0
- data/lib/viem/clients/public_client.rb +39 -0
- data/lib/viem/clients/test_client.rb +55 -0
- data/lib/viem/clients/wallet_client.rb +25 -0
- data/lib/viem/errors.rb +63 -0
- data/lib/viem/transports/base.rb +26 -0
- data/lib/viem/transports/fallback.rb +24 -0
- data/lib/viem/transports/http.rb +45 -0
- data/lib/viem/transports/web_socket.rb +64 -0
- data/lib/viem/utils/address.rb +31 -0
- data/lib/viem/utils/hash.rb +27 -0
- data/lib/viem/utils/hex.rb +66 -0
- data/lib/viem/utils/units.rb +37 -0
- data/lib/viem/version.rb +5 -0
- data/lib/viem_rb.rb +71 -0
- metadata +166 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module Ens
|
|
7
|
+
ENS_REGISTRY = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
|
8
|
+
|
|
9
|
+
ENS_PUBLIC_RESOLVER_ABI = [
|
|
10
|
+
{
|
|
11
|
+
"name" => "addr",
|
|
12
|
+
"type" => "function",
|
|
13
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
14
|
+
"outputs" => [{ "type" => "address", "name" => "" }],
|
|
15
|
+
"stateMutability" => "view"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name" => "name",
|
|
19
|
+
"type" => "function",
|
|
20
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
21
|
+
"outputs" => [{ "type" => "string", "name" => "" }],
|
|
22
|
+
"stateMutability" => "view"
|
|
23
|
+
}
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
REGISTRY_ABI = [
|
|
27
|
+
{
|
|
28
|
+
"name" => "resolver",
|
|
29
|
+
"type" => "function",
|
|
30
|
+
"inputs" => [{ "type" => "bytes32", "name" => "node" }],
|
|
31
|
+
"outputs" => [{ "type" => "address", "name" => "" }],
|
|
32
|
+
"stateMutability" => "view"
|
|
33
|
+
}
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
def get_ens_address(name:)
|
|
37
|
+
node = namehash(name)
|
|
38
|
+
resolver_addr = read_contract(
|
|
39
|
+
address: ENS_REGISTRY, abi: REGISTRY_ABI,
|
|
40
|
+
function_name: "resolver", args: [node]
|
|
41
|
+
)
|
|
42
|
+
return nil if Utils::Address.is_zero_address?(resolver_addr)
|
|
43
|
+
|
|
44
|
+
read_contract(
|
|
45
|
+
address: resolver_addr, abi: ENS_PUBLIC_RESOLVER_ABI,
|
|
46
|
+
function_name: "addr", args: [node]
|
|
47
|
+
)
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
raise Error, "ENS resolution failed for #{name}: #{e.message}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_ens_name(address:)
|
|
53
|
+
address = Utils::Address.get_address(address)
|
|
54
|
+
reversed = "#{address.downcase.delete_prefix("0x")}.addr.reverse"
|
|
55
|
+
node = namehash(reversed)
|
|
56
|
+
resolver_addr = read_contract(
|
|
57
|
+
address: ENS_REGISTRY, abi: REGISTRY_ABI,
|
|
58
|
+
function_name: "resolver", args: [node]
|
|
59
|
+
)
|
|
60
|
+
return nil if Utils::Address.is_zero_address?(resolver_addr)
|
|
61
|
+
|
|
62
|
+
read_contract(
|
|
63
|
+
address: resolver_addr, abi: ENS_PUBLIC_RESOLVER_ABI,
|
|
64
|
+
function_name: "name", args: [node]
|
|
65
|
+
)
|
|
66
|
+
rescue StandardError
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def namehash(name)
|
|
73
|
+
node = "\x00" * 32
|
|
74
|
+
return Eth::Util.keccak256(node) if name.empty?
|
|
75
|
+
|
|
76
|
+
name.split(".").reverse.each do |label|
|
|
77
|
+
label_hash = Eth::Util.keccak256(label)
|
|
78
|
+
node = Eth::Util.keccak256(node + label_hash)
|
|
79
|
+
end
|
|
80
|
+
node
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetBalance
|
|
7
|
+
def get_balance(address:, block_tag: "latest")
|
|
8
|
+
address = Utils::Address.get_address(address)
|
|
9
|
+
result = @transport.request("eth_getBalance", [address, block_tag.to_s])
|
|
10
|
+
Utils::Hex.hex_to_number(result)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetBlock
|
|
7
|
+
def get_block(block_number: nil, block_hash: nil, block_tag: "latest", include_transactions: false)
|
|
8
|
+
if block_hash
|
|
9
|
+
result = @transport.request("eth_getBlockByHash", [block_hash, include_transactions])
|
|
10
|
+
else
|
|
11
|
+
tag = block_number ? Utils::Hex.number_to_hex(block_number) : block_tag.to_s
|
|
12
|
+
result = @transport.request("eth_getBlockByNumber", [tag, include_transactions])
|
|
13
|
+
end
|
|
14
|
+
raise BlockNotFoundError, "Block not found" unless result
|
|
15
|
+
|
|
16
|
+
format_block(result)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_block_number
|
|
20
|
+
result = @transport.request("eth_blockNumber", [])
|
|
21
|
+
Utils::Hex.hex_to_number(result)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# Fields that should remain as hex strings (hashes, addresses)
|
|
27
|
+
BLOCK_STRING_FIELDS = %w[
|
|
28
|
+
hash parentHash sha3Uncles miner stateRoot transactionsRoot
|
|
29
|
+
receiptsRoot logsBloom mixHash nonce extraData
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
def format_block(raw)
|
|
33
|
+
raw.each_with_object({}) do |(k, v), h|
|
|
34
|
+
key = k.to_sym
|
|
35
|
+
if BLOCK_STRING_FIELDS.include?(k.to_s)
|
|
36
|
+
h[key] = v
|
|
37
|
+
elsif v.is_a?(String) && v.start_with?("0x") && v.match?(/\A0x[0-9a-f]+\z/)
|
|
38
|
+
h[key] = Utils::Hex.hex_to_number(v)
|
|
39
|
+
else
|
|
40
|
+
h[key] = v
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetCode
|
|
7
|
+
def get_code(address:, block_tag: "latest")
|
|
8
|
+
address = Utils::Address.get_address(address)
|
|
9
|
+
result = @transport.request("eth_getCode", [address, block_tag.to_s])
|
|
10
|
+
result == "0x" ? nil : result
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_storage_at(address:, slot:, block_tag: "latest")
|
|
14
|
+
address = Utils::Address.get_address(address)
|
|
15
|
+
slot = slot.is_a?(Integer) ? Utils::Hex.number_to_hex(slot, size: 32) : slot
|
|
16
|
+
@transport.request("eth_getStorageAt", [address, slot, block_tag.to_s])
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetGas
|
|
7
|
+
def get_gas_price
|
|
8
|
+
result = @transport.request("eth_gasPrice", [])
|
|
9
|
+
Utils::Hex.hex_to_number(result)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def get_fee_history(block_count:, newest_block: "latest", reward_percentiles: [])
|
|
13
|
+
result = @transport.request("eth_feeHistory", [
|
|
14
|
+
Utils::Hex.number_to_hex(block_count),
|
|
15
|
+
newest_block.to_s,
|
|
16
|
+
reward_percentiles
|
|
17
|
+
])
|
|
18
|
+
result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get_max_priority_fee_per_gas
|
|
22
|
+
result = @transport.request("eth_maxPriorityFeePerGas", [])
|
|
23
|
+
Utils::Hex.hex_to_number(result)
|
|
24
|
+
rescue RpcError
|
|
25
|
+
# Fallback: estimate from fee history
|
|
26
|
+
history = get_fee_history(block_count: 4, newest_block: "latest", reward_percentiles: [50])
|
|
27
|
+
rewards = (history["reward"] || []).flatten.map { |r| Utils::Hex.hex_to_number(r) }
|
|
28
|
+
rewards.sum / [rewards.size, 1].max
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetLogs
|
|
7
|
+
def get_logs(address: nil, event: nil, args: {}, from_block: nil, to_block: nil)
|
|
8
|
+
params = {}
|
|
9
|
+
params[:address] = address if address
|
|
10
|
+
params[:fromBlock] = from_block.is_a?(Integer) ? Utils::Hex.number_to_hex(from_block) : (from_block || "earliest")
|
|
11
|
+
params[:toBlock] = to_block.is_a?(Integer) ? Utils::Hex.number_to_hex(to_block) : (to_block || "latest")
|
|
12
|
+
params[:topics] = encode_event_topics(event, args) if event
|
|
13
|
+
results = @transport.request("eth_getLogs", [stringify_keys(params)])
|
|
14
|
+
results.map { |l| format_log(l) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def encode_event_topics(event_abi, args)
|
|
20
|
+
sig = event_signature(event_abi)
|
|
21
|
+
topic0 = Utils::Hash.keccak256(sig)
|
|
22
|
+
[topic0]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def event_signature(abi_item)
|
|
26
|
+
inputs = (abi_item["inputs"] || []).map { |i| i["type"] }.join(",")
|
|
27
|
+
"#{abi_item["name"]}(#{inputs})"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module GetTransaction
|
|
7
|
+
def get_transaction(hash:)
|
|
8
|
+
result = @transport.request("eth_getTransactionByHash", [hash])
|
|
9
|
+
raise TransactionNotFoundError, "Transaction #{hash} not found" unless result
|
|
10
|
+
|
|
11
|
+
format_transaction(result)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_transaction_receipt(hash:)
|
|
15
|
+
result = @transport.request("eth_getTransactionReceipt", [hash])
|
|
16
|
+
raise TransactionReceiptNotFoundError, "Receipt for #{hash} not found" unless result
|
|
17
|
+
|
|
18
|
+
format_receipt(result)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get_transaction_count(address:, block_tag: "latest")
|
|
22
|
+
address = Utils::Address.get_address(address)
|
|
23
|
+
result = @transport.request("eth_getTransactionCount", [address, block_tag.to_s])
|
|
24
|
+
Utils::Hex.hex_to_number(result)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def wait_for_transaction_receipt(hash:, poll_interval: 4, timeout: 120)
|
|
28
|
+
deadline = Time.now + timeout
|
|
29
|
+
loop do
|
|
30
|
+
receipt = @transport.request("eth_getTransactionReceipt", [hash])
|
|
31
|
+
return format_receipt(receipt) if receipt
|
|
32
|
+
raise WaitForTransactionReceiptTimeoutError, "Timeout after #{timeout}s waiting for #{hash}" if Time.now > deadline
|
|
33
|
+
|
|
34
|
+
sleep poll_interval
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def format_transaction(raw)
|
|
41
|
+
raw.transform_keys(&:to_sym).tap do |tx|
|
|
42
|
+
tx[:block_number] = Utils::Hex.hex_to_number(tx[:blockNumber]) if tx[:blockNumber]
|
|
43
|
+
tx[:transaction_index] = Utils::Hex.hex_to_number(tx[:transactionIndex]) if tx[:transactionIndex]
|
|
44
|
+
tx[:nonce] = Utils::Hex.hex_to_number(tx[:nonce]) if tx[:nonce]
|
|
45
|
+
tx[:gas] = Utils::Hex.hex_to_number(tx[:gas]) if tx[:gas]
|
|
46
|
+
tx[:gas_price] = Utils::Hex.hex_to_number(tx[:gasPrice]) if tx[:gasPrice]
|
|
47
|
+
tx[:value] = Utils::Hex.hex_to_number(tx[:value]) if tx[:value]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def format_receipt(raw)
|
|
52
|
+
raw.transform_keys(&:to_sym).tap do |r|
|
|
53
|
+
r[:block_number] = Utils::Hex.hex_to_number(r[:blockNumber]) if r[:blockNumber]
|
|
54
|
+
r[:transaction_index] = Utils::Hex.hex_to_number(r[:transactionIndex]) if r[:transactionIndex]
|
|
55
|
+
r[:gas_used] = Utils::Hex.hex_to_number(r[:gasUsed]) if r[:gasUsed]
|
|
56
|
+
r[:cumulative_gas_used] = Utils::Hex.hex_to_number(r[:cumulativeGasUsed]) if r[:cumulativeGasUsed]
|
|
57
|
+
r[:status] = Utils::Hex.hex_to_number(r[:status]) if r[:status]
|
|
58
|
+
r[:logs] = (r[:logs] || []).map { |l| format_log(l) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def format_log(raw)
|
|
63
|
+
raw.transform_keys(&:to_sym).tap do |l|
|
|
64
|
+
l[:block_number] = Utils::Hex.hex_to_number(l[:blockNumber]) if l[:blockNumber]
|
|
65
|
+
l[:transaction_index] = Utils::Hex.hex_to_number(l[:transactionIndex]) if l[:transactionIndex]
|
|
66
|
+
l[:log_index] = Utils::Hex.hex_to_number(l[:logIndex]) if l[:logIndex]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Public
|
|
6
|
+
module ReadContract
|
|
7
|
+
def read_contract(address:, abi:, function_name:, args: [], block_tag: "latest")
|
|
8
|
+
abi_item = find_abi_item(abi, function_name, type: "function")
|
|
9
|
+
data = Abi::Encoder.encode_function_data(abi_item, args: args)
|
|
10
|
+
result = call(to: address, data: data, block_tag: block_tag)
|
|
11
|
+
Abi::Decoder.decode_function_result(abi_item, result)
|
|
12
|
+
rescue RpcError => e
|
|
13
|
+
raise ContractFunctionExecutionError.new(e,
|
|
14
|
+
contract_address: address, function_name: function_name, args: args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def simulate_contract(address:, abi:, function_name:, args: [], account: nil, value: nil, block_tag: "latest")
|
|
18
|
+
abi_item = find_abi_item(abi, function_name, type: "function")
|
|
19
|
+
data = Abi::Encoder.encode_function_data(abi_item, args: args)
|
|
20
|
+
from = account&.address
|
|
21
|
+
result = call(to: address, data: data, from: from, value: value, block_tag: block_tag)
|
|
22
|
+
Abi::Decoder.decode_function_result(abi_item, result)
|
|
23
|
+
rescue RpcError => e
|
|
24
|
+
raise ContractFunctionExecutionError.new(e,
|
|
25
|
+
contract_address: address, function_name: function_name, args: args)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def find_abi_item(abi, name, type: nil)
|
|
31
|
+
item = abi.find do |i|
|
|
32
|
+
i["name"] == name.to_s && (type.nil? || i["type"] == type)
|
|
33
|
+
end
|
|
34
|
+
raise AbiEncodingError, "Function '#{name}' not found in ABI" unless item
|
|
35
|
+
|
|
36
|
+
item
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Wallet
|
|
6
|
+
module SendTransaction
|
|
7
|
+
def send_transaction(
|
|
8
|
+
to:,
|
|
9
|
+
value: 0,
|
|
10
|
+
data: nil,
|
|
11
|
+
gas: nil,
|
|
12
|
+
gas_price: nil,
|
|
13
|
+
max_fee_per_gas: nil,
|
|
14
|
+
max_priority_fee_per_gas: nil,
|
|
15
|
+
nonce: nil,
|
|
16
|
+
account: nil
|
|
17
|
+
)
|
|
18
|
+
acct = account || @account
|
|
19
|
+
raise AccountRequiredError unless acct
|
|
20
|
+
|
|
21
|
+
nonce ||= get_transaction_count(address: acct.address)
|
|
22
|
+
chain_id = @chain&.id || get_chain_id
|
|
23
|
+
|
|
24
|
+
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
|
+
tx = build_eip1559_tx(
|
|
31
|
+
to: to, value: value, data: data, gas: gas,
|
|
32
|
+
max_fee_per_gas: max_fee_per_gas,
|
|
33
|
+
max_priority_fee_per_gas: max_priority_fee_per_gas,
|
|
34
|
+
nonce: nonce, chain_id: chain_id
|
|
35
|
+
)
|
|
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
|
+
gas_price ||= get_gas_price
|
|
43
|
+
tx = build_legacy_tx(
|
|
44
|
+
to: to, value: value, data: data, gas: gas,
|
|
45
|
+
gas_price: gas_price, nonce: nonce, chain_id: chain_id
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
signed = sign_tx(acct, tx)
|
|
50
|
+
@transport.request("eth_sendRawTransaction", [signed])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def get_chain_id
|
|
56
|
+
result = @transport.request("eth_chainId", [])
|
|
57
|
+
Utils::Hex.hex_to_number(result)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_eip1559_tx(to:, value:, data:, gas:, max_fee_per_gas:, max_priority_fee_per_gas:, nonce:, chain_id:)
|
|
61
|
+
Eth::Tx::Eip1559.new({
|
|
62
|
+
chain_id: chain_id,
|
|
63
|
+
nonce: nonce,
|
|
64
|
+
max_priority_fee_per_gas: max_priority_fee_per_gas || Utils::Units.parse_gwei("1.5"),
|
|
65
|
+
max_fee_per_gas: max_fee_per_gas,
|
|
66
|
+
gas_limit: gas,
|
|
67
|
+
to: to,
|
|
68
|
+
value: value,
|
|
69
|
+
data: data || ""
|
|
70
|
+
})
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def build_legacy_tx(to:, value:, data:, gas:, gas_price:, nonce:, chain_id:)
|
|
74
|
+
Eth::Tx::Legacy.new({
|
|
75
|
+
chain_id: chain_id,
|
|
76
|
+
nonce: nonce,
|
|
77
|
+
gas_price: gas_price,
|
|
78
|
+
gas_limit: gas,
|
|
79
|
+
to: to,
|
|
80
|
+
value: value,
|
|
81
|
+
data: data || ""
|
|
82
|
+
})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def sign_tx(account, tx)
|
|
86
|
+
tx.sign(Eth::Key.new(priv: account.private_key.delete_prefix("0x")))
|
|
87
|
+
"0x#{tx.hex}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Wallet
|
|
6
|
+
module SignMessage
|
|
7
|
+
def sign_message(message:, account: nil)
|
|
8
|
+
acct = account || @account
|
|
9
|
+
raise AccountRequiredError unless acct
|
|
10
|
+
|
|
11
|
+
acct.sign_message(message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Wallet
|
|
6
|
+
module SignTypedData
|
|
7
|
+
def sign_typed_data(domain:, types:, primary_type:, message:, account: nil)
|
|
8
|
+
acct = account || @account
|
|
9
|
+
raise AccountRequiredError unless acct
|
|
10
|
+
|
|
11
|
+
acct.sign_typed_data(domain, types, message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Actions
|
|
5
|
+
module Wallet
|
|
6
|
+
module WriteContract
|
|
7
|
+
def write_contract(
|
|
8
|
+
address:,
|
|
9
|
+
abi:,
|
|
10
|
+
function_name:,
|
|
11
|
+
args: [],
|
|
12
|
+
value: nil,
|
|
13
|
+
gas: nil,
|
|
14
|
+
gas_price: nil,
|
|
15
|
+
max_fee_per_gas: nil,
|
|
16
|
+
max_priority_fee_per_gas: nil,
|
|
17
|
+
account: nil
|
|
18
|
+
)
|
|
19
|
+
acct = account || @account
|
|
20
|
+
raise AccountRequiredError unless acct
|
|
21
|
+
|
|
22
|
+
abi_item = find_abi_item(abi, function_name, type: "function")
|
|
23
|
+
data = Abi::Encoder.encode_function_data(abi_item, args: args)
|
|
24
|
+
|
|
25
|
+
send_transaction(
|
|
26
|
+
to: address, data: data, value: value || 0,
|
|
27
|
+
gas: gas, gas_price: gas_price,
|
|
28
|
+
max_fee_per_gas: max_fee_per_gas,
|
|
29
|
+
max_priority_fee_per_gas: max_priority_fee_per_gas,
|
|
30
|
+
account: acct
|
|
31
|
+
)
|
|
32
|
+
rescue RpcError => e
|
|
33
|
+
raise ContractFunctionExecutionError.new(e,
|
|
34
|
+
contract_address: address, function_name: function_name, args: args)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def deploy_contract(abi:, bytecode:, args: [], value: nil, gas: nil, account: nil)
|
|
38
|
+
acct = account || @account
|
|
39
|
+
raise AccountRequiredError unless acct
|
|
40
|
+
|
|
41
|
+
constructor = abi.find { |i| i["type"] == "constructor" }
|
|
42
|
+
data = Abi::Encoder.encode_deploy_data(bytecode, constructor, args: args)
|
|
43
|
+
send_transaction(to: nil, data: data, value: value || 0, gas: gas, account: acct)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Viem
|
|
4
|
+
module Chains
|
|
5
|
+
Chain = Struct.new(
|
|
6
|
+
:id, :name, :network, :native_currency,
|
|
7
|
+
:rpc_urls, :block_explorers, :testnet,
|
|
8
|
+
keyword_init: true
|
|
9
|
+
) do
|
|
10
|
+
def rpc_url
|
|
11
|
+
rpc_urls.dig(:default, :http, 0)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def testnet?
|
|
15
|
+
!!testnet
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
NativeCurrency = Struct.new(:name, :symbol, :decimals, keyword_init: true)
|
|
20
|
+
end
|
|
21
|
+
end
|