solace 0.0.3 → 0.0.5
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/CHANGELOG +12 -0
- data/README.md +140 -285
- data/lib/solace/address_lookup_table.rb +34 -18
- data/lib/solace/composers/base.rb +16 -11
- data/lib/solace/composers/spl_token_program_transfer_checked_composer.rb +27 -1
- data/lib/solace/composers/system_program_transfer_composer.rb +22 -1
- data/lib/solace/concerns/binary_serializable.rb +39 -0
- data/lib/solace/connection.rb +69 -38
- data/lib/solace/constants.rb +7 -14
- data/lib/solace/instruction.rb +30 -19
- data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +18 -3
- data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +24 -3
- data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +18 -1
- data/lib/solace/instructions/spl_token/mint_to_instruction.rb +16 -3
- data/lib/solace/instructions/spl_token/transfer_checked_instruction.rb +17 -1
- data/lib/solace/instructions/spl_token/transfer_instruction.rb +15 -2
- data/lib/solace/instructions/system_program/create_account_instruction.rb +18 -3
- data/lib/solace/instructions/system_program/transfer_instruction.rb +15 -7
- data/lib/solace/keypair.rb +64 -31
- data/lib/solace/message.rb +22 -10
- data/lib/solace/programs/associated_token_account.rb +45 -20
- data/lib/solace/programs/base.rb +6 -0
- data/lib/solace/programs/spl_token.rb +52 -14
- data/lib/solace/public_key.rb +45 -20
- data/lib/solace/serializers/address_lookup_table_deserializer.rb +3 -5
- data/lib/solace/serializers/address_lookup_table_serializer.rb +7 -7
- data/lib/solace/serializers/base_deserializer.rb +29 -19
- data/lib/solace/serializers/base_serializer.rb +18 -9
- data/lib/solace/serializers/instruction_deserializer.rb +5 -7
- data/lib/solace/serializers/instruction_serializer.rb +4 -6
- data/lib/solace/serializers/message_deserializer.rb +3 -5
- data/lib/solace/serializers/message_serializer.rb +3 -5
- data/lib/solace/serializers/transaction_deserializer.rb +5 -7
- data/lib/solace/serializers/transaction_serializer.rb +5 -7
- data/lib/solace/transaction.rb +38 -23
- data/lib/solace/transaction_composer.rb +47 -13
- data/lib/solace/utils/account_context.rb +64 -65
- data/lib/solace/utils/codecs.rb +56 -128
- data/lib/solace/utils/curve25519_dalek.rb +9 -4
- data/lib/solace/utils/pda.rb +22 -24
- data/lib/solace/version.rb +2 -1
- data/lib/solace.rb +4 -9
- metadata +7 -10
- data/lib/solace/instructions/base.rb +0 -21
- data/lib/solace/serializable_record.rb +0 -26
- data/lib/solace/serializers/base.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ea64862833bb39232a5157ed5d29387f8fa1e3780aeae55fcd1481592060d90
|
4
|
+
data.tar.gz: 6a62d0efc8eaaf900800168c5eaa6580b769460f0aaf77f104679fe99bbbcd74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 117e324eb46561a5a51e7dd4773d3b09a6e4d00e6c5b12d9c23c7848889e5a6f877c40da08fb726c4ae766313b5c37988e2cb4287e21b2360cee9530a2a2398a
|
7
|
+
data.tar.gz: f0622e37fd2e1d45424d1e9257705a5c084a1b8878742ed12d6cbf1f97fea568ac7876af673c64c8299653e3a65e072247663e5cb5957454e35cd82b61e68b99
|
data/CHANGELOG
CHANGED
@@ -16,6 +16,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
|
|
16
16
|
### Fixed
|
17
17
|
```
|
18
18
|
|
19
|
+
## [WIP] - yyyy-mm-dd
|
20
|
+
|
21
|
+
### Added
|
22
|
+
1.
|
23
|
+
|
24
|
+
### Changed
|
25
|
+
1. Change `private_key` method on Keypair to `pivate_key_bytes`
|
26
|
+
2. Change docs on most methods to include an `@example` section.
|
27
|
+
3. Change README to include a practical example of using composers.
|
28
|
+
|
29
|
+
### Fixed
|
30
|
+
|
19
31
|
## [0.0.3] - 2025-07-30
|
20
32
|
|
21
33
|
### Added
|
data/README.md
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
# Solace Ruby SDK Documentation
|
2
|
+
A Ruby SDK for the Solana blockchain.
|
2
3
|
|
3
4
|
## Overview
|
4
5
|
|
5
|
-
|
6
|
+
Solace is a comprehensive Ruby SDK for interacting with the Solana blockchain. It provides both low-level building blocks and high-level abstractions for composing, signing, and sending Solana transactions. The library aims to follow Ruby conventions while maintaining compatibility with Solana's binary protocols.
|
6
7
|
|
7
8
|
## Architecture
|
8
9
|
|
9
10
|
The Solace SDK is organized into several key layers:
|
10
11
|
|
11
|
-
### 1. **Core
|
12
|
+
### 1. **Core Classes** (Low-Level)
|
12
13
|
- **Keypair/PublicKey**: Ed25519 cryptographic operations
|
13
14
|
- **Connection**: RPC client for Solana nodes
|
14
|
-
- **Transaction/Message/Instruction**: Transaction building blocks
|
15
|
+
- **Transaction/Message/Instruction/AddressLookupTable**: Transaction building blocks
|
15
16
|
- **Serializers**: Binary serialization/deserialization system
|
16
17
|
|
17
18
|
### 2. **Instruction Builders** (Low-Level)
|
@@ -19,96 +20,32 @@ The Solace SDK is organized into several key layers:
|
|
19
20
|
- Handle binary data encoding and account indexing
|
20
21
|
- Located in `lib/solace/instructions/`
|
21
22
|
|
22
|
-
### 3. **
|
23
|
+
### 3. **Composers** (High-Level)
|
24
|
+
- Convenient interfaces for composing transactions and instructions
|
25
|
+
- Handle account ordering and header calculations for transactions
|
26
|
+
- Located in `lib/solace/composers`
|
27
|
+
|
28
|
+
### 3. **Programs** (High-Level)
|
23
29
|
- Convenient interfaces for interacting with on-chain programs
|
24
30
|
- Handle transaction assembly, signing, and submission
|
25
31
|
- Located in `lib/solace/programs/`
|
26
32
|
|
27
|
-
### 4. **Utilities** (
|
33
|
+
### 4. **Utilities** (Support modules & classes)
|
28
34
|
- **Codecs**: Base58/Base64 encoding, compact integers, little-endian encoding
|
29
35
|
- **PDA**: Program Derived Address generation
|
30
36
|
- **Curve25519**: Native curve operations via FFI
|
37
|
+
- **More...**: Checkout `lib/solace/utils`
|
31
38
|
|
32
39
|
## Core Components
|
33
40
|
|
34
|
-
### Keypair
|
35
|
-
|
36
|
-
The `Solace::Keypair` class represents Ed25519 keypairs for signing transactions.
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
# Generate a new random keypair
|
40
|
-
keypair = Solace::Keypair.generate
|
41
|
-
|
42
|
-
# Create from seed (32 bytes)
|
43
|
-
seed = SecureRandom.random_bytes(32)
|
44
|
-
keypair = Solace::Keypair.from_seed(seed)
|
45
|
-
|
46
|
-
# Create from secret key (64 bytes)
|
47
|
-
keypair = Solace::Keypair.from_secret_key(secret_key_bytes)
|
48
|
-
|
49
|
-
# Usage
|
50
|
-
puts keypair.address # Base58 public key
|
51
|
-
signature = keypair.sign(message) # Sign binary data
|
52
|
-
```
|
53
|
-
|
54
|
-
**Key Features:**
|
55
|
-
- Ed25519 signature support via RbNaCl
|
56
|
-
- Base58 address encoding
|
57
|
-
- Secure random generation
|
58
|
-
- Compatible with Solana's key format
|
59
|
-
|
60
|
-
### PublicKey
|
61
|
-
|
62
|
-
The `Solace::PublicKey` class represents Solana public keys with utility methods.
|
63
|
-
|
64
|
-
```ruby
|
65
|
-
# Create from bytes
|
66
|
-
pubkey = Solace::PublicKey.new(32_byte_array)
|
67
|
-
|
68
|
-
# Usage
|
69
|
-
puts pubkey.to_base58 # Base58 representation
|
70
|
-
puts pubkey.to_s # Same as to_base58
|
71
|
-
bytes = pubkey.to_bytes # Get raw bytes
|
72
|
-
```
|
73
|
-
|
74
|
-
**Key Features:**
|
75
|
-
- 32-byte Ed25519 public keys
|
76
|
-
- Base58 encoding/decoding
|
77
|
-
- PDA (Program Derived Address) support via mixin
|
78
|
-
- Equality comparison
|
79
|
-
|
80
|
-
### Connection
|
81
|
-
|
82
|
-
The `Solace::Connection` class provides RPC communication with Solana nodes.
|
83
|
-
|
84
|
-
```ruby
|
85
|
-
# Connect to local validator (default = http://localhost:8899)
|
86
|
-
connection = Solace::Connection.new
|
87
|
-
|
88
|
-
# Connect to devnet or other RPC nodes
|
89
|
-
connection = Solace::Connection.new('https://api.devnet.solana.com')
|
90
|
-
|
91
|
-
# RPC methods
|
92
|
-
balance = connection.get_balance(pubkey)
|
93
|
-
blockhash = connection.get_latest_blockhash
|
94
|
-
account_info = connection.get_account_info(pubkey)
|
95
|
-
signature = connection.send_transaction(transaction)
|
96
|
-
|
97
|
-
# Airdrop (devnet/testnet only)
|
98
|
-
response = connection.request_airdrop(pubkey, 1_000_000_000) # 1 SOL
|
99
|
-
connection.wait_for_confirmed_signature { response['result'] }
|
100
|
-
```
|
101
|
-
|
102
|
-
**Key Features:**
|
103
|
-
- JSON-RPC 2.0 client
|
104
|
-
- Automatic request ID generation
|
105
|
-
- Built-in error handling
|
106
|
-
- Support for all major RPC methods
|
107
|
-
- Transaction confirmation waiting
|
108
|
-
|
109
41
|
### Transaction & Message
|
110
42
|
|
111
|
-
Transactions contain a message and signatures. Messages contain instructions and metadata.
|
43
|
+
Transactions contain a message and signatures. Messages contain instructions and metadata. This core class is as simple as possible and provide the lowest level of abstraction for building and sending transactions. A developer is expected to:
|
44
|
+
|
45
|
+
1. Manually fill and order the accounts array
|
46
|
+
2. Manually fill and order the instructions array
|
47
|
+
3. Manually calculate the header
|
48
|
+
4. ...did I forget to say manually?
|
112
49
|
|
113
50
|
```ruby
|
114
51
|
# Create a message
|
@@ -143,7 +80,11 @@ signature = connection.send_transaction(transaction.serialize)
|
|
143
80
|
|
144
81
|
### Instruction
|
145
82
|
|
146
|
-
Instructions represent individual operations within a transaction.
|
83
|
+
Instructions represent individual operations within a transaction. Like messages, instructions are as simple as possible and provide the lowest level of abstraction for building and sending transactions. A developer is expected to:
|
84
|
+
|
85
|
+
1. Manually fill and order the accounts indices array
|
86
|
+
2. Manually specify the program index
|
87
|
+
3. Manually specify the data
|
147
88
|
|
148
89
|
```ruby
|
149
90
|
instruction = Solace::Instruction.new(
|
@@ -166,11 +107,9 @@ instruction.data # => [2, 0, 0, 0] + amount_bytes
|
|
166
107
|
|
167
108
|
## Low-Level Instruction Builders
|
168
109
|
|
169
|
-
|
110
|
+
Given that the low-level instruction class is fully available, it's easy to build higher-level instruction builders that wrap the low-level instruction class. These builders are service objects that create specific instruction types. They handle the binary encoding required by Solana programs.
|
170
111
|
|
171
|
-
|
172
|
-
|
173
|
-
#### Transfer Instruction
|
112
|
+
For example, the SystemProgram::TransferInstruction builder is a service object that creates and returns a Solace::Instruction object with the correct program index, accounts indices, and data for a System Program solana transfer.
|
174
113
|
|
175
114
|
```ruby
|
176
115
|
# Build a SOL transfer instruction
|
@@ -182,71 +121,33 @@ transfer_ix = Solace::Instructions::SystemProgram::TransferInstruction.build(
|
|
182
121
|
)
|
183
122
|
```
|
184
123
|
|
185
|
-
|
186
|
-
|
187
|
-
```ruby
|
188
|
-
# Build account creation instruction
|
189
|
-
create_ix = Solace::Instructions::SystemProgram::CreateAccountInstruction.build(
|
190
|
-
from_index: 0, # Payer account index
|
191
|
-
new_account_index: 1, # New account index
|
192
|
-
system_program_index: 2, # System program index
|
193
|
-
lamports: rent_lamports, # Rent-exempt amount
|
194
|
-
space: 165, # Account data size
|
195
|
-
owner: token_program_id # Owning program
|
196
|
-
)
|
197
|
-
```
|
198
|
-
|
199
|
-
### SPL Token Instructions
|
200
|
-
|
201
|
-
#### Initialize Mint Instruction
|
202
|
-
|
203
|
-
```ruby
|
204
|
-
# Initialize a new token mint
|
205
|
-
init_mint_ix = Solace::Instructions::SplToken::InitializeMintInstruction.build(
|
206
|
-
mint_account_index: 1, # Mint account index
|
207
|
-
rent_sysvar_index: 2, # Rent sysvar index
|
208
|
-
program_index: 3, # Token program index
|
209
|
-
decimals: 6, # Token decimals
|
210
|
-
mint_authority: authority_pubkey,
|
211
|
-
freeze_authority: freeze_pubkey # Optional
|
212
|
-
)
|
213
|
-
```
|
214
|
-
|
215
|
-
#### Mint To Instruction
|
124
|
+
Solace includes a number of these, and you can build your own as well.
|
216
125
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
)
|
226
|
-
```
|
227
|
-
|
228
|
-
#### Transfer Instruction
|
229
|
-
|
230
|
-
```ruby
|
231
|
-
# Transfer tokens between accounts
|
232
|
-
transfer_ix = Solace::Instructions::SplToken::TransferInstruction.build(
|
233
|
-
amount: 500_000, # Amount to transfer
|
234
|
-
source_index: 0, # Source token account
|
235
|
-
destination_index: 1, # Destination token account
|
236
|
-
owner_index: 2, # Owner/authority index
|
237
|
-
program_index: 3 # Token program index
|
238
|
-
)
|
239
|
-
```
|
126
|
+
- `Solace::Instructions::SystemProgram::TransferInstruction`
|
127
|
+
- `Solace::Instructions::SystemProgram::CreateAccountInstruction`
|
128
|
+
- `Solace::Instructions::SplToken::InitializeMintInstruction`
|
129
|
+
- `Solace::Instructions::SplToken::InitializeAccountInstruction`
|
130
|
+
- `Solace::Instructions::SplToken::MintToInstruction`
|
131
|
+
- `Solace::Instructions::SplToken::TransferInstruction`
|
132
|
+
- `Solace::Instructions::SplToken::TransferCheckedInstruction`
|
133
|
+
- `Solace::Instructions::AssociatedTokenAccount::CreateAssociatedTokenAccountInstruction`
|
240
134
|
|
241
135
|
**Common Patterns:**
|
242
136
|
- All builders use `.build()` class method
|
137
|
+
- All builders use `.data()` method to specify the instruction data
|
138
|
+
- All builders use named parameters and `_index` suffix for account indices
|
139
|
+
- All builders use a `program_index` parameter to specify the program index
|
243
140
|
- Account indices reference the transaction's accounts array
|
244
141
|
- Binary data encoding handled automatically
|
245
142
|
- Instruction-specific data layouts documented in comments
|
246
143
|
|
247
|
-
## High-Level Program
|
144
|
+
## High-Level Program Classes
|
145
|
+
|
146
|
+
**WARNING: Programs will probably get deprecated in favor of composers in the future.**
|
147
|
+
|
148
|
+
Now that we have the mid-level instruction builders, we can create high-level program classes that provide convenient interfaces for common operations, handling transaction assembly, signing, and submission.
|
248
149
|
|
249
|
-
|
150
|
+
For example, the `Solace::Programs::SplToken` class provides a high-level interface for interacting with the SPL Token Program.
|
250
151
|
|
251
152
|
### SPL Token Program
|
252
153
|
|
@@ -255,31 +156,34 @@ Program clients provide convenient interfaces for common operations, handling tr
|
|
255
156
|
spl_token = Solace::Programs::SplToken.new(connection: connection)
|
256
157
|
|
257
158
|
# Create a new token mint
|
258
|
-
|
159
|
+
response = spl_token.create_mint(
|
259
160
|
payer: payer_keypair,
|
260
161
|
decimals: 6,
|
261
162
|
mint_authority: authority_keypair,
|
262
163
|
freeze_authority: freeze_keypair, # Optional
|
263
164
|
mint_keypair: mint_keypair # Optional, generates if not provided
|
264
165
|
)
|
166
|
+
connection.wait_for_confirmed_signature { response['result'] }
|
265
167
|
|
266
168
|
# Mint tokens to an account
|
267
|
-
|
169
|
+
response = spl_token.mint_to(
|
268
170
|
payer: payer_keypair,
|
269
171
|
mint: mint_keypair,
|
270
172
|
destination: token_account_address,
|
271
173
|
amount: 1_000_000,
|
272
174
|
mint_authority: authority_keypair
|
273
175
|
)
|
176
|
+
connection.wait_for_confirmed_signature { response['result'] }
|
274
177
|
|
275
178
|
# Transfer tokens
|
276
|
-
|
179
|
+
response = spl_token.transfer(
|
277
180
|
payer: payer_keypair,
|
278
181
|
source: source_token_account,
|
279
182
|
destination: dest_token_account,
|
280
183
|
amount: 500_000,
|
281
184
|
owner: owner_keypair
|
282
185
|
)
|
186
|
+
connection.wait_for_confirmed_signature { response['result'] }
|
283
187
|
```
|
284
188
|
|
285
189
|
**Key Features:**
|
@@ -291,7 +195,7 @@ signature = spl_token.transfer(
|
|
291
195
|
|
292
196
|
### Prepare Methods
|
293
197
|
|
294
|
-
For more control, use "prepare" methods that return signed transactions without sending:
|
198
|
+
For more control, use "prepare" methods that return signed transactions without sending it on the program:
|
295
199
|
|
296
200
|
```ruby
|
297
201
|
# Prepare transaction without sending
|
@@ -308,54 +212,6 @@ puts transaction.serialize # Base64 transaction
|
|
308
212
|
signature = connection.send_transaction(transaction.serialize)
|
309
213
|
```
|
310
214
|
|
311
|
-
## Serialization System
|
312
|
-
|
313
|
-
Solace uses a serialization system for converting Ruby objects to/from Solana's binary format.
|
314
|
-
|
315
|
-
### SerializableRecord Base Class
|
316
|
-
|
317
|
-
```ruby
|
318
|
-
class Transaction < Solace::SerializableRecord
|
319
|
-
SERIALIZER = Solace::Serializers::TransactionSerializer
|
320
|
-
DESERIALIZER = Solace::Serializers::TransactionDeserializer
|
321
|
-
|
322
|
-
# Automatic serialization
|
323
|
-
def serialize
|
324
|
-
self.class::SERIALIZER.call(self)
|
325
|
-
end
|
326
|
-
|
327
|
-
# Automatic deserialization
|
328
|
-
def self.deserialize(io)
|
329
|
-
self::DESERIALIZER.call(io)
|
330
|
-
end
|
331
|
-
end
|
332
|
-
```
|
333
|
-
|
334
|
-
### Serializer Pattern
|
335
|
-
|
336
|
-
```ruby
|
337
|
-
class TransactionSerializer < Solace::Serializers::Base
|
338
|
-
STEPS = %i[
|
339
|
-
serialize_signatures
|
340
|
-
serialize_message
|
341
|
-
].freeze
|
342
|
-
|
343
|
-
def serialize_signatures
|
344
|
-
# Convert signatures to binary format
|
345
|
-
end
|
346
|
-
|
347
|
-
def serialize_message
|
348
|
-
# Serialize message component
|
349
|
-
end
|
350
|
-
end
|
351
|
-
```
|
352
|
-
|
353
|
-
**Key Features:**
|
354
|
-
- Step-based serialization process
|
355
|
-
- Automatic Base64 encoding
|
356
|
-
- Consistent error handling
|
357
|
-
- Reversible serialization/deserialization
|
358
|
-
|
359
215
|
## Utility Modules
|
360
216
|
|
361
217
|
### Codecs
|
@@ -387,9 +243,6 @@ seeds = ['metadata', mint_address, 'edition']
|
|
387
243
|
program_id = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'
|
388
244
|
|
389
245
|
address, bump = Solace::Utils::PDA.find_program_address(seeds, program_id)
|
390
|
-
|
391
|
-
# Create PDA directly
|
392
|
-
address = Solace::Utils::PDA.create_program_address(seeds + [bump], program_id)
|
393
246
|
```
|
394
247
|
|
395
248
|
**Key Features:**
|
@@ -407,6 +260,8 @@ on_curve = Solace::Utils::Curve25519Dalek.on_curve?(32_byte_point)
|
|
407
260
|
|
408
261
|
## Constants
|
409
262
|
|
263
|
+
**WARNING: Constants will probably get deprecated in favor of config file that developers can use to define their own constants with required values.**
|
264
|
+
|
410
265
|
Common Solana program IDs are defined in `Solace::Constants`:
|
411
266
|
|
412
267
|
```ruby
|
@@ -417,6 +272,79 @@ Solace::Constants::SYSVAR_RENT_PROGRAM_ID # 'SysvarRent11111111111
|
|
417
272
|
Solace::Constants::MEMO_PROGRAM_ID # 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'
|
418
273
|
```
|
419
274
|
|
275
|
+
## Composers
|
276
|
+
|
277
|
+
Composers are used to build transactions from multiple instructions. They handle all the low-level details of transaction assembly, such as account ordering, header calculation, and fee payer selection.
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
# Initialize a transaction composer
|
281
|
+
composer = Solace::TransactionComposer.new(connection: connection)
|
282
|
+
|
283
|
+
# Add an instruction composer
|
284
|
+
composer.add_instruction(
|
285
|
+
Solace::Composers::SystemProgramTransferComposer.new(
|
286
|
+
to: 'pubkey1',
|
287
|
+
from: 'pubkey2',
|
288
|
+
lamports: 100
|
289
|
+
)
|
290
|
+
)
|
291
|
+
|
292
|
+
# Add another instruction composer
|
293
|
+
composer.add_instruction(
|
294
|
+
Solace::Composers::SplTokenProgramTransferCheckedComposer.new(
|
295
|
+
from: 'pubkey4',
|
296
|
+
to: 'pubkey5',
|
297
|
+
mint: 'pubkey6',
|
298
|
+
authority: 'pubkey7',
|
299
|
+
amount: 1_000_000,
|
300
|
+
decimals: 6
|
301
|
+
)
|
302
|
+
)
|
303
|
+
|
304
|
+
# Set the fee payer
|
305
|
+
composer.set_fee_payer('pubkey8')
|
306
|
+
|
307
|
+
# Compose the transaction
|
308
|
+
tx = composer.compose_transaction
|
309
|
+
|
310
|
+
# Sign the transaction with all required signers
|
311
|
+
tx.sign(*any_required_signers)
|
312
|
+
```
|
313
|
+
|
314
|
+
Composers are intended to be extended by developers with custom instruction composers to interface with their own programs. Simply inherit from `Solace::Composers::Base` and implement the required methods.
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
class MyProgramComposer < Solace::Composers::Base
|
318
|
+
# All keyword arguments are passed to the constructor and available
|
319
|
+
# as a `params` hash.
|
320
|
+
#
|
321
|
+
# The setup_accounts method is called automatically by the transaction composer
|
322
|
+
# during compilation and should be used to add accounts to the account_context
|
323
|
+
# with the appropriate access permissions. Conditional logic is fine here given
|
324
|
+
# and available params to determine the access permissions.
|
325
|
+
def setup_accounts
|
326
|
+
account_context.add_writable_signer(params[:from])
|
327
|
+
account_context.add_writable_nonsigner(params[:to])
|
328
|
+
account_context.add_readonly_nonsigner(params[:program])
|
329
|
+
end
|
330
|
+
|
331
|
+
# The build_instruction method is called automatically by the transaction composer
|
332
|
+
# during compilation and should be used to build the instruction using an instruction builder.
|
333
|
+
#
|
334
|
+
# The passed context to the build_instruction method provides the indices of all accounts
|
335
|
+
# that were added to the account_context in the setup_accounts method. These are accessible
|
336
|
+
# by the index_of method of the context using the account address as a parameter.
|
337
|
+
def build_instruction(context)
|
338
|
+
Solace::Instructions::MyProgram::MyInstruction.build(
|
339
|
+
data: params[:data],
|
340
|
+
from_index: context.index_of(params[:from]),
|
341
|
+
to_index: context.index_of(params[:to]),
|
342
|
+
program_index: context.index_of(params[:program])
|
343
|
+
)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
```
|
347
|
+
|
420
348
|
## Practical Examples
|
421
349
|
|
422
350
|
### Complete SOL Transfer
|
@@ -425,12 +353,15 @@ Solace::Constants::MEMO_PROGRAM_ID # 'MemoSq4gqABAXKb96qnH8
|
|
425
353
|
require 'solace'
|
426
354
|
|
427
355
|
# Setup
|
428
|
-
connection = Solace::Connection.new('https://api.devnet.solana.com')
|
429
356
|
payer = Solace::Keypair.generate
|
430
357
|
recipient = Solace::Keypair.generate
|
431
358
|
|
359
|
+
# Create connection
|
360
|
+
connection = Solace::Connection.new('https://api.devnet.solana.com')
|
361
|
+
|
432
362
|
# Fund payer (devnet only)
|
433
|
-
connection.request_airdrop(payer.address, 1_000_000_000)
|
363
|
+
response = connection.request_airdrop(payer.address, 1_000_000_000)
|
364
|
+
connection.wait_for_confirmed_signature('finalized') { response['result'] }
|
434
365
|
|
435
366
|
# Build transfer instruction
|
436
367
|
transfer_ix = Solace::Instructions::SystemProgram::TransferInstruction.build(
|
@@ -455,9 +386,9 @@ message = Solace::Message.new(
|
|
455
386
|
# Sign and send
|
456
387
|
transaction = Solace::Transaction.new(message: message)
|
457
388
|
transaction.sign(payer)
|
458
|
-
signature = connection.send_transaction(transaction.serialize)
|
459
389
|
|
460
|
-
|
390
|
+
response = connection.send_transaction(transaction.serialize)
|
391
|
+
puts "Transaction: #{response['result']}"
|
461
392
|
```
|
462
393
|
|
463
394
|
### Complete Token Mint Creation
|
@@ -466,16 +397,19 @@ puts "Transaction: #{signature}"
|
|
466
397
|
require 'solace'
|
467
398
|
|
468
399
|
# Setup
|
469
|
-
connection = Solace::Connection.new('https://api.devnet.solana.com')
|
470
400
|
payer = Solace::Keypair.generate
|
471
401
|
mint_keypair = Solace::Keypair.generate
|
472
402
|
|
403
|
+
# Create connection
|
404
|
+
connection = Solace::Connection.new('https://api.devnet.solana.com')
|
405
|
+
|
473
406
|
# Fund payer
|
474
|
-
connection.request_airdrop(payer.address, 1_000_000_000)
|
407
|
+
response = connection.request_airdrop(payer.address, 1_000_000_000)
|
408
|
+
connection.wait_for_confirmed_signature('finalized') { response['result'] }
|
475
409
|
|
476
410
|
# High-level approach
|
477
|
-
|
478
|
-
signature =
|
411
|
+
program = Solace::Programs::SplToken.new(connection: connection)
|
412
|
+
signature = program.create_mint(
|
479
413
|
payer: payer,
|
480
414
|
decimals: 6,
|
481
415
|
mint_authority: payer,
|
@@ -487,49 +421,6 @@ puts "Mint created: #{mint_keypair.address}"
|
|
487
421
|
puts "Transaction: #{signature}"
|
488
422
|
```
|
489
423
|
|
490
|
-
### Manual Transaction Building
|
491
|
-
|
492
|
-
```ruby
|
493
|
-
# Low-level approach for maximum control
|
494
|
-
rent_lamports = connection.get_minimum_lamports_for_rent_exemption(82)
|
495
|
-
|
496
|
-
# Build instructions
|
497
|
-
create_account_ix = Solace::Instructions::SystemProgram::CreateAccountInstruction.build(
|
498
|
-
from_index: 0,
|
499
|
-
new_account_index: 1,
|
500
|
-
system_program_index: 4,
|
501
|
-
lamports: rent_lamports,
|
502
|
-
space: 82,
|
503
|
-
owner: Solace::Constants::TOKEN_PROGRAM_ID
|
504
|
-
)
|
505
|
-
|
506
|
-
initialize_mint_ix = Solace::Instructions::SplToken::InitializeMintInstruction.build(
|
507
|
-
mint_account_index: 1,
|
508
|
-
rent_sysvar_index: 2,
|
509
|
-
program_index: 3,
|
510
|
-
decimals: 6,
|
511
|
-
mint_authority: payer.address
|
512
|
-
)
|
513
|
-
|
514
|
-
# Assemble transaction
|
515
|
-
message = Solace::Message.new(
|
516
|
-
accounts: [
|
517
|
-
payer.address,
|
518
|
-
mint_keypair.address,
|
519
|
-
Solace::Constants::SYSVAR_RENT_PROGRAM_ID,
|
520
|
-
Solace::Constants::TOKEN_PROGRAM_ID,
|
521
|
-
Solace::Constants::SYSTEM_PROGRAM_ID
|
522
|
-
],
|
523
|
-
instructions: [create_account_ix, initialize_mint_ix],
|
524
|
-
recent_blockhash: connection.get_latest_blockhash,
|
525
|
-
header: [2, 0, 3] # 2 signers, 0 readonly signed, 3 readonly unsigned
|
526
|
-
)
|
527
|
-
|
528
|
-
transaction = Solace::Transaction.new(message: message)
|
529
|
-
transaction.sign(payer, mint_keypair)
|
530
|
-
signature = connection.send_transaction(transaction.serialize)
|
531
|
-
```
|
532
|
-
|
533
424
|
## Design Patterns
|
534
425
|
|
535
426
|
### Service Objects
|
@@ -539,43 +430,6 @@ Instruction builders follow the service object pattern:
|
|
539
430
|
- Consistent `.build()` interface
|
540
431
|
- Separate `.data()` methods for instruction data
|
541
432
|
|
542
|
-
### Serializable Records
|
543
|
-
Core data structures inherit from `SerializableRecord`:
|
544
|
-
- Automatic serialization/deserialization
|
545
|
-
- Consistent binary format handling
|
546
|
-
- SERIALIZER/DESERIALIZER constants pattern
|
547
|
-
|
548
|
-
### Mixin Modules
|
549
|
-
Shared functionality via mixins:
|
550
|
-
- `BinarySerializable` for serialization support
|
551
|
-
- `PDA` for Program Derived Address operations
|
552
|
-
- `Utils` modules for common operations
|
553
|
-
|
554
|
-
### Builder Pattern
|
555
|
-
High-level program methods use builder pattern:
|
556
|
-
- Fluent interfaces with keyword arguments
|
557
|
-
- Sensible defaults for optional parameters
|
558
|
-
- Automatic transaction assembly and signing
|
559
|
-
|
560
|
-
## Error Handling
|
561
|
-
|
562
|
-
The SDK provides comprehensive error handling:
|
563
|
-
|
564
|
-
```ruby
|
565
|
-
begin
|
566
|
-
signature = connection.send_transaction(transaction.serialize)
|
567
|
-
rescue StandardError => e
|
568
|
-
puts "Transaction failed: #{e.message}"
|
569
|
-
end
|
570
|
-
|
571
|
-
# PDA validation
|
572
|
-
begin
|
573
|
-
address = Solace::Utils::PDA.create_program_address(seeds, program_id)
|
574
|
-
rescue Solace::Utils::PDA::InvalidPDAError => e
|
575
|
-
puts "Invalid PDA: #{e.message}"
|
576
|
-
end
|
577
|
-
```
|
578
|
-
|
579
433
|
## Testing Support
|
580
434
|
|
581
435
|
The SDK includes comprehensive test utilities:
|
@@ -597,6 +451,7 @@ connection.wait_for_confirmed_signature { response['result'] }
|
|
597
451
|
## Dependencies
|
598
452
|
|
599
453
|
- **base58**: Base58 encoding/decoding
|
454
|
+
- **base64**: Base64 encoding/decoding
|
600
455
|
- **rbnacl**: Ed25519 cryptography
|
601
456
|
- **ffi**: Foreign Function Interface for native libraries
|
602
457
|
- **json**: JSON parsing for RPC
|