solace 0.1.4 → 0.1.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.
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction builder for Token-2022 CloseAccount.
7
+ #
8
+ # Closes a Token-2022 token account and transfers all remaining lamports
9
+ # to a destination account. The token account must have a balance of zero.
10
+ #
11
+ # Instruction discriminator: 9
12
+ #
13
+ # Accounts:
14
+ # 1. [writable] Token account to close
15
+ # 2. [writable] Destination account to receive lamports
16
+ # 3. [signer] Account authority
17
+ #
18
+ # @since 0.1.5
19
+ class CloseAccountInstruction
20
+ # Instruction discriminator for CloseAccount
21
+ INSTRUCTION_DISCRIMINATOR = [9].freeze
22
+
23
+ # Builds a CloseAccount instruction.
24
+ #
25
+ # @param account_index [Integer] Index of the token account to close
26
+ # @param destination_index [Integer] Index of the destination account
27
+ # @param authority_index [Integer] Index of the account authority
28
+ # @param program_index [Integer] Index of the Token-2022 program
29
+ # @return [Solace::Instruction] The constructed instruction
30
+ def self.build(account_index:, destination_index:, authority_index:, program_index:)
31
+ Solace::Instruction.new.tap do |ix|
32
+ ix.program_index = program_index
33
+ ix.accounts = [account_index, destination_index, authority_index]
34
+ ix.data = data
35
+ end
36
+ end
37
+
38
+ # Builds the data for a CloseAccount instruction.
39
+ #
40
+ # The BufferLayout is:
41
+ # - [Instruction Index (1 byte)]
42
+ #
43
+ # @return [Array] 1-byte instruction index
44
+ def self.data
45
+ INSTRUCTION_DISCRIMINATOR
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for initializing a new Token-2022 token account.
7
+ #
8
+ # Used in conjunction with the SystemProgram::CreateAccount instruction
9
+ # to create and initialize a new token account. For the common case of
10
+ # an associated token account, prefer
11
+ # {AssociatedTokenAccount::CreateAccountInstruction}.
12
+ #
13
+ # @example Build an InitializeAccount instruction
14
+ # instruction = Solace::Instructions::Token2022::InitializeAccountInstruction.build(
15
+ # account_index: 0,
16
+ # mint_index: 1,
17
+ # owner_index: 2,
18
+ # rent_sysvar_index: 3,
19
+ # program_index: 4
20
+ # )
21
+ #
22
+ # @since 0.1.5
23
+ class InitializeAccountInstruction
24
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
25
+ # Instruction index for Token-2022's InitializeAccount instruction.
26
+ INSTRUCTION_INDEX = [1].freeze
27
+
28
+ # Builds a Token2022::InitializeAccount instruction.
29
+ #
30
+ # @param account_index [Integer] Index of the new token account in the transaction's accounts.
31
+ # @param mint_index [Integer] Index of the mint account in the transaction's accounts.
32
+ # @param owner_index [Integer] Index of the owner of the new account in the transaction's accounts.
33
+ # @param rent_sysvar_index [Integer] Index of the Rent Sysvar in the transaction's accounts.
34
+ # @param program_index [Integer] Index of the Token-2022 program in the transaction's accounts.
35
+ # @return [Solace::Instruction]
36
+ def self.build(
37
+ account_index:,
38
+ mint_index:,
39
+ owner_index:,
40
+ rent_sysvar_index:,
41
+ program_index:
42
+ )
43
+ Solace::Instruction.new.tap do |ix|
44
+ ix.program_index = program_index
45
+ ix.accounts = [account_index, mint_index, owner_index, rent_sysvar_index]
46
+ ix.data = data
47
+ end
48
+ end
49
+
50
+ # Builds the data for a Token2022::InitializeAccount instruction.
51
+ #
52
+ # The BufferLayout is:
53
+ # - [Instruction Index (1 byte)]
54
+ #
55
+ # @return [Array] 1-byte instruction index
56
+ def self.data
57
+ INSTRUCTION_INDEX
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for initializing a new Token-2022 mint.
7
+ #
8
+ # @example Build an InitializeMint instruction
9
+ # instruction = Solace::Instructions::Token2022::InitializeMintInstruction.build(
10
+ # decimals: 6,
11
+ # mint_authority: mint_authority.address,
12
+ # freeze_authority: freeze_authority.address,
13
+ # rent_sysvar_index: 2,
14
+ # mint_account_index: 1,
15
+ # program_index: 3
16
+ # )
17
+ #
18
+ # @since 0.1.5
19
+ class InitializeMintInstruction
20
+ # Instruction index for Initialize Mint
21
+ INSTRUCTION_INDEX = [0].freeze
22
+
23
+ # Builds a Solace::Instruction for initializing a Token-2022 mint.
24
+ #
25
+ # The BufferLayout is:
26
+ # - [Instruction Index (1 byte)]
27
+ # - [Decimals (1 byte)]
28
+ # - [Mint authority (32 bytes)]
29
+ # - [Freeze authority option (1 byte)]
30
+ # - [Freeze authority (32 bytes)]
31
+ #
32
+ # @param decimals [Integer] Number of decimals for the token
33
+ # @param mint_authority [String] Public key of the mint authority
34
+ # @param freeze_authority [String, nil] Public key of the freeze authority
35
+ # @param rent_sysvar_index [Integer] Index of the rent sysvar in the transaction's accounts
36
+ # @param mint_account_index [Integer] Index of the mint account in the transaction's accounts
37
+ # @param program_index [Integer] Index of the Token-2022 Program in the transaction's accounts
38
+ # @return [Solace::Instruction]
39
+ def self.build(
40
+ decimals:,
41
+ mint_authority:,
42
+ rent_sysvar_index:,
43
+ mint_account_index:,
44
+ freeze_authority: nil,
45
+ program_index: 2
46
+ )
47
+ Solace::Instruction.new.tap do |ix|
48
+ ix.program_index = program_index
49
+ ix.accounts = [mint_account_index, rent_sysvar_index]
50
+ ix.data = data(decimals, mint_authority, freeze_authority)
51
+ end
52
+ end
53
+
54
+ # Instruction data for an initialize mint instruction.
55
+ #
56
+ # @param decimals [Integer] Number of decimals for the token
57
+ # @param mint_authority [String] Public key of the mint authority
58
+ # @param freeze_authority [String, nil] Public key of the freeze authority
59
+ # @return [Array<u8>] The instruction data
60
+ def self.data(decimals, mint_authority, freeze_authority)
61
+ INSTRUCTION_INDEX +
62
+ [decimals] +
63
+ Solace::Utils::Codecs.base58_to_bytes(mint_authority) +
64
+ (
65
+ if freeze_authority
66
+ [1] + Solace::Utils::Codecs.base58_to_bytes(freeze_authority)
67
+ else
68
+ [0]
69
+ end
70
+ )
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for minting Token-2022 tokens to a token account.
7
+ #
8
+ # @example Build a MintTo instruction
9
+ # instruction = Solace::Instructions::Token2022::MintToInstruction.build(
10
+ # amount: 100,
11
+ # mint_index: 1,
12
+ # mint_authority_index: 2,
13
+ # destination_index: 3,
14
+ # program_index: 4
15
+ # )
16
+ #
17
+ # @since 0.1.5
18
+ class MintToInstruction
19
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
20
+ # Instruction index for Token-2022's MintTo instruction.
21
+ INSTRUCTION_INDEX = [7].freeze
22
+
23
+ # Builds a MintTo instruction.
24
+ #
25
+ # @param amount [Integer] The amount of tokens to mint.
26
+ # @param mint_index [Integer] The index of the mint account.
27
+ # @param destination_index [Integer] The index of the token account to mint to.
28
+ # @param mint_authority_index [Integer] The index of the mint authority account.
29
+ # @param program_index [Integer] The index of the Token-2022 Program.
30
+ # @return [Solace::Instruction]
31
+ def self.build(
32
+ amount:,
33
+ mint_index:,
34
+ mint_authority_index:,
35
+ destination_index:,
36
+ program_index:
37
+ )
38
+ Solace::Instruction.new.tap do |ix|
39
+ ix.program_index = program_index
40
+ ix.accounts = [mint_index, destination_index, mint_authority_index]
41
+ ix.data = data(amount)
42
+ end
43
+ end
44
+
45
+ # Builds the data for a MintTo instruction.
46
+ #
47
+ # The BufferLayout is:
48
+ # - [Instruction Index (1 byte)]
49
+ # - [Amount (8 bytes)]
50
+ #
51
+ # @param amount [Integer] The amount of tokens to mint.
52
+ # @return [Array] 1-byte instruction index + 8-byte amount
53
+ def self.data(amount)
54
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ module Token2022
6
+ # Instruction for transferring Token-2022 tokens with decimal validation.
7
+ #
8
+ # @example Build a TransferChecked instruction
9
+ # instruction = Solace::Instructions::Token2022::TransferCheckedInstruction.build(
10
+ # amount: 100,
11
+ # decimals: 6,
12
+ # to_index: 1,
13
+ # from_index: 2,
14
+ # mint_index: 3,
15
+ # authority_index: 4,
16
+ # program_index: 5
17
+ # )
18
+ #
19
+ # @since 0.1.5
20
+ class TransferCheckedInstruction
21
+ # Token-2022 instruction index for Transfer Checked
22
+ INSTRUCTION_INDEX = [12].freeze
23
+
24
+ # Builds a Solace::Instruction for transferring Token-2022 tokens.
25
+ #
26
+ # The BufferLayout is:
27
+ # - [Instruction Index (1 byte)]
28
+ # - [Amount (8 bytes little-endian u64)]
29
+ # - [Decimals (1 byte)]
30
+ #
31
+ # @param amount [Integer] Amount to transfer (in tokens, according to mint's decimals)
32
+ # @param decimals [Integer] Number of decimals for the token
33
+ # @param to_index [Integer] Index of the destination token account in the transaction's accounts
34
+ # @param from_index [Integer] Index of the source token account in the transaction's accounts
35
+ # @param mint_index [Integer] Index of the mint in the transaction's accounts
36
+ # @param authority_index [Integer] Index of the authority (owner) in the transaction's accounts
37
+ # @param program_index [Integer] Index of the Token-2022 Program in the transaction's accounts
38
+ # @return [Solace::Instruction]
39
+ def self.build(
40
+ amount:,
41
+ decimals:,
42
+ to_index:,
43
+ from_index:,
44
+ mint_index:,
45
+ authority_index:,
46
+ program_index: 3
47
+ )
48
+ Solace::Instruction.new.tap do |ix|
49
+ ix.program_index = program_index
50
+ ix.accounts = [from_index, mint_index, to_index, authority_index]
51
+ ix.data = data(amount, decimals)
52
+ end
53
+ end
54
+
55
+ # Instruction data for a TransferChecked instruction.
56
+ #
57
+ # @param amount [Integer] Amount to transfer
58
+ # @param decimals [Integer] Number of decimals for the token
59
+ # @return [Array] 1-byte instruction index + 8-byte amount + 1-byte decimals
60
+ def self.data(amount, decimals)
61
+ INSTRUCTION_INDEX +
62
+ Solace::Utils::Codecs.encode_le_u64(amount).bytes +
63
+ [decimals]
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solace
4
+ module Instructions
5
+ # The Token2022 module contains instruction builders for the Token-2022 Program
6
+ # (formerly Token Extensions, +TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb+).
7
+ #
8
+ # Token-2022 is wire-compatible with the legacy SPL Token program for its
9
+ # base instructions (Transfer, TransferChecked, CloseAccount, MintTo,
10
+ # InitializeMint, InitializeAccount). The instruction discriminators and
11
+ # data layouts are identical; only the program the instruction targets
12
+ # differs. These builders are duplicated from the {SplToken} namespace so
13
+ # that each instruction is unambiguously bound to a single on-chain program.
14
+ #
15
+ # @see Solace::Instructions::SplToken
16
+ # @since 0.1.5
17
+ module Token2022
18
+ # Instruction for transferring tokens via the Token-2022 program.
19
+ #
20
+ # @example Build a Transfer instruction
21
+ # instruction = Solace::Instructions::Token2022::TransferInstruction.build(
22
+ # amount: 100,
23
+ # owner_index: 1,
24
+ # source_index: 2,
25
+ # destination_index: 3,
26
+ # program_index: 4
27
+ # )
28
+ #
29
+ # @since 0.1.5
30
+ class TransferInstruction
31
+ # @!attribute [Array<Integer>] INSTRUCTION_INDEX
32
+ # Instruction index for Token-2022's Transfer instruction.
33
+ INSTRUCTION_INDEX = [3].freeze
34
+
35
+ # Builds a Transfer instruction.
36
+ #
37
+ # @param amount [Integer] The amount of tokens to transfer.
38
+ # @param source_index [Integer] The index of the source token account.
39
+ # @param destination_index [Integer] The index of the destination token account.
40
+ # @param owner_index [Integer] The index of the source account's owner.
41
+ # @param program_index [Integer] The index of the Token-2022 Program.
42
+ # @return [Solace::Instruction]
43
+ def self.build(
44
+ amount:,
45
+ owner_index:,
46
+ source_index:,
47
+ destination_index:,
48
+ program_index:
49
+ )
50
+ Solace::Instruction.new.tap do |ix|
51
+ ix.program_index = program_index
52
+ ix.accounts = [source_index, destination_index, owner_index]
53
+ ix.data = data(amount)
54
+ end
55
+ end
56
+
57
+ # Builds the data for a Transfer instruction.
58
+ #
59
+ # The BufferLayout is:
60
+ # - [Instruction Index (1 byte)]
61
+ # - [Amount (8 bytes)]
62
+ #
63
+ # @param amount [Integer] The amount of tokens to transfer.
64
+ # @return [Array<Integer>] 1-byte instruction index + 8-byte amount
65
+ def self.data(amount)
66
+ INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(amount).bytes
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -37,14 +37,25 @@ module Solace
37
37
  class << self
38
38
  # Gets the address of an associated token account.
39
39
  #
40
+ # The token program ID is part of the ATA derivation seed. For mints
41
+ # owned by the legacy SPL Token program this is {Constants::TOKEN_PROGRAM_ID}
42
+ # (the default). For mints owned by the Token-2022 program (e.g. PYUSD on
43
+ # Solana), pass +token_program_id: Solace::Constants::TOKEN_2022_PROGRAM_ID+
44
+ # to derive the correct address. Deriving with the wrong program ID
45
+ # returns an address that does not exist on chain.
46
+ #
47
+ # Use {Connection#get_mint_program_id} to discover which token program
48
+ # owns a given mint at runtime.
49
+ #
40
50
  # @param owner [Keypair, PublicKey] The keypair of the owner.
41
51
  # @param mint [Keypair, PublicKey] The keypair of the mint.
42
- # @return [String] The address of the associated token account.
43
- def get_address(owner:, mint:)
52
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
53
+ # @return [Array<String, Integer>] The associated token account address and bump seed.
54
+ def get_address(owner:, mint:, token_program_id: Solace::Constants::TOKEN_PROGRAM_ID)
44
55
  Solace::Utils::PDA.find_program_address(
45
56
  [
46
57
  owner.to_s,
47
- Solace::Constants::TOKEN_PROGRAM_ID,
58
+ token_program_id.to_s,
48
59
  mint.to_s
49
60
  ],
50
61
  Solace::Constants::ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
@@ -73,14 +84,16 @@ module Solace
73
84
  # @param funder [Keypair] The keypair that will pay for rent of the new associated token account.
74
85
  # @param owner [Keypair, PublicKey] The keypair of the owner.
75
86
  # @param mint [Keypair, PublicKey] The keypair of the mint.
87
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
76
88
  # @return [String] The address of the associated token account
77
89
  def get_or_create_address(
78
90
  payer:,
79
91
  funder:,
80
92
  owner:,
81
- mint:
93
+ mint:,
94
+ token_program_id: Solace::Constants::TOKEN_PROGRAM_ID
82
95
  )
83
- ata_address, = get_address(owner: owner, mint: mint)
96
+ ata_address, = get_address(owner: owner, mint: mint, token_program_id: token_program_id)
84
97
 
85
98
  account_balance = connection.get_account_info(ata_address)
86
99
 
@@ -90,7 +103,8 @@ module Solace
90
103
  payer: payer,
91
104
  funder: funder,
92
105
  owner: owner,
93
- mint: mint
106
+ mint: mint,
107
+ token_program_id: token_program_id
94
108
  )
95
109
 
96
110
  connection.wait_for_confirmed_signature { tx.signature }
@@ -136,19 +150,22 @@ module Solace
136
150
  # @param owner [#to_s, PublicKey] The keypair of the owner.
137
151
  # @param mint [#to_s, PublicKey] The keypair of the mint.
138
152
  # @param funder [#to_s, PublicKey] The keypair that will pay for rent of the new associated token account.
139
- # @return [Transaction] The signed transaction.
153
+ # @param token_program_id [String] The token program that owns the mint (defaults to legacy SPL Token).
154
+ # @return [TransactionComposer] A composer with the create-ATA instruction.
140
155
  def compose_create_associated_token_account(
141
156
  funder:,
142
157
  owner:,
143
- mint:
158
+ mint:,
159
+ token_program_id: Solace::Constants::TOKEN_PROGRAM_ID
144
160
  )
145
- ata_address, = get_address(owner: owner, mint: mint)
161
+ ata_address, = get_address(owner: owner, mint: mint, token_program_id: token_program_id)
146
162
 
147
163
  ix = Solace::Composers::AssociatedTokenAccountProgramCreateAccountComposer.new(
148
164
  mint: mint,
149
165
  owner: owner,
150
166
  funder: funder,
151
- ata_address: ata_address
167
+ ata_address: ata_address,
168
+ token_program_id: token_program_id
152
169
  )
153
170
 
154
171
  TransactionComposer