diff --git a/programs/compute-budget/RequestHeapFrame.go b/programs/compute-budget/RequestHeapFrame.go new file mode 100644 index 00000000..920e5bf2 --- /dev/null +++ b/programs/compute-budget/RequestHeapFrame.go @@ -0,0 +1,121 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "errors" + + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +const MAX_HEAP_FRAME_BYTES uint32 = 256 * 1024 + +type RequestHeapFrame struct { + HeapSize uint32 +} + +func (obj *RequestHeapFrame) SetAccounts(accounts []*ag_solanago.AccountMeta) error { + return nil +} + +func (slice RequestHeapFrame) GetAccounts() (accounts []*ag_solanago.AccountMeta) { + return +} + +// NewRequestHeapFrameInstructionBuilder creates a new `RequestHeapFrame` instruction builder. +func NewRequestHeapFrameInstructionBuilder() *RequestHeapFrame { + nd := &RequestHeapFrame{} + return nd +} + +// Request heap frame in bytes +func (inst *RequestHeapFrame) SetHeapSize(heapSize uint32) *RequestHeapFrame { + inst.HeapSize = heapSize + return inst +} + +func (inst RequestHeapFrame) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: ag_binary.TypeIDFromUint8(Instruction_RequestHeapFrame), + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst RequestHeapFrame) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *RequestHeapFrame) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.HeapSize == 0 { + return errors.New("HeapSize parameter is not set") + } + if inst.HeapSize > MAX_HEAP_FRAME_BYTES { + return errors.New("HeapSize parameter exceeds the maximum heap frame bytes") + } + } + return nil +} + +func (inst *RequestHeapFrame) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("RequestHeapFrame")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("HeapSize", inst.HeapSize)) + }) + }) + }) +} + +func (obj RequestHeapFrame) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `HeapSize` param: + err = encoder.Encode(obj.HeapSize) + if err != nil { + return err + } + return nil +} +func (obj *RequestHeapFrame) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `HeapSize`: + err = decoder.Decode(&obj.HeapSize) + if err != nil { + return err + } + return nil +} + +// NewRequestHeapFrameInstruction declares a new RequestHeapFrame instruction with the provided parameters and accounts. +func NewRequestHeapFrameInstruction( + // Parameters: + heapSize uint32, +) *RequestHeapFrame { + return NewRequestHeapFrameInstructionBuilder().SetHeapSize(heapSize) +} diff --git a/programs/compute-budget/RequestHeapFrame_test.go b/programs/compute-budget/RequestHeapFrame_test.go new file mode 100644 index 00000000..dbe379de --- /dev/null +++ b/programs/compute-budget/RequestHeapFrame_test.go @@ -0,0 +1,42 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRequestHeapFrameInstruction(t *testing.T) { + + t.Run("should validate max heap size", func(t *testing.T) { + _, err := NewRequestHeapFrameInstruction(300000).ValidateAndBuild() + require.Error(t, err) + }) + + t.Run("should build ix", func(t *testing.T) { + ix, err := NewRequestHeapFrameInstruction(4000).ValidateAndBuild() + require.Nil(t, err) + + require.Equal(t, ProgramID, ix.ProgramID()) + require.Equal(t, 0, len(ix.Accounts())) + + data, err := ix.Data() + require.Nil(t, err) + require.Equal(t, []byte{0x1, 0xa0, 0xf, 0x0, 0x0}, data) + }) + +} diff --git a/programs/compute-budget/RequestUnitsDeprecated.go b/programs/compute-budget/RequestUnitsDeprecated.go new file mode 100644 index 00000000..8a9153b5 --- /dev/null +++ b/programs/compute-budget/RequestUnitsDeprecated.go @@ -0,0 +1,148 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "errors" + + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +type RequestUnitsDeprecated struct { + // Units to request + Units uint32 + + // Additional fee to add + AdditionalFee uint32 +} + +func (obj *RequestUnitsDeprecated) SetAccounts(accounts []*ag_solanago.AccountMeta) error { + return nil +} + +func (slice RequestUnitsDeprecated) GetAccounts() (accounts []*ag_solanago.AccountMeta) { + return +} + +// NewRequestUnitsDeprecatedInstructionBuilder creates a new `RequestUnitsDeprecated` instruction builder. +func NewRequestUnitsDeprecatedInstructionBuilder() *RequestUnitsDeprecated { + nd := &RequestUnitsDeprecated{} + return nd +} + +// Units to request +func (inst *RequestUnitsDeprecated) SetUnits(units uint32) *RequestUnitsDeprecated { + inst.Units = units + return inst +} + +// Additional fee to add +func (inst *RequestUnitsDeprecated) SetAdditionalFee(additionalFee uint32) *RequestUnitsDeprecated { + inst.AdditionalFee = additionalFee + return inst +} + +func (inst RequestUnitsDeprecated) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: ag_binary.TypeIDFromUint8(Instruction_RequestUnitsDeprecated), + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst RequestUnitsDeprecated) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *RequestUnitsDeprecated) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.Units == 0 { + return errors.New("Units parameter is not set") + } + if inst.Units > MAX_COMPUTE_UNIT_LIMIT { + return errors.New("Units parameter exceeds the maximum compute unit") + } + if inst.AdditionalFee == 0 { + return errors.New("AdditionalFee parameter is not set") + } + } + return nil +} + +func (inst *RequestUnitsDeprecated) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("RequestUnitsDeprecated")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param(" Units", inst.Units)) + paramsBranch.Child(ag_format.Param("AdditionalFee", inst.AdditionalFee)) + }) + }) + }) +} + +func (obj RequestUnitsDeprecated) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Units` param: + err = encoder.Encode(obj.Units) + if err != nil { + return err + } + + // Serialize `AdditionalFee` param: + err = encoder.Encode(obj.AdditionalFee) + if err != nil { + return err + } + return nil +} +func (obj *RequestUnitsDeprecated) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Units`: + err = decoder.Decode(&obj.Units) + if err != nil { + return err + } + + // Deserialize `AdditionalFee`: + err = decoder.Decode(&obj.AdditionalFee) + if err != nil { + return err + } + return nil +} + +// NewRequestUnitsDeprecatedInstruction declares a new RequestUnitsDeprecated instruction with the provided parameters and accounts. +func NewRequestUnitsDeprecatedInstruction( + // Parameters: + units uint32, + additionalFee uint32, +) *RequestUnitsDeprecated { + return NewRequestUnitsDeprecatedInstructionBuilder(). + SetUnits(units). + SetAdditionalFee(additionalFee) +} diff --git a/programs/compute-budget/RequestUnitsDeprecated_test.go b/programs/compute-budget/RequestUnitsDeprecated_test.go new file mode 100644 index 00000000..1751171d --- /dev/null +++ b/programs/compute-budget/RequestUnitsDeprecated_test.go @@ -0,0 +1,42 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewRequestUnitsDeprecatedInstruction(t *testing.T) { + + t.Run("should validate max units", func(t *testing.T) { + _, err := NewRequestUnitsDeprecatedInstruction(2000000, 1000).ValidateAndBuild() + require.Error(t, err) + }) + + t.Run("should build request units ix", func(t *testing.T) { + ix, err := NewRequestUnitsDeprecatedInstruction(1400000, 1000).ValidateAndBuild() + require.Nil(t, err) + + require.Equal(t, ProgramID, ix.ProgramID()) + require.Equal(t, 0, len(ix.Accounts())) + + data, err := ix.Data() + require.Nil(t, err) + require.Equal(t, []byte{0x0, 0xc0, 0x5c, 0x15, 0x0, 0xe8, 0x3, 0x0, 0x0}, data) + }) + +} diff --git a/programs/compute-budget/SetComputeUnitLimit.go b/programs/compute-budget/SetComputeUnitLimit.go new file mode 100644 index 00000000..f8c9432b --- /dev/null +++ b/programs/compute-budget/SetComputeUnitLimit.go @@ -0,0 +1,121 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "errors" + + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +const MAX_COMPUTE_UNIT_LIMIT = 1400000 + +type SetComputeUnitLimit struct { + Units uint32 +} + +func (obj *SetComputeUnitLimit) SetAccounts(accounts []*ag_solanago.AccountMeta) error { + return nil +} + +func (slice SetComputeUnitLimit) GetAccounts() (accounts []*ag_solanago.AccountMeta) { + return +} + +// NewSetComputeUnitLimitInstructionBuilder creates a new `SetComputeUnitLimit` instruction builder. +func NewSetComputeUnitLimitInstructionBuilder() *SetComputeUnitLimit { + nd := &SetComputeUnitLimit{} + return nd +} + +// Unit limit +func (inst *SetComputeUnitLimit) SetUnits(units uint32) *SetComputeUnitLimit { + inst.Units = units + return inst +} + +func (inst SetComputeUnitLimit) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: ag_binary.TypeIDFromUint8(Instruction_SetComputeUnitLimit), + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst SetComputeUnitLimit) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *SetComputeUnitLimit) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.Units == 0 { + return errors.New("Units parameter is not set") + } + if inst.Units > MAX_COMPUTE_UNIT_LIMIT { + return errors.New("Units parameter exceeds the maximum compute unit") + } + } + return nil +} + +func (inst *SetComputeUnitLimit) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("SetComputeUnitLimit")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("Units", inst.Units)) + }) + }) + }) +} + +func (obj SetComputeUnitLimit) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Units` param: + err = encoder.Encode(obj.Units) + if err != nil { + return err + } + return nil +} +func (obj *SetComputeUnitLimit) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Units`: + err = decoder.Decode(&obj.Units) + if err != nil { + return err + } + return nil +} + +// NewSetComputeUnitLimitInstruction declares a new SetComputeUnitLimit instruction with the provided parameters and accounts. +func NewSetComputeUnitLimitInstruction( + // Parameters: + units uint32, +) *SetComputeUnitLimit { + return NewSetComputeUnitLimitInstructionBuilder().SetUnits(units) +} diff --git a/programs/compute-budget/SetComputeUnitLimit_test.go b/programs/compute-budget/SetComputeUnitLimit_test.go new file mode 100644 index 00000000..8874850c --- /dev/null +++ b/programs/compute-budget/SetComputeUnitLimit_test.go @@ -0,0 +1,42 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSetComputeUnitLimitInstruction(t *testing.T) { + + t.Run("should validate max compute unit limit", func(t *testing.T) { + _, err := NewSetComputeUnitLimitInstruction(2000000).ValidateAndBuild() + require.Error(t, err) + }) + + t.Run("should build set compute limit ix", func(t *testing.T) { + ix, err := NewSetComputeUnitLimitInstruction(1400000).ValidateAndBuild() + require.Nil(t, err) + + require.Equal(t, ProgramID, ix.ProgramID()) + require.Equal(t, 0, len(ix.Accounts())) + + data, err := ix.Data() + require.Nil(t, err) + require.Equal(t, []byte{0x2, 0xc0, 0x5c, 0x15, 0x0}, data) + }) + +} diff --git a/programs/compute-budget/SetComputeUnitPrice.go b/programs/compute-budget/SetComputeUnitPrice.go new file mode 100644 index 00000000..3eb0b0d0 --- /dev/null +++ b/programs/compute-budget/SetComputeUnitPrice.go @@ -0,0 +1,115 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "errors" + + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_format "github.com/gagliardetto/solana-go/text/format" + ag_treeout "github.com/gagliardetto/treeout" +) + +type SetComputeUnitPrice struct { + MicroLamports uint64 +} + +func (obj *SetComputeUnitPrice) SetAccounts(accounts []*ag_solanago.AccountMeta) error { + return nil +} + +func (slice SetComputeUnitPrice) GetAccounts() (accounts []*ag_solanago.AccountMeta) { + return +} + +// NewSetComputeUnitPriceInstructionBuilder creates a new `SetComputeUnitPrice` instruction builder. +func NewSetComputeUnitPriceInstructionBuilder() *SetComputeUnitPrice { + nd := &SetComputeUnitPrice{} + return nd +} + +func (inst *SetComputeUnitPrice) SetMicroLamports(microLamports uint64) *SetComputeUnitPrice { + inst.MicroLamports = microLamports + return inst +} + +func (inst SetComputeUnitPrice) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: ag_binary.TypeIDFromUint8(Instruction_SetComputeUnitPrice), + }} +} + +// ValidateAndBuild validates the instruction parameters and accounts; +// if there is a validation error, it returns the error. +// Otherwise, it builds and returns the instruction. +func (inst SetComputeUnitPrice) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *SetComputeUnitPrice) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.MicroLamports == 0 { + return errors.New("MicroLamports parameter is not set") + } + } + return nil +} + +func (inst *SetComputeUnitPrice) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("SetComputeUnitPrice")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("MicroLamports", inst.MicroLamports)) + }) + }) + }) +} + +func (obj SetComputeUnitPrice) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `MicroLamports` param: + err = encoder.Encode(obj.MicroLamports) + if err != nil { + return err + } + return nil +} +func (obj *SetComputeUnitPrice) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `MicroLamports`: + err = decoder.Decode(&obj.MicroLamports) + if err != nil { + return err + } + return nil +} + +// NewSetComputeUnitPriceInstruction declares a new SetComputeUnitPrice instruction with the provided parameters and accounts. +func NewSetComputeUnitPriceInstruction( + // Parameters: + microLamports uint64, +) *SetComputeUnitPrice { + return NewSetComputeUnitPriceInstructionBuilder().SetMicroLamports(microLamports) +} diff --git a/programs/compute-budget/SetComputeUnitPrice_test.go b/programs/compute-budget/SetComputeUnitPrice_test.go new file mode 100644 index 00000000..78076c27 --- /dev/null +++ b/programs/compute-budget/SetComputeUnitPrice_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSetComputeUnitPriceInstruction(t *testing.T) { + ix, err := NewSetComputeUnitPriceInstruction(1000).ValidateAndBuild() + require.Nil(t, err) + + require.Equal(t, ProgramID, ix.ProgramID()) + require.Equal(t, 0, len(ix.Accounts())) + + data, err := ix.Data() + require.Nil(t, err) + require.Equal(t, []byte{0x3, 0xe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, data) +} diff --git a/programs/compute-budget/instruction.go b/programs/compute-budget/instruction.go new file mode 100644 index 00000000..ed4fe3c6 --- /dev/null +++ b/programs/compute-budget/instruction.go @@ -0,0 +1,160 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "bytes" + "fmt" + + ag_spew "github.com/davecgh/go-spew/spew" + ag_binary "github.com/gagliardetto/binary" + ag_solanago "github.com/gagliardetto/solana-go" + ag_text "github.com/gagliardetto/solana-go/text" + ag_treeout "github.com/gagliardetto/treeout" +) + +var ProgramID ag_solanago.PublicKey = ag_solanago.ComputeBudget + +func SetProgramID(pubkey ag_solanago.PublicKey) { + ProgramID = pubkey + ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) +} + +const ProgramName = "ComputeBudget" + +func init() { + if !ProgramID.IsZero() { + ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) + } +} + +const ( + // Deprecated + // after feature remove_deprecated_request_unit_ix::id() is activated + Instruction_RequestUnitsDeprecated uint8 = iota + + // Request a specific transaction-wide program heap region size in bytes. + // The value requested must be a multiple of 1024. This new heap region + // size applies to each program executed in the transaction, including all + // calls to CPIs. + Instruction_RequestHeapFrame + + // Set a specific compute unit limit that the transaction is allowed to consume. + Instruction_SetComputeUnitLimit + + // Set a compute unit price in "micro-lamports" to pay a higher transaction + // fee for higher transaction prioritization. + Instruction_SetComputeUnitPrice +) + +// InstructionIDToName returns the name of the instruction given its ID. +func InstructionIDToName(id uint8) string { + switch id { + case Instruction_RequestUnitsDeprecated: + return "RequestUnitsDeprecated" + case Instruction_RequestHeapFrame: + return "RequestHeapFrame" + case Instruction_SetComputeUnitLimit: + return "SetComputeUnitLimit" + case Instruction_SetComputeUnitPrice: + return "SetComputeUnitPrice" + default: + return "" + } +} + +type Instruction struct { + ag_binary.BaseVariant +} + +func (inst *Instruction) EncodeToTree(parent ag_treeout.Branches) { + if enToTree, ok := inst.Impl.(ag_text.EncodableToTree); ok { + enToTree.EncodeToTree(parent) + } else { + parent.Child(ag_spew.Sdump(inst)) + } +} + +var InstructionImplDef = ag_binary.NewVariantDefinition( + ag_binary.Uint8TypeIDEncoding, + []ag_binary.VariantType{ + { + "RequestUnitsDeprecated", (*RequestUnitsDeprecated)(nil), + }, + { + "RequestHeapFrame", (*RequestHeapFrame)(nil), + }, + { + "SetComputeUnitLimit", (*SetComputeUnitLimit)(nil), + }, + { + "SetComputeUnitPrice", (*SetComputeUnitPrice)(nil), + }, + }, +) + +func (inst *Instruction) ProgramID() ag_solanago.PublicKey { + return ProgramID +} + +func (inst *Instruction) Accounts() (out []*ag_solanago.AccountMeta) { + return inst.Impl.(ag_solanago.AccountsGettable).GetAccounts() +} + +func (inst *Instruction) Data() ([]byte, error) { + buf := new(bytes.Buffer) + if err := ag_binary.NewBinEncoder(buf).Encode(inst); err != nil { + return nil, fmt.Errorf("unable to encode instruction: %w", err) + } + return buf.Bytes(), nil +} + +func (inst *Instruction) TextEncode(encoder *ag_text.Encoder, option *ag_text.Option) error { + return encoder.Encode(inst.Impl, option) +} + +func (inst *Instruction) UnmarshalWithDecoder(decoder *ag_binary.Decoder) error { + return inst.BaseVariant.UnmarshalBinaryVariant(decoder, InstructionImplDef) +} + +func (inst Instruction) MarshalWithEncoder(encoder *ag_binary.Encoder) error { + err := encoder.WriteUint8(inst.TypeID.Uint8()) + if err != nil { + return fmt.Errorf("unable to write variant type: %w", err) + } + return encoder.Encode(inst.Impl) +} + +func registryDecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (interface{}, error) { + inst, err := DecodeInstruction(accounts, data) + if err != nil { + return nil, err + } + return inst, nil +} + +func DecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (*Instruction, error) { + inst := new(Instruction) + if err := ag_binary.NewBinDecoder(data).Decode(inst); err != nil { + return nil, fmt.Errorf("unable to decode instruction: %w", err) + } + if v, ok := inst.Impl.(ag_solanago.AccountsSettable); ok { + err := v.SetAccounts(accounts) + if err != nil { + return nil, fmt.Errorf("unable to set accounts for instruction: %w", err) + } + } + return inst, nil +} diff --git a/programs/compute-budget/instruction_test.go b/programs/compute-budget/instruction_test.go new file mode 100644 index 00000000..f411fe3d --- /dev/null +++ b/programs/compute-budget/instruction_test.go @@ -0,0 +1,110 @@ +// Copyright 2021 github.com/gagliardetto +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package computebudget + +import ( + "bytes" + "encoding/hex" + "testing" + + bin "github.com/gagliardetto/binary" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncodingInstruction(t *testing.T) { + tests := []struct { + name string + hexData string + expectInstruction *Instruction + }{ + { + name: "RequestUnitsDeprecated", + hexData: "00c05c1500e8030000", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: bin.TypeIDFromUint8(0), + Impl: &RequestUnitsDeprecated{ + Units: 1400000, + AdditionalFee: 1000, + }, + }, + }, + }, + { + name: "RequestHeapFrame", + hexData: "01a00f0000", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: bin.TypeIDFromUint8(1), + Impl: &RequestHeapFrame{ + HeapSize: 4000, + }, + }, + }, + }, + { + name: "SetComputeUnitLimit", + hexData: "02c05c1500", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: bin.TypeIDFromUint8(2), + Impl: &SetComputeUnitLimit{ + Units: 1400000, + }, + }, + }, + }, + { + name: "SetComputeUnitPrice", + hexData: "03e803000000000000", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: bin.TypeIDFromUint8(3), + Impl: &SetComputeUnitPrice{ + MicroLamports: 1000, + }, + }, + }, + }, + } + + t.Run("should encode", func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buf := new(bytes.Buffer) + err := bin.NewBinEncoder(buf).Encode(test.expectInstruction) + require.NoError(t, err) + + encodedHex := hex.EncodeToString(buf.Bytes()) + require.Equal(t, test.hexData, encodedHex) + }) + } + }) + + t.Run("should decode", func(t *testing.T) { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + data, err := hex.DecodeString(test.hexData) + require.NoError(t, err) + var instruction *Instruction + err = bin.NewBinDecoder(data).Decode(&instruction) + require.NoError(t, err) + assert.Equal(t, test.expectInstruction, instruction) + }) + } + }) + +}