Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send Native token in MultiESDTNFTTransfer #73

Merged
merged 6 commits into from
Jul 3, 2024
Merged
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
2 changes: 2 additions & 0 deletions multiversx_sdk/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@
MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS = 2
SDK_PY_SIGNER = "sdk-py"
UNKNOWN_SIGNER = "unknown"

EGLD_IDENTIFIER_FOR_MULTI_ESDTNFT_TRANSFER = "EGLD-000000"
8 changes: 7 additions & 1 deletion multiversx_sdk/core/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from multiversx_sdk.core.codec import (decode_unsigned_number,
encode_unsigned_number)
from multiversx_sdk.core.constants import TOKEN_RANDOM_SEQUENCE_LENGTH
from multiversx_sdk.core.constants import (
EGLD_IDENTIFIER_FOR_MULTI_ESDTNFT_TRANSFER, TOKEN_RANDOM_SEQUENCE_LENGTH)
from multiversx_sdk.core.errors import (BadUsageError,
InvalidTokenIdentifierError)
from multiversx_sdk.core.interfaces import IToken, ITokenIdentifierParts
Expand All @@ -20,6 +21,11 @@ def __init__(self, token: IToken, amount: int) -> None:
self.token = token
self.amount = amount

@staticmethod
def new_from_native_amount(amount: int) -> "TokenTransfer":
native_token = Token(EGLD_IDENTIFIER_FOR_MULTI_ESDTNFT_TRANSFER)
return TokenTransfer(native_token, amount)


class TokenIdentifierParts:
def __init__(self, ticker: str, random_sequence: str, nonce: int) -> None:
Expand Down
10 changes: 9 additions & 1 deletion multiversx_sdk/core/tokens_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from multiversx_sdk.core.errors import BadUsageError
from multiversx_sdk.core.tokens import (Token, TokenComputer,
TokenIdentifierParts)
TokenIdentifierParts, TokenTransfer)


class TestTokenComputer:
Expand Down Expand Up @@ -86,3 +86,11 @@ def test_compute_extended_identifier_from_parts(self):

assert fungible_identifier == "FNG-123456"
assert nft_identifier == "NFT-987654-0a"


def test_token_transfer_from_native_amount():
transfer = TokenTransfer.new_from_native_amount(1000000000000000000)

assert transfer.token.identifier == "EGLD-000000"
assert transfer.token.nonce == 0
assert transfer.amount == 1000000000000000000
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,35 @@ def test_create_transaction_for_execute_and_send_multiple_nfts(self):
assert transaction.data.decode() == "MultiESDTNFTTransfer@00000000000000000500b9353fe8407f87310c87e12fa1ac807f0485da39d152@02@4e46542d313233343536@01@01@4e46542d313233343536@2a@01@64756d6d79@07"
assert transaction.value == 0

def test_create_transaction_for_execute_and_send_native_and_nfts(self):
sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
contract = Address.new_from_bech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4")
function = "dummy"
gas_limit = 6000000
args = [7]

first_token = Token("NFT-123456", 1)
first_transfer = TokenTransfer(first_token, 1)
second_token = Token("NFT-123456", 42)
second_transfer = TokenTransfer(second_token, 1)

transaction = self.factory.create_transaction_for_execute(
sender=sender,
contract=contract,
function=function,
gas_limit=gas_limit,
arguments=args,
native_transfer_amount=1000000000000000000,
token_transfers=[first_transfer, second_transfer]
)

assert transaction.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
assert transaction.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
assert transaction.gas_limit == gas_limit
assert transaction.data
assert transaction.data.decode() == "MultiESDTNFTTransfer@00000000000000000500b9353fe8407f87310c87e12fa1ac807f0485da39d152@03@4e46542d313233343536@01@01@4e46542d313233343536@2a@01@45474c442d303030303030@@0de0b6b3a7640000@64756d6d79@07"
assert transaction.value == 0

def test_create_transaction_for_upgrade(self):
sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
contract_address = Address.new_from_bech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
from multiversx_sdk.core.constants import (ARGS_SEPARATOR,
CONTRACT_DEPLOY_ADDRESS,
VM_TYPE_WASM_VM)
from multiversx_sdk.core.errors import BadUsageError
from multiversx_sdk.core.interfaces import IAddress, ITokenTransfer
from multiversx_sdk.core.serializer import arg_to_string, args_to_buffers
from multiversx_sdk.core.tokens import TokenComputer
from multiversx_sdk.core.tokens import TokenComputer, TokenTransfer
from multiversx_sdk.core.transaction import Transaction
from multiversx_sdk.core.transactions_factories.token_transfers_data_builder import \
TokenTransfersDataBuilder
Expand Down Expand Up @@ -92,11 +91,15 @@ def create_transaction_for_execute(self,
receiver = contract

if native_transfer_amount and number_of_tokens:
raise BadUsageError("Can't send both native token and custom tokens(ESDT/NFT)")
native_tranfer = TokenTransfer.new_from_native_amount(native_transfer_amount)
token_transfers = list(token_transfers) + [native_tranfer]

native_transfer_amount = 0
number_of_tokens += 1

data_parts: List[str] = []

if len(token_transfers) == 1:
if number_of_tokens == 1:
Copy link
Contributor

Choose a reason for hiding this comment

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

If we move number_of_tokens = len(transfers) above this line, then maybe we don't need number_of_tokens += 1 anymore.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

indeed, but I was using it in the above if statement, as well.

transfer = token_transfers[0]

if self.token_computer.is_fungible(transfer.token):
Expand All @@ -105,7 +108,7 @@ def create_transaction_for_execute(self,
data_parts = self._data_args_builder.build_args_for_single_esdt_nft_transfer(
transfer=transfer, receiver=receiver)
receiver = sender
elif len(token_transfers) > 1:
elif number_of_tokens > 1:
data_parts = self._data_args_builder.build_args_for_multi_esdt_nft_transfer(
receiver=receiver, transfers=token_transfers)
receiver = sender
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from multiversx_sdk.core.errors import BadUsageError
from multiversx_sdk.core.interfaces import IAddress, ITokenTransfer
from multiversx_sdk.core.tokens import TokenComputer
from multiversx_sdk.core.tokens import TokenComputer, TokenTransfer
from multiversx_sdk.core.transaction import Transaction
from multiversx_sdk.core.transactions_factories.token_transfers_data_builder import \
TokenTransfersDataBuilder
Expand Down Expand Up @@ -76,3 +76,34 @@ def create_transaction_for_esdt_token_transfer(self,
gas_limit=extra_gas_for_transfer,
add_data_movement_gas=True
).build()

def create_transaction_for_transfer(self,
sender: IAddress,
receiver: IAddress,
native_amount: Optional[int] = None,
token_transfers: Optional[Sequence[ITokenTransfer]] = None,
data: Optional[bytes] = None) -> Transaction:
if not native_amount and not token_transfers:
raise BadUsageError("No native token amount or token transfers provided")

if token_transfers and data:
raise BadUsageError("Can't set data field when sending esdt tokens")

if native_amount and not token_transfers:
Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed, we handle this as a simple move balance.

return self.create_transaction_for_native_token_transfer(
sender=sender,
receiver=receiver,
native_amount=native_amount,
data=data.decode() if data else None
)

token_transfers = list(token_transfers) if token_transfers else []
Copy link
Contributor

Choose a reason for hiding this comment

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

The optional refactoring idea from above, if applicable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can do it, but I am not sure it's what we want.


native_transfer = TokenTransfer.new_from_native_amount(native_amount) if native_amount else None
token_transfers.append(native_transfer) if native_transfer else None

return self.create_transaction_for_esdt_token_transfer(
sender=sender,
receiver=receiver,
token_transfers=token_transfers
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import pytest

from multiversx_sdk.core.address import Address
from multiversx_sdk.core.errors import BadUsageError
from multiversx_sdk.core.tokens import Token, TokenTransfer
from multiversx_sdk.core.transactions_factories.transactions_factory_config import \
TransactionsFactoryConfig
Expand All @@ -8,14 +11,13 @@

class TestTransferTransactionsFactory:
transfer_factory = TransferTransactionsFactory(TransactionsFactoryConfig("D"))
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

def test_create_transaction_for_native_token_transfer_no_data(self):
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

transaction = self.transfer_factory.create_transaction_for_native_token_transfer(
sender=alice,
receiver=bob,
sender=self.alice,
receiver=self.bob,
native_amount=1000000000000000000
)

Expand All @@ -27,12 +29,9 @@ def test_create_transaction_for_native_token_transfer_no_data(self):
assert transaction.data == b""

def test_create_transaction_for_native_token_transfer_with_data(self):
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

transaction = self.transfer_factory.create_transaction_for_native_token_transfer(
sender=alice,
receiver=bob,
sender=self.alice,
receiver=self.bob,
native_amount=1000000000000000000,
data="test data"
)
Expand All @@ -45,15 +44,12 @@ def test_create_transaction_for_native_token_transfer_with_data(self):
assert transaction.data == b"test data"

def test_create_transaction_for_esdt_transfer(self):
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

foo_token = Token("FOO-123456")
token_transfer = TokenTransfer(foo_token, 1000000)

transaction = self.transfer_factory.create_transaction_for_esdt_token_transfer(
sender=alice,
receiver=bob,
sender=self.alice,
receiver=self.bob,
token_transfers=[token_transfer]
)

Expand All @@ -65,15 +61,12 @@ def test_create_transaction_for_esdt_transfer(self):
assert transaction.gas_limit == 410_000

def test_create_transaction_for_nft_transfer(self):
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

nft = Token("NFT-123456", 10)
token_transfer = TokenTransfer(nft, 1)

transaction = self.transfer_factory.create_transaction_for_esdt_token_transfer(
sender=alice,
receiver=bob,
sender=self.alice,
receiver=self.bob,
token_transfers=[token_transfer]
)

Expand All @@ -85,18 +78,15 @@ def test_create_transaction_for_nft_transfer(self):
assert transaction.gas_limit == 1_210_500

def test_create_transaction_for_multiple_nft_transfers(self):
alice = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")
bob = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx")

first_nft = Token("NFT-123456", 10)
first_transfer = TokenTransfer(first_nft, 1)

second_nft = Token("TEST-987654", 1)
second_transfer = TokenTransfer(second_nft, 1)

transaction = self.transfer_factory.create_transaction_for_esdt_token_transfer(
sender=alice,
receiver=bob,
sender=self.alice,
receiver=self.bob,
token_transfers=[first_transfer, second_transfer]
)

Expand All @@ -106,3 +96,42 @@ def test_create_transaction_for_multiple_nft_transfers(self):
assert transaction.chain_id == "D"
assert transaction.data.decode() == "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@02@4e46542d313233343536@0a@01@544553542d393837363534@01@01"
assert transaction.gas_limit == 1_466_000

def test_create_transaction_for_token_transfer_with_errors(self):
with pytest.raises(BadUsageError, match="No native token amount or token transfers provided"):
self.transfer_factory.create_transaction_for_transfer(
sender=self.alice,
receiver=self.bob
)

with pytest.raises(BadUsageError, match="Can't set data field when sending esdt tokens"):
nft = Token("NFT-123456", 10)
transfer = TokenTransfer(nft, 1)

self.transfer_factory.create_transaction_for_transfer(
sender=self.alice,
receiver=self.bob,
token_transfers=[transfer],
data="hello".encode()
)

def test_create_transaction_for_token_transfer(self):
first_nft = Token("NFT-123456", 10)
first_transfer = TokenTransfer(first_nft, 1)

second_nft = Token("TEST-987654", 1)
second_transfer = TokenTransfer(second_nft, 1)

transaction = self.transfer_factory.create_transaction_for_transfer(
sender=self.alice,
receiver=self.bob,
native_amount=1000000000000000000,
token_transfers=[first_transfer, second_transfer]
)

assert transaction.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
assert transaction.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
assert transaction.value == 0
assert transaction.chain_id == "D"
assert transaction.data.decode() == "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@4e46542d313233343536@0a@01@544553542d393837363534@01@01@45474c442d303030303030@@0de0b6b3a7640000"
assert transaction.gas_limit == 1_727_500
Loading