solace 0.0.2 → 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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +57 -0
  3. data/LICENSE +21 -0
  4. data/README.md +142 -287
  5. data/lib/solace/address_lookup_table.rb +34 -18
  6. data/lib/solace/composers/base.rb +45 -0
  7. data/lib/solace/composers/spl_token_program_transfer_checked_composer.rb +113 -0
  8. data/lib/solace/composers/system_program_transfer_composer.rb +80 -0
  9. data/lib/solace/concerns/binary_serializable.rb +39 -0
  10. data/lib/solace/connection.rb +101 -44
  11. data/lib/solace/constants.rb +7 -14
  12. data/lib/solace/instruction.rb +30 -19
  13. data/lib/solace/instructions/associated_token_account/create_associated_token_account_instruction.rb +18 -3
  14. data/lib/solace/instructions/spl_token/initialize_account_instruction.rb +24 -3
  15. data/lib/solace/instructions/spl_token/initialize_mint_instruction.rb +18 -1
  16. data/lib/solace/instructions/spl_token/mint_to_instruction.rb +16 -3
  17. data/lib/solace/instructions/spl_token/transfer_checked_instruction.rb +76 -0
  18. data/lib/solace/instructions/spl_token/transfer_instruction.rb +15 -2
  19. data/lib/solace/instructions/system_program/create_account_instruction.rb +18 -3
  20. data/lib/solace/instructions/system_program/transfer_instruction.rb +58 -0
  21. data/lib/solace/keypair.rb +64 -31
  22. data/lib/solace/message.rb +22 -10
  23. data/lib/solace/programs/associated_token_account.rb +58 -11
  24. data/lib/solace/programs/base.rb +6 -0
  25. data/lib/solace/programs/spl_token.rb +52 -14
  26. data/lib/solace/public_key.rb +45 -20
  27. data/lib/solace/serializers/address_lookup_table_deserializer.rb +3 -5
  28. data/lib/solace/serializers/address_lookup_table_serializer.rb +7 -7
  29. data/lib/solace/serializers/base_deserializer.rb +29 -19
  30. data/lib/solace/serializers/base_serializer.rb +18 -9
  31. data/lib/solace/serializers/instruction_deserializer.rb +5 -7
  32. data/lib/solace/serializers/instruction_serializer.rb +4 -6
  33. data/lib/solace/serializers/message_deserializer.rb +3 -5
  34. data/lib/solace/serializers/message_serializer.rb +3 -5
  35. data/lib/solace/serializers/transaction_deserializer.rb +5 -7
  36. data/lib/solace/serializers/transaction_serializer.rb +5 -7
  37. data/lib/solace/transaction.rb +38 -23
  38. data/lib/solace/transaction_composer.rb +115 -0
  39. data/lib/solace/utils/account_context.rb +252 -0
  40. data/lib/solace/utils/codecs.rb +56 -128
  41. data/lib/solace/utils/curve25519_dalek.rb +9 -4
  42. data/lib/solace/utils/pda.rb +22 -24
  43. data/lib/solace/version.rb +2 -1
  44. data/lib/solace.rb +9 -7
  45. metadata +15 -12
  46. data/lib/solace/instructions/transfer_checked_instruction.rb +0 -58
  47. data/lib/solace/instructions/transfer_instruction.rb +0 -48
  48. data/lib/solace/serializable_record.rb +0 -26
  49. data/lib/solace/serializers/base.rb +0 -31
@@ -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.2'
5
+ VERSION = '0.0.5'
5
6
  end
data/lib/solace.rb CHANGED
@@ -8,17 +8,14 @@ require_relative 'solace/constants'
8
8
  require_relative 'solace/connection'
9
9
  require_relative 'solace/utils/codecs'
10
10
  require_relative 'solace/utils/pda'
11
+ require_relative 'solace/utils/account_context'
11
12
  require_relative 'solace/utils/curve25519_dalek'
12
13
  require_relative 'solace/concerns/binary_serializable'
13
14
 
14
15
  # ✨ Serializers
15
- require_relative 'solace/serializers/base'
16
16
  require_relative 'solace/serializers/base_serializer'
17
17
  require_relative 'solace/serializers/base_deserializer'
18
18
 
19
- # Base classes
20
- require_relative 'solace/serializable_record'
21
-
22
19
  # 🧬 Primitives
23
20
  require_relative 'solace/keypair'
24
21
  require_relative 'solace/public_key'
@@ -26,14 +23,19 @@ require_relative 'solace/transaction'
26
23
  require_relative 'solace/message'
27
24
  require_relative 'solace/instruction'
28
25
  require_relative 'solace/address_lookup_table'
26
+ require_relative 'solace/transaction_composer'
27
+
28
+ # 📦 Composers (Builders)
29
+ #
30
+ # Glob require all instructions
31
+ Dir[File.join(__dir__, 'solace/composers', '**', '*.rb')].each { |file| require file }
29
32
 
30
33
  # 📦 Instructions (Builders)
31
- #
34
+ #
32
35
  # Glob require all instructions
33
- Dir[File.join(__dir__, 'solace/instructions', '**', '*.rb')].sort.each { |file| require file }
36
+ Dir[File.join(__dir__, 'solace/instructions', '**', '*.rb')].each { |file| require file }
34
37
 
35
38
  # 📦 Programs
36
39
  require_relative 'solace/programs/base'
37
40
  require_relative 'solace/programs/spl_token'
38
41
  require_relative 'solace/programs/associated_token_account'
39
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Scholl
@@ -52,38 +52,38 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '7.0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: rake
55
+ name: minitest
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '13.0'
60
+ version: '5.0'
61
61
  type: :development
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '13.0'
67
+ version: '5.0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: minitest
69
+ name: rake
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '5.0'
74
+ version: '13.0'
75
75
  type: :development
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '5.0'
81
+ version: '13.0'
82
82
  description: A Ruby library for working with Solana blockchain. Provides both low-level
83
83
  instruction builders and high-level program clients for interacting with Solana
84
84
  programs.
85
85
  email:
86
- - sebastian@scholl.io
86
+ - sebscholl@gmail.com
87
87
  executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
@@ -93,6 +93,9 @@ files:
93
93
  - README.md
94
94
  - lib/solace.rb
95
95
  - lib/solace/address_lookup_table.rb
96
+ - lib/solace/composers/base.rb
97
+ - lib/solace/composers/spl_token_program_transfer_checked_composer.rb
98
+ - lib/solace/composers/system_program_transfer_composer.rb
96
99
  - lib/solace/concerns/binary_serializable.rb
97
100
  - lib/solace/connection.rb
98
101
  - lib/solace/constants.rb
@@ -101,20 +104,18 @@ files:
101
104
  - lib/solace/instructions/spl_token/initialize_account_instruction.rb
102
105
  - lib/solace/instructions/spl_token/initialize_mint_instruction.rb
103
106
  - lib/solace/instructions/spl_token/mint_to_instruction.rb
107
+ - lib/solace/instructions/spl_token/transfer_checked_instruction.rb
104
108
  - lib/solace/instructions/spl_token/transfer_instruction.rb
105
109
  - lib/solace/instructions/system_program/create_account_instruction.rb
106
- - lib/solace/instructions/transfer_checked_instruction.rb
107
- - lib/solace/instructions/transfer_instruction.rb
110
+ - lib/solace/instructions/system_program/transfer_instruction.rb
108
111
  - lib/solace/keypair.rb
109
112
  - lib/solace/message.rb
110
113
  - lib/solace/programs/associated_token_account.rb
111
114
  - lib/solace/programs/base.rb
112
115
  - lib/solace/programs/spl_token.rb
113
116
  - lib/solace/public_key.rb
114
- - lib/solace/serializable_record.rb
115
117
  - lib/solace/serializers/address_lookup_table_deserializer.rb
116
118
  - lib/solace/serializers/address_lookup_table_serializer.rb
117
- - lib/solace/serializers/base.rb
118
119
  - lib/solace/serializers/base_deserializer.rb
119
120
  - lib/solace/serializers/base_serializer.rb
120
121
  - lib/solace/serializers/instruction_deserializer.rb
@@ -124,6 +125,8 @@ files:
124
125
  - lib/solace/serializers/transaction_deserializer.rb
125
126
  - lib/solace/serializers/transaction_serializer.rb
126
127
  - lib/solace/transaction.rb
128
+ - lib/solace/transaction_composer.rb
129
+ - lib/solace/utils/account_context.rb
127
130
  - lib/solace/utils/codecs.rb
128
131
  - lib/solace/utils/curve25519_dalek.rb
129
132
  - lib/solace/utils/libcurve25519_dalek-linux/libcurve25519_dalek.so
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solace
4
- module Instructions
5
- # Service object for building an SPL Token Program transfer instruction
6
- class TransferCheckedInstruction
7
- # SPL Token Program instruction index for Transfer Checked
8
- INSTRUCTION_INDEX = [12].freeze
9
-
10
- # Builds a Solace::Instruction for transferring SPL tokens
11
- #
12
- # SPL Token Program transfer instruction layout:
13
- # - 1 byte: instruction index (12 for transfer checked)
14
- # - 8 bytes: amount (u64, little-endian)
15
- # - 8 bytes: decimals (u64, little-endian)
16
- #
17
- # @param amount [Integer] Amount to transfer (in tokens, according to mint's decimals)
18
- # @param decimals [Integer] Number of decimals for the token
19
- # @param to_index [Integer] Index of the destination token account in the transaction's accounts
20
- # @param from_index [Integer] Index of the source token account in the transaction's accounts
21
- # @param mint_index [Integer] Index of the mint in the transaction's accounts
22
- # @param authority_index [Integer] Index of the authority (owner) in the transaction's accounts
23
- # @param program_index [Integer] Index of the SPL Token Program in the transaction's accounts (default: 3)
24
- # @return [Solace::Instruction]
25
- def self.build(
26
- amount:,
27
- decimals:,
28
- to_index:,
29
- from_index:,
30
- mint_index:,
31
- authority_index:,
32
- program_index: 3
33
- )
34
- Solace::Instruction.new.tap do |ix|
35
- ix.program_index = program_index
36
- ix.accounts = [from_index, mint_index, to_index, authority_index]
37
- ix.data = data(amount, decimals)
38
- end
39
- end
40
-
41
- # Instruction data for a token transfer instruction
42
- #
43
- # The BufferLayout is:
44
- # - [Instruction Index (1 byte)]
45
- # - [Amount (8 bytes little-endian u64)]
46
- # - [Decimals (8 bytes little-endian u64)]
47
- #
48
- # @param amount [Integer] Amount to transfer
49
- # @param decimals [Integer] Number of decimals for the token
50
- # @return [Array] 1-byte instruction index + 8-byte amount + decimals
51
- def self.data(amount, decimals)
52
- INSTRUCTION_INDEX +
53
- Solace::Utils::Codecs.encode_le_u64(amount).bytes +
54
- [decimals]
55
- end
56
- end
57
- end
58
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solace
4
- module Instructions
5
- # Service object for building a System Program transfer instruction
6
- class TransferInstruction
7
- # Instruction ID for System Transfer
8
- INSTRUCTION_ID = [2, 0, 0, 0].freeze
9
-
10
- # Builds a Solace::Instruction for transferring SOL
11
- #
12
- # System Program transfer instruction layout:
13
- # - 4 bytes: instruction index (0 for transfer)
14
- # - 8 bytes: amount (u64, little-endian)
15
- #
16
- # @param lamports [Integer] Amount to transfer (in lamports)
17
- # @param to_index [Integer] Index of the recipient in the transaction's accounts
18
- # @param from_index [Integer] Index of the sender in the transaction's accounts
19
- # @param program_index [Integer] Index of the program in the transaction's accounts (default: 2)
20
- # @return [Solace::Instruction]
21
- def self.build(
22
- lamports:,
23
- to_index:,
24
- from_index:,
25
- program_index: 2
26
- )
27
- Solace::Instruction.new.tap do |ix|
28
- ix.program_index = program_index
29
- ix.accounts = [from_index, to_index]
30
- ix.data = data(lamports)
31
- end
32
- end
33
-
34
- # Instruction data for a transfer instruction
35
- #
36
- # The BufferLayout is:
37
- # - [Instruction ID (4 bytes)]
38
- # - [Amount (8 bytes little-endian u64)]
39
- #
40
- # @param lamports [Integer] Amount to transfer (in lamports)
41
- # @return [Array] 4-byte instruction ID + 8-byte amount
42
- def self.data(lamports)
43
- INSTRUCTION_ID +
44
- Solace::Utils::Codecs.encode_le_u64(lamports).bytes
45
- end
46
- end
47
- end
48
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solace
4
- class SerializableRecord
5
- include Solace::Concerns::BinarySerializable
6
-
7
- # Parse instruction from io stream
8
- #
9
- # @param io [IO or StringIO] The input to read bytes from.
10
- # @return [Solace::Instruction] Parsed instruction object
11
- def self.deserialize(io)
12
- self::DESERIALIZER.call(io)
13
- rescue NameError => e
14
- raise "DESERIALIZER must be defined: #{e.message}"
15
- end
16
-
17
- # Serializes the transaction to a binary format
18
- #
19
- # @return [String] The serialized transaction (binary)
20
- def serialize
21
- self.class::SERIALIZER.call(self)
22
- rescue NameError => e
23
- raise "SERIALIZER must be defined: #{e.message}"
24
- end
25
- end
26
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solace
4
- module Serializers
5
- class Base
6
- include Solace::Utils
7
-
8
- # Proxy method to call the serializer and create a new instance
9
- #
10
- # @return [String] The serialized record (base64)
11
- def self.call(*args, **kwargs)
12
- new(*args, **kwargs).call
13
- end
14
-
15
- # Serializes the record
16
- #
17
- # @return [String] The serialized record (base64)
18
- def call
19
- bin = self.class::STEPS
20
- .map { |m| send(m) }
21
- .flatten
22
- .compact
23
- .pack('C*')
24
-
25
- Base64.strict_encode64(bin)
26
- rescue NameError => e
27
- raise "STEPS must be defined: #{e.message}"
28
- end
29
- end
30
- end
31
- end