From a16821d62732d5a2e159e1127f1b80eaf2f22f4f Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Tue, 21 Jul 2026 15:10:53 -0400 Subject: [PATCH 1/3] Improve CU limits for high-frequency transactions --- ocp/rpc/transaction/swap_handler.go | 37 ++++++- ocp/transaction/compute_budget.go | 135 ++++++++++++++++++++++++ ocp/transaction/compute_budget_test.go | 54 ++++++++++ ocp/transaction/transaction.go | 47 ++++++--- ocp/worker/currency/feeburner/worker.go | 8 +- ocp/worker/geyser/external_deposit.go | 4 +- solana/token/associated.go | 11 ++ 7 files changed, 274 insertions(+), 22 deletions(-) create mode 100644 ocp/transaction/compute_budget.go create mode 100644 ocp/transaction/compute_budget_test.go diff --git a/ocp/rpc/transaction/swap_handler.go b/ocp/rpc/transaction/swap_handler.go index 1b57f27..9012d3d 100644 --- a/ocp/rpc/transaction/swap_handler.go +++ b/ocp/rpc/transaction/swap_handler.go @@ -70,7 +70,6 @@ func NewReserveBuySwapHandler( amount: amount, selectedNonce: selectedNonce, - computeUnitLimit: 120_000, computeUnitPrice: 10_000, memoValue: "buy_v0", } @@ -152,6 +151,15 @@ func (h *ReserveBuySwapHandler) MakeInstructions(ctx context.Context) ([]solana. return nil, err } + _, temporaryCoreMintAtaBump, err := token.GetAssociatedAccountAndBump( + h.temporaryHolder.PublicKey().ToBytes(), + common.CoreMintAccount.PublicKey().ToBytes(), + ) + if err != nil { + return nil, err + } + h.computeUnitLimit = transaction_util.ReserveBuySwapComputeUnitLimit(temporaryCoreMintAtaBump) + transferFromSourceVmSwapAtaIxn := vm.NewTransferForSwapInstruction( &vm.TransferForSwapInstructionAccounts{ VmAuthority: sourceVmConfig.Authority.PublicKey().ToBytes(), @@ -258,7 +266,6 @@ func NewReserveSellSwapHandler( amount: amount, selectedNonce: selectedNonce, - computeUnitLimit: 145_000, computeUnitPrice: 10_000, memoValue: "sell_v0", } @@ -340,6 +347,15 @@ func (h *ReserveSellSwapHandler) MakeInstructions(ctx context.Context) ([]solana return nil, err } + _, temporarySourceCurrencyAtaBump, err := token.GetAssociatedAccountAndBump( + h.temporaryHolder.PublicKey().ToBytes(), + h.mint.PublicKey().ToBytes(), + ) + if err != nil { + return nil, err + } + h.computeUnitLimit = transaction_util.ReserveSellSwapComputeUnitLimit(temporarySourceCurrencyAtaBump) + transferFromSourceVmSwapAtaIxn := vm.NewTransferForSwapInstruction( &vm.TransferForSwapInstructionAccounts{ VmAuthority: sourceVmConfig.Authority.PublicKey().ToBytes(), @@ -449,7 +465,6 @@ func NewReserveBuySellSwapHandler( amount: amount, selectedNonce: selectedNonce, - computeUnitLimit: 250_000, computeUnitPrice: 10_000, memoValue: "buy_sell_v0", } @@ -558,6 +573,22 @@ func (h *ReserveBuySellSwapHandler) MakeInstructions(ctx context.Context) ([]sol return nil, err } + _, temporaryCoreMintAtaBump, err := token.GetAssociatedAccountAndBump( + h.temporaryHolder.PublicKey().ToBytes(), + common.CoreMintAccount.PublicKey().ToBytes(), + ) + if err != nil { + return nil, err + } + _, temporarySourceCurrencyAtaBump, err := token.GetAssociatedAccountAndBump( + h.temporaryHolder.PublicKey().ToBytes(), + h.fromMint.PublicKey().ToBytes(), + ) + if err != nil { + return nil, err + } + h.computeUnitLimit = transaction_util.ReserveBuySellSwapComputeUnitLimit(temporaryCoreMintAtaBump, temporarySourceCurrencyAtaBump) + transferFromSourceVmSwapAtaIxn := vm.NewTransferForSwapInstruction( &vm.TransferForSwapInstructionAccounts{ VmAuthority: sourceVmConfig.Authority.PublicKey().ToBytes(), diff --git a/ocp/transaction/compute_budget.go b/ocp/transaction/compute_budget.go new file mode 100644 index 0000000..a4349c2 --- /dev/null +++ b/ocp/transaction/compute_budget.go @@ -0,0 +1,135 @@ +package transaction + +// Compute unit limits for exec transactions, modeled against the VM program +// version that validates PDAs with create_program_address using stored bumps +// (a flat 1,500 CUs per derivation, independent of bump value). Deploying +// these limits against the older find_program_address-based VM program would +// under-budget VMs with low-bump PDAs. +// +// The ATA program still derives the associated token address with +// find_program_address, which walks candidate bumps from 255 down at 1,500 +// CUs each, so create-on-send carries the one remaining bump-dependent term. +const ( + // todo: optimize + baseInternalExecComputeUnits = 60_000 + baseExternalExecComputeUnits = 65_000 + + // vm check + 4 timelock/vault message derivations + numCreateDerivationsInternalExec = 5 + + // vm check + 2 message derivations + omnibus invoke_signed + numCreateDerivationsExternalExec = 4 + + // todo: optimize + baseReserveBuySwapComputeUnits = 90_000 + baseReserveSellSwapComputeUnits = 100_000 + baseReserveBuySellSwapComputeUnits = 150_000 + + // todo: optimize + baseExternalDepositComputeUnits = 25_000 + baseCloseVmDepositComputeUnits = 10_000 + + // todo: optimize + baseInitTimelockComputeUnits = 10_000 + + // init_timelock: vm + memory checks + numCreateDerivationsInitTimelock = 2 + + // init_timelock derives the withdraw receipt PDA from the VM's PoH value + // at execution time, so its bump is unknowable when the transaction is + // built. Budget for bump 232, which covers all but ~1 in 16M account + // creations. + withdrawReceiptFindComputeUnits = 36_000 + + // todo: optimize + baseAtaCreateComputeUnits = 15_000 + + cuPerPdaDerivation = 1_500 + + computeUnitMarginPercent = 15 +) + +func findPdaComputeUnits(bump uint8) uint32 { + return (256 - uint32(bump)) * cuPerPdaDerivation +} + +// WithComputeUnitMargin pads a measured or modeled compute unit count by the +// standard safety margin. +func WithComputeUnitMargin(computeUnits uint32) uint32 { + return computeUnits * (100 + computeUnitMarginPercent) / 100 +} + +// openAccountComputeUnitLimit computes the compute unit limit for an +// init_timelock transaction. The timelock state, vault and unlock PDAs +// remain find-derived on-chain even with stored-bump validation, since bump +// canonicality must be proven at account creation. +func openAccountComputeUnitLimit(timelockStateBump, vaultBump, unlockBump uint8) uint32 { + computeUnits := baseInitTimelockComputeUnits + + numCreateDerivationsInitTimelock*cuPerPdaDerivation + + findPdaComputeUnits(timelockStateBump) + + findPdaComputeUnits(vaultBump) + + findPdaComputeUnits(unlockBump) + + withdrawReceiptFindComputeUnits + return WithComputeUnitMargin(computeUnits) +} + +func internalExecComputeUnitLimit(numMemoryBanks int) uint32 { + return execComputeUnitLimit(baseInternalExecComputeUnits, numCreateDerivationsInternalExec, numMemoryBanks, 0) +} + +func externalExecComputeUnitLimit(numMemoryBanks int) uint32 { + return execComputeUnitLimit(baseExternalExecComputeUnits, numCreateDerivationsExternalExec, numMemoryBanks, 0) +} + +func externalExecWithAtaCreateComputeUnitLimit(numMemoryBanks int, ataBump uint8) uint32 { + return execComputeUnitLimit( + baseExternalExecComputeUnits, + numCreateDerivationsExternalExec, + numMemoryBanks, + baseAtaCreateComputeUnits+findPdaComputeUnits(ataBump), + ) +} + +func execComputeUnitLimit(baseComputeUnits, numCreateDerivations uint32, numMemoryBanks int, additionalComputeUnits uint32) uint32 { + computeUnits := baseComputeUnits + additionalComputeUnits + computeUnits += (numCreateDerivations + uint32(numMemoryBanks)) * cuPerPdaDerivation + return WithComputeUnitMargin(computeUnits) +} + +// ReserveBuySwapComputeUnitLimit computes the compute unit limit for a +// reserve buy swap transaction, whose only bump-dependent cost is creating +// the temporary core mint ATA. +func ReserveBuySwapComputeUnitLimit(temporaryAtaBump uint8) uint32 { + return WithComputeUnitMargin(baseReserveBuySwapComputeUnits + baseAtaCreateComputeUnits + findPdaComputeUnits(temporaryAtaBump)) +} + +// ReserveSellSwapComputeUnitLimit computes the compute unit limit for a +// reserve sell swap transaction, whose only bump-dependent cost is creating +// the temporary source currency ATA. +func ReserveSellSwapComputeUnitLimit(temporaryAtaBump uint8) uint32 { + return WithComputeUnitMargin(baseReserveSellSwapComputeUnits + baseAtaCreateComputeUnits + findPdaComputeUnits(temporaryAtaBump)) +} + +// ReserveBuySellSwapComputeUnitLimit computes the compute unit limit for a +// reserve buy/sell swap transaction, which creates temporary ATAs for both +// the core mint and the source currency. +func ReserveBuySellSwapComputeUnitLimit(temporaryCoreAtaBump, temporarySourceAtaBump uint8) uint32 { + return WithComputeUnitMargin( + baseReserveBuySellSwapComputeUnits + + 2*baseAtaCreateComputeUnits + + findPdaComputeUnits(temporaryCoreAtaBump) + + findPdaComputeUnits(temporarySourceAtaBump), + ) +} + +// ExternalDepositComputeUnitLimit computes the compute unit limit for a +// deposit_from_pda transaction sweeping an external deposit into the VM. +func ExternalDepositComputeUnitLimit() uint32 { + return WithComputeUnitMargin(baseExternalDepositComputeUnits) +} + +// CloseVmDepositComputeUnitLimit computes the compute unit limit for a +// close_deposit_account_if_empty transaction. +func CloseVmDepositComputeUnitLimit() uint32 { + return WithComputeUnitMargin(baseCloseVmDepositComputeUnits) +} diff --git a/ocp/transaction/compute_budget_test.go b/ocp/transaction/compute_budget_test.go new file mode 100644 index 0000000..c2affa5 --- /dev/null +++ b/ocp/transaction/compute_budget_test.go @@ -0,0 +1,54 @@ +package transaction + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExecComputeUnitLimits(t *testing.T) { + // base 60,000 + (5 creates + 2 banks) * 1,500 = 70,500, plus 15% margin + assert.EqualValues(t, 81_075, internalExecComputeUnitLimit(2)) + + // base 65,000 + (4 creates + 2 banks) * 1,500 = 74,000, plus 15% margin + assert.EqualValues(t, 85_100, externalExecComputeUnitLimit(2)) + + // Adding a memory bank costs one derivation plus margin + assert.EqualValues(t, 1_725, internalExecComputeUnitLimit(3)-internalExecComputeUnitLimit(2)) + + // The ATA derivation cost scales with how far the bump is from 255 + canonical := externalExecWithAtaCreateComputeUnitLimit(2, 255) + lowBump := externalExecWithAtaCreateComputeUnitLimit(2, 240) + assert.EqualValues(t, 15*cuPerPdaDerivation*115/100, lowBump-canonical) + + // A create-on-send transaction always budgets more than a plain external + // transfer + assert.Greater(t, canonical, externalExecComputeUnitLimit(2)) +} + +func TestReserveSwapComputeUnitLimits(t *testing.T) { + // base 90,000 + ATA create (15,000) + ATA find (1,500) = 106,500, plus + // 15% margin + assert.EqualValues(t, 122_475, ReserveBuySwapComputeUnitLimit(255)) + + // base 100,000 + ATA create (15,000) + ATA find (1,500) = 116,500, plus + // 15% margin + assert.EqualValues(t, 133_975, ReserveSellSwapComputeUnitLimit(255)) + + // base 150,000 + 2 ATA creates (30,000) + 2 ATA finds (3,000) = 183,000, + // plus 15% margin + assert.EqualValues(t, 210_450, ReserveBuySellSwapComputeUnitLimit(255, 255)) +} + +func TestOpenAccountComputeUnitLimit(t *testing.T) { + // base 10,000 + 2 creates (3,000) + state/vault/unlock finds (4,500) + + // withdraw receipt allowance (36,000) = 53,500, plus 15% margin + assert.EqualValues(t, 61_525, openAccountComputeUnitLimit(255, 255, 255)) + + // Low user bumps grow the limit by exactly the extra find iterations + assert.EqualValues( + t, + 15*cuPerPdaDerivation*115/100, + openAccountComputeUnitLimit(240, 255, 255)-openAccountComputeUnitLimit(255, 255, 255), + ) +} diff --git a/ocp/transaction/transaction.go b/ocp/transaction/transaction.go index fcd28a2..68a522f 100644 --- a/ocp/transaction/transaction.go +++ b/ocp/transaction/transaction.go @@ -60,7 +60,7 @@ func MakeOpenAccountTransaction( instructions := []solana.Instruction{ compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(100_000), + compute_budget.SetComputeUnitLimit(openAccountComputeUnitLimit(timelockAccounts.StateBump, timelockAccounts.VaultBump, timelockAccounts.UnlockBump)), initializeInstruction, } return MakeNoncedTransaction(nonce, instructions...) @@ -148,7 +148,7 @@ func MakeInternalWithdrawTransaction( instructions := []solana.Instruction{ compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(100_000), + compute_budget.SetComputeUnitLimit(internalExecComputeUnitLimit(mergedMemoryBanks.NumBanks())), execInstruction, } return MakeNoncedTransaction(nonce, instructions...) @@ -201,7 +201,7 @@ func MakeExternalWithdrawTransaction( instructions := []solana.Instruction{ compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(100_000), + compute_budget.SetComputeUnitLimit(externalExecComputeUnitLimit(mergedMemoryBanks.NumBanks())), execInstruction, } return MakeNoncedTransaction(nonce, instructions...) @@ -253,7 +253,7 @@ func MakeInternalTransferWithAuthorityTransaction( instructions := []solana.Instruction{ compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(100_000), + compute_budget.SetComputeUnitLimit(internalExecComputeUnitLimit(mergedMemoryBanks.NumBanks())), execInstruction, } return MakeNoncedTransaction(nonce, instructions...) @@ -310,15 +310,7 @@ func MakeExternalTransferWithAuthorityTransaction( }, ) - computeLimit := 100_000 - if isCreateOnSend { - computeLimit = 125_000 - } - - instructions := []solana.Instruction{ - compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(uint32(computeLimit)), - } + var instructions []solana.Instruction if isCreateOnSend { if externalDestinationOwner == nil { return solana.Transaction{}, errors.New("destination owner is required") @@ -335,7 +327,24 @@ func MakeExternalTransferWithAuthorityTransaction( return solana.Transaction{}, errors.New("invalid destination owner") } - instructions = append(instructions, createIdempotentInstruction) + _, ataBump, err := token.GetAssociatedAccountAndBump( + externalDestinationOwner.PublicKey().ToBytes(), + mint.PublicKey().ToBytes(), + ) + if err != nil { + return solana.Transaction{}, err + } + + instructions = []solana.Instruction{ + compute_budget.SetComputeUnitPrice(10_000), + compute_budget.SetComputeUnitLimit(externalExecWithAtaCreateComputeUnitLimit(mergedMemoryBanks.NumBanks(), ataBump)), + createIdempotentInstruction, + } + } else { + instructions = []solana.Instruction{ + compute_budget.SetComputeUnitPrice(10_000), + compute_budget.SetComputeUnitLimit(externalExecComputeUnitLimit(mergedMemoryBanks.NumBanks())), + } } instructions = append(instructions, execInstruction) return MakeNoncedTransaction(nonce, instructions...) @@ -400,3 +409,13 @@ func MergeMemoryBanks(accounts ...*common.Account) (*MergedMemoryBankResult, err Indices: indices, }, nil } + +func (r *MergedMemoryBankResult) NumBanks() int { + var count int + for _, bank := range []*ed25519.PublicKey{r.A, r.B, r.C, r.D} { + if bank != nil { + count++ + } + } + return count +} diff --git a/ocp/worker/currency/feeburner/worker.go b/ocp/worker/currency/feeburner/worker.go index 1b51588..5a49b6e 100644 --- a/ocp/worker/currency/feeburner/worker.go +++ b/ocp/worker/currency/feeburner/worker.go @@ -17,8 +17,10 @@ import ( ) const ( - burnFeesComputeUnitLimit = 100_000 - computeUnitPrice = 10_000 + perBurnComputeUnits = 5_000 + baseComputeUnits = 300 + + computeUnitPrice = 10_000 ) type burnTarget struct { @@ -161,7 +163,7 @@ func (p *runtime) makeBurnTransaction(batch []*burnTarget) solana.Transaction { ixns := make([]solana.Instruction, 0, len(batch)+2) ixns = append( ixns, - compute_budget.SetComputeUnitLimit(uint32(len(batch))*burnFeesComputeUnitLimit), + compute_budget.SetComputeUnitLimit(transaction_util.WithComputeUnitMargin(baseComputeUnits+uint32(len(batch))*perBurnComputeUnits)), compute_budget.SetComputeUnitPrice(computeUnitPrice), ) for _, target := range batch { diff --git a/ocp/worker/geyser/external_deposit.go b/ocp/worker/geyser/external_deposit.go index 1fd9dff..57bf2e2 100644 --- a/ocp/worker/geyser/external_deposit.go +++ b/ocp/worker/geyser/external_deposit.go @@ -120,7 +120,7 @@ func initiateExternalDepositIntoVm(ctx context.Context, data ocp_data.Provider, vmConfig.Authority.PublicKey().ToBytes(), memov2.Instruction(codeVmDepositMemoValue), compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(45_000), + compute_budget.SetComputeUnitLimit(transaction_util.ExternalDepositComputeUnitLimit()), vm.NewDepositFromPdaInstruction( &vm.DepositFromPdaInstructionAccounts{ VmAuthority: vmConfig.Authority.PublicKey().ToBytes(), @@ -437,7 +437,7 @@ func closeVmDepositAccount(ctx context.Context, data ocp_data.Provider, userAuth txn := solana.NewLegacyTransaction( vmConfig.Authority.PublicKey().ToBytes(), compute_budget.SetComputeUnitPrice(10_000), - compute_budget.SetComputeUnitLimit(25_000), + compute_budget.SetComputeUnitLimit(transaction_util.CloseVmDepositComputeUnitLimit()), vm.NewCloseDepositAccountIfEmptyInstruction( &vm.CloseDepositAccountIfEmptyInstructionAccounts{ VmAuthority: vmConfig.Authority.PublicKey().ToBytes(), diff --git a/solana/token/associated.go b/solana/token/associated.go index abd3819..2d7c121 100644 --- a/solana/token/associated.go +++ b/solana/token/associated.go @@ -32,6 +32,17 @@ func GetAssociatedAccount(wallet, mint ed25519.PublicKey) (ed25519.PublicKey, er ) } +// GetAssociatedAccountAndBump returns the associated account address and bump +// seed for an SPL token. +func GetAssociatedAccountAndBump(wallet, mint ed25519.PublicKey) (ed25519.PublicKey, uint8, error) { + return solana.FindProgramAddressAndBump( + AssociatedTokenAccountProgramKey, + wallet, + ProgramKey, + mint, + ) +} + // Reference: https://github.com/solana-program/associated-token-account/blob/0588a2c3558cc93c31d27bcc96f97cf559a767bc/program/src/instruction.rs#L9-L17 func CreateAssociatedTokenAccount(subsidizer, wallet, mint ed25519.PublicKey) (solana.Instruction, ed25519.PublicKey, error) { addr, err := GetAssociatedAccount(wallet, mint) From b1a40576617590fe35bc499866f7ce107245a1d1 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Wed, 22 Jul 2026 14:37:43 -0400 Subject: [PATCH 2/3] Tweak values --- ocp/transaction/compute_budget.go | 27 ++++++++++++-------------- ocp/transaction/compute_budget_test.go | 26 ++++++++++++------------- ocp/worker/currency/launcher/util.go | 2 +- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/ocp/transaction/compute_budget.go b/ocp/transaction/compute_budget.go index a4349c2..3d40d71 100644 --- a/ocp/transaction/compute_budget.go +++ b/ocp/transaction/compute_budget.go @@ -11,8 +11,7 @@ package transaction // CUs each, so create-on-send carries the one remaining bump-dependent term. const ( // todo: optimize - baseInternalExecComputeUnits = 60_000 - baseExternalExecComputeUnits = 65_000 + baseExecComputeUnits = 55_000 // vm check + 4 timelock/vault message derivations numCreateDerivationsInternalExec = 5 @@ -21,16 +20,16 @@ const ( numCreateDerivationsExternalExec = 4 // todo: optimize - baseReserveBuySwapComputeUnits = 90_000 - baseReserveSellSwapComputeUnits = 100_000 - baseReserveBuySellSwapComputeUnits = 150_000 + baseReserveBuySwapComputeUnits = 80_000 + baseAtaCreateComputeUnits + baseReserveSellSwapComputeUnits = 90_000 + baseAtaCreateComputeUnits + baseReserveBuySellSwapComputeUnits = 130_000 + 2*baseAtaCreateComputeUnits // todo: optimize baseExternalDepositComputeUnits = 25_000 baseCloseVmDepositComputeUnits = 10_000 // todo: optimize - baseInitTimelockComputeUnits = 10_000 + baseInitTimelockComputeUnits = 20_000 + withdrawReceiptFindComputeUnits // init_timelock: vm + memory checks numCreateDerivationsInitTimelock = 2 @@ -42,7 +41,7 @@ const ( withdrawReceiptFindComputeUnits = 36_000 // todo: optimize - baseAtaCreateComputeUnits = 15_000 + baseAtaCreateComputeUnits = 20_000 cuPerPdaDerivation = 1_500 @@ -68,22 +67,21 @@ func openAccountComputeUnitLimit(timelockStateBump, vaultBump, unlockBump uint8) numCreateDerivationsInitTimelock*cuPerPdaDerivation + findPdaComputeUnits(timelockStateBump) + findPdaComputeUnits(vaultBump) + - findPdaComputeUnits(unlockBump) + - withdrawReceiptFindComputeUnits + findPdaComputeUnits(unlockBump) return WithComputeUnitMargin(computeUnits) } func internalExecComputeUnitLimit(numMemoryBanks int) uint32 { - return execComputeUnitLimit(baseInternalExecComputeUnits, numCreateDerivationsInternalExec, numMemoryBanks, 0) + return execComputeUnitLimit(baseExecComputeUnits, numCreateDerivationsInternalExec, numMemoryBanks, 0) } func externalExecComputeUnitLimit(numMemoryBanks int) uint32 { - return execComputeUnitLimit(baseExternalExecComputeUnits, numCreateDerivationsExternalExec, numMemoryBanks, 0) + return execComputeUnitLimit(baseExecComputeUnits, numCreateDerivationsExternalExec, numMemoryBanks, 0) } func externalExecWithAtaCreateComputeUnitLimit(numMemoryBanks int, ataBump uint8) uint32 { return execComputeUnitLimit( - baseExternalExecComputeUnits, + baseExecComputeUnits, numCreateDerivationsExternalExec, numMemoryBanks, baseAtaCreateComputeUnits+findPdaComputeUnits(ataBump), @@ -100,14 +98,14 @@ func execComputeUnitLimit(baseComputeUnits, numCreateDerivations uint32, numMemo // reserve buy swap transaction, whose only bump-dependent cost is creating // the temporary core mint ATA. func ReserveBuySwapComputeUnitLimit(temporaryAtaBump uint8) uint32 { - return WithComputeUnitMargin(baseReserveBuySwapComputeUnits + baseAtaCreateComputeUnits + findPdaComputeUnits(temporaryAtaBump)) + return WithComputeUnitMargin(baseReserveBuySwapComputeUnits + findPdaComputeUnits(temporaryAtaBump)) } // ReserveSellSwapComputeUnitLimit computes the compute unit limit for a // reserve sell swap transaction, whose only bump-dependent cost is creating // the temporary source currency ATA. func ReserveSellSwapComputeUnitLimit(temporaryAtaBump uint8) uint32 { - return WithComputeUnitMargin(baseReserveSellSwapComputeUnits + baseAtaCreateComputeUnits + findPdaComputeUnits(temporaryAtaBump)) + return WithComputeUnitMargin(baseReserveSellSwapComputeUnits + findPdaComputeUnits(temporaryAtaBump)) } // ReserveBuySellSwapComputeUnitLimit computes the compute unit limit for a @@ -116,7 +114,6 @@ func ReserveSellSwapComputeUnitLimit(temporaryAtaBump uint8) uint32 { func ReserveBuySellSwapComputeUnitLimit(temporaryCoreAtaBump, temporarySourceAtaBump uint8) uint32 { return WithComputeUnitMargin( baseReserveBuySellSwapComputeUnits + - 2*baseAtaCreateComputeUnits + findPdaComputeUnits(temporaryCoreAtaBump) + findPdaComputeUnits(temporarySourceAtaBump), ) diff --git a/ocp/transaction/compute_budget_test.go b/ocp/transaction/compute_budget_test.go index c2affa5..3c1164f 100644 --- a/ocp/transaction/compute_budget_test.go +++ b/ocp/transaction/compute_budget_test.go @@ -7,11 +7,11 @@ import ( ) func TestExecComputeUnitLimits(t *testing.T) { - // base 60,000 + (5 creates + 2 banks) * 1,500 = 70,500, plus 15% margin - assert.EqualValues(t, 81_075, internalExecComputeUnitLimit(2)) + // base 55,000 + (5 creates + 2 banks) * 1,500 = 65,500, plus 15% margin + assert.EqualValues(t, 75_325, internalExecComputeUnitLimit(2)) - // base 65,000 + (4 creates + 2 banks) * 1,500 = 74,000, plus 15% margin - assert.EqualValues(t, 85_100, externalExecComputeUnitLimit(2)) + // base 55,000 + (4 creates + 2 banks) * 1,500 = 64,000, plus 15% margin + assert.EqualValues(t, 73_600, externalExecComputeUnitLimit(2)) // Adding a memory bank costs one derivation plus margin assert.EqualValues(t, 1_725, internalExecComputeUnitLimit(3)-internalExecComputeUnitLimit(2)) @@ -27,23 +27,23 @@ func TestExecComputeUnitLimits(t *testing.T) { } func TestReserveSwapComputeUnitLimits(t *testing.T) { - // base 90,000 + ATA create (15,000) + ATA find (1,500) = 106,500, plus + // base 80,000 + ATA create (20,000) + ATA find (1,500) = 101,500, plus // 15% margin - assert.EqualValues(t, 122_475, ReserveBuySwapComputeUnitLimit(255)) + assert.EqualValues(t, 116_725, ReserveBuySwapComputeUnitLimit(255)) - // base 100,000 + ATA create (15,000) + ATA find (1,500) = 116,500, plus + // base 90,000 + ATA create (20,000) + ATA find (1,500) = 111,500, plus // 15% margin - assert.EqualValues(t, 133_975, ReserveSellSwapComputeUnitLimit(255)) + assert.EqualValues(t, 128_225, ReserveSellSwapComputeUnitLimit(255)) - // base 150,000 + 2 ATA creates (30,000) + 2 ATA finds (3,000) = 183,000, + // base 130,000 + 2 ATA creates (40,000) + 2 ATA finds (3,000) = 173,000, // plus 15% margin - assert.EqualValues(t, 210_450, ReserveBuySellSwapComputeUnitLimit(255, 255)) + assert.EqualValues(t, 198_950, ReserveBuySellSwapComputeUnitLimit(255, 255)) } func TestOpenAccountComputeUnitLimit(t *testing.T) { - // base 10,000 + 2 creates (3,000) + state/vault/unlock finds (4,500) + - // withdraw receipt allowance (36,000) = 53,500, plus 15% margin - assert.EqualValues(t, 61_525, openAccountComputeUnitLimit(255, 255, 255)) + // base 20,000 + 2 creates (3,000) + state/vault/unlock finds (4,500) + + // withdraw receipt allowance (36,000) = 63,500, plus 15% margin + assert.EqualValues(t, 73_025, openAccountComputeUnitLimit(255, 255, 255)) // Low user bumps grow the limit by exactly the extra find iterations assert.EqualValues( diff --git a/ocp/worker/currency/launcher/util.go b/ocp/worker/currency/launcher/util.go index 620c434..401602d 100644 --- a/ocp/worker/currency/launcher/util.go +++ b/ocp/worker/currency/launcher/util.go @@ -802,7 +802,7 @@ func (p *runtime) populateNonceMemory(ctx context.Context, accounts *newCurrency err := func() error { ixns := []solana.Instruction{ - compute_budget.SetComputeUnitLimit(550_000), + compute_budget.SetComputeUnitLimit(400_000), compute_budget.SetComputeUnitPrice(10_000), } for i := range initVdnIxnsPerTxn { From d2404969a431db8b1929c28828d900540d299999 Mon Sep 17 00:00:00 2001 From: jeffyanta Date: Wed, 22 Jul 2026 14:40:12 -0400 Subject: [PATCH 3/3] Update comments --- ocp/transaction/compute_budget.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ocp/transaction/compute_budget.go b/ocp/transaction/compute_budget.go index 3d40d71..77ccbd1 100644 --- a/ocp/transaction/compute_budget.go +++ b/ocp/transaction/compute_budget.go @@ -1,14 +1,5 @@ package transaction -// Compute unit limits for exec transactions, modeled against the VM program -// version that validates PDAs with create_program_address using stored bumps -// (a flat 1,500 CUs per derivation, independent of bump value). Deploying -// these limits against the older find_program_address-based VM program would -// under-budget VMs with low-bump PDAs. -// -// The ATA program still derives the associated token address with -// find_program_address, which walks candidate bumps from 255 down at 1,500 -// CUs each, so create-on-send carries the one remaining bump-dependent term. const ( // todo: optimize baseExecComputeUnits = 55_000