4

I would like to set Compute Limit and Compute Unit Price before sending my transaction (Jupiter swap). I want to do this in order to optimize the Compute Units of my transaction. However, I cannot figure out how to set this using Python. This is currently how I build my transaction. Could anyone help with this?

data = {
    #// quoteResponse from /quote api
    "quoteResponse":quoteResponse,
    #// user public key to be used for the swap
    "userPublicKey": wallet_I_want_to_trade_with,
    #// auto wrap and unwrap SOL. default is true
    "wrapAndUnwrapSol": True,
    'dynamicComputeUnitLimit': True,
    'prioritizationFeeLamports': prioritizationFeeLamports
    #// feeAccount is optional. Use if you want to charge a fee.  feeBps must have been passed in /quote API.
    #// feeAccount: "fee_account_public_key"
    }


response = requests.post(url, headers=headers, json=data)
swapTransaction=response.json()

swapTransaction_string = json.dumps(swapTransaction['swapTransaction'])
swapTransaction_bytes=base64.b64encode(swapTransaction_string.encode('utf-8'))
swapTransaction_to_input = json.loads(swapTransaction_string)

raw_tx = solders.transaction.VersionedTransaction.from_bytes(
    base64.b64decode(swapTransaction_to_input)
)             

signed_txn = wallet.sign_message(message.to_bytes_versioned(raw_tx.message))
s_signed_txn = solders.transaction.VersionedTransaction.populate(
    raw_tx.message, [signed_txn]
)

encoded_tx = base64.b64encode(bytes(s_signed_txn)).decode("utf-8")

headers = {'Content-Type': 'application/json'}

blockhashResponse=solana_client.get_latest_blockhash(commitment='finalized')
lastValidBlockHeight=blockhashResponse.value.last_valid_block_height
data = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
        encoded_tx,
        {
            "skipPreflight": True,
            "preflightCommitment": "processed",
            "encoding": "base64",
            "maxRetries": 0,
            "minContextSlot": None,
            "recentBlockhash": lastValidBlockHeight
        },
    ],
}
response_tx=requests.post(endpoint,headers=headers,data=json.dumps(data)).json()
print(response_tx)

1 Answer 1

4

The idea is that the compute limit and compute limit price are instructions. And from the Jupiter API we already get a transactions object. Thus we have to add these priority fee instructions to the existing instructions of the transaction. The method I use for this is that I deconstruct the raw transaction object and rebuild it with the priority fee instructions added to it.

It is important to note that the priority fee instruction should normally be the first instruction thats executed thats why ix is set such that the priority fee instruction comes before the swapping instruction. Also the way I set up the priorityInstruction is that the program_id_index tells the instructions which address is associated to it in the account keys (in this case ComputeBudget111111111111111111111111111111) which is appended to the end of the accountKeys list, thus the index for this element in account keys is the length of the list itself.

rawTransaction=VersionedTransaction.from_bytes(base64.b64decode(apiTransaction))
priorityFee = set_compute_unit_price(gas)
accountKeys = rawTransaction.message.account_keys
lengthAccountKeys = len(accountKeys)
accountKeys.append(priorityFee.program_id)

rawInstructions = rawTransaction.message.instructions

priorityInstruction = CompiledInstruction(
    program_id_index=lengthAccountKeys,
    data=priorityFee.data,
    accounts=rawInstructions[0].accounts,
)
ix = [priorityInstruction, *rawInstructions]

newTransactionMessage = Message.new_with_compiled_instructions(
     num_required_signatures=rawTransaction.message.header.num_required_signatures,
     num_readonly_signed_accounts=rawTransaction.message.header.num_readonly_signed_accounts,
     num_readonly_unsigned_accounts=rawTransaction.message.header.num_readonly_unsigned_accounts,
     account_keys=accountKeys,
     recent_blockhash=recentBlockhash,
     instructions=ix,
)

userSignature = userKeypair.sign_message(message.to_bytes_versioned(newTransactionMessage))

In response to comments

  1. No, when inputting the priority fees you want to use into the API request on Jupiter, that will be the amount you spend for the priority fees in total. When adding the priority fees "manually" as an instruction you should be aware of difference between the compute unit price and compute unit limit. Lets first talk about what a "compute unit" is. A compute unit is kind of a measurement of how much computation you need in order to process your instructions. A single Solana transaction can have multiple instructions (luckily Jupiter transactions are normally only a single big instruction).

    By default, each instruction within a transaction has a compute unit limit of 200,000. This can be changed (to a max of 1.4M) using the set_compute_unit_limit(). But beware that a higher compute unit limit can decrease the chance that your transaction will be included on chain, since it takes more computation for a validator to process it.

    In the Jupiter API request in your question the param "dynamicComputeUnitLimit" is set to True. This parameter will basically simulate the transaction and then use set_compute_unit_limit() to set the compute unit of your transaction to an appropriate amount, whereas if that was false it would be set to 1,400,000, this a default value set by Jupiter. (If you are creating a transaction and its instructions from scratch it could be a good idea to use the same method as Jupiter does where you simulate the transaction and then use that as a ballpark as to how much your compute unit limit should be, but in the scope of using Jupiter just leave that param to True).

    The compute unit price is the price (in lamports) you are paying to the validator per unit of computation.

    For example, if you send a transaction with a single instruction, which has a compute unit limit of 200,000 and you set your compute unit price to 2.8 lamports per unit. Then you will end up paying a priority fee of 560000 lamports which is (560000 * 10^(-9) =) 0.00056 SOL.

  2. No, it only sets the price you are going to pay per compute unit. The function is defined in the solders python library.

  3. Remember that if you're using the Jupiter API and you are using the "prioritizationFeeLamports" param in your API request then there is no reason to change the compute unit price, since this is already done by Jupiter. Jupiter will automatically calculate how much you need to pay per compute unit in order for the priority fee to be the amount you specified. Thus the transaction already has the compute unit price instruction present and there is no reason to add it again.

    If you want to add it to a transaction which doesnt have it, follow the steps I mentioned in my initial answer, where you need to add the instruction manually.

    Also Solana is having a lot of congestion recently and some transactions do fail more often than others. Maybe try setting a higher priority fee.

4
  • thanks for you help! I have some questions: 1) isn't this setting the priority fee rather than the compute units? How would this be different than inputting a priority fee when using the Jupiter SWAP API (station.jup.ag/docs/apis/swap-api) 2) the "set_compute_unit_price(gas)" should output the priority fee in SOL lamports? Also, how do you define the function set_compute_unit_price(gas)? Commented Apr 16 at 11:36
  • I figured out how to use set_compute_unit_price(gas). I'm able to build the transaction, and get a TX ID when sending it, but the transaction never lands onchain (i'm sending it with max retries=0 and looping over the sendTransaction RPC until I get a transaction status, but I never get it because it doesn't land. Any ideas why this could happen? By the way, how can I set the set_compute_unit_limit in the transaction before sending it? I can't figure out how to do that. Commented Apr 17 at 16:06
  • 1
    I edited my original reply with some of the answers Commented Apr 20 at 9:10
  • Thank you very much! Commented Apr 20 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.