From ef812e50e3b0a9687c39b79a88d0417443461d2c Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 3 May 2024 09:59:01 -0700 Subject: [PATCH] [ECO-1571] Add abstracted unit testing (#37) Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- .github/PULL_REQUEST_TEMPLATE.md | 4 +- src/move/coin_factory/Move.toml | 16 +- src/move/coin_factory/publish_and_split.py | 36 +- .../coin_factory/sources/coin_factory.move | 21 +- src/move/emojicoin_dot_fun/Move.toml | 4 + .../sources/emojicoin_dot_fun.move | 656 +++++------------- .../emojicoin_dot_fun/sources/hex_codes.move | 63 +- .../sources/test_acquisitions.move | 28 + src/move/emojicoin_dot_fun/sources/tests.move | 546 +++++++++++++++ .../test_coin_factories/black_cat/Move.toml | 2 +- .../test_coin_factories/black_heart/Move.toml | 2 +- .../yellow_heart/Move.toml | 2 +- 12 files changed, 796 insertions(+), 584 deletions(-) create mode 100644 src/move/emojicoin_dot_fun/sources/test_acquisitions.move create mode 100644 src/move/emojicoin_dot_fun/sources/tests.move diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d3047ef19..60cee2dc9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,8 +15,8 @@ PR. - [ ] Did you update relevant documentation? - [ ] Did you add tests to cover new code or a fixed issue? - [ ] Did you update the changelog? -- [ ] Did you check off all checkboxes from the linked Linear task? (Ignore if - you are not a member of Econia Labs) +- [ ] Did you check off all checkboxes from the linked Linear task? + (Ignore if you are not a member of Econia Labs) > If a task does not apply to your PR, strike it through and mark it complete: diff --git a/src/move/coin_factory/Move.toml b/src/move/coin_factory/Move.toml index 98ad849bf..b0e9104f6 100644 --- a/src/move/coin_factory/Move.toml +++ b/src/move/coin_factory/Move.toml @@ -1,20 +1,12 @@ [addresses] -emojicoin_dot_fun = "_" -market_address = "_" +coin_factory = "_" -[dependencies.AptosFramework] +[dev-dependencies.AptosFramework] git = "https://github.com/aptos-labs/aptos-core.git" rev = "mainnet" subdir = "aptos-move/framework/aptos-framework" -[dev-addresses] -emojicoin_dot_fun = "0xcccccccc" -market_address = "0xbbbbbbbb" - -[dev-dependencies.EmojicoinDotFun] -local = "../emojicoin_dot_fun" - [package] -name = "CoinFactory" -upgrade_policy = "compatible" +name = "EmojicoinDotFunCoinFactory" +upgrade_policy = "immutable" version = "1.0.0" diff --git a/src/move/coin_factory/publish_and_split.py b/src/move/coin_factory/publish_and_split.py index 19f2f4185..4b36f33d3 100644 --- a/src/move/coin_factory/publish_and_split.py +++ b/src/move/coin_factory/publish_and_split.py @@ -4,25 +4,15 @@ import os import subprocess from pathlib import Path +from typing import Sequence -MARKET_ADDRESS = "market_address" -EMOJICOIN_DOT_FUN = "emojicoin_dot_fun" +FLAG_ADDRESS = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +MARKET_NAMED_ADDRESS = "market_address" +COIN_FACTORY_NAMED_ADDRESS = "coin_factory" PACKAGE_BYTECODE_PATH = "json/build_publish_payload.json" SPLIT_BYTECODE_PATH = "json/split_bytecode.json" METADATA_K, CODE_K = "metadata", "code" -# Mapping of named addresses to placeholder addresses for generating Move bytecode. -named_addresses = [ - # market_address - "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", - # emojicoin_dot_fun - "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", -] -NAMED_ADDRESSES = { - MARKET_ADDRESS: named_addresses[0], - EMOJICOIN_DOT_FUN: named_addresses[1], -} - def ensure_parent_directories_exist(s: str) -> Path: fp = Path(s) @@ -61,7 +51,7 @@ def split_and_replace_named_addresses( return lines_to_replace -def compare_contents_and_log(fp: Path, new_data: dict[str, list[str]]) -> None: +def compare_contents_and_log(fp: Path, new_data: dict[str, Sequence[str]]) -> None: metadata_bytes_changed = False module_bytes_changed = False @@ -86,7 +76,6 @@ def compare_contents_and_log(fp: Path, new_data: dict[str, list[str]]) -> None: # - Run this script from the coin factory module directory (where the `Move.toml` is) if __name__ == "__main__": json_path = ensure_parent_directories_exist(PACKAGE_BYTECODE_PATH) - named_addresses_args = ",".join([f"{k}={v}" for k, v in NAMED_ADDRESSES.items()]) try: file_contents_last_updated = os.path.getmtime(PACKAGE_BYTECODE_PATH) except FileNotFoundError: @@ -99,7 +88,7 @@ def compare_contents_and_log(fp: Path, new_data: dict[str, list[str]]) -> None: "--json-output-file", json_path, "--named-addresses", - named_addresses_args, + f"{COIN_FACTORY_NAMED_ADDRESS}={FLAG_ADDRESS}", "--assume-yes", "--included-artifacts=none", ], @@ -127,8 +116,6 @@ def compare_contents_and_log(fp: Path, new_data: dict[str, list[str]]) -> None: if metadata.startswith("0x"): metadata = metadata[2:] - metadata_lines = split_and_replace_named_addresses([metadata], NAMED_ADDRESSES) - bytecode_array: list[str] = cli_json_publish_output["args"][1]["value"] assert len(bytecode_array) == 1 bytecode = bytecode_array[0] @@ -136,13 +123,14 @@ def compare_contents_and_log(fp: Path, new_data: dict[str, list[str]]) -> None: if bytecode.startswith("0x"): bytecode = bytecode[2:] - assert NAMED_ADDRESSES[MARKET_ADDRESS] not in metadata - assert NAMED_ADDRESSES[MARKET_ADDRESS] in bytecode + assert FLAG_ADDRESS not in metadata - bytecode_lines = split_and_replace_named_addresses([bytecode], NAMED_ADDRESSES) + bytecode_lines = split_and_replace_named_addresses( + [bytecode], {MARKET_NAMED_ADDRESS: FLAG_ADDRESS} + ) - new_data: dict[str, list[str]] = { - METADATA_K: metadata_lines, + new_data = { + METADATA_K: metadata, CODE_K: bytecode_lines, } split_bytecode_path = ensure_parent_directories_exist(SPLIT_BYTECODE_PATH) diff --git a/src/move/coin_factory/sources/coin_factory.move b/src/move/coin_factory/sources/coin_factory.move index 4d2b61dac..73b2d9807 100644 --- a/src/move/coin_factory/sources/coin_factory.move +++ b/src/move/coin_factory/sources/coin_factory.move @@ -1,23 +1,4 @@ -module market_address::coin_factory { +module coin_factory::coin_factory { struct Emojicoin {} struct EmojicoinLP {} - - #[test_only] use aptos_std::type_info::{Self}; - #[test_only] use emojicoin_dot_fun::emojicoin_dot_fun; - - #[test] - fun test_type_info_consts_correct() { - let (module_name, emojicoin_struct, emojicoin_lp_struct) = - emojicoin_dot_fun::get_COIN_FACTORY_TYPE_CONSTANTS(); - - let emojicoin_type_info = type_info::type_of(); - let lp_type_info = type_info::type_of(); - - assert!(@market_address == type_info::account_address(&emojicoin_type_info), 0); - assert!(@market_address == type_info::account_address(&lp_type_info), 0); - assert!(module_name == type_info::module_name(&emojicoin_type_info), 0); - assert!(module_name == type_info::module_name(&lp_type_info), 0); - assert!(emojicoin_struct == type_info::struct_name(&emojicoin_type_info), 0); - assert!(emojicoin_lp_struct == type_info::struct_name(&lp_type_info), 0); - } } diff --git a/src/move/emojicoin_dot_fun/Move.toml b/src/move/emojicoin_dot_fun/Move.toml index 1a05d4068..953bf5dc4 100644 --- a/src/move/emojicoin_dot_fun/Move.toml +++ b/src/move/emojicoin_dot_fun/Move.toml @@ -7,6 +7,7 @@ rev = "mainnet" subdir = "aptos-move/framework/aptos-framework" [dev-addresses] +coin_factory = "0xaaaa" emojicoin_dot_fun = "0xc0de" [dev-dependencies.BlackCatCoinFactory] @@ -15,6 +16,9 @@ local = "../test_coin_factories/black_cat" [dev-dependencies.BlackHeartCoinFactory] local = "../test_coin_factories/black_heart" +[dev-dependencies.EmojicoinDotFunCoinFactory] +local = "../coin_factory" + [dev-dependencies.YellowHeartCoinFactory] local = "../test_coin_factories/yellow_heart" diff --git a/src/move/emojicoin_dot_fun/sources/emojicoin_dot_fun.move b/src/move/emojicoin_dot_fun/sources/emojicoin_dot_fun.move index 63f384924..498ff7f71 100644 --- a/src/move/emojicoin_dot_fun/sources/emojicoin_dot_fun.move +++ b/src/move/emojicoin_dot_fun/sources/emojicoin_dot_fun.move @@ -19,29 +19,9 @@ module emojicoin_dot_fun::emojicoin_dot_fun { use std::string::{Self, String}; use std::vector; - #[test_only] use aptos_std::aptos_coin; - #[test_only] use black_cat_market::coin_factory::{ - Emojicoin as BlackCatEmojicoin, - EmojicoinLP as BlackCatEmojicoinLP, - }; - #[test_only] use black_heart_market::coin_factory::{ - Emojicoin as BlackHeartEmojicoin, - EmojicoinLP as BlackHeartEmojicoinLP, - }; - - #[test_only] use yellow_heart_market::coin_factory::{ - Emojicoin as YellowHeartEmojicoin, - EmojicoinLP as YellowHeartEmojicoinLP, - BadType, - }; - #[test_only] const BLACK_CAT: vector = x"f09f9088e2808de2ac9b"; - #[test_only] const BLACK_HEART: vector = x"f09f96a4"; - #[test_only] const YELLOW_HEART: vector = x"f09f929b"; - #[test_only] const MICROSECONDS_PER_SECOND: u64 = 1_000_000; - const DECIMALS: u8 = 8; const MAX_SYMBOL_LENGTH: u8 = 10; - const MAX_CHAT_MESSAGE_LENGTH: u8 = 100; + const MAX_CHAT_MESSAGE_LENGTH: u64 = 100; const MONITOR_SUPPLY: bool = true; const COIN_FACTORY_AS_BYTES: vector = b"coin_factory"; @@ -234,6 +214,7 @@ module emojicoin_dot_fun::emojicoin_dot_fun { time: u64, registrant: address, integrator: address, + integrator_fee: u64, } #[event] @@ -297,7 +278,7 @@ module emojicoin_dot_fun::emojicoin_dot_fun { registry_address: address, sequence_info: SequenceInfo, coin_symbol_emojis: Table, u8>, - chat_emojis: Table, u8>, + supplemental_chat_emojis: Table, u8>, markets_by_emoji_bytes: SmartTable, address>, markets_by_market_id: SmartTable, extend_ref: ExtendRef, @@ -392,7 +373,7 @@ module emojicoin_dot_fun::emojicoin_dot_fun { ) acquires Market, Registry, RegistryAddress { assert!( - vector::length(&emoji_indices_sequence) <= (MAX_CHAT_MESSAGE_LENGTH as u64), + vector::length(&emoji_indices_sequence) <= MAX_CHAT_MESSAGE_LENGTH, E_CHAT_MESSAGE_TOO_LONG, ); @@ -404,12 +385,9 @@ module emojicoin_dot_fun::emojicoin_dot_fun { market_address, ); - // Verify all emoji bytes are either coin symbol emojis or chat emojis. + // Verify all emoji bytes are supported as chat emojis. assert!( - vector::all(&emoji_bytes, |emoji| { - is_a_supported_symbol_emoji(*emoji) || - is_a_supported_chat_emoji(*emoji) - }), + vector::all(&emoji_bytes, |emoji_ref| { is_a_supported_chat_emoji(*emoji_ref) }), E_NOT_SUPPORTED_CHAT_EMOJI, ); @@ -486,6 +464,15 @@ module emojicoin_dot_fun::emojicoin_dot_fun { registrant: &signer, emojis: vector>, integrator: address, + ) acquires Market, Registry, RegistryAddress { + register_market_inner(registrant, emojis, integrator, true); + } + + fun register_market_inner( + registrant: &signer, + emojis: vector>, + integrator: address, + publish_code: bool, ) acquires Market, Registry, RegistryAddress { let registry_ref_mut = borrow_registry_ref_mut(); @@ -500,29 +487,35 @@ module emojicoin_dot_fun::emojicoin_dot_fun { // Create the Market object and add it to the registry. let (market_address, market_signer) = create_market(registry_ref_mut, emoji_bytes); - // Publish coin types at market address. - let (metadata_bytecode, module_bytecode) = hex_codes::get_publish_code(market_address); - code::publish_package_txn(&market_signer, metadata_bytecode, vector[module_bytecode]); + // Publish coin types at market address, unless publication disabled for testing. + if (publish_code) { + let (metadata_bytecode, module_bytecode) = hex_codes::get_publish_code(market_address); + code::publish_package_txn(&market_signer, metadata_bytecode, vector[module_bytecode]); + }; - // Only charge the registrant if they have to pay for initializing the chat emojis. - // Otherwise, waive the register market fee, since the gas costs of initializing them - // is roughly the same. + // Waive market registration fee if registrant assumes the gas cost of initializing the + // supplemental chat emojis table during the registration of the first market, an operation + // that is roughly equal in cost to the static market registration fee. Else, charge fee. let registrant_address = signer::address_of(registrant); - let fee = if (ensure_chat_emojis_initialized(registry_ref_mut)) { + let registrant_assumes_cost_of_initializing_supplemental_chat_emojis = + ensure_supplemental_chat_emojis_initialized(registry_ref_mut); + let integrator_fee = if (registrant_assumes_cost_of_initializing_supplemental_chat_emojis) { 0 } else { + let fee = MARKET_REGISTRATION_FEE; let can_pay_fee = coin::is_account_registered(registrant_address) && - coin::balance(registrant_address) >= MARKET_REGISTRATION_FEE; + coin::balance(registrant_address) >= fee; assert!(can_pay_fee, E_UNABLE_TO_PAY_MARKET_REGISTRATION_FEE); - aptos_account::transfer(registrant, integrator, MARKET_REGISTRATION_FEE); - (MARKET_REGISTRATION_FEE as u128) - }; + aptos_account::transfer(registrant, integrator, fee); - // Update global integrator fees. - let global_cumulative_integrator_fees_ref_mut = - &mut registry_ref_mut.global_stats.cumulative_integrator_fees; - aggregator_v2::try_add(global_cumulative_integrator_fees_ref_mut, fee); + // Update global integrator fees. + let global_cumulative_integrator_fees_ref_mut = + &mut registry_ref_mut.global_stats.cumulative_integrator_fees; + aggregator_v2::try_add(global_cumulative_integrator_fees_ref_mut, (fee as u128)); + + fee + }; // Bump state. let market_ref_mut = borrow_global_mut(market_address); @@ -534,6 +527,7 @@ module emojicoin_dot_fun::emojicoin_dot_fun { time, registrant: registrant_address, integrator, + integrator_fee, }); bump_market_state( market_ref_mut, @@ -723,6 +717,12 @@ module emojicoin_dot_fun::emojicoin_dot_fun { ) } + inline fun is_a_supported_chat_emoji_inner(emoji: vector): bool { + let registry_ref = borrow_registry_ref(); + table::contains(®istry_ref.coin_symbol_emojis, emoji) || + table::contains(®istry_ref.supplemental_chat_emojis, emoji) + } + inline fun assign_supply_minuend_reserves_ref_mut( market_ref_mut: &mut Market, starts_in_bonding_curve: bool @@ -1218,20 +1218,20 @@ module emojicoin_dot_fun::emojicoin_dot_fun { ); } - /// A crank function to add all chat emojis to the registry if they haven't been added yet. - /// Returns true if the chat emojis were added, false otherwise. - inline fun ensure_chat_emojis_initialized( + /// Adds all supplemental chat emojis to the registry if they haven't been added yet. Returns + /// true if the supplemental chat emojis were added, false otherwise. + inline fun ensure_supplemental_chat_emojis_initialized( registry_ref_mut: &mut Registry ): bool acquires Registry, RegistryAddress { - let chat_emojis_ref_mut = &mut registry_ref_mut.chat_emojis; + let supplemental_chat_emojis_ref_mut = &mut registry_ref_mut.supplemental_chat_emojis; - let chat_emojis = hex_codes::get_chat_emojis(); - let emoji_0 = *vector::borrow(&chat_emojis, 0); - if (table::contains(chat_emojis_ref_mut, emoji_0)) { + let supplemental_chat_emojis = hex_codes::get_supplemental_chat_emojis(); + let emoji_0 = *vector::borrow(&supplemental_chat_emojis, 0); + if (table::contains(supplemental_chat_emojis_ref_mut, emoji_0)) { false } else { - vector::for_each(chat_emojis, |emoji| { - table::add(chat_emojis_ref_mut, emoji, 0); + vector::for_each(supplemental_chat_emojis, |emoji| { + table::add(supplemental_chat_emojis_ref_mut, emoji, 0); }); true } @@ -1253,7 +1253,7 @@ module emojicoin_dot_fun::emojicoin_dot_fun { nonce: 1, }, coin_symbol_emojis: table::new(), - chat_emojis: table::new(), + supplemental_chat_emojis: table::new(), markets_by_emoji_bytes: smart_table::new(), markets_by_market_id: smart_table::new(), extend_ref, @@ -1269,9 +1269,10 @@ module emojicoin_dot_fun::emojicoin_dot_fun { }, }; - // Load supported emojis into registry. + // Load supported coin symbol emojis into registry. + let coin_symbol_emojis_ref_mut = &mut registry.coin_symbol_emojis; vector::for_each_ref(&hex_codes::get_coin_symbol_emojis(), |emoji_bytes_ref| { - table::add(&mut registry.coin_symbol_emojis, *emoji_bytes_ref, 0); + table::add(coin_symbol_emojis_ref_mut, *emoji_bytes_ref, 0); }); move_to(®istry_signer, registry); @@ -1300,12 +1301,20 @@ module emojicoin_dot_fun::emojicoin_dot_fun { table::contains(&borrow_registry_ref().coin_symbol_emojis, hex_bytes) } + #[view] + /// Checks if an individual emoji is supported for usage in chat only. + public fun is_a_supplemental_chat_emoji( + hex_bytes: vector + ): bool acquires Registry, RegistryAddress { + table::contains(&borrow_registry_ref().supplemental_chat_emojis, hex_bytes) + } + #[view] /// Checks if an individual emoji is supported for usage in chat. public fun is_a_supported_chat_emoji( hex_bytes: vector ): bool acquires Registry, RegistryAddress { - table::contains(&borrow_registry_ref().chat_emojis, hex_bytes) + is_a_supported_chat_emoji_inner(hex_bytes) } #[view] @@ -1452,6 +1461,12 @@ module emojicoin_dot_fun::emojicoin_dot_fun { ) } + #[view] + public fun verified_symbol_emoji_bytes(emojis: vector>): vector + acquires Registry, RegistryAddress { + get_verified_symbol_emoji_bytes(borrow_registry_ref(), emojis) + } + public fun tvl_per_lp_coin_growth_q64( start: TVLtoLPCoinRatio, end: TVLtoLPCoinRatio, @@ -2181,447 +2196,116 @@ module emojicoin_dot_fun::emojicoin_dot_fun { ); } - inline fun update_periodic_state_trackers_for_liquidity_provision_operation( - tvl: u128, - lp_coin_supply: u128, - periodic_state_trackers_ref_mut: &mut vector, - ) { - } - - #[test_only] - public fun get_COIN_FACTORY_TYPE_CONSTANTS(): (vector, vector, vector) { - ( - COIN_FACTORY_AS_BYTES, - EMOJICOIN_STRUCT_NAME, - EMOJICOIN_LP_STRUCT_NAME, - ) - } - - #[test_only] - public fun get_EMOJICOIN_SUPPLY(): u64 { EMOJICOIN_SUPPLY } - - #[test_only] - fun create_market_and_init_coins( - symbol_bytes: vector, - ) acquires Registry, RegistryAddress, Market { - let registry_ref_mut = borrow_registry_ref_mut(); - let (market_address, market_signer) = create_market(registry_ref_mut, symbol_bytes); - - let (market_ref_mut, _) = get_market_ref_mut_and_signer_checked(market_address); - ensure_coins_initialized( - market_ref_mut, - &market_signer, - market_address, - ); - } - - #[test_only] - inline fun fund_account(account_address: address, amount: u64, aptos_framework: &signer) { - let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(aptos_framework); - let coins = coin::mint(amount, &mint_cap); - aptos_account::deposit_coins(account_address, coins); - coin::destroy_burn_cap(burn_cap); - coin::destroy_mint_cap(mint_cap); - } - - #[test_only] - fun init_module_for_testing() { - let framework_signer = account::create_signer_for_test(@aptos_framework); - let emojicoin_dot_fun_signer = account::create_signer_for_test(@emojicoin_dot_fun); - timestamp::set_time_has_started_for_testing(&framework_signer); - init_module(&emojicoin_dot_fun_signer); - } - - #[test] - fun test_cpamm_simple_swap_output_amount() { - // Buy all base from start of bonding curve. - let reserves = Reserves { base: BASE_VIRTUAL_CEILING, quote: QUOTE_VIRTUAL_FLOOR }; - let output = cpamm_simple_swap_output_amount(QUOTE_REAL_CEILING, false, reserves); - assert!(output == BASE_REAL_CEILING, 0); - // Sell all base to a bonding curve that is theoretically complete but has not transitioned. - reserves = Reserves { base: BASE_VIRTUAL_FLOOR, quote: QUOTE_VIRTUAL_CEILING }; - output = cpamm_simple_swap_output_amount(BASE_REAL_CEILING, true, reserves); - assert!(output == QUOTE_REAL_CEILING, 0); - } - - #[test(aptos_framework = @0x1, user = @0xAA)] - fun test_swap_function( - user: &signer, - aptos_framework: &signer, - ) acquires Registry, RegistryAddress, Market, LPCoinCapabilities { - aptos_account::create_account(@emojicoin_dot_fun); - init_module_for_testing(); - let symbol_bytes = YELLOW_HEART; - let registry_ref_mut = borrow_registry_ref_mut(); - let (market_address, _) = create_market(registry_ref_mut, symbol_bytes); - assert!(@yellow_heart_market == market_address, 0); - - let user_addr = signer::address_of(user); - let input_amount: u64 = 100_000_000; - fund_account(user_addr, input_amount, aptos_framework); - let integrator_address = @0xf00dcafe; - swap( - @yellow_heart_market, - user, - input_amount, - false, // Buy the emojicoins. - integrator_address, - 0, - ); - - assert!( - exists>( - @yellow_heart_market - ), - 0, - ); - } - - #[test, expected_failure(abort_code = E_SWAP_DIVIDE_BY_ZERO)] - fun test_cpamm_simple_swap_output_amount_divide_by_zero() { - cpamm_simple_swap_output_amount(0, true, Reserves { base: 0, quote: 16 }); - } - - #[test] - fun test_all_coin_symbol_emojis_under_10_bytes() { - let all_coin_symbol_emojis = hex_codes::get_coin_symbol_emojis(); - vector::for_each(all_coin_symbol_emojis, |bytes| { - let emoji_as_string = string::utf8(bytes); - assert!(string::length(&emoji_as_string) <= (MAX_SYMBOL_LENGTH as u64), 0); - }); - } - - #[test] - fun test_register_market_with_complex_emoji_happy_path() acquires Registry, RegistryAddress { - init_module_for_testing(); - let emojis = vector> [ - x"e29aa1", // High voltage. - x"f09f96a5efb88f", // Desktop computer. - ]; - - let registry_ref_mut = borrow_registry_ref_mut(); - let emoji_bytes = get_verified_symbol_emoji_bytes(registry_ref_mut, emojis); - - // Verify market is not already registered. - let markets_by_emoji_bytes_ref_mut = &mut registry_ref_mut.markets_by_emoji_bytes; - let already_registered = smart_table::contains(markets_by_emoji_bytes_ref_mut, emoji_bytes); - assert!(!already_registered, E_ALREADY_REGISTERED); - - create_market(registry_ref_mut, emoji_bytes); - let utf8_emoji = string::utf8(emoji_bytes); - assert!(utf8_emoji == string::utf8(x"e29aa1f09f96a5efb88f"), 0); - } - - #[test] - fun test_supported_symbol_emoji_happy_path() acquires Registry, RegistryAddress { - aptos_account::create_account(@emojicoin_dot_fun); - init_module_for_testing(); - let various_emojis = vector> [ - x"f09f868e", // AB button blood type, 1F18E. - x"f09fa6bbf09f8fbe", // Ear with hearing aid medium dark skin tone, 1F9BB 1F3FE. - x"f09f87a7f09f87b9", // Flag Bhutan, 1F1E7 1F1F9. - x"f09f9190f09f8fbe", // Open hands medium dark skin tone, 1F450 1F3FE. - x"f09fa4b0f09f8fbc", // Pregnant woman medium light skin tone, 1F930 1F3FC. - x"f09f9faa", // Purple square, 1F7EA. - x"f09f91abf09f8fbe", // Woman and man holding hands medium dark skin tone, 1F46B 1F3FE. - x"f09f91a9f09f8fbe", // Woman medium dark skin tone, 1F469 1F3FE. - x"f09fa795f09f8fbd", // Woman with headscarf medium skin tone, 1F9D5 1F3FD. - x"f09fa490", // Zipper mouth face, 1F910. - ]; - vector::for_each(various_emojis, |bytes| { - assert!(is_a_supported_symbol_emoji(bytes), 0); - }); - - // Test unsupported emojis. - assert!(!is_a_supported_symbol_emoji(x"0000"), 0); - assert!(!is_a_supported_symbol_emoji(x"fe0f"), 0); - assert!(!is_a_supported_symbol_emoji(x"1234"), 0); - assert!(!is_a_supported_symbol_emoji(x"f0fabcdefabcdeff0f"), 0); - assert!(!is_a_supported_symbol_emoji(x"f0f00dcafef0"), 0); - // Minimally qualified "head shaking horizontally". - assert!(!is_a_supported_symbol_emoji(x"f09f9982e2808de28694"), 0); - - // Specifically test a supported emoji, add some bunk data to it, and make sure it no longer - // works. - assert!(is_a_supported_symbol_emoji(x"e29d97"), 0); - assert!(!is_a_supported_symbol_emoji(x"e29d97ff"), 0); - assert!(!is_a_supported_symbol_emoji(x"ffe29d97"), 0); - } - - #[test] - fun test_valid_coin_types() { - valid_coin_types(@yellow_heart_market); - valid_coin_types(@black_heart_market); - valid_coin_types(@black_cat_market); - } - - #[test] - fun test_invalid_coin_types() { - // Duplicate types should be invalid. - assert!(!valid_coin_types(@emojicoin_dot_fun), 0); - // Duplicate LP types should be invalid. - assert!(!valid_coin_types(@emojicoin_dot_fun), 0); - // A bad Emojicoin type should be invalid. - assert!(!valid_coin_types(@emojicoin_dot_fun), 0); - // A bad EmojicoinLP type should be invalid. - assert!(!valid_coin_types(@emojicoin_dot_fun), 0); - // Backwards coin types that are otherwise valid should be invalid. - assert!(!valid_coin_types(@emojicoin_dot_fun), 0); - // A market address that doesn't match the types should be invalid. - assert!(!valid_coin_types(@0xc1de), 0); - } - - #[test, expected_failure(abort_code = E_INVALID_COIN_TYPES)] - fun test_initialize_coin_types_validates_coin_types() - acquires Market, Registry, RegistryAddress { - aptos_account::create_account(@emojicoin_dot_fun); - init_module_for_testing(); - let symbol_bytes = YELLOW_HEART; - let registry = borrow_registry_ref_mut(); - let (market_address, market_signer) = create_market(registry, symbol_bytes); - let (market_ref_mut, dupe_signer) = get_market_ref_mut_and_signer_checked(market_address); - assert!(signer::address_of(&market_signer) == signer::address_of(&dupe_signer), 0); - - ensure_coins_initialized( - market_ref_mut, - &market_signer, - market_address, - ); - } - - #[test] - fun test_concatenation() { - let base = string::utf8(b"base"); - let additional = string::utf8(b" additional"); - let concatenated = get_concatenation(base, additional); - assert!(concatenated == string::utf8(b"base additional"), 0); - // Ensure the base string was not mutated. - assert!(base == string::utf8(b"base"), 0); - } + #[test_only] const MICROSECONDS_PER_SECOND: u64 = 1_000_000; - #[test] - fun test_period_times() { - assert!(PERIOD_1M == 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_5M == 5 * 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_15M == 15 * 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_30M == 30 * 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_1H == 60 * 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_4H == 4 * 60 * 60 * MICROSECONDS_PER_SECOND, 0); - assert!(PERIOD_1D == 24 * 60 * 60 * MICROSECONDS_PER_SECOND, 0); + #[test_only] public fun assert_valid_coin_types_test_only( + market_address: address, + ) { + assert_valid_coin_types(market_address) } - #[test(user = @0xfa)] - fun test_chat_message_happy_path( - user: &signer, - ) acquires Registry, RegistryAddress, Market { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - let user_address = signer::address_of(user); - aptos_account::create_account(user_address); - chat( - user, - vector> [ - x"f09f98b6", // Cat face. - x"f09f98b7", // Cat face with tears of joy. - x"f09f98b8", // Cat face with wry smile. - x"f09f9088e2808de2ac9b", // Black cat. - x"f09f9294", // Broken heart. - ], - vector [ 3, 0, 2, 2, 1, 4 ], - black_cat_market_address, - ); - - let events_emitted = event::emitted_events(); - let chat_event = vector::pop_back(&mut events_emitted); - assert!( - chat_event == Chat { - market_metadata: MarketMetadata { - market_id: 1, - market_address: black_cat_market_address, - emoji_bytes: BLACK_CAT, - }, - emit_time: 0, - emit_market_nonce: 1, - user: user_address, - message: string::utf8( - x"f09f9088e2808de2ac9bf09f98b6f09f98b8f09f98b8f09f98b7f09f9294" - ), - user_emojicoin_balance: 0, - circulating_supply: 0, - balance_as_fraction_of_circulating_supply_q64: 0, - }, - 0 - ); - - // Post a max length chat message. - let one_hundred_zeroes = vector []; - for (i in 0..100) { - vector::push_back(&mut one_hundred_zeroes, 0); - }; - chat( - user, - vector> [ - x"f09f9088e2808de2ac9b", // Black cat. - ], - one_hundred_zeroes, - black_cat_market_address, - ); + #[test_only] public fun cpamm_simple_swap_output_amount_test_only( + input_amount: u64, + is_sell: bool, + reserves: Reserves, + ): u64 { + cpamm_simple_swap_output_amount(input_amount, is_sell, reserves) } - #[test(user = @0xfa), expected_failure(abort_code = E_NOT_SUPPORTED_CHAT_EMOJI)] - fun test_chat_message_not_supported_chat_emoji( - user: &signer, - ) acquires Registry, RegistryAddress, Market { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - let user_address = signer::address_of(user); - aptos_account::create_account(user_address); - chat( - user, - vector> [ - x"f09f98b7", // Cat face with tears of joy. - x"f09f", // Invalid emoji. - ], - vector [ 0, 1 ], - black_cat_market_address, - ); + #[test_only] public fun exists_lp_coin_capabilities( + market_address: address, + ): bool { + exists>(market_address) + } + + #[test_only] public fun get_concatenation_test_only( + base: String, + additional: String, + ): String { + get_concatenation(base, additional) + } + + #[test_only] public fun get_BASE_REAL_CEILING(): u64 { BASE_REAL_CEILING } + #[test_only] public fun get_BASE_VIRTUAL_CEILING(): u64 { BASE_VIRTUAL_CEILING } + #[test_only] public fun get_BASE_VIRTUAL_FLOOR(): u64 { BASE_VIRTUAL_FLOOR } + #[test_only] public fun get_COIN_FACTORY_AS_BYTES(): vector { COIN_FACTORY_AS_BYTES } + #[test_only] public fun get_EMOJICOIN_STRUCT_NAME(): vector { EMOJICOIN_STRUCT_NAME } + #[test_only] public fun get_EMOJICOIN_LP_NAME_SUFFIX(): vector { EMOJICOIN_LP_NAME_SUFFIX } + #[test_only] public fun get_EMOJICOIN_LP_STRUCT_NAME(): vector { EMOJICOIN_LP_STRUCT_NAME } + #[test_only] public fun get_EMOJICOIN_LP_SYMBOL_PREFIX(): vector { + EMOJICOIN_LP_SYMBOL_PREFIX + } + #[test_only] public fun get_EMOJICOIN_LP_NAME(): vector { + EMOJICOIN_LP_NAME_SUFFIX + } + #[test_only] public fun get_EMOJICOIN_NAME_SUFFIX(): vector { EMOJICOIN_NAME_SUFFIX } + #[test_only] public fun get_MAX_CHAT_MESSAGE_LENGTH(): u64 { MAX_CHAT_MESSAGE_LENGTH } + #[test_only] public fun get_MAX_SYMBOL_LENGTH(): u8 { MAX_SYMBOL_LENGTH } + #[test_only] public fun get_MARKET_REGISTRATION_FEE(): u64 { MARKET_REGISTRATION_FEE } + #[test_only] public fun get_MICROSECONDS_PER_SECOND(): u64 { MICROSECONDS_PER_SECOND } + #[test_only] public fun get_PERIOD_1M(): u64 { PERIOD_1M } + #[test_only] public fun get_PERIOD_5M(): u64 { PERIOD_5M } + #[test_only] public fun get_PERIOD_15M(): u64 { PERIOD_15M } + #[test_only] public fun get_PERIOD_30M(): u64 { PERIOD_30M } + #[test_only] public fun get_PERIOD_1H(): u64 { PERIOD_1H } + #[test_only] public fun get_PERIOD_4H(): u64 { PERIOD_4H } + #[test_only] public fun get_PERIOD_1D(): u64 { PERIOD_1D } + #[test_only] public fun get_QUOTE_REAL_CEILING(): u64 { QUOTE_REAL_CEILING } + #[test_only] public fun get_QUOTE_VIRTUAL_CEILING(): u64 { QUOTE_VIRTUAL_CEILING } + #[test_only] public fun get_QUOTE_VIRTUAL_FLOOR(): u64 { QUOTE_VIRTUAL_FLOOR } + + #[test_only] public fun init_module_test_only(account: &signer) { init_module(account) } + + #[test_only] public fun pack_reserves(base: u64, quote: u64): Reserves { + Reserves { base, quote } + } + + #[test_only] public fun register_market_without_publish( + registrant: &signer, + emojis: vector>, + integrator: address, + ) acquires Market, Registry, RegistryAddress { + register_market_inner(registrant, emojis, integrator, false); } - #[test(user = @0xfa), expected_failure(abort_code = E_CHAT_MESSAGE_TOO_LONG)] - fun test_chat_message_too_long( - user: &signer, - ) acquires Registry, RegistryAddress, Market { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - let user_address = signer::address_of(user); - aptos_account::create_account(user_address); - chat( + #[test_only] public fun unpack_chat( + chat: Chat, + ): ( + MarketMetadata, + u64, + u64, + address, + String, + u64, + u64, + u128, + ) { + let Chat { + market_metadata, + emit_time, + emit_market_nonce, user, - vector> [ - x"f09f8dba", // Beer mug. - ], - // 101 beer mugs, which is one emoji too long. - vector [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, - ], - black_cat_market_address, - ); - } - - #[test(user = @0xfa)] - fun test_auxiliary_emoji_in_chat_message( - user: &signer, - ) acquires Registry, RegistryAddress, Market { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - let user_address = signer::address_of(user); - aptos_account::create_account(user_address); - ensure_chat_emojis_initialized(registry_ref_mut); - chat( + message, + user_emojicoin_balance, + circulating_supply, + balance_as_fraction_of_circulating_supply_q64, + } = chat; + ( + market_metadata, + emit_time, + emit_market_nonce, user, - vector> [ - x"f09fa791e2808df09f9a80", // Astronaut. - x"f09fa6b8f09f8fbee2808de29982efb88f", // Man superhero: medium-dark skin tone. - ], - vector [ 0, 1 ], - black_cat_market_address, - ); - let events_emitted = event::emitted_events(); - let chat_event = vector::pop_back(&mut events_emitted); - assert!( - chat_event.message == - string::utf8(x"f09fa791e2808df09f9a80f09fa6b8f09f8fbee2808de29982efb88f"), - 0 - ); + message, + user_emojicoin_balance, + circulating_supply, + balance_as_fraction_of_circulating_supply_q64, + ) } - #[test(user = @0xfa), expected_failure(abort_code = E_NOT_SUPPORTED_CHAT_EMOJI)] - fun test_auxiliary_emoji_in_chat_message_not_yet_added( - user: &signer, - ) acquires Registry, RegistryAddress, Market { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - let user_address = signer::address_of(user); - aptos_account::create_account(user_address); - chat( - user, - vector> [ - x"f09fa791e2808df09f9a80", // Astronaut. - x"f09fa6b8f09f8fbee2808de29982efb88f", // Man superhero: medium-dark skin tone. - ], - vector [ 0, 1, 1, 0 ], - black_cat_market_address, - ); + #[test_only] public fun valid_coin_types_test_only( + market_address: address, + ): bool { + valid_coin_types(market_address) } - #[test] - fun test_hard_coded_emoji_market_addresses() acquires Registry, RegistryAddress { - init_module_for_testing(); - let registry_ref_mut = borrow_registry_ref_mut(); - let (yellow_heart_market_address, _) = create_market(registry_ref_mut, YELLOW_HEART); - let (black_heart_market_address, _) = create_market(registry_ref_mut, BLACK_HEART); - let (black_cat_market_address, _) = create_market(registry_ref_mut, BLACK_CAT); - // If this test fails, it's because we've changed either the way we create the - // registry object address or the market object address, and the hard-coded - // emoji market addresses need to be recalculated and updated. - assert!(yellow_heart_market_address == @yellow_heart_market, 0); - assert!(black_heart_market_address == @black_heart_market, 0); - assert!(black_cat_market_address == @black_cat_market, 0); - } - - #[test] - fun test_coin_names_and_symbols() acquires Market, Registry, RegistryAddress { - use std::string::{utf8}; - aptos_account::create_account(@emojicoin_dot_fun); - init_module_for_testing(); - create_market_and_init_coins(YELLOW_HEART); - create_market_and_init_coins(BLACK_HEART); - create_market_and_init_coins(BLACK_CAT); - - // Test the names and symbols for regular emoji coins. - let symbol_1 = utf8(YELLOW_HEART); - let symbol_2 = utf8(BLACK_HEART); - let symbol_3 = utf8(BLACK_CAT); - let name_1 = get_concatenation(symbol_1, utf8(EMOJICOIN_NAME_SUFFIX)); - let name_2 = get_concatenation(symbol_2, utf8(EMOJICOIN_NAME_SUFFIX)); - let name_3 = get_concatenation(symbol_3, utf8(EMOJICOIN_NAME_SUFFIX)); - assert!(coin::symbol() == symbol_1, 0); - assert!(coin::symbol() == symbol_2, 0); - assert!(coin::symbol() == symbol_3, 0); - assert!(coin::name() == name_1, 0); - assert!(coin::name() == name_2, 0); - assert!(coin::name() == name_3, 0); - - // Test the names and symbols for LP coins. - let lp_symbol_1 = get_concatenation(utf8(EMOJICOIN_LP_SYMBOL_PREFIX), utf8(b"1")); - let lp_symbol_2 = get_concatenation(utf8(EMOJICOIN_LP_SYMBOL_PREFIX), utf8(b"2")); - let lp_symbol_3 = get_concatenation(utf8(EMOJICOIN_LP_SYMBOL_PREFIX), utf8(b"3")); - let lp_name_1 = get_concatenation(symbol_1, utf8(EMOJICOIN_LP_NAME_SUFFIX)); - let lp_name_2 = get_concatenation(symbol_2, utf8(EMOJICOIN_LP_NAME_SUFFIX)); - let lp_name_3 = get_concatenation(symbol_3, utf8(EMOJICOIN_LP_NAME_SUFFIX)); - assert!(utf8(b"LP-1") == lp_symbol_1, 0); - assert!(utf8(b"LP-2") == lp_symbol_2, 0); - assert!(utf8(b"LP-3") == lp_symbol_3, 0); - assert!(coin::symbol() == lp_symbol_1, 0); - assert!(coin::symbol() == lp_symbol_2, 0); - assert!(coin::symbol() == lp_symbol_3, 0); - assert!(coin::name() == lp_name_1, 0); - assert!(coin::name() == lp_name_2, 0); - assert!(coin::name() == lp_name_3, 0); - } } diff --git a/src/move/emojicoin_dot_fun/sources/hex_codes.move b/src/move/emojicoin_dot_fun/sources/hex_codes.move index 6bf9dea6d..714dff6f9 100644 --- a/src/move/emojicoin_dot_fun/sources/hex_codes.move +++ b/src/move/emojicoin_dot_fun/sources/hex_codes.move @@ -2313,7 +2313,7 @@ module emojicoin_dot_fun::hex_codes { ] } - public(friend) inline fun get_chat_emojis(): vector> { + public(friend) inline fun get_supplemental_chat_emojis(): vector> { vector [ x"f09fa791e2808df09f8ea8", // Artist [1F9D1 200D 1F3A8] x"f09fa791f09f8fbfe2808df09f8ea8", // Artist: dark skin tone [1F9D1 1F3FF 200D 1F3A8] @@ -3529,13 +3529,8 @@ module emojicoin_dot_fun::hex_codes { ] } - - inline fun get_split_metadata_bytes(): vector> { - vector[ - x"0b436f696e466163746f72790100000000000000004034323438464543373042333736393141444146454538433538363532333230434635363134384545424145334441383143463943314636353542384645324436fb011f8b08000000000002ff8550bb6ec3300cdcf51581f6c8ee5aa043d1d63f1118062d318e6a4914f47093bfafd4285d12209cf8b83b927700a502c688716468e95b4bd26e5294a66376bbb71d9f38b310564c5343b6263b28f4e8143aa9318a779f280e012cfe505847b6e85481a7947c7cedba529ef22c24d90e2a726f608e2d95145014006701b74ab2a09dc352c73c2b1d6aeb8ab4b46177bc2d69f4fffaeea6afdb3f9f9486ec46664882a972427477cf5ee9dbfe9921fd59b678e44c7f9e5b543d0f72850547e6ca8d75fc51c4069089c285b3ec97000a274f46cb4b1d177f3c243d1be46cc31035fdad7c11bde839fb05ce4e380dac010000010c636f696e5f666163746f72790000000400000000000000000000000000000000000000000000000000000000000000010e4170746f734672616d65776f726b00000000000000000000000000000000000000000000000000000000000000010b4170746f735374646c696200000000000000000000000000000000000000000000000000000000000000010a4d6f76655374646c6962", - // Interpolate the BCS encoding of address @emojicoin_dot_fun here. - x"0f456d6f6a69636f696e446f7446756e00", - ] + inline fun get_metadata_bytes(): vector { + x"1a456d6f6a69636f696e446f7446756e436f696e466163746f72790200000000000000004044444231324644444631333830363943363442343332433944354131333034424531363033373839414538323637373330423244393939373942363744363733d2011f8b08000000000002ff3d8fc14ec3301044effe8acaf73ae58ac40101f9892a8a36f6365d1a7bad5d3b88bfc7a685dbcce88d76e70c2108aaa24ec633a5f902beb07c1f5e0e76b6c69c03eec7801953c0e409d5bde6c23a0a44fc62b94d66a5d2e16b29599f87a1d96b5d9ce73840278f1b2cfa909e055d03ac11dc7b2902a584cd6b5d02498fee64e41d87cbdf9147fddff7bf32f81bac3899d4c25efc88fc497dc23b97b1a6b7a6c6fb166b6a5e0502ce9937f2bfdb28c65a60d9d09a1d4589534f9fdcc99dacf9019ac891a815010000010c636f696e5f666163746f72790000000000" } inline fun get_split_module_bytes(): vector> { @@ -3546,40 +3541,34 @@ module emojicoin_dot_fun::hex_codes { ] } - public(friend) inline fun get_publish_code(publisher_addr: address): (vector, vector) { - // Interpolate into metadata. - let metadata_bytes = get_split_metadata_bytes(); - let replaced_metadata = vector[]; - vector::reverse(&mut metadata_bytes); - vector::append(&mut replaced_metadata, vector::pop_back(&mut metadata_bytes)); - vector::append(&mut replaced_metadata, bcs::to_bytes(&@emojicoin_dot_fun)); - vector::append(&mut replaced_metadata, vector::pop_back(&mut metadata_bytes)); + public(friend) inline fun get_publish_code(market_address: address): (vector, vector) { + // Interpolate market address into module bytecode. + let split_bytecode = get_split_module_bytes(); + let epilogue = vector::pop_back(&mut split_bytecode); + let module_bytecode = vector::pop_back(&mut split_bytecode); + vector::append(&mut module_bytecode, bcs::to_bytes(&market_address)); + vector::append(&mut module_bytecode, epilogue); - // Interpolate into module bytecode. - let module_bytecode = get_split_module_bytes(); - let replaced_bytecode = vector[]; - vector::reverse(&mut module_bytecode); - vector::append(&mut replaced_bytecode, vector::pop_back(&mut module_bytecode)); - vector::append(&mut replaced_bytecode, bcs::to_bytes(&publisher_addr)); - vector::append(&mut replaced_bytecode, vector::pop_back(&mut module_bytecode)); + (get_metadata_bytes(), module_bytecode) + } - (replaced_metadata, replaced_bytecode) + #[test_only] public fun get_metadata_bytes_test_only(): vector { + get_metadata_bytes() } - #[test] - fun test_get_publish_code() { - let publisher_address = @0x0000012345789abcdef012345789abcdef012345789abcdef012345789abcdef; - let (metadata_bytecode, module_bytecode) = get_publish_code(publisher_address); - assert!(@emojicoin_dot_fun == @0xc0de, 0); + #[test_only] public fun get_split_module_bytes_test_only(): vector> { + get_split_module_bytes() + } - let test_metadata_bytecode = *vector::borrow(&get_split_metadata_bytes(), 0); - vector::append(&mut test_metadata_bytecode, bcs::to_bytes(&@0xc0de)); - vector::append(&mut test_metadata_bytecode, *vector::borrow(&get_split_metadata_bytes(), 1)); + #[test_only] public fun get_coin_symbol_emojis_test_only(): vector> { + get_coin_symbol_emojis() + } - let test_module_bytecode = *vector::borrow(&get_split_module_bytes(), 0); - vector::append(&mut test_module_bytecode, bcs::to_bytes(&publisher_address)); - vector::append(&mut test_module_bytecode, *vector::borrow(&get_split_module_bytes(), 1)); - assert!(metadata_bytecode == test_metadata_bytecode, 1); - assert!(module_bytecode == test_module_bytecode, 2); + #[test_only] public fun get_publish_code_test_only(market_address: address): ( + vector, + vector, + ) { + get_publish_code(market_address) } + } diff --git a/src/move/emojicoin_dot_fun/sources/test_acquisitions.move b/src/move/emojicoin_dot_fun/sources/test_acquisitions.move new file mode 100644 index 000000000..b36a1e840 --- /dev/null +++ b/src/move/emojicoin_dot_fun/sources/test_acquisitions.move @@ -0,0 +1,28 @@ +// Test utilities that rely on the `acquires` keyword, isolated here so that tests don't have to be +// annotated with `acquires` themselves. +#[test_only] module emojicoin_dot_fun::test_acquisitions { + + use aptos_framework::account::{create_signer_for_test as get_signer}; + use aptos_framework::aptos_account; + use aptos_framework::aptos_coin::{Self, AptosCoin}; + use aptos_framework::coin::{Self, BurnCapability, Coin, MintCapability}; + + struct AptosCoinCapStore has key { + burn_cap: BurnCapability, + mint_cap: MintCapability, + } + + public fun mint_aptos_coin(amount: u64): Coin acquires AptosCoinCapStore { + if (!exists(@aptos_framework)) { + let framework_signer = get_signer(@aptos_framework); + let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(&framework_signer); + move_to(&framework_signer, AptosCoinCapStore { burn_cap, mint_cap }); + }; + coin::mint(amount, &borrow_global(@aptos_framework).mint_cap) + } + + public fun mint_aptos_coin_to(recipient: address, amount: u64) acquires AptosCoinCapStore { + aptos_account::deposit_coins(recipient, mint_aptos_coin(amount)) + } + +} diff --git a/src/move/emojicoin_dot_fun/sources/tests.move b/src/move/emojicoin_dot_fun/sources/tests.move new file mode 100644 index 000000000..4727a6e6c --- /dev/null +++ b/src/move/emojicoin_dot_fun/sources/tests.move @@ -0,0 +1,546 @@ +#[test_only] module emojicoin_dot_fun::tests { + + use aptos_framework::account::{create_signer_for_test as get_signer}; + use aptos_framework::aptos_account; + use aptos_framework::coin; + use aptos_framework::event; + use aptos_framework::timestamp; + use aptos_std::string_utils; + use black_cat_market::coin_factory::{ + Emojicoin as BlackCatEmojicoin, + EmojicoinLP as BlackCatEmojicoinLP, + }; + use black_heart_market::coin_factory::{ + Emojicoin as BlackHeartEmojicoin, + EmojicoinLP as BlackHeartEmojicoinLP, + }; + use coin_factory::coin_factory::{ + Emojicoin as CoinFactoryEmojicoin, + EmojicoinLP as CoinFactoryEmojicoinLP, + }; + use emojicoin_dot_fun::emojicoin_dot_fun::{ + Chat, + Self, + MarketMetadata, + assert_valid_coin_types_test_only as assert_valid_coin_types, + chat, + cpamm_simple_swap_output_amount_test_only as cpamm_simple_swap_output_amount, + exists_lp_coin_capabilities, + get_BASE_REAL_CEILING, + get_BASE_VIRTUAL_CEILING, + get_BASE_VIRTUAL_FLOOR, + get_COIN_FACTORY_AS_BYTES, + get_EMOJICOIN_STRUCT_NAME, + get_EMOJICOIN_LP_NAME_SUFFIX, + get_EMOJICOIN_LP_STRUCT_NAME, + get_EMOJICOIN_LP_SYMBOL_PREFIX, + get_EMOJICOIN_NAME_SUFFIX, + get_MAX_CHAT_MESSAGE_LENGTH, + get_MAX_SYMBOL_LENGTH, + get_MARKET_REGISTRATION_FEE, + get_MICROSECONDS_PER_SECOND, + get_PERIOD_1M, + get_PERIOD_5M, + get_PERIOD_15M, + get_PERIOD_30M, + get_PERIOD_1H, + get_PERIOD_4H, + get_PERIOD_1D, + get_QUOTE_REAL_CEILING, + get_QUOTE_VIRTUAL_CEILING, + get_QUOTE_VIRTUAL_FLOOR, + get_concatenation_test_only as get_concatenation, + verified_symbol_emoji_bytes, + init_module_test_only as init_module, + is_a_supported_chat_emoji, + is_a_supported_symbol_emoji, + market_metadata_by_emoji_bytes, + pack_reserves, + register_market, + register_market_without_publish, + swap, + unpack_market_metadata, + unpack_chat, + valid_coin_types_test_only as valid_coin_types, + }; + use emojicoin_dot_fun::hex_codes::{ + get_metadata_bytes_test_only as get_metadata_bytes, + get_split_module_bytes_test_only as get_split_module_bytes, + get_coin_symbol_emojis_test_only as get_coin_symbol_emojis, + get_publish_code_test_only as get_publish_code, + }; + use emojicoin_dot_fun::test_acquisitions::{ + mint_aptos_coin_to, + }; + use yellow_heart_market::coin_factory::{ + Emojicoin as YellowHeartEmojicoin, + EmojicoinLP as YellowHeartEmojicoinLP, + BadType, + }; + use std::bcs; + use std::option; + use std::string::{Self, String, utf8}; + use std::type_info; + use std::vector; + + struct TestChat has copy, drop, store { + market_metadata: MarketMetadata, + emit_time: u64, + emit_market_nonce: u64, + user: address, + message: String, + user_emojicoin_balance: u64, + circulating_supply: u64, + balance_as_fraction_of_circulating_supply_q64: u128, + } + + // Test market emoji bytes. + const BLACK_CAT: vector = x"f09f9088e2808de2ac9b"; + const BLACK_HEART: vector = x"f09f96a4"; + const YELLOW_HEART: vector = x"f09f929b"; + + const SWAP_BUY: bool = false; + const SWAP_SELL: bool = true; + + const USER: address = @0xaaaaa; + const INTEGRATOR: address = @0xbbbbb; + + public fun assert_chat( + test_chat: TestChat, + chat: Chat, + ) { + let ( + market_metadata, + emit_time, + emit_market_nonce, + user, + message, + user_emojicoin_balance, + circulating_supply, + balance_as_fraction_of_circulating_supply_q64, + ) = unpack_chat(chat); + assert!(market_metadata == test_chat.market_metadata, 0); + assert!(emit_time == test_chat.emit_time, 0); + assert!(emit_market_nonce == test_chat.emit_market_nonce, 0); + assert!(user == test_chat.user, 0); + assert!(message == test_chat.message, 0); + assert!(user_emojicoin_balance == test_chat.user_emojicoin_balance, 0); + assert!(circulating_supply == test_chat.circulating_supply, 0); + assert!( + balance_as_fraction_of_circulating_supply_q64 == + test_chat.balance_as_fraction_of_circulating_supply_q64, + 0 + ); + } + + public fun assert_test_market_address( + emoji_bytes: vector>, + hard_coded_address: address, + publish_code: bool, + ) { + mint_aptos_coin_to(USER, get_MARKET_REGISTRATION_FEE()); + if (publish_code) { // Only one publication operation allowed per transaction. + register_market(&get_signer(USER), emoji_bytes, INTEGRATOR); + } else { + register_market_without_publish(&get_signer(USER), emoji_bytes, INTEGRATOR); + }; + let derived_market_address = address_for_registered_market_by_emoji_bytes(emoji_bytes); + assert!(derived_market_address == hard_coded_address, 0); + } + + public fun assert_coin_name_and_symbol( + emoji_bytes: vector>, + expected_lp_symbol: vector, + ) { + init_market_and_coins_via_swap(emoji_bytes); + + // Test emojicoin name and symbol. + let symbol = utf8(verified_symbol_emoji_bytes(emoji_bytes)); + let name = get_concatenation(symbol, utf8(get_EMOJICOIN_NAME_SUFFIX())); + assert!(coin::symbol() == symbol, 0); + assert!(coin::name() == name, 0); + + // Test LP coin name and symbols. + let market_id = market_id_for_registered_market_by_emoji_bytes(emoji_bytes); + let lp_symbol = get_concatenation( + utf8(get_EMOJICOIN_LP_SYMBOL_PREFIX()), + string_utils::to_string(&market_id), + ); + assert!(utf8(expected_lp_symbol) == lp_symbol, 0); + let lp_name = get_concatenation(symbol, utf8(get_EMOJICOIN_LP_NAME_SUFFIX())); + assert!(coin::symbol() == lp_symbol, 0); + assert!(coin::name() == lp_name, 0); + } + + public fun address_for_registered_market_by_emoji_bytes( + emoji_bytes: vector>, + ): address { + let (_, market_address, _) = unpack_market_metadata( + metadata_for_registered_market_by_emoji_bytes(emoji_bytes) + ); + market_address + } + + public fun init_package() { + aptos_account::create_account(@emojicoin_dot_fun); + timestamp::set_time_has_started_for_testing(&get_signer(@aptos_framework)); + init_module(&get_signer(@emojicoin_dot_fun)); + } + + public fun init_market( + emoji_bytes: vector>, + ) { + mint_aptos_coin_to(USER, get_MARKET_REGISTRATION_FEE()); + register_market_without_publish(&get_signer(USER), emoji_bytes, INTEGRATOR); + } + + public fun init_market_and_coins_via_swap( + emoji_bytes: vector>, + ) { + init_market(emoji_bytes); + let input_amount = 100; + let integrator_fee_rate_bps = 0; + swap( + address_for_registered_market_by_emoji_bytes(emoji_bytes), + &get_signer(USER), + input_amount, + SWAP_BUY, + INTEGRATOR, + integrator_fee_rate_bps, + ); + } + + public fun market_id_for_registered_market_by_emoji_bytes( + emoji_bytes: vector>, + ): u64 { + let (market_id, _, _) = unpack_market_metadata( + metadata_for_registered_market_by_emoji_bytes(emoji_bytes) + ); + market_id + } + + public fun metadata_for_registered_market_by_emoji_bytes( + emoji_bytes: vector>, + ): MarketMetadata { + option::destroy_some( + market_metadata_by_emoji_bytes(verified_symbol_emoji_bytes(emoji_bytes)) + ) + } + + #[test] fun all_supported_emojis_under_10_bytes() { + let max_symbol_length = (get_MAX_SYMBOL_LENGTH() as u64); + vector::for_each(get_coin_symbol_emojis(), |bytes| { + let emoji_as_string = utf8(bytes); + assert!(string::length(&emoji_as_string) <= max_symbol_length, 0); + }); + } + + #[test, expected_failure( + abort_code = emojicoin_dot_fun::emojicoin_dot_fun::E_INVALID_COIN_TYPES, + location = emojicoin_dot_fun + )] fun assert_valid_coin_types_bad_types() { + init_package(); + init_market(vector[YELLOW_HEART]); + assert_valid_coin_types(@yellow_heart_market); + } + + #[test] fun chat_complex_emoji_sequences() { + init_package(); + let emojis = vector[ + x"f09fa791e2808df09f9a80", // Astronaut. + x"f09fa6b8f09f8fbee2808de29982efb88f", // Man superhero: medium-dark skin tone. + ]; + + // Verify neither supplemental chat emoji is supported before first market is registered. + assert!(!vector::all(&emojis, |emoji_ref| { is_a_supported_chat_emoji(*emoji_ref) }), 0); + + // Register a market, verify both emojis supported in chat. + init_market(vector[BLACK_CAT]); + assert!(vector::all(&emojis, |emoji_ref| { is_a_supported_chat_emoji(*emoji_ref) }), 0); + chat( + &get_signer(USER), + emojis, + vector[1, 0], + @black_cat_market, + ); + + // Chat again with a longer message. + chat( + &get_signer(USER), + vector> [ + x"f09f98b6", // Cat face. + x"f09f98b7", // Cat face with tears of joy. + x"f09f98b8", // Cat face with wry smile. + x"f09f9088e2808de2ac9b", // Black cat. + x"f09f9294", // Broken heart. + ], + vector[ 3, 0, 2, 2, 1, 4 ], + @black_cat_market, + ); + + // Post a max length chat message. + let emoji_indices_sequence = vector[]; + for (i in 0..get_MAX_CHAT_MESSAGE_LENGTH()) { + vector::push_back(&mut emoji_indices_sequence, 0); + }; + chat( + &get_signer(USER), + vector> [ + x"f09f9088e2808de2ac9b", // Black cat. + ], + emoji_indices_sequence, + @black_cat_market, + ); + + // Assert the emitted chat events. + let events_emitted = event::emitted_events(); + assert_chat( + TestChat { + market_metadata: metadata_for_registered_market_by_emoji_bytes(vector[BLACK_CAT]), + emit_time: 0, + emit_market_nonce: 2, + user: USER, + message: utf8(x"f09fa6b8f09f8fbee2808de29982efb88ff09fa791e2808df09f9a80"), + user_emojicoin_balance: 0, + circulating_supply: 0, + balance_as_fraction_of_circulating_supply_q64: 0, + }, + *vector::borrow(&events_emitted, 0), + ); + assert_chat( + TestChat { + market_metadata: metadata_for_registered_market_by_emoji_bytes(vector[BLACK_CAT]), + emit_time: 0, + emit_market_nonce: 3, + user: USER, + message: utf8(x"f09f9088e2808de2ac9bf09f98b6f09f98b8f09f98b8f09f98b7f09f9294"), + user_emojicoin_balance: 0, + circulating_supply: 0, + balance_as_fraction_of_circulating_supply_q64: 0, + }, + *vector::borrow(&events_emitted, 1), + ); + } + + #[test, expected_failure( + abort_code = emojicoin_dot_fun::emojicoin_dot_fun::E_NOT_SUPPORTED_CHAT_EMOJI, + location = emojicoin_dot_fun + )] fun chat_message_invalid_emoji() { + init_package(); + init_market(vector[BLACK_CAT]); + + chat( + &get_signer(USER), + vector> [ + x"f09f98b7", // Cat face with tears of joy. + x"f09f", // Invalid emoji. + ], + vector[ 0, 1], + @black_cat_market, + ); + } + + #[test, expected_failure( + abort_code = emojicoin_dot_fun::emojicoin_dot_fun::E_CHAT_MESSAGE_TOO_LONG, + location = emojicoin_dot_fun + )] fun chat_message_too_long() { + init_package(); + init_market(vector[BLACK_CAT]); + + // Try to send a chat message that is one emoji too long. + let emojis = vector[ + x"f09f8dba", // Beer mug. + ]; + let emoji_indices_sequence = vector[]; + for (i in 0..(get_MAX_CHAT_MESSAGE_LENGTH() + 1)) { + vector::push_back(&mut emoji_indices_sequence, 0); + }; + chat( + &get_signer(USER), + emojis, + emoji_indices_sequence, + @black_cat_market, + ); + } + + #[test] fun concatenation() { + let base = utf8(b"base"); + let additional = utf8(b" additional"); + let concatenated = get_concatenation(base, additional); + assert!(concatenated == utf8(b"base additional"), 0); + // Ensure the base string was not mutated. + assert!(base == utf8(b"base"), 0); + } + + #[test] fun coin_factory_type_info() { + let module_name = get_COIN_FACTORY_AS_BYTES(); + let emojicoin_struct = get_EMOJICOIN_STRUCT_NAME(); + let emojicoin_lp_struct = get_EMOJICOIN_LP_STRUCT_NAME(); + + let emojicoin_type_info = type_info::type_of(); + let lp_type_info = type_info::type_of(); + + assert!(@coin_factory == type_info::account_address(&emojicoin_type_info), 0); + assert!(@coin_factory == type_info::account_address(&lp_type_info), 0); + assert!(module_name == type_info::module_name(&emojicoin_type_info), 0); + assert!(module_name == type_info::module_name(&lp_type_info), 0); + assert!(emojicoin_struct == type_info::struct_name(&emojicoin_type_info), 0); + assert!(emojicoin_lp_struct == type_info::struct_name(&lp_type_info), 0); + } + + #[test] fun coin_names_and_symbols() { + init_package(); + + assert_coin_name_and_symbol( + vector[BLACK_CAT], + b"LP-1", + ); + assert_coin_name_and_symbol( + vector[BLACK_HEART], + b"LP-2", + ); + assert_coin_name_and_symbol( + vector[YELLOW_HEART], + b"LP-3", + ); + } + + #[test] fun cpamm_simple_swap_output_amount_buy_sell_all() { + // Buy all base from start of bonding curve. + let reserves = pack_reserves(get_BASE_VIRTUAL_CEILING(), get_QUOTE_VIRTUAL_FLOOR()); + let output = cpamm_simple_swap_output_amount(get_QUOTE_REAL_CEILING(), false, reserves); + assert!(output == get_BASE_REAL_CEILING(), 0); + // Sell all base to a bonding curve that is theoretically complete but has not transitioned. + reserves = pack_reserves(get_BASE_VIRTUAL_FLOOR(), get_QUOTE_VIRTUAL_CEILING()); + output = cpamm_simple_swap_output_amount(get_BASE_REAL_CEILING(), true, reserves); + assert!(output == get_QUOTE_REAL_CEILING(), 0); + } + + #[test, expected_failure( + abort_code = emojicoin_dot_fun::emojicoin_dot_fun::E_SWAP_DIVIDE_BY_ZERO, + location = emojicoin_dot_fun + )] fun cpamm_simple_swap_output_amount_divide_by_zero() { + cpamm_simple_swap_output_amount(0, true, pack_reserves(0, 16)); + } + + // Verify hard-coded test market addresses, derived from `@emojicoin_dot_fun` dev address. + #[test] fun derived_test_market_addresses() { + init_package(); + assert_test_market_address(vector[BLACK_CAT], @black_cat_market, true); + assert_test_market_address(vector[BLACK_HEART], @black_heart_market, false); + assert_test_market_address(vector[YELLOW_HEART], @yellow_heart_market, false); + } + + #[test] fun get_publish_code_expected() { + let market_address = @0xabcdef0123456789; + let expected_metadata_bytecode = get_metadata_bytes(); + + // Manually construct expected module bytecode. + let split_module_bytes = get_split_module_bytes(); + let expected_module_bytecode = *vector::borrow(&split_module_bytes, 0); + vector::append(&mut expected_module_bytecode, bcs::to_bytes(&market_address)); + vector::append(&mut expected_module_bytecode, *vector::borrow(&split_module_bytes, 1)); + + // Compare expected vs actual bytecode. + let (metadata_bytecode, module_bytecode) = get_publish_code(market_address); + assert!(metadata_bytecode == expected_metadata_bytecode, 0); + assert!(module_bytecode == expected_module_bytecode, 0); + } + + #[test] fun period_times() { + let ms_per_s = get_MICROSECONDS_PER_SECOND(); + assert!(get_PERIOD_1M() == 60 * ms_per_s, 0); + assert!(get_PERIOD_5M() == 5 * 60 * ms_per_s, 0); + assert!(get_PERIOD_15M() == 15 * 60 * ms_per_s, 0); + assert!(get_PERIOD_30M() == 30 * 60 * ms_per_s, 0); + assert!(get_PERIOD_1H() == 60 * 60 * ms_per_s, 0); + assert!(get_PERIOD_4H() == 4 * 60 * 60 * ms_per_s, 0); + assert!(get_PERIOD_1D() == 24 * 60 * 60 * ms_per_s, 0); + } + + #[test] fun register_market_with_compound_emoji_sequence() { + init_package(); + let emojis = vector[ + x"e29aa1", // High voltage. + x"f09f96a5efb88f", // Desktop computer. + ]; + let concatenated_bytes = verified_symbol_emoji_bytes(emojis); + + // Verify market is not already registered, register, then verify is registered. + assert!(market_metadata_by_emoji_bytes(concatenated_bytes) == option::none(), 0); + init_market(emojis); + let market_metadata = + option::destroy_some(market_metadata_by_emoji_bytes(concatenated_bytes)); + let (_, _, market_metadata_byes) = unpack_market_metadata(market_metadata); + assert!(market_metadata_byes == concatenated_bytes, 0); + } + + #[test] fun supported_symbol_emojis() { + init_package(); + let various_emojis = vector> [ + x"f09f868e", // AB button blood type, 1F18E. + x"f09fa6bbf09f8fbe", // Ear with hearing aid medium dark skin tone, 1F9BB 1F3FE. + x"f09f87a7f09f87b9", // Flag Bhutan, 1F1E7 1F1F9. + x"f09f9190f09f8fbe", // Open hands medium dark skin tone, 1F450 1F3FE. + x"f09fa4b0f09f8fbc", // Pregnant woman medium light skin tone, 1F930 1F3FC. + x"f09f9faa", // Purple square, 1F7EA. + x"f09f91abf09f8fbe", // Woman and man holding hands medium dark skin tone, 1F46B 1F3FE. + x"f09f91a9f09f8fbe", // Woman medium dark skin tone, 1F469 1F3FE. + x"f09fa795f09f8fbd", // Woman with headscarf medium skin tone, 1F9D5 1F3FD. + x"f09fa490", // Zipper mouth face, 1F910. + ]; + vector::for_each(various_emojis, |bytes| { + assert!(is_a_supported_symbol_emoji(bytes), 0); + }); + + // Test unsupported emojis. + assert!(!is_a_supported_symbol_emoji(x"0000"), 0); + assert!(!is_a_supported_symbol_emoji(x"fe0f"), 0); + assert!(!is_a_supported_symbol_emoji(x"1234"), 0); + assert!(!is_a_supported_symbol_emoji(x"f0fabcdefabcdeff0f"), 0); + assert!(!is_a_supported_symbol_emoji(x"f0f00dcafef0"), 0); + // Minimally qualified "head shaking horizontally". + assert!(!is_a_supported_symbol_emoji(x"f09f9982e2808de28694"), 0); + + // Verify a supported emoji, add invalid data to it, then verify it is no longer allowed. + assert!(is_a_supported_symbol_emoji(x"e29d97"), 0); + assert!(!is_a_supported_symbol_emoji(x"e29d97ff"), 0); + assert!(!is_a_supported_symbol_emoji(x"ffe29d97"), 0); + } + + #[test] fun swap_initializes_coin_capabilities() { + init_package(); + let emoji_bytes = vector[YELLOW_HEART]; + init_market_and_coins_via_swap(emoji_bytes); + assert!( + exists_lp_coin_capabilities( + @yellow_heart_market + ), + 0, + ); + } + + #[test] fun valid_coin_types_all_invalid() { + // Duplicate types should be invalid. + assert!(!valid_coin_types(@emojicoin_dot_fun), 0); + // Duplicate LP types should be invalid. + assert!(!valid_coin_types(@emojicoin_dot_fun), 0); + // A bad Emojicoin type should be invalid. + assert!(!valid_coin_types(@emojicoin_dot_fun), 0); + // A bad EmojicoinLP type should be invalid. + assert!(!valid_coin_types(@emojicoin_dot_fun), 0); + // Backwards coin types that are otherwise valid should be invalid. + assert!(!valid_coin_types(@emojicoin_dot_fun), 0); + // A market address that doesn't match the types should be invalid. + assert!(!valid_coin_types(@0xc1de), 0); + } + + #[test] fun valid_coin_types_all_valid() { + assert!( + valid_coin_types(@yellow_heart_market) && + valid_coin_types(@black_heart_market) && + valid_coin_types(@black_cat_market), + 0 + ); + } +} diff --git a/src/move/test_coin_factories/black_cat/Move.toml b/src/move/test_coin_factories/black_cat/Move.toml index 87c7d1965..4bb8fd4ca 100644 --- a/src/move/test_coin_factories/black_cat/Move.toml +++ b/src/move/test_coin_factories/black_cat/Move.toml @@ -8,5 +8,5 @@ subdir = "aptos-move/framework/aptos-framework" [package] name = "BlackCatCoinFactory" -upgrade_policy = "compatible" +upgrade_policy = "immutable" version = "1.0.0" diff --git a/src/move/test_coin_factories/black_heart/Move.toml b/src/move/test_coin_factories/black_heart/Move.toml index 6fbc448b2..eb2fe6c1a 100644 --- a/src/move/test_coin_factories/black_heart/Move.toml +++ b/src/move/test_coin_factories/black_heart/Move.toml @@ -8,5 +8,5 @@ subdir = "aptos-move/framework/aptos-framework" [package] name = "BlackHeartCoinFactory" -upgrade_policy = "compatible" +upgrade_policy = "immutable" version = "1.0.0" diff --git a/src/move/test_coin_factories/yellow_heart/Move.toml b/src/move/test_coin_factories/yellow_heart/Move.toml index 6f3916f9d..b7c2f8f8d 100644 --- a/src/move/test_coin_factories/yellow_heart/Move.toml +++ b/src/move/test_coin_factories/yellow_heart/Move.toml @@ -8,5 +8,5 @@ subdir = "aptos-move/framework/aptos-framework" [package] name = "YellowHeartCoinFactory" -upgrade_policy = "compatible" +upgrade_policy = "immutable" version = "1.0.0"