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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +12 -0
  3. data/README.md +140 -285
  4. data/lib/solace/address_lookup_table.rb +34 -18
  5. data/lib/solace/composers/base.rb +16 -11
  6. data/lib/solace/composers/spl_token_program_transfer_checked_composer.rb +27 -1
  7. data/lib/solace/composers/system_program_transfer_composer.rb +22 -1
  8. data/lib/solace/concerns/binary_serializable.rb +39 -0
  9. data/lib/solace/connection.rb +69 -38
  10. data/lib/solace/constants.rb +7 -14
  11. data/lib/solace/instruction.rb +30 -19
  12. data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +18 -3
  13. data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +24 -3
  14. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +18 -1
  15. data/lib/solace/instructions/spl_token/mint_to_instruction.rb +16 -3
  16. data/lib/solace/instructions/spl_token/transfer_checked_instruction.rb +17 -1
  17. data/lib/solace/instructions/spl_token/transfer_instruction.rb +15 -2
  18. data/lib/solace/instructions/system_program/create_account_instruction.rb +18 -3
  19. data/lib/solace/instructions/system_program/transfer_instruction.rb +15 -7
  20. data/lib/solace/keypair.rb +64 -31
  21. data/lib/solace/message.rb +22 -10
  22. data/lib/solace/programs/associated_token_account.rb +45 -20
  23. data/lib/solace/programs/base.rb +6 -0
  24. data/lib/solace/programs/spl_token.rb +52 -14
  25. data/lib/solace/public_key.rb +45 -20
  26. data/lib/solace/serializers/address_lookup_table_deserializer.rb +3 -5
  27. data/lib/solace/serializers/address_lookup_table_serializer.rb +7 -7
  28. data/lib/solace/serializers/base_deserializer.rb +29 -19
  29. data/lib/solace/serializers/base_serializer.rb +18 -9
  30. data/lib/solace/serializers/instruction_deserializer.rb +5 -7
  31. data/lib/solace/serializers/instruction_serializer.rb +4 -6
  32. data/lib/solace/serializers/message_deserializer.rb +3 -5
  33. data/lib/solace/serializers/message_serializer.rb +3 -5
  34. data/lib/solace/serializers/transaction_deserializer.rb +5 -7
  35. data/lib/solace/serializers/transaction_serializer.rb +5 -7
  36. data/lib/solace/transaction.rb +38 -23
  37. data/lib/solace/transaction_composer.rb +47 -13
  38. data/lib/solace/utils/account_context.rb +64 -65
  39. data/lib/solace/utils/codecs.rb +56 -128
  40. data/lib/solace/utils/curve25519_dalek.rb +9 -4
  41. data/lib/solace/utils/pda.rb +22 -24
  42. data/lib/solace/version.rb +2 -1
  43. data/lib/solace.rb +4 -9
  44. metadata +7 -10
  45. data/lib/solace/instructions/base.rb +0 -21
  46. data/lib/solace/serializable_record.rb +0 -26
  47. data/lib/solace/serializers/base.rb +0 -31
@@ -2,32 +2,57 @@
2
2
 
3
3
  module Solace
4
4
  module Utils
5
- # Internal utility for managing account context in transaction building
6
- # with automatic deduplication and sorting
5
+ # Utility for managing account context for composers
6
+ #
7
+ # This utility is used to manage the accounts in a transaction composer and instructions composer. It
8
+ # provides methods for managing the accounts and their permissions, as well as compiling the accounts
9
+ # into the final format required by the instruction builders. Concerns like deduplication and ordering
10
+ # are handled by this utility.
11
+ #
12
+ # @example Usage
13
+ # # Create a new account context
14
+ # context = Solace::Utils::AccountContext.new
15
+ #
16
+ # # Add accounts
17
+ # context.add_writable_signer('pubkey1')
18
+ # context.add_readonly_nonsigner('pubkey2')
19
+ #
20
+ # # Merge accounts from another context
21
+ # context = context.merge_from(other_context)
22
+ #
23
+ # # Set fee payer
24
+ # context.set_fee_payer('pubkey3')
25
+ #
26
+ # # Compile the accounts
27
+ # context.compile
28
+ #
29
+ # @see Solace::TransactionComposer
30
+ # @see Solace::Composers::Base
31
+ # @since 0.0.3
7
32
  class AccountContext
8
- # @!attribute DEFAULT_ACCOUNT
33
+ # @!attribute DEFAULT_ACCOUNT
9
34
  # The default account data
10
35
  #
11
36
  # @return [Hash] The default account data with lowest level of permissions
12
37
  DEFAULT_ACCOUNT = {
13
38
  signer: false,
14
39
  writable: false,
15
- fee_payer: false,
16
- }
17
-
18
- # @!attribute header
40
+ fee_payer: false
41
+ }.freeze
42
+
43
+ # @!attribute header
19
44
  # The header for the transaction
20
45
  #
21
46
  # @return [Array<Integer>] The header for the transaction
22
47
  attr_accessor :header
23
-
24
- # @!attribute accounts
48
+
49
+ # @!attribute accounts
25
50
  # The accounts in the transaction
26
51
  #
27
52
  # @return [Array<String>] The accounts
28
53
  attr_accessor :accounts
29
54
 
30
- # @!attribute pubkey_account_map
55
+ # @!attribute pubkey_account_map
31
56
  # The map of accounts
32
57
  #
33
58
  # @return [Hash] The map of accounts
@@ -42,36 +67,35 @@ module Solace
42
67
 
43
68
  # Set the fee payer account
44
69
  #
45
- # @param pubkey [Solace::Keypair | Solace::PublicKey | String] The pubkey of the fee payer account
70
+ # @param pubkey [Solace::Keypair, Solace::PublicKey, String] The pubkey of the fee payer account
46
71
  def set_fee_payer(pubkey)
47
72
  merge_account(pubkey, signer: true, writable: true, fee_payer: true)
48
73
  end
49
74
 
50
75
  # Add a signer account
51
76
  #
52
- # @param pubkey [Solace::Keypair | Solace::PublicKey | String] The pubkey of the signer account
77
+ # @param pubkey [Solace::Keypair, Solace::PublicKey, String] The pubkey of the signer account
53
78
  def add_writable_signer(pubkey)
54
79
  merge_account(pubkey, signer: true, writable: true)
55
80
  end
56
81
 
57
82
  # Add a writable account
58
83
  #
59
- # @param pubkey [Solace::Keypair | Solace::PublicKey | String] The pubkey of the writable account
84
+ # @param pubkey [Solace::Keypair, Solace::PublicKey, String] The pubkey of the writable account
60
85
  def add_writable_nonsigner(pubkey)
61
86
  merge_account(pubkey, signer: false, writable: true)
62
87
  end
63
-
88
+
64
89
  # Add a readonly signer account
65
90
  #
66
- # @param pubkey [Solace::Keypair | Solace::PublicKey | String] The pubkey of the readonly signer account
91
+ # @param pubkey [Solace::Keypair, Solace::PublicKey, String] The pubkey of the readonly signer account
67
92
  def add_readonly_signer(pubkey)
68
93
  merge_account(pubkey, signer: true, writable: false)
69
94
  end
70
-
71
95
 
72
96
  # Add a readonly account
73
97
  #
74
- # @param pubkey [Solace::Keypair | Solace::PublicKey | String] The pubkey of the readonly account
98
+ # @param pubkey [Solace::Keypair, Solace::PublicKey, String] The pubkey of the readonly account
75
99
  def add_readonly_nonsigner(pubkey)
76
100
  merge_account(pubkey, signer: false, writable: false)
77
101
  end
@@ -99,7 +123,7 @@ module Solace
99
123
  def writable?(pubkey)
100
124
  @pubkey_account_map[pubkey].try { |acc| acc[:writable] }
101
125
  end
102
-
126
+
103
127
  # Predicate to check if an account is a writable signer
104
128
  #
105
129
  # @param pubkey [String] The pubkey of the account
@@ -107,7 +131,7 @@ module Solace
107
131
  def writable_signer?(pubkey)
108
132
  @pubkey_account_map[pubkey].try { |acc| acc[:signer] && acc[:writable] }
109
133
  end
110
-
134
+
111
135
  # Predicate to check if an account is writable and not a signer
112
136
  #
113
137
  # @param pubkey [String] The pubkey of the account
@@ -131,23 +155,19 @@ module Solace
131
155
  def readonly_nonsigner?(pubkey)
132
156
  @pubkey_account_map[pubkey].try { |acc| !acc[:signer] && !acc[:writable] }
133
157
  end
134
-
158
+
135
159
  # Merge all accounts from another AccountContext into this one
136
160
  #
137
161
  # @param other_context [AccountContext] The other context to merge from
138
162
  def merge_from(other_context)
139
163
  other_context.pubkey_account_map.each do |pubkey, data|
140
- merge_account(
141
- pubkey,
142
- signer: data[:signer],
143
- writable: data[:writable],
144
- fee_payer: data[:fee_payer]
145
- )
164
+ signer, writable, fee_payer = data.values_at(:signer, :writable, :fee_payer)
165
+ merge_account(pubkey, signer: signer, writable: writable, fee_payer: fee_payer)
146
166
  end
147
167
  end
148
168
 
149
169
  # Compile accounts into final format
150
- #
170
+ #
151
171
  # Gets unique accounts and sorts them in the following order:
152
172
  # - Signers first (Solana requirement)
153
173
  # - Then writable accounts
@@ -156,10 +176,10 @@ module Solace
156
176
  # @return [Hash] The compiled accounts and header
157
177
  def compile
158
178
  self.header = calculate_header
159
- self.accounts = order_accounts
179
+ self.accounts = order_accounts
160
180
  self
161
181
  end
162
-
182
+
163
183
  # Index of a pubkey in the accounts array
164
184
  #
165
185
  # @param pubkey_str [String] The public key of the account
@@ -170,7 +190,7 @@ module Solace
170
190
 
171
191
  # Get map of indicies for pubkeys in accounts array
172
192
  #
173
- # @return [Hash<String, Integer>] The indices of the pubkeys in the accounts array
193
+ # @return [Hash{String => Integer}] The indices of the pubkeys in the accounts array
174
194
  def indices
175
195
  accounts.each_with_index.to_h
176
196
  end
@@ -179,22 +199,18 @@ module Solace
179
199
 
180
200
  # Add or merge an account into the context
181
201
  #
182
- # @param pubkey [String | Solace::PublicKey | Solace::Keypair] The public key of the account
202
+ # @param pubkey [String, Solace::PublicKey, Solace::Keypair] The public key of the account
183
203
  # @param signer [Boolean] Whether the account is a signer
184
204
  # @param writable [Boolean] Whether the account is writable
185
- def merge_account(
186
- pubkey,
187
- signer:,
188
- writable:,
189
- fee_payer: false
190
- )
191
- pubkey_str = resolve_pubkey(pubkey)
205
+ # @param [Boolean] fee_payer
206
+ def merge_account(pubkey, signer:, writable:, fee_payer: false)
207
+ pubkey_str = pubkey.is_a?(String) ? pubkey : pubkey.address
192
208
 
193
209
  @pubkey_account_map[pubkey_str] ||= DEFAULT_ACCOUNT.dup
194
210
  @pubkey_account_map[pubkey_str][:signer] ||= signer
195
211
  @pubkey_account_map[pubkey_str][:writable] ||= writable
196
212
  @pubkey_account_map[pubkey_str][:fee_payer] ||= fee_payer
197
-
213
+
198
214
  self
199
215
  end
200
216
 
@@ -203,16 +219,11 @@ module Solace
203
219
  # @return [Array<String>] The ordered accounts
204
220
  def order_accounts
205
221
  @pubkey_account_map.keys.sort_by do |pubkey|
206
- if fee_payer?(pubkey)
207
- 0
208
- elsif writable_signer?(pubkey)
209
- 1
210
- elsif readonly_signer?(pubkey)
211
- 2
212
- elsif writable_nonsigner?(pubkey)
213
- 2
214
- elsif readonly_nonsigner?(pubkey)
215
- 3
222
+ if fee_payer?(pubkey) then 0
223
+ elsif writable_signer?(pubkey) then 1
224
+ elsif readonly_signer?(pubkey) then 2
225
+ elsif writable_nonsigner?(pubkey) then 3
226
+ elsif readonly_nonsigner?(pubkey) then 4
216
227
  else
217
228
  raise ArgumentError, "Unknown account type for pubkey: #{pubkey}"
218
229
  end
@@ -228,26 +239,14 @@ module Solace
228
239
  #
229
240
  # @return [Array] The header for the transaction
230
241
  def calculate_header
231
- @pubkey_account_map.keys.reduce([0, 0, 0]) do |acc, pubkey|
242
+ @pubkey_account_map.keys.each_with_object([0, 0, 0]) do |pubkey, acc|
232
243
  acc[0] += 1 if signer?(pubkey)
233
-
234
- if readonly_signer?(pubkey)
235
- acc[1] += 1
236
- elsif readonly_nonsigner?(pubkey)
237
- acc[2] += 1
238
- end
239
244
 
240
- acc
245
+ if readonly_signer?(pubkey) then acc[1] += 1
246
+ elsif readonly_nonsigner?(pubkey) then acc[2] += 1
247
+ end
241
248
  end
242
249
  end
243
-
244
- # Resolve the pubkey from a Solace::PublicKey or String
245
- #
246
- # @param pubkey [String|Solace::PublicKey] The pubkey to resolve
247
- # @return [String] The resolved pubkey
248
- def resolve_pubkey(pubkey)
249
- pubkey.is_a?(String) ? pubkey : pubkey.address
250
- end
251
250
  end
252
251
  end
253
252
  end
@@ -7,206 +7,134 @@ require 'stringio'
7
7
 
8
8
  module Solace
9
9
  module Utils
10
+ # Module for encoding and decoding data
11
+ #
12
+ # @since 0.0.1
10
13
  module Codecs
11
- # =============================
12
- # Helper: IO Stream
13
- # =============================
14
- #
15
14
  # Creates a StringIO from a base64 string.
16
15
  #
17
- # Args:
18
- # base64 (String): The base64 string to decode
19
- #
20
- # Returns:
21
- # StringIO: A StringIO object containing the decoded bytes
22
- #
16
+ # @param base64 [String] The base64 string to decode
17
+ # @return [StringIO] A StringIO object containing the decoded bytes
23
18
  def self.base64_to_bytestream(base64)
24
19
  StringIO.new(Base64.decode64(base64))
25
20
  end
26
21
 
27
- # =============================
28
- # Helper: Compact-u16 Encoding (ShortVec)
29
- # =============================
30
- #
31
- # Encodes a u16 value in a compact form
32
- #
33
- # Args:
34
- # n (Integer): The u16 value to encode
22
+ # Encodes a compact-u16 value in a compact form (shortvec)
35
23
  #
36
- # Returns:
37
- # String: The compactly encoded u16 value
38
- #
39
- def self.encode_compact_u16(n)
24
+ # @param u16 [Integer] The compact-u16 value to encode
25
+ # @return [String] The compactly encoded compact-u16 value
26
+ def self.encode_compact_u16(u16)
40
27
  out = []
28
+
41
29
  loop do
42
30
  # In general, n >> 7 shifts the bits of n to the right by
43
31
  # 7 positions, effectively dividing n by 128 and discarding
44
32
  # the remainder (integer division). This is commonly used in
45
33
  # encoding schemes to process one "byte" (7 bits) at a time.
46
- if (n >> 7).zero?
47
- out << n
34
+ if (u16 >> 7).zero?
35
+ out << u16
48
36
  break
49
- else
50
- # The expression out << ((n & 0x7F) | 0x80) is used in variable-length
51
- # integer encoding, such as the compact-u16 encoding.
52
- #
53
- # n & 0x7F:
54
- # - 0x7F is 127 in decimal, or 0111 1111 in binary.
55
- # - n & 0x7F masks out all but the lowest 7 bits of n. This extracts the least significant 7 bits of n.
56
- #
57
- # (n & 0x7F) | 0x80:
58
- # - 0x80 is 128 in decimal, or 1000 0000 in binary.
59
- # - | (bitwise OR) sets the highest bit (the 8th bit) to 1.
60
- # - This is a signal that there are more bytes to come in the encoding (i.e., the value hasn't been fully encoded yet).
61
- #
62
- # out << ...:
63
- # - This appends the resulting byte to the out array.
64
- out << ((n & 0x7F) | 0x80)
65
- n >>= 7
66
37
  end
38
+ # The expression out << ((n & 0x7F) | 0x80) is used in variable-length
39
+ # integer encoding, such as the compact-u16 encoding.
40
+ #
41
+ # n & 0x7F:
42
+ # - 0x7F is 127 in decimal, or 0111 1111 in binary.
43
+ # - n & 0x7F masks out all but the lowest 7 bits of n. This extracts the least significant 7 bits of n.
44
+ #
45
+ # (n & 0x7F) | 0x80:
46
+ # - 0x80 is 128 in decimal, or 1000 0000 in binary.
47
+ # - | (bitwise OR) sets the highest bit (the 8th bit) to 1.
48
+ # - This is a signal that there are more bytes to come in the encoding (i.e., the value hasn't been fully
49
+ # encoded yet).
50
+ #
51
+ # out << ...:
52
+ # - This appends the resulting byte to the out array.
53
+ out << ((u16 & 0x7F) | 0x80)
54
+ u16 >>= 7
67
55
  end
56
+
68
57
  out.pack('C*')
69
58
  end
70
59
 
71
- # =============================
72
- # Helper: Compact-u16 Decoding (ShortVec)
73
- # =============================
74
- #
75
60
  # Decodes a compact-u16 (ShortVec) value from an IO-like object.
76
- # Reads bytes one at a time, accumulating the result until the MSB is 0.
77
- #
78
- # Args:
79
- # io (IO or StringIO): The input to read bytes from.
80
61
  #
81
- # Returns:
82
- # [Integer, Integer]: The decoded value and the number of bytes read.
62
+ # Reads bytes one at a time, accumulating the result until the MSB is 0.
83
63
  #
84
- def self.decode_compact_u16(io)
64
+ # @param stream [IO, StringIO] The input to read bytes from.
65
+ # @return [Integer, Integer] The decoded value and the number of bytes read.
66
+ def self.decode_compact_u16(stream)
85
67
  value = 0
86
68
  shift = 0
87
69
  bytes_read = 0
70
+
88
71
  loop do
89
- byte = io.read(1)
72
+ byte = stream.read(1)
90
73
  raise EOFError, 'Unexpected end of input while decoding compact-u16' unless byte
91
74
 
92
75
  byte = byte.ord
93
76
  value |= (byte & 0x7F) << shift
94
77
  bytes_read += 1
95
- break if (byte & 0x80).zero?
78
+ break if byte.nobits?(0x80)
96
79
 
97
80
  shift += 7
98
81
  end
82
+
99
83
  [value, bytes_read]
100
84
  end
101
85
 
102
- # =============================
103
- # Helper: Little-Endian u64 Encoding
104
- # =============================
105
- #
106
86
  # Encodes a u64 value in little-endian format
107
87
  #
108
- # Args:
109
- # n (Integer): The u64 value to encode
110
- #
111
- # Returns:
112
- # String: The little-endian encoded u64 value
113
- #
114
- def self.encode_le_u64(n)
115
- [n].pack('Q<') # 64-bit little-endian
88
+ # @param u64 [Integer] The u64 value to encode
89
+ # @return [String] The little-endian encoded u64 value
90
+ def self.encode_le_u64(u64)
91
+ [u64].pack('Q<') # 64-bit little-endian
116
92
  end
117
93
 
118
- # =============================
119
- # Helper: Little-Endian u64 Decoding
120
- # =============================
121
- #
122
94
  # Decodes a little-endian u64 value from a sequence of bytes
123
95
  #
124
- # Args:
125
- # io (IO or StringIO): The input to read bytes from.
126
- #
127
- # Returns:
128
- # Integer: The decoded u64 value
129
- #
130
- def self.decode_le_u64(io)
131
- io.read(8).unpack1('Q<')
96
+ # @param stream [IO, StringIO] The input to read bytes from.
97
+ # @return [Integer] The decoded u64 value
98
+ def self.decode_le_u64(stream)
99
+ stream.read(8).unpack1('Q<')
132
100
  end
133
101
 
134
- # =============================
135
- # Helper: Binary to Base58 Encoding
136
- # =============================
137
- #
138
102
  # Encodes a sequence of bytes in Base58 format
139
103
  #
140
- # Args:
141
- # bytes (String): The bytes to encode
142
- #
143
- # Returns:
144
- # String: The Base58 encoded string
145
- #
104
+ # @param binary [String] The bytes to encode
105
+ # @return [String] The Base58 encoded string
146
106
  def self.binary_to_base58(binary)
147
107
  Base58.binary_to_base58(binary, :bitcoin)
148
108
  end
149
109
 
150
- # =============================
151
- # Helper: Base58 Decoding
152
- # =============================
153
- #
154
110
  # Decodes a Base58 string into a binary string
155
111
  #
156
- # Args:
157
- # string (String): The Base58 encoded string
158
- #
159
- # Returns:
160
- # String: The decoded binary string
161
- #
112
+ # @param string [String] The Base58 encoded string
113
+ # @return [String] The decoded binary string
162
114
  def self.base58_to_binary(string)
163
115
  base58_to_bytes(string).pack('C*')
164
116
  end
165
117
 
166
- # =============================
167
- # Helper: Base58 Encoding
168
- # =============================
169
- #
170
118
  # Encodes a sequence of bytes in Base58 format
171
119
  #
172
- # Args:
173
- # bytes (String): The bytes to encode
174
- #
175
- # Returns:
176
- # String: The Base58 encoded string
177
- #
120
+ # @param bytes [String] The bytes to encode
121
+ # @return [String] The Base58 encoded string
178
122
  def self.bytes_to_base58(bytes)
179
123
  binary_to_base58(bytes.pack('C*'))
180
124
  end
181
125
 
182
- # =============================
183
- # Helper: Base58 Decoding
184
- # =============================
185
- #
186
126
  # Decodes a Base58 string into a sequence of bytes
187
127
  #
188
- # Args:
189
- # string (String): The Base58 encoded string
190
- #
191
- # Returns:
192
- # String: The decoded bytes
193
- #
128
+ # @param string [String] The Base58 encoded string
129
+ # @return [String] The decoded bytes
194
130
  def self.base58_to_bytes(string)
195
131
  Base58.base58_to_binary(string, :bitcoin).bytes
196
132
  end
197
133
 
198
- # =============================
199
- # Helper: Base58 Validation
200
- # =============================
201
- #
202
134
  # Checks if a string is a valid Base58 string
203
135
  #
204
- # Args:
205
- # string (String): The string to check
206
- #
207
- # Returns:
208
- # Boolean: True if the string is a valid Base58 string, false otherwise
209
- #
136
+ # @param string [String] The string to check
137
+ # @return [Boolean] True if the string is a valid Base58 string, false otherwise
210
138
  def self.valid_base58?(string)
211
139
  return false if string.nil? || string.empty?
212
140
 
@@ -5,6 +5,14 @@ require 'ffi'
5
5
 
6
6
  module Solace
7
7
  module Utils
8
+ # Module for interacting with the Curve25519 Dalek library
9
+ #
10
+ # This module provides a wrapper around the Curve25519 Dalek library, which is a pure-Rust
11
+ # implementation of the Curve25519 elliptic curve. It uses FFI to interface with the native
12
+ # rust library when checking if a point is on the curve.
13
+ #
14
+ # @see Solace::Utils::PDA
15
+ # @since 0.0.2
8
16
  module Curve25519Dalek
9
17
  extend FFI::Library
10
18
 
@@ -22,8 +30,7 @@ module Solace
22
30
  else raise 'Unsupported platform'
23
31
  end
24
32
 
25
- # The path to the native library
26
- #
33
+ # !@attribute LIB_PATH
27
34
  # @return [String] The path to the native library
28
35
  LIB_PATH = File.expand_path(libfile, __dir__)
29
36
 
@@ -31,8 +38,6 @@ module Solace
31
38
  ffi_lib LIB_PATH
32
39
 
33
40
  # Attach the native function
34
- #
35
- # @return [FFI::Function] The native function
36
41
  attach_function :is_on_curve, [:pointer], :int
37
42
 
38
43
  # Checks if a point is on the curve
@@ -5,27 +5,34 @@ require 'digest'
5
5
 
6
6
  module Solace
7
7
  module Utils
8
+ # Module for generating program addresses
9
+ #
10
+ # This module provides methods for generating program addresses from seeds and program IDs. It interfaces
11
+ # with the Curve25519 Dalek library to check if a point is on the curve. It also provides a method for
12
+ # converting seeds to bytes and a method for checking if a string looks like a base58 address.
13
+ #
14
+ # @see Solace::Utils::Curve25519Dalek
15
+ # @since 0.0.1
8
16
  module PDA
9
- # !@class InvalidPDAError
10
- # An error raised when an invalid PDA is generated
11
- #
12
- # @return [StandardError]
17
+ # InvalidPDAError is an error raised when an invalid PDA is generated
13
18
  class InvalidPDAError < StandardError; end
14
19
 
15
- # !@const PDA_MARKER
16
- # The marker used in PDA calculations
17
- #
18
- # @return [String]
20
+ # !@attribute PDA_MARKER
21
+ # PDA_MARKER is the marker used in PDA calculations
19
22
  PDA_MARKER = 'ProgramDerivedAddress'
20
23
 
21
- # !@const MAX_BUMP_SEED
22
- # The maximum seed value for PDA calculations
23
- #
24
- # @return [Integer]
24
+ # !@attribute MAX_BUMP_SEED
25
+ # The maximum seed value for PDA calculations
25
26
  MAX_BUMP_SEED = 255
26
27
 
27
28
  # Finds a valid program address by trying different seeds
28
29
  #
30
+ # @example Find a PDA with bump seed
31
+ # seeds = ['metadata', mint_address, 'edition']
32
+ # program_id = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'
33
+ #
34
+ # address, bump = Solace::Utils::PDA.find_program_address(seeds, program_id)
35
+ #
29
36
  # @param seeds [Array] The seeds to use in the calculation
30
37
  # @param program_id [String] The program ID to use in the calculation
31
38
  # @return [Array] The program address and bump seed
@@ -68,17 +75,9 @@ module Solace
68
75
  def self.seed_to_bytes(seed)
69
76
  case seed
70
77
  when String
71
- if looks_like_base58_address?(seed)
72
- Solace::Utils::Codecs.base58_to_bytes(seed)
73
- else
74
- seed.bytes
75
- end
78
+ looks_like_base58_address?(seed) ? Solace::Utils::Codecs.base58_to_bytes(seed) : seed.bytes
76
79
  when Integer
77
- if seed.between?(0, 255)
78
- [seed]
79
- else
80
- seed.digits(256)
81
- end
80
+ seed.between?(0, 255) ? [seed] : seed.digits(256)
82
81
  when Array
83
82
  seed
84
83
  else
@@ -91,8 +90,7 @@ module Solace
91
90
  # @param string [String] The string to check
92
91
  # @return [Boolean] True if the string looks like a base58 address, false otherwise
93
92
  def self.looks_like_base58_address?(string)
94
- string.length >= 32 &&
95
- string.length <= 44 &&
93
+ string.length.between?(32, 44) &&
96
94
  Solace::Utils::Codecs.valid_base58?(string)
97
95
  end
98
96
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Solace::VERSION is the version of the Solace gem
3
4
  module Solace
4
- VERSION = '0.0.3'
5
+ VERSION = '0.0.5'
5
6
  end
data/lib/solace.rb CHANGED
@@ -13,13 +13,9 @@ require_relative 'solace/utils/curve25519_dalek'
13
13
  require_relative 'solace/concerns/binary_serializable'
14
14
 
15
15
  # ✨ Serializers
16
- require_relative 'solace/serializers/base'
17
16
  require_relative 'solace/serializers/base_serializer'
18
17
  require_relative 'solace/serializers/base_deserializer'
19
18
 
20
- # Base classes
21
- require_relative 'solace/serializable_record'
22
-
23
19
  # 🧬 Primitives
24
20
  require_relative 'solace/keypair'
25
21
  require_relative 'solace/public_key'
@@ -30,17 +26,16 @@ require_relative 'solace/address_lookup_table'
30
26
  require_relative 'solace/transaction_composer'
31
27
 
32
28
  # 📦 Composers (Builders)
33
- #
29
+ #
34
30
  # Glob require all instructions
35
- Dir[File.join(__dir__, 'solace/composers', '**', '*.rb')].sort.each { |file| require file }
31
+ Dir[File.join(__dir__, 'solace/composers', '**', '*.rb')].each { |file| require file }
36
32
 
37
33
  # 📦 Instructions (Builders)
38
- #
34
+ #
39
35
  # Glob require all instructions
40
- Dir[File.join(__dir__, 'solace/instructions', '**', '*.rb')].sort.each { |file| require file }
36
+ Dir[File.join(__dir__, 'solace/instructions', '**', '*.rb')].each { |file| require file }
41
37
 
42
38
  # 📦 Programs
43
39
  require_relative 'solace/programs/base'
44
40
  require_relative 'solace/programs/spl_token'
45
41
  require_relative 'solace/programs/associated_token_account'
46
-