Skip to content

Commit 7a54a24

Browse files
authored
[migration] optimize for CoinStore<String> (aptos-labs#16660)
1 parent 8454749 commit 7a54a24

File tree

2 files changed

+99
-57
lines changed

2 files changed

+99
-57
lines changed

aptos-move/framework/aptos-framework/doc/coin.md

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,15 @@ Not enough coins to complete transaction
10551055

10561056

10571057

1058+
<a id="0x1_coin_MAX_DECIMALS"></a>
1059+
1060+
1061+
1062+
<pre><code><b>const</b> <a href="coin.md#0x1_coin_MAX_DECIMALS">MAX_DECIMALS</a>: u8 = 32;
1063+
</code></pre>
1064+
1065+
1066+
10581067
<a id="0x1_coin_EAGGREGATABLE_COIN_VALUE_TOO_LARGE"></a>
10591068

10601069
The value of aggregatable coin used for transaction fees redistribution does not fit in u64.
@@ -1105,6 +1114,16 @@ The coin converison map is not created yet.
11051114

11061115

11071116

1117+
<a id="0x1_coin_ECOIN_DECIMALS_TOO_LARGE"></a>
1118+
1119+
The decimals of the coin is too large.
1120+
1121+
1122+
<pre><code><b>const</b> <a href="coin.md#0x1_coin_ECOIN_DECIMALS_TOO_LARGE">ECOIN_DECIMALS_TOO_LARGE</a>: u64 = 29;
1123+
</code></pre>
1124+
1125+
1126+
11081127
<a id="0x1_coin_ECOIN_INFO_ADDRESS_MISMATCH"></a>
11091128

11101129
Address of account which is used to initialize a coin <code>CoinType</code> doesn't match the deployer of module
@@ -2092,39 +2111,44 @@ or disallow upgradability of total supply.
20922111
<b>if</b> (!<a href="../../aptos-stdlib/../move-stdlib/doc/features.md#0x1_features_coin_to_fungible_asset_migration_feature_enabled">features::coin_to_fungible_asset_migration_feature_enabled</a>()) {
20932112
<b>abort</b> <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_unavailable">error::unavailable</a>(<a href="coin.md#0x1_coin_ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED">ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED</a>)
20942113
};
2095-
<b>assert</b>!(<a href="coin.md#0x1_coin_is_coin_initialized">is_coin_initialized</a>&lt;CoinType&gt;(), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="coin.md#0x1_coin_ECOIN_INFO_NOT_PUBLISHED">ECOIN_INFO_NOT_PUBLISHED</a>));
2096-
2097-
<b>let</b> metadata = <a href="coin.md#0x1_coin_ensure_paired_metadata">ensure_paired_metadata</a>&lt;CoinType&gt;();
2098-
<b>let</b> store = <a href="primary_fungible_store.md#0x1_primary_fungible_store_ensure_primary_store_exists">primary_fungible_store::ensure_primary_store_exists</a>(<a href="account.md#0x1_account">account</a>, metadata);
20992114
<b>if</b> (<b>exists</b>&lt;<a href="coin.md#0x1_coin_CoinStore">CoinStore</a>&lt;CoinType&gt;&gt;(<a href="account.md#0x1_account">account</a>)) {
2100-
<b>let</b> <a href="coin.md#0x1_coin_CoinStore">CoinStore</a>&lt;CoinType&gt; { <a href="coin.md#0x1_coin">coin</a>, frozen, deposit_events, withdraw_events } = <b>move_from</b>&lt;<a href="coin.md#0x1_coin_CoinStore">CoinStore</a>&lt;CoinType&gt;&gt;(
2101-
<a href="account.md#0x1_account">account</a>
2102-
);
2103-
<a href="event.md#0x1_event_emit">event::emit</a>(
2104-
<a href="coin.md#0x1_coin_CoinStoreDeletion">CoinStoreDeletion</a> {
2115+
<b>let</b> <a href="coin.md#0x1_coin_CoinStore">CoinStore</a>&lt;CoinType&gt; { <a href="coin.md#0x1_coin">coin</a>, frozen, deposit_events, withdraw_events } =
2116+
<b>move_from</b>&lt;<a href="coin.md#0x1_coin_CoinStore">CoinStore</a>&lt;CoinType&gt;&gt;(<a href="account.md#0x1_account">account</a>);
2117+
<b>if</b> (<a href="coin.md#0x1_coin_is_coin_initialized">is_coin_initialized</a>&lt;CoinType&gt;()) {
2118+
<b>let</b> metadata = <a href="coin.md#0x1_coin_ensure_paired_metadata">ensure_paired_metadata</a>&lt;CoinType&gt;();
2119+
<b>let</b> store = <a href="primary_fungible_store.md#0x1_primary_fungible_store_ensure_primary_store_exists">primary_fungible_store::ensure_primary_store_exists</a>(<a href="account.md#0x1_account">account</a>, metadata);
2120+
2121+
<a href="event.md#0x1_event_emit">event::emit</a>(<a href="coin.md#0x1_coin_CoinStoreDeletion">CoinStoreDeletion</a> {
21052122
coin_type: <a href="../../aptos-stdlib/doc/type_info.md#0x1_type_info_type_name">type_info::type_name</a>&lt;CoinType&gt;(),
21062123
event_handle_creation_address: <a href="guid.md#0x1_guid_creator_address">guid::creator_address</a>(
21072124
<a href="event.md#0x1_event_guid">event::guid</a>(&deposit_events)
21082125
),
21092126
deleted_deposit_event_handle_creation_number: <a href="guid.md#0x1_guid_creation_num">guid::creation_num</a>(<a href="event.md#0x1_event_guid">event::guid</a>(&deposit_events)),
21102127
deleted_withdraw_event_handle_creation_number: <a href="guid.md#0x1_guid_creation_num">guid::creation_num</a>(<a href="event.md#0x1_event_guid">event::guid</a>(&withdraw_events))
2128+
});
2129+
2130+
<b>if</b> (<a href="coin.md#0x1_coin">coin</a>.value == 0) {
2131+
<a href="coin.md#0x1_coin_destroy_zero">destroy_zero</a>(<a href="coin.md#0x1_coin">coin</a>);
2132+
} <b>else</b> {
2133+
<a href="fungible_asset.md#0x1_fungible_asset_unchecked_deposit_with_no_events">fungible_asset::unchecked_deposit_with_no_events</a>(
2134+
object_address(&store),
2135+
<a href="coin.md#0x1_coin_coin_to_fungible_asset">coin_to_fungible_asset</a>(<a href="coin.md#0x1_coin">coin</a>)
2136+
);
2137+
};
2138+
2139+
// Note:
2140+
// It is possible the primary fungible store may already exist before this function call.
2141+
// In this case, <b>if</b> the <a href="account.md#0x1_account">account</a> owns a frozen <a href="coin.md#0x1_coin_CoinStore">CoinStore</a> and an unfrozen primary fungible store, this
2142+
// function would convert and deposit the rest <a href="coin.md#0x1_coin">coin</a> into the primary store and <b>freeze</b> it <b>to</b> make the
2143+
// `frozen` semantic <b>as</b> consistent <b>as</b> possible.
2144+
<b>if</b> (frozen != <a href="fungible_asset.md#0x1_fungible_asset_is_frozen">fungible_asset::is_frozen</a>(store)) {
2145+
<a href="fungible_asset.md#0x1_fungible_asset_set_frozen_flag_internal">fungible_asset::set_frozen_flag_internal</a>(store, frozen);
21112146
}
2112-
);
2113-
<b>if</b> (<a href="coin.md#0x1_coin">coin</a>.value == 0) {
2114-
<a href="coin.md#0x1_coin_destroy_zero">destroy_zero</a>(<a href="coin.md#0x1_coin">coin</a>);
21152147
} <b>else</b> {
2116-
<a href="fungible_asset.md#0x1_fungible_asset_unchecked_deposit_with_no_events">fungible_asset::unchecked_deposit_with_no_events</a>(object_address(&store), <a href="coin.md#0x1_coin_coin_to_fungible_asset">coin_to_fungible_asset</a>(<a href="coin.md#0x1_coin">coin</a>));
2148+
<a href="coin.md#0x1_coin_destroy_zero">destroy_zero</a>(<a href="coin.md#0x1_coin">coin</a>);
21172149
};
21182150
<a href="event.md#0x1_event_destroy_handle">event::destroy_handle</a>(deposit_events);
21192151
<a href="event.md#0x1_event_destroy_handle">event::destroy_handle</a>(withdraw_events);
2120-
// Note:
2121-
// It is possible the primary fungible store may already exist before this function call.
2122-
// In this case, <b>if</b> the <a href="account.md#0x1_account">account</a> owns a frozen <a href="coin.md#0x1_coin_CoinStore">CoinStore</a> and an unfrozen primary fungible store, this
2123-
// function would convert and deposit the rest <a href="coin.md#0x1_coin">coin</a> into the primary store and <b>freeze</b> it <b>to</b> make the
2124-
// `frozen` semantic <b>as</b> consistent <b>as</b> possible.
2125-
<b>if</b> (frozen != <a href="fungible_asset.md#0x1_fungible_asset_is_frozen">fungible_asset::is_frozen</a>(store)) {
2126-
<a href="fungible_asset.md#0x1_fungible_asset_set_frozen_flag_internal">fungible_asset::set_frozen_flag_internal</a>(store, frozen);
2127-
}
21282152
};
21292153
}
21302154
</code></pre>
@@ -3165,6 +3189,7 @@ Same as <code>initialize</code> but supply can be initialized to parallelizable
31653189

31663190
<b>assert</b>!(<a href="../../aptos-stdlib/../move-stdlib/doc/string.md#0x1_string_length">string::length</a>(&name) &lt;= <a href="coin.md#0x1_coin_MAX_COIN_NAME_LENGTH">MAX_COIN_NAME_LENGTH</a>, <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="coin.md#0x1_coin_ECOIN_NAME_TOO_LONG">ECOIN_NAME_TOO_LONG</a>));
31673191
<b>assert</b>!(<a href="../../aptos-stdlib/../move-stdlib/doc/string.md#0x1_string_length">string::length</a>(&symbol) &lt;= <a href="coin.md#0x1_coin_MAX_COIN_SYMBOL_LENGTH">MAX_COIN_SYMBOL_LENGTH</a>, <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="coin.md#0x1_coin_ECOIN_SYMBOL_TOO_LONG">ECOIN_SYMBOL_TOO_LONG</a>));
3192+
<b>assert</b>!(<a href="coin.md#0x1_coin_decimals">decimals</a> &lt;= <a href="coin.md#0x1_coin_MAX_DECIMALS">MAX_DECIMALS</a>, <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="coin.md#0x1_coin_ECOIN_DECIMALS_TOO_LARGE">ECOIN_DECIMALS_TOO_LARGE</a>));
31683193

31693194
<b>let</b> coin_info = <a href="coin.md#0x1_coin_CoinInfo">CoinInfo</a>&lt;CoinType&gt; {
31703195
name,
@@ -4401,6 +4426,27 @@ The creator of <code>CoinType</code> must be <code>@aptos_framework</code>.
44014426
</code></pre>
44024427

44034428

4429+
Make sure <code>name</code> and <code>symbol</code> are legal length.
4430+
Only the creator of <code>CoinType</code> can initialize.
4431+
4432+
4433+
<a id="0x1_coin_InitializeInternalSchema"></a>
4434+
4435+
4436+
<pre><code><b>schema</b> <a href="coin.md#0x1_coin_InitializeInternalSchema">InitializeInternalSchema</a>&lt;CoinType&gt; {
4437+
<a href="account.md#0x1_account">account</a>: <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>;
4438+
name: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;;
4439+
symbol: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;;
4440+
<b>let</b> account_addr = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(<a href="account.md#0x1_account">account</a>);
4441+
<b>let</b> coin_address = <a href="../../aptos-stdlib/doc/type_info.md#0x1_type_info_type_of">type_info::type_of</a>&lt;CoinType&gt;().account_address;
4442+
<b>aborts_if</b> coin_address != account_addr;
4443+
<b>aborts_if</b> <b>exists</b>&lt;<a href="coin.md#0x1_coin_CoinInfo">CoinInfo</a>&lt;CoinType&gt;&gt;(account_addr);
4444+
<b>aborts_if</b> len(name) &gt; <a href="coin.md#0x1_coin_MAX_COIN_NAME_LENGTH">MAX_COIN_NAME_LENGTH</a>;
4445+
<b>aborts_if</b> len(symbol) &gt; <a href="coin.md#0x1_coin_MAX_COIN_SYMBOL_LENGTH">MAX_COIN_SYMBOL_LENGTH</a>;
4446+
}
4447+
</code></pre>
4448+
4449+
44044450

44054451
<a id="@Specification_1_initialize_internal"></a>
44064452

aptos-move/framework/aptos-framework/sources/coin.move

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,16 @@ module aptos_framework::coin {
108108
/// APT pairing is not eanbled yet.
109109
const EAPT_PAIRING_IS_NOT_ENABLED: u64 = 28;
110110

111+
/// The decimals of the coin is too large.
112+
const ECOIN_DECIMALS_TOO_LARGE: u64 = 29;
113+
111114
//
112115
// Constants
113116
//
114117

115118
const MAX_COIN_NAME_LENGTH: u64 = 32;
116119
const MAX_COIN_SYMBOL_LENGTH: u64 = 32;
120+
const MAX_DECIMALS: u8 = 32;
117121

118122
/// Core data structures
119123

@@ -577,39 +581,44 @@ module aptos_framework::coin {
577581
if (!features::coin_to_fungible_asset_migration_feature_enabled()) {
578582
abort error::unavailable(ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED)
579583
};
580-
assert!(is_coin_initialized<CoinType>(), error::invalid_argument(ECOIN_INFO_NOT_PUBLISHED));
581-
582-
let metadata = ensure_paired_metadata<CoinType>();
583-
let store = primary_fungible_store::ensure_primary_store_exists(account, metadata);
584584
if (exists<CoinStore<CoinType>>(account)) {
585-
let CoinStore<CoinType> { coin, frozen, deposit_events, withdraw_events } = move_from<CoinStore<CoinType>>(
586-
account
587-
);
588-
event::emit(
589-
CoinStoreDeletion {
585+
let CoinStore<CoinType> { coin, frozen, deposit_events, withdraw_events } =
586+
move_from<CoinStore<CoinType>>(account);
587+
if (is_coin_initialized<CoinType>()) {
588+
let metadata = ensure_paired_metadata<CoinType>();
589+
let store = primary_fungible_store::ensure_primary_store_exists(account, metadata);
590+
591+
event::emit(CoinStoreDeletion {
590592
coin_type: type_info::type_name<CoinType>(),
591593
event_handle_creation_address: guid::creator_address(
592594
event::guid(&deposit_events)
593595
),
594596
deleted_deposit_event_handle_creation_number: guid::creation_num(event::guid(&deposit_events)),
595597
deleted_withdraw_event_handle_creation_number: guid::creation_num(event::guid(&withdraw_events))
598+
});
599+
600+
if (coin.value == 0) {
601+
destroy_zero(coin);
602+
} else {
603+
fungible_asset::unchecked_deposit_with_no_events(
604+
object_address(&store),
605+
coin_to_fungible_asset(coin)
606+
);
607+
};
608+
609+
// Note:
610+
// It is possible the primary fungible store may already exist before this function call.
611+
// In this case, if the account owns a frozen CoinStore and an unfrozen primary fungible store, this
612+
// function would convert and deposit the rest coin into the primary store and freeze it to make the
613+
// `frozen` semantic as consistent as possible.
614+
if (frozen != fungible_asset::is_frozen(store)) {
615+
fungible_asset::set_frozen_flag_internal(store, frozen);
596616
}
597-
);
598-
if (coin.value == 0) {
599-
destroy_zero(coin);
600617
} else {
601-
fungible_asset::unchecked_deposit_with_no_events(object_address(&store), coin_to_fungible_asset(coin));
618+
destroy_zero(coin);
602619
};
603620
event::destroy_handle(deposit_events);
604621
event::destroy_handle(withdraw_events);
605-
// Note:
606-
// It is possible the primary fungible store may already exist before this function call.
607-
// In this case, if the account owns a frozen CoinStore and an unfrozen primary fungible store, this
608-
// function would convert and deposit the rest coin into the primary store and freeze it to make the
609-
// `frozen` semantic as consistent as possible.
610-
if (frozen != fungible_asset::is_frozen(store)) {
611-
fungible_asset::set_frozen_flag_internal(store, frozen);
612-
}
613622
};
614623
}
615624

@@ -1037,6 +1046,7 @@ module aptos_framework::coin {
10371046

10381047
assert!(string::length(&name) <= MAX_COIN_NAME_LENGTH, error::invalid_argument(ECOIN_NAME_TOO_LONG));
10391048
assert!(string::length(&symbol) <= MAX_COIN_SYMBOL_LENGTH, error::invalid_argument(ECOIN_SYMBOL_TOO_LONG));
1049+
assert!(decimals <= MAX_DECIMALS, error::invalid_argument(ECOIN_DECIMALS_TOO_LARGE));
10401050

10411051
let coin_info = CoinInfo<CoinType> {
10421052
name,
@@ -1712,7 +1722,6 @@ module aptos_framework::coin {
17121722
}
17131723

17141724
#[test(other = @0x123)]
1715-
#[expected_failure(abort_code = 0x10003, location = Self)]
17161725
fun test_migration_coin_store_with_non_coin_type(other: signer) acquires CoinConversionMap, CoinStore, CoinInfo {
17171726
migrate_to_fungible_store<String>(&other);
17181727
}
@@ -2026,17 +2035,14 @@ module aptos_framework::coin {
20262035
});
20272036
}
20282037

2029-
#[test(account = @aptos_framework, aaron = @0xaa10, bob = @0xb0b)]
2038+
#[test(account = @aptos_framework, bob = @0xb0b)]
20302039
fun test_is_account_registered(
20312040
account: &signer,
2032-
aaron: &signer,
20332041
bob: &signer,
20342042
) acquires CoinConversionMap, CoinInfo, CoinStore {
20352043
let account_addr = signer::address_of(account);
2036-
let aaron_addr = signer::address_of(aaron);
20372044
let bob_addr = signer::address_of(bob);
20382045
account::create_account_for_test(account_addr);
2039-
account::create_account_for_test(aaron_addr);
20402046
account::create_account_for_test(bob_addr);
20412047
let apt_fa_feature = features::get_new_accounts_default_to_fa_apt_store_feature();
20422048
let fa_feature = features::get_new_accounts_default_to_fa_store_feature();
@@ -2046,23 +2052,13 @@ module aptos_framework::coin {
20462052
assert!(coin_store_exists<FakeMoney>(account_addr), 0);
20472053
assert!(is_account_registered<FakeMoney>(account_addr), 0);
20482054

2049-
assert!(!coin_store_exists<FakeMoney>(aaron_addr), 0);
2050-
assert!(!is_account_registered<FakeMoney>(aaron_addr), 0);
2051-
20522055
register<FakeMoney>(bob);
20532056
assert!(coin_store_exists<FakeMoney>(bob_addr), 0);
20542057
maybe_convert_to_fungible_store<FakeMoney>(bob_addr);
20552058
assert!(!coin_store_exists<FakeMoney>(bob_addr), 0);
20562059
register<FakeMoney>(bob);
20572060
assert!(!coin_store_exists<FakeMoney>(bob_addr), 0);
20582061

2059-
maybe_convert_to_fungible_store<FakeMoney>(aaron_addr);
2060-
let coin = mint<FakeMoney>(100, &mint_cap);
2061-
deposit(aaron_addr, coin);
2062-
2063-
assert!(!coin_store_exists<FakeMoney>(aaron_addr), 0);
2064-
assert!(is_account_registered<FakeMoney>(aaron_addr), 0);
2065-
20662062
maybe_convert_to_fungible_store<FakeMoney>(account_addr);
20672063
assert!(!coin_store_exists<FakeMoney>(account_addr), 0);
20682064
assert!(is_account_registered<FakeMoney>(account_addr), 0);

0 commit comments

Comments
 (0)