-
Notifications
You must be signed in to change notification settings - Fork 1
Add ComputeBudget SetComputeUnitPrice and SetComputeUnitLimit instructions and composers #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
2aa83c7
Add ComputeBudget SetComputeUnitPrice instruction and composer
fulf 725b5de
Add ComputeBudget SetComputeUnitLimit instruction and composer
fulf 2d13c2b
Make compute budget composer tests prove the instruction registers
fulf b882039
Restructure compute unit limit composer test per review
fulf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| PATH | ||
| remote: . | ||
| specs: | ||
| solace (0.1.6) | ||
| solace (0.1.7) | ||
| base58 (~> 0.2) | ||
| ffi (~> 1.15) | ||
| rbnacl (~> 7.0) | ||
|
|
||
58 changes: 58 additions & 0 deletions
58
gem/lib/solace/composers/compute_budget_program_set_compute_unit_limit_composer.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Solace | ||
| module Composers | ||
| # Composer for creating a compute budget set compute unit limit instruction. | ||
| # | ||
| # This composer resolves and orders the required accounts for a `SetComputeUnitLimit` | ||
| # instruction, sets up their access permissions, and delegates construction to the | ||
| # appropriate instruction builder (`Instructions::ComputeBudget::SetComputeUnitLimitInstruction`). | ||
| # | ||
| # It is used for capping the compute units a transaction may consume. | ||
| # | ||
| # Required accounts: | ||
| # - **Program**: Compute Budget program (readonly, non-signer) | ||
| # | ||
| # @example Compose and build a set compute unit limit instruction | ||
| # composer = ComputeBudgetProgramSetComputeUnitLimitComposer.new( | ||
| # units: 200_000 | ||
| # ) | ||
| # | ||
| # @see Instructions::ComputeBudget::SetComputeUnitLimitInstruction | ||
| # @since 0.1.7 | ||
| class ComputeBudgetProgramSetComputeUnitLimitComposer < Base | ||
| # Extracts the compute unit limit from the params | ||
| # | ||
| # @return [Integer] The compute unit limit | ||
| def units | ||
| params[:units] | ||
| end | ||
|
|
||
| # Returns the compute budget program id | ||
| # | ||
| # @return [String] The compute budget program id | ||
| def compute_budget_program | ||
| Constants::COMPUTE_BUDGET_PROGRAM_ID.to_s | ||
| end | ||
|
|
||
| # Setup accounts required for set compute unit limit instruction | ||
| # Called automatically during initialization | ||
| # | ||
| # @return [void] | ||
| def setup_accounts | ||
| account_context.add_readonly_nonsigner(compute_budget_program) | ||
| end | ||
|
|
||
| # Build instruction with resolved account indices | ||
| # | ||
| # @param account_context [Utils::AccountContext] The account context | ||
| # @return [Solace::Instruction] | ||
| def build_instruction(account_context) | ||
| Instructions::ComputeBudget::SetComputeUnitLimitInstruction.build( | ||
| units: units, | ||
| program_index: account_context.index_of(compute_budget_program) | ||
| ) | ||
| end | ||
| end | ||
| end | ||
| end |
58 changes: 58 additions & 0 deletions
58
gem/lib/solace/composers/compute_budget_program_set_compute_unit_price_composer.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Solace | ||
| module Composers | ||
| # Composer for creating a compute budget set compute unit price instruction. | ||
| # | ||
| # This composer resolves and orders the required accounts for a `SetComputeUnitPrice` | ||
| # instruction, sets up their access permissions, and delegates construction to the | ||
| # appropriate instruction builder (`Instructions::ComputeBudget::SetComputeUnitPriceInstruction`). | ||
| # | ||
| # It is used for attaching a priority fee to a transaction. | ||
| # | ||
| # Required accounts: | ||
| # - **Program**: Compute Budget program (readonly, non-signer) | ||
| # | ||
| # @example Compose and build a set compute unit price instruction | ||
| # composer = ComputeBudgetProgramSetComputeUnitPriceComposer.new( | ||
| # micro_lamports: 50_000 | ||
| # ) | ||
| # | ||
| # @see Instructions::ComputeBudget::SetComputeUnitPriceInstruction | ||
| # @since 0.1.7 | ||
| class ComputeBudgetProgramSetComputeUnitPriceComposer < Base | ||
| # Extracts the price per compute unit from the params | ||
| # | ||
| # @return [Integer] The price per compute unit (in micro-lamports) | ||
| def micro_lamports | ||
| params[:micro_lamports] | ||
| end | ||
|
|
||
| # Returns the compute budget program id | ||
| # | ||
| # @return [String] The compute budget program id | ||
| def compute_budget_program | ||
| Constants::COMPUTE_BUDGET_PROGRAM_ID.to_s | ||
| end | ||
|
|
||
| # Setup accounts required for set compute unit price instruction | ||
| # Called automatically during initialization | ||
| # | ||
| # @return [void] | ||
| def setup_accounts | ||
| account_context.add_readonly_nonsigner(compute_budget_program) | ||
| end | ||
|
|
||
| # Build instruction with resolved account indices | ||
| # | ||
| # @param account_context [Utils::AccountContext] The account context | ||
| # @return [Solace::Instruction] | ||
| def build_instruction(account_context) | ||
| Instructions::ComputeBudget::SetComputeUnitPriceInstruction.build( | ||
| micro_lamports: micro_lamports, | ||
| program_index: account_context.index_of(compute_budget_program) | ||
| ) | ||
| end | ||
| end | ||
| end | ||
| end |
51 changes: 51 additions & 0 deletions
51
gem/lib/solace/instructions/compute_budget/set_compute_unit_limit_instruction.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Solace | ||
| module Instructions | ||
| module ComputeBudget | ||
| # Instruction for setting the compute unit limit. | ||
| # | ||
| # This instruction is used to set the maximum number of compute units a | ||
| # transaction may consume. Together with the compute unit price, it | ||
| # determines the priority fee the transaction pays. | ||
| # | ||
| # @example Build a SetComputeUnitLimit instruction | ||
| # instruction = Solace::Instructions::ComputeBudget::SetComputeUnitLimitInstruction.build( | ||
| # units: 200_000, | ||
| # program_index: 1 | ||
| # ) | ||
| # | ||
| # @since 0.1.7 | ||
| class SetComputeUnitLimitInstruction | ||
| # @!attribute [Array<Integer>] INSTRUCTION_INDEX | ||
| # Instruction index for the Compute Budget Program's SetComputeUnitLimit instruction. | ||
| INSTRUCTION_INDEX = [2].freeze | ||
|
|
||
| # Builds a Solace::Instruction for setting the compute unit limit | ||
| # | ||
| # @param units [Integer] Maximum compute units the transaction may consume | ||
| # @param program_index [Integer] Index of the Compute Budget program in the transaction's accounts | ||
| # @return [Solace::Instruction] | ||
| def self.build(units:, program_index:) | ||
| Solace::Instruction.new.tap do |ix| | ||
| ix.program_index = program_index | ||
| ix.accounts = [] | ||
| ix.data = data(units) | ||
| end | ||
| end | ||
|
|
||
| # Instruction data for a set compute unit limit instruction | ||
| # | ||
| # The BufferLayout is: | ||
| # - [Instruction Index (1 byte)] | ||
| # - [Compute unit limit (4 bytes little-endian u32)] | ||
| # | ||
| # @param units [Integer] Maximum compute units the transaction may consume | ||
| # @return [Array<Integer>] 1-byte instruction index + 4-byte limit | ||
| def self.data(units) | ||
| INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u32(units).bytes | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
62 changes: 62 additions & 0 deletions
62
gem/lib/solace/instructions/compute_budget/set_compute_unit_price_instruction.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Solace | ||
| module Instructions | ||
| # The ComputeBudget module contains instruction builders for the Compute Budget Program. | ||
| # | ||
| # The Compute Budget program prices and provisions a transaction's execution. Its | ||
| # instructions take no accounts; each encodes a directive the runtime reads when | ||
| # scheduling and executing the transaction, such as the priority fee attached to it. | ||
| # | ||
| # This module contains classes that build the low-level instruction data required | ||
| # to interact with the Compute Budget Program. | ||
| # | ||
| # @see https://docs.solana.com/developing/programming-model/runtime#compute-budget | ||
| # @since 0.1.7 | ||
| module ComputeBudget | ||
| # Instruction for setting the compute unit price. | ||
| # | ||
| # This instruction is used to set the price (in micro-lamports per compute unit) | ||
| # a transaction pays as a priority fee, which validators use to order it during | ||
| # congestion. | ||
| # | ||
| # @example Build a SetComputeUnitPrice instruction | ||
| # instruction = Solace::Instructions::ComputeBudget::SetComputeUnitPriceInstruction.build( | ||
| # micro_lamports: 50_000, | ||
| # program_index: 1 | ||
| # ) | ||
| # | ||
| # @since 0.1.7 | ||
| class SetComputeUnitPriceInstruction | ||
| # @!attribute [Array<Integer>] INSTRUCTION_INDEX | ||
| # Instruction index for the Compute Budget Program's SetComputeUnitPrice instruction. | ||
| INSTRUCTION_INDEX = [3].freeze | ||
|
|
||
| # Builds a Solace::Instruction for setting the compute unit price | ||
| # | ||
| # @param micro_lamports [Integer] Price per compute unit (in micro-lamports) | ||
| # @param program_index [Integer] Index of the Compute Budget program in the transaction's accounts | ||
| # @return [Solace::Instruction] | ||
| def self.build(micro_lamports:, program_index:) | ||
| Solace::Instruction.new.tap do |ix| | ||
| ix.program_index = program_index | ||
| ix.accounts = [] | ||
| ix.data = data(micro_lamports) | ||
| end | ||
| end | ||
|
|
||
| # Instruction data for a set compute unit price instruction | ||
| # | ||
| # The BufferLayout is: | ||
| # - [Instruction Index (1 byte)] | ||
| # - [Price (8 bytes little-endian u64)] | ||
| # | ||
| # @param micro_lamports [Integer] Price per compute unit (in micro-lamports) | ||
| # @return [Array<Integer>] 1-byte instruction index + 8-byte price | ||
| def self.data(micro_lamports) | ||
| INSTRUCTION_INDEX + Solace::Utils::Codecs.encode_le_u64(micro_lamports).bytes | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,5 +2,5 @@ | |
|
|
||
| module Solace | ||
| # Latest version of the Solace gem. | ||
| VERSION = '0.1.6' | ||
| VERSION = '0.1.7' | ||
| end | ||
94 changes: 94 additions & 0 deletions
94
gem/test/solace/composers/compute_budget_program_set_compute_unit_limit_composer_test.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'test_helper' | ||
|
|
||
| describe Solace::Composers::ComputeBudgetProgramSetComputeUnitLimitComposer do | ||
| let(:bob) { Fixtures.load_keypair('bob') } | ||
| let(:anna) { Fixtures.load_keypair('anna') } | ||
|
|
||
| let(:connection) { Solace::Connection.new(commitment: 'processed') } | ||
| let(:transaction_composer) { Solace::TransactionComposer.new(connection: connection) } | ||
|
|
||
| let(:composer) do | ||
| Solace::Composers::ComputeBudgetProgramSetComputeUnitLimitComposer.new( | ||
| units: 20_000 | ||
| ) | ||
| end | ||
|
|
||
| let(:transfer_composer) do | ||
| Solace::Composers::SystemProgramTransferComposer.new( | ||
| to: anna, | ||
| from: bob, | ||
| lamports: 10_000 | ||
| ) | ||
| end | ||
|
|
||
| describe 'composed transaction' do | ||
| before(:all) do | ||
| # Add instructions and set fee payer | ||
| transaction_composer.add_instruction(composer) | ||
| transaction_composer.add_instruction(transfer_composer) | ||
| transaction_composer.set_fee_payer(bob) | ||
|
|
||
| # Compose and decode the transaction message | ||
| @decoded_message = Solace::Transaction.from(transaction_composer.compose_transaction.serialize).message | ||
| end | ||
|
|
||
| it 'includes the set compute unit limit instruction' do | ||
| instructions = @decoded_message.instructions.map { |ix| [@decoded_message.accounts[ix.program_index], ix.data] } | ||
|
|
||
| assert_includes instructions, [ | ||
| Solace::Constants::COMPUTE_BUDGET_PROGRAM_ID, | ||
| [2] + [20_000].pack('L<').bytes | ||
| ] | ||
| end | ||
| end | ||
|
|
||
| describe 'transaction consuming fewer compute units than the limit' do | ||
| before(:all) do | ||
| # Add instructions and set fee payer | ||
| transaction_composer.add_instruction(composer) | ||
| transaction_composer.add_instruction(transfer_composer) | ||
| transaction_composer.set_fee_payer(bob) | ||
|
|
||
| # Compose and sign transaction | ||
| tx = transaction_composer.compose_transaction | ||
| tx.sign(bob) | ||
|
|
||
| # Send transaction | ||
| @signature = connection.send_transaction(tx.serialize) | ||
| end | ||
|
|
||
| it 'is confirmed by the node' do | ||
| assert(connection.wait_for_confirmed_signature { @signature['result'] }) | ||
| end | ||
| end | ||
|
|
||
| describe 'transaction exceeding the compute unit limit' do | ||
| # Requests fewer units than the transaction needs | ||
| let(:composer) do | ||
| Solace::Composers::ComputeBudgetProgramSetComputeUnitLimitComposer.new( | ||
| units: 100 | ||
| ) | ||
| end | ||
|
|
||
| before(:all) do | ||
| # Add instructions and set fee payer | ||
| transaction_composer.add_instruction(composer) | ||
| transaction_composer.add_instruction(transfer_composer) | ||
| transaction_composer.set_fee_payer(bob) | ||
|
|
||
| # Compose and sign transaction | ||
| @transaction = transaction_composer.compose_transaction | ||
| @transaction.sign(bob) | ||
| end | ||
|
|
||
| it 'is rejected by the node' do | ||
| error = assert_raises(Solace::Errors::RPCError) do | ||
| connection.send_transaction(@transaction.serialize) | ||
| end | ||
|
|
||
| assert_match(/exceeded/i, error.message) | ||
| end | ||
| end | ||
| end | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.