Skip to content

Commit

Permalink
feat: reduce rpc calls in python sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
m30m committed Dec 5, 2024
1 parent d0e7b89 commit a118576
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ async def generate_take_order_ixs(
f"Sell token {order['state'].input_mint} amount: {Decimal(input_amount) / Decimal(10 ** input_mint_decimals)}\n"
f"Buy token {order['state'].output_mint} amount: {Decimal(output_amount) / Decimal(10**output_mint_decimals)}"
)
ixs_take_order = await self.limo_client.take_order_ix(
ixs_take_order = self.limo_client.take_order_ix(
self.private_key.pubkey(),
order,
input_amount,
Expand Down
90 changes: 37 additions & 53 deletions sdk/python/express_relay/svm/limo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ async def get_mint_decimals(self, mint: Pubkey) -> int:
decimals = decoded_data.decimals
return decimals

async def account_exists(self, address: Pubkey) -> bool:
account_info = await self._connection.get_account_info(address)
return account_info.value is not None

def create_associated_token_account_idempotent(
self, payer: Pubkey, owner: Pubkey, mint: Pubkey, token_program_id: Pubkey
) -> Instruction:
Expand Down Expand Up @@ -111,82 +107,72 @@ def create_associated_token_account_idempotent(
data=bytes([1]), # idempotent version of the instruction
)

async def get_ata_and_create_ixn_if_required(
def get_ata_and_create_ixn_if_required(
self,
owner: Pubkey,
token_mint_address: Pubkey,
token_program_id: Pubkey,
payer: Pubkey,
) -> Tuple[Pubkey, Sequence[Instruction]]:
ata = self.get_ata(owner, token_mint_address, token_program_id)
if not await self.account_exists(ata):
ix = self.create_associated_token_account_idempotent(
payer, owner, token_mint_address, token_program_id
)
return ata, [ix]
return ata, []
ix = self.create_associated_token_account_idempotent(
payer, owner, token_mint_address, token_program_id
)
return ata, [ix]

async def get_init_if_needed_wsol_create_and_close_ixs(
def get_init_if_needed_wsol_create_and_close_ixs(
self, owner: Pubkey, payer: Pubkey, amount_to_deposit_lamports: int
) -> WSOLInstructions:
"""
Returns necessary instructions to create, fill and close a wrapped SOL account.
If the account already exists:
it makes sure the balance is at least `amount_to_deposit_lamports` with no create or close instructions.
If the account does not exist:
it creates the account, fills it with `amount_to_deposit_lamports` lamports and close it in the end.
Creation instruction is idempotent.
Filling instruction doesn't take into account the current WSOL balance.
Closing instruction unwraps all the WSOL back to the owner, even if it was deposited by another transaction.
Args:
owner: Who owns the WSOL token account
payer: Who pays for the instructions
amount_to_deposit_lamports: Minimum amount of lamports required in the account
amount_to_deposit_lamports: Amount of lamports to deposit into the WSOL account
"""
ata = self.get_ata(owner, WRAPPED_SOL_MINT, TOKEN_PROGRAM_ID)
ata_info = await self._connection.get_account_info(ata)

create_ixs = []
close_ixs = []
if ata_info.value is None or len(ata_info.value.data) == 0:
create_ixs = [
self.create_associated_token_account_idempotent(
payer, owner, WRAPPED_SOL_MINT, TOKEN_PROGRAM_ID
)
]
close_ixs = [
spl_token.close_account(
spl_token.CloseAccountParams(
program_id=TOKEN_PROGRAM_ID,
account=ata,
dest=owner,
owner=owner,
)
)
]
create_ixs = [
self.create_associated_token_account_idempotent(
payer, owner, WRAPPED_SOL_MINT, TOKEN_PROGRAM_ID
)
]

fill_ixs = []
current_balance = (
TOKEN_ACCOUNT_LAYOUT.parse(ata_info.value.data).amount
if ata_info.value and len(ata_info.value.data) > 0
else 0
)
if current_balance < amount_to_deposit_lamports:
if amount_to_deposit_lamports > 0 and payer == owner:
fill_ixs = [
system_program.transfer(
TransferParams(
from_pubkey=owner,
to_pubkey=ata,
lamports=amount_to_deposit_lamports - current_balance,
lamports=amount_to_deposit_lamports,
)
),
spl_token.sync_native(
spl_token.SyncNativeParams(TOKEN_PROGRAM_ID, ata)
),
]

close_ixs = []
if payer == owner:
close_ixs = [
spl_token.close_account(
spl_token.CloseAccountParams(
program_id=TOKEN_PROGRAM_ID,
account=ata,
dest=owner,
owner=owner,
)
)
]
return WSOLInstructions(
create_ixs=create_ixs, fill_ixs=fill_ixs, close_ixs=close_ixs, ata=ata
)

async def take_order_ix(
def take_order_ix(
self,
taker: Pubkey,
order: OrderStateAndAddress,
Expand All @@ -199,10 +185,8 @@ async def take_order_ix(
Args:
taker: The taker's public key
order: The order to fulfill
input_amount_decimals: The amount of input tokens to take. Will be multiplied by 10 ** input_mint_decimals in this method.
output_amount_decimals: The amount of output tokens to provide. Will be multiplied by 10 ** output_mint_decimals in this method.
input_mint_decimals: input mint decimals (can be fetched via get_mint_decimals)
output_mint_decimals: output mint decimals (can be fetched via get_mint_decimals)
input_amount: The amount of input tokens to take.
output_amount: The amount of output tokens to provide.
express_relay_program_id: Express relay program id
Returns:
Expand All @@ -214,7 +198,7 @@ async def take_order_ix(
close_wsol_ixns: List[Instruction] = []
taker_input_ata: Pubkey
if order["state"].input_mint == WRAPPED_SOL_MINT:
instructions = await self.get_init_if_needed_wsol_create_and_close_ixs(
instructions = self.get_init_if_needed_wsol_create_and_close_ixs(
owner=taker, payer=taker, amount_to_deposit_lamports=0
)
ixs.extend(instructions["create_ixs"])
Expand All @@ -224,7 +208,7 @@ async def take_order_ix(
(
taker_input_ata,
create_taker_input_ata_ixs,
) = await self.get_ata_and_create_ixn_if_required(
) = self.get_ata_and_create_ixn_if_required(
owner=taker,
token_mint_address=order["state"].input_mint,
token_program_id=order["state"].input_mint_program_id,
Expand All @@ -236,7 +220,7 @@ async def take_order_ix(
maker_output_ata: Pubkey | None = None
intermediary_output_token_account: Pubkey | None = None
if order["state"].output_mint == WRAPPED_SOL_MINT:
instructions = await self.get_init_if_needed_wsol_create_and_close_ixs(
instructions = self.get_init_if_needed_wsol_create_and_close_ixs(
owner=taker, payer=taker, amount_to_deposit_lamports=output_amount
)
ixs.extend(instructions["create_ixs"])
Expand All @@ -249,7 +233,7 @@ async def take_order_ix(
(
taker_output_ata,
create_taker_output_ata_ixs,
) = await self.get_ata_and_create_ixn_if_required(
) = self.get_ata_and_create_ixn_if_required(
owner=taker,
token_mint_address=order["state"].output_mint,
token_program_id=order["state"].output_mint_program_id,
Expand All @@ -260,7 +244,7 @@ async def take_order_ix(
(
maker_output_ata,
create_maker_output_ata_ixs,
) = await self.get_ata_and_create_ixn_if_required(
) = self.get_ata_and_create_ixn_if_required(
owner=order["state"].maker,
token_mint_address=order["state"].output_mint,
token_program_id=order["state"].output_mint_program_id,
Expand Down

0 comments on commit a118576

Please sign in to comment.