solana-ruby-web3js 2.0.0 → 2.0.1

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: 9f614bab0b21438c2820d0035a0aa33567a11fedd3ecb998666b39e5ab28681d
4
- data.tar.gz: c63fd671d6328732c39994032f94aff219ff77eda8cd899c5d1f0c29f86f4524
3
+ metadata.gz: 83aeed228731c11fa62b87f4ab5761a6715754921effc58400764bbe43378409
4
+ data.tar.gz: a0cd63b8a7998539304ce06ed69692e86361fece542033d63fab7383a692ebb3
5
5
  SHA512:
6
- metadata.gz: 4015bdc6179d57105ea1da82daa9d6ec19f623098d636271fb0e7ab9210d563414eae364271e58be82a2ce9ed6dc0804de53db7e7a15c5d353da4ff30faf5124
7
- data.tar.gz: 8fca9ec2ccdcc5b3ea72f54fa6fad27fee3fe1aef20b145107382c523a0a70df90812f269fdcdee3d9d62b732e6ebe7ce6ad3e1514257d379db7088de55941d1
6
+ metadata.gz: 29277d937c3201916bd0bd1eec7172621a4a83b2b21d5045afa5a285f4d757ba12de5f99031da0f0030e08e68adc4c7de1cb6a55301463cfb05f0b5e63072ffc
7
+ data.tar.gz: 0b627b741396f666e92fe8b032d859e944c933eda6b8ac7da29201d7913f116195b0751bcc2247537ca3a2b461bff9d2999ac234669e56a2708efe39776d881d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- solana-ruby-web3js (1.0.1.beta3)
4
+ solana-ruby-web3js (2.0.0beta1)
5
5
  base58 (~> 0.2.3)
6
6
  base64 (~> 0.2.0)
7
7
  ed25519
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 Mainnet, 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 when needed.
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
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.sol_transfer(
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
+
@@ -4,26 +4,26 @@ module SolanaRuby
4
4
  attr_reader :size
5
5
 
6
6
  BITS = {
7
- 8 => { directive: 'C*', size: 1 },
8
- 32 => { directive: 'L*', size: 4 },
9
- 64 => { directive: 'Q*', size: 8 }
7
+ 8 => { directive: 'C', size: 1 }, # 8-bit unsigned integer
8
+ 32 => { directive: 'L<', size: 4 }, # 32-bit little-endian unsigned integer
9
+ 64 => { directive: 'Q<', size: 8 } # 64-bit little-endian unsigned integer
10
10
  }
11
11
 
12
12
  def initialize(bits)
13
13
  @bits = bits
14
14
  type = BITS[@bits]
15
- raise "Can only fit #{BITS.keys}" unless type
15
+ raise "Unsupported size. Supported sizes: #{BITS.keys.join(', ')} bits" unless type
16
16
  @size = type[:size]
17
17
  @directive = type[:directive]
18
18
  end
19
19
 
20
- # Serialize the unsigned integer into bytes
20
+ # Serialize the unsigned integer into properly aligned bytes
21
21
  def serialize(obj)
22
22
  raise "Can only serialize integers" unless obj.is_a?(Integer)
23
- raise "Cannot serialize negative integers" if obj < 0
23
+ raise "Cannot serialize negative integers" if obj.negative?
24
24
 
25
25
  if obj >= 256**@size
26
- raise "Integer too large (does not fit in #{@size} bytes)"
26
+ raise "Integer too large to fit in #{@size} bytes"
27
27
  end
28
28
 
29
29
  [obj].pack(@directive).bytes
@@ -31,7 +31,7 @@ module SolanaRuby
31
31
 
32
32
  # Deserialize bytes into the unsigned integer
33
33
  def deserialize(bytes)
34
- raise "Invalid serialization (wrong size)" if bytes.size != @size
34
+ raise "Invalid serialization (expected #{@size} bytes, got #{bytes.size})" if bytes.size != @size
35
35
 
36
36
  bytes.pack('C*').unpack(@directive).first
37
37
  end
@@ -21,5 +21,9 @@ module SolanaRuby
21
21
  def blob1
22
22
  Blob.new(1)
23
23
  end
24
+
25
+ def blob32
26
+ Blob.new(32)
27
+ end
24
28
  end
25
29
  end
@@ -1,3 +1,5 @@
1
+ Dir[File.join(__dir__, 'data_types', '*.rb')].each { |file| require file }
2
+
1
3
  module SolanaRuby
2
4
  class Transaction
3
5
  require 'rbnacl'
@@ -70,10 +72,10 @@ module SolanaRuby
70
72
  instructions.push(item)
71
73
  end
72
74
 
73
- def sign(keys)
74
- raise 'No signers' unless keys.any?
75
+ def sign(keypairs)
76
+ raise 'No signers' unless keypairs.any?
75
77
 
76
- keys = keys.uniq{ |k| key[:public_key] }
78
+ keys = keypairs.uniq { |kp| kp[:public_key] }
77
79
  @signatures = keys.map do |key|
78
80
  {
79
81
  signature: nil,
@@ -114,15 +116,17 @@ module SolanaRuby
114
116
  def compile_message
115
117
  check_for_errors
116
118
  fetch_message_data
117
- message = Message.new(
118
- header: {
119
- num_required_signatures: @num_required_signatures,
120
- num_readonly_signed_accounts: @num_readonly_signed_accounts,
121
- num_readonly_unsigned_accounts: @num_readonly_unsigned_accounts,
122
- },
123
- account_keys: @account_keys, recent_blockhash: recent_blockhash, instructions: @instructs
119
+
120
+ # add instruction structure
121
+ instructs = add_instructs
122
+
123
+ message_data = Message.new(
124
+ header: @header,
125
+ account_keys: @account_keys,
126
+ recent_blockhash: recent_blockhash,
127
+ instructions: instructs
124
128
  )
125
- message
129
+ message_data
126
130
  end
127
131
 
128
132
  def check_for_errors
@@ -168,11 +172,8 @@ module SolanaRuby
168
172
  # Split out signing from non-signing keys and count header values
169
173
  signed_keys = []
170
174
  unsigned_keys = []
171
- header_params = split_keys(unique_metas, signed_keys, unsigned_keys)
175
+ @header = split_keys(unique_metas, signed_keys, unsigned_keys)
172
176
  @account_keys = signed_keys + unsigned_keys
173
-
174
- # add instruction structure
175
- @instructs = add_instructs
176
177
  end
177
178
 
178
179
  def append_program_id(program_ids, account_metas)
@@ -245,19 +246,24 @@ module SolanaRuby
245
246
  end
246
247
 
247
248
  def split_keys(unique_metas, signed_keys, unsigned_keys)
248
- @num_required_signatures = 0
249
- @num_readonly_signed_accounts = 0
250
- @num_readonly_unsigned_accounts = 0
249
+ num_required_signatures = 0
250
+ num_readonly_signed_accounts = 0
251
+ num_readonly_unsigned_accounts = 0
251
252
  unique_metas.each do |meta|
252
253
  if meta[:is_signer]
253
254
  signed_keys.push(meta[:pubkey])
254
- @num_required_signatures += 1
255
- @num_readonly_signed_accounts += 1 if (!meta[:is_writable])
255
+ num_required_signatures += 1
256
+ num_readonly_signed_accounts += 1 if (!meta[:is_writable])
256
257
  else
257
258
  unsigned_keys.push(meta[:pubkey])
258
- @num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
259
+ num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
259
260
  end
260
261
  end
262
+ {
263
+ num_required_signatures: num_required_signatures,
264
+ num_readonly_signed_accounts: num_readonly_signed_accounts,
265
+ num_readonly_unsigned_accounts: num_readonly_unsigned_accounts,
266
+ }
261
267
  end
262
268
 
263
269
  def partial_sign(message, keys)
@@ -19,46 +19,59 @@ module SolanaRuby
19
19
  instruction: :uint8,
20
20
  amount: :uint64
21
21
  },
22
- # Create account layout
22
+ # Create account layout
23
23
  create_account: {
24
- instruction: :uint8,
24
+ instruction: :uint32,
25
25
  lamports: :uint64,
26
- space: :uint64
26
+ space: :uint64,
27
+ program_id: :blob32
27
28
  }
28
29
  }
29
30
 
30
31
  # 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 })
32
+ def self.account_instruction(from_pubkey, new_account_pubkey, lamports, space, program_id)
33
+ # Encode the instruction data
34
+ instruction_data = encode_data(
35
+ INSTRUCTION_LAYOUTS[:create_account],
36
+ {
37
+ instruction: 0, # '0' corresponds to the Create Account instruction
38
+ lamports: lamports, # The amount of lamports to transfer to the new account
39
+ space: space, # Amount of space allocated for the account's data
40
+ program_id: Base58.base58_to_binary(program_id, :bitcoin).bytes # Convert public key to binary
41
+ }
42
+ )
43
+
44
+ # Construct the transaction instruction
33
45
  create_account_instruction = TransactionInstruction.new(
34
46
  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 }
47
+ { pubkey: from_pubkey, is_signer: true, is_writable: true }, # Funder's account
48
+ { pubkey: new_account_pubkey, is_signer: true, is_writable: true } # New account
38
49
  ],
39
- program_id: owner_pubkey,
40
- data: instruction_data.bytes
50
+ program_id: program_id, # Use Solana's system program for account creation
51
+ data: instruction_data # Encoded instruction data
41
52
  )
53
+
54
+ # return instruction data
42
55
  create_account_instruction
43
56
  end
44
57
 
45
- def self.create_and_sign_transaction(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
58
+
59
+ def self.create_account(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash, program_id = SYSTEM_PROGRAM_ID)
46
60
  # Create the transaction
47
61
  transaction = Transaction.new
48
62
  transaction.set_fee_payer(from_pubkey)
49
63
  transaction.set_recent_blockhash(recent_blockhash)
50
64
 
51
65
  # 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
66
+ instruction = account_instruction(from_pubkey, new_account_pubkey, lamports, space, program_id)
67
+ transaction.add_instruction(instruction)
68
+
69
+ # return the transaction for signing
57
70
  transaction
58
71
  end
59
72
 
60
73
  # Method to create a SOL transfer instruction
61
- def self.transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
74
+ def self.transfer_sol_instruction(from_pubkey, to_pubkey, lamports)
62
75
  fields = INSTRUCTION_LAYOUTS[:sol_transfer]
63
76
  data = encode_data(fields, { instruction: 2, lamports: lamports })
64
77
  TransactionInstruction.new(
@@ -72,11 +85,11 @@ module SolanaRuby
72
85
  end
73
86
 
74
87
  # Helper to create a new transaction for SOL transfer
75
- def self.new_sol_transaction(from_pubkey, to_pubkey, lamports, recent_blockhash)
88
+ def self.sol_transfer(from_pubkey, to_pubkey, lamports, recent_blockhash)
76
89
  transaction = Transaction.new
77
90
  transaction.set_fee_payer(from_pubkey)
78
91
  transaction.set_recent_blockhash(recent_blockhash)
79
- transfer_instruction = transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
92
+ transfer_instruction = transfer_sol_instruction(from_pubkey, to_pubkey, lamports)
80
93
  transaction.add_instruction(transfer_instruction)
81
94
  transaction
82
95
  end
@@ -107,13 +120,14 @@ module SolanaRuby
107
120
  end
108
121
 
109
122
  # 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)
123
+ def self.create_associated_token_account(payer, mint, owner)
111
124
  data = [0, 0, 0, 0] # No data required for account creation
112
125
  create_account_instruction = TransactionInstruction.new(
113
126
  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 },
127
+ { pubkey: payer, is_signer: true, is_writable: true },
128
+ { pubkey: associated_token, is_signer: false, is_writable: true },
129
+ { pubkey: owner, is_signer: false, is_writable: false },
130
+ { pubkey: mint, is_signer: false, is_writable: false },
117
131
  { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, is_signer: false, is_writable: false },
118
132
  { pubkey: SYSTEM_PROGRAM_ID, is_signer: false, is_writable: false }
119
133
  ],
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolanaRuby
4
- VERSION = "2.0.0"
4
+ VERSION = "2.0.1"
5
5
  end
@@ -3,30 +3,55 @@
3
3
  Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/*.rb')].each { |file| require file }
4
4
  Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/**/*.rb')].each { |file| require file }
5
5
 
6
- # Testing Script
7
-
6
+ # Initialize Solana client
8
7
  client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
9
8
 
10
9
  # Fetch the recent blockhash
11
10
  recent_blockhash = client.get_latest_blockhash["blockhash"]
11
+ puts "Recent Blockhash: #{recent_blockhash}"
12
12
 
13
- # Generate a sender keypair and public key
14
- sender_keypair = SolanaRuby::Keypair.from_private_key("d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778")
13
+ # Sender keypair and public key
14
+ private_key = "d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778"
15
+ sender_keypair = SolanaRuby::Keypair.from_private_key(private_key)
15
16
  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"
17
+ puts "Sender Public Key: #{sender_pubkey}"
20
18
 
19
+ # Check sender's account balance
20
+ balance = client.get_balance(sender_pubkey)
21
+ puts "Sender account balance: #{balance} lamports"
22
+ if balance == 0
23
+ puts "Balance is zero, waiting for balance update..."
24
+ sleep(10)
25
+ end
21
26
 
22
- # Generate a receiver keypair and public key
27
+ # new keypair and public key (new account)
23
28
  new_account = SolanaRuby::Keypair.generate
24
29
  new_account_pubkey = new_account[:public_key]
30
+ puts "New Account Public Key: #{new_account_pubkey}"
31
+
32
+ # Parameters for account creation
33
+ lamports = 1 * 1_000_000_000 # Lamports to transfer
34
+ space = 165 # Space allocation (bytes)
35
+ program_id = SolanaRuby::TransactionHelper::SYSTEM_PROGRAM_ID
36
+
37
+ # Create and sign the transaction
38
+ transaction = SolanaRuby::TransactionHelper.create_account(
39
+ sender_pubkey,
40
+ new_account_pubkey,
41
+ lamports,
42
+ space,
43
+ recent_blockhash,
44
+ program_id
45
+ )
46
+
47
+ # Sign transaction with both sender and new account keypairs
48
+ transaction.sign([sender_keypair, new_account])
49
+
50
+ # Send the transaction
51
+ puts "Sending transaction..."
52
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
25
53
 
26
- # create a transaction instruction
27
- transaction = SolanaRuby::TransactionHelper.create_and_sign_transaction(sender_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
54
+ # Output transaction results
55
+ puts "Transaction Signature: #{response}"
56
+ puts "New account created successfully with Public Key: #{new_account_pubkey}"
28
57
 
29
- signed_transaction = transaction.sign([sender_keypair])
30
- sleep(5)
31
- response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
32
- puts "Response: #{response}"
@@ -35,7 +35,7 @@ puts "Receiver's full private key: #{keypair[:full_private_key]}"
35
35
  puts "Receiver's Public Key: #{keypair[:public_key]}"
36
36
 
37
37
  # Create a new transaction
38
- transaction = SolanaRuby::TransactionHelper.new_sol_transaction(
38
+ transaction = SolanaRuby::TransactionHelper.sol_transfer(
39
39
  sender_pubkey,
40
40
  receiver_pubkey,
41
41
  transfer_lamports,
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: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - BuildSquad
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-26 00:00:00.000000000 Z
11
+ date: 2024-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-client-simple