Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions cosmpy/aerial/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ def send_tokens(
memo: Optional[str] = None,
gas_limit: Optional[int] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""Send tokens.

Expand All @@ -384,6 +386,8 @@ def send_tokens(
:param memo: memo, defaults to None
:param gas_limit: gas limit, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None
:return: prepare and broadcast the transaction and transaction details
"""
# build up the store transaction
Expand All @@ -399,6 +403,8 @@ def send_tokens(
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
fee_amount=fee_amount,
fee_granter=fee_granter,
)

def query_validators(
Expand Down Expand Up @@ -501,6 +507,8 @@ def delegate_tokens(
memo: Optional[str] = None,
gas_limit: Optional[int] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""Delegate tokens.

Expand All @@ -510,6 +518,8 @@ def delegate_tokens(
:param memo: memo, defaults to None
:param gas_limit: gas limit, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None
:return: prepare and broadcast the transaction and transaction details
"""
tx = Transaction()
Expand All @@ -529,6 +539,8 @@ def delegate_tokens(
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
fee_amount=fee_amount,
fee_granter=fee_granter,
)

def redelegate_tokens(
Expand All @@ -540,6 +552,8 @@ def redelegate_tokens(
memo: Optional[str] = None,
gas_limit: Optional[int] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""Redelegate tokens.

Expand All @@ -550,6 +564,8 @@ def redelegate_tokens(
:param memo: memo, defaults to None
:param gas_limit: gas limit, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None
:return: prepare and broadcast the transaction and transaction details
"""
tx = Transaction()
Expand All @@ -570,6 +586,8 @@ def redelegate_tokens(
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
fee_amount=fee_amount,
fee_granter=fee_granter,
)

def undelegate_tokens(
Expand All @@ -580,6 +598,8 @@ def undelegate_tokens(
memo: Optional[str] = None,
gas_limit: Optional[int] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""Undelegate tokens.

Expand All @@ -589,6 +609,8 @@ def undelegate_tokens(
:param memo: memo, defaults to None
:param gas_limit: gas limit, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None
:return: prepare and broadcast the transaction and transaction details
"""
tx = Transaction()
Expand All @@ -608,6 +630,8 @@ def undelegate_tokens(
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
fee_amount=fee_amount,
fee_granter=fee_granter,
)

def claim_rewards(
Expand All @@ -617,6 +641,8 @@ def claim_rewards(
memo: Optional[str] = None,
gas_limit: Optional[int] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""claim rewards.

Expand All @@ -625,6 +651,8 @@ def claim_rewards(
:param memo: memo, defaults to None
:param gas_limit: gas limit, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None
:return: prepare and broadcast the transaction and transaction details
"""
tx = Transaction()
Expand All @@ -637,6 +665,8 @@ def claim_rewards(
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
fee_amount=fee_amount,
fee_granter=fee_granter,
)

def estimate_gas_for_tx(self, tx: Transaction) -> int:
Expand Down
113 changes: 92 additions & 21 deletions cosmpy/aerial/client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,93 @@
# ------------------------------------------------------------------------------
"""Helper functions."""
from datetime import timedelta
from typing import Any, Callable, List, Optional, Union
from typing import Any, Callable, List, Optional, Tuple, Union

from cosmpy.aerial.tx import SigningCfg
from cosmpy.aerial.coins import parse_coins
from cosmpy.aerial.tx import SigningCfg, Transaction
from cosmpy.aerial.tx_helpers import SubmittedTx
from cosmpy.crypto.address import Address
from cosmpy.protos.cosmos.base.query.v1beta1.pagination_pb2 import PageRequest
from cosmpy.protos.cosmos.tx.v1beta1.tx_pb2 import Fee


def simulate_tx(
client: "LedgerClient", # type: ignore # noqa: F821
tx: Transaction,
sender: "Wallet", # type: ignore # noqa: F821
account: "Account", # type: ignore # noqa: F821
memo: Optional[str] = None,
) -> Tuple[int, str]:
"""Estimate transaction fees based on either a provided amount, gas limit, or simulation.

:param client: Ledger client
:param tx: The transaction
:param sender: The transaction sender
:param account: The account
:param memo: Transaction memo, defaults to None

:return: Estimated gas_limit and fee amount tuple
"""
# we need to build up a representative transaction so that we can accurately simulate it
tx.seal(
SigningCfg.direct(sender.public_key(), account.sequence),
fee="",
gas_limit=0,
memo=memo,
)
tx.sign(sender.signer(), client.network_config.chain_id, account.number)
tx.complete()

# simulate the gas and fee for the transaction
gas_limit, fee = client.estimate_gas_and_fee_for_tx(tx)

return gas_limit, fee


def estimate_tx_fees(
client: "LedgerClient", # type: ignore # noqa: F821
tx: Transaction,
sender: "Wallet", # type: ignore # noqa: F821
amount: Optional[str] = None,
gas_limit: Optional[int] = None,
granter: Optional[Address] = None,
account: Optional["Account"] = None, # type: ignore # noqa: F821

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question out of ignorance: what is the difference between account and sender? Thanks
I can see below that account can be set from sender

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Account carries sequence and account_id and needs to be queried from node, that's why I am avoiding to re-query it.

memo: Optional[str] = None,
) -> Tuple[Fee, Optional["Account"]]: # type: ignore # noqa: F821
"""Estimate transaction fees based on either a provided amount, gas limit, or simulation.

:param client: Ledger client
:param tx: The transaction
:param sender: The transaction sender
:param amount: Transaction fee amount, defaults to None
:param gas_limit: The gas limit
:param granter: Transaction fee granter, defaults to None
:param account: The account
:param memo: Transaction memo, defaults to None

:return: Fee object and queried account tuple
"""
if gas_limit is None:
# Ensure we have the account info
account = account or client.query_account(sender.address())

# Simulate transaction to get gas and amount
gas_limit, estimated_amount = simulate_tx(client, tx, sender, account, memo)

# Use estimated amount if not provided
amount = amount or estimated_amount

else:
# Estimate amount based on provided gas if not already set
amount = amount or client.estimate_fee_from_gas(gas_limit)

fee = Fee(
amount=parse_coins(amount),
gas_limit=gas_limit,
granter=granter,
)

return fee, account


def prepare_and_broadcast_basic_transaction(
Expand All @@ -33,6 +115,8 @@ def prepare_and_broadcast_basic_transaction(
gas_limit: Optional[int] = None,
memo: Optional[str] = None,
timeout_height: Optional[int] = None,
fee_amount: Optional[str] = None,
fee_granter: Optional[Address] = None,
) -> SubmittedTx:
"""Prepare and broadcast basic transaction.

Expand All @@ -43,35 +127,22 @@ def prepare_and_broadcast_basic_transaction(
:param gas_limit: The gas limit
:param memo: Transaction memo, defaults to None
:param timeout_height: timeout height, defaults to None
:param fee_amount: Transaction fee amount, defaults to None
:param fee_granter: Transaction fee granter, defaults to None

:return: broadcast transaction
"""
fee, account = estimate_tx_fees(
client, tx, sender, fee_amount, gas_limit, fee_granter, account, memo
)
# query the account information for the sender
if account is None:
account = client.query_account(sender.address())

if gas_limit is not None:
# simply build the fee from the provided gas limit
fee = client.estimate_fee_from_gas(gas_limit)
else:
# we need to build up a representative transaction so that we can accurately simulate it
tx.seal(
SigningCfg.direct(sender.public_key(), account.sequence),
fee="",
gas_limit=0,
memo=memo,
)
tx.sign(sender.signer(), client.network_config.chain_id, account.number)
tx.complete()

# simulate the gas and fee for the transaction
gas_limit, fee = client.estimate_gas_and_fee_for_tx(tx)

# finally, build the final transaction that will be executed with the correct gas and fee values
# Build the final transaction
tx.seal(
SigningCfg.direct(sender.public_key(), account.sequence),
fee=fee,
gas_limit=gas_limit,
memo=memo,
timeout_height=timeout_height,
)
Expand Down
Loading
Loading