Skip to content

Commit e0f449f

Browse files
committed
Fix and add tests for change token supply in the wallet
1 parent 2a001bf commit e0f449f

File tree

11 files changed

+494
-83
lines changed

11 files changed

+494
-83
lines changed

test/functional/test_framework/wallet_cli_controller.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,23 @@ async def issue_new_token(self,
178178
amount_to_issue: str,
179179
number_of_decimals: int,
180180
metadata_uri: str,
181-
destination_address: str) -> Tuple[Optional[str], Optional[str]]:
182-
output = await self._write_command(f'issuenewtoken "{token_ticker}" "{amount_to_issue}" "{number_of_decimals}" "{metadata_uri}" {destination_address}\n')
181+
destination_address: str,
182+
token_supply: str = 'unlimited') -> Tuple[Optional[str], Optional[str]]:
183+
output = await self._write_command(f'issuenewtoken "{token_ticker}" "{amount_to_issue}" "{number_of_decimals}" "{metadata_uri}" {destination_address} {token_supply}\n')
183184
if output.startswith("A new token has been issued with ID"):
184185
return output[output.find(':')+2:], None
185186

186187
return None, output
187188

189+
async def mint_tokens(self, token_id: str, address: str, amount: int) -> str:
190+
return await self._write_command(f"minttokens {token_id} {address} {amount}\n")
191+
192+
async def redeem_tokens(self, token_id: str, amount: int) -> str:
193+
return await self._write_command(f"redeemtokens {token_id} {amount}\n")
194+
195+
async def lock_tokens(self, token_id: str) -> str:
196+
return await self._write_command(f"locktokens {token_id}\n")
197+
188198
async def issue_new_nft(self,
189199
destination_address: str,
190200
media_hash: str,

test/functional/test_runner.py

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class UnicodeOnWindowsError(ValueError):
131131
'wallet_recover_accounts.py',
132132
'wallet_get_address_usage.py',
133133
'wallet_tokens.py',
134+
'wallet_tokens_change_supply.py',
134135
'wallet_nfts.py',
135136
'wallet_delegations.py',
136137
'wallet_high_fee.py',

test/functional/wallet_tokens.py

+22-20
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from test_framework.mintlayer import (make_tx, reward_input, tx_input, ATOMS_PER_COIN)
3232
from test_framework.util import assert_in, assert_equal
3333
from test_framework.mintlayer import mintlayer_hash, block_input_data_obj
34-
from test_framework.wallet_cli_controller import WalletCliController
34+
from test_framework.wallet_cli_controller import DEFAULT_ACCOUNT_INDEX, WalletCliController
3535

3636
import asyncio
3737
import sys
@@ -90,7 +90,7 @@ async def async_test(self):
9090

9191
# Submit a valid transaction
9292
output = {
93-
'Transfer': [ { 'Coin': 101 * ATOMS_PER_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ],
93+
'Transfer': [ { 'Coin': 201 * ATOMS_PER_COIN }, { 'PublicKey': {'key': {'Secp256k1Schnorr' : {'pubkey_data': pub_key_bytes}}} } ],
9494
}
9595
encoded_tx, tx_id = make_tx([reward_input(tip_id)], [output], 0)
9696

@@ -107,7 +107,7 @@ async def async_test(self):
107107
assert_equal(await wallet.get_best_block_height(), '1')
108108
assert_equal(await wallet.get_best_block(), block_id)
109109

110-
assert_in("Coins amount: 101", await wallet.get_balance())
110+
assert_in("Coins amount: 201", await wallet.get_balance())
111111

112112
address = await wallet.new_address()
113113

@@ -144,31 +144,33 @@ async def async_test(self):
144144
self.generate_block()
145145
assert_in("Success", await wallet.sync())
146146

147-
# TODO: add support for tokens v1
148-
# Hint with tokens v1 they have to be minted before any could be sent
149-
# See https://github.com/mintlayer/mintlayer-core/issues/1237
150-
#assert_in(f"{token_id} amount: 10000", await wallet.get_balance())
147+
assert_in("The transaction was submitted successfully", await wallet.mint_tokens(token_id, address, 10000))
148+
149+
self.generate_block()
150+
assert_in("Success", await wallet.sync())
151+
152+
assert_in(f"{token_id} amount: 10000", await wallet.get_balance())
151153

152154
## create a new account and send some tokens to it
153-
#await wallet.create_new_account()
154-
#await wallet.select_account(1)
155-
#address = await wallet.new_address()
155+
await wallet.create_new_account()
156+
await wallet.select_account(1)
157+
address = await wallet.new_address()
156158

157-
#await wallet.select_account(0)
158-
#output = await wallet.send_tokens_to_address(token_id, address, 10.01)
159-
#assert_in("The transaction was submitted successfully", output)
159+
await wallet.select_account(DEFAULT_ACCOUNT_INDEX)
160+
output = await wallet.send_tokens_to_address(token_id, address, 10.01)
161+
assert_in("The transaction was submitted successfully", output)
160162

161-
#self.generate_block()
162-
#assert_in("Success", await wallet.sync())
163+
self.generate_block()
164+
assert_in("Success", await wallet.sync())
163165

164166
## check the new balance
165-
#assert_in(f"{token_id} amount: 9989.99", await wallet.get_balance())
167+
assert_in(f"{token_id} amount: 9989.99", await wallet.get_balance())
166168

167169
## try to issue a new token, should fail with not enough coins
168-
#token_id, err = await wallet.issue_new_token("XXX", "10000", 2, "http://uri", address)
169-
#assert token_id is None
170-
#assert err is not None
171-
#assert_in("Not enough funds", err)
170+
token_id, err = await wallet.issue_new_token("XXX", "10000", 2, "http://uri", address)
171+
assert token_id is None
172+
assert err is not None
173+
assert_in("Not enough funds", err)
172174

173175
if __name__ == '__main__':
174176
WalletTokens().main()

wallet/src/account/mod.rs

+64-37
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ impl Account {
261261

262262
total_fees_not_paid =
263263
(total_fees_not_paid + preselected_fee).ok_or(WalletError::OutputAmountOverflow)?;
264+
total_fees_not_paid = preselected_inputs
265+
.values()
266+
.try_fold(total_fees_not_paid, |total, (_amount, fee)| total + *fee)
267+
.ok_or(WalletError::OutputAmountOverflow)?;
268+
264269
let mut amount_to_be_paid_in_currency_with_fees = (amount_to_be_paid_in_currency_with_fees
265270
+ total_fees_not_paid)
266271
.ok_or(WalletError::OutputAmountOverflow)?;
@@ -720,6 +725,8 @@ impl Account {
720725
let outputs =
721726
make_mint_token_outputs(token_id, amount, address, self.chain_config.as_ref())?;
722727

728+
self.find_token(&token_id)?.total_supply.check_can_mint(amount)?;
729+
723730
self.change_token_supply_transaction(
724731
token_id,
725732
amount,
@@ -730,42 +737,6 @@ impl Account {
730737
)
731738
}
732739

733-
fn change_token_supply_transaction(
734-
&mut self,
735-
token_id: TokenId,
736-
amount: Amount,
737-
outputs: Vec<TxOutput>,
738-
db_tx: &mut impl WalletStorageWriteUnlocked,
739-
median_time: BlockTimestamp,
740-
fee_rate: CurrentFeeRate,
741-
) -> Result<SignedTransaction, WalletError> {
742-
let token_data = self.find_token(&token_id)?;
743-
let nonce = token_data
744-
.last_nonce
745-
.map_or(Some(AccountNonce::new(0)), |nonce| nonce.increment())
746-
.ok_or(WalletError::TokenIssuanceNonceOverflow(token_id))?;
747-
let tx_input = TxInput::Account(AccountOutPoint::new(
748-
nonce,
749-
AccountSpending::TokenSupply(token_id, amount),
750-
));
751-
752-
let request = SendRequest::new()
753-
.with_outputs(outputs)
754-
.with_inputs_and_destinations([(tx_input, token_data.reissuance_controller.clone())]);
755-
756-
let request = self.select_inputs_for_send_request(
757-
request,
758-
vec![],
759-
db_tx,
760-
median_time,
761-
fee_rate.current_fee_rate,
762-
fee_rate.consolidate_fee_rate,
763-
)?;
764-
765-
let tx = self.sign_transaction_from_req(request, db_tx)?;
766-
Ok(tx)
767-
}
768-
769740
pub fn redeem_tokens(
770741
&mut self,
771742
db_tx: &mut impl WalletStorageWriteUnlocked,
@@ -776,6 +747,8 @@ impl Account {
776747
) -> WalletResult<SignedTransaction> {
777748
let outputs = make_redeem_token_outputs(token_id, amount, self.chain_config.as_ref())?;
778749

750+
self.find_token(&token_id)?.total_supply.check_can_redeem(amount)?;
751+
779752
self.change_token_supply_transaction(
780753
token_id,
781754
amount,
@@ -795,6 +768,8 @@ impl Account {
795768
) -> WalletResult<SignedTransaction> {
796769
let outputs = make_lock_token_outputs(token_id, self.chain_config.as_ref())?;
797770

771+
self.find_token(&token_id)?.total_supply.check_can_lock()?;
772+
798773
self.change_token_supply_transaction(
799774
token_id,
800775
Amount::ZERO,
@@ -805,6 +780,43 @@ impl Account {
805780
)
806781
}
807782

783+
fn change_token_supply_transaction(
784+
&mut self,
785+
token_id: TokenId,
786+
amount: Amount,
787+
outputs: Vec<TxOutput>,
788+
db_tx: &mut impl WalletStorageWriteUnlocked,
789+
median_time: BlockTimestamp,
790+
fee_rate: CurrentFeeRate,
791+
) -> Result<SignedTransaction, WalletError> {
792+
let token_data = self.find_token(&token_id)?;
793+
794+
let nonce = token_data
795+
.last_nonce
796+
.map_or(Some(AccountNonce::new(0)), |nonce| nonce.increment())
797+
.ok_or(WalletError::TokenIssuanceNonceOverflow(token_id))?;
798+
let tx_input = TxInput::Account(AccountOutPoint::new(
799+
nonce,
800+
AccountSpending::TokenSupply(token_id, amount),
801+
));
802+
803+
let request = SendRequest::new()
804+
.with_outputs(outputs)
805+
.with_inputs_and_destinations([(tx_input, token_data.reissuance_controller.clone())]);
806+
807+
let request = self.select_inputs_for_send_request(
808+
request,
809+
vec![],
810+
db_tx,
811+
median_time,
812+
fee_rate.current_fee_rate,
813+
fee_rate.consolidate_fee_rate,
814+
)?;
815+
816+
let tx = self.sign_transaction_from_req(request, db_tx)?;
817+
Ok(tx)
818+
}
819+
808820
pub fn get_pos_gen_block_data(
809821
&self,
810822
db_tx: &impl WalletStorageReadUnlocked,
@@ -1364,6 +1376,10 @@ impl Account {
13641376
}
13651377
}
13661378

1379+
/// There are some preselected inputs like the Token account inputs with a nonce
1380+
/// that need to be included in the request
1381+
/// Here we group them up by currency and sum the total amount and fee they bring to the
1382+
/// transaction
13671383
fn group_preselected_inputs(
13681384
request: &SendRequest,
13691385
current_fee_rate: FeeRate,
@@ -1433,7 +1449,18 @@ fn group_outputs<T, Grouped: Clone>(
14331449
}
14341450
TxOutput::CreateStakePool(_, stake) => OutputValue::Coin(stake.value()),
14351451
TxOutput::DelegateStaking(amount, _) => OutputValue::Coin(*amount),
1436-
TxOutput::TokensOp(_) | TxOutput::CreateDelegationId(_, _) => continue,
1452+
TxOutput::TokensOp(token_output) => match token_output {
1453+
TokenOutput::MintTokens(token_id, amount, _) => {
1454+
OutputValue::TokenV1(*token_id, *amount)
1455+
}
1456+
TokenOutput::RedeemTokens(token_id, amount) => {
1457+
OutputValue::TokenV1(*token_id, *amount)
1458+
}
1459+
TokenOutput::IssueFungibleToken(_)
1460+
| TokenOutput::IssueNft(_, _, _)
1461+
| TokenOutput::LockCirculatingSupply(_) => continue,
1462+
},
1463+
TxOutput::CreateDelegationId(_, _) => continue,
14371464
TxOutput::ProduceBlockFromStake(_, _) => {
14381465
return Err(WalletError::UnsupportedTransactionOutput(Box::new(
14391466
get_tx_output(&output).clone(),

0 commit comments

Comments
 (0)