solana-ruby-web3js 2.0.0beta1 → 2.0.0beta2
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 +4 -4
- data/lib/solana_ruby/data_types/unsigned_int.rb +8 -8
- data/lib/solana_ruby/data_types.rb +4 -0
- data/lib/solana_ruby/transaction.rb +25 -21
- data/lib/solana_ruby/transaction_helper.rb +37 -23
- data/lib/solana_ruby/version.rb +1 -1
- data/transaction_testing/create_account.rb +40 -15
- data/transaction_testing/sol_transfer.rb +3 -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: 6bb6650de7a5beae14649fe9cfac20973923d0be4c4f74e9d1e8f373e70842b9
|
4
|
+
data.tar.gz: c3ad4316d61d4326327bb281a3eb64991011ae14d51a7974de6eaf9705d4ffa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94c8304f1d05d7cbd8ec22947d98ede21c1892b210ad5cdb43f3e6293f4c6ab288c8aa83b1ba6e40a28d08a191a32ba775966eeb370631d02112cac981309fb6
|
7
|
+
data.tar.gz: '0085b47b4f44786e0bd0af77827a12cbc191744a16aff61448cafff641a53efa798677ef51b3748966f76547b883fd767d3f48ffe5297f8519cc08eb1ee81e90'
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -342,7 +342,7 @@ To transfer SOL (the native cryptocurrency of the Solana blockchain) from one ac
|
|
342
342
|
|
343
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
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.
|
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
346
|
- An initialized client to interact with the Solana blockchain.
|
347
347
|
|
348
348
|
#### Example Usage:
|
@@ -364,7 +364,7 @@ To transfer SOL (the native cryptocurrency of the Solana blockchain) from one ac
|
|
364
364
|
sender_pubkey = sender_keypair[:public_key]
|
365
365
|
|
366
366
|
|
367
|
-
# Airdrop some lamports to the sender's account
|
367
|
+
# Airdrop some lamports to the sender's account when needed.
|
368
368
|
lamports = 10 * 1_000_000_000
|
369
369
|
sleep(1)
|
370
370
|
result = client.request_airdrop(sender_pubkey, lamports)
|
@@ -374,7 +374,7 @@ To transfer SOL (the native cryptocurrency of the Solana blockchain) from one ac
|
|
374
374
|
|
375
375
|
# Generate or use an existing receiver's public key
|
376
376
|
# Option 1: Generate a new keypair for the receiver
|
377
|
-
receiver_keypair = SolanaRuby::Keypair.generate
|
377
|
+
receiver_keypair = SolanaRuby::Keypair.generate
|
378
378
|
receiver_pubkey = receiver_keypair[:public_key]
|
379
379
|
# Option 2: Use an existing public key
|
380
380
|
# receiver_pubkey = 'InsertExistingPublicKeyHere'
|
@@ -385,7 +385,7 @@ To transfer SOL (the native cryptocurrency of the Solana blockchain) from one ac
|
|
385
385
|
puts "Receiver's Public Key: #{receiver_keypair[:public_key]}"
|
386
386
|
|
387
387
|
# Create a new transaction
|
388
|
-
transaction = SolanaRuby::TransactionHelper.
|
388
|
+
transaction = SolanaRuby::TransactionHelper.sol_transfer(
|
389
389
|
sender_pubkey,
|
390
390
|
receiver_pubkey,
|
391
391
|
transfer_lamports,
|
@@ -4,26 +4,26 @@ module SolanaRuby
|
|
4
4
|
attr_reader :size
|
5
5
|
|
6
6
|
BITS = {
|
7
|
-
8 => { directive: 'C
|
8
|
-
32 => { directive: 'L
|
9
|
-
64 => { directive: 'Q
|
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 "
|
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
|
23
|
+
raise "Cannot serialize negative integers" if obj.negative?
|
24
24
|
|
25
25
|
if obj >= 256**@size
|
26
|
-
raise "Integer too large
|
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 (
|
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
|
@@ -72,10 +72,10 @@ module SolanaRuby
|
|
72
72
|
instructions.push(item)
|
73
73
|
end
|
74
74
|
|
75
|
-
def sign(
|
76
|
-
raise 'No signers' unless
|
75
|
+
def sign(keypairs)
|
76
|
+
raise 'No signers' unless keypairs.any?
|
77
77
|
|
78
|
-
keys =
|
78
|
+
keys = keypairs.uniq { |kp| kp[:public_key] }
|
79
79
|
@signatures = keys.map do |key|
|
80
80
|
{
|
81
81
|
signature: nil,
|
@@ -116,15 +116,17 @@ module SolanaRuby
|
|
116
116
|
def compile_message
|
117
117
|
check_for_errors
|
118
118
|
fetch_message_data
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
account_keys: @account_keys,
|
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
|
126
128
|
)
|
127
|
-
|
129
|
+
message_data
|
128
130
|
end
|
129
131
|
|
130
132
|
def check_for_errors
|
@@ -170,11 +172,8 @@ module SolanaRuby
|
|
170
172
|
# Split out signing from non-signing keys and count header values
|
171
173
|
signed_keys = []
|
172
174
|
unsigned_keys = []
|
173
|
-
|
175
|
+
@header = split_keys(unique_metas, signed_keys, unsigned_keys)
|
174
176
|
@account_keys = signed_keys + unsigned_keys
|
175
|
-
|
176
|
-
# add instruction structure
|
177
|
-
@instructs = add_instructs
|
178
177
|
end
|
179
178
|
|
180
179
|
def append_program_id(program_ids, account_metas)
|
@@ -247,19 +246,24 @@ module SolanaRuby
|
|
247
246
|
end
|
248
247
|
|
249
248
|
def split_keys(unique_metas, signed_keys, unsigned_keys)
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
num_required_signatures = 0
|
250
|
+
num_readonly_signed_accounts = 0
|
251
|
+
num_readonly_unsigned_accounts = 0
|
253
252
|
unique_metas.each do |meta|
|
254
253
|
if meta[:is_signer]
|
255
254
|
signed_keys.push(meta[:pubkey])
|
256
|
-
|
257
|
-
|
255
|
+
num_required_signatures += 1
|
256
|
+
num_readonly_signed_accounts += 1 if (!meta[:is_writable])
|
258
257
|
else
|
259
258
|
unsigned_keys.push(meta[:pubkey])
|
260
|
-
|
259
|
+
num_readonly_unsigned_accounts += 1 if (!meta[:is_writable])
|
261
260
|
end
|
262
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
|
+
}
|
263
267
|
end
|
264
268
|
|
265
269
|
def partial_sign(message, keys)
|
@@ -19,46 +19,59 @@ 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: :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.
|
32
|
-
|
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:
|
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:
|
40
|
-
data: instruction_data
|
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
|
-
|
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
|
-
|
53
|
-
transaction.add_instruction(
|
54
|
-
|
55
|
-
#
|
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.
|
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.
|
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 =
|
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(
|
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:
|
115
|
-
{ pubkey:
|
116
|
-
{ pubkey:
|
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
|
],
|
data/lib/solana_ruby/version.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
14
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
27
|
-
|
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}"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/*.rb')].each { |file| require file }
|
2
|
+
Dir[File.join(File.dirname(__dir__), 'lib/solana_ruby/**/*.rb')].each { |file| require file }
|
1
3
|
require 'pry'
|
2
4
|
|
3
5
|
# SOL Transfer Testing Script
|
@@ -33,7 +35,7 @@ puts "Receiver's full private key: #{keypair[:full_private_key]}"
|
|
33
35
|
puts "Receiver's Public Key: #{keypair[:public_key]}"
|
34
36
|
|
35
37
|
# Create a new transaction
|
36
|
-
transaction = SolanaRuby::TransactionHelper.
|
38
|
+
transaction = SolanaRuby::TransactionHelper.sol_transfer(
|
37
39
|
sender_pubkey,
|
38
40
|
receiver_pubkey,
|
39
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.0beta2
|
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-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-client-simple
|