Skip to content

Commit 0e93f2a

Browse files
committed
Added e2e tests
1 parent b3eba69 commit 0e93f2a

File tree

5 files changed

+144
-11
lines changed

5 files changed

+144
-11
lines changed

bittensor/core/async_subtensor.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3134,7 +3134,7 @@ async def get_total_subnets(
31343134
return getattr(result, "value", None)
31353135

31363136
async def get_transfer_fee(
3137-
self, wallet: "Wallet", dest: str, value: Balance
3137+
self, wallet: "Wallet", dest: str, value: Balance, keep_alive: bool = True
31383138
) -> Balance:
31393139
"""
31403140
Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This
@@ -3146,6 +3146,8 @@ async def get_transfer_fee(
31463146
dest: The ``SS58`` address of the destination account.
31473147
value: The amount of tokens to be transferred, specified as a Balance object, or in Tao (float) or Rao
31483148
(int) units.
3149+
keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential
3150+
deposit) or not.
31493151
31503152
Returns:
31513153
bittensor.utils.balance.Balance: The estimated transaction fee for the transfer, represented as a Balance
@@ -3155,12 +3157,25 @@ async def get_transfer_fee(
31553157
wallet has sufficient funds to cover both the transfer amount and the associated costs. This function provides
31563158
a crucial tool for managing financial operations within the Bittensor network.
31573159
"""
3158-
value = check_and_convert_to_balance(value)
3160+
call_params = {"dest": dest}
3161+
if value is None:
3162+
call_function = "transfer_all"
3163+
if keep_alive:
3164+
call_params["keep_alive"] = True
3165+
else:
3166+
call_params["keep_alive"] = False
3167+
else:
3168+
value = check_and_convert_to_balance(value)
3169+
call_params["value"] = value.rao
3170+
if keep_alive:
3171+
call_function = "transfer_keep_alive"
3172+
else:
3173+
call_function = "transfer_allow_death"
31593174

31603175
call = await self.substrate.compose_call(
31613176
call_module="Balances",
3162-
call_function="transfer_keep_alive",
3163-
call_params={"dest": dest, "value": value.rao},
3177+
call_function=call_function,
3178+
call_params=call_params,
31643179
)
31653180

31663181
try:

bittensor/core/extrinsics/asyncex/transfer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ async def transfer_extrinsic(
150150
)
151151

152152
fee = await subtensor.get_transfer_fee(
153-
wallet=wallet, dest=destination, value=amount
153+
wallet=wallet, dest=destination, value=amount, keep_alive=keep_alive
154154
)
155155

156156
if not keep_alive:

bittensor/core/extrinsics/transfer.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ def transfer_extrinsic(
147147
else:
148148
existential_deposit = subtensor.get_existential_deposit(block=block)
149149

150-
fee = subtensor.get_transfer_fee(wallet=wallet, dest=dest, value=amount)
150+
fee = subtensor.get_transfer_fee(
151+
wallet=wallet, dest=dest, value=amount, keep_alive=keep_alive
152+
)
151153

152154
# Check if we have enough balance.
153155
if transfer_all is True:

bittensor/core/subtensor.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,7 +2243,13 @@ def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]:
22432243
)
22442244
return getattr(result, "value", None)
22452245

2246-
def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balance:
2246+
def get_transfer_fee(
2247+
self,
2248+
wallet: "Wallet",
2249+
dest: str,
2250+
value: Optional[Balance],
2251+
keep_alive: bool = True,
2252+
) -> Balance:
22472253
"""
22482254
Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This
22492255
function simulates the transfer to estimate the associated cost, taking into account the current network
@@ -2254,6 +2260,8 @@ def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balan
22542260
dest (str): The ``SS58`` address of the destination account.
22552261
value (Union[bittensor.utils.balance.Balance, float, int]): The amount of tokens to be transferred,
22562262
specified as a Balance object, or in Tao (float) or Rao (int) units.
2263+
keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential
2264+
deposit) or not.
22572265
22582266
Returns:
22592267
bittensor.utils.balance.Balance: The estimated transaction fee for the transfer, represented as a Balance
@@ -2263,11 +2271,24 @@ def get_transfer_fee(self, wallet: "Wallet", dest: str, value: Balance) -> Balan
22632271
has sufficient funds to cover both the transfer amount and the associated costs. This function provides a
22642272
crucial tool for managing financial operations within the Bittensor network.
22652273
"""
2266-
value = check_and_convert_to_balance(value)
2274+
call_params = {"dest": dest}
2275+
if value is None:
2276+
call_function = "transfer_all"
2277+
if keep_alive:
2278+
call_params["keep_alive"] = True
2279+
else:
2280+
call_params["keep_alive"] = False
2281+
else:
2282+
value = check_and_convert_to_balance(value)
2283+
call_params["value"] = value.rao
2284+
if keep_alive:
2285+
call_function = "transfer_keep_alive"
2286+
else:
2287+
call_function = "transfer_allow_death"
22672288
call = self.substrate.compose_call(
22682289
call_module="Balances",
2269-
call_function="transfer_keep_alive",
2270-
call_params={"dest": dest, "value": value.rao},
2290+
call_function=call_function,
2291+
call_params=call_params,
22712292
)
22722293

22732294
try:

tests/e2e_tests/test_transfer.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
import typing
2+
3+
from bittensor_wallet import Wallet
4+
import pytest
5+
16
from bittensor.utils.balance import Balance
27
from bittensor import logging
38

9+
if typing.TYPE_CHECKING:
10+
from bittensor.core.subtensor_api import SubtensorApi
11+
412
logging.set_trace()
513

614

7-
def test_transfer(subtensor, alice_wallet):
15+
def test_transfer(subtensor: "SubtensorApi", alice_wallet):
816
"""
917
Test the transfer mechanism on the chain
1018
@@ -47,3 +55,90 @@ def test_transfer(subtensor, alice_wallet):
4755
)
4856

4957
print("✅ Passed test_transfer")
58+
59+
60+
def test_transfer_all(subtensor: "Subtensor", alice_wallet):
61+
# create two dummy accounts we can drain
62+
dummy_account_1 = Wallet(path="/tmp/bittensor-dummy-account-1")
63+
dummy_account_2 = Wallet(path="/tmp/bittensor-dummy-account-2")
64+
dummy_account_1.create_new_coldkey(use_password=False, overwrite=True)
65+
dummy_account_2.create_new_coldkey(use_password=False, overwrite=True)
66+
67+
# fund the first dummy account
68+
assert subtensor.transfer(
69+
alice_wallet,
70+
dest=dummy_account_1.coldkeypub.ss58_address,
71+
amount=Balance.from_tao(2.0),
72+
wait_for_finalization=True,
73+
wait_for_inclusion=True,
74+
)
75+
# Account details before transfer
76+
existential_deposit = subtensor.get_existential_deposit()
77+
assert subtensor.transfer(
78+
wallet=dummy_account_1,
79+
dest=dummy_account_2.coldkeypub.ss58_address,
80+
amount=None,
81+
transfer_all=True,
82+
wait_for_finalization=True,
83+
wait_for_inclusion=True,
84+
keep_alive=True,
85+
)
86+
balance_after = subtensor.get_balance(dummy_account_1.coldkeypub.ss58_address)
87+
assert balance_after == existential_deposit
88+
assert subtensor.transfer(
89+
wallet=dummy_account_2,
90+
dest=alice_wallet.coldkeypub.ss58_address,
91+
amount=None,
92+
transfer_all=True,
93+
wait_for_inclusion=True,
94+
wait_for_finalization=True,
95+
keep_alive=False,
96+
)
97+
balance_after = subtensor.get_balance(dummy_account_2.coldkeypub.ss58_address)
98+
assert balance_after == Balance(0)
99+
100+
101+
@pytest.mark.asyncio
102+
async def test_async_transfer(async_subtensor: "SubtensorApi", alice_wallet):
103+
# create two dummy accounts we can drain
104+
dummy_account_1 = Wallet(path="/tmp/bittensor-dummy-account-3")
105+
dummy_account_2 = Wallet(path="/tmp/bittensor-dummy-account-4")
106+
dummy_account_1.create_new_coldkey(use_password=False, overwrite=True)
107+
dummy_account_2.create_new_coldkey(use_password=False, overwrite=True)
108+
109+
# fund the first dummy account
110+
assert await async_subtensor.transfer(
111+
alice_wallet,
112+
dest=dummy_account_1.coldkeypub.ss58_address,
113+
amount=Balance.from_tao(2.0),
114+
wait_for_finalization=True,
115+
wait_for_inclusion=True,
116+
)
117+
# Account details before transfer
118+
existential_deposit = await async_subtensor.get_existential_deposit()
119+
assert await async_subtensor.transfer(
120+
wallet=dummy_account_1,
121+
dest=dummy_account_2.coldkeypub.ss58_address,
122+
amount=None,
123+
transfer_all=True,
124+
wait_for_finalization=True,
125+
wait_for_inclusion=True,
126+
keep_alive=True,
127+
)
128+
balance_after = await async_subtensor.get_balance(
129+
dummy_account_1.coldkeypub.ss58_address
130+
)
131+
assert balance_after == existential_deposit
132+
assert await async_subtensor.transfer(
133+
wallet=dummy_account_2,
134+
dest=alice_wallet.coldkeypub.ss58_address,
135+
amount=None,
136+
transfer_all=True,
137+
wait_for_inclusion=True,
138+
wait_for_finalization=True,
139+
keep_alive=False,
140+
)
141+
balance_after = await async_subtensor.get_balance(
142+
dummy_account_2.coldkeypub.ss58_address
143+
)
144+
assert balance_after == Balance(0)

0 commit comments

Comments
 (0)