near 0.2.0 → 0.3.1
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/CHANGES.md +21 -8
- data/README.md +87 -6
- data/VERSION +1 -1
- data/lib/near/account.rb +10 -0
- data/lib/near/action.rb +141 -0
- data/lib/near/balance.rb +5 -1
- data/lib/near/block.rb +156 -4
- data/lib/near/cli/account.rb +21 -10
- data/lib/near/error.rb +18 -0
- data/lib/near/network.rb +99 -8
- data/lib/near/transaction.rb +113 -1
- data/lib/near.rb +2 -0
- metadata +75 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d113e40c4ca1414cd8bff319acdaeba7d7cd1a07ebc866ce875afd2080e44b1
|
4
|
+
data.tar.gz: ae7151a4a1022ecf3b9d652ae11bcabfffc427ccaad0cc07858fd662fb91cbd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 779325138cf044d38991e1911d8bf115f8559d86a530bda11e0b97a75367406386b63e81a7e9a3d9db69b70ab826ce146e0b326efdb9e69dcf3545083227c1bb
|
7
|
+
data.tar.gz: f5b55100bc19a9c8d3e6c165e66d7780b94a60ed1fea90bce0523cf77657b384b13c1439c8aabbc4db2b6d2ae5534ef186ad9d7ed82dd2bb3e85ee915f5e1fb0
|
data/CHANGES.md
CHANGED
@@ -5,21 +5,34 @@ 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.3.1 - 2025-02-05
|
9
|
+
|
10
|
+
## 0.3.0 - 2025-02-02
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- Implement block data accessors
|
14
|
+
- Implement transaction data accessors
|
15
|
+
- Implement action parsing
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
- Adopt Faraday as the HTTP client
|
19
|
+
- Improve HTTP error handling and retries
|
20
|
+
|
8
21
|
## 0.2.0 - 2025-01-29
|
9
22
|
|
10
23
|
### Added
|
11
|
-
- `Network#fetch(block)
|
12
|
-
- `NEAR.testnet`, `NEAR.mainnet
|
24
|
+
- `Network#fetch(block)`
|
25
|
+
- `NEAR.testnet`, `NEAR.mainnet`
|
13
26
|
|
14
27
|
### Changed
|
15
|
-
- Use the NEAR_CLI environmnet variable, if set
|
16
|
-
- Use a temporary file for `call_function
|
17
|
-
- Enhance `call_function()
|
18
|
-
- Enhance `create_account_with_funding()
|
19
|
-
- Enhance `delete_account()
|
28
|
+
- Use the NEAR_CLI environmnet variable, if set
|
29
|
+
- Use a temporary file for `call_function`
|
30
|
+
- Enhance `call_function()`
|
31
|
+
- Enhance `create_account_with_funding()`
|
32
|
+
- Enhance `delete_account()`
|
20
33
|
|
21
34
|
### Fixed
|
22
|
-
- Fix the formatting of balances
|
35
|
+
- Fix the formatting of balances
|
23
36
|
|
24
37
|
## 0.1.0 - 2025-01-19
|
25
38
|
|
data/README.md
CHANGED
@@ -6,10 +6,13 @@
|
|
6
6
|
[](https://rubydoc.info/gems/near)
|
7
7
|
|
8
8
|
**NEAR.rb** is a [Ruby] client library for the [NEAR Protocol].
|
9
|
-
It
|
9
|
+
It provides a [neardata.xyz] API client for block data as well as wraps the
|
10
|
+
[NEAR Command-Line Interface] (CLI) in a high-productivity Ruby interface.
|
10
11
|
|
11
12
|
## ✨ Features
|
12
13
|
|
14
|
+
- Fetches block data from the [neardata.xyz] API.
|
15
|
+
- Supports parsing of block, chunk, transaction, and action data.
|
13
16
|
- Wraps the complete CLI features in an idiomatic Ruby interface.
|
14
17
|
- Provides comprehensive account management operations.
|
15
18
|
- Supports token operations for NEAR and other assets.
|
@@ -17,14 +20,13 @@ It wraps the [NEAR command-line interface] (CLI) into a Ruby interface.
|
|
17
20
|
- Handles transaction construction, signing, and monitoring.
|
18
21
|
- Integrates with hardware wallets and secure key storage.
|
19
22
|
- Implements type-safe balance operations and input validation.
|
20
|
-
- Fetches block data from the [neardata.xyz] API.
|
21
23
|
- Supports both the [mainnet] and [testnet] environments.
|
22
24
|
- Offers cross-platform support with zero library dependencies.
|
23
25
|
- 100% free and unencumbered public domain software.
|
24
26
|
|
25
27
|
## 🛠️ Prerequisites
|
26
28
|
|
27
|
-
- [NEAR CLI] 0.
|
29
|
+
- [NEAR CLI] 0.18+
|
28
30
|
- [Ruby] 3.0+
|
29
31
|
|
30
32
|
## ⬇️ Installation
|
@@ -37,6 +39,23 @@ gem install near
|
|
37
39
|
|
38
40
|
## 👉 Examples
|
39
41
|
|
42
|
+
- [Importing the library](#importing-the-library)
|
43
|
+
- [Fetching block information](#fetching-block-information)
|
44
|
+
- [Tracking block production](#tracking-block-production)
|
45
|
+
- [Tracking chain transactions](#tracking-chain-transactions)
|
46
|
+
- [Tracking chain actions](#tracking-chain-actions)
|
47
|
+
- [Tracking contract interactions](#tracking-contract-interactions)
|
48
|
+
- [Instantiating the CLI wrapper](#instantiating-the-cli-wrapper)
|
49
|
+
- [Viewing account details](#viewing-account-details)
|
50
|
+
- [Importing accounts](#importing-accounts)
|
51
|
+
- [Creating accounts](#creating-accounts)
|
52
|
+
- [Deleting accounts](#deleting-accounts)
|
53
|
+
- [Managing access keys](#managing-access-keys)
|
54
|
+
- [Managing tokens](#managing-tokens)
|
55
|
+
- [Calling read-only contract methods](#calling-read-only-contract-methods)
|
56
|
+
- [Calling mutative contract methods](#calling-mutative-contract-methods)
|
57
|
+
- [Deploying a smart contract](#deploying-a-smart-contract)
|
58
|
+
|
40
59
|
### Importing the library
|
41
60
|
|
42
61
|
```ruby
|
@@ -53,6 +72,68 @@ global edge locations:
|
|
53
72
|
block = NEAR.testnet.fetch(186_132_854)
|
54
73
|
```
|
55
74
|
|
75
|
+
### Tracking block production
|
76
|
+
|
77
|
+
Thanks to the built-in [neardata.xyz] API client, it is trivial to
|
78
|
+
track the current tip of the chain in a robust and efficient manner:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
NEAR.testnet.fetch_blocks do |block|
|
82
|
+
puts [block.height, block.hash].join("\t")
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
### Tracking chain transactions
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
NEAR.testnet.fetch_blocks do |block|
|
90
|
+
puts block.inspect
|
91
|
+
|
92
|
+
block.each_transaction do |transaction|
|
93
|
+
puts "\t" + transaction.inspect
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
For a more elaborated example, see
|
99
|
+
[`examples/monitor_all_transactions.rb`](examples/monitor_all_transactions.rb):
|
100
|
+
|
101
|
+
[](examples/monitor_all_transactions.gif)
|
102
|
+
|
103
|
+
### Tracking chain actions
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
NEAR.testnet.fetch_blocks do |block|
|
107
|
+
puts block.inspect
|
108
|
+
|
109
|
+
block.each_action do |action|
|
110
|
+
puts "\t" + action.inspect
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
For a more elaborated example, see
|
116
|
+
[`examples/monitor_all_actions.rb`](examples/monitor_all_actions.rb):
|
117
|
+
|
118
|
+
[](examples/monitor_all_actions.gif)
|
119
|
+
|
120
|
+
### Tracking contract interactions
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
NEAR.testnet.fetch_blocks do |block|
|
124
|
+
puts block.inspect
|
125
|
+
|
126
|
+
block.find_actions(:FunctionCall, receiver: 'aurora', method_name: /^submit/) do |action|
|
127
|
+
puts "\t" + action.inspect
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
For a more elaborated example, see
|
133
|
+
[`examples/index_evm_transactions.rb`](examples/index_evm_transactions.rb):
|
134
|
+
|
135
|
+
[](examples/index_evm_transactions.gif)
|
136
|
+
|
56
137
|
### Instantiating the CLI wrapper
|
57
138
|
|
58
139
|
```ruby
|
@@ -106,7 +187,7 @@ result = testnet.import_account_with_private_key(
|
|
106
187
|
# Create an account funded from a faucet (testnet only):
|
107
188
|
result = testnet.create_account_with_faucet(
|
108
189
|
'mynewaccount.testnet',
|
109
|
-
'ed25519:HVPgAsZkZ7cwLZDqK313XJsDyqAvgBxrATcD7VacA8KE'
|
190
|
+
public_key: 'ed25519:HVPgAsZkZ7cwLZDqK313XJsDyqAvgBxrATcD7VacA8KE'
|
110
191
|
)
|
111
192
|
|
112
193
|
# Create an account funded by another account:
|
@@ -126,8 +207,8 @@ result = testnet.create_implicit_account('/path/to/credentials/folder')
|
|
126
207
|
```ruby
|
127
208
|
# Delete an existing account:
|
128
209
|
result = testnet.delete_account(
|
129
|
-
'
|
130
|
-
'
|
210
|
+
'my-obsolete-account.testnet', # account to delete
|
211
|
+
beneficiary: 'v2.faucet.nonofficial.testnet' # account receiving remaining balance
|
131
212
|
)
|
132
213
|
```
|
133
214
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
data/lib/near/account.rb
CHANGED
@@ -5,10 +5,20 @@
|
|
5
5
|
#
|
6
6
|
# @see https://nomicon.io/DataStructures/Account
|
7
7
|
class NEAR::Account
|
8
|
+
##
|
9
|
+
# @param [String, #to_s] id
|
10
|
+
# @return [NEAR::Account]
|
8
11
|
def self.parse(id)
|
9
12
|
self.new(id.to_s)
|
10
13
|
end
|
11
14
|
|
15
|
+
##
|
16
|
+
# @return [NEAR::Account]
|
17
|
+
def self.temp
|
18
|
+
timestamp = (Time.now.to_f * 1_000).to_i
|
19
|
+
self.new("temp-#{timestamp}.testnet")
|
20
|
+
end
|
21
|
+
|
12
22
|
##
|
13
23
|
# @param [String, #to_s] id
|
14
24
|
def initialize(id)
|
data/lib/near/action.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Represents a NEAR action.
|
8
|
+
#
|
9
|
+
# @see https://nomicon.io/RuntimeSpec/Actions
|
10
|
+
class NEAR::Action
|
11
|
+
class CreateAccount < NEAR::Action; end
|
12
|
+
|
13
|
+
class DeployContract < NEAR::Action; end
|
14
|
+
|
15
|
+
class FunctionCall < NEAR::Action
|
16
|
+
##
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :method_name
|
19
|
+
def method_name
|
20
|
+
@data['method_name']
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @return [String]
|
25
|
+
def json_args
|
26
|
+
@json_args ||= JSON.parse(self.args)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# @param [Symbol, String, #to_s] key
|
31
|
+
# @return [Object]
|
32
|
+
def [](key)
|
33
|
+
self.json_args[key.to_s] rescue nil
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# @return [String]
|
38
|
+
attr_reader :args
|
39
|
+
def args
|
40
|
+
@args ||= Base64.decode64(@data['args'])
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return [Integer]
|
45
|
+
attr_reader :gas
|
46
|
+
def gas
|
47
|
+
@data['gas']
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @return [NEAR::Balance]
|
52
|
+
attr_reader :deposit
|
53
|
+
def deposit
|
54
|
+
@deposit ||= NEAR::Balance.parse(@data['deposit'])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Transfer < NEAR::Action
|
59
|
+
##
|
60
|
+
# @return [NEAR::Balance]
|
61
|
+
attr_reader :deposit
|
62
|
+
def deposit
|
63
|
+
@deposit ||= NEAR::Balance.parse(@data['deposit'])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Stake < NEAR::Action; end
|
68
|
+
|
69
|
+
class AddKey < NEAR::Action; end
|
70
|
+
|
71
|
+
class DeleteKey < NEAR::Action; end
|
72
|
+
|
73
|
+
class DeleteAccount < NEAR::Action; end
|
74
|
+
|
75
|
+
class Delegate < NEAR::Action; end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @param [String, Hash] data
|
79
|
+
# @return [NEAR::Action]
|
80
|
+
def self.parse(data)
|
81
|
+
case
|
82
|
+
when data.is_a?(String) then case
|
83
|
+
when 'CreateAccount' then CreateAccount.new
|
84
|
+
when 'DeployContract' then DeployContract.new
|
85
|
+
when 'FunctionCall' then FunctionCall.new
|
86
|
+
when 'Transfer' then Transfer.new
|
87
|
+
when 'Stake' then Stake.new
|
88
|
+
when 'AddKey' then AddKey.new
|
89
|
+
when 'DeleteKey' then DeleteKey.new
|
90
|
+
when 'DeleteAccount' then DeleteAccount.new
|
91
|
+
when 'Delegate' then Delegate.new
|
92
|
+
else self.new
|
93
|
+
end
|
94
|
+
when data['CreateAccount'] then CreateAccount.new(data['CreateAccount'])
|
95
|
+
when data['DeployContract'] then DeployContract.new(data['DeployContract'])
|
96
|
+
when data['FunctionCall'] then FunctionCall.new(data['FunctionCall'])
|
97
|
+
when data['Transfer'] then Transfer.new(data['Transfer'])
|
98
|
+
when data['Stake'] then Stake.new(data['Stake'])
|
99
|
+
when data['AddKey'] then AddKey.new(data['AddKey'])
|
100
|
+
when data['DeleteKey'] then DeleteKey.new(data['DeleteKey'])
|
101
|
+
when data['DeleteAccount'] then DeleteAccount.new(data['DeleteAccount'])
|
102
|
+
when data['Delegate'] then Delegate.new(data['Delegate'])
|
103
|
+
else self.new(data[data.keys.first])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# @param [Hash, #to_h] data
|
109
|
+
def initialize(data = nil)
|
110
|
+
@data = data.to_h if data
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# @return [Symbol]
|
115
|
+
attr_reader :type
|
116
|
+
def type
|
117
|
+
self.class.name.split('::').last.to_sym
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# The action data.
|
122
|
+
#
|
123
|
+
# @return [Hash]
|
124
|
+
attr_reader :data
|
125
|
+
|
126
|
+
##
|
127
|
+
# @return [Hash]
|
128
|
+
def to_h; @data || {}; end
|
129
|
+
|
130
|
+
##
|
131
|
+
# @return [Symbol]
|
132
|
+
def to_sym; self.type; end
|
133
|
+
|
134
|
+
##
|
135
|
+
# @return [String]
|
136
|
+
def inspect
|
137
|
+
data = self.to_h
|
138
|
+
data['args'] = '...' if data['args']
|
139
|
+
"#<#{self.class.name} #{data.to_json}>"
|
140
|
+
end
|
141
|
+
end # NEAR::Block
|
data/lib/near/balance.rb
CHANGED
@@ -5,6 +5,10 @@ require 'bigdecimal'
|
|
5
5
|
##
|
6
6
|
# Represents a NEAR balance.
|
7
7
|
class NEAR::Balance
|
8
|
+
def self.parse(s)
|
9
|
+
self.new(s.to_f / 10**24)
|
10
|
+
end
|
11
|
+
|
8
12
|
def self.from_near(s)
|
9
13
|
self.new(s)
|
10
14
|
end
|
@@ -32,7 +36,7 @@ class NEAR::Balance
|
|
32
36
|
#
|
33
37
|
# @return [String]
|
34
38
|
def inspect
|
35
|
-
"
|
39
|
+
"Ⓝ #{@quantity.to_f}"
|
36
40
|
end
|
37
41
|
|
38
42
|
##
|
data/lib/near/block.rb
CHANGED
@@ -26,9 +26,11 @@ class NEAR::Block
|
|
26
26
|
##
|
27
27
|
# @param [Integer, #to_i] height
|
28
28
|
# @param [String, #to_s] hash
|
29
|
-
|
29
|
+
# @param [Hash, #to_h] data
|
30
|
+
def initialize(height: nil, hash: nil, data: nil)
|
30
31
|
@height = height.to_i if height
|
31
32
|
@hash = hash.to_s if hash
|
33
|
+
@data = data.to_h if data
|
32
34
|
end
|
33
35
|
|
34
36
|
##
|
@@ -43,6 +45,133 @@ class NEAR::Block
|
|
43
45
|
# @return [String]
|
44
46
|
attr_reader :hash
|
45
47
|
|
48
|
+
##
|
49
|
+
# The block data, if fetched.
|
50
|
+
#
|
51
|
+
# @return [Hash]
|
52
|
+
attr_reader :data
|
53
|
+
|
54
|
+
##
|
55
|
+
# @return [String]
|
56
|
+
attr_reader :author
|
57
|
+
def author
|
58
|
+
self.data['block']['author']
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# The block header.
|
63
|
+
#
|
64
|
+
# @return [Hash]
|
65
|
+
attr_reader :header
|
66
|
+
def header
|
67
|
+
self.data['block']['header']
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# @return [Array<Hash>]
|
72
|
+
attr_reader :chunks
|
73
|
+
def chunks
|
74
|
+
self.data['block']['chunks']
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# @return [Array<Hash>]
|
79
|
+
attr_reader :shards
|
80
|
+
def shards
|
81
|
+
self.data['shards']
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# The set of signers in the transactions in this block.
|
86
|
+
#
|
87
|
+
# @return [Array<NEAR::Account>]
|
88
|
+
def signers
|
89
|
+
self.collect_transaction_field('signer_id')
|
90
|
+
.map { |id| NEAR::Account.new(id) }
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# The set of receivers in the transactions in this block.
|
95
|
+
#
|
96
|
+
# @return [Array<NEAR::Account>]
|
97
|
+
def receivers
|
98
|
+
self.collect_transaction_field('receiver_id')
|
99
|
+
.map { |id| NEAR::Account.new(id) }
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Enumerates the transactions in this block.
|
104
|
+
#
|
105
|
+
# @yield [NEAR::Transaction]
|
106
|
+
# @yieldparam [NEAR::Transaction] transaction
|
107
|
+
# @yieldreturn [void]
|
108
|
+
# @return [Enumerator] if no block is given
|
109
|
+
def each_transaction(&)
|
110
|
+
return enum_for(:each_transaction) unless block_given?
|
111
|
+
self.each_chunk do |chunk|
|
112
|
+
next if !chunk['transactions']
|
113
|
+
chunk['transactions'].each do |tx|
|
114
|
+
yield NEAR::Transaction.parse(tx)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Enumerates the actions in this block.
|
121
|
+
#
|
122
|
+
# @yield [NEAR::Action, NEAR::Transaction]
|
123
|
+
# @yieldparam [NEAR::Action] action
|
124
|
+
# @yieldparam [NEAR::Transaction] transaction
|
125
|
+
# @yieldreturn [void]
|
126
|
+
# @return [Enumerator] if no block is given
|
127
|
+
def each_action(&)
|
128
|
+
return enum_for(:each_action) unless block_given?
|
129
|
+
self.each_transaction do |tx|
|
130
|
+
tx.each_action do |action|
|
131
|
+
yield action, tx
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# @param [Symbol, #to_sym] type
|
138
|
+
# @param [String, Regexp] signer
|
139
|
+
# @param [String, Regexp] receiver
|
140
|
+
# @param [String, Regexp] method_name
|
141
|
+
# @yield [NEAR::Action, NEAR::Transaction]
|
142
|
+
# @yieldparam [NEAR::Action] action
|
143
|
+
# @yieldparam [NEAR::Transaction] transaction
|
144
|
+
# @yieldreturn [void]
|
145
|
+
# @return [Enumerator] if no block is given
|
146
|
+
def find_actions(type, signer: nil, receiver: nil, method_name: nil, &)
|
147
|
+
return enum_for(:each_action) unless block_given?
|
148
|
+
type = type.to_sym
|
149
|
+
self.each_transaction do |tx|
|
150
|
+
next if signer && !(signer === tx.signer_id)
|
151
|
+
next if receiver && !(receiver === tx.receiver_id)
|
152
|
+
tx.each_action do |action|
|
153
|
+
next unless type == action.type
|
154
|
+
next if method_name && !(method_name === action.method_name)
|
155
|
+
yield action, tx
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Enumerates the chunks in this block.
|
162
|
+
#
|
163
|
+
# @yield [Hash]
|
164
|
+
# @yieldparam [Hash] chunk
|
165
|
+
# @yieldreturn [void]
|
166
|
+
# @return [Enumerator] if no block is given
|
167
|
+
def each_chunk(&)
|
168
|
+
return enum_for(:each_chunk) unless block_given?
|
169
|
+
self.shards.each do |shard|
|
170
|
+
chunk = shard['chunk']
|
171
|
+
yield chunk if chunk
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
46
175
|
##
|
47
176
|
# @return [Array<String>]
|
48
177
|
def to_cli_args
|
@@ -53,13 +182,36 @@ class NEAR::Block
|
|
53
182
|
|
54
183
|
##
|
55
184
|
# @return [Integer]
|
56
|
-
def to_i
|
57
|
-
@height
|
58
|
-
end
|
185
|
+
def to_i; @height; end
|
59
186
|
|
60
187
|
##
|
61
188
|
# @return [String]
|
62
189
|
def to_s
|
63
190
|
(@height || @hash || :now).to_s
|
64
191
|
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# @return [Hash]
|
195
|
+
def to_h; @data; end
|
196
|
+
|
197
|
+
##
|
198
|
+
# @return [String]
|
199
|
+
def inspect
|
200
|
+
"#<#{self.class.name} height: #{@height}, hash: #{@hash.inspect}>"
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
|
205
|
+
##
|
206
|
+
# @param [String] field
|
207
|
+
# @return [Array<String>]
|
208
|
+
def collect_transaction_field(field)
|
209
|
+
result = {}
|
210
|
+
self.shards.each do |shard|
|
211
|
+
shard['chunk']['transactions'].each do |tx|
|
212
|
+
result[tx['transaction']['receiver_id']] = true
|
213
|
+
end
|
214
|
+
end
|
215
|
+
result.keys
|
216
|
+
end
|
65
217
|
end # NEAR::Block
|
data/lib/near/cli/account.rb
CHANGED
@@ -70,15 +70,20 @@ module NEAR::CLI::Account
|
|
70
70
|
##
|
71
71
|
# Creates a new account sponsored by the faucet service.
|
72
72
|
#
|
73
|
-
# @param [
|
74
|
-
# @param [String] public_key
|
73
|
+
# @param [NEAR::Account, #to_s] new_account
|
74
|
+
# @param [String, nil] public_key
|
75
75
|
# @return [String]
|
76
|
-
def create_account_with_faucet(
|
76
|
+
def create_account_with_faucet(new_account, public_key: nil)
|
77
77
|
stdout, stderr = execute(
|
78
78
|
'account',
|
79
79
|
'create-account',
|
80
|
-
'sponsor-by-faucet-service',
|
81
|
-
|
80
|
+
'sponsor-by-faucet-service', new_account.to_s,
|
81
|
+
*case public_key
|
82
|
+
when nil then ['autogenerate-new-keypair', 'save-to-keychain']
|
83
|
+
when String then ['use-manually-provided-public-key', public_key]
|
84
|
+
when Array then public_key
|
85
|
+
else raise ArgumentError
|
86
|
+
end,
|
82
87
|
'network-config', @network,
|
83
88
|
'create'
|
84
89
|
)
|
@@ -88,16 +93,22 @@ module NEAR::CLI::Account
|
|
88
93
|
##
|
89
94
|
# Creates a new account funded by another account.
|
90
95
|
#
|
91
|
-
# @param [NEAR::Account] new_account
|
92
|
-
# @param [NEAR::Account] signer Account that signs & funds the transaction
|
93
|
-
# @param [
|
96
|
+
# @param [NEAR::Account, #to_s] new_account
|
97
|
+
# @param [NEAR::Account, #to_s] signer Account that signs & funds the transaction
|
98
|
+
# @param [String, nil] public_key
|
99
|
+
# @param [NEAR::Balance, #to_s] deposit Amount of NEAR to attach
|
94
100
|
# @return [String]
|
95
|
-
def create_account_with_funding(new_account, signer:, deposit: nil)
|
101
|
+
def create_account_with_funding(new_account, signer:, public_key: nil, deposit: nil)
|
96
102
|
stdout, stderr = execute(
|
97
103
|
'account',
|
98
104
|
'create-account',
|
99
105
|
'fund-myself', new_account.to_s, (deposit ? deposit.to_s : '0') + ' NEAR',
|
100
|
-
|
106
|
+
*case public_key
|
107
|
+
when nil then ['autogenerate-new-keypair', 'save-to-keychain']
|
108
|
+
when String then ['use-manually-provided-public-key', public_key]
|
109
|
+
when Array then public_key
|
110
|
+
else raise ArgumentError
|
111
|
+
end,
|
101
112
|
'sign-as', signer.to_s,
|
102
113
|
'network-config', @network,
|
103
114
|
'sign-with-keychain',
|
data/lib/near/error.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
class NEAR::Error < StandardError
|
4
|
+
class InvalidBlock < NEAR::Error
|
5
|
+
def initialize(block_height = nil)
|
6
|
+
@block_height = block_height.to_i if block_height
|
7
|
+
super(block_height ?
|
8
|
+
"Block height ##{block_height.to_i} does not exist" :
|
9
|
+
"Block height does not exist")
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :block_height
|
13
|
+
end # NEAR::Error::InvalidBlock
|
14
|
+
|
15
|
+
class TemporaryProblem < NEAR::Error; end
|
16
|
+
|
17
|
+
class UnexpectedProblem < NEAR::Error; end
|
18
|
+
end # NEAR::Error
|
data/lib/near/network.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# This is free and unencumbered software released into the public domain.
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday/follow_redirects'
|
5
|
+
require 'faraday/retry'
|
6
|
+
|
7
|
+
require_relative 'error'
|
4
8
|
|
5
9
|
##
|
6
10
|
# Represents a NEAR Protocol network.
|
@@ -24,20 +28,107 @@ class NEAR::Network
|
|
24
28
|
self.class.const_get(:NEARDATA_URL)
|
25
29
|
end
|
26
30
|
|
31
|
+
##
|
32
|
+
# Fetches a block range.
|
33
|
+
#
|
34
|
+
# The block data is fetched from the neardata.xyz API.
|
35
|
+
#
|
36
|
+
# @param [NEAR::Block, #to_i] from
|
37
|
+
# @param [NEAR::Block, #to_i] to
|
38
|
+
# @yield [NEAR::Block]
|
39
|
+
# @yieldparam [NEAR::Block] block
|
40
|
+
# @yieldreturn [void]
|
41
|
+
# @return [Enumerator]
|
42
|
+
def fetch_blocks(from: nil, to: nil, &)
|
43
|
+
return enum_for(:fetch_blocks) unless block_given?
|
44
|
+
from = self.fetch_latest.height unless from
|
45
|
+
from, to = from.to_i, to&.to_i
|
46
|
+
(from..to).each do |block_height|
|
47
|
+
yield self.fetch(block_height)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
27
51
|
##
|
28
52
|
# Fetches the block at the given height.
|
29
53
|
#
|
30
54
|
# The block data is fetched from the neardata.xyz API.
|
31
|
-
# If the block does not exist, `nil` is returned.
|
32
55
|
#
|
33
56
|
# @param [NEAR::Block, #to_i] block
|
34
|
-
# @return [
|
57
|
+
# @return [NEAR::Block]
|
58
|
+
# @raise [NEAR::Error::InvalidBlock] if the block does not exist
|
35
59
|
def fetch(block)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
60
|
+
self.fetch_neardata_block(block.to_i)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Fetches the latest finalized block.
|
65
|
+
#
|
66
|
+
# The block data is fetched from the neardata.xyz API.
|
67
|
+
# The block is guaranteed to exist.
|
68
|
+
#
|
69
|
+
# @return [NEAR::Block]
|
70
|
+
def fetch_latest
|
71
|
+
self.fetch_neardata_block(:final)
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
##
|
77
|
+
# @param [Integer, Symbol] block_id
|
78
|
+
# @return [NEAR::Block]
|
79
|
+
def fetch_neardata_block(block_id)
|
80
|
+
request_path = case block_id
|
81
|
+
when Integer then "/v0/block/#{block_id}"
|
82
|
+
when Symbol then "/v0/last_block/#{block_id}"
|
83
|
+
else raise ArgumentError
|
84
|
+
end
|
85
|
+
|
86
|
+
begin
|
87
|
+
block_data = self.fetch_neardata_url(request_path)
|
88
|
+
rescue Faraday::ResourceNotFound => error
|
89
|
+
case error.response[:body]['type']
|
90
|
+
when "BLOCK_DOES_NOT_EXIST", "BLOCK_HEIGHT_TOO_LOW", "BLOCK_HEIGHT_TOO_HIGH"
|
91
|
+
raise NEAR::Error::InvalidBlock, block_id
|
92
|
+
else raise NEAR::Error::UnexpectedProblem
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
return nil if block_data.nil? || block_data == 'null'
|
97
|
+
block_header = block_data['block']['header']
|
98
|
+
block_height = block_header['height'].to_i
|
99
|
+
block_hash = block_header['hash'].to_s
|
100
|
+
NEAR::Block.new(height: block_height, hash: block_hash, data: block_data)
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# @return [Object]
|
105
|
+
def fetch_neardata_url(path)
|
106
|
+
begin
|
107
|
+
retries ||= 0
|
108
|
+
self.http_client.get("#{neardata_url}#{path}").body
|
109
|
+
rescue Faraday::ConnectionFailed => error
|
110
|
+
# `Faraday::Retry` doesn't seem able to handle `ConnectionFailed`,
|
111
|
+
# so we'll handle it manually here. Note that in case of a DNS error,
|
112
|
+
# there's no point in retrying, so we'll wrap & re-raise the error.
|
113
|
+
case error.wrapped_exception
|
114
|
+
when Socket::ResolutionError then raise NEAR::Error::TemporaryProblem
|
115
|
+
else retry if (retries += 1) < 3
|
116
|
+
end
|
117
|
+
raise NEAR::Error::UnexpectedProblem
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# @return [Faraday::Connection]
|
123
|
+
def http_client
|
124
|
+
Faraday.new do |faraday|
|
125
|
+
faraday.response :raise_error
|
126
|
+
faraday.response :follow_redirects
|
127
|
+
faraday.response :json
|
128
|
+
faraday.request :retry, {
|
129
|
+
max: 3,
|
130
|
+
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed]
|
131
|
+
}.freeze
|
41
132
|
end
|
42
133
|
end
|
43
134
|
end # NEAR::Testnet
|
data/lib/near/transaction.rb
CHANGED
@@ -3,10 +3,21 @@
|
|
3
3
|
##
|
4
4
|
# Represents a NEAR transaction.
|
5
5
|
class NEAR::Transaction
|
6
|
+
##
|
7
|
+
# @param [Hash] json
|
8
|
+
# @return [NEAR::Transaction]
|
9
|
+
def self.parse(json)
|
10
|
+
tx_data = json['transaction']
|
11
|
+
NEAR::Transaction.new(tx_data['hash'], data: tx_data)
|
12
|
+
end
|
13
|
+
|
6
14
|
##
|
7
15
|
# @param [String, #to_s] hash
|
8
|
-
|
16
|
+
# @param [Hash, #to_h] data
|
17
|
+
# @return [void]
|
18
|
+
def initialize(hash, data: nil)
|
9
19
|
@hash = hash.to_s
|
20
|
+
@data = data.to_h if data
|
10
21
|
end
|
11
22
|
|
12
23
|
##
|
@@ -15,7 +26,108 @@ class NEAR::Transaction
|
|
15
26
|
# @return [String]
|
16
27
|
attr_reader :hash
|
17
28
|
|
29
|
+
##
|
30
|
+
# The transaction data, if available.
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
attr_reader :data
|
34
|
+
|
35
|
+
##
|
36
|
+
# The transaction signer account.
|
37
|
+
#
|
38
|
+
# @return [NEAR::Account]
|
39
|
+
attr_reader :signer
|
40
|
+
def signer
|
41
|
+
@signer ||= NEAR::Account.new(self.signer_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# The transaction signer ID.
|
46
|
+
#
|
47
|
+
# @return [String]
|
48
|
+
attr_reader :signer_id
|
49
|
+
def signer_id
|
50
|
+
self.data['signer_id']
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# The transaction receiver account.
|
55
|
+
#
|
56
|
+
# @return [NEAR::Account]
|
57
|
+
attr_reader :receiver
|
58
|
+
def receiver
|
59
|
+
@receiver ||= NEAR::Account.new(self.receiver_id)
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# The transaction receiver ID.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
attr_reader :receiver_id
|
67
|
+
def receiver_id
|
68
|
+
self.data['receiver_id']
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# @return [String]
|
73
|
+
attr_reader :public_key
|
74
|
+
def public_key
|
75
|
+
self.data['public_key']
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# @return [String]
|
80
|
+
attr_reader :signature
|
81
|
+
def signature
|
82
|
+
self.data['signature']
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# @return [Integer]
|
87
|
+
attr_reader :nonce
|
88
|
+
def nonce
|
89
|
+
self.data['nonce']
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# @return [Integer]
|
94
|
+
attr_reader :priority_fee
|
95
|
+
def priority_fee
|
96
|
+
self.data['priority_fee']
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# The transaction actions.
|
101
|
+
#
|
102
|
+
# @return [Array<Hash>]
|
103
|
+
attr_reader :actions
|
104
|
+
def actions
|
105
|
+
self.data['actions']
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# @yield [NEAR::Action]
|
110
|
+
# @yieldparam [NEAR::Action] action
|
111
|
+
# @yieldreturn [void]
|
112
|
+
# @return [Enumerator] if no block is given
|
113
|
+
def each_action(&)
|
114
|
+
return enum_for(:each_action) unless block_given?
|
115
|
+
self.data['actions'].each do |action|
|
116
|
+
yield NEAR::Action.parse(action)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
18
120
|
##
|
19
121
|
# @return [String]
|
20
122
|
def to_s; @hash; end
|
123
|
+
|
124
|
+
##
|
125
|
+
# @return [Hash]
|
126
|
+
def to_h; @data; end
|
127
|
+
|
128
|
+
##
|
129
|
+
# @return [String]
|
130
|
+
def inspect
|
131
|
+
"#<#{self.class.name} hash: #{@hash.inspect}, signer: #{self.signer}, receiver: #{self.receiver}, actions: #{self.actions.size}>"
|
132
|
+
end
|
21
133
|
end # NEAR::Transaction
|
data/lib/near.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
module NEAR; end
|
4
4
|
|
5
5
|
require_relative 'near/account'
|
6
|
+
require_relative 'near/action'
|
6
7
|
require_relative 'near/balance'
|
7
8
|
require_relative 'near/block'
|
8
9
|
require_relative 'near/cli'
|
10
|
+
require_relative 'near/error'
|
9
11
|
require_relative 'near/mainnet'
|
10
12
|
require_relative 'near/network'
|
11
13
|
require_relative 'near/testnet'
|
metadata
CHANGED
@@ -1,14 +1,84 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: near
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arto Bendiken
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-02-05 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: base64
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bigdecimal
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: faraday
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.12'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.12'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: faraday-follow_redirects
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.3'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0.3'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: faraday-retry
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.0'
|
12
82
|
- !ruby/object:Gem::Dependency
|
13
83
|
name: rspec
|
14
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,6 +120,7 @@ files:
|
|
50
120
|
- VERSION
|
51
121
|
- lib/near.rb
|
52
122
|
- lib/near/account.rb
|
123
|
+
- lib/near/action.rb
|
53
124
|
- lib/near/balance.rb
|
54
125
|
- lib/near/block.rb
|
55
126
|
- lib/near/cli.rb
|
@@ -59,6 +130,7 @@ files:
|
|
59
130
|
- lib/near/cli/staking.rb
|
60
131
|
- lib/near/cli/tokens.rb
|
61
132
|
- lib/near/cli/transaction.rb
|
133
|
+
- lib/near/error.rb
|
62
134
|
- lib/near/mainnet.rb
|
63
135
|
- lib/near/network.rb
|
64
136
|
- lib/near/testnet.rb
|
@@ -87,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
159
|
- !ruby/object:Gem::Version
|
88
160
|
version: '0'
|
89
161
|
requirements: []
|
90
|
-
rubygems_version: 3.6.
|
162
|
+
rubygems_version: 3.6.3
|
91
163
|
specification_version: 4
|
92
164
|
summary: 'NEAR.rb: NEAR for Ruby'
|
93
165
|
test_files: []
|