solana-ruby-web3js 2.0.0 → 2.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f614bab0b21438c2820d0035a0aa33567a11fedd3ecb998666b39e5ab28681d
4
- data.tar.gz: c63fd671d6328732c39994032f94aff219ff77eda8cd899c5d1f0c29f86f4524
3
+ metadata.gz: d2bd60b642f794a5923a3ff1d1b39e29e60aca6444445b0cf2e36e7393b18ff3
4
+ data.tar.gz: 95a61aab91ec4eaacb94fdc671027a2005b320b938a7a2132dc0daf9b643d45b
5
5
  SHA512:
6
- metadata.gz: 4015bdc6179d57105ea1da82daa9d6ec19f623098d636271fb0e7ab9210d563414eae364271e58be82a2ce9ed6dc0804de53db7e7a15c5d353da4ff30faf5124
7
- data.tar.gz: 8fca9ec2ccdcc5b3ea72f54fa6fad27fee3fe1aef20b145107382c523a0a70df90812f269fdcdee3d9d62b732e6ebe7ce6ad3e1514257d379db7088de55941d1
6
+ metadata.gz: 85acebeaeaebce2d39fe74f7d931a7e339e1a773d73f4774003a06a84bed4f94343b4f41269ac4bd1b2c7fbd186145ccba7d8d25b35b9aadee9a2b9f9a56c06e
7
+ data.tar.gz: 68b1d9bf25f64d1424e660387cc802e12842ea428547c4d4643b23489f2dab3db75c4d63a35d23159521b41634b11aef0befa9fa3193c2cf275f14f22412b70f
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.1)
5
5
  base58 (~> 0.2.3)
6
6
  base64 (~> 0.2.0)
7
7
  ed25519
data/README.md CHANGED
@@ -332,3 +332,134 @@ 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
+
407
+ ### Account Creation
408
+
409
+ The create_account helper allows creating a new account with specified parameters. This is commonly used to set up accounts for tokens, programs, or other allocations on the Solana blockchain.
410
+
411
+ #### Requirements:
412
+
413
+ - **Payer Public key**: The public key of the account funding the creation.
414
+ - **New Account Public Key**: The public key of the account to be created.
415
+ - **Lamports**: The amount of lamports to transfer to the new account.
416
+ - **Space**: The amount of space (in bytes) to allocate for the new account.
417
+ - **Recent Blockhash**: The latest blockhash for the transaction.
418
+ - **Program Id**: The program ID associated with the new account (default: System Program).
419
+
420
+ #### Example Usage:
421
+
422
+ require 'solana_ruby'
423
+
424
+ # Initialize the client (defaults to Mainnet(https://api.mainnet-beta.solana.com))
425
+ client = SolanaRuby::HttpClient.new('https://api.devnet.solana.com')
426
+
427
+ # Fetch the recent blockhash
428
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
429
+
430
+ # Generate or fetch the sender/payer keypair
431
+ # Option 1: Generate a new keypair
432
+ sender_keypair = SolanaRuby::Keypair.generate
433
+ # Option 2: Use an existing private key
434
+ # sender_keypair = SolanaRuby::Keypair.from_private_key("InsertPrivateKeyHere")
435
+ sender_pubkey = sender_keypair[:public_key]
436
+
437
+ # Generate new account keypair
438
+ new_account = SolanaRuby::Keypair.generate
439
+ new_account_pubkey = new_account[:public_key]
440
+
441
+ # Parameters for account creation
442
+ lamports = 1_000_000_000
443
+ space = 165
444
+ program_id = SolanaRuby::TransactionHelper::SYSTEM_PROGRAM_ID
445
+
446
+ # Create the account creation transaction
447
+ transaction = SolanaRuby::TransactionHelper.create_account(
448
+ sender_pubkey,
449
+ new_account_pubkey,
450
+ lamports,
451
+ space,
452
+ recent_blockhash,
453
+ program_id
454
+ )
455
+
456
+ # Sign with both keypairs
457
+ transaction.sign([sender_keypair, new_account])
458
+
459
+ # Send the transaction
460
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
461
+
462
+ # Output transaction results
463
+ puts "Transaction Signature: #{response}"
464
+ puts "New account created with Public Key: #{new_account_pubkey}"
465
+
@@ -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
@@ -0,0 +1,70 @@
1
+ module SolanaRuby
2
+ class Ed25519CurveChecker
3
+ # Constants for Ed25519
4
+ P = 2**255 - 19
5
+ ONE = 1
6
+
7
+ # Main function to check if a public key is on the Ed25519 curve
8
+ def self.on_curve?(public_key_bytes)
9
+ # Validate public key length
10
+ return false unless public_key_bytes.bytesize == 32 # Public key must be 32 bytes
11
+
12
+ begin
13
+ # Decode the y-coordinate from the public key
14
+ y = decode_y(public_key_bytes)
15
+
16
+ # Validate if y is a quadratic residue on the curve equation
17
+ y_squared = (y * y) % P
18
+ numerator = (y_squared - 1) % P
19
+ denominator = (D * y_squared + 1) % P
20
+
21
+ # Ensure denominator isn't zero to avoid invalid computation
22
+ return false if denominator.zero?
23
+
24
+ # Calculate x_squared = numerator * modular_inverse(denominator, P) mod P
25
+ x_squared = (numerator * modular_inverse(denominator, P)) % P
26
+
27
+ # Check if x_squared is a valid quadratic residue
28
+ quadratic_residue?(x_squared)
29
+ rescue StandardError => e
30
+ puts "Error during curve check: #{e.message}"
31
+ false
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Decode the y-coordinate from the public key
38
+ def self.decode_y(public_key_bytes)
39
+ # Converts byte array directly to integer and maps it onto the curve's modulus
40
+ public_key_bytes.unpack1('H*').to_i(16) % P
41
+ end
42
+
43
+ # Determine if value is a quadratic residue modulo P
44
+ def self.quadratic_residue?(value)
45
+ # Quadratic residues satisfy value^((p - 1) / 2) mod P == 1
46
+ value.pow((P - 1) / 2, P) == 1
47
+ end
48
+
49
+ # Modular inverse using the Extended Euclidean Algorithm
50
+ def self.modular_inverse(value, mod_value)
51
+ t, new_t = 0, 1
52
+ r, new_r = mod_value, value
53
+
54
+ while new_r != 0
55
+ quotient = r / new_r
56
+ t, new_t = new_t, t - quotient * new_t
57
+ r, new_r = new_r, r - quotient * new_r
58
+ end
59
+
60
+ raise ArgumentError, 'Value has no modular inverse' if r > 1
61
+
62
+ t += mod_value if t.negative?
63
+ t % mod_value
64
+ end
65
+ end
66
+
67
+ # Calculate the Ed25519 constant D
68
+ # D = -121665 * modular_inverse(121666, P) mod P
69
+ D = (-121665 * Ed25519CurveChecker.modular_inverse(121666, Ed25519CurveChecker::P)) % Ed25519CurveChecker::P
70
+ end
@@ -6,16 +6,9 @@ module SolanaRuby
6
6
  # Generates a new Ed25519 keypair
7
7
  def self.generate
8
8
  signing_key = RbNaCl::Signatures::Ed25519::SigningKey.generate
9
- public_key_bytes = signing_key.verify_key.to_bytes # Binary format for public key
10
9
  private_key_bytes = signing_key.to_bytes
11
- private_key_hex = private_key_bytes.unpack1('H*') # Hex format for private key
12
10
 
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
- }
11
+ keys(signing_key, private_key_bytes)
19
12
  end
20
13
 
21
14
  # Restores a keypair from a private key in hex format
@@ -28,8 +21,39 @@ module SolanaRuby
28
21
  # Initialize signing key
29
22
  signing_key = RbNaCl::Signatures::Ed25519::SigningKey.new(private_key_bytes)
30
23
 
24
+ keys(signing_key, private_key_bytes)
25
+ end
26
+
27
+ # Load a keypair from a JSON file
28
+ def self.load_keypair(file_path)
29
+ # Parse the JSON file
30
+ keypair_data = JSON.parse(File.read(file_path))
31
+
32
+ # Ensure it contains exactly 64 bytes for Ed25519 (32 private + 32 public)
33
+ raise "Invalid keypair length: expected 64 bytes, got #{keypair_data.length}" unless keypair_data.length == 64
34
+
35
+ # Convert the array to a binary string
36
+ private_key_bytes = keypair_data[0, 32].pack('C*')
37
+ public_key_bytes = keypair_data[32, 32].pack('C*')
38
+
39
+ # Create the signing key
40
+ signing_key = RbNaCl::Signatures::Ed25519::SigningKey.new(private_key_bytes)
41
+
42
+ # Verify the public key matches
43
+ raise "Public key mismatch" unless signing_key.verify_key.to_bytes == public_key_bytes
44
+
45
+ keys(signing_key, private_key_bytes)
46
+ rescue JSON::ParserError => e
47
+ raise "Failed to parse JSON file: #{e.message}"
48
+ end
49
+
50
+
51
+ private
52
+
53
+ def self.keys(signing_key, private_key_bytes)
31
54
  # Extract public key in binary format
32
55
  public_key_bytes = signing_key.verify_key.to_bytes
56
+ private_key_hex = private_key_bytes.unpack1('H*') # Hex format for private key
33
57
 
34
58
  # Return public key in Base58 format and private key in hex format
35
59
  {
@@ -0,0 +1,62 @@
1
+ require 'base58'
2
+ require 'openssl'
3
+
4
+ module SolanaRuby
5
+ class PublicKey
6
+ PUBLIC_KEY_LENGTH = 32
7
+
8
+ attr_reader :bn
9
+
10
+ def initialize(value)
11
+ case value
12
+ when PublicKey
13
+ @bn = value.bn
14
+ when String
15
+ decoded = decode_base58(value)
16
+ validate_length(decoded)
17
+ @bn = to_bn(decoded)
18
+ when Array
19
+ binary = value.pack('C*')
20
+ validate_length(binary)
21
+ @bn = to_bn(binary)
22
+ else
23
+ raise ArgumentError, "Unsupported input type: #{value.class}"
24
+ end
25
+ end
26
+
27
+ # Converts the public key to Base58
28
+ def to_base58
29
+ Base58.binary_to_base58(to_bytes, :bitcoin)
30
+ end
31
+
32
+ # Converts the public key to a binary string
33
+ def to_bytes
34
+ padded_bn = @bn.to_s(2) # Binary string from BigNum
35
+ if padded_bn.bytesize < PUBLIC_KEY_LENGTH
36
+ "\x00" * (PUBLIC_KEY_LENGTH - padded_bn.bytesize) + padded_bn
37
+ elsif padded_bn.bytesize > PUBLIC_KEY_LENGTH
38
+ raise "PublicKey byte length exceeds #{PUBLIC_KEY_LENGTH} bytes"
39
+ else
40
+ padded_bn
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def decode_base58(value)
47
+ Base58.base58_to_binary(value, :bitcoin)
48
+ rescue ArgumentError => e
49
+ raise ArgumentError, "Invalid Base58 encoding: #{e.message}"
50
+ end
51
+
52
+ def validate_length(data)
53
+ unless data.bytesize == PUBLIC_KEY_LENGTH
54
+ raise ArgumentError, "Invalid public key length: expected #{PUBLIC_KEY_LENGTH} bytes, got #{data.bytesize}"
55
+ end
56
+ end
57
+
58
+ def to_bn(input)
59
+ OpenSSL::BN.new(input, 2)
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,6 @@
1
+ Dir[File.join(__dir__, 'data_types', '*.rb')].each { |file| require file }
2
+ Dir[File.join(__dir__, 'transaction_helpers', '*.rb')].each { |file| require file }
3
+
1
4
  module SolanaRuby
2
5
  class Transaction
3
6
  require 'rbnacl'
@@ -70,10 +73,10 @@ module SolanaRuby
70
73
  instructions.push(item)
71
74
  end
72
75
 
73
- def sign(keys)
74
- raise 'No signers' unless keys.any?
76
+ def sign(keypairs)
77
+ raise 'No signers' unless keypairs.any?
75
78
 
76
- keys = keys.uniq{ |k| key[:public_key] }
79
+ keys = keypairs.uniq { |kp| kp[:public_key] }
77
80
  @signatures = keys.map do |key|
78
81
  {
79
82
  signature: nil,
@@ -114,15 +117,17 @@ module SolanaRuby
114
117
  def compile_message
115
118
  check_for_errors
116
119
  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
120
+
121
+ # add instruction structure
122
+ instructs = add_instructs
123
+
124
+ message_data = Message.new(
125
+ header: @header,
126
+ account_keys: @account_keys,
127
+ recent_blockhash: recent_blockhash,
128
+ instructions: instructs
124
129
  )
125
- message
130
+ message_data
126
131
  end
127
132
 
128
133
  def check_for_errors
@@ -168,11 +173,8 @@ module SolanaRuby
168
173
  # Split out signing from non-signing keys and count header values
169
174
  signed_keys = []
170
175
  unsigned_keys = []
171
- header_params = split_keys(unique_metas, signed_keys, unsigned_keys)
176
+ @header = split_keys(unique_metas, signed_keys, unsigned_keys)
172
177
  @account_keys = signed_keys + unsigned_keys
173
-
174
- # add instruction structure
175
- @instructs = add_instructs
176
178
  end
177
179
 
178
180
  def append_program_id(program_ids, account_metas)
@@ -245,19 +247,24 @@ module SolanaRuby
245
247
  end
246
248
 
247
249
  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
250
+ num_required_signatures = 0
251
+ num_readonly_signed_accounts = 0
252
+ num_readonly_unsigned_accounts = 0
251
253
  unique_metas.each do |meta|
252
254
  if meta[:is_signer]
253
255
  signed_keys.push(meta[:pubkey])
254
- @num_required_signatures += 1
255
- @num_readonly_signed_accounts += 1 if (!meta[:is_writable])
256
+ num_required_signatures += 1
257
+ num_readonly_signed_accounts += 1 if (!meta[:is_writable])
256
258
  else
257
259
  unsigned_keys.push(meta[:pubkey])
258
- @num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
260
+ num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
259
261
  end
260
262
  end
263
+ {
264
+ num_required_signatures: num_required_signatures,
265
+ num_readonly_signed_accounts: num_readonly_signed_accounts,
266
+ num_readonly_unsigned_accounts: num_readonly_unsigned_accounts,
267
+ }
261
268
  end
262
269
 
263
270
  def partial_sign(message, keys)
@@ -1,12 +1,13 @@
1
1
  module SolanaRuby
2
2
  class TransactionHelper
3
3
  require 'base58'
4
- require 'pry'
5
4
 
6
5
  # Constants for program IDs
7
6
  SYSTEM_PROGRAM_ID = '11111111111111111111111111111111'
8
7
  TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
9
- ASSOCIATED_TOKEN_PROGRAM_ID = 'ATokenGP3evbxxpQ7bYPLNNaxD2c4bqtvWjpKbmz6HjH'
8
+ ASSOCIATED_TOKEN_PROGRAM_ID = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'
9
+ SYSVAR_RENT_ID = 'SysvarRent111111111111111111111111111111111'
10
+
10
11
 
11
12
  INSTRUCTION_LAYOUTS = {
12
13
  # Native SOL transfer
@@ -19,108 +20,202 @@ module SolanaRuby
19
20
  instruction: :uint8,
20
21
  amount: :uint64
21
22
  },
22
- # Create account layout
23
+ # Create account layout
23
24
  create_account: {
24
- instruction: :uint8,
25
+ instruction: :uint32,
25
26
  lamports: :uint64,
26
- space: :uint64
27
+ space: :uint64,
28
+ program_id: :blob32
29
+ },
30
+ # SPL token transfer_checked
31
+ spl_transfer_checked: {
32
+ instruction: :uint8,
33
+ amount: :uint64,
34
+ decimals: :uint8
35
+ },
36
+ # mint spl tokens
37
+ spl_mint_to: {
38
+ instruction: :uint8,
39
+ amount: :uint64
40
+ },
41
+ # burn spl tokens
42
+ spl_burn: {
43
+ instruction: :uint8,
44
+ amount: :uint64
27
45
  }
28
46
  }
29
47
 
30
48
  # 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
49
+ def self.account_instruction(from_pubkey, new_account_pubkey, lamports, space, program_id)
50
+ # Encode the instruction data
51
+ instruction_data = encode_data(
52
+ INSTRUCTION_LAYOUTS[:create_account],
53
+ {
54
+ instruction: 0, # '0' corresponds to the Create Account instruction
55
+ lamports: lamports, # The amount of lamports to transfer to the new account
56
+ space: space, # Amount of space allocated for the account's data
57
+ program_id: Base58.base58_to_binary(program_id, :bitcoin).bytes # Convert public key to binary
58
+ }
41
59
  )
42
- create_account_instruction
60
+
61
+ keys = [
62
+ { pubkey: from_pubkey, is_signer: true, is_writable: true }, # Funder's account
63
+ { pubkey: new_account_pubkey, is_signer: true, is_writable: true } # New account
64
+ ]
65
+
66
+ # return instruction data
67
+ create_instruction(keys, instruction_data, program_id)
43
68
  end
44
69
 
45
- def self.create_and_sign_transaction(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
70
+
71
+ def self.create_account(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash, program_id = SYSTEM_PROGRAM_ID)
46
72
  # Create the transaction
47
73
  transaction = Transaction.new
48
74
  transaction.set_fee_payer(from_pubkey)
49
75
  transaction.set_recent_blockhash(recent_blockhash)
50
76
 
51
77
  # 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
78
+ instruction = account_instruction(from_pubkey, new_account_pubkey, lamports, space, program_id)
79
+ transaction.add_instruction(instruction)
80
+
81
+ # return the transaction for signing
57
82
  transaction
58
83
  end
59
84
 
60
85
  # Method to create a SOL transfer instruction
61
- def self.transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
86
+ def self.transfer_sol_instruction(from_pubkey, to_pubkey, lamports)
62
87
  fields = INSTRUCTION_LAYOUTS[:sol_transfer]
63
88
  data = encode_data(fields, { instruction: 2, lamports: lamports })
64
- TransactionInstruction.new(
65
- keys: [
89
+ keys = [
66
90
  { pubkey: from_pubkey, is_signer: true, is_writable: true },
67
91
  { pubkey: to_pubkey, is_signer: false, is_writable: true }
68
- ],
69
- program_id: SYSTEM_PROGRAM_ID,
70
- data: data
71
- )
92
+ ]
93
+
94
+ create_instruction(keys, data, SYSTEM_PROGRAM_ID)
72
95
  end
73
96
 
74
97
  # Helper to create a new transaction for SOL transfer
75
- def self.new_sol_transaction(from_pubkey, to_pubkey, lamports, recent_blockhash)
98
+ def self.sol_transfer(from_pubkey, to_pubkey, lamports, recent_blockhash)
76
99
  transaction = Transaction.new
77
100
  transaction.set_fee_payer(from_pubkey)
78
101
  transaction.set_recent_blockhash(recent_blockhash)
79
- transfer_instruction = transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
102
+ transfer_instruction = transfer_sol_instruction(from_pubkey, to_pubkey, lamports)
80
103
  transaction.add_instruction(transfer_instruction)
81
104
  transaction
82
105
  end
83
106
 
84
107
  # 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
- )
108
+ def self.transfer_spl_token(source, token, destination, owner, amount, decimals, multi_signers)
109
+ fields = INSTRUCTION_LAYOUTS[:spl_transfer_checked]
110
+ data = encode_data(fields, { instruction: 12, amount: amount, decimals: decimals }) # Instruction type 3: Transfer tokens
111
+ keys = SolanaRuby::TransactionHelpers::TokenAccount.add_signers(
112
+ [{ pubkey: source, is_signer: false, is_writable: true },
113
+ { pubkey: token, is_signer: false, is_writable: false },
114
+ { pubkey: destination, is_signer: false, is_writable: true }],
115
+ owner, multi_signers)
116
+
117
+ create_instruction(keys, data)
97
118
  end
98
119
 
99
120
  # Helper to create a new transaction for SPL token transfer
100
- def self.new_spl_token_transaction(source, destination, owner, amount, recent_blockhash)
121
+ def self.new_spl_token_transaction(source, mint, destination, owner, amount, decimals, recent_blockhash, multi_signers=[])
101
122
  transaction = Transaction.new
102
123
  transaction.set_fee_payer(owner)
103
124
  transaction.set_recent_blockhash(recent_blockhash)
104
- transfer_instruction = transfer_spl_token(source, destination, owner, amount)
125
+ transfer_instruction = transfer_spl_token(source, mint, destination, owner, amount, decimals, multi_signers)
105
126
  transaction.add_instruction(transfer_instruction)
106
127
  transaction
107
128
  end
108
129
 
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
130
+ # Method to create an associated token account
131
+ def self.create_associated_token_account(payer, mint, owner, recent_blockhash, program_id = SYSTEM_PROGRAM_ID)
132
+ transaction = Transaction.new
133
+ transaction.set_fee_payer(payer) # Payer funds the transaction
134
+ transaction.set_recent_blockhash(recent_blockhash)
135
+
136
+ # Derive the associated token account address
137
+ associated_token_account_pubkey = SolanaRuby::TransactionHelpers::TokenAccount.get_associated_token_address(mint, owner)
138
+ puts "associated_token_account_pubkey: #{associated_token_account_pubkey}"
139
+
140
+
141
+ # Create the associated token account instruction
112
142
  create_account_instruction = TransactionInstruction.new(
113
143
  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 }
144
+ { pubkey: payer, is_signer: true, is_writable: true }, # Payer account
145
+ { pubkey: associated_token_account_pubkey, is_signer: false, is_writable: true }, # New ATA
146
+ { pubkey: owner, is_signer: false, is_writable: false }, # Owner of the ATA
147
+ { pubkey: mint, is_signer: false, is_writable: false }, # Token mint
148
+ { pubkey: SYSTEM_PROGRAM_ID, is_signer: false, is_writable: false }, # System program
149
+ { pubkey: TOKEN_PROGRAM_ID, is_signer: false, is_writable: false }, # Token program
150
+ { pubkey: SYSVAR_RENT_ID, is_signer: false, is_writable: false }
119
151
  ],
120
152
  program_id: ASSOCIATED_TOKEN_PROGRAM_ID,
121
- data: data
153
+ data: [] # No data required for creating an associated token account
122
154
  )
123
- create_account_instruction
155
+
156
+ # Add the instruction to the transaction
157
+ transaction.add_instruction(create_account_instruction)
158
+ transaction
159
+ end
160
+
161
+ # Method to create a mint instruction for SPL tokens
162
+ def self.mint_spl_token(mint, destination, mint_authority, amount, multi_signers = [])
163
+ fields = INSTRUCTION_LAYOUTS[:spl_mint_to]
164
+ data = encode_data(fields, { instruction: 7, amount: amount }) # Instruction type 7: Mint to
165
+ keys = SolanaRuby::TransactionHelpers::TokenAccount.add_signers(
166
+ [{ pubkey: mint, is_signer: false, is_writable: true },
167
+ { pubkey: destination, is_signer: false, is_writable: true }],
168
+ mint_authority, multi_signers)
169
+
170
+ create_instruction(keys, data)
171
+ end
172
+
173
+ # Helper to create a transaction for minting SPL tokens
174
+ def self.mint_spl_tokens(mint, destination, mint_authority, amount, recent_blockhash, multi_signers = [])
175
+ transaction = Transaction.new
176
+ transaction.set_fee_payer(mint_authority)
177
+ transaction.set_recent_blockhash(recent_blockhash)
178
+ mint_instruction = mint_spl_token(mint, destination, mint_authority, amount, multi_signers)
179
+ transaction.add_instruction(mint_instruction)
180
+ transaction
181
+ end
182
+
183
+ # Method to create a burn instruction for SPL tokens
184
+ def self.burn_spl_token(token_account, mint, mint_authority, amount, multi_signers = [])
185
+ # Define the fields for the burn instruction
186
+ fields = INSTRUCTION_LAYOUTS[:spl_burn]
187
+
188
+ # Encode the instruction data
189
+ data = encode_data(fields, { instruction: 8, amount: amount }) # Instruction type 8: Burn
190
+
191
+ keys = SolanaRuby::TransactionHelpers::TokenAccount.add_signers(
192
+ [
193
+ { pubkey: token_account, is_signer: false, is_writable: true }, # Token account holding tokens to burn
194
+ { pubkey: mint, is_signer: false, is_writable: true } # Mint address
195
+ ], mint_authority, multi_signers)
196
+
197
+ # Return the transaction instruction
198
+ create_instruction(keys, data)
199
+ end
200
+
201
+ # Helper to create a transaction for burning SPL tokens
202
+ def self.burn_spl_tokens(token_account, mint, owner, amount, recent_blockhash, multi_signers = [])
203
+ # Create a new transaction
204
+ transaction = Transaction.new
205
+ transaction.set_fee_payer(owner)
206
+ transaction.set_recent_blockhash(recent_blockhash)
207
+
208
+ # Add the burn instruction to the transaction
209
+ burn_instruction = burn_spl_token(token_account, mint, owner, amount, multi_signers)
210
+ transaction.add_instruction(burn_instruction)
211
+
212
+ # Return the transaction for signing
213
+ transaction
214
+ end
215
+
216
+ # Derive the associated token account address
217
+ def self.get_associated_token_address(mint, owner, program_id)
218
+ SolanaRuby::TransactionHelpers::TokenAccount.get_associated_token_address(mint, owner, program_id)
124
219
  end
125
220
 
126
221
  # Utility to encode data using predefined layouts
@@ -134,5 +229,13 @@ module SolanaRuby
134
229
  layout = SolanaRuby::DataTypes::Layout.new(fields)
135
230
  layout.deserialize(data)
136
231
  end
232
+
233
+ def self.create_instruction(keys, data, toke_program_id = TOKEN_PROGRAM_ID)
234
+ TransactionInstruction.new(
235
+ keys: keys,
236
+ program_id: toke_program_id,
237
+ data: data
238
+ )
239
+ end
137
240
  end
138
241
  end
@@ -0,0 +1,73 @@
1
+ module SolanaRuby
2
+ module TransactionHelpers
3
+ class TokenAccount
4
+ # Associated Token Program ID
5
+ ASSOCIATED_TOKEN_PROGRAM_ID = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'.freeze
6
+
7
+ # Token Program ID
8
+ TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'.freeze
9
+
10
+ def self.get_associated_token_address(mint, payer)
11
+ mint_bytes = Base58.base58_to_binary(mint, :bitcoin)
12
+ payer_bytes = Base58.base58_to_binary(payer, :bitcoin)
13
+ associated_program_bytes = Base58.base58_to_binary(ASSOCIATED_TOKEN_PROGRAM_ID, :bitcoin)
14
+
15
+ # Derive associated token account PDA
16
+ seeds = [
17
+ payer_bytes,
18
+ Base58.base58_to_binary(TOKEN_PROGRAM_ID, :bitcoin),
19
+ mint_bytes
20
+ ]
21
+
22
+ # Attempt to find the first valid off-curve PDA
23
+ associated_token_account_pubkey = find_program_address(seeds, associated_program_bytes)
24
+
25
+ # Return the computed Base58 PDA string
26
+ Base58.binary_to_base58(associated_token_account_pubkey, :bitcoin)
27
+ end
28
+
29
+ def self.add_signers(keys, owner_or_authority, multi_signers)
30
+ if multi_signers.any?
31
+ keys.push({ pubkey: owner_or_authority, is_signer: false, is_writable: false })
32
+ multi_signers.each do |signer|
33
+ pubkey = signer.is_a?(String) ? signer : signer.public_key
34
+ keys.push({ pubkey: pubkey, is_signer: true, is_writable: false })
35
+ end
36
+ else
37
+ keys.push({ pubkey: owner_or_authority, is_signer: true, is_writable: false })
38
+ end
39
+ keys
40
+ end
41
+
42
+ private
43
+
44
+ def self.find_program_address(seeds, program_id)
45
+ nonce = 255
46
+ loop do
47
+ # Combine the current nonce with the seeds
48
+ seeds_with_nonce = seeds + [[nonce].pack('C*')]
49
+ hashed_buffer = hash_seeds(seeds_with_nonce, program_id)
50
+
51
+ # Debugging: Log every generated address for inspection
52
+ puts "Testing nonce #{nonce}: #{Base58.binary_to_base58(hashed_buffer, :bitcoin)}"
53
+
54
+ # Check if it's valid and off-curve
55
+ if !SolanaRuby::Ed25519CurveChecker.on_curve?(hashed_buffer)
56
+ puts "Found valid PDA with nonce #{nonce}: #{Base58.binary_to_base58(hashed_buffer, :bitcoin)}"
57
+ return hashed_buffer
58
+ end
59
+
60
+ # Decrement nonce safely
61
+ nonce -= 1
62
+ raise "Unable to find a valid PDA address off the curve" if nonce < 0
63
+ end
64
+ end
65
+
66
+ def self.hash_seeds(seeds, program_id)
67
+ # Combine seeds and program ID with the PDA derivation logic
68
+ buffer = seeds.flatten.join + program_id + "ProgramDerivedAddress"
69
+ RbNaCl::Hash.sha256(buffer)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -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.2"
5
5
  end
data/lib/solana_ruby.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  Dir[File.join(__dir__, 'solana_ruby', '*.rb')].each { |file| require file }
4
4
  # Dir["solana_ruby/*.rb"].each { |f| require_relative f.delete(".rb") }
5
- require 'pry'
6
5
  module SolanaRuby
7
6
  class Error < StandardError; end
8
7
  end
@@ -0,0 +1,37 @@
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
+ # SOL Transfer Testing Script
7
+
8
+ # Initialize the Solana client
9
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
10
+
11
+ # Fetch the recent blockhash
12
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
13
+ token_account = "C2wY5TKnj52S4s9yRUTNqitRe5gmFokSCoppJS6t63aa"
14
+ mint_address = "5FQhi6Kq3CKDaB3bus21ZqcL7wyeZNR18otFGoDfrZXU"
15
+ mint_authority = SolanaRuby::Keypair.load_keypair('/Users/chinaputtaiahbellamkonda/.config/solana/id.json')
16
+ owner = mint_authority[:public_key]
17
+ amount = 500_000 # Number of tokens to burn
18
+
19
+ transaction = SolanaRuby::TransactionHelper.burn_spl_tokens(
20
+ token_account,
21
+ mint_address,
22
+ owner,
23
+ amount,
24
+ recent_blockhash
25
+ )
26
+
27
+ # Sign and send the transaction
28
+ resp = transaction.sign([mint_authority])
29
+
30
+ puts "signature: #{resp}"
31
+
32
+ # Send the transaction
33
+ puts "Sending transaction..."
34
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
35
+
36
+ # Output transaction results
37
+ puts "Transaction Signature: #{response}"
@@ -3,30 +3,58 @@
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)
16
+ sender_keypair = SolanaRuby::Keypair.load_keypair('/Users/chinaputtaiahbellamkonda/.config/solana/id.json')
15
17
  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"
18
+ puts "Sender Public Key: #{sender_pubkey}"
20
19
 
20
+ # Check sender's account balance
21
+ balance = client.get_balance(sender_pubkey)
22
+ puts "Sender account balance: #{balance} lamports"
23
+ if balance == 0
24
+ puts "Balance is zero, waiting for balance update..."
25
+ sleep(10)
26
+ end
21
27
 
22
- # Generate a receiver keypair and public key
28
+ # new keypair and public key (new account)
23
29
  new_account = SolanaRuby::Keypair.generate
24
30
  new_account_pubkey = new_account[:public_key]
31
+ puts "New Account Public Key: #{new_account_pubkey}"
32
+ puts "New Account Private Key: #{new_account[:private_key]}"
33
+ puts "New Account Full Private Key: #{new_account[:full_private_key]}"
34
+
35
+ # Parameters for account creation
36
+ lamports = 1 * 1_000_000_000 # Lamports to transfer
37
+ space = 165 # Space allocation (bytes)
38
+ program_id = SolanaRuby::TransactionHelper::SYSTEM_PROGRAM_ID
39
+
40
+ # Create and sign the transaction
41
+ transaction = SolanaRuby::TransactionHelper.create_account(
42
+ sender_pubkey,
43
+ new_account_pubkey,
44
+ lamports,
45
+ space,
46
+ recent_blockhash,
47
+ program_id
48
+ )
49
+
50
+ # Sign transaction with both sender and new account keypairs
51
+ transaction.sign([sender_keypair, new_account])
52
+
53
+ # Send the transaction
54
+ puts "Sending transaction..."
55
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
25
56
 
26
- # create a transaction instruction
27
- transaction = SolanaRuby::TransactionHelper.create_and_sign_transaction(sender_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
57
+ # Output transaction results
58
+ puts "Transaction Signature: #{response}"
59
+ puts "New account created successfully with Public Key: #{new_account_pubkey}"
28
60
 
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,52 @@
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
+ # SOL Transfer Testing Script
7
+
8
+ # Initialize the Solana client
9
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
10
+
11
+ # Fetch the recent blockhash
12
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
13
+
14
+ payer = SolanaRuby::Keypair.load_keypair('/Users/chinaputtaiahbellamkonda/.config/solana/id.json')
15
+ payer_pubkey = payer[:public_key]
16
+
17
+ # Generate a sender keypair and public key
18
+ owner = SolanaRuby::Keypair.generate
19
+ # owner = SolanaRuby::Keypair.from_private_key("2ce523e98cfd207a216a9ac4ef8b41c38c53a302af2022d2e89e1256d1b6a1d0")
20
+ owner_pubkey = owner[:public_key]
21
+ puts "owner public key: #{owner_pubkey}"
22
+ puts "payer private key: #{owner[:private_key]}"
23
+
24
+ # Airdrop some lamports to the sender's account
25
+ # lamports = 10 * 1_000_000_000
26
+ # sleep(1)
27
+ # result = client.request_airdrop(payer_pubkey, lamports)
28
+ # puts "Solana Balance #{lamports} lamports added sucessfully for the public key: #{payer_pubkey}"
29
+ # sleep(10)
30
+
31
+
32
+ mint_pubkey = "5xxFuuvLiB6Gz3vbaqgkjf8fvEDXowftFiL14qUSgPiM"
33
+ program_id = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
34
+ puts "payer public key: #{payer_pubkey}"
35
+
36
+ # associated_token_address = SolanaRuby::TransactionHelpers::TokenAccount.get_associated_token_address(mint_pubkey, payer_pubkey, program_id)
37
+
38
+ # puts "Associated Token Address: #{associated_token_address}"
39
+
40
+ transaction = SolanaRuby::TransactionHelper.create_associated_token_account(payer_pubkey, mint_pubkey, owner_pubkey, recent_blockhash)
41
+
42
+ resp = transaction.sign([payer])
43
+
44
+ puts "signature: #{resp}"
45
+
46
+ # Send the transaction
47
+ puts "Sending transaction..."
48
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
49
+
50
+ # Output transaction results
51
+ puts "Transaction Signature: #{response}"
52
+
@@ -0,0 +1,40 @@
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
+ # SOL Transfer Testing Script
7
+
8
+ # Initialize the Solana client
9
+ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
10
+
11
+ # Fetch the recent blockhash
12
+ recent_blockhash = client.get_latest_blockhash["blockhash"]
13
+
14
+ # Example parameters
15
+ mint_account = "5FQhi6Kq3CKDaB3bus21ZqcL7wyeZNR18otFGoDfrZXU"
16
+ destination_account = "C2wY5TKnj52S4s9yRUTNqitRe5gmFokSCoppJS6t63aa"
17
+ mint_authority = SolanaRuby::Keypair.load_keypair('/Users/chinaputtaiahbellamkonda/.config/solana/id.json')
18
+ amount = 1_000_000 # Amount to mint in smallest units
19
+ multi_signers = [] # If multi-signature is used, include public keys here
20
+
21
+ # Create a mint transaction
22
+ transaction = SolanaRuby::TransactionHelper.mint_spl_tokens(
23
+ mint_account,
24
+ destination_account,
25
+ mint_authority[:public_key],
26
+ amount,
27
+ recent_blockhash,
28
+ multi_signers
29
+ )
30
+
31
+ resp = transaction.sign([mint_authority])
32
+
33
+ puts "signature: #{resp}"
34
+
35
+ # Send the transaction
36
+ puts "Sending transaction..."
37
+ response = client.send_transaction(transaction.to_base64, { encoding: 'base64' })
38
+
39
+ # Output transaction results
40
+ puts "Transaction Signature: #{response}"
@@ -1,6 +1,6 @@
1
1
  Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/*.rb')].each { |file| require file }
2
2
  Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/**/*.rb')].each { |file| require file }
3
- require 'pry'
3
+ # require 'pry'
4
4
 
5
5
  # SOL Transfer Testing Script
6
6
 
@@ -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,
@@ -13,7 +13,7 @@ client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
13
13
  recent_blockhash = client.get_latest_blockhash["blockhash"]
14
14
 
15
15
  # Generate a sender keypair and public key
16
- fee_payer = SolanaRuby::Keypair.from_private_key("d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778")
16
+ fee_payer = SolanaRuby::Keypair.load_keypair('/Users/chinaputtaiahbellamkonda/.config/solana/id.json')
17
17
  fee_payer_pubkey = fee_payer[:public_key]
18
18
  lamports = 10 * 1_000_000_000
19
19
  space = 165
@@ -26,20 +26,23 @@ puts "sender account balance: #{balance}, wait for few seconds to update the bal
26
26
  # # Generate a receiver keypair and public key
27
27
  keypair = SolanaRuby::Keypair.generate
28
28
  receiver_pubkey = keypair[:public_key]
29
- transfer_lamports = 1 * 1_000_000
29
+ transfer_lamports = 1_000_000
30
30
  # puts "Payer's full private key: #{sender_keypair[:full_private_key]}"
31
31
  # # puts "Receiver's full private key: #{keypair[:full_private_key]}"
32
32
  # # puts "Receiver's Public Key: #{keypair[:public_key]}"
33
- mint_address = '9BvJGQC5FkLJzUC2TmYpi1iU8n9vt2388GLT5zvu8S1G'
33
+ mint_address = 'G4JEi3UprH2rNz6hjBfQakLSR5EHXtS5Ry8MRFTQS5wG'
34
34
  token_program_id = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
35
35
 
36
36
  # Create a new transaction
37
37
  transaction = SolanaRuby::TransactionHelper.new_spl_token_transaction(
38
- "9BvJGQC5FkLJzUC2TmYpi1iU8n9vt2388GLT5zvu8S1G",
38
+ "86w17eQHEoBcHY2rTdd5q9LjJL6Rx9QE6J1C9xgGkjt",
39
+ mint_address,
39
40
  receiver_pubkey,
40
41
  fee_payer_pubkey,
41
42
  transfer_lamports,
42
- recent_blockhash
43
+ 6,
44
+ recent_blockhash,
45
+ []
43
46
  )
44
47
  # # Get the sender's private key (ensure it's a string)
45
48
  private_key = fee_payer[:private_key]
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.2
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: 2025-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-client-simple
@@ -235,6 +235,7 @@ files:
235
235
  - lib/solana_ruby/data_types/near_int64.rb
236
236
  - lib/solana_ruby/data_types/sequence.rb
237
237
  - lib/solana_ruby/data_types/unsigned_int.rb
238
+ - lib/solana_ruby/ed25519_curve_checker.rb
238
239
  - lib/solana_ruby/http_client.rb
239
240
  - lib/solana_ruby/http_methods/account_methods.rb
240
241
  - lib/solana_ruby/http_methods/basic_methods.rb
@@ -247,8 +248,10 @@ files:
247
248
  - lib/solana_ruby/http_methods/transaction_methods.rb
248
249
  - lib/solana_ruby/keypair.rb
249
250
  - lib/solana_ruby/message.rb
251
+ - lib/solana_ruby/public_key.rb
250
252
  - lib/solana_ruby/transaction.rb
251
253
  - lib/solana_ruby/transaction_helper.rb
254
+ - lib/solana_ruby/transaction_helpers/token_account.rb
252
255
  - lib/solana_ruby/transaction_instruction.rb
253
256
  - lib/solana_ruby/utils.rb
254
257
  - lib/solana_ruby/version.rb
@@ -259,7 +262,10 @@ files:
259
262
  - lib/solana_ruby/web_socket_methods/root_methods.rb
260
263
  - lib/solana_ruby/web_socket_methods/signature_methods.rb
261
264
  - lib/solana_ruby/web_socket_methods/slot_methods.rb
265
+ - transaction_testing/burn_spl_tokens.rb
262
266
  - transaction_testing/create_account.rb
267
+ - transaction_testing/create_spl_token_account.rb
268
+ - transaction_testing/mint_spl_tokens.rb
263
269
  - transaction_testing/sol_transfer.rb
264
270
  - transaction_testing/spl_token_transfer.rb
265
271
  homepage: https://github.com/Build-Squad/solana-ruby
@@ -284,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
284
290
  - !ruby/object:Gem::Version
285
291
  version: '0'
286
292
  requirements: []
287
- rubygems_version: 3.5.23
293
+ rubygems_version: 3.5.20
288
294
  signing_key:
289
295
  specification_version: 4
290
296
  summary: Solana Ruby SDK