solana-ruby-web3js 2.0.0beta2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +0 -72
- data/lib/solana_ruby/data_types/unsigned_int.rb +8 -8
- data/lib/solana_ruby/data_types.rb +0 -4
- data/lib/solana_ruby/transaction.rb +21 -27
- data/lib/solana_ruby/transaction_helper.rb +23 -37
- data/lib/solana_ruby/version.rb +1 -1
- data/transaction_testing/create_account.rb +15 -40
- data/transaction_testing/sol_transfer.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f614bab0b21438c2820d0035a0aa33567a11fedd3ecb998666b39e5ab28681d
|
4
|
+
data.tar.gz: c63fd671d6328732c39994032f94aff219ff77eda8cd899c5d1f0c29f86f4524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4015bdc6179d57105ea1da82daa9d6ec19f623098d636271fb0e7ab9210d563414eae364271e58be82a2ce9ed6dc0804de53db7e7a15c5d353da4ff30faf5124
|
7
|
+
data.tar.gz: 8fca9ec2ccdcc5b3ea72f54fa6fad27fee3fe1aef20b145107382c523a0a70df90812f269fdcdee3d9d62b732e6ebe7ce6ad3e1514257d379db7088de55941d1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -332,75 +332,3 @@ 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
|
9
|
-
64 => { directive: 'Q
|
7
|
+
8 => { directive: 'C*', size: 1 },
|
8
|
+
32 => { directive: 'L*', size: 4 },
|
9
|
+
64 => { directive: 'Q*', size: 8 }
|
10
10
|
}
|
11
11
|
|
12
12
|
def initialize(bits)
|
13
13
|
@bits = bits
|
14
14
|
type = BITS[@bits]
|
15
|
-
raise "
|
15
|
+
raise "Can only fit #{BITS.keys}" unless type
|
16
16
|
@size = type[:size]
|
17
17
|
@directive = type[:directive]
|
18
18
|
end
|
19
19
|
|
20
|
-
# Serialize the unsigned integer into
|
20
|
+
# Serialize the unsigned integer into 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
|
23
|
+
raise "Cannot serialize negative integers" if obj < 0
|
24
24
|
|
25
25
|
if obj >= 256**@size
|
26
|
-
raise "Integer too large
|
26
|
+
raise "Integer too large (does not 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 (
|
34
|
+
raise "Invalid serialization (wrong size)" if bytes.size != @size
|
35
35
|
|
36
36
|
bytes.pack('C*').unpack(@directive).first
|
37
37
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
Dir[File.join(__dir__, 'data_types', '*.rb')].each { |file| require file }
|
2
|
-
|
3
1
|
module SolanaRuby
|
4
2
|
class Transaction
|
5
3
|
require 'rbnacl'
|
@@ -72,10 +70,10 @@ module SolanaRuby
|
|
72
70
|
instructions.push(item)
|
73
71
|
end
|
74
72
|
|
75
|
-
def sign(
|
76
|
-
raise 'No signers' unless
|
73
|
+
def sign(keys)
|
74
|
+
raise 'No signers' unless keys.any?
|
77
75
|
|
78
|
-
keys =
|
76
|
+
keys = keys.uniq{ |k| key[:public_key] }
|
79
77
|
@signatures = keys.map do |key|
|
80
78
|
{
|
81
79
|
signature: nil,
|
@@ -116,17 +114,15 @@ module SolanaRuby
|
|
116
114
|
def compile_message
|
117
115
|
check_for_errors
|
118
116
|
fetch_message_data
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
account_keys: @account_keys,
|
126
|
-
recent_blockhash: recent_blockhash,
|
127
|
-
instructions: instructs
|
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
|
128
124
|
)
|
129
|
-
|
125
|
+
message
|
130
126
|
end
|
131
127
|
|
132
128
|
def check_for_errors
|
@@ -172,8 +168,11 @@ module SolanaRuby
|
|
172
168
|
# Split out signing from non-signing keys and count header values
|
173
169
|
signed_keys = []
|
174
170
|
unsigned_keys = []
|
175
|
-
|
171
|
+
header_params = split_keys(unique_metas, signed_keys, unsigned_keys)
|
176
172
|
@account_keys = signed_keys + unsigned_keys
|
173
|
+
|
174
|
+
# add instruction structure
|
175
|
+
@instructs = add_instructs
|
177
176
|
end
|
178
177
|
|
179
178
|
def append_program_id(program_ids, account_metas)
|
@@ -246,24 +245,19 @@ module SolanaRuby
|
|
246
245
|
end
|
247
246
|
|
248
247
|
def split_keys(unique_metas, signed_keys, unsigned_keys)
|
249
|
-
num_required_signatures = 0
|
250
|
-
num_readonly_signed_accounts = 0
|
251
|
-
num_readonly_unsigned_accounts = 0
|
248
|
+
@num_required_signatures = 0
|
249
|
+
@num_readonly_signed_accounts = 0
|
250
|
+
@num_readonly_unsigned_accounts = 0
|
252
251
|
unique_metas.each do |meta|
|
253
252
|
if meta[:is_signer]
|
254
253
|
signed_keys.push(meta[:pubkey])
|
255
|
-
num_required_signatures += 1
|
256
|
-
num_readonly_signed_accounts += 1 if (!meta[:is_writable])
|
254
|
+
@num_required_signatures += 1
|
255
|
+
@num_readonly_signed_accounts += 1 if (!meta[:is_writable])
|
257
256
|
else
|
258
257
|
unsigned_keys.push(meta[:pubkey])
|
259
|
-
num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
|
258
|
+
@num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
|
260
259
|
end
|
261
260
|
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
|
-
}
|
267
261
|
end
|
268
262
|
|
269
263
|
def partial_sign(message, keys)
|
@@ -19,59 +19,46 @@ module SolanaRuby
|
|
19
19
|
instruction: :uint8,
|
20
20
|
amount: :uint64
|
21
21
|
},
|
22
|
-
|
22
|
+
# Create account layout
|
23
23
|
create_account: {
|
24
|
-
instruction: :
|
24
|
+
instruction: :uint8,
|
25
25
|
lamports: :uint64,
|
26
|
-
space: :uint64
|
27
|
-
program_id: :blob32
|
26
|
+
space: :uint64
|
28
27
|
}
|
29
28
|
}
|
30
29
|
|
31
30
|
# Method to create a system account (e.g., for SPL token or SOL)
|
32
|
-
def self.
|
33
|
-
|
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
|
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 })
|
45
33
|
create_account_instruction = TransactionInstruction.new(
|
46
34
|
keys: [
|
47
|
-
{ pubkey: from_pubkey, is_signer: true, is_writable: true },
|
48
|
-
{ pubkey: new_account_pubkey, is_signer:
|
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 }
|
49
38
|
],
|
50
|
-
program_id:
|
51
|
-
data: instruction_data
|
39
|
+
program_id: owner_pubkey,
|
40
|
+
data: instruction_data.bytes
|
52
41
|
)
|
53
|
-
|
54
|
-
# return instruction data
|
55
42
|
create_account_instruction
|
56
43
|
end
|
57
44
|
|
58
|
-
|
59
|
-
def self.create_account(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash, program_id = SYSTEM_PROGRAM_ID)
|
45
|
+
def self.create_and_sign_transaction(from_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
|
60
46
|
# Create the transaction
|
61
47
|
transaction = Transaction.new
|
62
48
|
transaction.set_fee_payer(from_pubkey)
|
63
49
|
transaction.set_recent_blockhash(recent_blockhash)
|
64
50
|
|
65
51
|
# Add the create account instruction to the transaction
|
66
|
-
|
67
|
-
transaction.add_instruction(
|
68
|
-
|
69
|
-
#
|
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
|
70
57
|
transaction
|
71
58
|
end
|
72
59
|
|
73
60
|
# Method to create a SOL transfer instruction
|
74
|
-
def self.
|
61
|
+
def self.transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
|
75
62
|
fields = INSTRUCTION_LAYOUTS[:sol_transfer]
|
76
63
|
data = encode_data(fields, { instruction: 2, lamports: lamports })
|
77
64
|
TransactionInstruction.new(
|
@@ -85,11 +72,11 @@ module SolanaRuby
|
|
85
72
|
end
|
86
73
|
|
87
74
|
# Helper to create a new transaction for SOL transfer
|
88
|
-
def self.
|
75
|
+
def self.new_sol_transaction(from_pubkey, to_pubkey, lamports, recent_blockhash)
|
89
76
|
transaction = Transaction.new
|
90
77
|
transaction.set_fee_payer(from_pubkey)
|
91
78
|
transaction.set_recent_blockhash(recent_blockhash)
|
92
|
-
transfer_instruction =
|
79
|
+
transfer_instruction = transfer_sol_transaction(from_pubkey, to_pubkey, lamports)
|
93
80
|
transaction.add_instruction(transfer_instruction)
|
94
81
|
transaction
|
95
82
|
end
|
@@ -120,14 +107,13 @@ module SolanaRuby
|
|
120
107
|
end
|
121
108
|
|
122
109
|
# Method to create an associated token account for a given token mint
|
123
|
-
def self.create_associated_token_account(
|
110
|
+
def self.create_associated_token_account(from_pubkey, token_mint, owner_pubkey)
|
124
111
|
data = [0, 0, 0, 0] # No data required for account creation
|
125
112
|
create_account_instruction = TransactionInstruction.new(
|
126
113
|
keys: [
|
127
|
-
{ pubkey:
|
128
|
-
{ pubkey:
|
129
|
-
{ pubkey:
|
130
|
-
{ pubkey: mint, is_signer: false, is_writable: false },
|
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 },
|
131
117
|
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, is_signer: false, is_writable: false },
|
132
118
|
{ pubkey: SYSTEM_PROGRAM_ID, is_signer: false, is_writable: false }
|
133
119
|
],
|
data/lib/solana_ruby/version.rb
CHANGED
@@ -3,55 +3,30 @@
|
|
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
|
-
#
|
6
|
+
# Testing Script
|
7
|
+
|
7
8
|
client = SolanaRuby::HttpClient.new('http://127.0.0.1:8899')
|
8
9
|
|
9
10
|
# Fetch the recent blockhash
|
10
11
|
recent_blockhash = client.get_latest_blockhash["blockhash"]
|
11
|
-
puts "Recent Blockhash: #{recent_blockhash}"
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
sender_keypair = SolanaRuby::Keypair.from_private_key(private_key)
|
13
|
+
# Generate a sender keypair and public key
|
14
|
+
sender_keypair = SolanaRuby::Keypair.from_private_key("d22867a84ee1d91485a52c587793002dcaa7ce79a58bb605b3af2682099bb778")
|
16
15
|
sender_pubkey = sender_keypair[:public_key]
|
17
|
-
|
18
|
-
|
19
|
-
# Check sender's account balance
|
16
|
+
lamports = 1 * 1_000_000_000
|
17
|
+
space = 165
|
20
18
|
balance = client.get_balance(sender_pubkey)
|
21
|
-
puts "
|
22
|
-
|
23
|
-
puts "Balance is zero, waiting for balance update..."
|
24
|
-
sleep(10)
|
25
|
-
end
|
19
|
+
puts "sender account balance: #{balance}, wait for few seconds to update the balance in solana when the balance 0"
|
20
|
+
|
26
21
|
|
27
|
-
#
|
22
|
+
# Generate a receiver keypair and public key
|
28
23
|
new_account = SolanaRuby::Keypair.generate
|
29
24
|
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' })
|
53
25
|
|
54
|
-
#
|
55
|
-
|
56
|
-
puts "New account created successfully with Public Key: #{new_account_pubkey}"
|
26
|
+
# create a transaction instruction
|
27
|
+
transaction = SolanaRuby::TransactionHelper.create_and_sign_transaction(sender_pubkey, new_account_pubkey, lamports, space, recent_blockhash)
|
57
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}"
|
@@ -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.
|
38
|
+
transaction = SolanaRuby::TransactionHelper.new_sol_transaction(
|
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.
|
4
|
+
version: 2.0.0
|
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-
|
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
|