diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/Move.toml b/Move.toml index 3d402f3..16b0bc7 100644 --- a/Move.toml +++ b/Move.toml @@ -7,4 +7,4 @@ rev = 'a58060f88d66b90a0548a506e22270b04a328db3' subdir = 'aptos-move/framework/aptos-framework' [addresses] -cetus_amm = "0x1" +cetus_amm = "_" diff --git a/sources/amm_config.move b/sources/amm_config.move index dc1c9af..c121990 100644 --- a/sources/amm_config.move +++ b/sources/amm_config.move @@ -7,6 +7,7 @@ module cetus_amm::config { // const ECONFIG_NOT_HAS_PRIVILEGE: u64 = 1001; + const ECONFIG_POOL_PAUSE: u64 = 1002; struct PoolFeeConfig has key { trade_fee_numerator: u64, @@ -84,5 +85,11 @@ module cetus_amm::config { error::invalid_argument(ECONFIG_NOT_HAS_PRIVILEGE)); } + public fun assert_pause() acquires PoolPauseStatus { + assert!( + !get_pool_pause(), + error::invalid_argument(ECONFIG_POOL_PAUSE)); + } + } \ No newline at end of file diff --git a/sources/amm_math.move b/sources/amm_math.move index 995e2a5..2faf480 100644 --- a/sources/amm_math.move +++ b/sources/amm_math.move @@ -11,7 +11,7 @@ module cetus_amm::amm_math { public fun safe_compare_mul_u128(a1: u128, b1: u128, a2: u128, b2: u128): u8 { let left = u256::mul(u256::from_u128(a1), u256::from_u128(b1)); let right = u256::mul(u256::from_u128(a2), u256::from_u128(b2)); - u256::compare(left, right) + u256::compare(&left, &right) } public fun safe_mul_div_u128(x: u128, y: u128, z: u128): u128 { diff --git a/sources/amm_script.move b/sources/amm_script.move index 2de9ae5..7744d66 100644 --- a/sources/amm_script.move +++ b/sources/amm_script.move @@ -2,12 +2,27 @@ module cetus_amm::amm_script { use cetus_amm::config; use cetus_amm::amm_swap; - public entry fun init_fee_config() { - + public entry fun set_pool_fee_config( + account: signer, + trade_fee_numerator: u64, + trade_fee_denominator: u64, + protocol_fee_numerator: u64, + protocol_fee_denominator: u64 + ) { + config::set_pool_fee_config( + &account, + trade_fee_numerator, + trade_fee_denominator, + protocol_fee_numerator, + protocol_fee_denominator, + ) } - public entry fun register_pool(account: signer) { - + public entry fun init_pool(account: signer, protocol_fee_to: address) { + amm_swap::init_pool ( + &account, + protocol_fee_to, + ) } /// Add liquidity for user @@ -38,23 +53,31 @@ module cetus_amm::amm_script { amount_b_min); } - public entry fun swap_exact_token_for_token( + public entry fun swap_exact_coin_for_coin( signer: signer, amount_a_in: u128, amount_b_out_min: u128, ) { - + amm_swap::swap_exact_coin_for_coin ( + &signer, + amount_a_in, + amount_b_out_min, + ) } - public entry fun swap_token_for_exact_token( + public entry fun swap_coin_for_exact_coin( signer: signer, amount_a_in_max: u128, amount_b_out: u128, ) { - + amm_swap::swap_coin_for_exact_coin ( + &signer, + amount_a_in_max, + amount_b_out, + ) } - public entry fun set_pause_status(status:bool) { - + public entry fun set_pause_status(account: signer, pause:bool) { + config::set_pool_pause(&account, pause) } } \ No newline at end of file diff --git a/sources/amm_swap.move b/sources/amm_swap.move index 3f9191f..bb83888 100644 --- a/sources/amm_swap.move +++ b/sources/amm_swap.move @@ -9,9 +9,10 @@ module cetus_amm::amm_swap { use aptos_framework::coin::{Self, Coin, BurnCapability, MintCapability}; use aptos_framework::coins; use aptos_std::comparator; - use cetus_amm::utils::{Self, assert_is_coin, compare_coin}; + use cetus_amm::utils::{Self, assert_is_coin, compare_coin, get_amount_out, get_amount_in}; use cetus_amm::config::{Self, assert_admin}; use cetus_amm::amm_math::{Self, quote, sqrt, min}; + use aptos_std::type_info; const MINIMUM_LIQUIDITY: u64 = 1000; @@ -28,6 +29,15 @@ module cetus_amm::amm_swap { const ERROR_LIQUIDITY_INSUFFICIENT_MINTED: u64 = 4006; const ERROR_LIQUIDITY_ADD_LIQUIDITY_FAILED: u64 = 4007; const ERROR_LIQUIDITY_SWAP_BURN_CALC_INVALID: u64 = 4008; + const ESWAP_COIN_INSUFFICIENT: u64 = 4009; + const ESWAP_SWAPOUT_CALC_INVALID: u64 = 4010; + const ESWAP_B_OUT_LESSTHAN_EXPECTED: u64 = 4011; + const ESWAP_INVALID_COIN_PAIR: u64 = 4012; + const ESWAP_A_IN_OVER_LIMIT_MAX: u64 = 4013; + + const EQUAL: u8 = 0; + const LESS_THAN: u8 = 1; + const GREATER_THAN: u8 = 2; struct PoolLiquidityCoin {} @@ -50,7 +60,10 @@ module cetus_amm::amm_swap { struct InitPoolEvent has store, drop { - + coin_a_info: type_info::TypeInfo, + coin_b_info: type_info::TypeInfo, + account: address, + protocol_fee_to: address, } struct AddLiquidityEvent has store, drop { @@ -70,7 +83,19 @@ module cetus_amm::amm_swap { } struct SwapEvent has store, drop { + coin_a_info: type_info::TypeInfo, + coin_b_info: type_info::TypeInfo, + account: address, + a_in: u128, + b_out: u128, + } + struct SwapFeeEvent has store, drop { + coin_a_info: type_info::TypeInfo, + coin_b_info: type_info::TypeInfo, + account: address, + fee_address: address, + fee_out: u128, } struct PoolSwapEventHandle has key { @@ -78,9 +103,10 @@ module cetus_amm::amm_swap { add_liquidity_events: EventHandle, remove_liquidity_events: EventHandle, swap_events: EventHandle, + swap_fee_events: EventHandle, } - public fun init_pool(account: &signer, protocol_fee_to: address) { + public fun init_pool(account: &signer, protocol_fee_to: address) acquires PoolSwapEventHandle { //check coin type assert_is_coin(); assert_is_coin(); @@ -98,9 +124,18 @@ module cetus_amm::amm_swap { comparator::is_smaller_than(&compare_coin()), error::invalid_argument(ESWAP_COINS_COMPARE_NOT_EQUIP_SMALLER)); + //reigister lp coin let(burn_capability, mint_capability) = register_liquidity_coin(account); - move_to(account,LiquidityTokenCapability{mint:mint_capability,burn:burn_capability}); + //make pool + let pool = make_pool(protocol_fee_to, burn_capability, mint_capability); + move_to(account, pool); + + //init event handle + init_event_handle(account); + + //emit init pool event + emit_init_pool_event(account, protocol_fee_to); } public fun add_liquidity( @@ -260,20 +295,132 @@ module cetus_amm::amm_swap { (coin::extract(&mut pool.coin_a , amount0), coin::extract(&mut pool.coin_b, amount1)) } - public fun swap_exact_tokens_for_Tokens( + public fun swap_and_emit_event( + account: &signer, + coin_a_in: Coin, + coin_b_out: u128, + coin_b_in: Coin, + coin_a_out: u128 + ) :(Coin, Coin, Coin, Coin) acquires Pool, PoolSwapEventHandle { + let (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = swap(coin_a_in, coin_b_out, coin_b_in, coin_a_out); + let event_handle = borrow_global_mut(config::admin_address()); + event::emit_event( + &mut event_handle.swap_events, + SwapEvent { + coin_a_info: type_info::type_of(), + coin_b_info: type_info::type_of(), + account: signer::address_of(account), + a_in: (coin::value(&coin_a_out) as u128), + b_out: (coin::value(&coin_b_out) as u128) + } + ); + (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) + } + + public fun swap( + coin_a_in: Coin, + coin_b_out: u128, + coin_b_in: Coin, + coin_a_out: u128, + ): (Coin, Coin, Coin, Coin) acquires Pool{ + config::assert_pause(); + + let a_in_value = coin::value(&coin_a_in); + let b_in_value = coin::value(&coin_b_in); + assert!( + a_in_value > 0 || b_in_value > 0, + error::invalid_argument(ESWAP_COIN_INSUFFICIENT)); + + let (a_reserve, b_reserve) = get_reserves(); + let pool = borrow_global_mut>(config::admin_address()); + coin::merge(&mut pool.coin_a, coin_a_in); + coin::merge(&mut pool.coin_b, coin_b_in); + + let coin_a_swapped = coin::extract(&mut pool.coin_a, (coin_a_out as u64)); + let coin_b_swapped = coin::extract(&mut pool.coin_b, (coin_b_out as u64)); + { + let a_reserve_new = coin::value(&pool.coin_a); + let b_reserve_new = coin::value(&pool.coin_b); + let (fee_numerator, fee_denominator) = config::get_trade_fee(); + + let a_adjusted = (a_reserve_new as u128) * (fee_denominator as u128) - (a_in_value as u128) * (fee_numerator as u128); + let b_adjusted = (b_reserve_new as u128) * (fee_denominator as u128) - (b_in_value as u128) * (fee_numerator as u128); + + let cmp_order = amm_math::safe_compare_mul_u128(a_adjusted, b_adjusted, (a_reserve as u128), (b_reserve as u128)); + assert!( + (EQUAL == cmp_order || GREATER_THAN == cmp_order), + error::invalid_argument(ESWAP_SWAPOUT_CALC_INVALID)); + }; + + let (protocol_fee_numberator, protocol_fee_denominator) = calc_swap_protocol_fee_rate(); + let a_swap_fee = coin::extract(&mut pool.coin_a, (amm_math::safe_mul_div_u128((a_in_value as u128), protocol_fee_numberator, protocol_fee_denominator) as u64)); + let b_swap_fee = coin::extract(&mut pool.coin_b, (amm_math::safe_mul_div_u128((b_in_value as u128), protocol_fee_numberator, protocol_fee_denominator) as u64)); + + (coin_a_swapped, coin_b_swapped, a_swap_fee, b_swap_fee) + } + + public fun swap_exact_coin_for_coin( account: &signer, amount_a_in: u128, amount_b_out_min: u128, - ) { + ) acquires Pool, PoolSwapEventHandle { + assert!( + !comparator::is_equal(&compare_coin()), + error::invalid_argument(ESWAP_INVALID_COIN_PAIR)); + let sender = signer::address_of(account); + if (!coin::is_account_registered(sender)) coins::register_internal(account); + + let b_out = compute_b_out(amount_a_in); + assert!(b_out >= amount_b_out_min, + error::invalid_argument(ESWAP_B_OUT_LESSTHAN_EXPECTED)); + + let coin_a = coin::withdraw(account, (amount_a_in as u64)); + let (coin_a_out, coin_b_out); + let (coin_a_fee, coin_b_fee); + if (comparator::is_smaller_than(&compare_coin())) { + (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = swap_and_emit_event(account, coin_a, b_out, coin::zero(), 0); + } else { + (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = swap_and_emit_event(account, coin::zero(), 0, coin_a, b_out); + }; + + coin::destroy_zero(coin_a_out); + coin::deposit(sender, coin_b_out); + coin::destroy_zero(coin_b_fee); + + handle_swap_protocol_fee(sender, coin_a_fee); } - public fun swap_tokens_for_exact_tokens( + public fun swap_coin_for_exact_coin( account: &signer, amount_a_in_max: u128, amount_b_out: u128 - ) { + ) acquires Pool, PoolSwapEventHandle { + assert!( + !comparator::is_equal(&compare_coin()), + error::invalid_argument(ESWAP_INVALID_COIN_PAIR)); + + let sender = signer::address_of(account); + if (!coin::is_account_registered(sender)) coins::register_internal(account); + + let a_in = compute_a_in(amount_b_out); + assert!(a_in <= amount_a_in_max, + error::invalid_argument(ESWAP_A_IN_OVER_LIMIT_MAX)); + + let coin_a = coin::withdraw(account, (a_in as u64)); + let (coin_a_out, coin_b_out); + let (coin_a_fee, coin_b_fee); + if (comparator::is_smaller_than(&compare_coin())) { + (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = swap_and_emit_event(account, coin_a, amount_b_out, coin::zero(), 0); + } else { + (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = swap_and_emit_event(account, coin::zero(), 0, coin_a, amount_b_out); + }; + coin::destroy_zero(coin_a_out); + coin::deposit(sender, coin_b_out); + coin::destroy_zero(coin_b_fee); + + handle_swap_protocol_fee(sender, coin_a_fee); } fun make_pool( @@ -306,6 +453,34 @@ module cetus_amm::amm_swap { (burn_capability, mint_capability) } + fun init_event_handle(account: &signer) { + if (!exists(signer::address_of(account))) { + move_to(account, PoolSwapEventHandle { + init_pool_events: event::new_event_handle(account), + add_liquidity_events: event::new_event_handle(account), + remove_liquidity_events: event::new_event_handle(account), + swap_events: event::new_event_handle(account), + swap_fee_events: event::new_event_handle(account), + }); + } + } + + fun emit_init_pool_event( + account: &signer, + protocol_fee_to: address + ) acquires PoolSwapEventHandle { + let event_handle = borrow_global_mut(config::admin_address()); + event::emit_event( + &mut event_handle.init_pool_events, + InitPoolEvent { + coin_a_info: type_info::type_of(), + coin_b_info: type_info::type_of(), + account: signer::address_of(account), + protocol_fee_to: protocol_fee_to, + } + ); + } + public fun get_reserves(): (u128, u128) acquires Pool { let pool = borrow_global>(config::admin_address()); let a_reserve = (coin::value(&pool.coin_a) as u128); @@ -313,4 +488,94 @@ module cetus_amm::amm_swap { (a_reserve, b_reserve) } + public fun calc_swap_protocol_fee_rate() : (u128, u128) { + let (fee_numerator, fee_denominator) = config::get_trade_fee(); + let (protocol_fee_numberator, protocol_fee_denominator) = config::get_protocol_fee(); + ((fee_numerator * protocol_fee_numberator as u128), (fee_denominator * protocol_fee_denominator as u128)) + } + + public fun compute_b_out(amount_a_in: u128): u128 acquires Pool{ + let (fee_numerator, fee_denominator) = config::get_trade_fee(); + let (reserve_a, reserve_b) = get_reserves(); + get_amount_out(amount_a_in, (reserve_a as u128), (reserve_b as u128), fee_numerator, fee_denominator) + } + + public fun compute_a_in(amount_b_out: u128): u128 acquires Pool { + let (reserve_a, reserve_b) = get_reserves(); + let (fee_numerator, fee_denominator) = config::get_trade_fee(); + get_amount_in(amount_b_out, reserve_a, reserve_b, fee_numerator, fee_denominator) + } + + public fun handle_swap_protocol_fee(signer_address: address, token_a: Coin) acquires PoolSwapEventHandle, Pool { + let pool = borrow_global>(config::admin_address()); + intra_handle_swap_protocol_fee(signer_address, pool.protocol_fee_to, token_a); + } + + fun intra_handle_swap_protocol_fee( + signer_address: address, + fee_address: address, + coin_a: Coin + ) acquires PoolSwapEventHandle, Pool { + let (fee_handle, fee_out) = swap_fee_direct_deposit(fee_address, coin_a); + if (fee_handle) { + assert!( + !comparator::is_equal(&compare_coin()), + error::invalid_argument(ESWAP_INVALID_COIN_PAIR)); + + if (comparator::is_smaller_than(&compare_coin())) { + emit_swap_fee_event(signer_address, fee_address, fee_out); + } else { + emit_swap_fee_event(signer_address, fee_address, fee_out); + }; + } + + } + + fun swap_fee_direct_deposit( + fee_address: address, + coin_a: Coin): (bool, u128) acquires Pool { + if (!coin::is_account_registered(fee_address)) { + let a_value = coin::value(&coin_a); + coin::deposit(fee_address, coin_a); + return (true, (a_value as u128)) + } else { + assert!( + !comparator::is_equal(&compare_coin()), + error::invalid_argument(ESWAP_INVALID_COIN_PAIR)); + + if (comparator::is_smaller_than(&compare_coin())) { + return_back_to_lp_pool(coin_a, coin::zero()); + } else { + return_back_to_lp_pool(coin::zero(), coin_a); + }; + }; + (true, (0 as u128)) + } + + fun return_back_to_lp_pool( + a_in: coin::Coin, + b_in: coin::Coin, + ) acquires Pool { + let pool = borrow_global_mut>(config::admin_address()); + coin::merge(&mut pool.coin_a, a_in); + coin::merge(&mut pool.coin_b, b_in); + } + + fun emit_swap_fee_event ( + signer_address: address, + fee_address: address, + fee_out: u128 + ) acquires PoolSwapEventHandle { + let event_handle = borrow_global_mut(config::admin_address()); + event::emit_event( + &mut event_handle.swap_fee_events, + SwapFeeEvent { + coin_a_info: type_info::type_of(), + coin_b_info: type_info::type_of(), + account: signer_address, + fee_address: fee_address, + fee_out: fee_out, + } + ); + } } \ No newline at end of file diff --git a/sources/amm_utils.move b/sources/amm_utils.move index f1bd2de..96a0024 100644 --- a/sources/amm_utils.move +++ b/sources/amm_utils.move @@ -3,15 +3,16 @@ module cetus_amm::utils { use aptos_framework::coin; use aptos_std::type_info; use aptos_std::comparator; - + use cetus_amm::amm_math::{Self, safe_mul_div_u128}; // // Errors // - const EUTIL_SWAP_COIN_NOT_EXISTS: u64 = 5001; + const EUTILS_SWAP_COIN_NOT_EXISTS: u64 = 5001; + const EUTILS_PARAMETER_INVALID: u64 = 5002; public fun assert_is_coin() : bool { - assert!(coin::is_coin_initialized(), error::invalid_argument(EUTIL_SWAP_COIN_NOT_EXISTS)); + assert!(coin::is_coin_initialized(), error::invalid_argument(EUTILS_SWAP_COIN_NOT_EXISTS)); true } @@ -21,4 +22,38 @@ module cetus_amm::utils { comparator::compare(&type_info_a, &type_info_b) } + + public fun get_amount_in( + amount_out: u128, + reserve_in: u128, + reserve_out: u128, + fee_numerator: u64, + fee_denumerator: u64): u128 { + assert!(amount_out > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(reserve_in > 0 && reserve_out > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(fee_denumerator > 0 && fee_numerator > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(fee_denumerator > fee_numerator, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(reserve_out > amount_out, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + + let denominator = (reserve_out - amount_out) * ((fee_denumerator - fee_numerator) as u128); + safe_mul_div_u128(amount_out * (fee_denumerator as u128), reserve_in, denominator) + 1 + } + + public fun get_amount_out( + amount_in: u128, + reserve_in: u128, + reserve_out: u128, + fee_numerator: u64, + fee_denumerator: u64 + ): u128 { + assert!(amount_in > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(reserve_in > 0 && reserve_out > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + + assert!(fee_denumerator > 0 && fee_numerator > 0, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + assert!(fee_denumerator > fee_numerator, error::invalid_argument(EUTILS_PARAMETER_INVALID)); + + let amount_in_with_fee = amount_in * ((fee_denumerator - fee_numerator) as u128); + let denominator = reserve_in * (fee_denumerator as u128) + amount_in_with_fee; + safe_mul_div_u128(amount_in_with_fee, reserve_out, denominator) + } } \ No newline at end of file diff --git a/sources/u256.move b/sources/u256.move new file mode 100644 index 0000000..786f610 --- /dev/null +++ b/sources/u256.move @@ -0,0 +1,1130 @@ + +// SPDX-License-Identifier: Apache-2.0 +// Copied from: https://github.com/pontem-network/U256/blob/main/sources/u256.move + +/// @title u256 +/// @dev The implementation of large numbers written in Move language. +/// Code derived from original work by Andrew Poelstra +/// +/// Rust Bitcoin Library +/// Written in 2014 by +/// Andrew Poelstra +/// +/// To the extent possible under law, the author(s) have dedicated all +/// copyright and related and neighboring rights to this software to +/// the public domain worldwide. This software is distributed without +/// any warranty. +/// +/// Simplified impl by Parity Team - https://github.com/paritytech/parity-common/blob/master/uint/src/uint.rs +/// +/// Features: +/// * mul +/// * div +/// * add +/// * sub +/// * shift left +/// * shift right +/// * compare +/// * if math overflows the contract crashes. +/// +/// Would be nice to help with the following TODO list: +/// * pow() , sqrt(). +/// * math funcs that don't abort on overflows, but just returns reminders. +/// * Export of low_u128 (see original implementation). +/// * Export of low_u64 (see original implementation). +/// * Gas Optimisation: +/// * We can optimize by replacing bytecode, as far as we know Move VM itself support slices, so probably +/// we can try to replace parts works with (`v0`,`v1`,`v2`,`v3` etc) works. +/// * More? +/// * More tests (see current tests and TODOs i left): +/// * u256_arithmetic_test - https://github.com/paritytech/bigint/blob/master/src/uint.rs#L1338 +/// * More from - https://github.com/paritytech/bigint/blob/master/src/uint.rs +/// * Division: +/// * Could be improved with div_mod_small (current version probably would took a lot of resources for small numbers). +/// * Also could be improved with Knuth, TAOCP, Volume 2, section 4.3.1, Algorithm D (see link to Parity above). +module cetus_amm::u256 { + use std::bcs; + use std::vector; + + // Errors. + /// When can't cast `U256` to `u128` (e.g. number too large). + const ECAST_OVERFLOW: u64 = 0; + + /// When trying to get or put word into U256 but it's out of index. + const EWORDS_OVERFLOW: u64 = 1; + + /// When math overflows. + const EOVERFLOW: u64 = 2; + + /// When attempted to divide by zero. + const EDIV_BY_ZERO: u64 = 3; + + /// When trying to call `from_bytes` on a vector of length != 32. + const EVECTOR_LENGTH_NOT_32_BYTES: u64 = 4; + + // Constants. + + /// Max `u64` value. + const U64_MAX: u128 = 18446744073709551615; + + /// Max `u128` value. + const U128_MAX: u128 = 340282366920938463463374607431768211455; + + /// Total words in `U256` (64 * 4 = 256). + const WORDS: u64 = 4; + + /// When both `U256` equal. + const EQUAL: u8 = 0; + + /// When `a` is less than `b`. + const LESS_THAN: u8 = 1; + + /// When `b` is greater than `b`. + const GREATER_THAN: u8 = 2; + + // Data structs. + + /// The `U256` resource. + /// Contains 4 u64 numbers. + struct U256 has copy, drop, store { + v0: u64, + v1: u64, + v2: u64, + v3: u64, + } + + /// Double `U256` used for multiple (to store overflow). + struct DU256 has copy, drop, store { + v0: u64, + v1: u64, + v2: u64, + v3: u64, + v4: u64, + v5: u64, + v6: u64, + v7: u64, + } + + // Public functions. + /// Adds two `U256` and returns sum. + public fun add(a: U256, b: U256): U256 { + let ret = zero(); + let carry = 0u64; + + let i = 0; + while (i < WORDS) { + let a1 = get(&a, i); + let b1 = get(&b, i); + + if (carry != 0) { + let (res1, is_overflow1) = overflowing_add(a1, b1); + let (res2, is_overflow2) = overflowing_add(res1, carry); + put(&mut ret, i, res2); + + carry = 0; + if (is_overflow1) { + carry = carry + 1; + }; + + if (is_overflow2) { + carry = carry + 1; + } + } else { + let (res, is_overflow) = overflowing_add(a1, b1); + put(&mut ret, i, res); + + carry = 0; + if (is_overflow) { + carry = 1; + }; + }; + + i = i + 1; + }; + + assert!(carry == 0, EOVERFLOW); + + ret + } + + /// Convert `U256` to `u128` value if possible (otherwise it aborts). + public fun as_u128(a: U256): u128 { + assert!(a.v2 == 0 && a.v3 == 0, ECAST_OVERFLOW); + ((a.v1 as u128) << 64) + (a.v0 as u128) + } + + /// Convert `U256` to `u64` value if possible (otherwise it aborts). + public fun as_u64(a: U256): u64 { + assert!(a.v1 == 0 && a.v2 == 0 && a.v3 == 0, ECAST_OVERFLOW); + a.v0 + } + + /// Compares two `U256` numbers. + public fun compare(a: &U256, b: &U256): u8 { + let i = WORDS; + while (i > 0) { + i = i - 1; + let a1 = get(a, i); + let b1 = get(b, i); + + if (a1 != b1) { + if (a1 < b1) { + return LESS_THAN + } else { + return GREATER_THAN + } + } + }; + + EQUAL + } + + /// Returns a `U256` from `u64` value. + public fun from_u64(val: u64): U256 { + from_u128((val as u128)) + } + + /// Returns a `U256` from `u128` value. + public fun from_u128(val: u128): U256 { + let (a2, a1) = split_u128(val); + + U256 { + v0: a1, + v1: a2, + v2: 0, + v3: 0, + } + } + + /// Multiples two `U256`. + public fun mul(a: U256, b: U256): U256 { + let ret = DU256 { + v0: 0, + v1: 0, + v2: 0, + v3: 0, + v4: 0, + v5: 0, + v6: 0, + v7: 0, + }; + + let i = 0; + while (i < WORDS) { + let carry = 0u64; + let b1 = get(&b, i); + + let j = 0; + while (j < WORDS) { + let a1 = get(&a, j); + + if (a1 != 0 || carry != 0) { + let (hi, low) = split_u128((a1 as u128) * (b1 as u128)); + + let overflow = { + let existing_low = get_d(&ret, i + j); + let (low, o) = overflowing_add(low, existing_low); + put_d(&mut ret, i + j, low); + if (o) { + 1 + } else { + 0 + } + }; + + carry = { + let existing_hi = get_d(&ret, i + j + 1); + let hi = hi + overflow; + let (hi, o0) = overflowing_add(hi, carry); + let (hi, o1) = overflowing_add(hi, existing_hi); + put_d(&mut ret, i + j + 1, hi); + + if (o0 || o1) { + 1 + } else { + 0 + } + }; + }; + + j = j + 1; + }; + + i = i + 1; + }; + + let (r, overflow) = du256_to_u256(ret); + assert!(!overflow, EOVERFLOW); + r + } + + /// Subtracts two `U256`, returns result. + public fun sub(a: U256, b: U256): U256 { + let ret = zero(); + + let carry = 0u64; + + let i = 0; + while (i < WORDS) { + let a1 = get(&a, i); + let b1 = get(&b, i); + + if (carry != 0) { + let (res1, is_overflow1) = overflowing_sub(a1, b1); + let (res2, is_overflow2) = overflowing_sub(res1, carry); + put(&mut ret, i, res2); + + carry = 0; + if (is_overflow1) { + carry = carry + 1; + }; + + if (is_overflow2) { + carry = carry + 1; + } + } else { + let (res, is_overflow) = overflowing_sub(a1, b1); + put(&mut ret, i, res); + + carry = 0; + if (is_overflow) { + carry = 1; + }; + }; + + i = i + 1; + }; + + assert!(carry == 0, EOVERFLOW); + ret + } + + /// Divide `a` by `b`. + public fun div(a: U256, b: U256): U256 { + let ret = zero(); + + let a_bits = bits(&a); + let b_bits = bits(&b); + + assert!(b_bits != 0, EDIV_BY_ZERO); // DIVIDE BY ZERO. + if (a_bits < b_bits) { + // Immidiatelly return. + return ret + }; + + let shift = a_bits - b_bits; + b = shl(b, (shift as u8)); + + loop { + let cmp = compare(&a, &b); + if (cmp == GREATER_THAN || cmp == EQUAL) { + let index = shift / 64; + let m = get(&ret, index); + let c = m | 1 << ((shift % 64) as u8); + put(&mut ret, index, c); + + a = sub(a, b); + }; + + b = shr(b, 1); + if (shift == 0) { + break + }; + + shift = shift - 1; + }; + + ret + } + + /// Shift right `a` by `shift`. + public fun shr(a: U256, shift: u8): U256 { + let ret = zero(); + + let word_shift = (shift as u64) / 64; + let bit_shift = (shift as u64) % 64; + + let i = word_shift; + while (i < WORDS) { + let m = get(&a, i) >> (bit_shift as u8); + put(&mut ret, i - word_shift, m); + i = i + 1; + }; + + if (bit_shift > 0) { + let j = word_shift + 1; + while (j < WORDS) { + let m = get(&ret, j - word_shift - 1) + (get(&a, j) << (64 - (bit_shift as u8))); + put(&mut ret, j - word_shift - 1, m); + j = j + 1; + }; + }; + + ret + } + + /// Shift left `a` by `shift`. + public fun shl(a: U256, shift: u8): U256 { + let ret = zero(); + + let word_shift = (shift as u64) / 64; + let bit_shift = (shift as u64) % 64; + + let i = word_shift; + while (i < WORDS) { + let m = get(&a, i - word_shift) << (bit_shift as u8); + put(&mut ret, i, m); + i = i + 1; + }; + + if (bit_shift > 0) { + let j = word_shift + 1; + + while (j < WORDS) { + let m = get(&ret, j) + (get(&a, j - 1 - word_shift) >> (64 - (bit_shift as u8))); + put(&mut ret, j, m); + j = j + 1; + }; + }; + + ret + } + + /// Returns `a` AND `b`. + public fun and(a: &U256, b: &U256): U256 { + let ret = zero(); + + let i = 0; + while (i < WORDS) { + let m = get(a, i) & get(b, i); + put(&mut ret, i, m); + i = i + 1; + }; + + ret + } + + /// Returns `a` OR `b`. + public fun or(a: &U256, b: &U256): U256 { + let ret = zero(); + + let i = 0; + while (i < WORDS) { + let m = get(a, i) | get(b, i); + put(&mut ret, i, m); + i = i + 1; + }; + + ret + } + + /// Returns `a` XOR `b`. + public fun xor(a: &U256, b: &U256): U256 { + let ret = zero(); + + let i = 0; + while (i < WORDS) { + let m = get(a, i) ^ get(b, i); + put(&mut ret, i, m); + i = i + 1; + }; + + ret + } + + /// Returns `U256` equals to zero. + public fun zero(): U256 { + U256 { + v0: 0, + v1: 0, + v2: 0, + v3: 0, + } + } + + // Private functions. + /// Get bits used to store `a`. + fun bits(a: &U256): u64 { + let i = 1; + while (i < WORDS) { + let a1 = get(a, WORDS - i); + if (a1 > 0) { + return ((0x40 * (WORDS - i + 1)) - (leading_zeros_u64(a1) as u64)) + }; + + i = i + 1; + }; + + let a1 = get(a, 0); + 0x40 - (leading_zeros_u64(a1) as u64) + } + + /// Get leading zeros of a binary representation of `a`. + fun leading_zeros_u64(a: u64): u8 { + if (a == 0) { + return 64 + }; + + let a1 = a & 0xFFFFFFFF; + let a2 = a >> 32; + + if (a2 == 0) { + let bit = 32; + + while (bit >= 1) { + let b = (a1 >> (bit-1)) & 1; + if (b != 0) { + break + }; + + bit = bit - 1; + }; + + (32 - bit) + 32 + } else { + let bit = 64; + while (bit >= 1) { + let b = (a >> (bit-1)) & 1; + if (b != 0) { + break + }; + bit = bit - 1; + }; + + 64 - bit + } + } + + /// Similar to Rust `overflowing_add`. + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would occur. + /// If an overflow would have occurred then the wrapped value is returned. + fun overflowing_add(a: u64, b: u64): (u64, bool) { + let a128 = (a as u128); + let b128 = (b as u128); + + let r = a128 + b128; + if (r > U64_MAX) { + // overflow + let overflow = r - U64_MAX - 1; + ((overflow as u64), true) + } else { + (((a128 + b128) as u64), false) + } + } + + /// Similar to Rust `overflowing_sub`. + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would occur. + /// If an overflow would have occurred then the wrapped value is returned. + fun overflowing_sub(a: u64, b: u64): (u64, bool) { + if (a < b) { + let r = b - a; + ((U64_MAX as u64) - r + 1, true) + } else { + (a - b, false) + } + } + + /// Extracts two `u64` from `a` `u128`. + fun split_u128(a: u128): (u64, u64) { + let a1 = ((a >> 64) as u64); + let a2 = ((a & 0xFFFFFFFFFFFFFFFF) as u64); + + (a1, a2) + } + + /// Get word from `a` by index `i`. + public fun get(a: &U256, i: u64): u64 { + if (i == 0) { + a.v0 + } else if (i == 1) { + a.v1 + } else if (i == 2) { + a.v2 + } else if (i == 3) { + a.v3 + } else { + abort EWORDS_OVERFLOW + } + } + + /// Get word from `DU256` by index. + fun get_d(a: &DU256, i: u64): u64 { + if (i == 0) { + a.v0 + } else if (i == 1) { + a.v1 + } else if (i == 2) { + a.v2 + } else if (i == 3) { + a.v3 + } else if (i == 4) { + a.v4 + } else if (i == 5) { + a.v5 + } else if (i == 6) { + a.v6 + } else if (i == 7) { + a.v7 + } else { + abort EWORDS_OVERFLOW + } + } + + /// Put new word `val` into `U256` by index `i`. + fun put(a: &mut U256, i: u64, val: u64) { + if (i == 0) { + a.v0 = val; + } else if (i == 1) { + a.v1 = val; + } else if (i == 2) { + a.v2 = val; + } else if (i == 3) { + a.v3 = val; + } else { + abort EWORDS_OVERFLOW + } + } + + /// Put new word into `DU256` by index `i`. + fun put_d(a: &mut DU256, i: u64, val: u64) { + if (i == 0) { + a.v0 = val; + } else if (i == 1) { + a.v1 = val; + } else if (i == 2) { + a.v2 = val; + } else if (i == 3) { + a.v3 = val; + } else if (i == 4) { + a.v4 = val; + } else if (i == 5) { + a.v5 = val; + } else if (i == 6) { + a.v6 = val; + } else if (i == 7) { + a.v7 = val; + } else { + abort EWORDS_OVERFLOW + } + } + + /// Convert `DU256` to `U256`. + fun du256_to_u256(a: DU256): (U256, bool) { + let b = U256 { + v0: a.v0, + v1: a.v1, + v2: a.v2, + v3: a.v3, + }; + + let overflow = false; + if (a.v4 != 0 || a.v5 != 0 || a.v6 != 0 || a.v7 != 0) { + overflow = true; + }; + + (b, overflow) + } + + /// Converts `vector` `a` to a `U256`. + public fun from_bytes(a: &vector): U256 { + assert!(vector::length(a) == 32, EVECTOR_LENGTH_NOT_32_BYTES); + let ret = zero(); + put(&mut ret, 0, ((*vector::borrow(a, 0) as u64) << 7) + ((*vector::borrow(a, 1) as u64) << 6) + + ((*vector::borrow(a, 2) as u64) << 5) + ((*vector::borrow(a, 3) as u64) << 4) + + ((*vector::borrow(a, 4) as u64) << 3) + ((*vector::borrow(a, 5) as u64) << 2) + + ((*vector::borrow(a, 6) as u64) << 1) + (*vector::borrow(a, 7) as u64)); + put(&mut ret, 1, ((*vector::borrow(a, 8) as u64) << 7) + ((*vector::borrow(a, 9) as u64) << 6) + + ((*vector::borrow(a, 10) as u64) << 5) + ((*vector::borrow(a, 11) as u64) << 4) + + ((*vector::borrow(a, 12) as u64) << 3) + ((*vector::borrow(a, 13) as u64) << 2) + + ((*vector::borrow(a, 14) as u64) << 1) + (*vector::borrow(a, 15) as u64)); + put(&mut ret, 2, ((*vector::borrow(a, 16) as u64) << 7) + ((*vector::borrow(a, 17) as u64) << 6) + + ((*vector::borrow(a, 18) as u64) << 5) + ((*vector::borrow(a, 19) as u64) << 4) + + ((*vector::borrow(a, 20) as u64) << 3) + ((*vector::borrow(a, 21) as u64) << 2) + + ((*vector::borrow(a, 22) as u64) << 1) + (*vector::borrow(a, 23) as u64)); + put(&mut ret, 3, ((*vector::borrow(a, 24) as u64) << 7) + ((*vector::borrow(a, 25) as u64) << 6) + + ((*vector::borrow(a, 26) as u64) << 5) + ((*vector::borrow(a, 27) as u64) << 4) + + ((*vector::borrow(a, 28) as u64) << 3) + ((*vector::borrow(a, 29) as u64) << 2) + + ((*vector::borrow(a, 30) as u64) << 1) + (*vector::borrow(a, 31) as u64)); + ret + } + + /// Converts `U256` `a` to a `vector`. + public fun to_bytes(a: &U256): vector { + let ret = vector::empty(); + vector::append(&mut ret, bcs::to_bytes(&get(a, 0))); + vector::append(&mut ret, bcs::to_bytes(&get(a, 1))); + vector::append(&mut ret, bcs::to_bytes(&get(a, 2))); + vector::append(&mut ret, bcs::to_bytes(&get(a, 3))); + ret + } + + // Tests. + #[test] + fun test_get_d() { + let a = DU256 { + v0: 1, + v1: 2, + v2: 3, + v3: 4, + v4: 5, + v5: 6, + v6: 7, + v7: 8, + }; + + assert!(get_d(&a, 0) == 1, 0); + assert!(get_d(&a, 1) == 2, 1); + assert!(get_d(&a, 2) == 3, 2); + assert!(get_d(&a, 3) == 4, 3); + assert!(get_d(&a, 4) == 5, 4); + assert!(get_d(&a, 5) == 6, 5); + assert!(get_d(&a, 6) == 7, 6); + assert!(get_d(&a, 7) == 8, 7); + } + + #[test] + #[expected_failure(abort_code = 1)] + fun test_get_d_overflow() { + let a = DU256 { + v0: 1, + v1: 2, + v2: 3, + v3: 4, + v4: 5, + v5: 6, + v6: 7, + v7: 8, + }; + + get_d(&a, 8); + } + + #[test] + fun test_put_d() { + let a = DU256 { + v0: 1, + v1: 2, + v2: 3, + v3: 4, + v4: 5, + v5: 6, + v6: 7, + v7: 8, + }; + + put_d(&mut a, 0, 10); + put_d(&mut a, 1, 20); + put_d(&mut a, 2, 30); + put_d(&mut a, 3, 40); + put_d(&mut a, 4, 50); + put_d(&mut a, 5, 60); + put_d(&mut a, 6, 70); + put_d(&mut a, 7, 80); + + assert!(get_d(&a, 0) == 10, 0); + assert!(get_d(&a, 1) == 20, 1); + assert!(get_d(&a, 2) == 30, 2); + assert!(get_d(&a, 3) == 40, 3); + assert!(get_d(&a, 4) == 50, 4); + assert!(get_d(&a, 5) == 60, 5); + assert!(get_d(&a, 6) == 70, 6); + assert!(get_d(&a, 7) == 80, 7); + } + + #[test] + #[expected_failure(abort_code = 1)] + fun test_put_d_overflow() { + let a = DU256 { + v0: 1, + v1: 2, + v2: 3, + v3: 4, + v4: 5, + v5: 6, + v6: 7, + v7: 8, + }; + + put_d(&mut a, 8, 0); + } + + #[test] + fun test_du256_to_u256() { + let a = DU256 { + v0: 255, + v1: 100, + v2: 50, + v3: 300, + v4: 0, + v5: 0, + v6: 0, + v7: 0, + }; + + let (m, overflow) = du256_to_u256(a); + assert!(!overflow, 0); + assert!(m.v0 == a.v0, 1); + assert!(m.v1 == a.v1, 2); + assert!(m.v2 == a.v2, 3); + assert!(m.v3 == a.v3, 4); + + a.v4 = 100; + a.v5 = 5; + + let (m, overflow) = du256_to_u256(a); + assert!(overflow, 5); + assert!(m.v0 == a.v0, 6); + assert!(m.v1 == a.v1, 7); + assert!(m.v2 == a.v2, 8); + assert!(m.v3 == a.v3, 9); + } + + #[test] + fun test_get() { + let a = U256 { + v0: 1, + v1: 2, + v2: 3, + v3: 4, + }; + + assert!(get(&a, 0) == 1, 0); + assert!(get(&a, 1) == 2, 1); + assert!(get(&a, 2) == 3, 2); + assert!(get(&a, 3) == 4, 3); + } + + #[test] + #[expected_failure(abort_code = 1)] + fun test_get_aborts() { + let _ = get(&zero(), 4); + } + + #[test] + fun test_put() { + let a = zero(); + put(&mut a, 0, 255); + assert!(get(&a, 0) == 255, 0); + + put(&mut a, 1, (U64_MAX as u64)); + assert!(get(&a, 1) == (U64_MAX as u64), 1); + + put(&mut a, 2, 100); + assert!(get(&a, 2) == 100, 2); + + put(&mut a, 3, 3); + assert!(get(&a, 3) == 3, 3); + + put(&mut a, 2, 0); + assert!(get(&a, 2) == 0, 4); + } + + #[test] + #[expected_failure(abort_code = 1)] + fun test_put_overflow() { + let a = zero(); + put(&mut a, 6, 255); + } + + #[test] + fun test_from_u128() { + let i = 0; + while (i < 1024) { + let big = from_u128(i); + assert!(as_u128(big) == i, 0); + i = i + 1; + }; + } + + #[test] + fun test_add() { + let a = from_u128(1000); + let b = from_u128(500); + + let s = as_u128(add(a, b)); + assert!(s == 1500, 0); + + a = from_u128(U64_MAX); + b = from_u128(U64_MAX); + + s = as_u128(add(a, b)); + assert!(s == (U64_MAX + U64_MAX), 1); + } + + #[test] + #[expected_failure(abort_code = 2)] + fun test_add_overflow() { + let max = (U64_MAX as u64); + + let a = U256 { + v0: max, + v1: max, + v2: max, + v3: max + }; + + let _ = add(a, from_u128(1)); + } + + #[test] + fun test_sub() { + let a = from_u128(1000); + let b = from_u128(500); + + let s = as_u128(sub(a, b)); + assert!(s == 500, 0); + } + + #[test] + #[expected_failure(abort_code = 2)] + fun test_sub_overflow() { + let a = from_u128(0); + let b = from_u128(1); + + let _ = sub(a, b); + } + + #[test] + #[expected_failure(abort_code = 0)] + fun test_too_big_to_cast_to_u128() { + let a = from_u128(U128_MAX); + let b = from_u128(U128_MAX); + + let _ = as_u128(add(a, b)); + } + + #[test] + fun test_overflowing_add() { + let (n, z) = overflowing_add(10, 10); + assert!(n == 20, 0); + assert!(!z, 1); + + (n, z) = overflowing_add((U64_MAX as u64), 1); + assert!(n == 0, 2); + assert!(z, 3); + + (n, z) = overflowing_add((U64_MAX as u64), 10); + assert!(n == 9, 4); + assert!(z, 5); + + (n, z) = overflowing_add(5, 8); + assert!(n == 13, 6); + assert!(!z, 7); + } + + #[test] + fun test_overflowing_sub() { + let (n, z) = overflowing_sub(10, 5); + assert!(n == 5, 0); + assert!(!z, 1); + + (n, z) = overflowing_sub(0, 1); + assert!(n == (U64_MAX as u64), 2); + assert!(z, 3); + + (n, z) = overflowing_sub(10, 10); + assert!(n == 0, 4); + assert!(!z, 5); + } + + #[test] + fun test_split_u128() { + let (a1, a2) = split_u128(100); + assert!(a1 == 0, 0); + assert!(a2 == 100, 1); + + (a1, a2) = split_u128(U64_MAX + 1); + assert!(a1 == 1, 2); + assert!(a2 == 0, 3); + } + + #[test] + fun test_mul() { + let a = from_u128(285); + let b = from_u128(375); + + let c = as_u128(mul(a, b)); + assert!(c == 106875, 0); + + a = from_u128(0); + b = from_u128(1); + + c = as_u128(mul(a, b)); + + assert!(c == 0, 1); + + a = from_u128(U64_MAX); + b = from_u128(2); + + c = as_u128(mul(a, b)); + + assert!(c == 36893488147419103230, 2); + + a = from_u128(U128_MAX); + b = from_u128(U128_MAX); + + let z = mul(a, b); + assert!(bits(&z) == 256, 3); + } + + #[test] + #[expected_failure(abort_code = 2)] + fun test_mul_overflow() { + let max = (U64_MAX as u64); + + let a = U256 { + v0: max, + v1: max, + v2: max, + v3: max, + }; + + let _ = mul(a, from_u128(2)); + } + + #[test] + fun test_zero() { + let a = as_u128(zero()); + assert!(a == 0, 0); + + let a = zero(); + assert!(a.v0 == 0, 1); + assert!(a.v1 == 0, 2); + assert!(a.v2 == 0, 3); + assert!(a.v3 == 0, 4); + } + + #[test] + fun test_from_u64() { + let a = as_u128(from_u64(100)); + assert!(a == 100, 0); + + // TODO: more tests. + } + + #[test] + fun test_compare() { + let a = from_u128(1000); + let b = from_u128(50); + + let cmp = compare(&a, &b); + assert!(cmp == 2, 0); + + a = from_u128(100); + b = from_u128(100); + cmp = compare(&a, &b); + + assert!(cmp == 0, 1); + + a = from_u128(50); + b = from_u128(75); + + cmp = compare(&a, &b); + assert!(cmp == 1, 2); + } + + #[test] + fun test_leading_zeros_u64() { + let a = leading_zeros_u64(0); + assert!(a == 64, 0); + + let a = leading_zeros_u64(1); + assert!(a == 63, 1); + + // TODO: more tests. + } + + #[test] + fun test_bits() { + let a = bits(&from_u128(0)); + assert!(a == 0, 0); + + a = bits(&from_u128(255)); + assert!(a == 8, 1); + + a = bits(&from_u128(256)); + assert!(a == 9, 2); + + a = bits(&from_u128(300)); + assert!(a == 9, 3); + + a = bits(&from_u128(60000)); + assert!(a == 16, 4); + + a = bits(&from_u128(70000)); + assert!(a == 17, 5); + + let b = from_u64(70000); + let sh = shl(b, 100); + assert!(bits(&sh) == 117, 6); + + let sh = shl(sh, 100); + assert!(bits(&sh) == 217, 7); + + let sh = shl(sh, 100); + assert!(bits(&sh) == 0, 8); + } + + #[test] + fun test_shift_left() { + let a = from_u128(100); + let b = shl(a, 2); + + assert!(as_u128(b) == 400, 0); + + // TODO: more shift left tests. + } + + #[test] + fun test_shift_right() { + let a = from_u128(100); + let b = shr(a, 2); + + assert!(as_u128(b) == 25, 0); + + // TODO: more shift right tests. + } + + #[test] + fun test_div() { + let a = from_u128(100); + let b = from_u128(5); + let d = div(a, b); + + assert!(as_u128(d) == 20, 0); + + let a = from_u128(U64_MAX); + let b = from_u128(U128_MAX); + let d = div(a, b); + assert!(as_u128(d) == 0, 1); + + let a = from_u128(U64_MAX); + let b = from_u128(U128_MAX); + let d = div(a, b); + assert!(as_u128(d) == 0, 2); + + let a = from_u128(U128_MAX); + let b = from_u128(U64_MAX); + let d = div(a, b); + assert!(as_u128(d) == 18446744073709551617, 2); + } + + #[test] + #[expected_failure(abort_code=3)] + fun test_div_by_zero() { + let a = from_u128(1); + let _z = div(a, from_u128(0)); + } + + #[test] + fun test_as_u64() { + let _ = as_u64(from_u64((U64_MAX as u64))); + let _ = as_u64(from_u128(1)); + } + + #[test] + #[expected_failure(abort_code=0)] + fun test_as_u64_overflow() { + let _ = as_u64(from_u128(U128_MAX)); + } +}