Module orca_whirlpool.internal.transaction.transaction_builder

Expand source code
from typing import Optional
from solders.keypair import Keypair
from solders.transaction import Transaction
from solders.signature import Signature
from solders import compute_budget
from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Commitment, Confirmed
from .types import Instruction, TransactionPayload

EMPTY_INSTRUCTION = Instruction([], [], [])

CONFIRM_TRANSACTION_CHECK_INTERVAL_SECOND = 1


class TransactionBuilder:
    def __init__(self, connection: AsyncClient, fee_payer: Keypair):
        self._connection = connection
        self._fee_payer = fee_payer
        self._instructions = []
        self._signers = []

    def set_compute_unit_limit(self, units: int):
        self._instructions.insert(0, Instruction(
            instructions=[compute_budget.set_compute_unit_limit(units)],
            cleanup_instructions=[],
            signers=[]
        ))

    def set_compute_unit_price(self, micro_lamports: int):
        self._instructions.insert(0, Instruction(
            instructions=[compute_budget.set_compute_unit_price(micro_lamports)],
            cleanup_instructions=[],
            signers=[]
        ))

    def add_instruction(self, instruction: Instruction) -> "TransactionBuilder":
        if not is_empty_instruction(instruction):
            self._instructions.append(instruction)
        return self

    def add_signer(self, signer: Keypair) -> "TransactionBuilder":
        self._signers.append(signer)
        return self

    def is_empty(self) -> bool:
        return len(self._instructions) == 0

    def pack_instructions(self, merge_cleanup_instructions: bool) -> Instruction:
        instructions = []
        cleanup_instructions = []
        signers = []
        for instruction in self._instructions:
            instructions.extend(instruction.instructions)
            cleanup_instructions = instruction.cleanup_instructions + cleanup_instructions
            signers.extend(instruction.signers)

        if merge_cleanup_instructions:
            instructions.extend(cleanup_instructions)
            cleanup_instructions = []

        return Instruction(
            instructions=instructions,
            cleanup_instructions=cleanup_instructions,
            signers=signers,
        )

    def build(self) -> TransactionPayload:
        packed = self.pack_instructions(True)

        return TransactionPayload(
            transaction=Transaction.new_with_payer(packed.instructions, self._fee_payer.pubkey()),
            signers=[self._fee_payer] + packed.signers + self._signers,
        )

    async def build_and_execute(self, commitment: Optional[Commitment] = Confirmed) -> Signature:
        payload = self.build()

        latest_blockhash = (await self._connection.get_latest_blockhash()).value

        tx = Transaction(payload.signers, payload.transaction.message, latest_blockhash.blockhash)
        signature = (await self._connection.send_raw_transaction(bytes(tx))).value
        await self._connection.confirm_transaction(
            signature,
            commitment,
            sleep_seconds=CONFIRM_TRANSACTION_CHECK_INTERVAL_SECOND,
            last_valid_block_height=latest_blockhash.last_valid_block_height
        )
        return signature


def is_empty_instruction(instruction: Instruction) -> bool:
    if len(instruction.instructions) > 0:
        return False
    if len(instruction.cleanup_instructions) > 0:
        return False
    if len(instruction.signers) > 0:
        return False
    return True

Functions

def is_empty_instruction(instruction: Instruction) ‑> bool
Expand source code
def is_empty_instruction(instruction: Instruction) -> bool:
    if len(instruction.instructions) > 0:
        return False
    if len(instruction.cleanup_instructions) > 0:
        return False
    if len(instruction.signers) > 0:
        return False
    return True

Classes

class TransactionBuilder (connection: solana.rpc.async_api.AsyncClient, fee_payer: solders.keypair.Keypair)
Expand source code
class TransactionBuilder:
    def __init__(self, connection: AsyncClient, fee_payer: Keypair):
        self._connection = connection
        self._fee_payer = fee_payer
        self._instructions = []
        self._signers = []

    def set_compute_unit_limit(self, units: int):
        self._instructions.insert(0, Instruction(
            instructions=[compute_budget.set_compute_unit_limit(units)],
            cleanup_instructions=[],
            signers=[]
        ))

    def set_compute_unit_price(self, micro_lamports: int):
        self._instructions.insert(0, Instruction(
            instructions=[compute_budget.set_compute_unit_price(micro_lamports)],
            cleanup_instructions=[],
            signers=[]
        ))

    def add_instruction(self, instruction: Instruction) -> "TransactionBuilder":
        if not is_empty_instruction(instruction):
            self._instructions.append(instruction)
        return self

    def add_signer(self, signer: Keypair) -> "TransactionBuilder":
        self._signers.append(signer)
        return self

    def is_empty(self) -> bool:
        return len(self._instructions) == 0

    def pack_instructions(self, merge_cleanup_instructions: bool) -> Instruction:
        instructions = []
        cleanup_instructions = []
        signers = []
        for instruction in self._instructions:
            instructions.extend(instruction.instructions)
            cleanup_instructions = instruction.cleanup_instructions + cleanup_instructions
            signers.extend(instruction.signers)

        if merge_cleanup_instructions:
            instructions.extend(cleanup_instructions)
            cleanup_instructions = []

        return Instruction(
            instructions=instructions,
            cleanup_instructions=cleanup_instructions,
            signers=signers,
        )

    def build(self) -> TransactionPayload:
        packed = self.pack_instructions(True)

        return TransactionPayload(
            transaction=Transaction.new_with_payer(packed.instructions, self._fee_payer.pubkey()),
            signers=[self._fee_payer] + packed.signers + self._signers,
        )

    async def build_and_execute(self, commitment: Optional[Commitment] = Confirmed) -> Signature:
        payload = self.build()

        latest_blockhash = (await self._connection.get_latest_blockhash()).value

        tx = Transaction(payload.signers, payload.transaction.message, latest_blockhash.blockhash)
        signature = (await self._connection.send_raw_transaction(bytes(tx))).value
        await self._connection.confirm_transaction(
            signature,
            commitment,
            sleep_seconds=CONFIRM_TRANSACTION_CHECK_INTERVAL_SECOND,
            last_valid_block_height=latest_blockhash.last_valid_block_height
        )
        return signature

Methods

def add_instruction(self, instruction: Instruction) ‑> TransactionBuilder
Expand source code
def add_instruction(self, instruction: Instruction) -> "TransactionBuilder":
    if not is_empty_instruction(instruction):
        self._instructions.append(instruction)
    return self
def add_signer(self, signer: solders.keypair.Keypair) ‑> TransactionBuilder
Expand source code
def add_signer(self, signer: Keypair) -> "TransactionBuilder":
    self._signers.append(signer)
    return self
def build(self) ‑> TransactionPayload
Expand source code
def build(self) -> TransactionPayload:
    packed = self.pack_instructions(True)

    return TransactionPayload(
        transaction=Transaction.new_with_payer(packed.instructions, self._fee_payer.pubkey()),
        signers=[self._fee_payer] + packed.signers + self._signers,
    )
async def build_and_execute(self, commitment: Optional[solana.rpc.commitment.Commitment] = 'confirmed') ‑> solders.signature.Signature
Expand source code
async def build_and_execute(self, commitment: Optional[Commitment] = Confirmed) -> Signature:
    payload = self.build()

    latest_blockhash = (await self._connection.get_latest_blockhash()).value

    tx = Transaction(payload.signers, payload.transaction.message, latest_blockhash.blockhash)
    signature = (await self._connection.send_raw_transaction(bytes(tx))).value
    await self._connection.confirm_transaction(
        signature,
        commitment,
        sleep_seconds=CONFIRM_TRANSACTION_CHECK_INTERVAL_SECOND,
        last_valid_block_height=latest_blockhash.last_valid_block_height
    )
    return signature
def is_empty(self) ‑> bool
Expand source code
def is_empty(self) -> bool:
    return len(self._instructions) == 0
def pack_instructions(self, merge_cleanup_instructions: bool) ‑> Instruction
Expand source code
def pack_instructions(self, merge_cleanup_instructions: bool) -> Instruction:
    instructions = []
    cleanup_instructions = []
    signers = []
    for instruction in self._instructions:
        instructions.extend(instruction.instructions)
        cleanup_instructions = instruction.cleanup_instructions + cleanup_instructions
        signers.extend(instruction.signers)

    if merge_cleanup_instructions:
        instructions.extend(cleanup_instructions)
        cleanup_instructions = []

    return Instruction(
        instructions=instructions,
        cleanup_instructions=cleanup_instructions,
        signers=signers,
    )
def set_compute_unit_limit(self, units: int)
Expand source code
def set_compute_unit_limit(self, units: int):
    self._instructions.insert(0, Instruction(
        instructions=[compute_budget.set_compute_unit_limit(units)],
        cleanup_instructions=[],
        signers=[]
    ))
def set_compute_unit_price(self, micro_lamports: int)
Expand source code
def set_compute_unit_price(self, micro_lamports: int):
    self._instructions.insert(0, Instruction(
        instructions=[compute_budget.set_compute_unit_price(micro_lamports)],
        cleanup_instructions=[],
        signers=[]
    ))