From 95de1714c2abfcd6c9136b73047fa27aa3d93710 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:51:03 -0800 Subject: [PATCH 01/64] Add rewards escrow --- src/move/rewards/Move.toml | 2 +- .../sources/emojicoin_dot_fun_rewards.move | 53 +++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/move/rewards/Move.toml b/src/move/rewards/Move.toml index 08e52d06e..3183a0dc3 100644 --- a/src/move/rewards/Move.toml +++ b/src/move/rewards/Move.toml @@ -18,4 +18,4 @@ local = "../test_coin_factories/black_heart" authors = ["Econia Labs (developers@econialabs.com)"] name = "EmojicoinDotFunRewards" upgrade_policy = "compatible" -version = "1.1.0" +version = "1.2.0" diff --git a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move index 4334c1e9e..48785c1ec 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move @@ -5,7 +5,7 @@ module rewards::emojicoin_dot_fun_rewards { use aptos_framework::account::{Self, SignerCapability}; use aptos_framework::aptos_account; use aptos_framework::aptos_coin::AptosCoin; - use aptos_framework::coin; + use aptos_framework::coin::{Self, Coin}; use aptos_framework::event; use aptos_framework::randomness; use emojicoin_dot_fun::emojicoin_dot_fun::{Self, Swap}; @@ -49,6 +49,10 @@ module rewards::emojicoin_dot_fun_rewards { /// Reward value in APT per reward per tier. const APT_REWARD_AMOUNTS_PER_TIER: vector = vector[1, 2, 5, 10, 100, 500]; + struct EmojicoinDotFunRewardsEscrow has key { + aptos_coin: Coin + } + struct RewardTier has drop, store { apt_amount_per_reward: u64, n_rewards_disbursed: u64, @@ -67,6 +71,12 @@ module rewards::emojicoin_dot_fun_rewards { octas_reward_amount: u64 } + #[event] + struct EmojicoinDotFunRewardsRedemption has copy, drop, store { + swap: Swap, + octas_reward_amount: u64 + } + #[randomness] entry fun swap_with_rewards( swapper: &signer, @@ -74,10 +84,37 @@ module rewards::emojicoin_dot_fun_rewards { input_amount: u64, is_sell: bool, min_output_amount: u64 - ) acquires Vault { + ) acquires EmojicoinDotFunRewardsEscrow, Vault { - // Simulate swap to get integrator fee, then execute swap. + // If a swap buy, first exhaust funds from the rewards escrow if it exists. let swapper_address = signer::address_of(swapper); + if (!is_sell && exists(swapper_address)) { + let EmojicoinDotFunRewardsEscrow { aptos_coin } = + move_from(swapper_address); + let octas_reward_amount = coin::value(&aptos_coin); + aptos_account::deposit_coins(swapper_address, aptos_coin); + let swap = emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address, + octas_reward_amount, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS + ); + emojicoin_dot_fun::swap( + swapper, + market_address, + octas_reward_amount, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS, + 1 + ); + event::emit(EmojicoinDotFunRewardsRedemption { swap, octas_reward_amount }); + if (input_amount == 0) return; // Then return if user only wanted to redeem escrow. + }; + + // Simulate swap to get integrator fee, then execute swap. let swap = emojicoin_dot_fun::simulate_swap( swapper_address, @@ -153,7 +190,15 @@ module rewards::emojicoin_dot_fun_rewards { let vault_signer = account::create_signer_with_capability(vault_signer_cap_ref); event::emit(EmojicoinDotFunRewards { swap, octas_reward_amount }); - aptos_account::transfer(&vault_signer, swapper_address, octas_reward_amount); + + // Top off escrow if it exists, otherwise create it. + let aptos_coin = if (exists(swapper_address)) { + let EmojicoinDotFunRewardsEscrow { aptos_coin } = + move_from(swapper_address); + aptos_coin + } else coin::zero(); + coin::merge(&mut aptos_coin, coin::withdraw(&vault_signer, octas_reward_amount)); + move_to(swapper, EmojicoinDotFunRewardsEscrow { aptos_coin }); } } From 7e4c9c35cd0ac8c8e5f82f54323e398406860d77 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:58:33 -0800 Subject: [PATCH 02/64] Update README --- src/move/rewards/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/move/rewards/README.md b/src/move/rewards/README.md index 788c741ef..efa3d873e 100644 --- a/src/move/rewards/README.md +++ b/src/move/rewards/README.md @@ -1,8 +1,9 @@ # `emojicoin-dot-fun` rewards This package contains an overloaded version of the `swap` function for -`emojicoin-dot-fun`, `swap_with_rewards`, which gives users an opportunity for a -reward based on the amount of integrator fees they pay for their swap. +`emojicoin-dot-fun`, `emojioin_dot_fun_rewards::swap_with_rewards`, which gives +users an opportunity for a reward based on the amount of integrator fees they +pay for their swap. The rewards vault can be loaded up via the `fund_tiers` function. @@ -13,6 +14,10 @@ nominal volume amount. See `reward_tiers` for more. For ease of parameter modeling, named constants use values in `APT`, which are converted to octas internally. +The `emojicoin_dot_fun_claim_link` module contains a system for administering +pre-paid swaps using private keys encoded in "claim links", akin to magic links +for website login. + ## Publish commands Set variables: From dfcd42c6aca8ebfd624c194eb21e900fe6b14511 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:51:18 -0800 Subject: [PATCH 03/64] Add arena stub --- .../rewards/sources/emojicoin_dot_fun_arena.move | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/move/rewards/sources/emojicoin_dot_fun_arena.move diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move new file mode 100644 index 000000000..c21aa367a --- /dev/null +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -0,0 +1,13 @@ +module rewards::emojicoin_dot_fun_arena { + + use emojicoin_dot_fun::emojicoin_dot_fun::{Self, MarketView}; + use std::option::{Self, Option}; + + struct Melee { + markets: vector
, + start_time: u64, + n_participants: u64, + market_views_at_start: vector, + market_views_at_end: Option> + } +} From c3c52a3fbc07e91b58df9b22b28c5a36872af629 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:20:55 -0800 Subject: [PATCH 04/64] Add first melee init --- .../sources/emojicoin_dot_fun_arena.move | 186 +++++++++++++++++- .../sources/emojicoin_dot_fun_rewards.move | 32 +-- 2 files changed, 198 insertions(+), 20 deletions(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move index c21aa367a..842a55c64 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -1,13 +1,187 @@ module rewards::emojicoin_dot_fun_arena { - use emojicoin_dot_fun::emojicoin_dot_fun::{Self, MarketView}; + use aptos_framework::account::{Self, SignerCapability}; + use aptos_framework::aptos_coin::AptosCoin; + use aptos_framework::coin::{Self, Coin}; + use aptos_framework::randomness::Self; + use aptos_framework::timestamp; + use aptos_std::smart_table::{Self, SmartTable}; + use emojicoin_dot_fun::emojicoin_dot_fun::{ + Self, + MarketMetadata, + registry_view, + unpack_registry_view + }; use std::option::{Self, Option}; - struct Melee { - markets: vector
, + const REGISTRY_SEED: vector = b"Arena registry"; + const U64_MAX: u64 = 0xffffffffffffffff; + + const LOCK_IN_PERIOD_HOURS: u64 = 12; + const MELEE_DURATION_HOURS: u64 = 36; + const MICROSECONDS_PER_HOUR: u64 = 3_600_000_000; + + const REWARDS_PER_MELEE: u64 = 100_000_000 * 1250; + + struct Melee has copy, drop, store { + /// 1-indexed for conformity with market ID indexing. + melee_id: u64, + /// Market with lower market ID first. + market_metadatas: vector, + available_rewards: u64, + /// In microseconds. start_time: u64, - n_participants: u64, - market_views_at_start: vector, - market_views_at_end: Option> + lock_in_period: u64, + duration: u64 + } + + struct Nil has store {} + + struct MeleeEscrow has key { + melee_id: u64, + /// Indentical to `Melee.market_metadatas`. + market_metadatas: vector, + emojicoin_0: Coin, + emojicoin_1: Coin, + tap_out_fee: u64 + } + + struct UserMelees has key { + /// Set of serial IDs of all melees the user has entered. + melee_ids: SmartTable + } + + struct Registry has key { + /// Map from melee serial ID to the melee. + melees_by_id: SmartTable, + /// Map from a sorted combination of market IDs (lower ID first) to the melee serial ID. + melees_by_market_combo_sorted_market_ids: SmartTable, u64>, + /// Approves transfers from the vault. + signer_capability: SignerCapability + } + + #[randomness] + entry fun enter( + entrant: &signer, + melee_id: u64, + market_addresses: vector
, + buy_emojicoin_0: bool, + input_amount: u64, + lock_in: bool + ) {} + + #[randomness] + entry fun swap( + swapper: &signer, + melee_id: u64, + market_addresses: vector
, + buy_emojicoin_0: bool + ) {} + + #[randomness] + entry fun exit( + participant: &signer, melee_id: u64 + ) {} + + inline fun crank_schedule() { + let registry_ref_mut = &mut Registry[@rewards]; + let n_melees = registry_ref_mut.melees_by_id.length(); + } + + inline fun last_period_boundary(time: u64, period: u64): u64 { + (time / period) * period + } + + inline fun next_melee_market_ids(): vector { + let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = + unpack_registry_view(registry_view()); + let market_ids; + loop { + let random_market_id_0 = random_market_id(n_markets); + let random_market_id_1 = random_market_id(n_markets); + if (random_market_id_0 == random_market_id_1) continue; + market_ids = sort_unique_market_ids(random_market_id_0, random_market_id_1); + if (!Registry[@rewards].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { + break; + } + }; + market_ids + + } + + /// Market IDs are 1-indexed. + inline fun random_market_id(n_markets: u64): u64 { + randomness::u64_range(0, n_markets) + 1 + } + + inline fun sort_unique_market_ids(market_id_0: u64, market_id_1: u64): vector { + if (market_id_0 < market_id_1) { + vector[market_id_0, market_id_1] + } else { + vector[market_id_1, market_id_0] + } + } + + /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. + inline fun psuedo_random_u64(seed: u64): u64 { + seed = seed ^ (seed << 13); + seed = seed ^ (seed >> 7); + seed = seed ^ (seed << 17); + seed + } + + fun init_module(rewards: &signer) { + /// Get first melee tuple, without using randomness APIs. + let time = timestamp::now_microseconds(); + let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = + unpack_registry_view(registry_view()); + let pseudo_random_market_id_0 = time % n_markets + 1; + let pseudo_random_market_id_1; + loop { + pseudo_random_market_id_1 = psuedo_random_u64(time) % n_markets + 1; + if (pseudo_random_market_id_1 != pseudo_random_market_id_0) break; + }; + let market_ids = + sort_unique_market_ids( + pseudo_random_market_id_0, pseudo_random_market_id_1 + ); + + // Initialize first melee. + let start_time = + last_period_boundary(time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR); + let melees_by_id = smart_table::new(); + melees_by_id.add( + 1, + Melee { + melee_id: 1, + market_metadatas: vector[ + option::destroy_some( + emojicoin_dot_fun::market_metadata_by_market_id(market_ids[0]) + ), + option::destroy_some( + emojicoin_dot_fun::market_metadata_by_market_id(market_ids[1]) + ) + ], + available_rewards: REWARDS_PER_MELEE, + start_time, + lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, + duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR + } + ); + let melees_by_market_combo_sorted_market_ids = smart_table::new(); + melees_by_market_combo_sorted_market_ids.add(market_ids, 1); + + // Store registry resource. + let (vault_signer, signer_capability) = + account::create_resource_account(rewards, REGISTRY_SEED); + move_to( + rewards, + Registry { + melees_by_id, + melees_by_market_combo_sorted_market_ids, + signer_capability + } + ); + coin::register(&vault_signer); } } diff --git a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move index 48785c1ec..a82b81d80 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move @@ -93,14 +93,15 @@ module rewards::emojicoin_dot_fun_rewards { move_from(swapper_address); let octas_reward_amount = coin::value(&aptos_coin); aptos_account::deposit_coins(swapper_address, aptos_coin); - let swap = emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address, - octas_reward_amount, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS - ); + let swap = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address, + octas_reward_amount, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS + ); emojicoin_dot_fun::swap( swapper, market_address, @@ -192,12 +193,15 @@ module rewards::emojicoin_dot_fun_rewards { event::emit(EmojicoinDotFunRewards { swap, octas_reward_amount }); // Top off escrow if it exists, otherwise create it. - let aptos_coin = if (exists(swapper_address)) { - let EmojicoinDotFunRewardsEscrow { aptos_coin } = - move_from(swapper_address); - aptos_coin - } else coin::zero(); - coin::merge(&mut aptos_coin, coin::withdraw(&vault_signer, octas_reward_amount)); + let aptos_coin = + if (exists(swapper_address)) { + let EmojicoinDotFunRewardsEscrow { aptos_coin } = + move_from(swapper_address); + aptos_coin + } else coin::zero(); + coin::merge( + &mut aptos_coin, coin::withdraw(&vault_signer, octas_reward_amount) + ); move_to(swapper, EmojicoinDotFunRewardsEscrow { aptos_coin }); } } From a517ccca40c75136d0cd2217bfa6fefde13b6f7c Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:21:20 -0800 Subject: [PATCH 05/64] Revert tiered rewards changes --- .../sources/emojicoin_dot_fun_rewards.move | 57 ++----------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move index a82b81d80..4334c1e9e 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_rewards.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_rewards.move @@ -5,7 +5,7 @@ module rewards::emojicoin_dot_fun_rewards { use aptos_framework::account::{Self, SignerCapability}; use aptos_framework::aptos_account; use aptos_framework::aptos_coin::AptosCoin; - use aptos_framework::coin::{Self, Coin}; + use aptos_framework::coin; use aptos_framework::event; use aptos_framework::randomness; use emojicoin_dot_fun::emojicoin_dot_fun::{Self, Swap}; @@ -49,10 +49,6 @@ module rewards::emojicoin_dot_fun_rewards { /// Reward value in APT per reward per tier. const APT_REWARD_AMOUNTS_PER_TIER: vector = vector[1, 2, 5, 10, 100, 500]; - struct EmojicoinDotFunRewardsEscrow has key { - aptos_coin: Coin - } - struct RewardTier has drop, store { apt_amount_per_reward: u64, n_rewards_disbursed: u64, @@ -71,12 +67,6 @@ module rewards::emojicoin_dot_fun_rewards { octas_reward_amount: u64 } - #[event] - struct EmojicoinDotFunRewardsRedemption has copy, drop, store { - swap: Swap, - octas_reward_amount: u64 - } - #[randomness] entry fun swap_with_rewards( swapper: &signer, @@ -84,38 +74,10 @@ module rewards::emojicoin_dot_fun_rewards { input_amount: u64, is_sell: bool, min_output_amount: u64 - ) acquires EmojicoinDotFunRewardsEscrow, Vault { - - // If a swap buy, first exhaust funds from the rewards escrow if it exists. - let swapper_address = signer::address_of(swapper); - if (!is_sell && exists(swapper_address)) { - let EmojicoinDotFunRewardsEscrow { aptos_coin } = - move_from(swapper_address); - let octas_reward_amount = coin::value(&aptos_coin); - aptos_account::deposit_coins(swapper_address, aptos_coin); - let swap = - emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address, - octas_reward_amount, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS - ); - emojicoin_dot_fun::swap( - swapper, - market_address, - octas_reward_amount, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS, - 1 - ); - event::emit(EmojicoinDotFunRewardsRedemption { swap, octas_reward_amount }); - if (input_amount == 0) return; // Then return if user only wanted to redeem escrow. - }; + ) acquires Vault { // Simulate swap to get integrator fee, then execute swap. + let swapper_address = signer::address_of(swapper); let swap = emojicoin_dot_fun::simulate_swap( swapper_address, @@ -191,18 +153,7 @@ module rewards::emojicoin_dot_fun_rewards { let vault_signer = account::create_signer_with_capability(vault_signer_cap_ref); event::emit(EmojicoinDotFunRewards { swap, octas_reward_amount }); - - // Top off escrow if it exists, otherwise create it. - let aptos_coin = - if (exists(swapper_address)) { - let EmojicoinDotFunRewardsEscrow { aptos_coin } = - move_from(swapper_address); - aptos_coin - } else coin::zero(); - coin::merge( - &mut aptos_coin, coin::withdraw(&vault_signer, octas_reward_amount) - ); - move_to(swapper, EmojicoinDotFunRewardsEscrow { aptos_coin }); + aptos_account::transfer(&vault_signer, swapper_address, octas_reward_amount); } } From 4e91f778898c105d9d75e36d6de31e54fa04a98e Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:45:55 -0800 Subject: [PATCH 06/64] Add entry entry func --- .../sources/emojicoin_dot_fun_arena.move | 315 ++++++++++++++---- 1 file changed, 249 insertions(+), 66 deletions(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move index 842a55c64..123afcc5b 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -1,6 +1,7 @@ module rewards::emojicoin_dot_fun_arena { use aptos_framework::account::{Self, SignerCapability}; + use aptos_framework::aptos_account; use aptos_framework::aptos_coin::AptosCoin; use aptos_framework::coin::{Self, Coin}; use aptos_framework::randomness::Self; @@ -9,19 +10,29 @@ module rewards::emojicoin_dot_fun_arena { use emojicoin_dot_fun::emojicoin_dot_fun::{ Self, MarketMetadata, + market_view, registry_view, + unpack_market_metadata, unpack_registry_view }; use std::option::{Self, Option}; + use std::signer; + /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; + const U64_MAX: u64 = 0xffffffffffffffff; const LOCK_IN_PERIOD_HOURS: u64 = 12; const MELEE_DURATION_HOURS: u64 = 36; const MICROSECONDS_PER_HOUR: u64 = 3_600_000_000; - const REWARDS_PER_MELEE: u64 = 100_000_000 * 1250; + /// Flat integrator fee. + const INTEGRATOR_FEE_RATE_BPS: u8 = 100; + const REWARDS_PER_MELEE: u64 = 1500 * 100_000_000; + const MATCH_PERCENTAGE: u64 = 20; + const MAX_PERCENTAGE: u64 = 100; + const MAX_MATCH_AMOUNT: u64 = 100_000_000; struct Melee has copy, drop, store { /// 1-indexed for conformity with market ID indexing. @@ -46,11 +57,6 @@ module rewards::emojicoin_dot_fun_arena { tap_out_fee: u64 } - struct UserMelees has key { - /// Set of serial IDs of all melees the user has entered. - melee_ids: SmartTable - } - struct Registry has key { /// Map from melee serial ID to the melee. melees_by_id: SmartTable, @@ -60,78 +66,176 @@ module rewards::emojicoin_dot_fun_arena { signer_capability: SignerCapability } + struct UserMelees has key { + /// Set of serial IDs of all melees the user has entered. + melee_ids: SmartTable + } + #[randomness] entry fun enter( entrant: &signer, - melee_id: u64, - market_addresses: vector
, buy_emojicoin_0: bool, input_amount: u64, lock_in: bool - ) {} - - #[randomness] - entry fun swap( - swapper: &signer, - melee_id: u64, - market_addresses: vector
, - buy_emojicoin_0: bool - ) {} - - #[randomness] - entry fun exit( - participant: &signer, melee_id: u64 - ) {} + ) acquires MeleeEscrow, Registry, UserMelees { + if (crank_schedule()) return; // Can not enter melee if cranking ends it. - inline fun crank_schedule() { - let registry_ref_mut = &mut Registry[@rewards]; - let n_melees = registry_ref_mut.melees_by_id.length(); - } - - inline fun last_period_boundary(time: u64, period: u64): u64 { - (time / period) * period - } + // Verify coin types for the current melee by calling the market view function. + let current_melee_ref = borrow_current_melee_ref(); + let market_metadatas = current_melee_ref.market_metadatas; + let (_, market_address_0, _) = unpack_market_metadata(market_metadatas[0]); + market_view(market_address_0); + let (_, market_address_1, _) = unpack_market_metadata(market_metadatas[1]); + market_view(market_address_1); - inline fun next_melee_market_ids(): vector { - let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = - unpack_registry_view(registry_view()); - let market_ids; - loop { - let random_market_id_0 = random_market_id(n_markets); - let random_market_id_1 = random_market_id(n_markets); - if (random_market_id_0 == random_market_id_1) continue; - market_ids = sort_unique_market_ids(random_market_id_0, random_market_id_1); - if (!Registry[@rewards].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { - break; - } + // Create escrow and user melees resources if they don't exist. + let melee_id = current_melee_ref.melee_id; + let entrant_address = signer::address_of(entrant); + if (!exists>( + entrant_address + )) { + move_to( + entrant, + MeleeEscrow { + melee_id, + market_metadatas, + emojicoin_0: coin::zero(), + emojicoin_1: coin::zero(), + tap_out_fee: 0 + } + ); + if (!exists(entrant_address)) { + move_to(entrant, UserMelees { melee_ids: smart_table::new() }); + }; + let user_melee_ids_ref_mut = &mut UserMelees[entrant_address].melee_ids; + user_melee_ids_ref_mut.add(melee_id, Nil {}); }; - market_ids - } + // Try locking in if user selects the option. + let match_amount = + if (lock_in) { + let escrow_ref_mut = + &mut MeleeEscrow[entrant_address]; + let current_tap_out_fee = escrow_ref_mut.tap_out_fee; + let lock_in_period_end_time = + current_melee_ref.start_time + current_melee_ref.lock_in_period; + let lock_ins_still_allowed = + timestamp::now_microseconds() < lock_in_period_end_time; + if (current_tap_out_fee < MAX_MATCH_AMOUNT && lock_ins_still_allowed) { + let eligible_match_amount = MAX_MATCH_AMOUNT - current_tap_out_fee; + eligible_match_amount = if (eligible_match_amount + < current_melee_ref.available_rewards) { + eligible_match_amount + } else { + current_melee_ref.available_rewards + }; + let requested_match_amount = + ( + ((input_amount as u128) * (MATCH_PERCENTAGE as u128) + / (MAX_PERCENTAGE as u128)) as u64 + ); + let actual_match_amount = + if (eligible_match_amount < requested_match_amount) { + eligible_match_amount + } else { + requested_match_amount + }; + if (actual_match_amount > 0) { + escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + + actual_match_amount; + let registry_ref_mut = &mut Registry[@rewards]; + let available_rewards_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; + available_rewards_ref_mut = available_rewards_ref_mut + - actual_match_amount; + let vault_signer = + account::create_signer_with_capability( + ®istry_ref_mut.signer_capability + ); + aptos_account::transfer( + &vault_signer, entrant_address, actual_match_amount + ); + }; + actual_match_amount + } else { 0 } + } else { 0 }; - /// Market IDs are 1-indexed. - inline fun random_market_id(n_markets: u64): u64 { - randomness::u64_range(0, n_markets) + 1 - } - - inline fun sort_unique_market_ids(market_id_0: u64, market_id_1: u64): vector { - if (market_id_0 < market_id_1) { - vector[market_id_0, market_id_1] + // Execute a swap then immediately move funds into escrow. + let input_amount_after_matching = input_amount + match_amount; + let escrow_ref_mut = + &mut MeleeEscrow[entrant_address]; + if (buy_emojicoin_0) { + let swap = + emojicoin_dot_fun::simulate_swap( + entrant_address, + market_address_0, + input_amount_after_matching, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS + ); + let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(swap); + emojicoin_dot_fun::swap( + entrant, + market_address_0, + input_amount_after_matching, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS, + 1 + ); + coin::merge( + &mut escrow_ref_mut.emojicoin_0, coin::withdraw(entrant, net_proceeds) + ); } else { - vector[market_id_1, market_id_0] + let swap = + emojicoin_dot_fun::simulate_swap( + entrant_address, + market_address_1, + input_amount_after_matching, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS + ); + let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(swap); + emojicoin_dot_fun::swap( + entrant, + market_address_1, + input_amount_after_matching, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS, + 1 + ); + coin::merge( + &mut escrow_ref_mut.emojicoin_1, coin::withdraw(entrant, net_proceeds) + ); } } - /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. - inline fun psuedo_random_u64(seed: u64): u64 { - seed = seed ^ (seed << 13); - seed = seed ^ (seed >> 7); - seed = seed ^ (seed << 17); - seed + #[randomness] + entry fun exit( + participant: &signer, melee_id: u64 + ) acquires Registry { + let may_have_to_pay_tap_out_fee = !crank_schedule(); + // Ensure registry available rewards incremented correctly. + } + + #[randomness] + entry fun swap( + swapper: &signer, + melee_id: u64, + market_addresses: vector
, + buy_emojicoin_0: bool + ) acquires Registry { + if (crank_schedule()) {} + else {} } fun init_module(rewards: &signer) { - /// Get first melee tuple, without using randomness APIs. + // Get first melee market addresses, without using randomness APIs. let time = timestamp::now_microseconds(); let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = unpack_registry_view(registry_view()); @@ -154,14 +258,11 @@ module rewards::emojicoin_dot_fun_arena { 1, Melee { melee_id: 1, - market_metadatas: vector[ - option::destroy_some( - emojicoin_dot_fun::market_metadata_by_market_id(market_ids[0]) - ), + market_metadatas: market_ids.map_ref(|market_id_ref| { option::destroy_some( - emojicoin_dot_fun::market_metadata_by_market_id(market_ids[1]) + emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) ) - ], + }), available_rewards: REWARDS_PER_MELEE, start_time, lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, @@ -184,4 +285,86 @@ module rewards::emojicoin_dot_fun_arena { ); coin::register(&vault_signer); } + + /// Cranks schedule and returns `true` if a melee has ended as a result. + inline fun crank_schedule(): bool { + let time = timestamp::now_microseconds(); + let registry_ref = &Registry[@rewards]; + let n_melees = registry_ref.melees_by_id.length(); + let most_recent_melee_ref = registry_ref.melees_by_id.borrow(n_melees); + if (most_recent_melee_ref.start_time + most_recent_melee_ref.duration >= time) { + let market_ids = next_melee_market_ids(); + let melee_id = n_melees + 1; + let registry_ref_mut = &mut Registry[@rewards]; + registry_ref_mut.melees_by_id.add( + melee_id, + Melee { + melee_id, + market_metadatas: market_ids.map_ref(|market_id_ref| { + option::destroy_some( + emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) + ) + }), + available_rewards: REWARDS_PER_MELEE, + start_time: last_period_boundary( + time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR + ), + lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, + duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR + } + ); + registry_ref_mut.melees_by_market_combo_sorted_market_ids.add( + market_ids, melee_id + ); + true + } else false + } + + inline fun last_period_boundary(time: u64, period: u64): u64 { + (time / period) * period + } + + inline fun next_melee_market_ids(): vector { + let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = + unpack_registry_view(registry_view()); + let market_ids; + loop { + let random_market_id_0 = random_market_id(n_markets); + let random_market_id_1 = random_market_id(n_markets); + if (random_market_id_0 == random_market_id_1) continue; + market_ids = sort_unique_market_ids(random_market_id_0, random_market_id_1); + if (!Registry[@rewards].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { + break; + } + }; + market_ids + + } + + /// Market IDs are 1-indexed. + inline fun random_market_id(n_markets: u64): u64 { + randomness::u64_range(0, n_markets) + 1 + } + + inline fun sort_unique_market_ids(market_id_0: u64, market_id_1: u64): vector { + if (market_id_0 < market_id_1) { + vector[market_id_0, market_id_1] + } else { + vector[market_id_1, market_id_0] + } + } + + /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. + inline fun psuedo_random_u64(seed: u64): u64 { + seed = seed ^ (seed << 13); + seed = seed ^ (seed >> 7); + seed = seed ^ (seed << 17); + seed + } + + inline fun borrow_current_melee_ref(): &Melee { + let registry_ref = &Registry[@rewards]; + let n_melees = registry_ref.melees_by_id.length(); + registry_ref.melees_by_id.borrow(n_melees) + } } From 7c3c72653ad7025487e9228ec35afb3a051b4432 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:14:49 -0800 Subject: [PATCH 07/64] Add swap API --- .../sources/emojicoin_dot_fun_arena.move | 237 ++++++++++++++++-- 1 file changed, 211 insertions(+), 26 deletions(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move index 123afcc5b..99e734658 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -15,7 +15,7 @@ module rewards::emojicoin_dot_fun_arena { unpack_market_metadata, unpack_registry_view }; - use std::option::{Self, Option}; + use std::option::Self; use std::signer; /// Resource account address seed for the registry. @@ -29,6 +29,7 @@ module rewards::emojicoin_dot_fun_arena { /// Flat integrator fee. const INTEGRATOR_FEE_RATE_BPS: u8 = 100; + const INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE: u8 = 50; const REWARDS_PER_MELEE: u64 = 1500 * 100_000_000; const MATCH_PERCENTAGE: u64 = 20; const MAX_PERCENTAGE: u64 = 100; @@ -50,8 +51,6 @@ module rewards::emojicoin_dot_fun_arena { struct MeleeEscrow has key { melee_id: u64, - /// Indentical to `Melee.market_metadatas`. - market_metadatas: vector, emojicoin_0: Coin, emojicoin_1: Coin, tap_out_fee: u64 @@ -98,7 +97,6 @@ module rewards::emojicoin_dot_fun_arena { entrant, MeleeEscrow { melee_id, - market_metadatas, emojicoin_0: coin::zero(), emojicoin_1: coin::zero(), tap_out_fee: 0 @@ -145,8 +143,8 @@ module rewards::emojicoin_dot_fun_arena { + actual_match_amount; let registry_ref_mut = &mut Registry[@rewards]; let available_rewards_ref_mut = - registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; - available_rewards_ref_mut = available_rewards_ref_mut + &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut - actual_match_amount; let vault_signer = account::create_signer_with_capability( @@ -218,9 +216,10 @@ module rewards::emojicoin_dot_fun_arena { #[randomness] entry fun exit( participant: &signer, melee_id: u64 - ) acquires Registry { - let may_have_to_pay_tap_out_fee = !crank_schedule(); - // Ensure registry available rewards incremented correctly. + ) acquires MeleeEscrow, Registry { + exit_inner( + participant, melee_id, !crank_schedule() + ); } #[randomness] @@ -229,9 +228,180 @@ module rewards::emojicoin_dot_fun_arena { melee_id: u64, market_addresses: vector
, buy_emojicoin_0: bool - ) acquires Registry { - if (crank_schedule()) {} - else {} + ) acquires MeleeEscrow, Registry { + let exit_once_done = crank_schedule(); + + // Return early if type arguments or melee ID is passed incorrectly, but only after cranking + // schedule. + let swapper_address = signer::address_of(swapper); + if (!exists>( + swapper_address + )) { + return; + }; + let escrow_ref_mut = + &mut MeleeEscrow[swapper_address]; + if (escrow_ref_mut.melee_id != melee_id) + return; + let (market_address_0, market_address_1) = + (market_addresses[0], market_addresses[1]); + + if (buy_emojicoin_0) { + // Move emojicoin 1 balance out of escrow. + let emojicoin_1_ref_mut = &mut escrow_ref_mut.emojicoin_1; + let input_amount = coin::value(emojicoin_1_ref_mut); + aptos_account::deposit_coins( + swapper_address, coin::extract_all(emojicoin_1_ref_mut) + ); + + // Get amount of APT recieved by selling emojicoin 1, then execute swap. + let swap_to_apt = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_1, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(swap_to_apt); + emojicoin_dot_fun::swap( + swapper, + market_address_1, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Get amount of emojicoin 0 recieved by buying it with APT proceeds. + let swap_to_emojicoin_0 = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_0, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let ( + _, + _, + _, + _, + _, + _, + _, + _, + net_proceeds_in_emojicoin_0, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_0); + emojicoin_dot_fun::swap( + swapper, + market_address_0, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Move emojicoin 0 balance to escrow. + coin::merge( + &mut escrow_ref_mut.emojicoin_0, + coin::withdraw(swapper, net_proceeds_in_emojicoin_0) + ); + } else { + // Move emojicoin 0 balance out of escrow. + let emojicoin_0_ref_mut = &mut escrow_ref_mut.emojicoin_0; + let input_amount = coin::value(emojicoin_0_ref_mut); + aptos_account::deposit_coins( + swapper_address, coin::extract_all(emojicoin_0_ref_mut) + ); + + // Get amount of APT recieved by selling emojicoin 0, then execute swap. + let swap_to_apt = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_0, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(swap_to_apt); + emojicoin_dot_fun::swap( + swapper, + market_address_1, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Get amount of emojicoin 1 recieved by buying it with APT proceeds. + let swap_to_emojicoin_1 = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_1, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let ( + _, + _, + _, + _, + _, + _, + _, + _, + net_proceeds_in_emojicoin_1, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_1); + emojicoin_dot_fun::swap( + swapper, + market_address_1, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Move emojicoin 1 balance to escrow. + coin::merge( + &mut escrow_ref_mut.emojicoin_1, + coin::withdraw(swapper, net_proceeds_in_emojicoin_1) + ); + }; + + if (exit_once_done) + exit_inner( + swapper, melee_id, false + ); } fun init_module(rewards: &signer) { @@ -286,6 +456,27 @@ module rewards::emojicoin_dot_fun_arena { coin::register(&vault_signer); } + inline fun borrow_current_melee_ref(): &Melee { + let registry_ref = &Registry[@rewards]; + let n_melees = registry_ref.melees_by_id.length(); + registry_ref.melees_by_id.borrow(n_melees) + } + + inline fun exit_inner( + participant: &signer, melee_id: u64, may_have_to_pay_tap_out_fee: bool + ) acquires Registry { + let participant_address = signer::address_of(participant); + // Only allow exit if user has corresponding melee resourcce and melee ID matches. + if (exists>( + participant_address + )) { + let escrow_ref_mut = + &mut MeleeEscrow[participant_address]; + // Only allow exit if melee ID matches. + if (escrow_ref_mut.melee_id == melee_id) {} + } + } + /// Cranks schedule and returns `true` if a melee has ended as a result. inline fun crank_schedule(): bool { let time = timestamp::now_microseconds(); @@ -341,6 +532,14 @@ module rewards::emojicoin_dot_fun_arena { } + /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. + inline fun psuedo_random_u64(seed: u64): u64 { + seed = seed ^ (seed << 13); + seed = seed ^ (seed >> 7); + seed = seed ^ (seed << 17); + seed + } + /// Market IDs are 1-indexed. inline fun random_market_id(n_markets: u64): u64 { randomness::u64_range(0, n_markets) + 1 @@ -353,18 +552,4 @@ module rewards::emojicoin_dot_fun_arena { vector[market_id_1, market_id_0] } } - - /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. - inline fun psuedo_random_u64(seed: u64): u64 { - seed = seed ^ (seed << 13); - seed = seed ^ (seed >> 7); - seed = seed ^ (seed << 17); - seed - } - - inline fun borrow_current_melee_ref(): &Melee { - let registry_ref = &Registry[@rewards]; - let n_melees = registry_ref.melees_by_id.length(); - registry_ref.melees_by_id.borrow(n_melees) - } } From 9da045fa338587e28012b90f06ed3b9a7908b805 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:29:39 -0800 Subject: [PATCH 08/64] Finalize prototype --- .../sources/emojicoin_dot_fun_arena.move | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move index 99e734658..0a4e605f5 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -18,6 +18,9 @@ module rewards::emojicoin_dot_fun_arena { use std::option::Self; use std::signer; + /// Signer does not correspond to rewards account. + const E_NOT_REWARDS: u64 = 0; + /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -70,6 +73,22 @@ module rewards::emojicoin_dot_fun_arena { melee_ids: SmartTable } + public entry fun fund_vault(rewards: &signer, amount: u64) acquires Registry { + assert!(signer::address_of(rewards) == @rewards, E_NOT_REWARDS); + let vault_address = + account::get_signer_capability_address(&Registry[@rewards].signer_capability); + aptos_account::transfer(rewards, vault_address, amount); + } + + public entry fun withdraw_from_vault(rewards: &signer, amount: u64) acquires Registry { + assert!(signer::address_of(rewards) == @rewards, E_NOT_REWARDS); + let vault_signer = + account::create_signer_with_capability( + &Registry[@rewards].signer_capability + ); + aptos_account::transfer(&vault_signer, @rewards, amount); + } + #[randomness] entry fun enter( entrant: &signer, @@ -127,6 +146,17 @@ module rewards::emojicoin_dot_fun_arena { } else { current_melee_ref.available_rewards }; + let vault_balance = + coin::balance( + account::get_signer_capability_address( + &Registry[@rewards].signer_capability + ) + ); + eligible_match_amount = if (eligible_match_amount < vault_balance) { + eligible_match_amount + } else { + vault_balance + }; let requested_match_amount = ( ((input_amount as u128) * (MATCH_PERCENTAGE as u128) @@ -473,7 +503,36 @@ module rewards::emojicoin_dot_fun_arena { let escrow_ref_mut = &mut MeleeEscrow[participant_address]; // Only allow exit if melee ID matches. - if (escrow_ref_mut.melee_id == melee_id) {} + if (escrow_ref_mut.melee_id == melee_id) { + // Update available rewards and transfer tap out fee to vault if applicable. + if (may_have_to_pay_tap_out_fee) { + let registry_ref_mut = &mut Registry[@rewards]; + let exited_melee_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(melee_id); + let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; + let available_rewards_ref_mut = + &mut exited_melee_ref_mut.available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut + + *tap_out_fee_ref_mut; + let vault_address = + account::get_signer_capability_address( + ®istry_ref_mut.signer_capability + ); + aptos_account::transfer( + participant, vault_address, *tap_out_fee_ref_mut + ); + *tap_out_fee_ref_mut = 0; + }; + // Move emojicoin balances out of escrow. + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_0) + ); + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_1) + ); + }; } } From 6de06c9bf6aa3860c64addfedcd172988d2cb5d0 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:48:10 -0800 Subject: [PATCH 09/64] Update few params from review --- src/move/rewards/sources/emojicoin_dot_fun_arena.move | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/rewards/sources/emojicoin_dot_fun_arena.move index 0a4e605f5..cdbd0136a 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/rewards/sources/emojicoin_dot_fun_arena.move @@ -43,7 +43,7 @@ module rewards::emojicoin_dot_fun_arena { melee_id: u64, /// Market with lower market ID first. market_metadatas: vector, - available_rewards: u64, + available_rewards_in_octas: u64, /// In microseconds. start_time: u64, lock_in_period: u64, @@ -542,7 +542,7 @@ module rewards::emojicoin_dot_fun_arena { let registry_ref = &Registry[@rewards]; let n_melees = registry_ref.melees_by_id.length(); let most_recent_melee_ref = registry_ref.melees_by_id.borrow(n_melees); - if (most_recent_melee_ref.start_time + most_recent_melee_ref.duration >= time) { + if (time >= most_recent_melee_ref.start_time + most_recent_melee_ref.duration) { let market_ids = next_melee_market_ids(); let melee_id = n_melees + 1; let registry_ref_mut = &mut Registry[@rewards]; From 566bf3027f0edd2c74a478852f0c39b33c0fd4bb Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:50:47 -0800 Subject: [PATCH 10/64] Relocate to new dir --- src/move/emojicoin_arena/Move.toml | 21 +++++++++++++++++++ .../sources/emojicoin_dot_fun_arena.move | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/move/emojicoin_arena/Move.toml rename src/move/{rewards => emojicoin_arena}/sources/emojicoin_dot_fun_arena.move (99%) diff --git a/src/move/emojicoin_arena/Move.toml b/src/move/emojicoin_arena/Move.toml new file mode 100644 index 000000000..3183a0dc3 --- /dev/null +++ b/src/move/emojicoin_arena/Move.toml @@ -0,0 +1,21 @@ +[addresses] +integrator = "_" +rewards = "_" + +[dependencies.EmojicoinDotFun] +local = "../emojicoin_dot_fun" + +[dev-addresses] +coin_factory = "0xaaaa" +emojicoin_dot_fun = "0xc0de" +integrator = "0xccc" +rewards = "0xddd" + +[dev-dependencies.BlackHeartCoinFactory] +local = "../test_coin_factories/black_heart" + +[package] +authors = ["Econia Labs (developers@econialabs.com)"] +name = "EmojicoinDotFunRewards" +upgrade_policy = "compatible" +version = "1.2.0" diff --git a/src/move/rewards/sources/emojicoin_dot_fun_arena.move b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move similarity index 99% rename from src/move/rewards/sources/emojicoin_dot_fun_arena.move rename to src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move index cdbd0136a..d4a51f620 100644 --- a/src/move/rewards/sources/emojicoin_dot_fun_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move @@ -1,4 +1,4 @@ -module rewards::emojicoin_dot_fun_arena { +module arena::emojicoin_arena { use aptos_framework::account::{Self, SignerCapability}; use aptos_framework::aptos_account; From 0952fc5706d5bd25d5c970da39e2bd0ebf6f180a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:56:35 -0800 Subject: [PATCH 11/64] Make compilation succed --- src/move/emojicoin_arena/Move.toml | 15 +++--- .../sources/emojicoin_dot_fun_arena.move | 54 +++++++++---------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/move/emojicoin_arena/Move.toml b/src/move/emojicoin_arena/Move.toml index 3183a0dc3..8ba400cc4 100644 --- a/src/move/emojicoin_arena/Move.toml +++ b/src/move/emojicoin_arena/Move.toml @@ -1,21 +1,18 @@ [addresses] +arena = "_" integrator = "_" -rewards = "_" [dependencies.EmojicoinDotFun] local = "../emojicoin_dot_fun" [dev-addresses] -coin_factory = "0xaaaa" +arena = "0xaaa" +coin_factory = "0xbbb" emojicoin_dot_fun = "0xc0de" -integrator = "0xccc" -rewards = "0xddd" - -[dev-dependencies.BlackHeartCoinFactory] -local = "../test_coin_factories/black_heart" +integrator = "0xddd" [package] authors = ["Econia Labs (developers@econialabs.com)"] -name = "EmojicoinDotFunRewards" +name = "EmojicoinArena" upgrade_policy = "compatible" -version = "1.2.0" +version = "1.0.0" diff --git a/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move index d4a51f620..038942733 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move @@ -18,8 +18,8 @@ module arena::emojicoin_arena { use std::option::Self; use std::signer; - /// Signer does not correspond to rewards account. - const E_NOT_REWARDS: u64 = 0; + /// Signer does not correspond to arena account. + const E_NOT_ARENA: u64 = 0; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -73,20 +73,18 @@ module arena::emojicoin_arena { melee_ids: SmartTable } - public entry fun fund_vault(rewards: &signer, amount: u64) acquires Registry { - assert!(signer::address_of(rewards) == @rewards, E_NOT_REWARDS); + public entry fun fund_vault(arena: &signer, amount: u64) acquires Registry { + assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); let vault_address = - account::get_signer_capability_address(&Registry[@rewards].signer_capability); - aptos_account::transfer(rewards, vault_address, amount); + account::get_signer_capability_address(&Registry[@arena].signer_capability); + aptos_account::transfer(arena, vault_address, amount); } - public entry fun withdraw_from_vault(rewards: &signer, amount: u64) acquires Registry { - assert!(signer::address_of(rewards) == @rewards, E_NOT_REWARDS); + public entry fun withdraw_from_vault(arena: &signer, amount: u64) acquires Registry { + assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); let vault_signer = - account::create_signer_with_capability( - &Registry[@rewards].signer_capability - ); - aptos_account::transfer(&vault_signer, @rewards, amount); + account::create_signer_with_capability(&Registry[@arena].signer_capability); + aptos_account::transfer(&vault_signer, @arena, amount); } #[randomness] @@ -141,15 +139,15 @@ module arena::emojicoin_arena { if (current_tap_out_fee < MAX_MATCH_AMOUNT && lock_ins_still_allowed) { let eligible_match_amount = MAX_MATCH_AMOUNT - current_tap_out_fee; eligible_match_amount = if (eligible_match_amount - < current_melee_ref.available_rewards) { + < current_melee_ref.available_rewards_in_octas) { eligible_match_amount } else { - current_melee_ref.available_rewards + current_melee_ref.available_rewards_in_octas }; let vault_balance = coin::balance( account::get_signer_capability_address( - &Registry[@rewards].signer_capability + &Registry[@arena].signer_capability ) ); eligible_match_amount = if (eligible_match_amount < vault_balance) { @@ -171,9 +169,9 @@ module arena::emojicoin_arena { if (actual_match_amount > 0) { escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + actual_match_amount; - let registry_ref_mut = &mut Registry[@rewards]; + let registry_ref_mut = &mut Registry[@arena]; let available_rewards_ref_mut = - &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; + &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards_in_octas; *available_rewards_ref_mut = *available_rewards_ref_mut - actual_match_amount; let vault_signer = @@ -434,7 +432,7 @@ module arena::emojicoin_arena { ); } - fun init_module(rewards: &signer) { + fun init_module(arena: &signer) { // Get first melee market addresses, without using randomness APIs. let time = timestamp::now_microseconds(); let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = @@ -463,7 +461,7 @@ module arena::emojicoin_arena { emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) ) }), - available_rewards: REWARDS_PER_MELEE, + available_rewards_in_octas: REWARDS_PER_MELEE, start_time, lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR @@ -474,9 +472,9 @@ module arena::emojicoin_arena { // Store registry resource. let (vault_signer, signer_capability) = - account::create_resource_account(rewards, REGISTRY_SEED); + account::create_resource_account(arena, REGISTRY_SEED); move_to( - rewards, + arena, Registry { melees_by_id, melees_by_market_combo_sorted_market_ids, @@ -487,7 +485,7 @@ module arena::emojicoin_arena { } inline fun borrow_current_melee_ref(): &Melee { - let registry_ref = &Registry[@rewards]; + let registry_ref = &Registry[@arena]; let n_melees = registry_ref.melees_by_id.length(); registry_ref.melees_by_id.borrow(n_melees) } @@ -506,12 +504,12 @@ module arena::emojicoin_arena { if (escrow_ref_mut.melee_id == melee_id) { // Update available rewards and transfer tap out fee to vault if applicable. if (may_have_to_pay_tap_out_fee) { - let registry_ref_mut = &mut Registry[@rewards]; + let registry_ref_mut = &mut Registry[@arena]; let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; let available_rewards_ref_mut = - &mut exited_melee_ref_mut.available_rewards; + &mut exited_melee_ref_mut.available_rewards_in_octas; *available_rewards_ref_mut = *available_rewards_ref_mut + *tap_out_fee_ref_mut; let vault_address = @@ -539,13 +537,13 @@ module arena::emojicoin_arena { /// Cranks schedule and returns `true` if a melee has ended as a result. inline fun crank_schedule(): bool { let time = timestamp::now_microseconds(); - let registry_ref = &Registry[@rewards]; + let registry_ref = &Registry[@arena]; let n_melees = registry_ref.melees_by_id.length(); let most_recent_melee_ref = registry_ref.melees_by_id.borrow(n_melees); if (time >= most_recent_melee_ref.start_time + most_recent_melee_ref.duration) { let market_ids = next_melee_market_ids(); let melee_id = n_melees + 1; - let registry_ref_mut = &mut Registry[@rewards]; + let registry_ref_mut = &mut Registry[@arena]; registry_ref_mut.melees_by_id.add( melee_id, Melee { @@ -555,7 +553,7 @@ module arena::emojicoin_arena { emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) ) }), - available_rewards: REWARDS_PER_MELEE, + available_rewards_in_octas: REWARDS_PER_MELEE, start_time: last_period_boundary( time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR ), @@ -583,7 +581,7 @@ module arena::emojicoin_arena { let random_market_id_1 = random_market_id(n_markets); if (random_market_id_0 == random_market_id_1) continue; market_ids = sort_unique_market_ids(random_market_id_0, random_market_id_1); - if (!Registry[@rewards].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { + if (!Registry[@arena].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { break; } }; From 08fe32f887171c1001c8357ac49d23ccef71e630 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:59:13 -0800 Subject: [PATCH 12/64] Use concise type args --- .../sources/emojicoin_dot_fun_arena.move | 75 ++++++++----------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move index 038942733..d6cd9615b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move @@ -52,10 +52,10 @@ module arena::emojicoin_arena { struct Nil has store {} - struct MeleeEscrow has key { + struct MeleeEscrow has key { melee_id: u64, - emojicoin_0: Coin, - emojicoin_1: Coin, + emojicoin_0: Coin, + emojicoin_1: Coin, tap_out_fee: u64 } @@ -88,7 +88,7 @@ module arena::emojicoin_arena { } #[randomness] - entry fun enter( + entry fun enter( entrant: &signer, buy_emojicoin_0: bool, input_amount: u64, @@ -100,19 +100,17 @@ module arena::emojicoin_arena { let current_melee_ref = borrow_current_melee_ref(); let market_metadatas = current_melee_ref.market_metadatas; let (_, market_address_0, _) = unpack_market_metadata(market_metadatas[0]); - market_view(market_address_0); + market_view(market_address_0); let (_, market_address_1, _) = unpack_market_metadata(market_metadatas[1]); - market_view(market_address_1); + market_view(market_address_1); // Create escrow and user melees resources if they don't exist. let melee_id = current_melee_ref.melee_id; let entrant_address = signer::address_of(entrant); - if (!exists>( - entrant_address - )) { + if (!exists>(entrant_address)) { move_to( entrant, - MeleeEscrow { + MeleeEscrow { melee_id, emojicoin_0: coin::zero(), emojicoin_1: coin::zero(), @@ -130,7 +128,7 @@ module arena::emojicoin_arena { let match_amount = if (lock_in) { let escrow_ref_mut = - &mut MeleeEscrow[entrant_address]; + &mut MeleeEscrow[entrant_address]; let current_tap_out_fee = escrow_ref_mut.tap_out_fee; let lock_in_period_end_time = current_melee_ref.start_time + current_melee_ref.lock_in_period; @@ -188,11 +186,10 @@ module arena::emojicoin_arena { // Execute a swap then immediately move funds into escrow. let input_amount_after_matching = input_amount + match_amount; - let escrow_ref_mut = - &mut MeleeEscrow[entrant_address]; + let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; if (buy_emojicoin_0) { let swap = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( entrant_address, market_address_0, input_amount_after_matching, @@ -202,7 +199,7 @@ module arena::emojicoin_arena { ); let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = emojicoin_dot_fun::unpack_swap(swap); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( entrant, market_address_0, input_amount_after_matching, @@ -216,7 +213,7 @@ module arena::emojicoin_arena { ); } else { let swap = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( entrant_address, market_address_1, input_amount_after_matching, @@ -226,7 +223,7 @@ module arena::emojicoin_arena { ); let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = emojicoin_dot_fun::unpack_swap(swap); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( entrant, market_address_1, input_amount_after_matching, @@ -242,16 +239,14 @@ module arena::emojicoin_arena { } #[randomness] - entry fun exit( + entry fun exit( participant: &signer, melee_id: u64 ) acquires MeleeEscrow, Registry { - exit_inner( - participant, melee_id, !crank_schedule() - ); + exit_inner(participant, melee_id, !crank_schedule()); } #[randomness] - entry fun swap( + entry fun swap( swapper: &signer, melee_id: u64, market_addresses: vector
, @@ -262,13 +257,10 @@ module arena::emojicoin_arena { // Return early if type arguments or melee ID is passed incorrectly, but only after cranking // schedule. let swapper_address = signer::address_of(swapper); - if (!exists>( - swapper_address - )) { + if (!exists>(swapper_address)) { return; }; - let escrow_ref_mut = - &mut MeleeEscrow[swapper_address]; + let escrow_ref_mut = &mut MeleeEscrow[swapper_address]; if (escrow_ref_mut.melee_id != melee_id) return; let (market_address_0, market_address_1) = @@ -284,7 +276,7 @@ module arena::emojicoin_arena { // Get amount of APT recieved by selling emojicoin 1, then execute swap. let swap_to_apt = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( swapper_address, market_address_1, input_amount, @@ -294,7 +286,7 @@ module arena::emojicoin_arena { ); let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = emojicoin_dot_fun::unpack_swap(swap_to_apt); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( swapper, market_address_1, input_amount, @@ -306,7 +298,7 @@ module arena::emojicoin_arena { // Get amount of emojicoin 0 recieved by buying it with APT proceeds. let swap_to_emojicoin_0 = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( swapper_address, market_address_0, net_proceeds_in_apt, @@ -334,7 +326,7 @@ module arena::emojicoin_arena { _, _ ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_0); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( swapper, market_address_0, net_proceeds_in_apt, @@ -359,7 +351,7 @@ module arena::emojicoin_arena { // Get amount of APT recieved by selling emojicoin 0, then execute swap. let swap_to_apt = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( swapper_address, market_address_0, input_amount, @@ -369,7 +361,7 @@ module arena::emojicoin_arena { ); let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = emojicoin_dot_fun::unpack_swap(swap_to_apt); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( swapper, market_address_1, input_amount, @@ -381,7 +373,7 @@ module arena::emojicoin_arena { // Get amount of emojicoin 1 recieved by buying it with APT proceeds. let swap_to_emojicoin_1 = - emojicoin_dot_fun::simulate_swap( + emojicoin_dot_fun::simulate_swap( swapper_address, market_address_1, net_proceeds_in_apt, @@ -409,7 +401,7 @@ module arena::emojicoin_arena { _, _ ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_1); - emojicoin_dot_fun::swap( + emojicoin_dot_fun::swap( swapper, market_address_1, net_proceeds_in_apt, @@ -426,10 +418,7 @@ module arena::emojicoin_arena { ); }; - if (exit_once_done) - exit_inner( - swapper, melee_id, false - ); + if (exit_once_done) exit_inner(swapper, melee_id, false); } fun init_module(arena: &signer) { @@ -490,16 +479,14 @@ module arena::emojicoin_arena { registry_ref.melees_by_id.borrow(n_melees) } - inline fun exit_inner( + inline fun exit_inner( participant: &signer, melee_id: u64, may_have_to_pay_tap_out_fee: bool ) acquires Registry { let participant_address = signer::address_of(participant); // Only allow exit if user has corresponding melee resourcce and melee ID matches. - if (exists>( - participant_address - )) { + if (exists>(participant_address)) { let escrow_ref_mut = - &mut MeleeEscrow[participant_address]; + &mut MeleeEscrow[participant_address]; // Only allow exit if melee ID matches. if (escrow_ref_mut.melee_id == melee_id) { // Update available rewards and transfer tap out fee to vault if applicable. From db71ab87b1183e246971b612634188d083f24511 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:06:55 -0800 Subject: [PATCH 13/64] Rename module --- .../{emojicoin_dot_fun_arena.move => emojicoin_arena.move} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/move/emojicoin_arena/sources/{emojicoin_dot_fun_arena.move => emojicoin_arena.move} (100%) diff --git a/src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move similarity index 100% rename from src/move/emojicoin_arena/sources/emojicoin_dot_fun_arena.move rename to src/move/emojicoin_arena/sources/emojicoin_arena.move From 1a76472369e96167fbea9339c7d2ad6674b9ad61 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:36:27 -0800 Subject: [PATCH 14/64] Add helper to get number of registered markets --- .../emojicoin_arena/sources/emojicoin_arena.move | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index d6cd9615b..7cbaba5c0 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -424,8 +424,7 @@ module arena::emojicoin_arena { fun init_module(arena: &signer) { // Get first melee market addresses, without using randomness APIs. let time = timestamp::now_microseconds(); - let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = - unpack_registry_view(registry_view()); + let n_markets = get_n_registered_markets(); let pseudo_random_market_id_0 = time % n_markets + 1; let pseudo_random_market_id_1; loop { @@ -555,13 +554,18 @@ module arena::emojicoin_arena { } else false } + inline fun get_n_registered_markets(): u64 { + let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = + unpack_registry_view(registry_view()); + n_markets + } + inline fun last_period_boundary(time: u64, period: u64): u64 { (time / period) * period } inline fun next_melee_market_ids(): vector { - let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = - unpack_registry_view(registry_view()); + let n_markets = get_n_registered_markets(); let market_ids; loop { let random_market_id_0 = random_market_id(n_markets); From b3a8cd9af071ca5ef47e3ba2f234c0abbad8d3a2 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:53:46 -0800 Subject: [PATCH 15/64] Add psuedo-randomness module --- .../emojicoin_arena/sources/pseudo_randomness.move | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/move/emojicoin_arena/sources/pseudo_randomness.move diff --git a/src/move/emojicoin_arena/sources/pseudo_randomness.move b/src/move/emojicoin_arena/sources/pseudo_randomness.move new file mode 100644 index 000000000..28f4e585f --- /dev/null +++ b/src/move/emojicoin_arena/sources/pseudo_randomness.move @@ -0,0 +1,11 @@ +module arena::pseudo_randomness { + + use aptos_framework::transaction_context; + use std::bcs; + + /// Pseudo-random proxy for `aptos_framework::randomness::next_32_bytes`. + inline fun next_32_bytes(): vector { + bcs::to_bytes(transaction_context::generate_auid_address()) + } + +} \ No newline at end of file From 62f444ea3893691dfb4e19e5864aa0a75ff21886 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:14:04 -0800 Subject: [PATCH 16/64] Finalize pseudo-randomness, tidy up vars --- .../sources/emojicoin_arena.move | 61 ++++++++----------- .../sources/pseudo_randomness.move | 26 +++++++- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 7cbaba5c0..9a0652843 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -7,6 +7,7 @@ module arena::emojicoin_arena { use aptos_framework::randomness::Self; use aptos_framework::timestamp; use aptos_std::smart_table::{Self, SmartTable}; + use arena::pseudo_randomness; use emojicoin_dot_fun::emojicoin_dot_fun::{ Self, MarketMetadata, @@ -39,9 +40,9 @@ module arena::emojicoin_arena { const MAX_MATCH_AMOUNT: u64 = 100_000_000; struct Melee has copy, drop, store { - /// 1-indexed for conformity with market ID indexing. + /// 1-indexed for conformity with emojicoin market ID indexing. melee_id: u64, - /// Market with lower market ID first. + /// Metadata for market with lower market ID comes first. market_metadatas: vector, available_rewards_in_octas: u64, /// In microseconds. @@ -63,7 +64,7 @@ module arena::emojicoin_arena { /// Map from melee serial ID to the melee. melees_by_id: SmartTable, /// Map from a sorted combination of market IDs (lower ID first) to the melee serial ID. - melees_by_market_combo_sorted_market_ids: SmartTable, u64>, + melee_ids_by_market_ids: SmartTable, u64>, /// Approves transfers from the vault. signer_capability: SignerCapability } @@ -254,8 +255,8 @@ module arena::emojicoin_arena { ) acquires MeleeEscrow, Registry { let exit_once_done = crank_schedule(); - // Return early if type arguments or melee ID is passed incorrectly, but only after cranking - // schedule. + // Return early if type arguments or melee ID are passed incorrectly, but only after + // cranking schedule. let swapper_address = signer::address_of(swapper); if (!exists>(swapper_address)) { return; @@ -422,19 +423,17 @@ module arena::emojicoin_arena { } fun init_module(arena: &signer) { - // Get first melee market addresses, without using randomness APIs. + // Use pseudo-randomness to get market IDs for the first melee, since randomness is not + // supported during `init_module`. let time = timestamp::now_microseconds(); let n_markets = get_n_registered_markets(); - let pseudo_random_market_id_0 = time % n_markets + 1; - let pseudo_random_market_id_1; + let market_id_0 = pseudo_random_market_id(n_markets); + let market_id_1; loop { - pseudo_random_market_id_1 = psuedo_random_u64(time) % n_markets + 1; - if (pseudo_random_market_id_1 != pseudo_random_market_id_0) break; + market_id_1 = pseudo_random_market_id(n_markets); + if (market_id_1 != market_id_0) break; }; - let market_ids = - sort_unique_market_ids( - pseudo_random_market_id_0, pseudo_random_market_id_1 - ); + let market_ids = sort_unique_market_ids(market_id_0, market_id_1); // Initialize first melee. let start_time = @@ -455,19 +454,15 @@ module arena::emojicoin_arena { duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR } ); - let melees_by_market_combo_sorted_market_ids = smart_table::new(); - melees_by_market_combo_sorted_market_ids.add(market_ids, 1); + let melee_ids_by_market_ids = smart_table::new(); + melee_ids_by_market_ids.add(market_ids, 1); // Store registry resource. let (vault_signer, signer_capability) = account::create_resource_account(arena, REGISTRY_SEED); move_to( arena, - Registry { - melees_by_id, - melees_by_market_combo_sorted_market_ids, - signer_capability - } + Registry { melees_by_id, melee_ids_by_market_ids, signer_capability } ); coin::register(&vault_signer); } @@ -547,9 +542,7 @@ module arena::emojicoin_arena { duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR } ); - registry_ref_mut.melees_by_market_combo_sorted_market_ids.add( - market_ids, melee_id - ); + registry_ref_mut.melee_ids_by_market_ids.add(market_ids, melee_id); true } else false } @@ -568,24 +561,20 @@ module arena::emojicoin_arena { let n_markets = get_n_registered_markets(); let market_ids; loop { - let random_market_id_0 = random_market_id(n_markets); - let random_market_id_1 = random_market_id(n_markets); - if (random_market_id_0 == random_market_id_1) continue; - market_ids = sort_unique_market_ids(random_market_id_0, random_market_id_1); - if (!Registry[@arena].melees_by_market_combo_sorted_market_ids.contains(market_ids)) { + let market_id_0 = random_market_id(n_markets); + let market_id_1 = random_market_id(n_markets); + if (market_id_0 == market_id_1) continue; + market_ids = sort_unique_market_ids(market_id_0, market_id_1); + if (!Registry[@arena].melee_ids_by_market_ids.contains(market_ids)) break; - } }; market_ids } - /// Psuedo random number generator based on xorshift64 algorithm from Wikipedia. - inline fun psuedo_random_u64(seed: u64): u64 { - seed = seed ^ (seed << 13); - seed = seed ^ (seed >> 7); - seed = seed ^ (seed << 17); - seed + /// Pseudo-random proxy for `random_market_id` for use during `init_module`. + inline fun pseudo_random_market_id(n_markets: u64): u64 { + pseudo_randomness::u64_range(0, n_markets) + 1 } /// Market IDs are 1-indexed. diff --git a/src/move/emojicoin_arena/sources/pseudo_randomness.move b/src/move/emojicoin_arena/sources/pseudo_randomness.move index 28f4e585f..d96d7e141 100644 --- a/src/move/emojicoin_arena/sources/pseudo_randomness.move +++ b/src/move/emojicoin_arena/sources/pseudo_randomness.move @@ -2,10 +2,32 @@ module arena::pseudo_randomness { use aptos_framework::transaction_context; use std::bcs; + use std::vector; + + friend arena::emojicoin_arena; + + /// Pseudo-random proxy for `aptos_framework::randomness::u64_range`. + public(friend) inline fun u64_range(min_incl: u64, max_excl: u64): u64 { + let range = ((max_excl - min_incl) as u256); + let sample = ((u256_integer() % range) as u64); + + min_incl + sample + } /// Pseudo-random proxy for `aptos_framework::randomness::next_32_bytes`. inline fun next_32_bytes(): vector { - bcs::to_bytes(transaction_context::generate_auid_address()) + bcs::to_bytes(&transaction_context::generate_auid_address()) } -} \ No newline at end of file + /// Pseudo-random proxy for `aptos_framework::randomness::u256_integer`. + inline fun u256_integer(): u256 { + let raw = next_32_bytes(); + let i = 0; + let ret: u256 = 0; + while (i < 32) { + ret = ret * 256 + (vector::pop_back(&mut raw) as u256); + i = i + 1; + }; + ret + } +} From 75a31847c868c971bef826a8c43ae1889ef3dcbd Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 19:35:46 -0800 Subject: [PATCH 17/64] Add indexing fields --- .../sources/emojicoin_arena.move | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 9a0652843..a1d89162b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -1,6 +1,7 @@ module arena::emojicoin_arena { use aptos_framework::account::{Self, SignerCapability}; + use aptos_framework::aggregator_v2::{Self, Aggregator}; use aptos_framework::aptos_account; use aptos_framework::aptos_coin::AptosCoin; use aptos_framework::coin::{Self, Coin}; @@ -39,27 +40,43 @@ module arena::emojicoin_arena { const MAX_PERCENTAGE: u64 = 100; const MAX_MATCH_AMOUNT: u64 = 100_000_000; - struct Melee has copy, drop, store { + struct Melee has store { /// 1-indexed for conformity with emojicoin market ID indexing. melee_id: u64, /// Metadata for market with lower market ID comes first. market_metadatas: vector, - available_rewards_in_octas: u64, /// In microseconds. start_time: u64, + /// How long after start time users can lock in. lock_in_period: u64, - duration: u64 + /// How long melee lasts after start time. + duration: u64, + /// Amount of rewards available for distribution in octas. + available_rewards: u64, + /// Active entrants. + entrants: SmartTable, + /// Number of melee-specific swaps. + n_melee_swaps: Aggregator, + /// Volume of melee-specific swaps in octas. + melee_swaps_volume: Aggregator } - struct Nil has store {} - struct MeleeEscrow has key { melee_id: u64, emojicoin_0: Coin, emojicoin_1: Coin, + /// Volume of user's melee-specific swaps in octas. + melee_swaps_volume: u128, + /// Number of swaps user has executed during the melee. + n_melee_swaps: u64, + /// APT entered into the melee. Normalized to equivalent amount when topping off. + octas_entered: u64, + /// Octas user must pay to exit the melee, if they have locked in. tap_out_fee: u64 } + struct Nil has store {} + struct Registry has key { /// Map from melee serial ID to the melee. melees_by_id: SmartTable, @@ -115,7 +132,10 @@ module arena::emojicoin_arena { melee_id, emojicoin_0: coin::zero(), emojicoin_1: coin::zero(), - tap_out_fee: 0 + octas_entered: 0, + tap_out_fee: 0, + melee_swaps_volume: 0, + n_melee_swaps: 0 } ); if (!exists(entrant_address)) { @@ -138,10 +158,10 @@ module arena::emojicoin_arena { if (current_tap_out_fee < MAX_MATCH_AMOUNT && lock_ins_still_allowed) { let eligible_match_amount = MAX_MATCH_AMOUNT - current_tap_out_fee; eligible_match_amount = if (eligible_match_amount - < current_melee_ref.available_rewards_in_octas) { + < current_melee_ref.available_rewards) { eligible_match_amount } else { - current_melee_ref.available_rewards_in_octas + current_melee_ref.available_rewards }; let vault_balance = coin::balance( @@ -170,7 +190,7 @@ module arena::emojicoin_arena { + actual_match_amount; let registry_ref_mut = &mut Registry[@arena]; let available_rewards_ref_mut = - &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards_in_octas; + &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; *available_rewards_ref_mut = *available_rewards_ref_mut - actual_match_amount; let vault_signer = @@ -448,10 +468,13 @@ module arena::emojicoin_arena { emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) ) }), - available_rewards_in_octas: REWARDS_PER_MELEE, start_time, lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, - duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR + duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR, + available_rewards: REWARDS_PER_MELEE, + entrants: smart_table::new(), + n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator() } ); let melee_ids_by_market_ids = smart_table::new(); @@ -490,7 +513,7 @@ module arena::emojicoin_arena { registry_ref_mut.melees_by_id.borrow_mut(melee_id); let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; let available_rewards_ref_mut = - &mut exited_melee_ref_mut.available_rewards_in_octas; + &mut exited_melee_ref_mut.available_rewards; *available_rewards_ref_mut = *available_rewards_ref_mut + *tap_out_fee_ref_mut; let vault_address = @@ -534,12 +557,15 @@ module arena::emojicoin_arena { emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) ) }), - available_rewards_in_octas: REWARDS_PER_MELEE, start_time: last_period_boundary( time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR ), lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, - duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR + duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR, + available_rewards: REWARDS_PER_MELEE, + entrants: smart_table::new(), + n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator() } ); registry_ref_mut.melee_ids_by_market_ids.add(market_ids, melee_id); From 846b949bc9ef3f635af0c3c13c2bdfe61b593251 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 19:43:09 -0800 Subject: [PATCH 18/64] Add fields to track coin escrow holdings --- .../sources/emojicoin_arena.move | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index a1d89162b..39919805b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -58,10 +58,15 @@ module arena::emojicoin_arena { /// Number of melee-specific swaps. n_melee_swaps: Aggregator, /// Volume of melee-specific swaps in octas. - melee_swaps_volume: Aggregator + melee_swaps_volume: Aggregator, + /// Amount of emojicoin 0 locked in all melee escrows for the melee. + emojicoin_0_locked: Aggregator, + /// Amount of emojicoin 1 locked in all melee escrows for the melee. + emojicoin_1_locked: Aggregator } struct MeleeEscrow has key { + /// `Melee.market_id`. melee_id: u64, emojicoin_0: Coin, emojicoin_1: Coin, @@ -474,7 +479,9 @@ module arena::emojicoin_arena { available_rewards: REWARDS_PER_MELEE, entrants: smart_table::new(), n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator() + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), + emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), + emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() } ); let melee_ids_by_market_ids = smart_table::new(); @@ -500,7 +507,7 @@ module arena::emojicoin_arena { participant: &signer, melee_id: u64, may_have_to_pay_tap_out_fee: bool ) acquires Registry { let participant_address = signer::address_of(participant); - // Only allow exit if user has corresponding melee resourcce and melee ID matches. + // Only allow exit if user has corresponding melee resource and melee ID matches. if (exists>(participant_address)) { let escrow_ref_mut = &mut MeleeEscrow[participant_address]; @@ -565,7 +572,9 @@ module arena::emojicoin_arena { available_rewards: REWARDS_PER_MELEE, entrants: smart_table::new(), n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator() + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), + emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), + emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() } ); registry_ref_mut.melee_ids_by_market_ids.add(market_ids, melee_id); From 4e60e0f8943950207e669c07c6ec77bed72be6f4 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:14:12 -0800 Subject: [PATCH 19/64] Add assorted abstractions --- .../sources/emojicoin_arena.move | 179 ++++++++++-------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 39919805b..720f31b76 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -27,18 +27,18 @@ module arena::emojicoin_arena { const REGISTRY_SEED: vector = b"Arena registry"; const U64_MAX: u64 = 0xffffffffffffffff; - - const LOCK_IN_PERIOD_HOURS: u64 = 12; - const MELEE_DURATION_HOURS: u64 = 36; - const MICROSECONDS_PER_HOUR: u64 = 3_600_000_000; + const MAX_PERCENTAGE: u64 = 100; /// Flat integrator fee. const INTEGRATOR_FEE_RATE_BPS: u8 = 100; const INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE: u8 = 50; - const REWARDS_PER_MELEE: u64 = 1500 * 100_000_000; - const MATCH_PERCENTAGE: u64 = 20; - const MAX_PERCENTAGE: u64 = 100; - const MAX_MATCH_AMOUNT: u64 = 100_000_000; + + // Default parameters for new melees. + const DEFAULT_DURATION: u64 = 36 * 3_600_000_000; + const DEFAULT_LOCK_IN_PERIOD: u64 = 12 * 3_600_000_000; + const DEFAULT_AVAILABLE_REWADS: u64 = 1500 * 100_000_000; + const DEFAULT_MATCH_PERCENTAGE: u64 = 50; + const DEFAULT_MAX_MATCH_AMOUNT: u64 = 10 * 100_000_000; struct Melee has store { /// 1-indexed for conformity with emojicoin market ID indexing. @@ -47,12 +47,16 @@ module arena::emojicoin_arena { market_metadatas: vector, /// In microseconds. start_time: u64, - /// How long after start time users can lock in. - lock_in_period: u64, /// How long melee lasts after start time. duration: u64, - /// Amount of rewards available for distribution in octas. + /// How long after start time users can lock in. + lock_in_period: u64, + /// Amount of rewards available for distribution in octas, conditional on vault balance. available_rewards: u64, + /// Percentage of user's input amount to match in octas, when locking in. + match_percentage: u64, + /// Maximum amount of APT to match in octas, when locking in. + max_match_amount: u64, /// Active entrants. entrants: SmartTable, /// Number of melee-specific swaps. @@ -68,13 +72,16 @@ module arena::emojicoin_arena { struct MeleeEscrow has key { /// `Melee.market_id`. melee_id: u64, + /// Emojicoin 0 holdings. emojicoin_0: Coin, + /// Emojicoin 1 holdings. emojicoin_1: Coin, /// Volume of user's melee-specific swaps in octas. melee_swaps_volume: u128, /// Number of swaps user has executed during the melee. n_melee_swaps: u64, - /// APT entered into the melee. Normalized to equivalent amount when topping off. + /// APT entered into the melee, used as a benchmark for PnL calculations. Normalized when + /// topping off, reset to zero when exiting. octas_entered: u64, /// Octas user must pay to exit the melee, if they have locked in. tap_out_fee: u64 @@ -88,7 +95,17 @@ module arena::emojicoin_arena { /// Map from a sorted combination of market IDs (lower ID first) to the melee serial ID. melee_ids_by_market_ids: SmartTable, u64>, /// Approves transfers from the vault. - signer_capability: SignerCapability + signer_capability: SignerCapability, + /// `Melee.duration` for new melees. + new_melee_duration: u64, + /// `Melee.lock_in_period` for new melees. + new_melee_lock_in_period: u64, + /// `Melee.available_rewards` for new melees. + new_melee_available_rewards: u64, + /// `Melee.match_percentage` for new melees. + new_melee_match_percentage: u64, + /// `Melee.max_match_amount` for new melees. + new_melee_max_match_amount: u64 } struct UserMelees has key { @@ -160,8 +177,10 @@ module arena::emojicoin_arena { current_melee_ref.start_time + current_melee_ref.lock_in_period; let lock_ins_still_allowed = timestamp::now_microseconds() < lock_in_period_end_time; - if (current_tap_out_fee < MAX_MATCH_AMOUNT && lock_ins_still_allowed) { - let eligible_match_amount = MAX_MATCH_AMOUNT - current_tap_out_fee; + if (current_tap_out_fee < current_melee_ref.max_match_amount + && lock_ins_still_allowed) { + let eligible_match_amount = + current_melee_ref.max_match_amount - current_tap_out_fee; eligible_match_amount = if (eligible_match_amount < current_melee_ref.available_rewards) { eligible_match_amount @@ -181,7 +200,8 @@ module arena::emojicoin_arena { }; let requested_match_amount = ( - ((input_amount as u128) * (MATCH_PERCENTAGE as u128) + ((input_amount as u128) + * (current_melee_ref.match_percentage as u128) / (MAX_PERCENTAGE as u128)) as u64 ); let actual_match_amount = @@ -447,10 +467,27 @@ module arena::emojicoin_arena { if (exit_once_done) exit_inner(swapper, melee_id, false); } - fun init_module(arena: &signer) { + fun init_module(arena: &signer) acquires Registry { + // Store registry resource. + let (vault_signer, signer_capability) = + account::create_resource_account(arena, REGISTRY_SEED); + move_to( + arena, + Registry { + melees_by_id: smart_table::new(), + melee_ids_by_market_ids: smart_table::new(), + signer_capability, + new_melee_duration: DEFAULT_DURATION, + new_melee_lock_in_period: DEFAULT_LOCK_IN_PERIOD, + new_melee_available_rewards: DEFAULT_AVAILABLE_REWADS, + new_melee_match_percentage: DEFAULT_MATCH_PERCENTAGE, + new_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT + } + ); + coin::register(&vault_signer); + // Use pseudo-randomness to get market IDs for the first melee, since randomness is not // supported during `init_module`. - let time = timestamp::now_microseconds(); let n_markets = get_n_registered_markets(); let market_id_0 = pseudo_random_market_id(n_markets); let market_id_1; @@ -458,43 +495,13 @@ module arena::emojicoin_arena { market_id_1 = pseudo_random_market_id(n_markets); if (market_id_1 != market_id_0) break; }; - let market_ids = sort_unique_market_ids(market_id_0, market_id_1); - - // Initialize first melee. - let start_time = - last_period_boundary(time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR); - let melees_by_id = smart_table::new(); - melees_by_id.add( - 1, - Melee { - melee_id: 1, - market_metadatas: market_ids.map_ref(|market_id_ref| { - option::destroy_some( - emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) - ) - }), - start_time, - lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, - duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR, - available_rewards: REWARDS_PER_MELEE, - entrants: smart_table::new(), - n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), - emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), - emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() - } - ); - let melee_ids_by_market_ids = smart_table::new(); - melee_ids_by_market_ids.add(market_ids, 1); - // Store registry resource. - let (vault_signer, signer_capability) = - account::create_resource_account(arena, REGISTRY_SEED); - move_to( - arena, - Registry { melees_by_id, melee_ids_by_market_ids, signer_capability } + // Register the first melee. + register_melee( + &mut Registry[@arena], + 0, + sort_unique_market_ids(market_id_0, market_id_1) ); - coin::register(&vault_signer); } inline fun borrow_current_melee_ref(): &Melee { @@ -552,32 +559,8 @@ module arena::emojicoin_arena { let n_melees = registry_ref.melees_by_id.length(); let most_recent_melee_ref = registry_ref.melees_by_id.borrow(n_melees); if (time >= most_recent_melee_ref.start_time + most_recent_melee_ref.duration) { - let market_ids = next_melee_market_ids(); - let melee_id = n_melees + 1; - let registry_ref_mut = &mut Registry[@arena]; - registry_ref_mut.melees_by_id.add( - melee_id, - Melee { - melee_id, - market_metadatas: market_ids.map_ref(|market_id_ref| { - option::destroy_some( - emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) - ) - }), - start_time: last_period_boundary( - time, LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR - ), - lock_in_period: LOCK_IN_PERIOD_HOURS * MICROSECONDS_PER_HOUR, - duration: MELEE_DURATION_HOURS * MICROSECONDS_PER_HOUR, - available_rewards: REWARDS_PER_MELEE, - entrants: smart_table::new(), - n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), - emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), - emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() - } - ); - registry_ref_mut.melee_ids_by_market_ids.add(market_ids, melee_id); + let next_melee_market_ids = next_melee_market_ids(registry_ref); + register_melee(&mut Registry[@arena], n_melees, next_melee_market_ids); true } else false } @@ -592,7 +575,7 @@ module arena::emojicoin_arena { (time / period) * period } - inline fun next_melee_market_ids(): vector { + inline fun next_melee_market_ids(registry_ref: &Registry): vector { let n_markets = get_n_registered_markets(); let market_ids; loop { @@ -600,7 +583,7 @@ module arena::emojicoin_arena { let market_id_1 = random_market_id(n_markets); if (market_id_0 == market_id_1) continue; market_ids = sort_unique_market_ids(market_id_0, market_id_1); - if (!Registry[@arena].melee_ids_by_market_ids.contains(market_ids)) + if (!registry_ref.melee_ids_by_market_ids.contains(market_ids)) break; }; market_ids @@ -617,6 +600,40 @@ module arena::emojicoin_arena { randomness::u64_range(0, n_markets) + 1 } + inline fun register_melee( + registry_ref_mut: &mut Registry, + n_melees: u64, + sorted_unique_market_ids: vector + ) { + let melee_id = n_melees + 1; + registry_ref_mut.melees_by_id.add( + melee_id, + Melee { + melee_id, + market_metadatas: sorted_unique_market_ids.map_ref(|market_id_ref| { + option::destroy_some( + emojicoin_dot_fun::market_metadata_by_market_id(*market_id_ref) + ) + }), + start_time: last_period_boundary( + timestamp::now_microseconds(), + registry_ref_mut.new_melee_lock_in_period + ), + lock_in_period: registry_ref_mut.new_melee_lock_in_period, + duration: registry_ref_mut.new_melee_duration, + available_rewards: registry_ref_mut.new_melee_available_rewards, + match_percentage: registry_ref_mut.new_melee_match_percentage, + max_match_amount: registry_ref_mut.new_melee_max_match_amount, + entrants: smart_table::new(), + n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), + emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), + emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() + } + ); + registry_ref_mut.melee_ids_by_market_ids.add(sorted_unique_market_ids, melee_id); + } + inline fun sort_unique_market_ids(market_id_0: u64, market_id_1: u64): vector { if (market_id_0 < market_id_1) { vector[market_id_0, market_id_1] From fd76e2fd4fad0e7bdfc08a29e3d256fb31d7938b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:25:21 -0800 Subject: [PATCH 20/64] Add new melee parameter setters --- .../sources/emojicoin_arena.move | 77 +++++++++++++++++-- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 720f31b76..46dd9f329 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -22,6 +22,12 @@ module arena::emojicoin_arena { /// Signer does not correspond to arena account. const E_NOT_ARENA: u64 = 0; + /// New melee duration is too short. + const E_NEW_DURATION_TOO_SHORT: u64 = 1; + /// New melee lock-in period is too long. + const E_NEW_LOCK_IN_PERIOD_TOO_LONG: u64 = 2; + /// New melee match percentage is too high. + const E_NEW_MATCH_PERCENTAGE_TOO_HIGH: u64 = 3; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -114,17 +120,62 @@ module arena::emojicoin_arena { } public entry fun fund_vault(arena: &signer, amount: u64) acquires Registry { - assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); - let vault_address = - account::get_signer_capability_address(&Registry[@arena].signer_capability); - aptos_account::transfer(arena, vault_address, amount); + aptos_account::transfer( + arena, + account::get_signer_capability_address( + &borrow_registry_ref_checked(arena).signer_capability + ), + amount + ); + } + + public entry fun set_new_melee_available_rewards( + arena: &signer, amount: u64 + ) acquires Registry { + borrow_registry_ref_mut_checked(arena).new_melee_available_rewards = amount; + } + + public entry fun set_new_melee_duration(arena: &signer, duration: u64) acquires Registry { + let registry_ref_mut = borrow_registry_ref_mut_checked(arena); + assert!( + duration > registry_ref_mut.new_melee_lock_in_period, + E_NEW_DURATION_TOO_SHORT + ); + registry_ref_mut.new_melee_duration = duration; + } + + public entry fun set_new_melee_lock_in_period( + arena: &signer, lock_in_period: u64 + ) acquires Registry { + let registry_ref_mut = borrow_registry_ref_mut_checked(arena); + assert!( + lock_in_period < registry_ref_mut.new_melee_duration, + E_NEW_LOCK_IN_PERIOD_TOO_LONG + ); + registry_ref_mut.new_melee_lock_in_period = lock_in_period; + } + + public entry fun set_new_melee_match_percentage( + arena: &signer, match_percentage: u64 + ) acquires Registry { + assert!(match_percentage <= MAX_PERCENTAGE, E_NEW_MATCH_PERCENTAGE_TOO_HIGH); + borrow_registry_ref_mut_checked(arena).new_melee_match_percentage = match_percentage; + } + + public entry fun set_new_melee_max_match_amount( + arena: &signer, max_match_amount: u64 + ) acquires Registry { + borrow_registry_ref_mut_checked(arena).new_melee_max_match_amount = max_match_amount; } public entry fun withdraw_from_vault(arena: &signer, amount: u64) acquires Registry { - assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); - let vault_signer = - account::create_signer_with_capability(&Registry[@arena].signer_capability); - aptos_account::transfer(&vault_signer, @arena, amount); + aptos_account::transfer( + &account::create_signer_with_capability( + &borrow_registry_ref_checked(arena).signer_capability + ), + @arena, + amount + ); } #[randomness] @@ -510,6 +561,16 @@ module arena::emojicoin_arena { registry_ref.melees_by_id.borrow(n_melees) } + inline fun borrow_registry_ref_checked(arena: &signer): &Registry { + assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); + &Registry[@arena] + } + + inline fun borrow_registry_ref_mut_checked(arena: &signer): &mut Registry { + assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); + &mut Registry[@arena] + } + inline fun exit_inner( participant: &signer, melee_id: u64, may_have_to_pay_tap_out_fee: bool ) acquires Registry { From 3dbe1d944ae889a27b5d68da867842157864cd23 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:00:38 -0800 Subject: [PATCH 21/64] Add more abstractions, tuning --- .../sources/emojicoin_arena.move | 87 +++++++++++-------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 46dd9f329..c5eab18e1 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -28,6 +28,10 @@ module arena::emojicoin_arena { const E_NEW_LOCK_IN_PERIOD_TOO_LONG: u64 = 2; /// New melee match percentage is too high. const E_NEW_MATCH_PERCENTAGE_TOO_HIGH: u64 = 3; + /// User's melee escrow has nonzero emojicoin 0 balance. + const E_ENTER_COIN_BALANCE_0: u64 = 4; + /// User's melee escrow has nonzero emojicoin 1 balance. + const E_ENTER_COIN_BALANCE_1: u64 = 5; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -185,18 +189,21 @@ module arena::emojicoin_arena { input_amount: u64, lock_in: bool ) acquires MeleeEscrow, Registry, UserMelees { - if (crank_schedule()) return; // Can not enter melee if cranking ends it. + let (melee_just_ended, registry_ref_mut, _time, n_melees_before_cranking) = + crank_schedule(); + if (melee_just_ended) return; // Can not enter melee if cranking ends it. // Verify coin types for the current melee by calling the market view function. - let current_melee_ref = borrow_current_melee_ref(); - let market_metadatas = current_melee_ref.market_metadatas; + let current_melee_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(n_melees_before_cranking); + let market_metadatas = current_melee_ref_mut.market_metadatas; let (_, market_address_0, _) = unpack_market_metadata(market_metadatas[0]); market_view(market_address_0); let (_, market_address_1, _) = unpack_market_metadata(market_metadatas[1]); market_view(market_address_1); // Create escrow and user melees resources if they don't exist. - let melee_id = current_melee_ref.melee_id; + let melee_id = current_melee_ref_mut.melee_id; let entrant_address = signer::address_of(entrant); if (!exists>(entrant_address)) { move_to( @@ -218,30 +225,34 @@ module arena::emojicoin_arena { user_melee_ids_ref_mut.add(melee_id, Nil {}); }; + // Verify user's melee escrow has no coins in it. + let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; + assert!(coin::value(&escrow_ref_mut.emojicoin_0) == 0, E_ENTER_COIN_BALANCE_0); + assert!(coin::value(&escrow_ref_mut.emojicoin_1) == 0, E_ENTER_COIN_BALANCE_1); + // Try locking in if user selects the option. let match_amount = if (lock_in) { - let escrow_ref_mut = - &mut MeleeEscrow[entrant_address]; let current_tap_out_fee = escrow_ref_mut.tap_out_fee; let lock_in_period_end_time = - current_melee_ref.start_time + current_melee_ref.lock_in_period; + current_melee_ref_mut.start_time + + current_melee_ref_mut.lock_in_period; let lock_ins_still_allowed = timestamp::now_microseconds() < lock_in_period_end_time; - if (current_tap_out_fee < current_melee_ref.max_match_amount + if (current_tap_out_fee < current_melee_ref_mut.max_match_amount && lock_ins_still_allowed) { let eligible_match_amount = - current_melee_ref.max_match_amount - current_tap_out_fee; + current_melee_ref_mut.max_match_amount - current_tap_out_fee; eligible_match_amount = if (eligible_match_amount - < current_melee_ref.available_rewards) { + < current_melee_ref_mut.available_rewards) { eligible_match_amount } else { - current_melee_ref.available_rewards + current_melee_ref_mut.available_rewards }; let vault_balance = coin::balance( account::get_signer_capability_address( - &Registry[@arena].signer_capability + ®istry_ref_mut.signer_capability ) ); eligible_match_amount = if (eligible_match_amount < vault_balance) { @@ -252,7 +263,7 @@ module arena::emojicoin_arena { let requested_match_amount = ( ((input_amount as u128) - * (current_melee_ref.match_percentage as u128) + * (current_melee_ref_mut.match_percentage as u128) / (MAX_PERCENTAGE as u128)) as u64 ); let actual_match_amount = @@ -339,7 +350,9 @@ module arena::emojicoin_arena { entry fun exit( participant: &signer, melee_id: u64 ) acquires MeleeEscrow, Registry { - exit_inner(participant, melee_id, !crank_schedule()); + let (melee_just_ended, _registry_ref_mut, _time, _n_melees_before_cranking) = + crank_schedule(); + exit_inner(participant, melee_id, !melee_just_ended); } #[randomness] @@ -349,7 +362,8 @@ module arena::emojicoin_arena { market_addresses: vector
, buy_emojicoin_0: bool ) acquires MeleeEscrow, Registry { - let exit_once_done = crank_schedule(); + let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = + crank_schedule(); // Return early if type arguments or melee ID are passed incorrectly, but only after // cranking schedule. @@ -555,12 +569,6 @@ module arena::emojicoin_arena { ); } - inline fun borrow_current_melee_ref(): &Melee { - let registry_ref = &Registry[@arena]; - let n_melees = registry_ref.melees_by_id.length(); - registry_ref.melees_by_id.borrow(n_melees) - } - inline fun borrow_registry_ref_checked(arena: &signer): &Registry { assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); &Registry[@arena] @@ -572,7 +580,7 @@ module arena::emojicoin_arena { } inline fun exit_inner( - participant: &signer, melee_id: u64, may_have_to_pay_tap_out_fee: bool + participant: &signer, melee_id: u64, melee_is_current: bool ) acquires Registry { let participant_address = signer::address_of(participant); // Only allow exit if user has corresponding melee resource and melee ID matches. @@ -582,7 +590,7 @@ module arena::emojicoin_arena { // Only allow exit if melee ID matches. if (escrow_ref_mut.melee_id == melee_id) { // Update available rewards and transfer tap out fee to vault if applicable. - if (may_have_to_pay_tap_out_fee) { + if (melee_is_current) { let registry_ref_mut = &mut Registry[@arena]; let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); @@ -613,17 +621,21 @@ module arena::emojicoin_arena { } } - /// Cranks schedule and returns `true` if a melee has ended as a result. - inline fun crank_schedule(): bool { + /// Cranks schedule and returns `true` if a melee has ended as a result, along with assorted + /// variables, to reduce borrows and lookups in the caller. + inline fun crank_schedule(): (bool, &mut Registry, u64, u64) { let time = timestamp::now_microseconds(); - let registry_ref = &Registry[@arena]; - let n_melees = registry_ref.melees_by_id.length(); - let most_recent_melee_ref = registry_ref.melees_by_id.borrow(n_melees); - if (time >= most_recent_melee_ref.start_time + most_recent_melee_ref.duration) { - let next_melee_market_ids = next_melee_market_ids(registry_ref); - register_melee(&mut Registry[@arena], n_melees, next_melee_market_ids); - true - } else false + let registry_ref_mut = &mut Registry[@arena]; + let n_melees_before_cranking = registry_ref_mut.melees_by_id.length(); + let current_melee_ref = + registry_ref_mut.melees_by_id.borrow(n_melees_before_cranking); + let cranked = + if (time >= current_melee_ref.start_time + current_melee_ref.duration) { + let market_ids = next_melee_market_ids(registry_ref_mut); + register_melee(registry_ref_mut, n_melees_before_cranking, market_ids); + true + } else false; + (cranked, registry_ref_mut, time, n_melees_before_cranking) } inline fun get_n_registered_markets(): u64 { @@ -636,7 +648,8 @@ module arena::emojicoin_arena { (time / period) * period } - inline fun next_melee_market_ids(registry_ref: &Registry): vector { + /// Accepts a mutable reference to avoid freezing references up the stack. + inline fun next_melee_market_ids(registry_ref_mut: &mut Registry): vector { let n_markets = get_n_registered_markets(); let market_ids; loop { @@ -644,7 +657,7 @@ module arena::emojicoin_arena { let market_id_1 = random_market_id(n_markets); if (market_id_0 == market_id_1) continue; market_ids = sort_unique_market_ids(market_id_0, market_id_1); - if (!registry_ref.melee_ids_by_market_ids.contains(market_ids)) + if (!registry_ref_mut.melee_ids_by_market_ids.contains(market_ids)) break; }; market_ids @@ -663,10 +676,10 @@ module arena::emojicoin_arena { inline fun register_melee( registry_ref_mut: &mut Registry, - n_melees: u64, + n_melees_before_registration: u64, sorted_unique_market_ids: vector ) { - let melee_id = n_melees + 1; + let melee_id = n_melees_before_registration + 1; registry_ref_mut.melees_by_id.add( melee_id, Melee { From ce98ef552568724711e4d0d512f8b03340ddb773 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:32:07 -0800 Subject: [PATCH 22/64] Add matching abstraction --- .../sources/emojicoin_arena.move | 149 ++++++++++-------- 1 file changed, 85 insertions(+), 64 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index c5eab18e1..304c9a4f9 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -93,7 +93,8 @@ module arena::emojicoin_arena { /// APT entered into the melee, used as a benchmark for PnL calculations. Normalized when /// topping off, reset to zero when exiting. octas_entered: u64, - /// Octas user must pay to exit the melee, if they have locked in. + /// Octas user must pay to exit the melee, if they have locked in. Equivalent to the amount + /// of APT they have been matched by locking in. tap_out_fee: u64 } @@ -189,11 +190,11 @@ module arena::emojicoin_arena { input_amount: u64, lock_in: bool ) acquires MeleeEscrow, Registry, UserMelees { - let (melee_just_ended, registry_ref_mut, _time, n_melees_before_cranking) = + let (melee_just_ended, registry_ref_mut, time, n_melees_before_cranking) = crank_schedule(); if (melee_just_ended) return; // Can not enter melee if cranking ends it. - // Verify coin types for the current melee by calling the market view function. + // Verify that coin types are for the current melee by calling the market view function. let current_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(n_melees_before_cranking); let market_metadatas = current_melee_ref_mut.market_metadatas; @@ -225,72 +226,39 @@ module arena::emojicoin_arena { user_melee_ids_ref_mut.add(melee_id, Nil {}); }; - // Verify user's melee escrow has no coins in it. + // Verify that user does not split balance between the two emojicoins. let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; - assert!(coin::value(&escrow_ref_mut.emojicoin_0) == 0, E_ENTER_COIN_BALANCE_0); - assert!(coin::value(&escrow_ref_mut.emojicoin_1) == 0, E_ENTER_COIN_BALANCE_1); + if (buy_emojicoin_0) + assert!( + coin::value(&escrow_ref_mut.emojicoin_1) == 0, E_ENTER_COIN_BALANCE_1 + ) + else + assert!( + coin::value(&escrow_ref_mut.emojicoin_0) == 0, E_ENTER_COIN_BALANCE_0 + ); - // Try locking in if user selects the option. + // Try matching user's contribution if they elect to lock in. let match_amount = if (lock_in) { - let current_tap_out_fee = escrow_ref_mut.tap_out_fee; - let lock_in_period_end_time = - current_melee_ref_mut.start_time - + current_melee_ref_mut.lock_in_period; - let lock_ins_still_allowed = - timestamp::now_microseconds() < lock_in_period_end_time; - if (current_tap_out_fee < current_melee_ref_mut.max_match_amount - && lock_ins_still_allowed) { - let eligible_match_amount = - current_melee_ref_mut.max_match_amount - current_tap_out_fee; - eligible_match_amount = if (eligible_match_amount - < current_melee_ref_mut.available_rewards) { - eligible_match_amount - } else { - current_melee_ref_mut.available_rewards - }; - let vault_balance = - coin::balance( - account::get_signer_capability_address( - ®istry_ref_mut.signer_capability - ) - ); - eligible_match_amount = if (eligible_match_amount < vault_balance) { - eligible_match_amount - } else { - vault_balance - }; - let requested_match_amount = - ( - ((input_amount as u128) - * (current_melee_ref_mut.match_percentage as u128) - / (MAX_PERCENTAGE as u128)) as u64 - ); - let actual_match_amount = - if (eligible_match_amount < requested_match_amount) { - eligible_match_amount - } else { - requested_match_amount - }; - if (actual_match_amount > 0) { - escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee - + actual_match_amount; - let registry_ref_mut = &mut Registry[@arena]; - let available_rewards_ref_mut = - &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - - actual_match_amount; - let vault_signer = - account::create_signer_with_capability( - ®istry_ref_mut.signer_capability - ); - aptos_account::transfer( - &vault_signer, entrant_address, actual_match_amount - ); - }; - actual_match_amount - } else { 0 } + match_amount( + input_amount, + escrow_ref_mut, + current_melee_ref_mut, + registry_ref_mut, + time + ) } else { 0 }; + if (match_amount > 0) { + escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; + let available_rewards_ref_mut = + &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut - match_amount; + let vault_signer = + account::create_signer_with_capability( + ®istry_ref_mut.signer_capability + ); + aptos_account::transfer(&vault_signer, entrant_address, match_amount); + }; // Execute a swap then immediately move funds into escrow. let input_amount_after_matching = input_amount + match_amount; @@ -648,6 +616,59 @@ module arena::emojicoin_arena { (time / period) * period } + /// Uses mutable references to avoid freezing references up the stack. + inline fun match_amount( + input_amount: u64, + escrow_ref_mut: &mut MeleeEscrow, + current_melee_ref_mut: &mut Melee, + registry_ref_mut: &mut Registry, + time: u64 + ): u64 { + // Can only get matched if lock-in period is still active and matching not maxed out yet. + let lock_ins_still_allowed = + time + < current_melee_ref_mut.start_time + + current_melee_ref_mut.lock_in_period; + let tap_out_fee = escrow_ref_mut.tap_out_fee; + let can_match_more = tap_out_fee < current_melee_ref_mut.max_match_amount; + if (lock_ins_still_allowed && can_match_more) { + // Eligible match amount is the maximum match amount minus the tap out fee. + let eligible_match_amount = + current_melee_ref_mut.max_match_amount - tap_out_fee; + // Correct eligible match amount for available melee rewards and vault balance. + eligible_match_amount = if (eligible_match_amount + < current_melee_ref_mut.available_rewards) { + eligible_match_amount + } else { + current_melee_ref_mut.available_rewards + }; + let vault_balance = + coin::balance( + account::get_signer_capability_address( + ®istry_ref_mut.signer_capability + ) + ); + eligible_match_amount = if (eligible_match_amount < vault_balance) { + eligible_match_amount + } else { + vault_balance + }; + // Requested match amount is the input amount times the match percentage. + let requested_match_amount = + ( + ((input_amount as u128) + * (current_melee_ref_mut.match_percentage as u128) + / (MAX_PERCENTAGE as u128)) as u64 + ); + // Correct actual match amount for the minimum of eligible and requested match amounts. + if (eligible_match_amount < requested_match_amount) { + eligible_match_amount + } else { + requested_match_amount + } + } else { 0 } + } + /// Accepts a mutable reference to avoid freezing references up the stack. inline fun next_melee_market_ids(registry_ref_mut: &mut Registry): vector { let n_markets = get_n_registered_markets(); From d8515095700e9a4409736f2bb0bceba65dbca1e2 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:32:18 -0800 Subject: [PATCH 23/64] Add entered/exited melee indexing --- .../sources/emojicoin_arena.move | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 304c9a4f9..3684f36c1 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -98,7 +98,7 @@ module arena::emojicoin_arena { tap_out_fee: u64 } - struct Nil has store {} + struct Nil has drop, store {} struct Registry has key { /// Map from melee serial ID to the melee. @@ -121,7 +121,11 @@ module arena::emojicoin_arena { struct UserMelees has key { /// Set of serial IDs of all melees the user has entered. - melee_ids: SmartTable + entered_melee_ids: SmartTable, + /// Set of serial IDs of all melees the user has exited. + exited_melee_ids: SmartTable, + /// Set of serial IDs of all melees the user has entered but not exited. + unexited_melee_ids: SmartTable } public entry fun fund_vault(arena: &signer, amount: u64) acquires Registry { @@ -220,12 +224,23 @@ module arena::emojicoin_arena { } ); if (!exists(entrant_address)) { - move_to(entrant, UserMelees { melee_ids: smart_table::new() }); + move_to( + entrant, + UserMelees { + entered_melee_ids: smart_table::new(), + exited_melee_ids: smart_table::new(), + unexited_melee_ids: smart_table::new() + } + ); }; - let user_melee_ids_ref_mut = &mut UserMelees[entrant_address].melee_ids; - user_melee_ids_ref_mut.add(melee_id, Nil {}); }; + // Update user melees resource. + let user_melees_ref_mut = &mut UserMelees[entrant_address]; + ensure_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); + ensure_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); + ensure_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + // Verify that user does not split balance between the two emojicoins. let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; if (buy_emojicoin_0) @@ -317,7 +332,7 @@ module arena::emojicoin_arena { #[randomness] entry fun exit( participant: &signer, melee_id: u64 - ) acquires MeleeEscrow, Registry { + ) acquires MeleeEscrow, Registry, UserMelees { let (melee_just_ended, _registry_ref_mut, _time, _n_melees_before_cranking) = crank_schedule(); exit_inner(participant, melee_id, !melee_just_ended); @@ -329,7 +344,7 @@ module arena::emojicoin_arena { melee_id: u64, market_addresses: vector
, buy_emojicoin_0: bool - ) acquires MeleeEscrow, Registry { + ) acquires MeleeEscrow, Registry, UserMelees { let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = crank_schedule(); @@ -547,6 +562,22 @@ module arena::emojicoin_arena { &mut Registry[@arena] } + inline fun ensure_contains( + map_ref_mut: &mut SmartTable, key: u64 + ) { + if (!map_ref_mut.contains(key)) { + map_ref_mut.add(key, Nil {}); + } + } + + inline fun ensure_not_contains( + map_ref_mut: &mut SmartTable, key: u64 + ) { + if (map_ref_mut.contains(key)) { + map_ref_mut.remove(key); + } + } + inline fun exit_inner( participant: &signer, melee_id: u64, melee_is_current: bool ) acquires Registry { @@ -576,6 +607,7 @@ module arena::emojicoin_arena { ); *tap_out_fee_ref_mut = 0; }; + // Move emojicoin balances out of escrow. aptos_account::deposit_coins( participant_address, @@ -585,6 +617,13 @@ module arena::emojicoin_arena { participant_address, coin::extract_all(&mut escrow_ref_mut.emojicoin_1) ); + + // Update user melees resource. + let user_melees_ref_mut = &mut UserMelees[participant_address]; + ensure_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + ensure_not_contains( + &mut user_melees_ref_mut.unexited_melee_ids, melee_id + ); }; } } From 75c75700b4db68ff25cee851ed4ce08c1697c26d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:45:52 -0800 Subject: [PATCH 24/64] Switch to interpolation without lock-in period --- .../sources/emojicoin_arena.move | 115 ++++++------------ 1 file changed, 37 insertions(+), 78 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 3684f36c1..5c59f9902 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -8,6 +8,7 @@ module arena::emojicoin_arena { use aptos_framework::randomness::Self; use aptos_framework::timestamp; use aptos_std::smart_table::{Self, SmartTable}; + use aptos_std::math64::min; use arena::pseudo_randomness; use emojicoin_dot_fun::emojicoin_dot_fun::{ Self, @@ -38,6 +39,7 @@ module arena::emojicoin_arena { const U64_MAX: u64 = 0xffffffffffffffff; const MAX_PERCENTAGE: u64 = 100; + const MIN_MELEE_DURATION: u64 = 1 * 3_600_000_000; /// Flat integrator fee. const INTEGRATOR_FEE_RATE_BPS: u8 = 100; @@ -45,10 +47,9 @@ module arena::emojicoin_arena { // Default parameters for new melees. const DEFAULT_DURATION: u64 = 36 * 3_600_000_000; - const DEFAULT_LOCK_IN_PERIOD: u64 = 12 * 3_600_000_000; - const DEFAULT_AVAILABLE_REWADS: u64 = 1500 * 100_000_000; - const DEFAULT_MATCH_PERCENTAGE: u64 = 50; - const DEFAULT_MAX_MATCH_AMOUNT: u64 = 10 * 100_000_000; + const DEFAULT_AVAILABLE_REWADS: u64 = 1000 * 100_000_000; + const DEFAULT_MAX_MATCH_PERCENTAGE: u64 = 50; + const DEFAULT_MAX_MATCH_AMOUNT: u64 = 5 * 100_000_000; struct Melee has store { /// 1-indexed for conformity with emojicoin market ID indexing. @@ -59,12 +60,10 @@ module arena::emojicoin_arena { start_time: u64, /// How long melee lasts after start time. duration: u64, - /// How long after start time users can lock in. - lock_in_period: u64, /// Amount of rewards available for distribution in octas, conditional on vault balance. available_rewards: u64, - /// Percentage of user's input amount to match in octas, when locking in. - match_percentage: u64, + /// Max percentage of user's input amount to match in octas, when locking in. + max_match_percentage: u64, /// Maximum amount of APT to match in octas, when locking in. max_match_amount: u64, /// Active entrants. @@ -93,8 +92,8 @@ module arena::emojicoin_arena { /// APT entered into the melee, used as a benchmark for PnL calculations. Normalized when /// topping off, reset to zero when exiting. octas_entered: u64, - /// Octas user must pay to exit the melee, if they have locked in. Equivalent to the amount - /// of APT they have been matched by locking in. + /// Octas user must pay to exit the melee before it ends, if they have locked in. Equivalent + /// to the amount of APT they have been matched by locking in. tap_out_fee: u64 } @@ -109,12 +108,10 @@ module arena::emojicoin_arena { signer_capability: SignerCapability, /// `Melee.duration` for new melees. new_melee_duration: u64, - /// `Melee.lock_in_period` for new melees. - new_melee_lock_in_period: u64, /// `Melee.available_rewards` for new melees. new_melee_available_rewards: u64, - /// `Melee.match_percentage` for new melees. - new_melee_match_percentage: u64, + /// `Melee.max_match_percentage` for new melees. + new_melee_max_match_percentage: u64, /// `Melee.max_match_amount` for new melees. new_melee_max_match_amount: u64 } @@ -146,29 +143,15 @@ module arena::emojicoin_arena { public entry fun set_new_melee_duration(arena: &signer, duration: u64) acquires Registry { let registry_ref_mut = borrow_registry_ref_mut_checked(arena); - assert!( - duration > registry_ref_mut.new_melee_lock_in_period, - E_NEW_DURATION_TOO_SHORT - ); + assert!(duration >= MIN_MELEE_DURATION, E_NEW_DURATION_TOO_SHORT); registry_ref_mut.new_melee_duration = duration; } - public entry fun set_new_melee_lock_in_period( - arena: &signer, lock_in_period: u64 + public entry fun set_new_melee_max_match_percentage( + arena: &signer, max_match_percentage: u64 ) acquires Registry { - let registry_ref_mut = borrow_registry_ref_mut_checked(arena); - assert!( - lock_in_period < registry_ref_mut.new_melee_duration, - E_NEW_LOCK_IN_PERIOD_TOO_LONG - ); - registry_ref_mut.new_melee_lock_in_period = lock_in_period; - } - - public entry fun set_new_melee_match_percentage( - arena: &signer, match_percentage: u64 - ) acquires Registry { - assert!(match_percentage <= MAX_PERCENTAGE, E_NEW_MATCH_PERCENTAGE_TOO_HIGH); - borrow_registry_ref_mut_checked(arena).new_melee_match_percentage = match_percentage; + assert!(max_match_percentage <= MAX_PERCENTAGE, E_NEW_MATCH_PERCENTAGE_TOO_HIGH); + borrow_registry_ref_mut_checked(arena).new_melee_max_match_percentage = max_match_percentage; } public entry fun set_new_melee_max_match_amount( @@ -526,9 +509,8 @@ module arena::emojicoin_arena { melee_ids_by_market_ids: smart_table::new(), signer_capability, new_melee_duration: DEFAULT_DURATION, - new_melee_lock_in_period: DEFAULT_LOCK_IN_PERIOD, new_melee_available_rewards: DEFAULT_AVAILABLE_REWADS, - new_melee_match_percentage: DEFAULT_MATCH_PERCENTAGE, + new_melee_max_match_percentage: DEFAULT_MAX_MATCH_PERCENTAGE, new_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT } ); @@ -663,49 +645,28 @@ module arena::emojicoin_arena { registry_ref_mut: &mut Registry, time: u64 ): u64 { - // Can only get matched if lock-in period is still active and matching not maxed out yet. - let lock_ins_still_allowed = - time - < current_melee_ref_mut.start_time - + current_melee_ref_mut.lock_in_period; - let tap_out_fee = escrow_ref_mut.tap_out_fee; - let can_match_more = tap_out_fee < current_melee_ref_mut.max_match_amount; - if (lock_ins_still_allowed && can_match_more) { - // Eligible match amount is the maximum match amount minus the tap out fee. - let eligible_match_amount = - current_melee_ref_mut.max_match_amount - tap_out_fee; - // Correct eligible match amount for available melee rewards and vault balance. - eligible_match_amount = if (eligible_match_amount - < current_melee_ref_mut.available_rewards) { - eligible_match_amount - } else { - current_melee_ref_mut.available_rewards - }; - let vault_balance = + min( + // Scale down input amount for matching percentage and time elapsed in one compound + // operation, to reduce truncation errors. + ((input_amount as u256) + * (current_melee_ref_mut.max_match_percentage as u256) + * ((time as u256) - (current_melee_ref_mut.start_time as u256)) + / ((MAX_PERCENTAGE as u256) * (current_melee_ref_mut.duration as u256)) as u64), + min( + // Correct for vault balance. coin::balance( account::get_signer_capability_address( ®istry_ref_mut.signer_capability ) - ); - eligible_match_amount = if (eligible_match_amount < vault_balance) { - eligible_match_amount - } else { - vault_balance - }; - // Requested match amount is the input amount times the match percentage. - let requested_match_amount = - ( - ((input_amount as u128) - * (current_melee_ref_mut.match_percentage as u128) - / (MAX_PERCENTAGE as u128)) as u64 - ); - // Correct actual match amount for the minimum of eligible and requested match amounts. - if (eligible_match_amount < requested_match_amount) { - eligible_match_amount - } else { - requested_match_amount - } - } else { 0 } + ), + min( + // Correct for available rewards in current melee. + current_melee_ref_mut.available_rewards, + // Correct for max match amount user is eligible for. + current_melee_ref_mut.max_match_amount - escrow_ref_mut.tap_out_fee + ) + ) + ) } /// Accepts a mutable reference to avoid freezing references up the stack. @@ -750,13 +711,11 @@ module arena::emojicoin_arena { ) }), start_time: last_period_boundary( - timestamp::now_microseconds(), - registry_ref_mut.new_melee_lock_in_period + timestamp::now_microseconds(), registry_ref_mut.new_melee_duration ), - lock_in_period: registry_ref_mut.new_melee_lock_in_period, duration: registry_ref_mut.new_melee_duration, available_rewards: registry_ref_mut.new_melee_available_rewards, - match_percentage: registry_ref_mut.new_melee_match_percentage, + max_match_percentage: registry_ref_mut.new_melee_max_match_percentage, max_match_amount: registry_ref_mut.new_melee_max_match_amount, entrants: smart_table::new(), n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), From 3142571ac5e47dbacb51e09c0b1cfefe3727b18f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 13 Dec 2024 19:21:34 -0800 Subject: [PATCH 25/64] Add abort for unsuccessful lock in --- .../sources/emojicoin_arena.move | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 5c59f9902..7257fcb5e 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -33,6 +33,8 @@ module arena::emojicoin_arena { const E_ENTER_COIN_BALANCE_0: u64 = 4; /// User's melee escrow has nonzero emojicoin 1 balance. const E_ENTER_COIN_BALANCE_1: u64 = 5; + /// Elected to lock in but unable to match. + const E_UNABLE_TO_LOCK_IN: u64 = 6; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -238,25 +240,31 @@ module arena::emojicoin_arena { // Try matching user's contribution if they elect to lock in. let match_amount = if (lock_in) { - match_amount( - input_amount, - escrow_ref_mut, - current_melee_ref_mut, - registry_ref_mut, - time - ) - } else { 0 }; - if (match_amount > 0) { - escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; - let available_rewards_ref_mut = - &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - match_amount; - let vault_signer = - account::create_signer_with_capability( - ®istry_ref_mut.signer_capability + // Verify that user can even lock in. + let match_amount = + match_amount( + input_amount, + escrow_ref_mut, + current_melee_ref_mut, + registry_ref_mut, + time + ); + assert!(match_amount > 0, E_UNABLE_TO_LOCK_IN); + + // Update counters, transfer APT to entrant. + escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; + let available_rewards_ref_mut = + &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut - match_amount; + aptos_account::transfer( + &account::create_signer_with_capability( + ®istry_ref_mut.signer_capability + ), + entrant_address, + match_amount ); - aptos_account::transfer(&vault_signer, entrant_address, match_amount); - }; + match_amount + } else 0; // Execute a swap then immediately move funds into escrow. let input_amount_after_matching = input_amount + match_amount; From 129d45e94d7d8429d9d7c89ef889d19dc03c295d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 13 Dec 2024 19:39:55 -0800 Subject: [PATCH 26/64] Abstract swap buy into escrow --- .../sources/emojicoin_arena.move | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 7257fcb5e..284cb801a 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -270,52 +270,20 @@ module arena::emojicoin_arena { let input_amount_after_matching = input_amount + match_amount; let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; if (buy_emojicoin_0) { - let swap = - emojicoin_dot_fun::simulate_swap( - entrant_address, - market_address_0, - input_amount_after_matching, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS - ); - let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(swap); - emojicoin_dot_fun::swap( + swap_buy_into_escrow( entrant, + entrant_address, market_address_0, input_amount_after_matching, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS, - 1 - ); - coin::merge( - &mut escrow_ref_mut.emojicoin_0, coin::withdraw(entrant, net_proceeds) + &mut escrow_ref_mut.emojicoin_0 ); } else { - let swap = - emojicoin_dot_fun::simulate_swap( - entrant_address, - market_address_1, - input_amount_after_matching, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS - ); - let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(swap); - emojicoin_dot_fun::swap( + swap_buy_into_escrow( entrant, - market_address_1, + entrant_address, + market_address_0, input_amount_after_matching, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS, - 1 - ); - coin::merge( - &mut escrow_ref_mut.emojicoin_1, coin::withdraw(entrant, net_proceeds) + &mut escrow_ref_mut.emojicoin_1 ); } } @@ -742,4 +710,34 @@ module arena::emojicoin_arena { vector[market_id_1, market_id_0] } } + + inline fun swap_buy_into_escrow( + swapper: &signer, + swapper_address: address, + market_address: address, + input_amount: u64, + escrow_coin_ref_mut: &mut Coin + ) { + let simulated_swap = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address, + input_amount, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS + ); + let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(simulated_swap); + emojicoin_dot_fun::swap( + swapper, + market_address, + input_amount, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS, + 1 + ); + coin::merge(escrow_coin_ref_mut, coin::withdraw(swapper, net_proceeds)); + } } From 72d9af52910eb31fb44999fedbc5acb4ff5ccbec Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 08:17:10 -0800 Subject: [PATCH 27/64] Add swap within escrow helper --- .../sources/emojicoin_arena.move | 214 ++++++------------ 1 file changed, 73 insertions(+), 141 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 284cb801a..ffda732cf 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -320,154 +320,22 @@ module arena::emojicoin_arena { (market_addresses[0], market_addresses[1]); if (buy_emojicoin_0) { - // Move emojicoin 1 balance out of escrow. - let emojicoin_1_ref_mut = &mut escrow_ref_mut.emojicoin_1; - let input_amount = coin::value(emojicoin_1_ref_mut); - aptos_account::deposit_coins( - swapper_address, coin::extract_all(emojicoin_1_ref_mut) - ); - - // Get amount of APT recieved by selling emojicoin 1, then execute swap. - let swap_to_apt = - emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address_1, - input_amount, - true, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE - ); - let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(swap_to_apt); - emojicoin_dot_fun::swap( + swap_within_escrow( swapper, + swapper_address, market_address_1, - input_amount, - true, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); - - // Get amount of emojicoin 0 recieved by buying it with APT proceeds. - let swap_to_emojicoin_0 = - emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address_0, - net_proceeds_in_apt, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE - ); - let ( - _, - _, - _, - _, - _, - _, - _, - _, - net_proceeds_in_emojicoin_0, - _, - _, - _, - _, - _, - _, - _, - _, - _ - ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_0); - emojicoin_dot_fun::swap( - swapper, market_address_0, - net_proceeds_in_apt, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); - - // Move emojicoin 0 balance to escrow. - coin::merge( - &mut escrow_ref_mut.emojicoin_0, - coin::withdraw(swapper, net_proceeds_in_emojicoin_0) + &mut escrow_ref_mut.emojicoin_1, + &mut escrow_ref_mut.emojicoin_0 ); } else { - // Move emojicoin 0 balance out of escrow. - let emojicoin_0_ref_mut = &mut escrow_ref_mut.emojicoin_0; - let input_amount = coin::value(emojicoin_0_ref_mut); - aptos_account::deposit_coins( - swapper_address, coin::extract_all(emojicoin_0_ref_mut) - ); - - // Get amount of APT recieved by selling emojicoin 0, then execute swap. - let swap_to_apt = - emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address_0, - input_amount, - true, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE - ); - let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(swap_to_apt); - emojicoin_dot_fun::swap( - swapper, - market_address_1, - input_amount, - true, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); - - // Get amount of emojicoin 1 recieved by buying it with APT proceeds. - let swap_to_emojicoin_1 = - emojicoin_dot_fun::simulate_swap( - swapper_address, - market_address_1, - net_proceeds_in_apt, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE - ); - let ( - _, - _, - _, - _, - _, - _, - _, - _, - net_proceeds_in_emojicoin_1, - _, - _, - _, - _, - _, - _, - _, - _, - _ - ) = emojicoin_dot_fun::unpack_swap(swap_to_emojicoin_1); - emojicoin_dot_fun::swap( + swap_within_escrow( swapper, + swapper_address, + market_address_0, market_address_1, - net_proceeds_in_apt, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); - - // Move emojicoin 1 balance to escrow. - coin::merge( - &mut escrow_ref_mut.emojicoin_1, - coin::withdraw(swapper, net_proceeds_in_emojicoin_1) + &mut escrow_ref_mut.emojicoin_0, + &mut escrow_ref_mut.emojicoin_1 ); }; @@ -740,4 +608,68 @@ module arena::emojicoin_arena { ); coin::merge(escrow_coin_ref_mut, coin::withdraw(swapper, net_proceeds)); } + + inline fun swap_within_escrow( + swapper: &signer, + swapper_address: address, + market_address_from: address, + market_address_to: address, + escrow_from_coin_ref_mut: &mut Coin, + escrow_to_coin_ref_mut: &mut Coin + ) { + // Move all from coins out of escrow. + let input_amount = coin::value(escrow_from_coin_ref_mut); + aptos_account::deposit_coins( + swapper_address, coin::extract_all(escrow_from_coin_ref_mut) + ); + + // Get amount of APT recieved by selling from coin, then execute swap. + let simulated_swap_to_apt = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_from, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(simulated_swap_to_apt); + emojicoin_dot_fun::swap( + swapper, + market_address_from, + input_amount, + true, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Get amount of to coin recieved by buying it with APT proceeds. + let simulated_swap_to_to_coin = + emojicoin_dot_fun::simulate_swap( + swapper_address, + market_address_to, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE + ); + let (_, _, _, _, _, _, _, _, net_proceeds_in_to_coin, _, _, _, _, _, _, _, _, _) = + emojicoin_dot_fun::unpack_swap(simulated_swap_to_to_coin); + emojicoin_dot_fun::swap( + swapper, + market_address_to, + net_proceeds_in_apt, + false, + @integrator, + INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, + 1 + ); + + // Move to coin to escrow. + coin::merge( + escrow_to_coin_ref_mut, coin::withdraw(swapper, net_proceeds_in_to_coin) + ); + } } From 57b03888bca8e066a2e94050c04f03063faa1cba Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 08:35:48 -0800 Subject: [PATCH 28/64] Abstract swapping with stats --- .../sources/emojicoin_arena.move | 91 ++++++++----------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index ffda732cf..c19e8747d 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -270,20 +270,30 @@ module arena::emojicoin_arena { let input_amount_after_matching = input_amount + match_amount; let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; if (buy_emojicoin_0) { - swap_buy_into_escrow( - entrant, - entrant_address, - market_address_0, - input_amount_after_matching, - &mut escrow_ref_mut.emojicoin_0 + let (net_proceeds, _) = + swap_with_stats( + entrant, + entrant_address, + market_address_0, + input_amount_after_matching, + false, + INTEGRATOR_FEE_RATE_BPS + ); + coin::merge( + &mut escrow_ref_mut.emojicoin_0, coin::withdraw(entrant, net_proceeds) ); } else { - swap_buy_into_escrow( - entrant, - entrant_address, - market_address_0, - input_amount_after_matching, - &mut escrow_ref_mut.emojicoin_1 + let (net_proceeds, _) = + swap_with_stats( + entrant, + entrant_address, + market_address_1, + input_amount_after_matching, + false, + INTEGRATOR_FEE_RATE_BPS + ); + coin::merge( + &mut escrow_ref_mut.emojicoin_1, coin::withdraw(entrant, net_proceeds) ); } } @@ -579,34 +589,35 @@ module arena::emojicoin_arena { } } - inline fun swap_buy_into_escrow( + inline fun swap_with_stats( swapper: &signer, swapper_address: address, market_address: address, input_amount: u64, - escrow_coin_ref_mut: &mut Coin - ) { + is_sell: bool, + integrator_fee_rate_bps: u8 + ): (u64, u64) { let simulated_swap = emojicoin_dot_fun::simulate_swap( swapper_address, market_address, input_amount, - false, + is_sell, @integrator, - INTEGRATOR_FEE_RATE_BPS + integrator_fee_rate_bps ); - let (_, _, _, _, _, _, _, _, net_proceeds, _, _, _, _, _, _, _, _, _) = + let (_, _, _, _, _, _, _, _, net_proceeds, _, quote_volume, _, _, _, _, _, _, _) = emojicoin_dot_fun::unpack_swap(simulated_swap); emojicoin_dot_fun::swap( swapper, market_address, input_amount, - false, + is_sell, @integrator, - INTEGRATOR_FEE_RATE_BPS, + integrator_fee_rate_bps, 1 ); - coin::merge(escrow_coin_ref_mut, coin::withdraw(swapper, net_proceeds)); + (net_proceeds, quote_volume) } inline fun swap_within_escrow( @@ -623,49 +634,27 @@ module arena::emojicoin_arena { swapper_address, coin::extract_all(escrow_from_coin_ref_mut) ); - // Get amount of APT recieved by selling from coin, then execute swap. - let simulated_swap_to_apt = - emojicoin_dot_fun::simulate_swap( + // Swap into APT. + let (net_proceeds_in_apt, _) = + swap_with_stats( + swapper, swapper_address, market_address_from, input_amount, true, - @integrator, INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE ); - let (_, _, _, _, _, _, _, _, net_proceeds_in_apt, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(simulated_swap_to_apt); - emojicoin_dot_fun::swap( - swapper, - market_address_from, - input_amount, - true, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); - // Get amount of to coin recieved by buying it with APT proceeds. - let simulated_swap_to_to_coin = - emojicoin_dot_fun::simulate_swap( + // Swap into to coin. + let (net_proceeds_in_to_coin, _) = + swap_with_stats( + swapper, swapper_address, market_address_to, net_proceeds_in_apt, false, - @integrator, INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE ); - let (_, _, _, _, _, _, _, _, net_proceeds_in_to_coin, _, _, _, _, _, _, _, _, _) = - emojicoin_dot_fun::unpack_swap(simulated_swap_to_to_coin); - emojicoin_dot_fun::swap( - swapper, - market_address_to, - net_proceeds_in_apt, - false, - @integrator, - INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE, - 1 - ); // Move to coin to escrow. coin::merge( From b8afef6e0e9dbb0574e68c9fd2d5585aef11bbf1 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 08:49:35 -0800 Subject: [PATCH 29/64] Simplify side selection logic --- .../sources/emojicoin_arena.move | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index c19e8747d..8ea98081a 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -7,8 +7,9 @@ module arena::emojicoin_arena { use aptos_framework::coin::{Self, Coin}; use aptos_framework::randomness::Self; use aptos_framework::timestamp; - use aptos_std::smart_table::{Self, SmartTable}; use aptos_std::math64::min; + use aptos_std::smart_table::{Self, SmartTable}; + use aptos_std::type_info; use arena::pseudo_randomness; use emojicoin_dot_fun::emojicoin_dot_fun::{ Self, @@ -35,6 +36,8 @@ module arena::emojicoin_arena { const E_ENTER_COIN_BALANCE_1: u64 = 5; /// Elected to lock in but unable to match. const E_UNABLE_TO_LOCK_IN: u64 = 6; + /// Provided escrow coin type is invalid. + const E_INVALID_ESCROW_COIN_TYPE: u64 = 7; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -173,11 +176,8 @@ module arena::emojicoin_arena { } #[randomness] - entry fun enter( - entrant: &signer, - buy_emojicoin_0: bool, - input_amount: u64, - lock_in: bool + entry fun enter( + entrant: &signer, input_amount: u64, lock_in: bool ) acquires MeleeEscrow, Registry, UserMelees { let (melee_just_ended, registry_ref_mut, time, n_melees_before_cranking) = crank_schedule(); @@ -226,9 +226,23 @@ module arena::emojicoin_arena { ensure_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); ensure_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + // Verify user is selecting one of the two emojicoin types. + let coin_0_type_info = type_info::type_of(); + let coin_1_type_info = type_info::type_of(); + let escrow_coin_type_info = type_info::type_of(); + let buy_coin_0 = + if (coin_0_type_info == escrow_coin_type_info) true + else { + assert!( + escrow_coin_type_info == coin_1_type_info, + E_INVALID_ESCROW_COIN_TYPE + ); + false + }; + // Verify that user does not split balance between the two emojicoins. let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; - if (buy_emojicoin_0) + if (buy_coin_0) assert!( coin::value(&escrow_ref_mut.emojicoin_1) == 0, E_ENTER_COIN_BALANCE_1 ) @@ -269,7 +283,7 @@ module arena::emojicoin_arena { // Execute a swap then immediately move funds into escrow. let input_amount_after_matching = input_amount + match_amount; let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; - if (buy_emojicoin_0) { + if (buy_coin_0) { let (net_proceeds, _) = swap_with_stats( entrant, @@ -309,10 +323,7 @@ module arena::emojicoin_arena { #[randomness] entry fun swap( - swapper: &signer, - melee_id: u64, - market_addresses: vector
, - buy_emojicoin_0: bool + swapper: &signer, melee_id: u64, market_addresses: vector
) acquires MeleeEscrow, Registry, UserMelees { let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = crank_schedule(); @@ -326,26 +337,24 @@ module arena::emojicoin_arena { let escrow_ref_mut = &mut MeleeEscrow[swapper_address]; if (escrow_ref_mut.melee_id != melee_id) return; - let (market_address_0, market_address_1) = - (market_addresses[0], market_addresses[1]); - if (buy_emojicoin_0) { - swap_within_escrow( + if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { + swap_within_escrow( swapper, swapper_address, - market_address_1, - market_address_0, - &mut escrow_ref_mut.emojicoin_1, - &mut escrow_ref_mut.emojicoin_0 + market_addresses[0], + market_addresses[1], + &mut escrow_ref_mut.emojicoin_0, + &mut escrow_ref_mut.emojicoin_1 ); } else { - swap_within_escrow( + swap_within_escrow( swapper, swapper_address, - market_address_0, - market_address_1, - &mut escrow_ref_mut.emojicoin_0, - &mut escrow_ref_mut.emojicoin_1 + market_addresses[1], + market_addresses[0], + &mut escrow_ref_mut.emojicoin_1, + &mut escrow_ref_mut.emojicoin_0 ); }; From 2043f04e6e7c4a7a2110a4dfa5d6d14ebf1fe785 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:10:35 -0800 Subject: [PATCH 30/64] Add additional indexing, spell escapes --- cfg/cspell-dictionary.txt | 4 ++- .../sources/emojicoin_arena.move | 36 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/cfg/cspell-dictionary.txt b/cfg/cspell-dictionary.txt index 1f81c6aa2..2cf869fc3 100644 --- a/cfg/cspell-dictionary.txt +++ b/cfg/cspell-dictionary.txt @@ -1,9 +1,10 @@ -ADBEEF +adbeef aland allowlister aptn aptos aptoslabs +auid autoscale barthelemy bento @@ -66,6 +67,7 @@ marino mayen melilla merperson +metadatas moai mosquitto mqtt diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 8ea98081a..fb5198904 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -1,3 +1,4 @@ +// cspell:word unexited module arena::emojicoin_arena { use aptos_framework::account::{Self, SignerCapability}; @@ -52,7 +53,7 @@ module arena::emojicoin_arena { // Default parameters for new melees. const DEFAULT_DURATION: u64 = 36 * 3_600_000_000; - const DEFAULT_AVAILABLE_REWADS: u64 = 1000 * 100_000_000; + const DEFAULT_AVAILABLE_REWARDS: u64 = 1000 * 100_000_000; const DEFAULT_MAX_MATCH_PERCENTAGE: u64 = 50; const DEFAULT_MAX_MATCH_AMOUNT: u64 = 5 * 100_000_000; @@ -71,8 +72,14 @@ module arena::emojicoin_arena { max_match_percentage: u64, /// Maximum amount of APT to match in octas, when locking in. max_match_amount: u64, - /// Active entrants. - entrants: SmartTable, + /// All entrants who have entered the melee. + all_entrants: SmartTable, + /// Active entrants in the melee. + active_entrants: SmartTable, + /// Entrants who have exited the melee. + exited_entrants: SmartTable, + /// Entrants who have locked in. + locked_in_entrants: SmartTable, /// Number of melee-specific swaps. n_melee_swaps: Aggregator, /// Volume of melee-specific swaps in octas. @@ -118,7 +125,15 @@ module arena::emojicoin_arena { /// `Melee.max_match_percentage` for new melees. new_melee_max_match_percentage: u64, /// `Melee.max_match_amount` for new melees. - new_melee_max_match_amount: u64 + new_melee_max_match_amount: u64, + /// All entrants who have entered a melee. + all_entrants: SmartTable, + /// Number of melee-specific swaps. + n_melee_swaps: Aggregator, + /// Volume of melee-specific swaps in octas. + melee_swaps_volume: Aggregator, + /// Amount of octas disbursed as rewards. Decremented when a user taps out. + rewards_disbursed: u64 } struct UserMelees has key { @@ -372,9 +387,13 @@ module arena::emojicoin_arena { melee_ids_by_market_ids: smart_table::new(), signer_capability, new_melee_duration: DEFAULT_DURATION, - new_melee_available_rewards: DEFAULT_AVAILABLE_REWADS, + new_melee_available_rewards: DEFAULT_AVAILABLE_REWARDS, new_melee_max_match_percentage: DEFAULT_MAX_MATCH_PERCENTAGE, - new_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT + new_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT, + all_entrants: smart_table::new(), + n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), + melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), + rewards_disbursed: 0 } ); coin::register(&vault_signer); @@ -580,7 +599,10 @@ module arena::emojicoin_arena { available_rewards: registry_ref_mut.new_melee_available_rewards, max_match_percentage: registry_ref_mut.new_melee_max_match_percentage, max_match_amount: registry_ref_mut.new_melee_max_match_amount, - entrants: smart_table::new(), + all_entrants: smart_table::new(), + active_entrants: smart_table::new(), + exited_entrants: smart_table::new(), + locked_in_entrants: smart_table::new(), n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), From 29e3d43eef9ef600e12d81f343ff9f6fec126d12 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:11:37 -0800 Subject: [PATCH 31/64] Revert rewards module changes --- src/move/rewards/Move.toml | 2 +- src/move/rewards/README.md | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/move/rewards/Move.toml b/src/move/rewards/Move.toml index 3183a0dc3..08e52d06e 100644 --- a/src/move/rewards/Move.toml +++ b/src/move/rewards/Move.toml @@ -18,4 +18,4 @@ local = "../test_coin_factories/black_heart" authors = ["Econia Labs (developers@econialabs.com)"] name = "EmojicoinDotFunRewards" upgrade_policy = "compatible" -version = "1.2.0" +version = "1.1.0" diff --git a/src/move/rewards/README.md b/src/move/rewards/README.md index efa3d873e..788c741ef 100644 --- a/src/move/rewards/README.md +++ b/src/move/rewards/README.md @@ -1,9 +1,8 @@ # `emojicoin-dot-fun` rewards This package contains an overloaded version of the `swap` function for -`emojicoin-dot-fun`, `emojioin_dot_fun_rewards::swap_with_rewards`, which gives -users an opportunity for a reward based on the amount of integrator fees they -pay for their swap. +`emojicoin-dot-fun`, `swap_with_rewards`, which gives users an opportunity for a +reward based on the amount of integrator fees they pay for their swap. The rewards vault can be loaded up via the `fund_tiers` function. @@ -14,10 +13,6 @@ nominal volume amount. See `reward_tiers` for more. For ease of parameter modeling, named constants use values in `APT`, which are converted to octas internally. -The `emojicoin_dot_fun_claim_link` module contains a system for administering -pre-paid swaps using private keys encoded in "claim links", akin to magic links -for website login. - ## Publish commands Set variables: From 93fc7eec1a6b94ad60ce6e7447f9f393709d9d66 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:31:45 -0800 Subject: [PATCH 32/64] Alphabetize inline func --- .../sources/emojicoin_arena.move | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index fb5198904..b8f3dcab9 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -426,6 +426,23 @@ module arena::emojicoin_arena { &mut Registry[@arena] } + /// Cranks schedule and returns `true` if a melee has ended as a result, along with assorted + /// variables, to reduce borrows and lookups in the caller. + inline fun crank_schedule(): (bool, &mut Registry, u64, u64) { + let time = timestamp::now_microseconds(); + let registry_ref_mut = &mut Registry[@arena]; + let n_melees_before_cranking = registry_ref_mut.melees_by_id.length(); + let current_melee_ref = + registry_ref_mut.melees_by_id.borrow(n_melees_before_cranking); + let cranked = + if (time >= current_melee_ref.start_time + current_melee_ref.duration) { + let market_ids = next_melee_market_ids(registry_ref_mut); + register_melee(registry_ref_mut, n_melees_before_cranking, market_ids); + true + } else false; + (cranked, registry_ref_mut, time, n_melees_before_cranking) + } + inline fun ensure_contains( map_ref_mut: &mut SmartTable, key: u64 ) { @@ -492,23 +509,6 @@ module arena::emojicoin_arena { } } - /// Cranks schedule and returns `true` if a melee has ended as a result, along with assorted - /// variables, to reduce borrows and lookups in the caller. - inline fun crank_schedule(): (bool, &mut Registry, u64, u64) { - let time = timestamp::now_microseconds(); - let registry_ref_mut = &mut Registry[@arena]; - let n_melees_before_cranking = registry_ref_mut.melees_by_id.length(); - let current_melee_ref = - registry_ref_mut.melees_by_id.borrow(n_melees_before_cranking); - let cranked = - if (time >= current_melee_ref.start_time + current_melee_ref.duration) { - let market_ids = next_melee_market_ids(registry_ref_mut); - register_melee(registry_ref_mut, n_melees_before_cranking, market_ids); - true - } else false; - (cranked, registry_ref_mut, time, n_melees_before_cranking) - } - inline fun get_n_registered_markets(): u64 { let (_, _, _, n_markets, _, _, _, _, _, _, _, _) = unpack_registry_view(registry_view()); From cdc1a951bee2fcdfbb7b195bfead0f7a7fe107fd Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:43:12 -0800 Subject: [PATCH 33/64] Tweak match amount math --- .../sources/emojicoin_arena.move | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index b8f3dcab9..03104f8e4 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -527,28 +527,36 @@ module arena::emojicoin_arena { registry_ref_mut: &mut Registry, time: u64 ): u64 { - min( - // Scale down input amount for matching percentage and time elapsed in one compound - // operation, to reduce truncation errors. - ((input_amount as u256) - * (current_melee_ref_mut.max_match_percentage as u256) - * ((time as u256) - (current_melee_ref_mut.start_time as u256)) - / ((MAX_PERCENTAGE as u256) * (current_melee_ref_mut.duration as u256)) as u64), + let elapsed_time = ((time - current_melee_ref_mut.start_time) as u256); + let duration = (current_melee_ref_mut.duration as u256); + if (elapsed_time >= duration) { 0 } + else { min( - // Correct for vault balance. - coin::balance( - account::get_signer_capability_address( - ®istry_ref_mut.signer_capability - ) + // Scale down input amount for matching percentage and time elapsed in one compound + // operation, to reduce truncation errors. + ( + ((input_amount as u256) + * (current_melee_ref_mut.max_match_percentage as u256) + * (duration - elapsed_time)) / ((MAX_PERCENTAGE as u256) + * duration) as u64 ), min( - // Correct for available rewards in current melee. - current_melee_ref_mut.available_rewards, - // Correct for max match amount user is eligible for. - current_melee_ref_mut.max_match_amount - escrow_ref_mut.tap_out_fee + // Correct for vault balance. + coin::balance( + account::get_signer_capability_address( + ®istry_ref_mut.signer_capability + ) + ), + min( + // Correct for available rewards in current melee. + current_melee_ref_mut.available_rewards, + // Correct for max match amount user is eligible for. + current_melee_ref_mut.max_match_amount + - escrow_ref_mut.tap_out_fee + ) ) ) - ) + } } /// Accepts a mutable reference to avoid freezing references up the stack. From 6243746cac24b88db89ee7c4f354121236c223f3 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:24:04 -0800 Subject: [PATCH 34/64] Remove admin setter guardrails --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 03104f8e4..f75c86a9b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -45,7 +45,6 @@ module arena::emojicoin_arena { const U64_MAX: u64 = 0xffffffffffffffff; const MAX_PERCENTAGE: u64 = 100; - const MIN_MELEE_DURATION: u64 = 1 * 3_600_000_000; /// Flat integrator fee. const INTEGRATOR_FEE_RATE_BPS: u8 = 100; @@ -163,14 +162,12 @@ module arena::emojicoin_arena { public entry fun set_new_melee_duration(arena: &signer, duration: u64) acquires Registry { let registry_ref_mut = borrow_registry_ref_mut_checked(arena); - assert!(duration >= MIN_MELEE_DURATION, E_NEW_DURATION_TOO_SHORT); registry_ref_mut.new_melee_duration = duration; } public entry fun set_new_melee_max_match_percentage( arena: &signer, max_match_percentage: u64 ) acquires Registry { - assert!(max_match_percentage <= MAX_PERCENTAGE, E_NEW_MATCH_PERCENTAGE_TOO_HIGH); borrow_registry_ref_mut_checked(arena).new_melee_max_match_percentage = max_match_percentage; } From 88c4770805155e646256bb1645de648e87334f35 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:27:56 -0800 Subject: [PATCH 35/64] Clarify table helper func names --- .../sources/emojicoin_arena.move | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index f75c86a9b..be27d106d 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -234,9 +234,9 @@ module arena::emojicoin_arena { // Update user melees resource. let user_melees_ref_mut = &mut UserMelees[entrant_address]; - ensure_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); - ensure_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - ensure_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + add_if_not_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); + add_if_not_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); + remove_if_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); // Verify user is selecting one of the two emojicoin types. let coin_0_type_info = type_info::type_of(); @@ -413,6 +413,14 @@ module arena::emojicoin_arena { ); } + inline fun add_if_not_contains( + map_ref_mut: &mut SmartTable, key: u64 + ) { + if (!map_ref_mut.contains(key)) { + map_ref_mut.add(key, Nil {}); + } + } + inline fun borrow_registry_ref_checked(arena: &signer): &Registry { assert!(signer::address_of(arena) == @arena, E_NOT_ARENA); &Registry[@arena] @@ -440,22 +448,6 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } - inline fun ensure_contains( - map_ref_mut: &mut SmartTable, key: u64 - ) { - if (!map_ref_mut.contains(key)) { - map_ref_mut.add(key, Nil {}); - } - } - - inline fun ensure_not_contains( - map_ref_mut: &mut SmartTable, key: u64 - ) { - if (map_ref_mut.contains(key)) { - map_ref_mut.remove(key); - } - } - inline fun exit_inner( participant: &signer, melee_id: u64, melee_is_current: bool ) acquires Registry { @@ -498,8 +490,8 @@ module arena::emojicoin_arena { // Update user melees resource. let user_melees_ref_mut = &mut UserMelees[participant_address]; - ensure_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); - ensure_not_contains( + add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + remove_if_contains( &mut user_melees_ref_mut.unexited_melee_ids, melee_id ); }; @@ -617,6 +609,14 @@ module arena::emojicoin_arena { registry_ref_mut.melee_ids_by_market_ids.add(sorted_unique_market_ids, melee_id); } + inline fun remove_if_contains( + map_ref_mut: &mut SmartTable, key: u64 + ) { + if (map_ref_mut.contains(key)) { + map_ref_mut.remove(key); + } + } + inline fun sort_unique_market_ids(market_id_0: u64, market_id_1: u64): vector { if (market_id_0 < market_id_1) { vector[market_id_0, market_id_1] From 3b50bcdb8faf4f66d2b048697c8fd0130872803d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:40:49 -0800 Subject: [PATCH 36/64] Refactor crank burden during error handling --- .../sources/emojicoin_arena.move | 104 +++++++++--------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index be27d106d..188930610 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -39,6 +39,10 @@ module arena::emojicoin_arena { const E_UNABLE_TO_LOCK_IN: u64 = 6; /// Provided escrow coin type is invalid. const E_INVALID_ESCROW_COIN_TYPE: u64 = 7; + /// User has no escrow resource. + const E_SWAP_NO_ESCROW: u64 = 8; + /// Swapper has no funds in escrow to swap. + const E_SWAP_NO_FUNDS: u64 = 9; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -326,29 +330,30 @@ module arena::emojicoin_arena { #[randomness] entry fun exit( - participant: &signer, melee_id: u64 + participant: &signer ) acquires MeleeEscrow, Registry, UserMelees { let (melee_just_ended, _registry_ref_mut, _time, _n_melees_before_cranking) = crank_schedule(); - exit_inner(participant, melee_id, !melee_just_ended); + exit_inner(participant, !melee_just_ended); } #[randomness] entry fun swap( - swapper: &signer, melee_id: u64, market_addresses: vector
+ swapper: &signer, market_addresses: vector
) acquires MeleeEscrow, Registry, UserMelees { - let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = - crank_schedule(); - // Return early if type arguments or melee ID are passed incorrectly, but only after - // cranking schedule. + // Verify that swapper has an escrow resource. let swapper_address = signer::address_of(swapper); - if (!exists>(swapper_address)) { - return; - }; + assert!( + exists>(swapper_address), + E_SWAP_NO_ESCROW + ); let escrow_ref_mut = &mut MeleeEscrow[swapper_address]; - if (escrow_ref_mut.melee_id != melee_id) - return; + + // Try cranking the schedule, and if a new melee starts, flag that user's escrow should be + // emptied immediately after the swap. + let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = + crank_schedule(); if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { swap_within_escrow( @@ -360,6 +365,7 @@ module arena::emojicoin_arena { &mut escrow_ref_mut.emojicoin_1 ); } else { + assert!(coin::value(&escrow_ref_mut.emojicoin_1) > 0, E_SWAP_NO_FUNDS); swap_within_escrow( swapper, swapper_address, @@ -370,7 +376,7 @@ module arena::emojicoin_arena { ); }; - if (exit_once_done) exit_inner(swapper, melee_id, false); + if (exit_once_done) exit_inner(swapper, false); } fun init_module(arena: &signer) acquires Registry { @@ -449,52 +455,48 @@ module arena::emojicoin_arena { } inline fun exit_inner( - participant: &signer, melee_id: u64, melee_is_current: bool + participant: &signer, melee_is_current: bool ) acquires Registry { let participant_address = signer::address_of(participant); - // Only allow exit if user has corresponding melee resource and melee ID matches. + // Only allow exit if user has corresponding melee resource. if (exists>(participant_address)) { let escrow_ref_mut = &mut MeleeEscrow[participant_address]; - // Only allow exit if melee ID matches. - if (escrow_ref_mut.melee_id == melee_id) { - // Update available rewards and transfer tap out fee to vault if applicable. - if (melee_is_current) { - let registry_ref_mut = &mut Registry[@arena]; - let exited_melee_ref_mut = - registry_ref_mut.melees_by_id.borrow_mut(melee_id); - let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; - let available_rewards_ref_mut = - &mut exited_melee_ref_mut.available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - + *tap_out_fee_ref_mut; - let vault_address = - account::get_signer_capability_address( - ®istry_ref_mut.signer_capability - ); - aptos_account::transfer( - participant, vault_address, *tap_out_fee_ref_mut + let melee_id = escrow_ref_mut.melee_id; + // Update available rewards and transfer tap out fee to vault if applicable. + if (melee_is_current) { + let registry_ref_mut = &mut Registry[@arena]; + let exited_melee_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(melee_id); + let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; + let available_rewards_ref_mut = + &mut exited_melee_ref_mut.available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut + + *tap_out_fee_ref_mut; + let vault_address = + account::get_signer_capability_address( + ®istry_ref_mut.signer_capability ); - *tap_out_fee_ref_mut = 0; - }; - - // Move emojicoin balances out of escrow. - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_0) - ); - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_1) - ); - - // Update user melees resource. - let user_melees_ref_mut = &mut UserMelees[participant_address]; - add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); - remove_if_contains( - &mut user_melees_ref_mut.unexited_melee_ids, melee_id + aptos_account::transfer( + participant, vault_address, *tap_out_fee_ref_mut ); + *tap_out_fee_ref_mut = 0; }; + + // Move emojicoin balances out of escrow. + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_0) + ); + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_1) + ); + + // Update user melees resource. + let user_melees_ref_mut = &mut UserMelees[participant_address]; + add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); } } From e40c8ac9f1ea35bb03046142d6cabb720d99e7df Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:53:28 -0800 Subject: [PATCH 37/64] Add pseudo-randomness rationale --- src/move/emojicoin_arena/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/move/emojicoin_arena/README.md diff --git a/src/move/emojicoin_arena/README.md b/src/move/emojicoin_arena/README.md new file mode 100644 index 000000000..a21cda292 --- /dev/null +++ b/src/move/emojicoin_arena/README.md @@ -0,0 +1,13 @@ +# Emojicoin Arena + +## A note on psuedo-randomness + +Since randomness is not supported in `init_module` per [`aptos-core` #15436], +psuedo-random proxies are used for the first crank. For a detailed rationale +that explains how this is effectively random in practice, see +[this `emojicoin-dot-fun` pull request comment]. + +[`aptos-core` #15436]: https://github.com/aptos-labs/aptos-core/issues/15436 +[this `emojicoin-dot-fun` pull request comment]: https://github.com/econia-labs/emojicoin-dot-fun/pull/408#discussion_r1887856202 + + From 75a668e7dd059f04c919d65133f8f90955bd245d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:54:40 -0800 Subject: [PATCH 38/64] Run mdformat, fix spell error --- src/move/emojicoin_arena/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/move/emojicoin_arena/README.md b/src/move/emojicoin_arena/README.md index a21cda292..ccd10fb19 100644 --- a/src/move/emojicoin_arena/README.md +++ b/src/move/emojicoin_arena/README.md @@ -1,13 +1,11 @@ # Emojicoin Arena -## A note on psuedo-randomness +## A note on pseudo-randomness Since randomness is not supported in `init_module` per [`aptos-core` #15436], psuedo-random proxies are used for the first crank. For a detailed rationale that explains how this is effectively random in practice, see [this `emojicoin-dot-fun` pull request comment]. -[`aptos-core` #15436]: https://github.com/aptos-labs/aptos-core/issues/15436 [this `emojicoin-dot-fun` pull request comment]: https://github.com/econia-labs/emojicoin-dot-fun/pull/408#discussion_r1887856202 - - +[`aptos-core` #15436]: https://github.com/aptos-labs/aptos-core/issues/15436 From 7534bf97cc619fa247275bb9c0b4c9447adbeb7b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:08:16 -0800 Subject: [PATCH 39/64] Refactor inline exit func var passing --- .../sources/emojicoin_arena.move | 103 ++++++++++-------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 188930610..5671d2959 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -40,7 +40,7 @@ module arena::emojicoin_arena { /// Provided escrow coin type is invalid. const E_INVALID_ESCROW_COIN_TYPE: u64 = 7; /// User has no escrow resource. - const E_SWAP_NO_ESCROW: u64 = 8; + const E_NO_ESCROW: u64 = 8; /// Swapper has no funds in escrow to swap. const E_SWAP_NO_FUNDS: u64 = 9; @@ -332,9 +332,18 @@ module arena::emojicoin_arena { entry fun exit( participant: &signer ) acquires MeleeEscrow, Registry, UserMelees { - let (melee_just_ended, _registry_ref_mut, _time, _n_melees_before_cranking) = - crank_schedule(); - exit_inner(participant, !melee_just_ended); + let participant_address = signer::address_of(participant); + assert!( + exists>(participant_address), + E_NO_ESCROW + ); + let (melee_just_ended, registry_ref_mut, _, _) = crank_schedule(); + exit_inner( + participant, + participant_address, + registry_ref_mut, + !melee_just_ended + ); } #[randomness] @@ -346,14 +355,13 @@ module arena::emojicoin_arena { let swapper_address = signer::address_of(swapper); assert!( exists>(swapper_address), - E_SWAP_NO_ESCROW + E_NO_ESCROW ); let escrow_ref_mut = &mut MeleeEscrow[swapper_address]; // Try cranking the schedule, and if a new melee starts, flag that user's escrow should be // emptied immediately after the swap. - let (exit_once_done, _registry_ref_mut, _time, _n_melees_before_cranking) = - crank_schedule(); + let (exit_once_done, registry_ref_mut, _, _) = crank_schedule(); if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { swap_within_escrow( @@ -376,7 +384,13 @@ module arena::emojicoin_arena { ); }; - if (exit_once_done) exit_inner(swapper, false); + if (exit_once_done) + exit_inner( + swapper, + swapper_address, + registry_ref_mut, + false + ); } fun init_module(arena: &signer) acquires Registry { @@ -454,50 +468,43 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } + /// Assumes user has an escrow resource. inline fun exit_inner( - participant: &signer, melee_is_current: bool + participant: &signer, + participant_address: address, + registry_ref_mut: &mut Registry, + melee_is_current: bool ) acquires Registry { - let participant_address = signer::address_of(participant); - // Only allow exit if user has corresponding melee resource. - if (exists>(participant_address)) { - let escrow_ref_mut = - &mut MeleeEscrow[participant_address]; - let melee_id = escrow_ref_mut.melee_id; - // Update available rewards and transfer tap out fee to vault if applicable. - if (melee_is_current) { - let registry_ref_mut = &mut Registry[@arena]; - let exited_melee_ref_mut = - registry_ref_mut.melees_by_id.borrow_mut(melee_id); - let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; - let available_rewards_ref_mut = - &mut exited_melee_ref_mut.available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - + *tap_out_fee_ref_mut; - let vault_address = - account::get_signer_capability_address( - ®istry_ref_mut.signer_capability - ); - aptos_account::transfer( - participant, vault_address, *tap_out_fee_ref_mut - ); - *tap_out_fee_ref_mut = 0; - }; + let escrow_ref_mut = &mut MeleeEscrow[participant_address]; + let melee_id = escrow_ref_mut.melee_id; + + // Update available rewards and transfer tap out fee back to vault if applicable. + if (melee_is_current) { + let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); + let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; + let available_rewards_ref_mut = &mut exited_melee_ref_mut.available_rewards; + *available_rewards_ref_mut = *available_rewards_ref_mut + + *tap_out_fee_ref_mut; + let vault_address = + account::get_signer_capability_address(®istry_ref_mut.signer_capability); + aptos_account::transfer(participant, vault_address, *tap_out_fee_ref_mut); + *tap_out_fee_ref_mut = 0; + }; - // Move emojicoin balances out of escrow. - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_0) - ); - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_1) - ); + // Move emojicoin balances out of escrow. + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_0) + ); + aptos_account::deposit_coins( + participant_address, + coin::extract_all(&mut escrow_ref_mut.emojicoin_1) + ); - // Update user melees resource. - let user_melees_ref_mut = &mut UserMelees[participant_address]; - add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); - remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - } + // Update user melees resource. + let user_melees_ref_mut = &mut UserMelees[participant_address]; + add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); } inline fun get_n_registered_markets(): u64 { From cd5b9e2705f2a87e06c2fa55ed56f547c7ccf04b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:24:39 -0800 Subject: [PATCH 40/64] Add available rewards setter funcs --- .../sources/emojicoin_arena.move | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 5671d2959..befa62ef8 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -69,12 +69,12 @@ module arena::emojicoin_arena { start_time: u64, /// How long melee lasts after start time. duration: u64, - /// Amount of rewards available for distribution in octas, conditional on vault balance. - available_rewards: u64, /// Max percentage of user's input amount to match in octas, when locking in. max_match_percentage: u64, /// Maximum amount of APT to match in octas, when locking in. max_match_amount: u64, + /// Amount of rewards available for distribution in octas, conditional on vault balance. + available_rewards: u64, /// All entrants who have entered the melee. all_entrants: SmartTable, /// Active entrants in the melee. @@ -283,9 +283,7 @@ module arena::emojicoin_arena { // Update counters, transfer APT to entrant. escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; - let available_rewards_ref_mut = - &mut registry_ref_mut.melees_by_id.borrow_mut(melee_id).available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - match_amount; + current_melee_ref_mut.available_rewards_decrement(match_amount); aptos_account::transfer( &account::create_signer_with_capability( ®istry_ref_mut.signer_capability @@ -468,6 +466,14 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } + inline fun available_rewards_decrement(self: &mut Melee, amount: u64) { + self.available_rewards = self.available_rewards - amount; + } + + inline fun available_rewards_increment(self: &mut Melee, amount: u64) { + self.available_rewards = self.available_rewards + amount; + } + /// Assumes user has an escrow resource. inline fun exit_inner( participant: &signer, @@ -482,9 +488,7 @@ module arena::emojicoin_arena { if (melee_is_current) { let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; - let available_rewards_ref_mut = &mut exited_melee_ref_mut.available_rewards; - *available_rewards_ref_mut = *available_rewards_ref_mut - + *tap_out_fee_ref_mut; + exited_melee_ref_mut.available_rewards_increment(*tap_out_fee_ref_mut); let vault_address = account::get_signer_capability_address(®istry_ref_mut.signer_capability); aptos_account::transfer(participant, vault_address, *tap_out_fee_ref_mut); @@ -602,9 +606,9 @@ module arena::emojicoin_arena { timestamp::now_microseconds(), registry_ref_mut.new_melee_duration ), duration: registry_ref_mut.new_melee_duration, - available_rewards: registry_ref_mut.new_melee_available_rewards, max_match_percentage: registry_ref_mut.new_melee_max_match_percentage, max_match_amount: registry_ref_mut.new_melee_max_match_amount, + available_rewards: registry_ref_mut.new_melee_available_rewards, all_entrants: smart_table::new(), active_entrants: smart_table::new(), exited_entrants: smart_table::new(), From 03ca8d1bd8e037ace2bc5f009a11f9586b1fa5c2 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:32:48 -0800 Subject: [PATCH 41/64] Update src/move/emojicoin_arena/README.md Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/README.md b/src/move/emojicoin_arena/README.md index ccd10fb19..bef98052b 100644 --- a/src/move/emojicoin_arena/README.md +++ b/src/move/emojicoin_arena/README.md @@ -3,7 +3,7 @@ ## A note on pseudo-randomness Since randomness is not supported in `init_module` per [`aptos-core` #15436], -psuedo-random proxies are used for the first crank. For a detailed rationale +pseudo-random substitute implementations are used for the first crank. For a detailed rationale that explains how this is effectively random in practice, see [this `emojicoin-dot-fun` pull request comment]. From a6a4eb5b984b07ac980d1ee944de66f126665d76 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:33:21 -0800 Subject: [PATCH 42/64] Fix line breaking --- src/move/emojicoin_arena/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/move/emojicoin_arena/README.md b/src/move/emojicoin_arena/README.md index bef98052b..e40843ce3 100644 --- a/src/move/emojicoin_arena/README.md +++ b/src/move/emojicoin_arena/README.md @@ -3,8 +3,8 @@ ## A note on pseudo-randomness Since randomness is not supported in `init_module` per [`aptos-core` #15436], -pseudo-random substitute implementations are used for the first crank. For a detailed rationale -that explains how this is effectively random in practice, see +pseudo-random substitute implementations are used for the first crank. For a +detailed rationale that explains how this is effectively random in practice, see [this `emojicoin-dot-fun` pull request comment]. [this `emojicoin-dot-fun` pull request comment]: https://github.com/econia-labs/emojicoin-dot-fun/pull/408#discussion_r1887856202 From ce7856b6424368bcead7bcd3449f5cf783e3153f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:41:23 -0800 Subject: [PATCH 43/64] Add swap buy/sell wrappers --- .../sources/emojicoin_arena.move | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index befa62ef8..9adc54689 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -299,12 +299,11 @@ module arena::emojicoin_arena { let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; if (buy_coin_0) { let (net_proceeds, _) = - swap_with_stats( + swap_with_stats_buy_emojicoin( entrant, entrant_address, market_address_0, input_amount_after_matching, - false, INTEGRATOR_FEE_RATE_BPS ); coin::merge( @@ -312,12 +311,11 @@ module arena::emojicoin_arena { ); } else { let (net_proceeds, _) = - swap_with_stats( + swap_with_stats_buy_emojicoin( entrant, entrant_address, market_address_1, input_amount_after_matching, - false, INTEGRATOR_FEE_RATE_BPS ); coin::merge( @@ -643,7 +641,7 @@ module arena::emojicoin_arena { swapper_address: address, market_address: address, input_amount: u64, - is_sell: bool, + sell_to_apt: bool, integrator_fee_rate_bps: u8 ): (u64, u64) { let simulated_swap = @@ -651,7 +649,7 @@ module arena::emojicoin_arena { swapper_address, market_address, input_amount, - is_sell, + sell_to_apt, @integrator, integrator_fee_rate_bps ); @@ -661,7 +659,7 @@ module arena::emojicoin_arena { swapper, market_address, input_amount, - is_sell, + sell_to_apt, @integrator, integrator_fee_rate_bps, 1 @@ -669,6 +667,40 @@ module arena::emojicoin_arena { (net_proceeds, quote_volume) } + inline fun swap_with_stats_buy_emojicoin( + swapper: &signer, + swapper_address: address, + market_address: address, + input_amount: u64, + integrator_fee_rate_bps: u8 + ): (u64, u64) { + swap_with_stats( + swapper, + swapper_address, + market_address, + input_amount, + false, + integrator_fee_rate_bps + ) + } + + inline fun swap_with_stats_sell_to_apt( + swapper: &signer, + swapper_address: address, + market_address: address, + input_amount: u64, + integrator_fee_rate_bps: u8 + ): (u64, u64) { + swap_with_stats( + swapper, + swapper_address, + market_address, + input_amount, + true, + integrator_fee_rate_bps + ) + } + inline fun swap_within_escrow( swapper: &signer, swapper_address: address, @@ -685,23 +717,21 @@ module arena::emojicoin_arena { // Swap into APT. let (net_proceeds_in_apt, _) = - swap_with_stats( + swap_with_stats_sell_to_apt( swapper, swapper_address, market_address_from, input_amount, - true, INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE ); - // Swap into to coin. + // Swap into to emojicoin. let (net_proceeds_in_to_coin, _) = - swap_with_stats( + swap_with_stats_buy_emojicoin( swapper, swapper_address, market_address_to, net_proceeds_in_apt, - false, INTEGRATOR_FEE_RATE_BPS_DUAL_ROUTE ); From 5e71013c8b3f5ef54c1a9bf65cdb2ed162f1450d Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:46:38 -0800 Subject: [PATCH 44/64] Update src/move/emojicoin_arena/sources/pseudo_randomness.move Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/sources/pseudo_randomness.move | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/move/emojicoin_arena/sources/pseudo_randomness.move b/src/move/emojicoin_arena/sources/pseudo_randomness.move index d96d7e141..83f475028 100644 --- a/src/move/emojicoin_arena/sources/pseudo_randomness.move +++ b/src/move/emojicoin_arena/sources/pseudo_randomness.move @@ -6,7 +6,8 @@ module arena::pseudo_randomness { friend arena::emojicoin_arena; - /// Pseudo-random proxy for `aptos_framework::randomness::u64_range`. + /// Pseudo-random substitute for `aptos_framework::randomness::u64_range`, since + /// the randomness API is not available during `init_module`. public(friend) inline fun u64_range(min_incl: u64, max_excl: u64): u64 { let range = ((max_excl - min_incl) as u256); let sample = ((u256_integer() % range) as u64); @@ -14,12 +15,14 @@ module arena::pseudo_randomness { min_incl + sample } - /// Pseudo-random proxy for `aptos_framework::randomness::next_32_bytes`. + /// Pseudo-random substitute for `aptos_framework::randomness::next_32_bytes`, since + /// the randomness API is not available during `init_module`. inline fun next_32_bytes(): vector { bcs::to_bytes(&transaction_context::generate_auid_address()) } - /// Pseudo-random proxy for `aptos_framework::randomness::u256_integer`. + /// Pseudo-random substitute for `aptos_framework::randomness::u256_integer`, since + /// the randomness API is not available during `init_module`. inline fun u256_integer(): u256 { let raw = next_32_bytes(); let i = 0; From 78c992e56bff3b4131a187a8789c2d46ddd1b226 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:54:32 -0800 Subject: [PATCH 45/64] Update available rewards comment --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 9adc54689..96262b5d7 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -73,7 +73,8 @@ module arena::emojicoin_arena { max_match_percentage: u64, /// Maximum amount of APT to match in octas, when locking in. max_match_amount: u64, - /// Amount of rewards available for distribution in octas, conditional on vault balance. + /// Amount of rewards that are available to claim for this melee while it is still active. + /// Measured in octas, conditional on vault balance. available_rewards: u64, /// All entrants who have entered the melee. all_entrants: SmartTable, From 456119622a6037a4f0a1aefc934e0c4db614965a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:56:05 -0800 Subject: [PATCH 46/64] Update src/move/emojicoin_arena/sources/emojicoin_arena.move Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 96262b5d7..e27b6cfa1 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -76,13 +76,13 @@ module arena::emojicoin_arena { /// Amount of rewards that are available to claim for this melee while it is still active. /// Measured in octas, conditional on vault balance. available_rewards: u64, - /// All entrants who have entered the melee. + /// All entrants who have entered the melee, used as a set. all_entrants: SmartTable, - /// Active entrants in the melee. + /// Active entrants in the melee, used as a set. active_entrants: SmartTable, - /// Entrants who have exited the melee. + /// Entrants who have exited the melee, used as a set. exited_entrants: SmartTable, - /// Entrants who have locked in. + /// Entrants who have locked in, used as a set. locked_in_entrants: SmartTable, /// Number of melee-specific swaps. n_melee_swaps: Aggregator, From b240f04a4c9debcaaac671fd51fcc4c3970f5469 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:57:36 -0800 Subject: [PATCH 47/64] Update melee ID typo --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index e27b6cfa1..aa17775ed 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -95,7 +95,7 @@ module arena::emojicoin_arena { } struct MeleeEscrow has key { - /// `Melee.market_id`. + /// Corresponding `Melee.melee_id`. melee_id: u64, /// Emojicoin 0 holdings. emojicoin_0: Coin, From 331da3e21d22e92d3a54a5282ecf1cbc6985b7d3 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:01:35 -0800 Subject: [PATCH 48/64] Update doc comment on tap out fee --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index aa17775ed..8f203b12f 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -109,7 +109,8 @@ module arena::emojicoin_arena { /// topping off, reset to zero when exiting. octas_entered: u64, /// Octas user must pay to exit the melee before it ends, if they have locked in. Equivalent - /// to the amount of APT they have been matched by locking in. + /// to the current total amount of APT rewards they have been matched across all + /// transactions since they began depositing funds into an empty escrow. tap_out_fee: u64 } From 8155b7f3d86d1aaf6e37ff2d729f31abedd6ada4 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:03:09 -0800 Subject: [PATCH 49/64] Update src/move/emojicoin_arena/sources/emojicoin_arena.move Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 8f203b12f..651f17df3 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -117,7 +117,7 @@ module arena::emojicoin_arena { struct Nil has drop, store {} struct Registry has key { - /// Map from melee serial ID to the melee. + /// A map of each melee's `melee_id` to the melee. melees_by_id: SmartTable, /// Map from a sorted combination of market IDs (lower ID first) to the melee serial ID. melee_ids_by_market_ids: SmartTable, u64>, From f694f3b332728806fa9f3928c154bc93c7a83743 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:04:36 -0800 Subject: [PATCH 50/64] Specify next vs new melee --- .../sources/emojicoin_arena.move | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 651f17df3..3bdc11997 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -123,14 +123,14 @@ module arena::emojicoin_arena { melee_ids_by_market_ids: SmartTable, u64>, /// Approves transfers from the vault. signer_capability: SignerCapability, - /// `Melee.duration` for new melees. - new_melee_duration: u64, - /// `Melee.available_rewards` for new melees. - new_melee_available_rewards: u64, - /// `Melee.max_match_percentage` for new melees. - new_melee_max_match_percentage: u64, - /// `Melee.max_match_amount` for new melees. - new_melee_max_match_amount: u64, + /// `Melee.duration` for next melee. + next_melee_duration: u64, + /// `Melee.available_rewards` for next melee. + next_melee_available_rewards: u64, + /// `Melee.max_match_percentage` for next melee. + next_melee_max_match_percentage: u64, + /// `Melee.max_match_amount` for next melee. + next_melee_max_match_amount: u64, /// All entrants who have entered a melee. all_entrants: SmartTable, /// Number of melee-specific swaps. @@ -160,27 +160,27 @@ module arena::emojicoin_arena { ); } - public entry fun set_new_melee_available_rewards( + public entry fun set_next_melee_available_rewards( arena: &signer, amount: u64 ) acquires Registry { - borrow_registry_ref_mut_checked(arena).new_melee_available_rewards = amount; + borrow_registry_ref_mut_checked(arena).next_melee_available_rewards = amount; } - public entry fun set_new_melee_duration(arena: &signer, duration: u64) acquires Registry { + public entry fun set_next_melee_duration(arena: &signer, duration: u64) acquires Registry { let registry_ref_mut = borrow_registry_ref_mut_checked(arena); - registry_ref_mut.new_melee_duration = duration; + registry_ref_mut.next_melee_duration = duration; } - public entry fun set_new_melee_max_match_percentage( + public entry fun set_next_melee_max_match_percentage( arena: &signer, max_match_percentage: u64 ) acquires Registry { - borrow_registry_ref_mut_checked(arena).new_melee_max_match_percentage = max_match_percentage; + borrow_registry_ref_mut_checked(arena).next_melee_max_match_percentage = max_match_percentage; } - public entry fun set_new_melee_max_match_amount( + public entry fun set_next_melee_max_match_amount( arena: &signer, max_match_amount: u64 ) acquires Registry { - borrow_registry_ref_mut_checked(arena).new_melee_max_match_amount = max_match_amount; + borrow_registry_ref_mut_checked(arena).next_melee_max_match_amount = max_match_amount; } public entry fun withdraw_from_vault(arena: &signer, amount: u64) acquires Registry { @@ -401,10 +401,10 @@ module arena::emojicoin_arena { melees_by_id: smart_table::new(), melee_ids_by_market_ids: smart_table::new(), signer_capability, - new_melee_duration: DEFAULT_DURATION, - new_melee_available_rewards: DEFAULT_AVAILABLE_REWARDS, - new_melee_max_match_percentage: DEFAULT_MAX_MATCH_PERCENTAGE, - new_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT, + next_melee_duration: DEFAULT_DURATION, + next_melee_available_rewards: DEFAULT_AVAILABLE_REWARDS, + next_melee_max_match_percentage: DEFAULT_MAX_MATCH_PERCENTAGE, + next_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT, all_entrants: smart_table::new(), n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), @@ -603,12 +603,12 @@ module arena::emojicoin_arena { ) }), start_time: last_period_boundary( - timestamp::now_microseconds(), registry_ref_mut.new_melee_duration + timestamp::now_microseconds(), registry_ref_mut.next_melee_duration ), - duration: registry_ref_mut.new_melee_duration, - max_match_percentage: registry_ref_mut.new_melee_max_match_percentage, - max_match_amount: registry_ref_mut.new_melee_max_match_amount, - available_rewards: registry_ref_mut.new_melee_available_rewards, + duration: registry_ref_mut.next_melee_duration, + max_match_percentage: registry_ref_mut.next_melee_max_match_percentage, + max_match_amount: registry_ref_mut.next_melee_max_match_amount, + available_rewards: registry_ref_mut.next_melee_available_rewards, all_entrants: smart_table::new(), active_entrants: smart_table::new(), exited_entrants: smart_table::new(), From 12b9848960f28bd471c2c7b2c635f24befd821da Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:05:26 -0800 Subject: [PATCH 51/64] Update src/move/emojicoin_arena/sources/emojicoin_arena.move Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 3bdc11997..fd547e37b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -206,8 +206,10 @@ module arena::emojicoin_arena { registry_ref_mut.melees_by_id.borrow_mut(n_melees_before_cranking); let market_metadatas = current_melee_ref_mut.market_metadatas; let (_, market_address_0, _) = unpack_market_metadata(market_metadatas[0]); + // Ensures the function aborts if Coin0 doesn't match LP0. market_view(market_address_0); let (_, market_address_1, _) = unpack_market_metadata(market_metadatas[1]); + // Ensures the function aborts if Coin1 doesn't match LP1. market_view(market_address_1); // Create escrow and user melees resources if they don't exist. From f219e9c406f8067106d7ef533c230b0e3b32d38b Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:13:58 -0800 Subject: [PATCH 52/64] Clarify match math --- .../sources/emojicoin_arena.move | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index fd547e37b..bbb40350b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -535,30 +535,39 @@ module arena::emojicoin_arena { let duration = (current_melee_ref_mut.duration as u256); if (elapsed_time >= duration) { 0 } else { - min( - // Scale down input amount for matching percentage and time elapsed in one compound - // operation, to reduce truncation errors. + // Scale down input amount for matching percentage and remaining time in one compound + // operation, to reduce truncation errors. Equivalent to: + // + // max match percentage remaining time + // input_amount * -------------------- * -------------- + // 100 duration + let raw_match_amount = ( ((input_amount as u256) * (current_melee_ref_mut.max_match_percentage as u256) * (duration - elapsed_time)) / ((MAX_PERCENTAGE as u256) * duration) as u64 - ), + ); + // Correct for the amount that is available in the vault. + let corrected_for_vault_balance = min( - // Correct for vault balance. + raw_match_amount, coin::balance( account::get_signer_capability_address( ®istry_ref_mut.signer_capability ) - ), - min( - // Correct for available rewards in current melee. - current_melee_ref_mut.available_rewards, - // Correct for max match amount user is eligible for. - current_melee_ref_mut.max_match_amount - - escrow_ref_mut.tap_out_fee ) - ) + ); + // Correct for available rewards in melee. + let corrected_for_melee_available_rewards = + min( + corrected_for_vault_balance, + current_melee_ref_mut.available_rewards + ); + // Correct for the max match amount that the user is eligible for. + min( + corrected_for_melee_available_rewards, + current_melee_ref_mut.max_match_amount - escrow_ref_mut.tap_out_fee ) } } From 05b84a5661242e64de2746a56add25920dbf1d0f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:41:13 -0800 Subject: [PATCH 53/64] Add assorted escrow/index update logic --- .../sources/emojicoin_arena.move | 85 +++++++++++++------ 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index bbb40350b..deb3a520a 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -43,6 +43,8 @@ module arena::emojicoin_arena { const E_NO_ESCROW: u64 = 8; /// Swapper has no funds in escrow to swap. const E_SWAP_NO_FUNDS: u64 = 9; + /// User has no funds in escrow to withdraw. + const E_EXIT_NO_FUNDS: u64 = 10; /// Resource account address seed for the registry. const REGISTRY_SEED: vector = b"Arena registry"; @@ -325,7 +327,11 @@ module arena::emojicoin_arena { coin::merge( &mut escrow_ref_mut.emojicoin_1, coin::withdraw(entrant, net_proceeds) ); - } + }; + + // Update melee state. + current_melee_ref_mut.all_entrants_add_if_not_contains(entrant_address); + current_melee_ref_mut.active_entrants_add_if_not_contains(entrant_address); } #[randomness] @@ -433,8 +439,8 @@ module arena::emojicoin_arena { ); } - inline fun add_if_not_contains( - map_ref_mut: &mut SmartTable, key: u64 + inline fun add_if_not_contains( + map_ref_mut: &mut SmartTable, key: T ) { if (!map_ref_mut.contains(key)) { map_ref_mut.add(key, Nil {}); @@ -468,6 +474,24 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } + inline fun all_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.all_entrants, address); + } + + inline fun active_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.active_entrants, address); + } + + inline fun active_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.active_entrants, address); + } + inline fun available_rewards_decrement(self: &mut Melee, amount: u64) { self.available_rewards = self.available_rewards - amount; } @@ -485,32 +509,39 @@ module arena::emojicoin_arena { ) acquires Registry { let escrow_ref_mut = &mut MeleeEscrow[participant_address]; let melee_id = escrow_ref_mut.melee_id; + let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); - // Update available rewards and transfer tap out fee back to vault if applicable. + // Charge tap out fee if applicable. if (melee_is_current) { - let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; - exited_melee_ref_mut.available_rewards_increment(*tap_out_fee_ref_mut); - let vault_address = - account::get_signer_capability_address(®istry_ref_mut.signer_capability); - aptos_account::transfer(participant, vault_address, *tap_out_fee_ref_mut); - *tap_out_fee_ref_mut = 0; + if (*tap_out_fee_ref_mut > 0) { + let vault_address = + account::get_signer_capability_address( + ®istry_ref_mut.signer_capability + ); + aptos_account::transfer( + participant, vault_address, *tap_out_fee_ref_mut + ); + *tap_out_fee_ref_mut = 0; + exited_melee_ref_mut.available_rewards_increment(*tap_out_fee_ref_mut); + } }; - // Move emojicoin balances out of escrow. - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_0) - ); - aptos_account::deposit_coins( - participant_address, - coin::extract_all(&mut escrow_ref_mut.emojicoin_1) - ); + // Withdraw emojicoin balances from escrow. + if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { + withdraw_from_escrow(participant_address, &mut escrow_ref_mut.emojicoin_0); + } else { + assert!(coin::value(&escrow_ref_mut.emojicoin_1) > 0, E_EXIT_NO_FUNDS); + withdraw_from_escrow(participant_address, &mut escrow_ref_mut.emojicoin_1); + }; - // Update user melees resource. + // Update user state. let user_melees_ref_mut = &mut UserMelees[participant_address]; add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); + + // Update melee state. + exited_melee_ref_mut.active_entrants_remove_if_contains(participant_address); } inline fun get_n_registered_markets(): u64 { @@ -633,8 +664,8 @@ module arena::emojicoin_arena { registry_ref_mut.melee_ids_by_market_ids.add(sorted_unique_market_ids, melee_id); } - inline fun remove_if_contains( - map_ref_mut: &mut SmartTable, key: u64 + inline fun remove_if_contains( + map_ref_mut: &mut SmartTable, key: T ) { if (map_ref_mut.contains(key)) { map_ref_mut.remove(key); @@ -724,9 +755,7 @@ module arena::emojicoin_arena { ) { // Move all from coins out of escrow. let input_amount = coin::value(escrow_from_coin_ref_mut); - aptos_account::deposit_coins( - swapper_address, coin::extract_all(escrow_from_coin_ref_mut) - ); + withdraw_from_escrow(swapper_address, escrow_from_coin_ref_mut); // Swap into APT. let (net_proceeds_in_apt, _) = @@ -753,4 +782,10 @@ module arena::emojicoin_arena { escrow_to_coin_ref_mut, coin::withdraw(swapper, net_proceeds_in_to_coin) ); } + + inline fun withdraw_from_escrow( + recipient: address, escrow_coin_ref_mut: &mut Coin + ) { + aptos_account::deposit_coins(recipient, coin::extract_all(escrow_coin_ref_mut)); + } } From 63e7e80c1c9b1eed63878893a47d715a62d1e274 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:53:59 -0800 Subject: [PATCH 54/64] Add more melee-level indexing --- .../sources/emojicoin_arena.move | 105 +++++++++++++----- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index deb3a520a..c2c50bc0a 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -287,9 +287,7 @@ module arena::emojicoin_arena { ); assert!(match_amount > 0, E_UNABLE_TO_LOCK_IN); - // Update counters, transfer APT to entrant. - escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; - current_melee_ref_mut.available_rewards_decrement(match_amount); + // Transfer APT to entrant. aptos_account::transfer( &account::create_signer_with_capability( ®istry_ref_mut.signer_capability @@ -297,7 +295,17 @@ module arena::emojicoin_arena { entrant_address, match_amount ); + + // Update melee state. + current_melee_ref_mut.available_rewards_decrement(match_amount); + current_melee_ref_mut.locked_in_entrants_add_if_not_contains( + entrant_address + ); + + // Update escrow state. + escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; match_amount + } else 0; // Execute a swap then immediately move funds into escrow. @@ -332,6 +340,7 @@ module arena::emojicoin_arena { // Update melee state. current_melee_ref_mut.all_entrants_add_if_not_contains(entrant_address); current_melee_ref_mut.active_entrants_add_if_not_contains(entrant_address); + current_melee_ref_mut.exited_entrants_remove_if_contains(entrant_address); } #[randomness] @@ -369,26 +378,33 @@ module arena::emojicoin_arena { // emptied immediately after the swap. let (exit_once_done, registry_ref_mut, _, _) = crank_schedule(); - if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { - swap_within_escrow( - swapper, - swapper_address, - market_addresses[0], - market_addresses[1], - &mut escrow_ref_mut.emojicoin_0, - &mut escrow_ref_mut.emojicoin_1 - ); - } else { - assert!(coin::value(&escrow_ref_mut.emojicoin_1) > 0, E_SWAP_NO_FUNDS); - swap_within_escrow( - swapper, - swapper_address, - market_addresses[1], - market_addresses[0], - &mut escrow_ref_mut.emojicoin_1, - &mut escrow_ref_mut.emojicoin_0 - ); - }; + let quote_volume = + if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { + swap_within_escrow( + swapper, + swapper_address, + market_addresses[0], + market_addresses[1], + &mut escrow_ref_mut.emojicoin_0, + &mut escrow_ref_mut.emojicoin_1 + ) + } else { + assert!(coin::value(&escrow_ref_mut.emojicoin_1) > 0, E_SWAP_NO_FUNDS); + swap_within_escrow( + swapper, + swapper_address, + market_addresses[1], + market_addresses[0], + &mut escrow_ref_mut.emojicoin_1, + &mut escrow_ref_mut.emojicoin_0 + ) + }; + + // Update melee state. + let swap_melee_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(escrow_ref_mut.melee_id); + swap_melee_ref_mut.n_melee_swaps_increment(); + swap_melee_ref_mut.melee_swaps_volume_increment((quote_volume as u128)); if (exit_once_done) exit_inner( @@ -542,6 +558,20 @@ module arena::emojicoin_arena { // Update melee state. exited_melee_ref_mut.active_entrants_remove_if_contains(participant_address); + exited_melee_ref_mut.exited_entrants_add_if_not_contains(participant_address); + exited_melee_ref_mut.locked_in_entrants_remove_if_contains(participant_address); + } + + inline fun exited_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.exited_entrants, address); + } + + inline fun exited_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.exited_entrants, address); } inline fun get_n_registered_markets(): u64 { @@ -554,6 +584,18 @@ module arena::emojicoin_arena { (time / period) * period } + inline fun locked_in_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.locked_in_entrants, address); + } + + inline fun locked_in_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.locked_in_entrants, address); + } + /// Uses mutable references to avoid freezing references up the stack. inline fun match_amount( input_amount: u64, @@ -603,6 +645,16 @@ module arena::emojicoin_arena { } } + inline fun melee_swaps_volume_increment( + self: &mut Melee, amount: u128 + ) { + aggregator_v2::add(&mut self.melee_swaps_volume, amount); + } + + inline fun n_melee_swaps_increment(self: &mut Melee) { + aggregator_v2::add(&mut self.n_melee_swaps, 1); + } + /// Accepts a mutable reference to avoid freezing references up the stack. inline fun next_melee_market_ids(registry_ref_mut: &mut Registry): vector { let n_markets = get_n_registered_markets(); @@ -752,7 +804,7 @@ module arena::emojicoin_arena { market_address_to: address, escrow_from_coin_ref_mut: &mut Coin, escrow_to_coin_ref_mut: &mut Coin - ) { + ): u64 { // Move all from coins out of escrow. let input_amount = coin::value(escrow_from_coin_ref_mut); withdraw_from_escrow(swapper_address, escrow_from_coin_ref_mut); @@ -768,7 +820,7 @@ module arena::emojicoin_arena { ); // Swap into to emojicoin. - let (net_proceeds_in_to_coin, _) = + let (net_proceeds_in_to_coin, quote_volume) = swap_with_stats_buy_emojicoin( swapper, swapper_address, @@ -781,6 +833,9 @@ module arena::emojicoin_arena { coin::merge( escrow_to_coin_ref_mut, coin::withdraw(swapper, net_proceeds_in_to_coin) ); + + // Return quote volume on second swap only, to avoid double-counting. + quote_volume } inline fun withdraw_from_escrow( From c93ece601392ee803970e6b1687b18e34539065a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:59:45 -0800 Subject: [PATCH 55/64] Update locked in exit indexing --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index c2c50bc0a..37e45ecd5 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -84,7 +84,8 @@ module arena::emojicoin_arena { active_entrants: SmartTable, /// Entrants who have exited the melee, used as a set. exited_entrants: SmartTable, - /// Entrants who have locked in, used as a set. + /// Entrants who have locked in, used as a set. If user exits before melee ends they are + /// removed from this set. If they exit after the melee ends, they are not removed. locked_in_entrants: SmartTable, /// Number of melee-specific swaps. n_melee_swaps: Aggregator, @@ -539,7 +540,12 @@ module arena::emojicoin_arena { participant, vault_address, *tap_out_fee_ref_mut ); *tap_out_fee_ref_mut = 0; + + // Update melee state. exited_melee_ref_mut.available_rewards_increment(*tap_out_fee_ref_mut); + exited_melee_ref_mut.locked_in_entrants_remove_if_contains( + participant_address + ); } }; @@ -559,7 +565,6 @@ module arena::emojicoin_arena { // Update melee state. exited_melee_ref_mut.active_entrants_remove_if_contains(participant_address); exited_melee_ref_mut.exited_entrants_add_if_not_contains(participant_address); - exited_melee_ref_mut.locked_in_entrants_remove_if_contains(participant_address); } inline fun exited_entrants_add_if_not_contains( From 21441ea3cf0db04f5b9661873d4d6f9ada057ecb Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:40:34 -0800 Subject: [PATCH 56/64] Update entered/matched fields --- .../sources/emojicoin_arena.move | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 37e45ecd5..688939cd5 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -108,13 +108,13 @@ module arena::emojicoin_arena { melee_swaps_volume: u128, /// Number of swaps user has executed during the melee. n_melee_swaps: u64, - /// APT entered into the melee, used as a benchmark for PnL calculations. Normalized when - /// topping off, reset to zero when exiting. + /// Cumulative amount of APT entered into the melee since the most recent deposit into an + /// empty escrow. Inclusive of total amount matched from locking in since most recent + /// deposit into an empty escrow. Reset to 0 upon exit. octas_entered: u64, - /// Octas user must pay to exit the melee before it ends, if they have locked in. Equivalent - /// to the current total amount of APT rewards they have been matched across all - /// transactions since they began depositing funds into an empty escrow. - tap_out_fee: u64 + /// Cumulative amount of APT matched since most recent deposit into an empty escrow, reset + /// to 0 upon exit. Must be paid back in full when tapping out. + octas_matched: u64 } struct Nil has drop, store {} @@ -226,7 +226,7 @@ module arena::emojicoin_arena { emojicoin_0: coin::zero(), emojicoin_1: coin::zero(), octas_entered: 0, - tap_out_fee: 0, + octas_matched: 0, melee_swaps_volume: 0, n_melee_swaps: 0 } @@ -304,7 +304,8 @@ module arena::emojicoin_arena { ); // Update escrow state. - escrow_ref_mut.tap_out_fee = escrow_ref_mut.tap_out_fee + match_amount; + escrow_ref_mut.octas_matched = escrow_ref_mut.octas_matched + + match_amount; match_amount } else 0; @@ -530,7 +531,7 @@ module arena::emojicoin_arena { // Charge tap out fee if applicable. if (melee_is_current) { - let tap_out_fee_ref_mut = &mut escrow_ref_mut.tap_out_fee; + let tap_out_fee_ref_mut = &mut escrow_ref_mut.octas_matched; if (*tap_out_fee_ref_mut > 0) { let vault_address = account::get_signer_capability_address( @@ -645,7 +646,7 @@ module arena::emojicoin_arena { // Correct for the max match amount that the user is eligible for. min( corrected_for_melee_available_rewards, - current_melee_ref_mut.max_match_amount - escrow_ref_mut.tap_out_fee + current_melee_ref_mut.max_match_amount - escrow_ref_mut.octas_matched ) } } From 68b273f2ed802986d2834f7a6b236c2871a6c9c9 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:56:15 -0800 Subject: [PATCH 57/64] Add total emojicoins locked calculations --- .../sources/emojicoin_arena.move | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 688939cd5..b0102655d 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -380,31 +380,55 @@ module arena::emojicoin_arena { // emptied immediately after the swap. let (exit_once_done, registry_ref_mut, _, _) = crank_schedule(); + // Swap, updating total emojicoin locked values based on side. + let swap_melee_ref_mut = + registry_ref_mut.melees_by_id.borrow_mut(escrow_ref_mut.melee_id); + let emojicoin_0_ref_mut = &mut escrow_ref_mut.emojicoin_0; + let emojicoin_1_ref_mut = &mut escrow_ref_mut.emojicoin_1; + let emojicoin_0_locked_before_swap = coin::value(emojicoin_0_ref_mut); + let emojicoin_1_locked_before_swap = coin::value(emojicoin_1_ref_mut); let quote_volume = - if (coin::value(&escrow_ref_mut.emojicoin_0) > 0) { - swap_within_escrow( - swapper, - swapper_address, - market_addresses[0], - market_addresses[1], - &mut escrow_ref_mut.emojicoin_0, - &mut escrow_ref_mut.emojicoin_1 - ) + if (emojicoin_0_locked_before_swap > 0) { + let quote_volume = + swap_within_escrow( + swapper, + swapper_address, + market_addresses[0], + market_addresses[1], + emojicoin_0_ref_mut, + emojicoin_1_ref_mut + ); + swap_melee_ref_mut.emojicoin_0_locked_decrement( + emojicoin_0_locked_before_swap + ); + swap_melee_ref_mut.emojicoin_1_locked_increment( + coin::value(emojicoin_1_ref_mut) + ); + quote_volume } else { - assert!(coin::value(&escrow_ref_mut.emojicoin_1) > 0, E_SWAP_NO_FUNDS); - swap_within_escrow( - swapper, - swapper_address, - market_addresses[1], - market_addresses[0], - &mut escrow_ref_mut.emojicoin_1, - &mut escrow_ref_mut.emojicoin_0 - ) + assert!(emojicoin_1_locked_before_swap > 0, E_SWAP_NO_FUNDS); + swap_melee_ref_mut.emojicoin_1_locked_decrement( + emojicoin_1_locked_before_swap + ); + let quote_volume = + swap_within_escrow( + swapper, + swapper_address, + market_addresses[1], + market_addresses[0], + emojicoin_1_ref_mut, + emojicoin_0_ref_mut + ); + swap_melee_ref_mut.emojicoin_1_locked_decrement( + emojicoin_1_locked_before_swap + ); + swap_melee_ref_mut.emojicoin_0_locked_increment( + coin::value(emojicoin_0_ref_mut) + ); + quote_volume }; // Update melee state. - let swap_melee_ref_mut = - registry_ref_mut.melees_by_id.borrow_mut(escrow_ref_mut.melee_id); swap_melee_ref_mut.n_melee_swaps_increment(); swap_melee_ref_mut.melee_swaps_volume_increment((quote_volume as u128)); @@ -518,6 +542,22 @@ module arena::emojicoin_arena { self.available_rewards = self.available_rewards + amount; } + inline fun emojicoin_0_locked_decrement(self: &mut Melee, amount: u64) { + aggregator_v2::sub(&mut self.emojicoin_0_locked, amount); + } + + inline fun emojicoin_0_locked_increment(self: &mut Melee, amount: u64) { + aggregator_v2::add(&mut self.emojicoin_0_locked, amount); + } + + inline fun emojicoin_1_locked_decrement(self: &mut Melee, amount: u64) { + aggregator_v2::sub(&mut self.emojicoin_1_locked, amount); + } + + inline fun emojicoin_1_locked_increment(self: &mut Melee, amount: u64) { + aggregator_v2::add(&mut self.emojicoin_1_locked, amount); + } + /// Assumes user has an escrow resource. inline fun exit_inner( participant: &signer, From 3e13dbe74eccc656494e894b5a0b3c06c3fabde9 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:03:13 -0800 Subject: [PATCH 58/64] Add struct prefix --- .../sources/emojicoin_arena.move | 164 +++++++++--------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index b0102655d..f7dfcd55b 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -298,8 +298,8 @@ module arena::emojicoin_arena { ); // Update melee state. - current_melee_ref_mut.available_rewards_decrement(match_amount); - current_melee_ref_mut.locked_in_entrants_add_if_not_contains( + current_melee_ref_mut.melee_available_rewards_decrement(match_amount); + current_melee_ref_mut.melee_locked_in_entrants_add_if_not_contains( entrant_address ); @@ -340,9 +340,9 @@ module arena::emojicoin_arena { }; // Update melee state. - current_melee_ref_mut.all_entrants_add_if_not_contains(entrant_address); - current_melee_ref_mut.active_entrants_add_if_not_contains(entrant_address); - current_melee_ref_mut.exited_entrants_remove_if_contains(entrant_address); + current_melee_ref_mut.melee_all_entrants_add_if_not_contains(entrant_address); + current_melee_ref_mut.melee_active_entrants_add_if_not_contains(entrant_address); + current_melee_ref_mut.melee_exited_entrants_remove_if_contains(entrant_address); } #[randomness] @@ -398,7 +398,7 @@ module arena::emojicoin_arena { emojicoin_0_ref_mut, emojicoin_1_ref_mut ); - swap_melee_ref_mut.emojicoin_0_locked_decrement( + swap_melee_ref_mut.melee_emojicoin_0_locked_decrement( emojicoin_0_locked_before_swap ); swap_melee_ref_mut.emojicoin_1_locked_increment( @@ -429,7 +429,7 @@ module arena::emojicoin_arena { }; // Update melee state. - swap_melee_ref_mut.n_melee_swaps_increment(); + swap_melee_ref_mut.melee_n_melee_swaps_increment(); swap_melee_ref_mut.melee_swaps_volume_increment((quote_volume as u128)); if (exit_once_done) @@ -516,48 +516,6 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } - inline fun all_entrants_add_if_not_contains( - self: &mut Melee, address: address - ) { - add_if_not_contains(&mut self.all_entrants, address); - } - - inline fun active_entrants_add_if_not_contains( - self: &mut Melee, address: address - ) { - add_if_not_contains(&mut self.active_entrants, address); - } - - inline fun active_entrants_remove_if_contains( - self: &mut Melee, address: address - ) { - remove_if_contains(&mut self.active_entrants, address); - } - - inline fun available_rewards_decrement(self: &mut Melee, amount: u64) { - self.available_rewards = self.available_rewards - amount; - } - - inline fun available_rewards_increment(self: &mut Melee, amount: u64) { - self.available_rewards = self.available_rewards + amount; - } - - inline fun emojicoin_0_locked_decrement(self: &mut Melee, amount: u64) { - aggregator_v2::sub(&mut self.emojicoin_0_locked, amount); - } - - inline fun emojicoin_0_locked_increment(self: &mut Melee, amount: u64) { - aggregator_v2::add(&mut self.emojicoin_0_locked, amount); - } - - inline fun emojicoin_1_locked_decrement(self: &mut Melee, amount: u64) { - aggregator_v2::sub(&mut self.emojicoin_1_locked, amount); - } - - inline fun emojicoin_1_locked_increment(self: &mut Melee, amount: u64) { - aggregator_v2::add(&mut self.emojicoin_1_locked, amount); - } - /// Assumes user has an escrow resource. inline fun exit_inner( participant: &signer, @@ -583,8 +541,10 @@ module arena::emojicoin_arena { *tap_out_fee_ref_mut = 0; // Update melee state. - exited_melee_ref_mut.available_rewards_increment(*tap_out_fee_ref_mut); - exited_melee_ref_mut.locked_in_entrants_remove_if_contains( + exited_melee_ref_mut.melee_available_rewards_increment( + *tap_out_fee_ref_mut + ); + exited_melee_ref_mut.melee_locked_in_entrants_remove_if_contains( participant_address ); } @@ -604,20 +564,8 @@ module arena::emojicoin_arena { remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); // Update melee state. - exited_melee_ref_mut.active_entrants_remove_if_contains(participant_address); - exited_melee_ref_mut.exited_entrants_add_if_not_contains(participant_address); - } - - inline fun exited_entrants_add_if_not_contains( - self: &mut Melee, address: address - ) { - add_if_not_contains(&mut self.exited_entrants, address); - } - - inline fun exited_entrants_remove_if_contains( - self: &mut Melee, address: address - ) { - remove_if_contains(&mut self.exited_entrants, address); + exited_melee_ref_mut.melee_active_entrants_remove_if_contains(participant_address); + exited_melee_ref_mut.melee_exited_entrants_add_if_not_contains(participant_address); } inline fun get_n_registered_markets(): u64 { @@ -630,18 +578,6 @@ module arena::emojicoin_arena { (time / period) * period } - inline fun locked_in_entrants_add_if_not_contains( - self: &mut Melee, address: address - ) { - add_if_not_contains(&mut self.locked_in_entrants, address); - } - - inline fun locked_in_entrants_remove_if_contains( - self: &mut Melee, address: address - ) { - remove_if_contains(&mut self.locked_in_entrants, address); - } - /// Uses mutable references to avoid freezing references up the stack. inline fun match_amount( input_amount: u64, @@ -691,13 +627,85 @@ module arena::emojicoin_arena { } } + inline fun melee_active_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.active_entrants, address); + } + + inline fun melee_active_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.active_entrants, address); + } + + inline fun melee_all_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.all_entrants, address); + } + + inline fun melee_available_rewards_decrement( + self: &mut Melee, amount: u64 + ) { + self.available_rewards = self.available_rewards - amount; + } + + inline fun melee_available_rewards_increment( + self: &mut Melee, amount: u64 + ) { + self.available_rewards = self.available_rewards + amount; + } + + inline fun melee_emojicoin_0_locked_decrement( + self: &mut Melee, amount: u64 + ) { + aggregator_v2::sub(&mut self.emojicoin_0_locked, amount); + } + + inline fun emojicoin_0_locked_increment(self: &mut Melee, amount: u64) { + aggregator_v2::add(&mut self.emojicoin_0_locked, amount); + } + + inline fun emojicoin_1_locked_decrement(self: &mut Melee, amount: u64) { + aggregator_v2::sub(&mut self.emojicoin_1_locked, amount); + } + + inline fun emojicoin_1_locked_increment(self: &mut Melee, amount: u64) { + aggregator_v2::add(&mut self.emojicoin_1_locked, amount); + } + + inline fun melee_exited_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.exited_entrants, address); + } + + inline fun melee_exited_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.exited_entrants, address); + } + + inline fun melee_locked_in_entrants_add_if_not_contains( + self: &mut Melee, address: address + ) { + add_if_not_contains(&mut self.locked_in_entrants, address); + } + + inline fun melee_locked_in_entrants_remove_if_contains( + self: &mut Melee, address: address + ) { + remove_if_contains(&mut self.locked_in_entrants, address); + } + inline fun melee_swaps_volume_increment( self: &mut Melee, amount: u128 ) { aggregator_v2::add(&mut self.melee_swaps_volume, amount); } - inline fun n_melee_swaps_increment(self: &mut Melee) { + inline fun melee_n_melee_swaps_increment(self: &mut Melee) { aggregator_v2::add(&mut self.n_melee_swaps, 1); } From cac0d712f8639a90d0b9aa3bc42f1520fa001a81 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:08:25 -0800 Subject: [PATCH 59/64] Simplify fields schema --- .../sources/emojicoin_arena.move | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index f7dfcd55b..13c492e57 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -88,9 +88,9 @@ module arena::emojicoin_arena { /// removed from this set. If they exit after the melee ends, they are not removed. locked_in_entrants: SmartTable, /// Number of melee-specific swaps. - n_melee_swaps: Aggregator, + n_swaps: Aggregator, /// Volume of melee-specific swaps in octas. - melee_swaps_volume: Aggregator, + swaps_volume: Aggregator, /// Amount of emojicoin 0 locked in all melee escrows for the melee. emojicoin_0_locked: Aggregator, /// Amount of emojicoin 1 locked in all melee escrows for the melee. @@ -104,10 +104,10 @@ module arena::emojicoin_arena { emojicoin_0: Coin, /// Emojicoin 1 holdings. emojicoin_1: Coin, - /// Volume of user's melee-specific swaps in octas. - melee_swaps_volume: u128, /// Number of swaps user has executed during the melee. - n_melee_swaps: u64, + n_swaps: u64, + /// Volume of user's melee-specific swaps in octas. + swaps_volume: u128, /// Cumulative amount of APT entered into the melee since the most recent deposit into an /// empty escrow. Inclusive of total amount matched from locking in since most recent /// deposit into an empty escrow. Reset to 0 upon exit. @@ -137,11 +137,11 @@ module arena::emojicoin_arena { /// All entrants who have entered a melee. all_entrants: SmartTable, /// Number of melee-specific swaps. - n_melee_swaps: Aggregator, + n_swaps: Aggregator, /// Volume of melee-specific swaps in octas. - melee_swaps_volume: Aggregator, - /// Amount of octas disbursed as rewards. Decremented when a user taps out. - rewards_disbursed: u64 + swaps_volume: Aggregator, + /// Amount of octas matched. Decremented when a user taps out. + octas_matched: u64 } struct UserMelees has key { @@ -227,8 +227,8 @@ module arena::emojicoin_arena { emojicoin_1: coin::zero(), octas_entered: 0, octas_matched: 0, - melee_swaps_volume: 0, - n_melee_swaps: 0 + swaps_volume: 0, + n_swaps: 0 } ); if (!exists(entrant_address)) { @@ -429,7 +429,7 @@ module arena::emojicoin_arena { }; // Update melee state. - swap_melee_ref_mut.melee_n_melee_swaps_increment(); + swap_melee_ref_mut.melee_n_swaps_increment(); swap_melee_ref_mut.melee_swaps_volume_increment((quote_volume as u128)); if (exit_once_done) @@ -456,9 +456,9 @@ module arena::emojicoin_arena { next_melee_max_match_percentage: DEFAULT_MAX_MATCH_PERCENTAGE, next_melee_max_match_amount: DEFAULT_MAX_MATCH_AMOUNT, all_entrants: smart_table::new(), - n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), - rewards_disbursed: 0 + n_swaps: aggregator_v2::create_unbounded_aggregator(), + swaps_volume: aggregator_v2::create_unbounded_aggregator(), + octas_matched: 0 } ); coin::register(&vault_signer); @@ -699,14 +699,14 @@ module arena::emojicoin_arena { remove_if_contains(&mut self.locked_in_entrants, address); } + inline fun melee_n_swaps_increment(self: &mut Melee) { + aggregator_v2::add(&mut self.n_swaps, 1); + } + inline fun melee_swaps_volume_increment( self: &mut Melee, amount: u128 ) { - aggregator_v2::add(&mut self.melee_swaps_volume, amount); - } - - inline fun melee_n_melee_swaps_increment(self: &mut Melee) { - aggregator_v2::add(&mut self.n_melee_swaps, 1); + aggregator_v2::add(&mut self.swaps_volume, amount); } /// Accepts a mutable reference to avoid freezing references up the stack. @@ -761,8 +761,8 @@ module arena::emojicoin_arena { active_entrants: smart_table::new(), exited_entrants: smart_table::new(), locked_in_entrants: smart_table::new(), - n_melee_swaps: aggregator_v2::create_unbounded_aggregator(), - melee_swaps_volume: aggregator_v2::create_unbounded_aggregator(), + n_swaps: aggregator_v2::create_unbounded_aggregator(), + swaps_volume: aggregator_v2::create_unbounded_aggregator(), emojicoin_0_locked: aggregator_v2::create_unbounded_aggregator(), emojicoin_1_locked: aggregator_v2::create_unbounded_aggregator() } From 324bed37285a4ca27e7ff4c58f1aa85140de7ad7 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:11:39 -0800 Subject: [PATCH 60/64] Rename to escrow --- .../sources/emojicoin_arena.move | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 13c492e57..5becd36b8 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -62,6 +62,8 @@ module arena::emojicoin_arena { const DEFAULT_MAX_MATCH_PERCENTAGE: u64 = 50; const DEFAULT_MAX_MATCH_AMOUNT: u64 = 5 * 100_000_000; + struct Nil has drop, store {} + struct Melee has store { /// 1-indexed for conformity with emojicoin market ID indexing. melee_id: u64, @@ -97,28 +99,6 @@ module arena::emojicoin_arena { emojicoin_1_locked: Aggregator } - struct MeleeEscrow has key { - /// Corresponding `Melee.melee_id`. - melee_id: u64, - /// Emojicoin 0 holdings. - emojicoin_0: Coin, - /// Emojicoin 1 holdings. - emojicoin_1: Coin, - /// Number of swaps user has executed during the melee. - n_swaps: u64, - /// Volume of user's melee-specific swaps in octas. - swaps_volume: u128, - /// Cumulative amount of APT entered into the melee since the most recent deposit into an - /// empty escrow. Inclusive of total amount matched from locking in since most recent - /// deposit into an empty escrow. Reset to 0 upon exit. - octas_entered: u64, - /// Cumulative amount of APT matched since most recent deposit into an empty escrow, reset - /// to 0 upon exit. Must be paid back in full when tapping out. - octas_matched: u64 - } - - struct Nil has drop, store {} - struct Registry has key { /// A map of each melee's `melee_id` to the melee. melees_by_id: SmartTable, @@ -144,6 +124,26 @@ module arena::emojicoin_arena { octas_matched: u64 } + struct Escrow has key { + /// Corresponding `Melee.melee_id`. + melee_id: u64, + /// Emojicoin 0 holdings. + emojicoin_0: Coin, + /// Emojicoin 1 holdings. + emojicoin_1: Coin, + /// Number of swaps user has executed during the melee. + n_swaps: u64, + /// Volume of user's melee-specific swaps in octas. + swaps_volume: u128, + /// Cumulative amount of APT entered into the melee since the most recent deposit into an + /// empty escrow. Inclusive of total amount matched from locking in since most recent + /// deposit into an empty escrow. Reset to 0 upon exit. + octas_entered: u64, + /// Cumulative amount of APT matched since most recent deposit into an empty escrow, reset + /// to 0 upon exit. Must be paid back in full when tapping out. + octas_matched: u64 + } + struct UserMelees has key { /// Set of serial IDs of all melees the user has entered. entered_melee_ids: SmartTable, @@ -199,7 +199,7 @@ module arena::emojicoin_arena { #[randomness] entry fun enter( entrant: &signer, input_amount: u64, lock_in: bool - ) acquires MeleeEscrow, Registry, UserMelees { + ) acquires Escrow, Registry, UserMelees { let (melee_just_ended, registry_ref_mut, time, n_melees_before_cranking) = crank_schedule(); if (melee_just_ended) return; // Can not enter melee if cranking ends it. @@ -218,10 +218,10 @@ module arena::emojicoin_arena { // Create escrow and user melees resources if they don't exist. let melee_id = current_melee_ref_mut.melee_id; let entrant_address = signer::address_of(entrant); - if (!exists>(entrant_address)) { + if (!exists>(entrant_address)) { move_to( entrant, - MeleeEscrow { + Escrow { melee_id, emojicoin_0: coin::zero(), emojicoin_1: coin::zero(), @@ -264,7 +264,7 @@ module arena::emojicoin_arena { }; // Verify that user does not split balance between the two emojicoins. - let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; + let escrow_ref_mut = &mut Escrow[entrant_address]; if (buy_coin_0) assert!( coin::value(&escrow_ref_mut.emojicoin_1) == 0, E_ENTER_COIN_BALANCE_1 @@ -312,7 +312,7 @@ module arena::emojicoin_arena { // Execute a swap then immediately move funds into escrow. let input_amount_after_matching = input_amount + match_amount; - let escrow_ref_mut = &mut MeleeEscrow[entrant_address]; + let escrow_ref_mut = &mut Escrow[entrant_address]; if (buy_coin_0) { let (net_proceeds, _) = swap_with_stats_buy_emojicoin( @@ -346,12 +346,10 @@ module arena::emojicoin_arena { } #[randomness] - entry fun exit( - participant: &signer - ) acquires MeleeEscrow, Registry, UserMelees { + entry fun exit(participant: &signer) acquires Escrow, Registry, UserMelees { let participant_address = signer::address_of(participant); assert!( - exists>(participant_address), + exists>(participant_address), E_NO_ESCROW ); let (melee_just_ended, registry_ref_mut, _, _) = crank_schedule(); @@ -366,15 +364,15 @@ module arena::emojicoin_arena { #[randomness] entry fun swap( swapper: &signer, market_addresses: vector
- ) acquires MeleeEscrow, Registry, UserMelees { + ) acquires Escrow, Registry, UserMelees { // Verify that swapper has an escrow resource. let swapper_address = signer::address_of(swapper); assert!( - exists>(swapper_address), + exists>(swapper_address), E_NO_ESCROW ); - let escrow_ref_mut = &mut MeleeEscrow[swapper_address]; + let escrow_ref_mut = &mut Escrow[swapper_address]; // Try cranking the schedule, and if a new melee starts, flag that user's escrow should be // emptied immediately after the swap. @@ -523,7 +521,7 @@ module arena::emojicoin_arena { registry_ref_mut: &mut Registry, melee_is_current: bool ) acquires Registry { - let escrow_ref_mut = &mut MeleeEscrow[participant_address]; + let escrow_ref_mut = &mut Escrow[participant_address]; let melee_id = escrow_ref_mut.melee_id; let exited_melee_ref_mut = registry_ref_mut.melees_by_id.borrow_mut(melee_id); @@ -581,7 +579,7 @@ module arena::emojicoin_arena { /// Uses mutable references to avoid freezing references up the stack. inline fun match_amount( input_amount: u64, - escrow_ref_mut: &mut MeleeEscrow, + escrow_ref_mut: &mut Escrow, current_melee_ref_mut: &mut Melee, registry_ref_mut: &mut Registry, time: u64 From ea2b5d8e51a1391896127a6b043f31df123cba2c Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:36:55 -0800 Subject: [PATCH 61/64] Add assorted onchain indexing --- .../sources/emojicoin_arena.move | 158 ++++++++++++++---- 1 file changed, 128 insertions(+), 30 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 5becd36b8..88cf0bfc7 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -243,12 +243,6 @@ module arena::emojicoin_arena { }; }; - // Update user melees resource. - let user_melees_ref_mut = &mut UserMelees[entrant_address]; - add_if_not_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); - add_if_not_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - remove_if_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); - // Verify user is selecting one of the two emojicoin types. let coin_0_type_info = type_info::type_of(); let coin_1_type_info = type_info::type_of(); @@ -274,7 +268,7 @@ module arena::emojicoin_arena { coin::value(&escrow_ref_mut.emojicoin_0) == 0, E_ENTER_COIN_BALANCE_0 ); - // Try matching user's contribution if they elect to lock in. + // Match a portion of user's contribution if they elect to lock in. let match_amount = if (lock_in) { // Verify that user can even lock in. @@ -303,9 +297,12 @@ module arena::emojicoin_arena { entrant_address ); + // Update registry state. + registry_ref_mut.registry_octas_matched_increment(match_amount); + // Update escrow state. - escrow_ref_mut.octas_matched = escrow_ref_mut.octas_matched - + match_amount; + escrow_ref_mut.escrow_octas_matched_increment(match_amount); + match_amount } else 0; @@ -343,6 +340,19 @@ module arena::emojicoin_arena { current_melee_ref_mut.melee_all_entrants_add_if_not_contains(entrant_address); current_melee_ref_mut.melee_active_entrants_add_if_not_contains(entrant_address); current_melee_ref_mut.melee_exited_entrants_remove_if_contains(entrant_address); + + // Update registry state. + registry_ref_mut.registry_all_entrants_add_if_not_contains(entrant_address); + + // Update escrow state. + escrow_ref_mut.escrow_octas_entered_increment(input_amount_after_matching); + + // Update user melees state. + let user_melees_ref_mut = &mut UserMelees[entrant_address]; + add_if_not_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); + add_if_not_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); + remove_if_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + } #[randomness] @@ -399,13 +409,13 @@ module arena::emojicoin_arena { swap_melee_ref_mut.melee_emojicoin_0_locked_decrement( emojicoin_0_locked_before_swap ); - swap_melee_ref_mut.emojicoin_1_locked_increment( + swap_melee_ref_mut.melee_emojicoin_1_locked_increment( coin::value(emojicoin_1_ref_mut) ); quote_volume } else { assert!(emojicoin_1_locked_before_swap > 0, E_SWAP_NO_FUNDS); - swap_melee_ref_mut.emojicoin_1_locked_decrement( + swap_melee_ref_mut.melee_emojicoin_1_locked_decrement( emojicoin_1_locked_before_swap ); let quote_volume = @@ -417,18 +427,27 @@ module arena::emojicoin_arena { emojicoin_1_ref_mut, emojicoin_0_ref_mut ); - swap_melee_ref_mut.emojicoin_1_locked_decrement( + swap_melee_ref_mut.melee_emojicoin_1_locked_decrement( emojicoin_1_locked_before_swap ); - swap_melee_ref_mut.emojicoin_0_locked_increment( + swap_melee_ref_mut.melee_emojicoin_0_locked_increment( coin::value(emojicoin_0_ref_mut) ); quote_volume }; // Update melee state. + let quote_volume_u128 = (quote_volume as u128); swap_melee_ref_mut.melee_n_swaps_increment(); - swap_melee_ref_mut.melee_swaps_volume_increment((quote_volume as u128)); + swap_melee_ref_mut.melee_swaps_volume_increment(quote_volume_u128); + + // Update registry state. + registry_ref_mut.registry_n_swaps_increment(); + registry_ref_mut.registry_swaps_volume_increment(quote_volume_u128); + + // Update escrow state. + escrow_ref_mut.escrow_n_swaps_increment(); + escrow_ref_mut.escrow_swaps_volume_increment(quote_volume_u128); if (exit_once_done) exit_inner( @@ -437,6 +456,7 @@ module arena::emojicoin_arena { registry_ref_mut, false ); + } fun init_module(arena: &signer) acquires Registry { @@ -514,6 +534,45 @@ module arena::emojicoin_arena { (cranked, registry_ref_mut, time, n_melees_before_cranking) } + inline fun escrow_n_swaps_increment( + self: &mut Escrow + ) { + self.n_swaps = self.n_swaps + 1; + } + + inline fun escrow_octas_entered_increment( + self: &mut Escrow, + amount: u64 + ) { + self.octas_entered = self.octas_entered + amount; + } + + inline fun escrow_octas_entered_reset( + self: &mut Escrow + ) { + self.octas_entered = 0; + } + + inline fun escrow_octas_matched_increment( + self: &mut Escrow, + amount: u64 + ) { + self.octas_matched = self.octas_matched + amount; + } + + inline fun escrow_octas_matched_reset( + self: &mut Escrow + ) { + self.octas_matched = 0; + } + + inline fun escrow_swaps_volume_increment( + self: &mut Escrow, + amount: u128 + ) { + self.swaps_volume = self.swaps_volume + amount; + } + /// Assumes user has an escrow resource. inline fun exit_inner( participant: &signer, @@ -527,24 +586,25 @@ module arena::emojicoin_arena { // Charge tap out fee if applicable. if (melee_is_current) { - let tap_out_fee_ref_mut = &mut escrow_ref_mut.octas_matched; - if (*tap_out_fee_ref_mut > 0) { + let octas_matched = escrow_ref_mut.octas_matched; + if (octas_matched > 0) { let vault_address = account::get_signer_capability_address( ®istry_ref_mut.signer_capability ); - aptos_account::transfer( - participant, vault_address, *tap_out_fee_ref_mut - ); - *tap_out_fee_ref_mut = 0; + aptos_account::transfer(participant, vault_address, octas_matched); // Update melee state. - exited_melee_ref_mut.melee_available_rewards_increment( - *tap_out_fee_ref_mut - ); + exited_melee_ref_mut.melee_available_rewards_increment(octas_matched); exited_melee_ref_mut.melee_locked_in_entrants_remove_if_contains( participant_address ); + + // Update registry state. + registry_ref_mut.registry_octas_matched_decrement(octas_matched); + + // Update escrow state. + escrow_ref_mut.escrow_octas_matched_reset(); } }; @@ -556,14 +616,18 @@ module arena::emojicoin_arena { withdraw_from_escrow(participant_address, &mut escrow_ref_mut.emojicoin_1); }; - // Update user state. + // Update melee state. + exited_melee_ref_mut.melee_active_entrants_remove_if_contains(participant_address); + exited_melee_ref_mut.melee_exited_entrants_add_if_not_contains(participant_address); + + // Update escrow state. + escrow_ref_mut.escrow_octas_entered_reset(); + + // Update user melees state. let user_melees_ref_mut = &mut UserMelees[participant_address]; add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - // Update melee state. - exited_melee_ref_mut.melee_active_entrants_remove_if_contains(participant_address); - exited_melee_ref_mut.melee_exited_entrants_add_if_not_contains(participant_address); } inline fun get_n_registered_markets(): u64 { @@ -661,15 +725,21 @@ module arena::emojicoin_arena { aggregator_v2::sub(&mut self.emojicoin_0_locked, amount); } - inline fun emojicoin_0_locked_increment(self: &mut Melee, amount: u64) { + inline fun melee_emojicoin_0_locked_increment( + self: &mut Melee, amount: u64 + ) { aggregator_v2::add(&mut self.emojicoin_0_locked, amount); } - inline fun emojicoin_1_locked_decrement(self: &mut Melee, amount: u64) { + inline fun melee_emojicoin_1_locked_decrement( + self: &mut Melee, amount: u64 + ) { aggregator_v2::sub(&mut self.emojicoin_1_locked, amount); } - inline fun emojicoin_1_locked_increment(self: &mut Melee, amount: u64) { + inline fun melee_emojicoin_1_locked_increment( + self: &mut Melee, amount: u64 + ) { aggregator_v2::add(&mut self.emojicoin_1_locked, amount); } @@ -768,6 +838,34 @@ module arena::emojicoin_arena { registry_ref_mut.melee_ids_by_market_ids.add(sorted_unique_market_ids, melee_id); } + inline fun registry_all_entrants_add_if_not_contains( + self: &mut Registry, address: address + ) { + add_if_not_contains(&mut self.all_entrants, address); + } + + inline fun registry_n_swaps_increment(self: &mut Registry) { + aggregator_v2::add(&mut self.n_swaps, 1); + } + + inline fun registry_octas_matched_decrement( + self: &mut Registry, amount: u64 + ) { + self.octas_matched = self.octas_matched - amount; + } + + inline fun registry_octas_matched_increment( + self: &mut Registry, amount: u64 + ) { + self.octas_matched = self.octas_matched + amount; + } + + inline fun registry_swaps_volume_increment( + self: &mut Registry, amount: u128 + ) { + aggregator_v2::add(&mut self.swaps_volume, amount); + } + inline fun remove_if_contains( map_ref_mut: &mut SmartTable, key: T ) { From ba1d1d8038fb50cb6f90a5ddee7b2aef5225162a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:57:40 -0800 Subject: [PATCH 62/64] Add preliminary indexing logic --- .../sources/emojicoin_arena.move | 109 ++++++++++++------ 1 file changed, 75 insertions(+), 34 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index 88cf0bfc7..dd08a89d9 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -307,51 +307,65 @@ module arena::emojicoin_arena { } else 0; - // Execute a swap then immediately move funds into escrow. + // Execute a swap then immediately move funds into escrow, updating total emojicoin locked + // values based on side. let input_amount_after_matching = input_amount + match_amount; - let escrow_ref_mut = &mut Escrow[entrant_address]; - if (buy_coin_0) { - let (net_proceeds, _) = - swap_with_stats_buy_emojicoin( - entrant, - entrant_address, - market_address_0, - input_amount_after_matching, - INTEGRATOR_FEE_RATE_BPS + let quote_volume = + if (buy_coin_0) { + let (net_proceeds, quote_volume) = + swap_with_stats_buy_emojicoin( + entrant, + entrant_address, + market_address_0, + input_amount_after_matching, + INTEGRATOR_FEE_RATE_BPS + ); + let emojicoin_0_ref_mut = &mut escrow_ref_mut.emojicoin_0; + coin::merge(emojicoin_0_ref_mut, coin::withdraw(entrant, net_proceeds)); + current_melee_ref_mut.melee_emojicoin_0_locked_increment( + coin::value(emojicoin_0_ref_mut) ); - coin::merge( - &mut escrow_ref_mut.emojicoin_0, coin::withdraw(entrant, net_proceeds) - ); - } else { - let (net_proceeds, _) = - swap_with_stats_buy_emojicoin( - entrant, - entrant_address, - market_address_1, - input_amount_after_matching, - INTEGRATOR_FEE_RATE_BPS + quote_volume + } else { + let (net_proceeds, quote_volume) = + swap_with_stats_buy_emojicoin( + entrant, + entrant_address, + market_address_1, + input_amount_after_matching, + INTEGRATOR_FEE_RATE_BPS + ); + let emojicoin_1_ref_mut = &mut escrow_ref_mut.emojicoin_1; + coin::merge(emojicoin_1_ref_mut, coin::withdraw(entrant, net_proceeds)); + current_melee_ref_mut.melee_emojicoin_0_locked_increment( + coin::value(emojicoin_1_ref_mut) ); - coin::merge( - &mut escrow_ref_mut.emojicoin_1, coin::withdraw(entrant, net_proceeds) - ); - }; + quote_volume + }; // Update melee state. + let quote_volume_u128 = (quote_volume as u128); + current_melee_ref_mut.melee_n_swaps_increment(); + current_melee_ref_mut.melee_swaps_volume_increment(quote_volume_u128); current_melee_ref_mut.melee_all_entrants_add_if_not_contains(entrant_address); current_melee_ref_mut.melee_active_entrants_add_if_not_contains(entrant_address); current_melee_ref_mut.melee_exited_entrants_remove_if_contains(entrant_address); // Update registry state. + registry_ref_mut.registry_n_swaps_increment(); + registry_ref_mut.registry_swaps_volume_increment(quote_volume_u128); registry_ref_mut.registry_all_entrants_add_if_not_contains(entrant_address); // Update escrow state. + escrow_ref_mut.escrow_n_swaps_increment(); + escrow_ref_mut.escrow_swaps_volume_increment(quote_volume_u128); escrow_ref_mut.escrow_octas_entered_increment(input_amount_after_matching); // Update user melees state. let user_melees_ref_mut = &mut UserMelees[entrant_address]; - add_if_not_contains(&mut user_melees_ref_mut.entered_melee_ids, melee_id); - add_if_not_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - remove_if_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); + user_melees_ref_mut.user_melees_entered_melee_ids_add_if_not_contains(melee_id); + user_melees_ref_mut.user_melees_unexited_melee_ids_add_if_not_contains(melee_id); + user_melees_ref_mut.user_melees_exited_melee_ids_remove_if_contains(melee_id); } @@ -602,9 +616,6 @@ module arena::emojicoin_arena { // Update registry state. registry_ref_mut.registry_octas_matched_decrement(octas_matched); - - // Update escrow state. - escrow_ref_mut.escrow_octas_matched_reset(); } }; @@ -622,12 +633,12 @@ module arena::emojicoin_arena { // Update escrow state. escrow_ref_mut.escrow_octas_entered_reset(); + escrow_ref_mut.escrow_octas_matched_reset(); // Update user melees state. let user_melees_ref_mut = &mut UserMelees[participant_address]; - add_if_not_contains(&mut user_melees_ref_mut.exited_melee_ids, melee_id); - remove_if_contains(&mut user_melees_ref_mut.unexited_melee_ids, melee_id); - + user_melees_ref_mut.user_melees_exited_melee_ids_add_if_not_contains(melee_id); + user_melees_ref_mut.user_melees_unexited_melee_ids_remove_if_contains(melee_id); } inline fun get_n_registered_markets(): u64 { @@ -988,6 +999,36 @@ module arena::emojicoin_arena { quote_volume } + inline fun user_melees_entered_melee_ids_add_if_not_contains( + self: &mut UserMelees, melee_id: u64 + ) { + add_if_not_contains(&mut self.entered_melee_ids, melee_id); + } + + inline fun user_melees_exited_melee_ids_add_if_not_contains( + self: &mut UserMelees, melee_id: u64 + ) { + add_if_not_contains(&mut self.exited_melee_ids, melee_id); + } + + inline fun user_melees_exited_melee_ids_remove_if_contains( + self: &mut UserMelees, melee_id: u64 + ) { + remove_if_contains(&mut self.exited_melee_ids, melee_id); + } + + inline fun user_melees_unexited_melee_ids_add_if_not_contains( + self: &mut UserMelees, melee_id: u64 + ) { + add_if_not_contains(&mut self.unexited_melee_ids, melee_id); + } + + inline fun user_melees_unexited_melee_ids_remove_if_contains( + self: &mut UserMelees, melee_id: u64 + ) { + remove_if_contains(&mut self.unexited_melee_ids, melee_id); + } + inline fun withdraw_from_escrow( recipient: address, escrow_coin_ref_mut: &mut Coin ) { From cf2e7e8151c576d633927d0da26c38472ef03e7f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:37:49 -0800 Subject: [PATCH 63/64] Update src/move/emojicoin_arena/sources/emojicoin_arena.move Co-authored-by: Matt <90358481+xbtmatt@users.noreply.github.com> --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index dd08a89d9..fc5befd2a 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -804,7 +804,8 @@ module arena::emojicoin_arena { } - /// Pseudo-random proxy for `random_market_id` for use during `init_module`. + /// Pseudo-random substitute for `random_market_id`, since the Aptos randomness API + /// is not available during `init_module`. inline fun pseudo_random_market_id(n_markets: u64): u64 { pseudo_randomness::u64_range(0, n_markets) + 1 } From d7d8e9241ff89bef35df5dc7d16c3e3621a368e7 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:38:53 -0800 Subject: [PATCH 64/64] Update line breaking --- src/move/emojicoin_arena/sources/emojicoin_arena.move | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/move/emojicoin_arena/sources/emojicoin_arena.move b/src/move/emojicoin_arena/sources/emojicoin_arena.move index fc5befd2a..60080ac17 100644 --- a/src/move/emojicoin_arena/sources/emojicoin_arena.move +++ b/src/move/emojicoin_arena/sources/emojicoin_arena.move @@ -804,8 +804,8 @@ module arena::emojicoin_arena { } - /// Pseudo-random substitute for `random_market_id`, since the Aptos randomness API - /// is not available during `init_module`. + /// Pseudo-random substitute for `random_market_id`, since the Aptos randomness API is not + /// available during `init_module`. inline fun pseudo_random_market_id(n_markets: u64): u64 { pseudo_randomness::u64_range(0, n_markets) + 1 }