solana-ruby-web3js 2.0.0beta1 → 2.0.0beta2
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 +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
|