solana-ruby-web3js 1.0.1.beta3 → 2.0.0beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 769dd8aff42953255243d337b811ab9bd61e27e19e6957d235e9e015300f925b
4
- data.tar.gz: e940aa048c15af34c5f06bf5de1db3716e8f68f1f83c41fe480a6de748082310
3
+ metadata.gz: 37360e6f27ca521194022c29c3419d2dc06e5dcae0499af04892e3ca29299b0d
4
+ data.tar.gz: c5748b0ddacfaa170e79aba9a16b883c64a05ac7f776f0589c3914a5ad418356
5
5
  SHA512:
6
- metadata.gz: 7cd8eb6a1ff5a1c749cb1ded759323bd1a7a43dea78e6d19444f1d1e8dc77ca24f757c5fc53412431020f09960db0cef8f2bd519641a0bdc5790c4dc8e60a581
7
- data.tar.gz: d6c68d6c97ae798d5b79b4fcaae962ac3e34e7e2d28d61b80124b4725b3926803c49f20bd055bc7c6af9b46229558111a3ea0dae932d15750f726484a0c6b460
6
+ metadata.gz: 683573ae5898ae932dea765b65cc4d73136f198429920be4e923da76bfed3b515e941f96761238f42dd92cedadc7d28c03f43dc3357fb07451a4940b3a2e93e0
7
+ data.tar.gz: e6c3b0e84760561c6b22290f3dab047cb041f9de7ed02d1a7cd88cc5ed285a6fb5c2ca97e7a719f709044c044b3c769aa0d0c612bb35b5894e0e70f1955e9c4e
data/.DS_Store CHANGED
Binary file
data/Gemfile.lock CHANGED
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- solana-ruby-web3js (1.0.1.beta1)
4
+ solana-ruby-web3js (1.0.1.beta3)
5
5
  base58 (~> 0.2.3)
6
6
  base64 (~> 0.2.0)
7
+ ed25519
8
+ rbnacl (~> 6.0)
7
9
  websocket-client-simple (~> 0.8.0)
8
10
 
9
11
  GEM
@@ -64,6 +66,8 @@ GEM
64
66
  ed25519 (1.3.0)
65
67
  erubi (1.13.0)
66
68
  event_emitter (0.2.6)
69
+ ffi (1.17.0)
70
+ ffi (1.17.0-arm64-darwin)
67
71
  flay (2.13.3)
68
72
  erubi (~> 1.10)
69
73
  path_expander (~> 1.0)
@@ -93,6 +97,8 @@ GEM
93
97
  public_suffix (6.0.1)
94
98
  racc (1.8.1)
95
99
  rainbow (3.1.1)
100
+ rbnacl (6.0.1)
101
+ ffi
96
102
  reek (6.3.0)
97
103
  dry-schema (~> 1.13.0)
98
104
  parser (~> 3.3.0)
data/README.md CHANGED
@@ -332,3 +332,75 @@ The following methods are supported by the SolanaRuby::WebSocketClient:
332
332
 
333
333
  Several methods have optional parameters where default options are defined in the client. These options can be customized or overridden when calling the methods, but if left unspecified, the client will use its internal defaults.
334
334
 
335
+ ## Transaction Helpers
336
+
337
+ ### Transfer SOL Between Accounts
338
+
339
+ To transfer SOL (the native cryptocurrency of the Solana blockchain) from one account to another, follow these steps:
340
+
341
+ #### Requirements:
342
+
343
+ - **Sender's Keypair:** Either generate a new keypair or provide the private key for an existing sender account. This keypair is used to sign the transaction.
344
+ - **Receiver's Public Key:** Specify the public key of the destination account. You can generate a new keypair for the receiver or use an existing public key.
345
+ - **Airdrop Functionality:** For Devnet or Testnet transactions, ensure that the sender's account is funded with sufficient lamports using the Solana airdrop feature.
346
+ - An initialized client to interact with the Solana blockchain.
347
+
348
+ #### Example Usage:
349
+
350
+ require 'solana_ruby'
351
+
352
+ # Initialize the client (defaults to Mainnet(https://api.mainnet-beta.solana.com))
353
+ client = SolanaRuby::HttpClient.new('https://api.devnet.solana.com')
354
+
355
+ # Fetch the recent blockhash
356
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
357
+
358
+ # Generate or fetch the sender's keypair
359
+ # Option 1: Generate a new keypair
360
+ sender_keypair = SolanaRuby::Keypair.generate
361
+ # Option 2: Use an existing private key
362
+ # sender_keypair = SolanaRuby::Keypair.from_private_key("InsertPrivateKeyHere")
363
+
364
+ sender_pubkey = sender_keypair[:public_key]
365
+
366
+
367
+ # Airdrop some lamports to the sender's account
368
+ lamports = 10 * 1_000_000_000
369
+ sleep(1)
370
+ result = client.request_airdrop(sender_pubkey, lamports)
371
+ puts "Solana Balance #{lamports} lamports added sucessfully for the public key: #{sender_pubkey}"
372
+ sleep(10)
373
+
374
+
375
+ # Generate or use an existing receiver's public key
376
+ # Option 1: Generate a new keypair for the receiver
377
+ receiver_keypair = SolanaRuby::Keypair.generate # generate receiver keypair
378
+ receiver_pubkey = receiver_keypair[:public_key]
379
+ # Option 2: Use an existing public key
380
+ # receiver_pubkey = 'InsertExistingPublicKeyHere'
381
+
382
+ transfer_lamports = 1 * 1_000_000
383
+ puts "Payer's full private key: #{sender_keypair[:full_private_key]}"
384
+ puts "Receiver's full private key: #{receiver_keypair[:full_private_key]}"
385
+ puts "Receiver's Public Key: #{receiver_keypair[:public_key]}"
386
+
387
+ # Create a new transaction
388
+ transaction = SolanaRuby::TransactionHelper.new_sol_transaction(
389
+ sender_pubkey,
390
+ receiver_pubkey,
391
+ transfer_lamports,
392
+ recent_blockhash
393
+ )
394
+
395
+ # Get the sender's private key (ensure it's a string)
396
+ private_key = sender_keypair[:private_key]
397
+ puts "Private key type: #{private_key.class}, Value: #{private_key.inspect}"
398
+
399
+ # Sign the transaction
400
+ signed_transaction = transaction.sign([sender_keypair])
401
+
402
+ # Send the transaction to the Solana network
403
+ sleep(5)
404
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
405
+ puts "Response: #{response}"
406
+
@@ -0,0 +1,30 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ class Blob
4
+ attr_reader :size
5
+
6
+ # Constructor to initialize size of the blob
7
+ def initialize(size)
8
+ raise ArgumentError, "Size must be a positive integer" unless size.is_a?(Integer) && size > 0
9
+ @size = size
10
+ end
11
+
12
+ # Serialize the given object to a byte array
13
+ def serialize(obj)
14
+ # Ensure obj is an array and then convert to byte array
15
+ obj = [obj] unless obj.is_a?(Array)
16
+ raise ArgumentError, "Object must be an array of bytes" unless obj.all? { |e| e.is_a?(Integer) && e.between?(0, 255) }
17
+
18
+ obj.pack('C*').bytes
19
+ end
20
+
21
+ # Deserialize a byte array into the original object format
22
+ def deserialize(bytes)
23
+ # Ensure the byte array is of the correct size
24
+ raise ArgumentError, "Byte array size must match the expected size" unless bytes.length == @size
25
+
26
+ bytes.pack('C*')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ class Layout
4
+ attr_reader :fields
5
+
6
+ def initialize(fields)
7
+ @fields = fields
8
+ end
9
+
10
+ def serialize(params)
11
+ fields.flat_map do |field, type|
12
+ data_type = type.is_a?(Symbol) ? SolanaRuby::DataTypes.send(type) : type
13
+ data_type.serialize(params[field])
14
+ end
15
+ end
16
+
17
+ def deserialize(bytes)
18
+ result = {}
19
+ fields.map do |field, type|
20
+ data_type = type.is_a?(Symbol) ? SolanaRuby::DataTypes.send(type) : type
21
+ result[field] = data_type.deserialize(bytes.shift(data_type.size))
22
+ end
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ class NearInt64
4
+ attr_reader :size
5
+
6
+ V2E32 = 2.pow(32)
7
+
8
+ def initialize
9
+ @size = 8
10
+ end
11
+
12
+ def serialize(obj)
13
+ uint = UnsignedInt.new(32)
14
+ numbers = divmod_int64(obj)
15
+ numbers.map{|x| uint.serialize(x)}.flatten
16
+ end
17
+
18
+ def deserialize(bytes)
19
+ raise "Invalid serialization (wrong size)" if @size && bytes.size != @size
20
+ uint = UnsignedInt.new(32)
21
+ half_size = @size/2
22
+
23
+ lo, hi = [bytes[0..half_size-1], bytes[half_size..-1]].map{|x| uint.deserialize(x)}
24
+
25
+ rounded_int64(hi, lo)
26
+ end
27
+
28
+ def divmod_int64(obj)
29
+ obj = obj * 1.0
30
+ hi32 = (obj / V2E32).floor
31
+ lo32 = (obj - (hi32 * V2E32)).floor
32
+ [lo32, hi32]
33
+ end
34
+
35
+ def rounded_int64(hi32, lo32)
36
+ hi32 * V2E32 + lo32
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ class Sequence
4
+ def initialize count, type
5
+ @count = count
6
+ @type = type
7
+ end
8
+
9
+ def serialize items
10
+ items.map do |item|
11
+ @type.serialize(item)
12
+ end.flatten
13
+ end
14
+
15
+ def deserialize bytes
16
+ @count.times.map do
17
+ current_bytes = bytes.shift(@type.size)
18
+ @type.deserialize(current_bytes)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ class UnsignedInt
4
+ attr_reader :size
5
+
6
+ BITS = {
7
+ 8 => { directive: 'C*', size: 1 },
8
+ 32 => { directive: 'L*', size: 4 },
9
+ 64 => { directive: 'Q*', size: 8 }
10
+ }
11
+
12
+ def initialize(bits)
13
+ @bits = bits
14
+ type = BITS[@bits]
15
+ raise "Can only fit #{BITS.keys}" unless type
16
+ @size = type[:size]
17
+ @directive = type[:directive]
18
+ end
19
+
20
+ # Serialize the unsigned integer into bytes
21
+ def serialize(obj)
22
+ raise "Can only serialize integers" unless obj.is_a?(Integer)
23
+ raise "Cannot serialize negative integers" if obj < 0
24
+
25
+ if obj >= 256**@size
26
+ raise "Integer too large (does not fit in #{@size} bytes)"
27
+ end
28
+
29
+ [obj].pack(@directive).bytes
30
+ end
31
+
32
+ # Deserialize bytes into the unsigned integer
33
+ def deserialize(bytes)
34
+ raise "Invalid serialization (wrong size)" if bytes.size != @size
35
+
36
+ bytes.pack('C*').unpack(@directive).first
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module SolanaRuby
2
+ module DataTypes
3
+ extend self
4
+
5
+ def uint8
6
+ UnsignedInt.new(8)
7
+ end
8
+
9
+ def uint32
10
+ UnsignedInt.new(32)
11
+ end
12
+
13
+ def uint64
14
+ UnsignedInt.new(64)
15
+ end
16
+
17
+ def near_int64
18
+ NearInt64.new
19
+ end
20
+
21
+ def blob1
22
+ Blob.new(1)
23
+ end
24
+ end
25
+ end
@@ -23,7 +23,8 @@ module SolanaRuby
23
23
 
24
24
  def request(method, params = [])
25
25
  http = Net::HTTP.new(@uri.host, @uri.port)
26
- http.use_ssl = true
26
+ local_hosts = ['localhost', '127.0.0.1', '[::1]']
27
+ http.use_ssl = true unless local_hosts.include?(@uri.host.downcase)
27
28
 
28
29
  request = Net::HTTP::Post.new(@uri.request_uri, {'Content-Type' => 'application/json'})
29
30
  request.body = {
@@ -0,0 +1,42 @@
1
+ module SolanaRuby
2
+ class Keypair
3
+ require 'rbnacl'
4
+ require 'base58'
5
+
6
+ # Generates a new Ed25519 keypair
7
+ def self.generate
8
+ signing_key = RbNaCl::Signatures::Ed25519::SigningKey.generate
9
+ public_key_bytes = signing_key.verify_key.to_bytes # Binary format for public key
10
+ private_key_bytes = signing_key.to_bytes
11
+ private_key_hex = private_key_bytes.unpack1('H*') # Hex format for private key
12
+
13
+ # Convert public key binary to Base58 for readability and compatibility
14
+ {
15
+ public_key: Base58.binary_to_base58(public_key_bytes, :bitcoin),
16
+ private_key: private_key_hex,
17
+ full_private_key: Base58.binary_to_base58((private_key_bytes + public_key_bytes), :bitcoin)
18
+ }
19
+ end
20
+
21
+ # Restores a keypair from a private key in hex format
22
+ def self.from_private_key(private_key_hex)
23
+ raise ArgumentError, "Invalid private key length" unless private_key_hex.size == 64
24
+
25
+ # Convert hex private key to binary format for signing key
26
+ private_key_bytes = [private_key_hex].pack('H*')
27
+
28
+ # Initialize signing key
29
+ signing_key = RbNaCl::Signatures::Ed25519::SigningKey.new(private_key_bytes)
30
+
31
+ # Extract public key in binary format
32
+ public_key_bytes = signing_key.verify_key.to_bytes
33
+
34
+ # Return public key in Base58 format and private key in hex format
35
+ {
36
+ public_key: Base58.binary_to_base58(public_key_bytes, :bitcoin),
37
+ private_key: private_key_hex,
38
+ full_private_key: Base58.binary_to_base58((private_key_bytes + public_key_bytes), :bitcoin)
39
+ }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,107 @@
1
+ module SolanaRuby
2
+ class Message
3
+ PUBKEY_LENGTH = 32
4
+
5
+ attr_reader :header, :account_keys, :recent_blockhash, :instructions
6
+
7
+ def initialize(header:, account_keys:, recent_blockhash:, instructions:)
8
+ @header = header
9
+ @account_keys = account_keys
10
+ @recent_blockhash = recent_blockhash
11
+ @instructions = instructions
12
+ end
13
+
14
+ def self.from(bytes)
15
+ bytes = bytes.dup
16
+ num_required_signatures = bytes.shift
17
+ num_readonly_signed_accounts = bytes.shift
18
+ num_readonly_unsigned_accounts = bytes.shift
19
+ account_count = Utils.decode_length(bytes)
20
+
21
+ account_keys = account_count.times.map do
22
+ account_bytes = bytes.slice!(0, PUBKEY_LENGTH)
23
+ Utils.bytes_to_base58(account_bytes)
24
+ end
25
+
26
+ recent_blockhash_bytes = bytes.slice!(0, PUBKEY_LENGTH)
27
+ recent_blockhash = Utils.bytes_to_base58(recent_blockhash_bytes)
28
+
29
+ instruction_count = Utils.decode_length(bytes)
30
+ instructions = instruction_count.times.map do
31
+ program_id_index = bytes.shift
32
+ account_count = Utils.decode_length(bytes)
33
+
34
+ accounts = bytes.slice!(0, account_count)
35
+
36
+ data_length = Utils.decode_length(bytes)
37
+ data_bytes = bytes.slice!(0, data_length)
38
+ {program_id_index: program_id_index, accounts: accounts, data: data_bytes}
39
+ end
40
+ self.new({
41
+ header: {
42
+ num_required_signatures: num_required_signatures,
43
+ num_readonly_signed_accounts:num_readonly_signed_accounts,
44
+ num_readonly_unsigned_accounts:num_readonly_unsigned_accounts,
45
+ },
46
+ account_keys: account_keys,
47
+ recent_blockhash: recent_blockhash,
48
+ instructions: instructions
49
+ })
50
+ end
51
+
52
+ def serialize
53
+ num_keys = account_keys.length
54
+ key_count = Utils.encode_length(num_keys)
55
+
56
+ layout = SolanaRuby::DataTypes::Layout.new({
57
+ num_required_signatures: :blob1,
58
+ num_readonly_signed_accounts: :blob1,
59
+ num_readonly_unsigned_accounts: :blob1,
60
+ key_count: SolanaRuby::DataTypes::Blob.new(key_count.length),
61
+ keys: SolanaRuby::DataTypes::Sequence.new(num_keys, SolanaRuby::DataTypes::Blob.new(32)),
62
+ recent_blockhash: SolanaRuby::DataTypes::Blob.new(32)
63
+ })
64
+
65
+ sign_data = layout.serialize({
66
+ num_required_signatures: header[:num_required_signatures],
67
+ num_readonly_signed_accounts: header[:num_readonly_signed_accounts],
68
+ num_readonly_unsigned_accounts: header[:num_readonly_unsigned_accounts],
69
+ key_count: key_count,
70
+ keys: account_keys.map{|x| Utils.base58_to_bytes(x)},
71
+ recent_blockhash: Utils.base58_to_bytes(recent_blockhash)
72
+ })
73
+
74
+ instruction_count = Utils.encode_length(@instructions.length)
75
+ sign_data += instruction_count
76
+
77
+ data = @instructions.map do |instruction|
78
+ instruction_layout = SolanaRuby::DataTypes::Layout.new({
79
+ program_id_index: :uint8,
80
+ key_indices_count: SolanaRuby::DataTypes::Blob.new(key_count.length),
81
+ key_indices: SolanaRuby::DataTypes::Sequence.new(num_keys, SolanaRuby::DataTypes::Blob.new(8)),
82
+ data_length: SolanaRuby::DataTypes::Blob.new(key_count.length),
83
+ data: SolanaRuby::DataTypes::Sequence.new(num_keys, SolanaRuby::DataTypes::UnsignedInt.new(8)),
84
+ })
85
+
86
+ key_indices_count = Utils.encode_length(instruction[:accounts].length)
87
+ data_count = Utils.encode_length(instruction[:data].length)
88
+
89
+ instruction_layout.serialize({
90
+ program_id_index: instruction[:program_id_index],
91
+ key_indices_count: key_indices_count,
92
+ key_indices: instruction[:accounts],
93
+ data_length: data_count,
94
+ data: instruction[:data]
95
+ })
96
+ end.flatten
97
+
98
+ sign_data += data
99
+ sign_data
100
+ end
101
+
102
+ def is_account_writable(index)
103
+ index < header[:num_required_signatures] - header[:num_readonly_signed_accounts] ||
104
+ (index >= header[:num_required_signatures] && index < account_keys.length - header[:num_readonly_unsigned_accounts])
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,283 @@
1
+ Dir[File.join(__dir__, 'data_types', '*.rb')].each { |file| require file }
2
+
3
+ module SolanaRuby
4
+ class Transaction
5
+ require 'rbnacl'
6
+ SIGNATURE_LENGTH = 64
7
+ PACKET_DATA_SIZE = 1280 - 40 - 8
8
+ DEFAULT_SIGNATURE = Array.new(64, 0)
9
+
10
+ attr_accessor :instructions, :signatures, :fee_payer, :recent_blockhash, :message
11
+
12
+ def initialize(recent_blockhash: nil, signatures: [], instructions: [], fee_payer: nil)
13
+ @recent_blockhash = recent_blockhash
14
+ @signatures = signatures
15
+ @instructions = instructions
16
+ @fee_payer = fee_payer
17
+ end
18
+
19
+ def add_instruction(instruction)
20
+ @instructions << instruction
21
+ end
22
+
23
+ def set_fee_payer(pubkey)
24
+ puts "Setting fee payer: #{pubkey.inspect}" # Debugging output
25
+ @fee_payer = pubkey # Store as-is since Base58 gem can handle encoding/decoding
26
+ end
27
+
28
+ def set_recent_blockhash(blockhash)
29
+ # raise "Invalid Base58 blockhash" unless Base58.valid?(blockhash)
30
+ @recent_blockhash = blockhash # Store as-is for similar reasons
31
+ end
32
+
33
+ def self.from(base64_string)
34
+ bytes = Base64.decode64(base64_string).bytes
35
+ signature_count = Utils.decode_length(bytes)
36
+ signatures = signature_count.times.map do
37
+ signature_bytes = bytes.slice!(0, SIGNATURE_LENGTH)
38
+ Utils.bytes_to_base58(signature_bytes)
39
+ end
40
+ msg = Message.from(bytes)
41
+ self.populate(msg, signatures)
42
+ end
43
+
44
+ def serialize
45
+ sign_data = serialize_message
46
+
47
+ signature_count = Utils.encode_length(signatures.length)
48
+ raise 'invalid length!' if signatures.length > 256
49
+
50
+ wire_transaction = signature_count
51
+
52
+ signatures.each do |signature|
53
+ if signature
54
+ signature_bytes = signature[:signature]
55
+ raise 'signature is empty' unless (signature_bytes)
56
+ raise 'signature has invalid length' unless (signature_bytes.length == 64)
57
+ wire_transaction += signature_bytes
58
+ raise "Transaction too large: #{wire_transaction.length} > #{PACKET_DATA_SIZE}" unless wire_transaction.length <= PACKET_DATA_SIZE
59
+ wire_transaction
60
+ end
61
+ end
62
+
63
+ wire_transaction += sign_data
64
+ wire_transaction
65
+ end
66
+
67
+ def to_base64
68
+ Base64.strict_encode64(serialize.pack('C*'))
69
+ end
70
+
71
+ def add(item)
72
+ instructions.push(item)
73
+ end
74
+
75
+ def sign(keys)
76
+ raise 'No signers' unless keys.any?
77
+
78
+ keys = keys.uniq{ |k| key[:public_key] }
79
+ @signatures = keys.map do |key|
80
+ {
81
+ signature: nil,
82
+ public_key: key[:public_key]
83
+ }
84
+ end
85
+
86
+ message = compile_message
87
+ partial_sign(message, keys)
88
+ true
89
+ end
90
+
91
+ private
92
+
93
+ def serialize_message
94
+ compile.serialize
95
+ end
96
+
97
+ def compile
98
+ message = compile_message
99
+ signed_keys = message.account_keys.slice(0, message.header[:num_required_signatures])
100
+
101
+ if signatures.length == signed_keys.length
102
+ valid = signatures.each_with_index.all?{|pair, i| signed_keys[i] == pair[:public_key]}
103
+ return message if valid
104
+ end
105
+
106
+ @signatures = signed_keys.map do |public_key|
107
+ {
108
+ signature: nil,
109
+ public_key: public_key
110
+ }
111
+ end
112
+
113
+ message
114
+ end
115
+
116
+ def compile_message
117
+ check_for_errors
118
+ fetch_message_data
119
+ message = Message.new(
120
+ header: {
121
+ num_required_signatures: @num_required_signatures,
122
+ num_readonly_signed_accounts: @num_readonly_signed_accounts,
123
+ num_readonly_unsigned_accounts: @num_readonly_unsigned_accounts,
124
+ },
125
+ account_keys: @account_keys, recent_blockhash: recent_blockhash, instructions: @instructs
126
+ )
127
+ message
128
+ end
129
+
130
+ def check_for_errors
131
+ raise 'Transaction recent_blockhash required' unless recent_blockhash
132
+
133
+ puts 'No instructions provided' if instructions.length < 1
134
+
135
+ if fee_payer.nil? && signatures.length > 0 && signatures[0][:public_key]
136
+ @fee_payer = signatures[0][:public_key] if (signatures.length > 0 && signatures[0][:public_key])
137
+ end
138
+
139
+ raise('Transaction fee payer required') if @fee_payer.nil?
140
+
141
+ instructions.each_with_index do |instruction, i|
142
+ raise("Transaction instruction index #{i} has undefined program id") unless instruction.program_id
143
+ end
144
+ end
145
+
146
+ def fetch_message_data
147
+ program_ids = []
148
+ account_metas= []
149
+
150
+ instructions.each do |instruction|
151
+ account_metas += instruction.keys
152
+ program_ids.push(instruction.program_id) unless program_ids.include?(instruction.program_id)
153
+ end
154
+
155
+ # Append programID account metas
156
+ append_program_id(program_ids, account_metas)
157
+
158
+ # Sort. Prioritizing first by signer, then by writable
159
+ signer_order(account_metas)
160
+
161
+ # Cull duplicate account metas
162
+ unique_metas = []
163
+ add_unique_meta_data(unique_metas, account_metas)
164
+
165
+ add_fee_payer_meta(unique_metas)
166
+
167
+ # Disallow unknown signers
168
+ disallow_signers(signatures, unique_metas)
169
+
170
+ # Split out signing from non-signing keys and count header values
171
+ signed_keys = []
172
+ unsigned_keys = []
173
+ header_params = split_keys(unique_metas, signed_keys, unsigned_keys)
174
+ @account_keys = signed_keys + unsigned_keys
175
+
176
+ # add instruction structure
177
+ @instructs = add_instructs
178
+ end
179
+
180
+ def append_program_id(program_ids, account_metas)
181
+ program_ids.each do |programId|
182
+ account_metas.push({
183
+ pubkey: programId,
184
+ is_signer: false,
185
+ is_writable: false,
186
+ })
187
+ end
188
+ end
189
+
190
+ def signer_order(account_metas)
191
+ account_metas.sort! do |x, y|
192
+ check_signer = x[:is_signer] == y[:is_signer] ? nil : x[:is_signer] ? -1 : 1
193
+ check_writable = x[:is_writable] == y[:is_writable] ? nil : (x[:is_writable] ? -1 : 1)
194
+ (check_signer || check_writable) || 0
195
+ end
196
+ end
197
+
198
+ def add_unique_meta_data(unique_metas, account_metas)
199
+ account_metas.each do |account_meta|
200
+ pubkey_string = account_meta[:pubkey]
201
+ unique_index = unique_metas.find_index{|x| x[:pubkey] == pubkey_string }
202
+ if unique_index
203
+ unique_metas[unique_index][:is_writable] = unique_metas[unique_index][:is_writable] || account_meta[:is_writable]
204
+ else
205
+ unique_metas.push(account_meta);
206
+ end
207
+ end
208
+ end
209
+
210
+ def add_fee_payer_meta(unique_metas)
211
+ # Move fee payer to the front
212
+ fee_payer_index = unique_metas.find_index { |x| x[:pubkey] == fee_payer }
213
+ if fee_payer_index
214
+ payer_meta = unique_metas.delete_at(fee_payer_index)
215
+ payer_meta[:is_signer] = true
216
+ payer_meta[:is_writable] = true
217
+ unique_metas.unshift(payer_meta)
218
+ else
219
+ unique_metas.unshift({
220
+ pubkey: fee_payer,
221
+ is_signer: true,
222
+ is_writable: true,
223
+ })
224
+ end
225
+ end
226
+
227
+ def disallow_signers(signatures, unique_metas)
228
+ signatures.each do |signature|
229
+ unique_index = unique_metas.find_index{ |x| x[:pubkey] == signature[:public_key] }
230
+
231
+ if unique_index
232
+ unique_metas[unique_index][:is_signer] = true unless unique_metas[unique_index][:is_signer]
233
+ else
234
+ raise "unknown signer: #{signature[:public_key]}"
235
+ end
236
+ end
237
+ end
238
+
239
+ def add_instructs
240
+ instructions.map do |instruction|
241
+ {
242
+ program_id_index: @account_keys.index(instruction.program_id),
243
+ accounts: instruction.keys.map { |meta| @account_keys.index(meta[:pubkey]) },
244
+ data: instruction.data
245
+ }
246
+ end
247
+ end
248
+
249
+ def split_keys(unique_metas, signed_keys, unsigned_keys)
250
+ @num_required_signatures = 0
251
+ @num_readonly_signed_accounts = 0
252
+ @num_readonly_unsigned_accounts = 0
253
+ unique_metas.each do |meta|
254
+ if meta[:is_signer]
255
+ signed_keys.push(meta[:pubkey])
256
+ @num_required_signatures += 1
257
+ @num_readonly_signed_accounts += 1 if (!meta[:is_writable])
258
+ else
259
+ unsigned_keys.push(meta[:pubkey])
260
+ @num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
261
+ end
262
+ end
263
+ end
264
+
265
+ def partial_sign(message, keys)
266
+ sign_data = message.serialize
267
+ keys.each do |key|
268
+ private_key_bytes = [key[:private_key]].pack('H*')
269
+ signing_key = RbNaCl::Signatures::Ed25519::SigningKey.new(private_key_bytes)
270
+ signature = signing_key.sign(sign_data.pack('C*')).bytes
271
+ add_signature(key[:public_key], signature)
272
+ end
273
+ end
274
+
275
+ def add_signature(pubkey, signature)
276
+ raise 'error' unless signature.length === 64
277
+ index = signatures.find_index{|s| s[:public_key] == pubkey}
278
+ raise "unknown signer: #{pubkey}" unless index
279
+
280
+ @signatures[index][:signature] = signature
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,138 @@
1
+ module SolanaRuby
2
+ class TransactionHelper
3
+ require 'base58'
4
+ require 'pry'
5
+
6
+ # Constants for program IDs
7
+ SYSTEM_PROGRAM_ID = '11111111111111111111111111111111'
8
+ TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
9
+ ASSOCIATED_TOKEN_PROGRAM_ID = 'ATokenGP3evbxxpQ7bYPLNNaxD2c4bqtvWjpKbmz6HjH'
10
+
11
+ INSTRUCTION_LAYOUTS = {
12
+ # Native SOL transfer
13
+ sol_transfer: {
14
+ instruction: :uint32,
15
+ lamports: :near_int64
16
+ },
17
+ # SPL token transfer
18
+ spl_transfer: {
19
+ instruction: :uint8,
20
+ amount: :uint64
21
+ },
22
+ # Create account layout
23
+ create_account: {
24
+ instruction: :uint8,
25
+ lamports: :uint64,
26
+ space: :uint64
27
+ }
28
+ }
29
+
30
+ # Method to create a system account (e.g., for SPL token or SOL)
31
+ def self.create_account(from_pubkey, new_account_pubkey, lamports, space, owner_pubkey = SYSTEM_PROGRAM_ID)
32
+ instruction_data = encode_data(INSTRUCTION_LAYOUTS[:create_account], { instruction: 0, lamports: lamports, space: space })
33
+ create_account_instruction = TransactionInstruction.new(
34
+ keys: [
35
+ { pubkey: from_pubkey, is_signer: true, is_writable: true },
36
+ { pubkey: new_account_pubkey, is_signer: false, is_writable: true },
37
+ { pubkey: owner_pubkey, is_signer: false, is_writable: false }
38
+ ],
39
+ program_id: owner_pubkey,
40
+ data: instruction_data.bytes
41
+ )
42
+ create_account_instruction
43
+ end
44
+
45
+ def self.create_and_sign_transaction(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
46
+ # Create the transaction
47
+ transaction = Transaction.new
48
+ transaction.set_fee_payer(from_pubkey)
49
+ transaction.set_recent_blockhash(recent_blockhash)
50
+
51
+ # Add the create account instruction to the transaction
52
+ create_account_instruction = create_account(from_pubkey, new_account_pubkey, lamports, space)
53
+ transaction.add_instruction(create_account_instruction)
54
+
55
+ # You would then sign the transaction and send it as needed
56
+ # Example: signing and sending the transaction
57
+ transaction
58
+ end
59
+
60
+ # Method to create a SOL transfer instruction
61
+ def self.transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
62
+ fields = INSTRUCTION_LAYOUTS[:sol_transfer]
63
+ data = encode_data(fields, { instruction: 2, lamports: lamports })
64
+ TransactionInstruction.new(
65
+ keys: [
66
+ { pubkey: from_pubkey, is_signer: true, is_writable: true },
67
+ { pubkey: to_pubkey, is_signer: false, is_writable: true }
68
+ ],
69
+ program_id: SYSTEM_PROGRAM_ID,
70
+ data: data
71
+ )
72
+ end
73
+
74
+ # Helper to create a new transaction for SOL transfer
75
+ def self.new_sol_transaction(from_pubkey, to_pubkey, lamports, recent_blockhash)
76
+ transaction = Transaction.new
77
+ transaction.set_fee_payer(from_pubkey)
78
+ transaction.set_recent_blockhash(recent_blockhash)
79
+ transfer_instruction = transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
80
+ transaction.add_instruction(transfer_instruction)
81
+ transaction
82
+ end
83
+
84
+ # Method to create an SPL token transfer instruction
85
+ def self.transfer_spl_token(source, destination, owner, amount)
86
+ fields = INSTRUCTION_LAYOUTS[:spl_transfer]
87
+ data = encode_data(fields, { instruction: 3, amount: amount }) # Instruction type 3: Transfer tokens
88
+ TransactionInstruction.new(
89
+ keys: [
90
+ { pubkey: source, is_signer: false, is_writable: true },
91
+ { pubkey: destination, is_signer: false, is_writable: true },
92
+ { pubkey: owner, is_signer: true, is_writable: false }
93
+ ],
94
+ program_id: TOKEN_PROGRAM_ID,
95
+ data: data
96
+ )
97
+ end
98
+
99
+ # Helper to create a new transaction for SPL token transfer
100
+ def self.new_spl_token_transaction(source, destination, owner, amount, recent_blockhash)
101
+ transaction = Transaction.new
102
+ transaction.set_fee_payer(owner)
103
+ transaction.set_recent_blockhash(recent_blockhash)
104
+ transfer_instruction = transfer_spl_token(source, destination, owner, amount)
105
+ transaction.add_instruction(transfer_instruction)
106
+ transaction
107
+ end
108
+
109
+ # Method to create an associated token account for a given token mint
110
+ def self.create_associated_token_account(from_pubkey, token_mint, owner_pubkey)
111
+ data = [0, 0, 0, 0] # No data required for account creation
112
+ create_account_instruction = TransactionInstruction.new(
113
+ keys: [
114
+ { pubkey: from_pubkey, is_signer: true, is_writable: true },
115
+ { pubkey: owner_pubkey, is_signer: false, is_writable: true },
116
+ { pubkey: token_mint, is_signer: false, is_writable: false },
117
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, is_signer: false, is_writable: false },
118
+ { pubkey: SYSTEM_PROGRAM_ID, is_signer: false, is_writable: false }
119
+ ],
120
+ program_id: ASSOCIATED_TOKEN_PROGRAM_ID,
121
+ data: data
122
+ )
123
+ create_account_instruction
124
+ end
125
+
126
+ # Utility to encode data using predefined layouts
127
+ def self.encode_data(fields, data)
128
+ layout = SolanaRuby::DataTypes::Layout.new(fields)
129
+ layout.serialize(data)
130
+ end
131
+
132
+ # Utility to decode data using predefined layouts
133
+ def self.decode_data(fields, data)
134
+ layout = SolanaRuby::DataTypes::Layout.new(fields)
135
+ layout.deserialize(data)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ module SolanaRuby
2
+ class TransactionInstruction
3
+ require 'base58'
4
+
5
+ attr_accessor :keys, :program_id, :data
6
+
7
+ def initialize(keys:, program_id:, data:)
8
+ @keys = keys # Array of account metadata hashes
9
+ @program_id = program_id # Program ID in Base58
10
+ @data = data # Binary data for the instruction
11
+ end
12
+
13
+ def serialize
14
+ serialized_instruction = ""
15
+
16
+ # Convert and serialize the program ID from Base58 to binary
17
+ program_id_binary = Base58.base58_to_binary(@program_id)
18
+ serialized_instruction << program_id_binary
19
+
20
+ # Serialize the number of keys
21
+ serialized_instruction << [@keys.length].pack("C")
22
+
23
+ # Serialize each key (pubkey in binary, is_signer, is_writable flags)
24
+ @keys.each do |key_meta|
25
+ # Convert public key to binary and serialize it
26
+ pubkey_binary = Base58.base58_to_binary(key_meta[:pubkey])
27
+ serialized_instruction << pubkey_binary
28
+
29
+ # Serialize meta flags (is_signer and is_writable)
30
+ meta_flags = (key_meta[:is_signer] ? 1 : 0) | (key_meta[:is_writable] ? 2 : 0)
31
+ serialized_instruction << [meta_flags].pack("C")
32
+ end
33
+
34
+ # Serialize data length (encoded as a single byte, can adjust with C, S, and L accordingly if data is larger)
35
+ serialized_instruction << [@data.length].pack("C")
36
+
37
+ # Serialize the actual data in binary format
38
+ serialized_instruction << @data
39
+ serialized_instruction
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ require 'base58'
2
+ require 'digest/sha2'
3
+
4
+ module SolanaRuby
5
+ class Utils
6
+ class << self
7
+ # Decodes a length-prefixed byte array using a variable-length encoding.
8
+ def decode_length(bytes)
9
+ raise ArgumentError, "Input must be an array of bytes" unless bytes.is_a?(Array)
10
+
11
+ length = 0
12
+ size = 0
13
+ loop do
14
+ raise "Unexpected end of bytes during length decoding" if bytes.empty?
15
+
16
+ byte = bytes.shift
17
+ length |= (byte & 0x7F) << (size * 7)
18
+ size += 1
19
+ break if (byte & 0x80).zero?
20
+ end
21
+ length
22
+ end
23
+
24
+ # Encodes a length as a variable-length byte array.
25
+ def encode_length(length)
26
+ raise ArgumentError, "Length must be a non-negative integer" unless length.is_a?(Integer) && length >= 0
27
+
28
+ bytes = []
29
+ loop do
30
+ byte = length & 0x7F
31
+ length >>= 7
32
+ if length.zero?
33
+ bytes << byte
34
+ break
35
+ else
36
+ bytes << (byte | 0x80)
37
+ end
38
+ end
39
+ bytes
40
+ end
41
+
42
+ # Converts a byte array to a Base58-encoded string.
43
+ def bytes_to_base58(bytes)
44
+ raise ArgumentError, "Input must be an array of bytes" unless bytes.is_a?(Array)
45
+
46
+ Base58.binary_to_base58(bytes.pack('C*'), :bitcoin)
47
+ end
48
+
49
+ # Converts a Base58-encoded string to a byte array.
50
+ def base58_to_bytes(base58_string)
51
+ raise ArgumentError, "Input must be a non-empty string" unless base58_string.is_a?(String) && !base58_string.empty?
52
+
53
+ Base58.base58_to_binary(base58_string, :bitcoin).bytes
54
+ rescue ArgumentError
55
+ raise "Invalid Base58 string: #{base58_string}"
56
+ end
57
+
58
+ # Computes the SHA-256 hash of the given data and returns it as a hexadecimal string.
59
+ def sha256(data)
60
+ raise ArgumentError, "Data must be a string" unless data.is_a?(String)
61
+
62
+ Digest::SHA256.hexdigest(data)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolanaRuby
4
- VERSION = "1.0.1.beta3"
4
+ VERSION = "2.0.0beta1"
5
5
  end
data/lib/solana_ruby.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "solana_ruby/version"
4
- require_relative "solana_ruby/http_client"
5
- require_relative "solana_ruby/web_socket_client"
3
+ Dir[File.join(__dir__, 'solana_ruby', '*.rb')].each { |file| require file }
6
4
  # Dir["solana_ruby/*.rb"].each { |f| require_relative f.delete(".rb") }
7
-
5
+ require 'pry'
8
6
  module SolanaRuby
9
7
  class Error < StandardError; end
10
8
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/*.rb')].each { |file| require file }
4
+ Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/**/*.rb')].each { |file| require file }
5
+
6
+ # Testing Script
7
+
8
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
9
+
10
+ # Fetch the recent blockhash
11
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
12
+
13
+ # Generate a sender keypair and public key
14
+ sender_keypair = SolanaRuby::Keypair.from_private_key("d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778")
15
+ sender_pubkey = sender_keypair[:public_key]
16
+ lamports = 1 * 1_000_000_000
17
+ space = 165
18
+ balance = client.get_balance(sender_pubkey)
19
+ puts "sender account balance: #{balance}, wait for few seconds to update the balance in solana when the balance 0"
20
+
21
+
22
+ # Generate a receiver keypair and public key
23
+ new_account = SolanaRuby::Keypair.generate
24
+ new_account_pubkey = new_account[:public_key]
25
+
26
+ # create a transaction instruction
27
+ transaction = SolanaRuby::TransactionHelper.create_and_sign_transaction(sender_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
28
+
29
+ signed_transaction = transaction.sign([sender_keypair])
30
+ sleep(5)
31
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
32
+ puts "Response: #{response}"
@@ -0,0 +1,54 @@
1
+ require 'pry'
2
+
3
+ # SOL Transfer Testing Script
4
+
5
+ # Initialize the Solana client
6
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
7
+
8
+ # Fetch the recent blockhash
9
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
10
+
11
+ # Generate a sender keypair and public key or Fetch payers keypair using private key
12
+ # sender_keypair = SolanaRuby::Keypair.from_private_key("InsertPrivateKeyHere")
13
+ sender_keypair = SolanaRuby::Keypair.generate
14
+ sender_pubkey = sender_keypair[:public_key]
15
+
16
+
17
+ # Airdrop some lamports to the sender's account
18
+ lamports = 10 * 1_000_000_000
19
+ sleep(1)
20
+ result = client.request_airdrop(sender_pubkey, lamports)
21
+ puts "Solana Balance #{lamports} lamports added sucessfully for the public key: #{sender_pubkey}"
22
+ sleep(10)
23
+
24
+
25
+ # Generate or existing receiver keypair and public key
26
+ keypair = SolanaRuby::Keypair.generate # generate receiver keypair
27
+ receiver_pubkey = keypair[:public_key]
28
+ # receiver_pubkey = 'InsertExistingPublicKeyHere'
29
+
30
+ transfer_lamports = 1 * 1_000_000
31
+ puts "Payer's full private key: #{sender_keypair[:full_private_key]}"
32
+ puts "Receiver's full private key: #{keypair[:full_private_key]}"
33
+ puts "Receiver's Public Key: #{keypair[:public_key]}"
34
+
35
+ # Create a new transaction
36
+ transaction = SolanaRuby::TransactionHelper.new_sol_transaction(
37
+ sender_pubkey,
38
+ receiver_pubkey,
39
+ transfer_lamports,
40
+ recent_blockhash
41
+ )
42
+
43
+ # Get the sender's private key (ensure it's a string)
44
+ private_key = sender_keypair[:private_key]
45
+ puts "Private key type: #{private_key.class}, Value: #{private_key.inspect}"
46
+
47
+ # Sign the transaction
48
+ signed_transaction = transaction.sign([sender_keypair])
49
+
50
+ # Send the transaction to the Solana network
51
+ sleep(5)
52
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
53
+ puts "Response: #{response}"
54
+
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/*.rb')].each { |file| require file }
4
+ Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/**/*.rb')].each { |file| require file }
5
+ # Dir["solana_ruby/*.rb"].each { |f| require_relative f.delete(".rb") }
6
+
7
+
8
+ # Testing Script
9
+
10
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
11
+
12
+ # Fetch the recent blockhash
13
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
14
+
15
+ # Generate a sender keypair and public key
16
+ fee_payer = SolanaRuby::Keypair.from_private_key("d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778")
17
+ fee_payer_pubkey = fee_payer[:public_key]
18
+ lamports = 10 * 1_000_000_000
19
+ space = 165
20
+
21
+ # get balance for the fee payer
22
+ balance = client.get_balance(fee_payer_pubkey)
23
+ puts "sender account balance: #{balance}, wait for few seconds to update the balance in solana when the balance 0"
24
+
25
+
26
+ # # Generate a receiver keypair and public key
27
+ keypair = SolanaRuby::Keypair.generate
28
+ receiver_pubkey = keypair[:public_key]
29
+ transfer_lamports = 1 * 1_000_000
30
+ # puts "Payer's full private key: #{sender_keypair[:full_private_key]}"
31
+ # # puts "Receiver's full private key: #{keypair[:full_private_key]}"
32
+ # # puts "Receiver's Public Key: #{keypair[:public_key]}"
33
+ mint_address = '9BvJGQC5FkLJzUC2TmYpi1iU8n9vt2388GLT5zvu8S1G'
34
+ token_program_id = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
35
+
36
+ # Create a new transaction
37
+ transaction = SolanaRuby::TransactionHelper.new_spl_token_transaction(
38
+ "9BvJGQC5FkLJzUC2TmYpi1iU8n9vt2388GLT5zvu8S1G",
39
+ receiver_pubkey,
40
+ fee_payer_pubkey,
41
+ transfer_lamports,
42
+ recent_blockhash
43
+ )
44
+ # # Get the sender's private key (ensure it's a string)
45
+ private_key = fee_payer[:private_key]
46
+ puts "Private key type: #{private_key.class}, Value: #{private_key.inspect}"
47
+
48
+ # Sign the transaction
49
+ signed_transaction = transaction.sign([fee_payer])
50
+
51
+ # Send the transaction to the Solana network
52
+ sleep(5)
53
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
54
+ puts "Response: #{response}"
55
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solana-ruby-web3js
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1.beta3
4
+ version: 2.0.0beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - BuildSquad
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-03 00:00:00.000000000 Z
11
+ date: 2024-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-client-simple
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rbnacl
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ed25519
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: brakeman
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -201,6 +229,12 @@ files:
201
229
  - lib/solana_ruby.rb
202
230
  - lib/solana_ruby/.DS_Store
203
231
  - lib/solana_ruby/base_client.rb
232
+ - lib/solana_ruby/data_types.rb
233
+ - lib/solana_ruby/data_types/blob.rb
234
+ - lib/solana_ruby/data_types/layout.rb
235
+ - lib/solana_ruby/data_types/near_int64.rb
236
+ - lib/solana_ruby/data_types/sequence.rb
237
+ - lib/solana_ruby/data_types/unsigned_int.rb
204
238
  - lib/solana_ruby/http_client.rb
205
239
  - lib/solana_ruby/http_methods/account_methods.rb
206
240
  - lib/solana_ruby/http_methods/basic_methods.rb
@@ -211,6 +245,12 @@ files:
211
245
  - lib/solana_ruby/http_methods/slot_methods.rb
212
246
  - lib/solana_ruby/http_methods/token_methods.rb
213
247
  - lib/solana_ruby/http_methods/transaction_methods.rb
248
+ - lib/solana_ruby/keypair.rb
249
+ - lib/solana_ruby/message.rb
250
+ - lib/solana_ruby/transaction.rb
251
+ - lib/solana_ruby/transaction_helper.rb
252
+ - lib/solana_ruby/transaction_instruction.rb
253
+ - lib/solana_ruby/utils.rb
214
254
  - lib/solana_ruby/version.rb
215
255
  - lib/solana_ruby/web_socket_client.rb
216
256
  - lib/solana_ruby/web_socket_handlers.rb
@@ -219,6 +259,9 @@ files:
219
259
  - lib/solana_ruby/web_socket_methods/root_methods.rb
220
260
  - lib/solana_ruby/web_socket_methods/signature_methods.rb
221
261
  - lib/solana_ruby/web_socket_methods/slot_methods.rb
262
+ - transaction_testing/create_account.rb
263
+ - transaction_testing/sol_transfer.rb
264
+ - transaction_testing/spl_token_transfer.rb
222
265
  homepage: https://github.com/Build-Squad/solana-ruby
223
266
  licenses:
224
267
  - MIT
@@ -241,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
284
  - !ruby/object:Gem::Version
242
285
  version: '0'
243
286
  requirements: []
244
- rubygems_version: 3.5.20
287
+ rubygems_version: 3.5.23
245
288
  signing_key:
246
289
  specification_version: 4
247
290
  summary: Solana Ruby SDK