diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b1ba157fd..62f1b9a5c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -43,5 +43,7 @@ /examples/blog @wubuku @jolestar /sdk @wow-sven @yubing744 /infra/dashboards @wow-sven @yubing744 -/infra/rooch-portal @wow-sven @TwilightLogic @Mine77 +/infra/rooch-portal-v2 @wow-sven @Mine77 /docs @wow-sven @geometryolife @Mine77 + +/apps @mx819812523 @jolestar diff --git a/apps/grow_bitcoin/release/1/package.rpd b/apps/grow_bitcoin/release/1/package.rpd new file mode 100644 index 000000000..b97642302 Binary files /dev/null and b/apps/grow_bitcoin/release/1/package.rpd differ diff --git a/apps/grow_bitcoin/release/2/package.rpd b/apps/grow_bitcoin/release/2/package.rpd new file mode 100644 index 000000000..78f8498ac Binary files /dev/null and b/apps/grow_bitcoin/release/2/package.rpd differ diff --git a/apps/grow_bitcoin/sources/grow_bitcoin.move b/apps/grow_bitcoin/sources/grow_bitcoin.move index c56faef2a..90c8ac539 100644 --- a/apps/grow_bitcoin/sources/grow_bitcoin.move +++ b/apps/grow_bitcoin/sources/grow_bitcoin.move @@ -3,7 +3,7 @@ module grow_bitcoin::grow_bitcoin { - use std::string; + use std::string::{Self, String}; use std::option; use std::u64; use bitcoin_move::bbn; @@ -45,6 +45,12 @@ module grow_bitcoin::grow_bitcoin { // values above (or equal to) the threshold are interpreted as block times (UNIX timestamp, seconds since epoch). const LOCK_TIME_THRESHOLD: u32 = 500_000_000; + const TOTAL_GROW_SUPPLY: u128 = 2100_0000_0000; + + const GROW_NAME:vector = b"Grow Bitcoin"; + const GROW_SYMBOL:vector = b"GROW"; + const GROW_ICON_URL: vector = b""; + const ErrorWrongDeployer: u64 = 1; const ErrorAlreadyDeployed: u64 = 2; const ErrorWrongFarmTime: u64 = 3; @@ -59,10 +65,7 @@ module grow_bitcoin::grow_bitcoin { const ErrorNotStaked: u64 = 12; const ErrorAssetExist: u64 = 13; const ErrorBitcoinClientError: u64 = 14; - - spec module { - pragma verify = false; - } + const ErrorWrongTimeRange: u64 = 15; const EXP_SCALE: u128 = 1000000000000000000;// e18 @@ -114,6 +117,7 @@ module grow_bitcoin::grow_bitcoin { struct GROW has key, store {} struct FarmingAsset has key { + asset_total_value: u64, asset_total_weight: u64, harvest_index: u128, last_update_timestamp: u64, @@ -138,6 +142,7 @@ module grow_bitcoin::grow_bitcoin { /// To store user's asset token struct Stake has key, store { + asset_type: String, asset_weight: u64, last_harvest_index: u128, gain: u128, @@ -151,6 +156,7 @@ module grow_bitcoin::grow_bitcoin { struct StakeEvent has copy, drop { asset_id: ObjectID, + asset_type: String, asset_weight: u64, account: address, timestamp: u64 @@ -158,6 +164,7 @@ module grow_bitcoin::grow_bitcoin { struct UnStakeEvent has copy, drop { asset_id: ObjectID, + asset_type: String, asset_weight: u64, gain: u128, account: address, @@ -166,6 +173,7 @@ module grow_bitcoin::grow_bitcoin { struct HarvestEvent has copy, drop { asset_id: ObjectID, + asset_type: String, harvest_index: u128, gain: u128, account: address, @@ -174,6 +182,7 @@ module grow_bitcoin::grow_bitcoin { struct RemoveExpiredEvent has copy, drop { asset_id: ObjectID, + asset_type: String, account: address, } @@ -188,30 +197,35 @@ module grow_bitcoin::grow_bitcoin { account::move_resource_to(&grow_bitcoin_signer, SubscriberInfo { subscriber }); + + let start_time = timestamp::now_seconds(); + let duration_seconds = (PerDaySeconds as u64) *180u64; + let end_time = start_time + duration_seconds; + let release_per_second = TOTAL_GROW_SUPPLY / (duration_seconds as u128); + do_deploy(release_per_second, start_time, end_time); } - public entry fun deploy( - signer: &signer, + fun do_deploy( release_per_second: u128, start_time: u64, end_time: u64, - name: vector, - symbol: vector, - decimals: u8, - _cap: &mut Object ) { - assert!(signer::address_of(signer) == DEPLOYER, ErrorWrongDeployer); assert!(!account::exists_resource(DEPLOYER), ErrorAlreadyDeployed); + assert!(start_time < end_time, ErrorWrongTimeRange); let now_seconds = timestamp::now_seconds(); + assert!(start_time >= now_seconds, ErrorWrongTimeRange); + let coin_info = coin::register_extend( - string::utf8(name), - string::utf8(symbol), - option::none(), - decimals, + string::utf8(GROW_NAME), + string::utf8(GROW_SYMBOL), + option::some(string::utf8(GROW_ICON_URL)), + 0u8, ); - account::move_resource_to(signer, FarmingAsset { + let grow_bitcoin_signer = signer::module_signer(); + account::move_resource_to(&grow_bitcoin_signer, FarmingAsset { + asset_total_value: 0, asset_total_weight: 0, harvest_index: 0, last_update_timestamp: now_seconds, @@ -224,10 +238,15 @@ module grow_bitcoin::grow_bitcoin { }); } + public entry fun update_coin_icon(){ + let farming_asset = account::borrow_mut_resource(DEPLOYER); + coin::upsert_icon_url(&mut farming_asset.coin_info, string::utf8(GROW_ICON_URL)); + } + public fun modify_parameter( - _cap: &mut Object, release_per_second: u128, - alive: bool + alive: bool, + _cap: &mut Object, ) { // Not support to shuttingdown alive state. assert!(alive, ErrorNotAlive); @@ -257,8 +276,9 @@ module grow_bitcoin::grow_bitcoin { ) { assert!(!utxo::contains_temp_state(asset), ErrorAlreadyStaked); utxo::add_temp_state(asset, StakeInfo {}); - let asset_weight = value( object::borrow(asset)) * calculate_time_lock_weight(0); - do_stake(signer, object::id(asset), asset_weight); + let utxo_value = value( object::borrow(asset)); + let asset_weight = utxo_value * calculate_time_lock_weight(0); + do_stake(signer, asset, utxo_value, asset_weight); } public entry fun stake_bbn( @@ -268,17 +288,22 @@ module grow_bitcoin::grow_bitcoin { assert!(!bbn::contains_temp_state(asset), ErrorAlreadyStaked); bbn::add_temp_state(asset, StakeInfo {}); let bbn_stake_seal = object::borrow(asset); - let asset_weight = bbn::staking_value(bbn_stake_seal) * calculate_time_lock_weight( + let stake_value = bbn::staking_value(bbn_stake_seal); + let asset_weight = stake_value * calculate_time_lock_weight( (((bbn::staking_time(bbn_stake_seal) as u64) + bbn::block_height(bbn_stake_seal)) as u32) ); - do_stake(signer, object::id(asset), asset_weight); + do_stake(signer, asset, stake_value, asset_weight); } - fun do_stake( + fun do_stake( signer: &signer, - asset_id: ObjectID, + asset: &mut Object, + asset_value: u64, asset_weight: u64 ) { + let asset_id = object::id(asset); + let asset_type = type_info::type_name(); + process_expired_state(); let account = signer::address_of(signer); @@ -289,6 +314,7 @@ module grow_bitcoin::grow_bitcoin { let now_seconds = timestamp::now_seconds(); emit(StakeEvent{ asset_id, + asset_type, asset_weight, account, timestamp: now_seconds @@ -308,11 +334,13 @@ module grow_bitcoin::grow_bitcoin { }; let stake_table = account::borrow_mut_resource(account); table::add(&mut stake_table.stake, asset_id, Stake { + asset_type, asset_weight, last_harvest_index: 0, gain, }); farming_asset.harvest_index = 0; + farming_asset.asset_total_value = asset_value; farming_asset.asset_total_weight = asset_weight; } else { let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); @@ -323,10 +351,12 @@ module grow_bitcoin::grow_bitcoin { }; let stake_table = account::borrow_mut_resource(account); table::add(&mut stake_table.stake, asset_id, Stake { + asset_type, asset_weight, last_harvest_index: new_harvest_index, gain: 0, }); + farming_asset.asset_total_value = farming_asset.asset_total_value + asset_value; farming_asset.asset_total_weight = farming_asset.asset_total_weight + asset_weight; farming_asset.harvest_index = new_harvest_index; }; @@ -354,7 +384,7 @@ module grow_bitcoin::grow_bitcoin { let farming_asset = account::borrow_mut_resource(DEPLOYER); let user_stake = account::borrow_mut_resource(account); table::remove(&mut farming_asset.stake_table, asset_id); - let Stake { last_harvest_index, asset_weight, gain } = + let Stake { asset_type, last_harvest_index, asset_weight, gain } = table::remove(&mut user_stake.stake, asset_id); let now_seconds = if (timestamp::now_seconds() < farming_asset.end_time) { timestamp::now_seconds() @@ -368,6 +398,7 @@ module grow_bitcoin::grow_bitcoin { let withdraw_token = coin::mint_extend(&mut farming_asset.coin_info, (total_gain as u256)); emit(UnStakeEvent{ asset_id, + asset_type, asset_weight, gain: total_gain, account, @@ -421,6 +452,7 @@ module grow_bitcoin::grow_bitcoin { // let withdraw_amount = total_gain; emit(HarvestEvent{ asset_id, + asset_type: stake.asset_type, harvest_index: new_harvest_index, gain: total_gain, account, @@ -469,10 +501,10 @@ module grow_bitcoin::grow_bitcoin { stake.gain + new_gain } - /// Query total stake count from yield farming resource - public fun query_total_stake(): u64 { + /// Query total stake value(in satoshis) and weight from yield farming resource + public fun query_total_stake(): (u64, u64) { let farming_asset = account::borrow_resource(DEPLOYER); - farming_asset.asset_total_weight + (farming_asset.asset_total_value, farming_asset.asset_total_weight) } /// Query stake weight from user staking objects. @@ -630,12 +662,14 @@ module grow_bitcoin::grow_bitcoin { let account = table::remove(&mut farming_asset.stake_table, asset_id); let user_stake = account::borrow_mut_resource(account); let Stake { + asset_type, asset_weight, last_harvest_index: _, gain: _ } = table::remove(&mut user_stake.stake, asset_id); emit(RemoveExpiredEvent{ asset_id, - account + asset_type, + account, }); farming_asset.asset_total_weight = farming_asset.asset_total_weight - asset_weight; } @@ -645,10 +679,8 @@ module grow_bitcoin::grow_bitcoin { bitcoin_move::genesis::init_for_test(); add_latest_block(100, @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21); init(); - let admin_cap_id = object::named_object_id(); - let grow_bitcoin_signer = signer::module_signer(); - let admin_cap = object::borrow_mut_object(&grow_bitcoin_signer, admin_cap_id); - deploy(&sender, 1, 0, 200, b"BTC Holder Coin", b"HDC", 6, admin_cap); + let _admin_cap = app_admin::admin::init_for_test(); + //deploy(1, 0, 200, admin_cap); let seconds = 100; let tx_id = @0x77dfc2fe598419b00641c296181a96cf16943697f573480b023b77cce82ada21; let sat_value = 100000000; @@ -659,17 +691,22 @@ module grow_bitcoin::grow_bitcoin { stake(&sender, &mut utxo); timestamp::fast_forward_seconds_for_test(seconds); stake(&sender, &mut utxo2); + let (total_value, total_weight) = query_total_stake(); + assert!(total_value == 2 * sat_value, 1); + assert!(total_weight == 2 * sat_value * calculate_time_lock_weight(0), 2); timestamp::fast_forward_seconds_for_test(seconds); let amount = query_gov_token_amount(utxo_id); let amount2 = query_gov_token_amount(utxo_id2); - assert!(amount == 150, 1); - assert!(amount2 == 50, 2); + // std::debug::print(&amount); + // std::debug::print(&amount2); + assert!(amount == 2025450, 1); + assert!(amount2 == 675150, 2); let coin = do_unstake(&sender, object::id(&utxo)); - assert!(coin::value(&coin) == 150, 3); + assert!(coin::value(&coin) == (amount as u256), 3); let amount = query_gov_token_amount(utxo_id); assert!(amount == 0, 4); let coin2 = do_harvest(&sender, object::id(&utxo2)); - assert!(coin::value(&coin2) == 50, 4); + assert!(coin::value(&coin2) == (amount2 as u256), 5); coin::destroy_for_testing(coin2); utxo::drop_for_testing(utxo2); // remove_expired_stake(utxo_id2); diff --git a/crates/testsuite/features/portal.feature b/crates/testsuite/features/apps.feature similarity index 82% rename from crates/testsuite/features/portal.feature rename to crates/testsuite/features/apps.feature index 915413bf7..691540df3 100644 --- a/crates/testsuite/features/portal.feature +++ b/crates/testsuite/features/apps.feature @@ -1,8 +1,8 @@ -Feature: Rooch Portal contract tests +Feature: RoochDAO Apps contract tests @serial - Scenario: gas_market + Scenario: gas_market & gas_faucet Given a bitcoind server for gas_market Given a server for gas_market @@ -24,8 +24,6 @@ Feature: Rooch Portal contract tests Then assert: "{{$.move[-1].execution_info.status.type}} == executed" Then cmd: "move publish -p ../../apps/gas_faucet --named-addresses app_admin=default,gas_faucet=default --json" Then assert: "{{$.move[-1].execution_info.status.type}} == executed" - Then cmd: "move publish -p ../../apps/grow_bitcoin --named-addresses app_admin=default,grow_bitcoin=default --json" - Then assert: "{{$.move[-1].execution_info.status.type}} == executed" Then cmd: "object -t default::gas_market::RGasMarket" Then cmd: "object -t default::gas_faucet::RGasFaucet" @@ -112,4 +110,45 @@ Feature: Rooch Portal contract tests Then assert: "{{$.move[-1].execution_info.status.type}} != executed" + @serial + Scenario: grow_bitcoin + Given a bitcoind server for grow_bitcoin + Given a server for grow_bitcoin + + Then cmd: "account list --json" + + # mint utxos + Then cmd bitcoin-cli: "generatetoaddress 101 {{$.account[0].default.bitcoin_address}}" + Then sleep: "10" + + Then cmd: "move run --function rooch_framework::gas_coin::faucet_entry --args u256:1000000000000000 --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + + # publish grow_bitcoin via default address + Then cmd: "move publish -p ../../apps/app_admin --named-addresses app_admin=default --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + Then cmd: "move publish -p ../../apps/grow_bitcoin --named-addresses app_admin=default,grow_bitcoin=default --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + + Then cmd: "object -t default::admin::AdminCap" + + Then cmd: "object -o {{$.account[0].default.bitcoin_address}} -t 0x4::utxo::UTXO" + Then cmd: "move run --function default::grow_bitcoin::stake --args object:{{$.object[-1].data[0].id}} --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + + Then cmd bitcoin-cli: "generatetoaddress 1 {{$.account[0].default.bitcoin_address}}" + Then sleep: "5" + + Then cmd: "move run --function default::grow_bitcoin::harvest --args object:{{$.object[-1].data[0].id}} --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + + Then cmd bitcoin-cli: "generatetoaddress 1 {{$.account[0].default.bitcoin_address}}" + Then sleep: "5" + + Then cmd: "move run --function default::grow_bitcoin::unstake --args object:{{$.object[-1].data[0].id}} --json" + Then assert: "{{$.move[-1].execution_info.status.type}} == executed" + + # check the GRow balance of default + Then cmd: "account balance -a {{$.account[0].default.address}} --coin-type default::grow_bitcoin::GROW --json" + Then assert: "{{$.account[-1].GROW.balance}} != 0"