diff --git a/Cargo.lock b/Cargo.lock index 00067762f..f6be7d76e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4938,7 +4938,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "248.0.0" +version = "249.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -7938,7 +7938,7 @@ dependencies = [ [[package]] name = "pallet-dca" -version = "1.4.9" +version = "1.5.0" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -11929,7 +11929,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.22.5" +version = "1.23.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/Makefile b/Makefile index fed60e662..07c8c5003 100644 --- a/Makefile +++ b/Makefile @@ -1,59 +1,61 @@ +cargo := cargo --config net.git-fetch-with-cli=true + .PHONY: build build: - cargo build --release --locked + $(cargo) build --release --locked .PHONY: build-release build-release: - cargo build --release --locked --features metadata-hash + $(cargo) build --release --locked --features metadata-hash .PHONY: check check: - cargo check --release + $(cargo) check --release .PHONY: build-benchmarks build-benchmarks: - cargo build --release --features runtime-benchmarks + $(cargo) build --release --features runtime-benchmarks .PHONY: test test: - cargo test --locked + $(cargo) test --locked .PHONY: test-release test-release: - cargo test --release --locked + $(cargo) test --release --locked .PHONY: test-benchmarks test-benchmarks: - cargo test --release --features runtime-benchmarks + $(cargo) test --release --features runtime-benchmarks .PHONY: coverage coverage: - cargo tarpaulin --avoid-cfg-tarpaulin --all-features --workspace --locked --exclude-files node/* --exclude-files runtime/* --exclude-files infrastructure/* --exclude-files utils/* --exclude-files **/weights.rs --ignore-tests -o Xml -o lcov --timeout 120 + $(cargo) tarpaulin --avoid-cfg-tarpaulin --all-features --workspace --locked --exclude-files node/* --exclude-files runtime/* --exclude-files infrastructure/* --exclude-files utils/* --exclude-files **/weights.rs --ignore-tests -o Xml -o lcov --timeout 120 .PHONY: clippy clippy: - cargo clippy --release --locked --all-targets -- -D warnings -A deprecated + $(cargo) clippy --release --locked --all-targets -- -D warnings -A deprecated .PHONY: clippy-all clippy-all: - cargo clippy --release --locked --all-targets --all-features -- -D warnings -A deprecated + $(cargo) clippy --release --locked --all-targets --all-features -- -D warnings -A deprecated .PHONY: format format: - cargo fmt + $(cargo) fmt .PHONY: try-runtime try-runtime: - cargo build --release --features try-runtime + $(cargo) build --release --features try-runtime try-runtime --runtime ./target/release/wbuild/hydradx-runtime/hydradx_runtime.wasm on-runtime-upgrade --checks all live --uri wss://rpc.hydradx.cloud:443 .PHONY: build-docs build-docs: - cargo doc --release --target-dir ./HydraDX-dev-docs --no-deps + $(cargo) doc --release --target-dir ./HydraDX-dev-docs --no-deps .PHONY: clean clean: - cargo clean + $(cargo) clean .PHONY: docker docker: diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 2524e59f9..00ecc5b21 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.22.5" +version = "1.23.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" @@ -32,14 +32,14 @@ warehouse-liquidity-mining = { workspace = true } pallet-otc = { workspace = true } pallet-otc-settlements = { workspace = true } pallet-relaychain-info = { workspace = true } -pallet-route-executor = { workspace = true} -pallet-dca = { workspace = true} +pallet-route-executor = { workspace = true } +pallet-dca = { workspace = true } pallet-dynamic-fees = { workspace = true } pallet-dynamic-evm-fee = { workspace = true } -pallet-staking = { workspace = true} -pallet-lbp = { workspace = true} -pallet-xyk = { workspace = true} -pallet-evm-accounts = { workspace = true} +pallet-staking = { workspace = true } +pallet-lbp = { workspace = true } +pallet-xyk = { workspace = true } +pallet-evm-accounts = { workspace = true } pallet-treasury = { workspace = true } pallet-democracy = { workspace = true } diff --git a/integration-tests/src/dca.rs b/integration-tests/src/dca.rs index 6d146769f..24b0ffbdc 100644 --- a/integration-tests/src/dca.rs +++ b/integration-tests/src/dca.rs @@ -61,7 +61,7 @@ mod omnipool { let schedule = DCA::schedules(schedule_id); assert!(schedule.is_some()); - let next_block_id = block_id + 1; + let next_block_id = block_id + 2; let schedule = DCA::schedule_ids_per_block(next_block_id); assert!(!schedule.is_empty()); expect_hydra_last_events(vec![pallet_dca::Event::Scheduled { @@ -96,7 +96,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -131,7 +131,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -158,7 +158,7 @@ mod omnipool { let amount_out = 100 * UNITS; let schedule1 = Schedule { owner: AccountId::from(ALICE), - period: 1u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -183,7 +183,7 @@ mod omnipool { //Act and assert let schedule_id = 0; - set_relaychain_block_number(11); + set_relaychain_block_number(12); let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); @@ -191,20 +191,20 @@ mod omnipool { assert_reserved_balance!(&ALICE.into(), HDX, dca_budget - fee); assert_eq!(DCA::retries_on_error(schedule_id), 1); - set_relaychain_block_number(21); + set_relaychain_block_number(22); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_balance!(ALICE.into(), HDX, ALICE_INITIAL_NATIVE_BALANCE - dca_budget); assert_reserved_balance!(&ALICE.into(), HDX, dca_budget - 2 * fee); assert_eq!(DCA::retries_on_error(schedule_id), 2); - set_relaychain_block_number(41); + set_relaychain_block_number(42); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_balance!(ALICE.into(), HDX, ALICE_INITIAL_NATIVE_BALANCE - dca_budget); assert_reserved_balance!(&ALICE.into(), HDX, dca_budget - 3 * fee); assert_eq!(DCA::retries_on_error(schedule_id), 3); //After this retry we terminate - set_relaychain_block_number(81); + set_relaychain_block_number(82); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_balance!(ALICE.into(), HDX, ALICE_INITIAL_NATIVE_BALANCE - 4 * fee); assert_reserved_balance!(&ALICE.into(), HDX, 0); @@ -236,7 +236,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), LRNA, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(LRNA, &Treasury::account_id()); @@ -270,7 +270,7 @@ mod omnipool { create_schedule(ALICE, schedule1); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -355,7 +355,7 @@ mod omnipool { create_schedule(ALICE, schedule1); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(LRNA, &Treasury::account_id()); @@ -440,7 +440,7 @@ mod omnipool { assert_reserved_balance!(&ALICE.into(), HDX, dca_budget); //Act - run_to_block(11, 40); + run_to_block(11, 50); //Assert assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE + 700 * UNITS); @@ -483,7 +483,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let amount_out = 71_214_372_591_631; @@ -519,7 +519,7 @@ mod omnipool { BOB.into(), )); - rococo_run_to_block(11); + rococo_run_to_block(12); let alice_init_hdx_balance = 5000 * UNITS; assert_ok!(Balances::force_set_balance( @@ -565,7 +565,7 @@ mod omnipool { ETH, BOB.into(), )); - rococo_run_to_block(12); + rococo_run_to_block(14); //Assert let schedule_id = 0; @@ -575,43 +575,6 @@ mod omnipool { }); } - #[test] - fn sell_schedule_should_sell_remaining_in_next_trade_when_there_is_not_enough_left() { - TestNet::reset(); - Hydra::execute_with(|| { - //Arrange - init_omnipool_with_oracle_for_block_10(); - let alice_init_hdx_balance = 5000 * UNITS; - assert_ok!(Balances::force_set_balance( - RuntimeOrigin::root(), - ALICE.into(), - alice_init_hdx_balance, - )); - - let dca_budget = 1000 * UNITS; - let amount_to_sell = 700 * UNITS; - let schedule1 = - schedule_fake_with_sell_order(ALICE, PoolType::Omnipool, dca_budget, HDX, DAI, amount_to_sell); - create_schedule(ALICE, schedule1); - - assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); - assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); - assert_reserved_balance!(&ALICE.into(), HDX, dca_budget); - assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); - - //Act - run_to_block(11, 15); - - //Assert - let schedule_id = 0; - let schedule = DCA::schedules(schedule_id); - assert!(schedule.is_none()); - - assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); - assert_reserved_balance!(&ALICE.into(), HDX, 0); - }); - } - #[test] fn sell_schedule_execution_should_work_without_route() { TestNet::reset(); @@ -641,7 +604,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -670,7 +633,7 @@ mod omnipool { let amount_to_sell = 100 * UNITS; let schedule1 = Schedule { owner: AccountId::from(ALICE), - period: 1u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -696,7 +659,7 @@ mod omnipool { //Act and Assert let schedule_id = 0; - set_relaychain_block_number(11); + set_relaychain_block_number(12); let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); @@ -705,20 +668,20 @@ mod omnipool { assert_eq!(DCA::retries_on_error(schedule_id), 1); - set_relaychain_block_number(21); + set_relaychain_block_number(22); assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_reserved_balance!(&ALICE.into(), HDX, dca_budget - 2 * fee); assert_eq!(DCA::retries_on_error(schedule_id), 2); - set_relaychain_block_number(41); + set_relaychain_block_number(42); assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_reserved_balance!(&ALICE.into(), HDX, dca_budget - 3 * fee); assert_eq!(DCA::retries_on_error(schedule_id), 3); //At this point, the schedule will be terminated as retries max number of times - set_relaychain_block_number(81); + set_relaychain_block_number(82); assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - 4 * fee); assert_reserved_balance!(&ALICE.into(), HDX, 0); @@ -750,7 +713,7 @@ mod omnipool { assert_balance!(&Treasury::account_id(), LRNA, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let amount_out = 142499995765917; @@ -791,7 +754,7 @@ mod omnipool { assert_reserved_balance!(&ALICE.into(), HDX, dca_budget); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -882,7 +845,7 @@ mod omnipool { assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(LRNA, &Treasury::account_id()); @@ -1178,14 +1141,14 @@ mod omnipool { }) .unwrap(); - let alice_init_hdx_balance = 1000 * UNITS + fee + 1; + let alice_init_hdx_balance = 2 * (1000 * UNITS + fee) + 1; assert_ok!(Balances::force_set_balance( RuntimeOrigin::root(), ALICE.into(), alice_init_hdx_balance, )); - let dca_budget = 1000 * UNITS + fee; + let dca_budget = 2 * (1000 * UNITS + fee); let schedule1 = schedule_fake_with_sell_order(ALICE, PoolType::Omnipool, dca_budget, HDX, DAI, amount_to_sell); create_schedule(ALICE, schedule1); @@ -1196,12 +1159,12 @@ mod omnipool { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + run_to_block(11, 17); //Assert check_if_no_failed_events(); - assert_balance!(ALICE.into(), HDX, 0); assert_reserved_balance!(&ALICE.into(), HDX, 0); + assert_balance!(ALICE.into(), HDX, 0); }); } } @@ -1266,7 +1229,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), asset_a, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(asset_a, &Treasury::account_id()); @@ -1338,7 +1301,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), asset_a, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(asset_a, &Treasury::account_id()); @@ -1420,7 +1383,7 @@ mod stableswap { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -1442,7 +1405,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -1700,7 +1663,7 @@ mod stableswap { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -1722,7 +1685,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), stable_asset_1, 0); //Act - set_relaychain_block_number(1001); + set_relaychain_block_number(1002); //Assert let fee = Currencies::free_balance(stable_asset_1, &Treasury::account_id()); @@ -2008,7 +1971,7 @@ mod stableswap { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2030,7 +1993,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -2089,7 +2052,7 @@ mod stableswap { amount_to_buy, dca_budget, ); - set_relaychain_block_number(10); + set_relaychain_block_number(12); create_schedule(ALICE, schedule1); @@ -2099,7 +2062,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), asset_a, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(14); //Assert let fee = Currencies::free_balance(asset_a, &Treasury::account_id()); @@ -2189,7 +2152,7 @@ mod stableswap { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2211,7 +2174,7 @@ mod stableswap { assert_balance!(&Treasury::account_id(), stable_asset_1, 0); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(stable_asset_1, &Treasury::account_id()); @@ -2285,7 +2248,7 @@ mod xyk { let treasury_init_balance = Balances::free_balance(Treasury::account_id()); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let amount_out = 151105924242426; @@ -2339,7 +2302,7 @@ mod xyk { assert_reserved_balance!(&ALICE.into(), HDX, dca_budget); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE + amount_to_buy); @@ -2442,7 +2405,7 @@ mod all_pools { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2468,7 +2431,7 @@ mod all_pools { ); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let amount_to_receive = 380211607465242; @@ -2590,7 +2553,7 @@ mod with_onchain_route { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2612,7 +2575,7 @@ mod with_onchain_route { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget); @@ -2704,7 +2667,7 @@ mod with_onchain_route { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2726,7 +2689,7 @@ mod with_onchain_route { assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(HDX, &Treasury::account_id()) - TREASURY_ACCOUNT_INIT_BALANCE; @@ -2788,7 +2751,7 @@ mod with_onchain_route { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2831,7 +2794,7 @@ mod with_onchain_route { assert_balance!(ALICE.into(), HDX, ALICE_INITIAL_NATIVE_BALANCE); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(DOT, &Treasury::account_id()); @@ -2935,7 +2898,7 @@ mod with_onchain_route { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -2972,7 +2935,7 @@ mod with_onchain_route { create_schedule(ALICE, schedule); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(stable_asset_1, &Treasury::account_id()); @@ -3055,7 +3018,7 @@ mod with_onchain_route { let schedule = Schedule { owner: AccountId::from(ALICE), - period: 3u32, + period: 5u32, total_amount: dca_budget, max_retries: None, stability_threshold: None, @@ -3092,13 +3055,13 @@ mod with_onchain_route { create_schedule(ALICE, schedule); //Act - set_relaychain_block_number(11); + set_relaychain_block_number(12); //Assert let fee = Currencies::free_balance(DOT, &Treasury::account_id()); assert!(fee > 0, "The treasury did not receive the fee"); - assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance + 277_665_116_680_343); + assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance + 277781714835263); assert_reserved_balance!(&ALICE.into(), DOT, dca_budget - amount_to_sell - fee); }); } @@ -3160,7 +3123,7 @@ fn schedule_fake_with_buy_order_with_route( ) -> Schedule { Schedule { owner: AccountId::from(ALICE), - period: 2u32, + period: 5u32, total_amount: budget, max_retries: None, stability_threshold: None, @@ -3207,7 +3170,7 @@ fn schedule_fake_with_sell_order_with_route( ) -> Schedule { Schedule { owner: AccountId::from(owner), - period: 3u32, + period: 5u32, total_amount, max_retries: None, stability_threshold: None, diff --git a/pallets/dca/Cargo.toml b/pallets/dca/Cargo.toml index 4d3427ce4..46d04cf67 100644 --- a/pallets/dca/Cargo.toml +++ b/pallets/dca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-dca' -version = "1.4.9" +version = "1.5.0" description = 'A pallet to manage DCA scheduling' authors = ['GalacticCouncil'] edition = '2021' @@ -26,13 +26,13 @@ frame-system = { workspace = true } #cumumlus cumulus-primitives-core = { workspace = true } -cumulus-pallet-parachain-system ={ workspace = true } +cumulus-pallet-parachain-system = { workspace = true } # HydraDX dependencies pallet-omnipool = { workspace = true } hydradx-traits = { workspace = true } hydradx-adapters = { workspace = true } -pallet-ema-oracle = { workspace = true } +pallet-ema-oracle = { workspace = true } hydra-dx-math = { workspace = true } @@ -44,7 +44,7 @@ frame-benchmarking = { workspace = true, optional = true } sp-core = { workspace = true, optional = true } sp-io = { workspace = true, optional = true } -primitives = { path="../../primitives", default-features = false } +primitives = { path = "../../primitives", default-features = false } [dev-dependencies] @@ -64,24 +64,24 @@ primitive-types = { version = "0.12.0", default-features = false } [features] default = ['std'] std = [ - 'codec/std', - 'frame-support/std', - 'frame-system/std', - 'sp-runtime/std', - 'sp-std/std', - 'sp-core/std', - 'sp-io/std', - "scale-info/std", - "orml-tokens/std", - "hydradx-traits/std", - "hydradx-adapters/std", - "pallet-omnipool/std", - "pallet-ema-oracle/std", + 'codec/std', + 'frame-support/std', + 'frame-system/std', + 'sp-runtime/std', + 'sp-std/std', + 'sp-core/std', + 'sp-io/std', + "scale-info/std", + "orml-tokens/std", + "hydradx-traits/std", + "hydradx-adapters/std", + "pallet-omnipool/std", + "pallet-ema-oracle/std", ] runtime-benchmarks = [ - "frame-benchmarking", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/dca/src/lib.rs b/pallets/dca/src/lib.rs index f0504baea..5d0216d68 100644 --- a/pallets/dca/src/lib.rs +++ b/pallets/dca/src/lib.rs @@ -86,10 +86,10 @@ use orml_traits::{arithmetic::CheckedAdd, MultiCurrency, NamedMultiReservableCur use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use sp_runtime::helpers_128bit::multiply_by_rational_with_rounding; -use sp_runtime::traits::{CheckedMul, One}; +use sp_runtime::traits::CheckedMul; use sp_runtime::{ traits::{BlockNumberProvider, Saturating}, - ArithmeticError, BoundedVec, DispatchError, FixedPointNumber, FixedU128, Permill, Rounding, + ArithmeticError, BoundedVec, DispatchError, FixedPointNumber, FixedU128, Percent, Permill, Rounding, }; use sp_std::vec::Vec; @@ -123,6 +123,7 @@ pub mod pallet { use hydra_dx_math::ema::EmaPrice; use hydradx_traits::{NativePriceOracle, PriceOracle}; use orml_traits::NamedMultiReservableCurrency; + use sp_runtime::Percent; #[pallet::pallet] pub struct Pallet(_); @@ -158,10 +159,12 @@ pub mod pallet { &schedule, &mut randomness_generator, ) { - if e != Error::::PriceUnstable.into() { + if e == Error::::PriceUnstable.into() || e == Error::::Bumped.into() { + continue; + } else { Self::terminate_schedule(schedule_id, &schedule, e); - }; - continue; + continue; + } }; match Self::execute_trade(schedule_id, &schedule) { @@ -251,6 +254,10 @@ pub mod pallet { #[pallet::constant] type MaxPriceDifferenceBetweenBlocks: Get; + ///Max configurable price difference allowed between blocks + #[pallet::constant] + type MaxConfigurablePriceDifferenceBetweenBlocks: Get; + ///The number of max schedules to be executed per block #[pallet::constant] type MaxSchedulePerBlock: Get; @@ -259,6 +266,14 @@ pub mod pallet { #[pallet::constant] type MaxNumberOfRetriesOnError: Get; + ///Minimal period between executions + #[pallet::constant] + type MinimalPeriod: Get; + + ///Chance of the random rescheduling + #[pallet::constant] + type BumpChance: Get; + /// Minimum trading limit for a single trade #[pallet::constant] type MinimumTradingLimit: Get; @@ -348,11 +363,13 @@ pub mod pallet { BlockNumberIsNotInFuture, ///Price is unstable as price change from oracle data is bigger than max allowed PriceUnstable, + ///Order was randomly rescheduled to next block + Bumped, ///Error occurred when calculating price CalculatingPriceError, ///The total amount to be reserved is smaller than min budget TotalAmountIsSmallerThanMinBudget, - ///The budget is too low for executing one DCA + ///The budget is too low for executing at least two orders BudgetTooLow, ///There is no free block found to plan DCA execution NoFreeBlockFound, @@ -360,7 +377,7 @@ pub mod pallet { ManuallyTerminated, ///Max number of retries reached for schedule MaxRetryReached, - ///Absolutely trade limit reached reached, leading to retry + ///Absolutely trade limit reached, leading to retry TradeLimitReached, ///Slippage limit calculated from oracle is reached, leading to retry SlippageLimitReached, @@ -368,6 +385,10 @@ pub mod pallet { NoParentHashFound, ///Error that should not really happen only in case of invalid state of the schedule storage entries InvalidState, + ///Period should be longer than 5 blocks + PeriodTooShort, + ///Stability threshold cannot be higher than `MaxConfigurablePriceDifferenceBetweenBlock` + StabilityThresholdTooHigh, } /// Id sequencer for schedules @@ -425,7 +446,7 @@ pub mod pallet { /// Parameters: /// - `origin`: schedule owner /// - `schedule`: schedule details - /// - `start_execution_block`: start execution block for the schedule + /// - `start_execution_block`: first possible execution block for the schedule /// /// Emits `Scheduled` and `ExecutionPlanned` event when successful. /// @@ -449,6 +470,17 @@ pub mod pallet { schedule.total_amount >= min_budget, Error::::TotalAmountIsSmallerThanMinBudget ); + ensure!( + schedule.period >= BlockNumberFor::::from(T::MinimalPeriod::get()), + Error::::PeriodTooShort + ); + ensure!( + match schedule.stability_threshold { + Some(threshold) => threshold <= T::MaxConfigurablePriceDifferenceBetweenBlocks::get(), + None => true, + }, + Error::::StabilityThresholdTooHigh + ); let transaction_fee = Self::get_transaction_fee(&schedule.order)?; @@ -469,9 +501,7 @@ pub mod pallet { Error::::MinTradeAmountNotReached ); - let amount_in_with_transaction_fee = amount_in - .checked_add(transaction_fee) - .ok_or(ArithmeticError::Overflow)?; + let amount_in_with_transaction_fee = amount_in.saturating_add(transaction_fee).saturating_mul(2); ensure!( amount_in_with_transaction_fee <= schedule.total_amount, Error::::BudgetTooLow @@ -496,7 +526,7 @@ pub mod pallet { schedule.total_amount, )?; - let blocknumber_for_first_schedule_execution = Self::get_next_execution_block(start_execution_block)?; + let blocknumber_for_first_schedule_execution = Self::get_first_execution_block(start_execution_block)?; let mut randomness_generator = Self::get_randomness_generator( frame_system::Pallet::::current_block_number(), @@ -548,7 +578,7 @@ pub mod pallet { Self::try_unreserve_all(schedule_id, &schedule); - let next_execution_block = Self::get_next_execution_block(next_execution_block)?; + let next_execution_block = next_execution_block.ok_or(Error::::ScheduleNotFound)?; //Remove schedule id from next execution block ScheduleIdsPerBlock::::try_mutate_exists( @@ -591,20 +621,26 @@ impl Pallet { block: current_blocknumber, error: err, }); - rand::rngs::StdRng::seed_from_u64(0) + + StdRng::seed_from_u64(0) } } } - fn get_next_execution_block( + fn get_first_execution_block( start_execution_block: Option>, ) -> Result, DispatchError> { + let current_block_number = frame_system::Pallet::::current_block_number(); let blocknumber_for_first_schedule_execution = match start_execution_block { - Some(blocknumber) => Ok(blocknumber), + Some(blocknumber) => { + let number = current_block_number.saturating_add(2u32.into()).max(blocknumber); + let remainder: u32 = (number.into() % 5).as_u32(); + let diff = if remainder == 0 { 0 } else { 5u32 - remainder }; + Ok(number.saturating_add(diff.into())) + } None => { - let current_block_number = frame_system::Pallet::::current_block_number(); let next_block_number = current_block_number - .checked_add(&BlockNumberFor::::one()) + .checked_add(&BlockNumberFor::::from(2u32)) .ok_or(ArithmeticError::Overflow)?; Ok::, ArithmeticError>(next_block_number) @@ -621,6 +657,12 @@ impl Pallet { schedule: &Schedule>, randomness_generator: &mut StdRng, ) -> DispatchResult { + if Percent::from_percent(randomness_generator.gen_range(0..100)) <= T::BumpChance::get() { + let next_block = current_blocknumber.saturating_add(1u32.into()); + Self::plan_schedule_for_block(&schedule.owner, next_block, schedule_id, randomness_generator)?; + return Err(Error::::Bumped.into()); + } + Self::take_transaction_fee_from_user(schedule_id, schedule, weight_for_dca_execution)?; if Self::is_price_unstable(schedule) { @@ -1097,7 +1139,7 @@ impl RandomnessProvider for Pallet { let hash_value = T::RelayChainBlockHashProvider::parent_hash().ok_or(Error::::NoParentHashFound)?; let hash_bytes = hash_value.as_fixed_bytes(); let mut seed_arr = [0u8; 8]; - let max_len = hash_bytes.len().min(seed_arr.len()); //We ensure that we don't copy more bytes, preventing potential panics + let max_len = hash_bytes.len().min(seed_arr.len()); // Ensure we don't copy more bytes, preventing potential panics seed_arr[..max_len].copy_from_slice(&hash_bytes[..max_len]); let seed = match salt { diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index 36b8f0230..376724cd5 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -34,12 +34,12 @@ use pallet_currencies::BasicCurrencyAdapter; use primitive_types::U128; use sp_core::H256; use sp_runtime::traits::{AccountIdConversion, BlockNumberProvider, ConstU32}; -use sp_runtime::Perbill; use sp_runtime::Permill; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup, One}, BuildStorage, DispatchError, }; +use sp_runtime::{Perbill, Percent}; use hydra_dx_math::support::rational::{round_to_rational, Rounding}; use sp_runtime::traits::Zero; @@ -643,9 +643,13 @@ parameter_types! { pub MinBudgetInNativeCurrency: Balance= MIN_BUDGET.with(|v| *v.borrow()); pub MaxSchedulePerBlock: u32 = 20; pub OmnipoolMaxAllowedPriceDifference: Permill = MAX_PRICE_DIFFERENCE.with(|v| *v.borrow()); + pub MaxConfigurablePriceDifference: Permill = Permill::from_percent(20); + pub MinimalPeriod: u32 = 5; + pub BumpChance: Percent = Percent::from_percent(0); pub NamedReserveId: NamedReserveIdentifier = *b"dcaorder"; pub MaxNumberOfRetriesOnError: u8 = 3; } +use rand::SeedableRng; pub struct RandomnessProviderMock {} @@ -676,6 +680,9 @@ impl Config for Test { type RouteExecutor = RouteExecutor; type RouteProvider = DefaultRouteProvider; type MaxPriceDifferenceBetweenBlocks = OmnipoolMaxAllowedPriceDifference; + type MaxConfigurablePriceDifferenceBetweenBlocks = MaxConfigurablePriceDifference; + type MinimalPeriod = MinimalPeriod; + type BumpChance = BumpChance; type NamedReserveId = NamedReserveId; type MaxNumberOfRetriesOnError = MaxNumberOfRetriesOnError; type TechnicalOrigin = EnsureRoot; @@ -715,8 +722,7 @@ use hydra_dx_math::types::Ratio; use hydradx_traits::router::{ExecutorError, PoolType, RefundEdCalculator, RouteProvider, Trade, TradeExecution}; use pallet_currencies::fungibles::FungibleCurrencies; use pallet_omnipool::traits::ExternalPriceProvider; -use rand::prelude::StdRng; -use rand::SeedableRng; +use rand::rngs::StdRng; use smallvec::smallvec; pub struct DummyNFT; diff --git a/pallets/dca/src/tests/on_initialize.rs b/pallets/dca/src/tests/on_initialize.rs index f5f27dde4..d04568d6b 100644 --- a/pallets/dca/src/tests/on_initialize.rs +++ b/pallets/dca/src/tests/on_initialize.rs @@ -64,7 +64,7 @@ fn successful_sell_dca_execution_should_emit_trade_executed_event() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let schedule_id = 0; @@ -79,7 +79,7 @@ fn successful_sell_dca_execution_should_emit_trade_executed_event() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 602, } .into(), ]); @@ -119,7 +119,7 @@ fn successful_buy_dca_execution_should_emit_trade_executed_event() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let schedule_id = 0; @@ -134,7 +134,7 @@ fn successful_buy_dca_execution_should_emit_trade_executed_event() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 602, } .into(), ]); @@ -174,7 +174,7 @@ fn one_sell_dca_execution_should_unreserve_amount_in() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let remaining_named_reserve = total_amount - amount_to_sell - SELL_DCA_FEE_IN_NATIVE; @@ -199,7 +199,7 @@ fn one_sell_dca_execution_should_unreserve_amount_in() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 602, } .into(), ]) @@ -216,7 +216,7 @@ fn sell_schedule_should_sell_remaining_when_there_is_not_enough_left() { //Arrange proceed_to_blocknumber(1, 500); - let total_amount = *AMOUNT_OUT_FOR_OMNIPOOL_SELL * 3 / 2; + let total_amount = *AMOUNT_OUT_FOR_OMNIPOOL_SELL * 5 / 2; let amount_to_sell = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; let schedule = ScheduleBuilder::new() @@ -239,17 +239,13 @@ fn sell_schedule_should_sell_remaining_when_there_is_not_enough_left() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert - assert_executed_sell_trades!(vec![SellExecution { - asset_in: HDX, - asset_out: BTC, - amount_in: *AMOUNT_OUT_FOR_OMNIPOOL_SELL, - min_buy_amount: *AMOUNT_OUT_FOR_OMNIPOOL_SELL, - }]); + assert_number_of_executed_sell_trades!(2); - set_to_blocknumber(601); + set_to_blocknumber(702); //Assert assert_executed_sell_trades!(vec![ @@ -262,7 +258,13 @@ fn sell_schedule_should_sell_remaining_when_there_is_not_enough_left() { SellExecution { asset_in: HDX, asset_out: BTC, - amount_in: total_amount - amount_to_sell - SELL_DCA_FEE_IN_NATIVE - SELL_DCA_FEE_IN_NATIVE, + amount_in: *AMOUNT_OUT_FOR_OMNIPOOL_SELL, + min_buy_amount: *AMOUNT_OUT_FOR_OMNIPOOL_SELL, + }, + SellExecution { + asset_in: HDX, + asset_out: BTC, + amount_in: total_amount - 2 * (amount_to_sell + SELL_DCA_FEE_IN_NATIVE) - SELL_DCA_FEE_IN_NATIVE, min_buy_amount: *AMOUNT_OUT_FOR_OMNIPOOL_SELL, } ]); @@ -282,12 +284,12 @@ fn sell_schedule_should_continue_when_there_is_exact_amount_in_left_as_remaining //Arrange proceed_to_blocknumber(1, 500); - let total_amount = *AMOUNT_OUT_FOR_OMNIPOOL_SELL * 2 + SELL_DCA_FEE_IN_NATIVE; + let total_amount = 3 * *AMOUNT_OUT_FOR_OMNIPOOL_SELL * 2 + SELL_DCA_FEE_IN_NATIVE; let amount_to_sell = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) - .with_period(1) + .with_period(5) .with_order(Order::Sell { asset_in: HDX, asset_out: BTC, @@ -305,7 +307,8 @@ fn sell_schedule_should_continue_when_there_is_exact_amount_in_left_as_remaining assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(504); //Assert assert_executed_sell_trades!(vec![SellExecution { @@ -353,7 +356,7 @@ fn one_buy_dca_execution_should_unreserve_exact_amount_in() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_buy_trades!(vec![BuyExecution { @@ -411,7 +414,7 @@ fn one_buy_dca_execution_should_calculate_exact_amount_in_when_multiple_pools_in assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_buy_trades!(vec![ @@ -468,7 +471,7 @@ fn full_sell_dca_should_be_completed_with_selling_leftover_in_last_trade() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - proceed_to_blocknumber(501, 801); + proceed_to_blocknumber(502, 802); //Assert assert_eq!(0, Currencies::reserved_balance(HDX, &ALICE)); @@ -530,7 +533,7 @@ fn full_sell_dca_should_be_completed_when_some_successful_dca_execution_happened proceed_to_blocknumber(1, 500); let amount_to_sell = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; - let total_amount = amount_to_sell + SELL_DCA_FEE_IN_NATIVE + SELL_DCA_FEE_IN_NATIVE / 2; + let total_amount = 2 * (amount_to_sell + SELL_DCA_FEE_IN_NATIVE) + SELL_DCA_FEE_IN_NATIVE / 2; let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) @@ -557,8 +560,8 @@ fn full_sell_dca_should_be_completed_when_some_successful_dca_execution_happened //Assert assert_eq!(0, Currencies::reserved_balance(HDX, &ALICE)); - assert_number_of_executed_sell_trades!(1); - assert_balance!(ALICE, BTC, *AMOUNT_OUT_FOR_OMNIPOOL_SELL); + assert_number_of_executed_sell_trades!(2); + assert_balance!(ALICE, BTC, 2 * *AMOUNT_OUT_FOR_OMNIPOOL_SELL); let schedule_id = 0; assert_that_dca_is_completed(ALICE, schedule_id); @@ -617,7 +620,7 @@ fn full_buy_dca_should_be_completed_when_some_successful_dca_execution_happened_ proceed_to_blocknumber(1, 500); let total_amount = - CALCULATED_AMOUNT_IN_FOR_OMNIPOOL_BUY + BUY_DCA_FEE_IN_NATIVE + BUY_DCA_FEE_IN_NATIVE / 2; + 2 * (CALCULATED_AMOUNT_IN_FOR_OMNIPOOL_BUY + BUY_DCA_FEE_IN_NATIVE) + BUY_DCA_FEE_IN_NATIVE / 2; let amount_to_buy = 10 * ONE; let schedule = ScheduleBuilder::new() @@ -641,13 +644,13 @@ fn full_buy_dca_should_be_completed_when_some_successful_dca_execution_happened_ assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - proceed_to_blocknumber(501, 801); + proceed_to_blocknumber(502, 802); //Assert assert_eq!(0, Currencies::reserved_balance(HDX, &ALICE)); - assert_number_of_executed_buy_trades!(1); - assert_balance!(ALICE, BTC, CALCULATED_AMOUNT_IN_FOR_OMNIPOOL_BUY); + assert_number_of_executed_buy_trades!(2); + assert_balance!(ALICE, BTC, 2 * CALCULATED_AMOUNT_IN_FOR_OMNIPOOL_BUY); let schedule_id = 0; assert_that_dca_is_completed(ALICE, schedule_id); @@ -714,7 +717,7 @@ fn full_sell_dca_should_be_completed_for_multiple_users() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &BOB)); //Act - proceed_to_blocknumber(501, 801); + proceed_to_blocknumber(502, 802); //Assert assert_eq!(0, Currencies::reserved_balance(HDX, &ALICE)); @@ -767,7 +770,7 @@ fn multiple_sell_dca_should_be_completed_for_one_user() { assert_eq!(total_amount + total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - proceed_to_blocknumber(501, 801); + proceed_to_blocknumber(502, 802); //Assert assert_eq!(0, Currencies::reserved_balance(HDX, &ALICE)); @@ -958,7 +961,7 @@ fn one_buy_dca_execution_should_use_default_max_price_diff_for_max_limit_calcula assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_number_of_executed_buy_trades!(1); @@ -1008,13 +1011,13 @@ fn schedule_is_planned_for_next_block_when_one_execution_finished() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_number_of_executed_buy_trades!(1); let schedule_id = 0; - assert_scheduled_ids!(601, vec![schedule_id]); + assert_scheduled_ids!(602, vec![schedule_id]); }); } @@ -1053,13 +1056,13 @@ fn schedule_is_planned_with_period_when_block_has_already_planned_schedule() { proceed_to_blocknumber(1, 500); let schedule_id_2 = 1; - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(501))); //Act - set_to_blocknumber(501); + set_to_blocknumber(505); //Assert - assert_scheduled_ids!(601, vec![schedule_id, schedule_id_2]); + assert_scheduled_ids!(605, vec![schedule_id, schedule_id_2]); }); } @@ -1091,13 +1094,13 @@ fn buy_dca_schedule_should_be_retried_when_trade_limit_error_happens() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); - set_to_blocknumber(501); + set_to_blocknumber(502); assert_number_of_executed_buy_trades!(0); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(1, retries); expect_dca_events(vec![ @@ -1110,7 +1113,7 @@ fn buy_dca_schedule_should_be_retried_when_trade_limit_error_happens() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 511, + block: 512, } .into(), ]); @@ -1145,12 +1148,12 @@ fn sell_dca_schedule_should_be_retried_when_trade_limit_error_happens() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); - set_to_blocknumber(501); + set_to_blocknumber(502); assert_number_of_executed_sell_trades!(0); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(1, retries); expect_dca_events(vec![ @@ -1163,7 +1166,7 @@ fn sell_dca_schedule_should_be_retried_when_trade_limit_error_happens() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 511, + block: 512, } .into(), ]); @@ -1202,10 +1205,10 @@ fn dca_trade_unallocation_should_be_rolled_back_when_trade_fails() { assert_eq!(Currencies::reserved_balance(HDX, &ALICE), total_amount); assert_eq!(DCA::remaining_amounts(schedule_id).unwrap(), total_amount); - set_to_blocknumber(501); + set_to_blocknumber(502); assert_number_of_executed_buy_trades!(0); - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); assert_eq!( Currencies::reserved_balance(HDX, &ALICE), @@ -1245,13 +1248,13 @@ fn dca_schedule_should_terminate_when_error_is_not_configured_to_continue_on() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let schedule_id = 0; assert_number_of_executed_buy_trades!(0); - assert!(DCA::schedule_ids_per_block(601).is_empty()); + assert!(DCA::schedule_ids_per_block(602).is_empty()); assert_that_dca_is_terminated(ALICE, schedule_id, pallet_omnipool::Error::::NotAllowed.into()); }); } @@ -1284,16 +1287,16 @@ fn dca_schedule_should_continue_on_multiple_failures_then_terminated() { //Act and assert let schedule_id = 0; - set_to_blocknumber(501); - assert_scheduled_ids!(511, vec![schedule_id]); + set_to_blocknumber(502); + assert_scheduled_ids!(512, vec![schedule_id]); - set_to_blocknumber(511); - assert_scheduled_ids!(531, vec![schedule_id]); + set_to_blocknumber(512); + assert_scheduled_ids!(532, vec![schedule_id]); - set_to_blocknumber(531); - assert_scheduled_ids!(571, vec![schedule_id]); + set_to_blocknumber(532); + assert_scheduled_ids!(572, vec![schedule_id]); - set_to_blocknumber(571); + set_to_blocknumber(572); assert!(DCA::schedules(schedule_id).is_none()); assert_number_of_executed_buy_trades!(0); }); @@ -1329,26 +1332,26 @@ fn dca_schedule_should_use_specified_max_retry_count() { //Act and assert let schedule_id = 0; - set_to_blocknumber(501); - assert_scheduled_ids!(511, vec![schedule_id]); + set_to_blocknumber(502); + assert_scheduled_ids!(512, vec![schedule_id]); - set_to_blocknumber(511); - assert_scheduled_ids!(531, vec![schedule_id]); + set_to_blocknumber(512); + assert_scheduled_ids!(532, vec![schedule_id]); - set_to_blocknumber(531); - assert_scheduled_ids!(571, vec![schedule_id]); + set_to_blocknumber(532); + assert_scheduled_ids!(572, vec![schedule_id]); - set_to_blocknumber(571); - assert_scheduled_ids!(651, vec![schedule_id]); + set_to_blocknumber(572); + assert_scheduled_ids!(652, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(4, retries); - set_to_blocknumber(651); - assert_scheduled_ids!(811, vec![schedule_id]); + set_to_blocknumber(652); + assert_scheduled_ids!(812, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(5, retries); - set_to_blocknumber(811); + set_to_blocknumber(812); assert!(DCA::schedules(schedule_id).is_none()); assert_number_of_executed_buy_trades!(0); }); @@ -1382,8 +1385,8 @@ fn buy_dca_schedule_should_continue_on_slippage_error() { //Act and assert let schedule_id = 0; - set_to_blocknumber(501); - assert_scheduled_ids!(511, vec![schedule_id]); + set_to_blocknumber(502); + assert_scheduled_ids!(512, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(1, retries); }); @@ -1422,8 +1425,8 @@ fn sell_dca_schedule_continue_on_slippage_error() { //Act and assert let schedule_id = 0; - set_to_blocknumber(501); - assert_scheduled_ids!(511, vec![schedule_id]); + set_to_blocknumber(502); + assert_scheduled_ids!(512, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(1, retries); }); @@ -1462,16 +1465,16 @@ fn dca_schedule_retry_should_be_reset_when_successful_trade_after_failed_ones() //Act and assert let schedule_id = 0; - set_to_blocknumber(501); - assert_scheduled_ids!(511, vec![schedule_id]); + set_to_blocknumber(502); + assert_scheduled_ids!(512, vec![schedule_id]); - set_to_blocknumber(511); - assert_scheduled_ids!(531, vec![schedule_id]); + set_to_blocknumber(512); + assert_scheduled_ids!(532, vec![schedule_id]); set_max_price_diff(Permill::from_percent(10)); - set_to_blocknumber(531); - assert_scheduled_ids!(531 + ONE_HUNDRED_BLOCKS, vec![schedule_id]); + set_to_blocknumber(532); + assert_scheduled_ids!(532 + ONE_HUNDRED_BLOCKS, vec![schedule_id]); assert_number_of_executed_sell_trades!(1); let retries = DCA::retries_on_error(schedule_id); @@ -1510,7 +1513,7 @@ fn execution_fee_should_be_taken_from_user_in_sold_currency_in_case_of_successfu //Act assert_balance!(TreasuryAccount::get(), DAI, 0); - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_balance!(TreasuryAccount::get(), DAI, BUY_DCA_FEE_IN_DAI); @@ -1555,7 +1558,7 @@ fn execution_fee_should_be_still_taken_from_user_in_sold_currency_in_case_of_fai assert_balance!(TreasuryAccount::get(), DAI, 0); assert_balance!(ALICE, BTC, 0); - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_balance!(TreasuryAccount::get(), DAI, BUY_DCA_FEE_IN_DAI); @@ -1598,7 +1601,7 @@ fn execution_fee_should_be_taken_from_user_in_sold_currency_in_case_of_successfu //Act assert_balance!(TreasuryAccount::get(), DAI, 0); - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_balance!(TreasuryAccount::get(), DAI, SELL_DCA_FEE_IN_DAI); @@ -1644,7 +1647,7 @@ fn sell_dca_native_execution_fee_should_be_taken_and_sent_to_treasury() { assert_balance!(TreasuryAccount::get(), HDX, 0); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_balance!(TreasuryAccount::get(), HDX, SELL_DCA_FEE_IN_NATIVE); @@ -1658,7 +1661,7 @@ fn sell_dca_native_execution_fee_should_be_taken_and_sent_to_treasury() { } #[test] -fn sell_dca_should_be_completed_when_trade_amount_is_total_budget_plus_fee() { +fn sell_dca_should_be_completed_when_last_trade_amount_is_total_budget_plus_fee() { ExtBuilder::default() .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) .build() @@ -1667,7 +1670,7 @@ fn sell_dca_should_be_completed_when_trade_amount_is_total_budget_plus_fee() { proceed_to_blocknumber(1, 500); let amount_to_sell = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; - let total_amount = amount_to_sell + SELL_DCA_FEE_IN_NATIVE; + let total_amount = 2 * (amount_to_sell + SELL_DCA_FEE_IN_NATIVE); let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) @@ -1690,10 +1693,11 @@ fn sell_dca_should_be_completed_when_trade_amount_is_total_budget_plus_fee() { assert_balance!(TreasuryAccount::get(), HDX, 0); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert - assert_number_of_executed_sell_trades!(1); + assert_number_of_executed_sell_trades!(2); assert_that_dca_is_completed(ALICE, 0); }); } @@ -1729,7 +1733,7 @@ fn buy_dca_native_execution_fee_should_be_taken_and_sent_to_treasury() { assert_balance!(TreasuryAccount::get(), HDX, 0); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_balance!(TreasuryAccount::get(), HDX, BUY_DCA_FEE_IN_NATIVE); @@ -1773,7 +1777,7 @@ fn slippage_limit_should_be_used_for_buy_dca_when_it_is_smaller_than_specified_t assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert //No trade happens because slippage limit is too small @@ -1813,11 +1817,11 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_max }) .build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(501))); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, None)); assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_sell_trades!(vec![]); @@ -1827,7 +1831,7 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_max ); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); expect_dca_events(vec![ DcaEvent::TradeFailed { id: schedule_id, @@ -1838,7 +1842,7 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_max DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 511, + block: 512, } .into(), ]); @@ -1876,11 +1880,11 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_use }) .build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(501))); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, None)); assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_sell_trades!(vec![]); @@ -1890,7 +1894,7 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_use ); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); expect_dca_events(vec![ DcaEvent::TradeFailed { id: schedule_id, @@ -1901,7 +1905,7 @@ fn one_sell_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_use DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 511, + block: 512, } .into(), ]); @@ -1942,7 +1946,7 @@ fn one_buy_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_max_ assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_buy_trades!(vec![]); @@ -1952,7 +1956,7 @@ fn one_buy_dca_execution_should_be_rescheduled_when_price_diff_is_more_than_max_ ); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); }); } @@ -1990,7 +1994,7 @@ fn specified_slippage_should_be_used_in_circuit_breaker_price_check() { assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert assert_executed_buy_trades!(vec![]); @@ -2000,7 +2004,7 @@ fn specified_slippage_should_be_used_in_circuit_breaker_price_check() { ); let schedule_id = 0; - assert_scheduled_ids!(511, vec![schedule_id]); + assert_scheduled_ids!(512, vec![schedule_id]); let retries = DCA::retries_on_error(schedule_id); assert_eq!(1, retries); @@ -2018,12 +2022,11 @@ fn dca_should_be_terminated_when_dca_cannot_be_planned_due_to_not_free_blocks() let total_amount = 5 * *AMOUNT_OUT_FOR_OMNIPOOL_SELL; let amount_to_sell = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; - let one_block = 1; let schedule_id = 0; let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) - .with_period(one_block) + .with_period(5) .with_order(Order::Sell { asset_in: HDX, asset_out: BTC, @@ -2037,16 +2040,20 @@ fn dca_should_be_terminated_when_dca_cannot_be_planned_due_to_not_free_blocks() }) .build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(995))); assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); for _ in RangeInclusive::new(1, 220) { let schedule = ScheduleBuilder::new().build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(502))); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); } //Act - set_to_blocknumber(501); + set_to_blocknumber(995); //Assert assert_executed_sell_trades!(vec![SellExecution { @@ -2076,7 +2083,7 @@ fn dca_should_be_terminated_when_price_change_is_big_but_no_free_blocks_to_repla let schedule_id = 0; let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) - .with_period(1) + .with_period(5) .with_order(Order::Sell { asset_in: HDX, asset_out: BTC, @@ -2090,16 +2097,20 @@ fn dca_should_be_terminated_when_price_change_is_big_but_no_free_blocks_to_repla }) .build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(501))); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(995))); assert_eq!(total_amount, Currencies::reserved_balance(HDX, &ALICE)); for _ in RangeInclusive::new(1, 220) { let schedule = ScheduleBuilder::new().build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(511))); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1005) + )); //995 + 10 because 10 is the retry delay } //Act - set_to_blocknumber(501); + set_to_blocknumber(995); //Assert assert_executed_sell_trades!(vec![]); @@ -2135,16 +2146,16 @@ fn dca_should_be_executed_and_replanned_through_multiple_blocks_when_all_consque }) .build(); - let mut execution_block = 501; for _ in RangeInclusive::new(1, 220) { assert_ok!(DCA::schedule( RuntimeOrigin::signed(ALICE), schedule.clone(), - Option::Some(501) + Some(1000) )); } //Check if first block is fully filled + let mut execution_block = 1000; let actual_schedule_ids = DCA::schedule_ids_per_block(execution_block); assert_eq!(20, actual_schedule_ids.len()); @@ -2156,10 +2167,10 @@ fn dca_should_be_executed_and_replanned_through_multiple_blocks_when_all_consque } //Act - proceed_to_blocknumber(501, 1524); + proceed_to_blocknumber(1000, 2000); //Assert - assert_number_of_executed_sell_trades!(1860); + assert_number_of_executed_sell_trades!(1740); //Assert if none of the schedule is terminated for schedule_id in RangeInclusive::new(0, 119) { @@ -2168,45 +2179,6 @@ fn dca_should_be_executed_and_replanned_through_multiple_blocks_when_all_consque }); } -#[test] -fn dca_sell_schedule_should_be_completed_after_one_trade_when_total_amount_is_equal_to_amount_in_plus_fee() { - ExtBuilder::default() - .with_endowed_accounts(vec![(ALICE, HDX, 5000 * ONE)]) - .build() - .execute_with(|| { - //Arrange - proceed_to_blocknumber(1, 500); - - let amount_in = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; - let total_amount = amount_in + SELL_DCA_FEE_IN_NATIVE; - let schedule = ScheduleBuilder::new() - .with_period(ONE_HUNDRED_BLOCKS) - .with_total_amount(total_amount) - .with_order(Order::Sell { - asset_in: HDX, - asset_out: BTC, - amount_in, - min_amount_out: Balance::MIN, - route: create_bounded_vec(vec![Trade { - pool: Omnipool, - asset_in: HDX, - asset_out: BTC, - }]), - }) - .build(); - - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); - - //Act - set_to_blocknumber(501); - - //Assert - let schedule_id = 0; - assert_number_of_executed_sell_trades!(1); - assert_that_dca_is_completed(ALICE, schedule_id); - }); -} - #[test] fn dca_sell_schedule_should_be_terminated_when_schedule_allocation_is_more_than_reserved_funds() { ExtBuilder::default() @@ -2217,7 +2189,7 @@ fn dca_sell_schedule_should_be_terminated_when_schedule_allocation_is_more_than_ proceed_to_blocknumber(1, 500); let amount_in = *AMOUNT_OUT_FOR_OMNIPOOL_SELL; - let total_amount = amount_in + SELL_DCA_FEE_IN_NATIVE; + let total_amount = 2 * (amount_in + SELL_DCA_FEE_IN_NATIVE); let schedule = ScheduleBuilder::new() .with_period(ONE_HUNDRED_BLOCKS) .with_total_amount(total_amount) @@ -2236,10 +2208,10 @@ fn dca_sell_schedule_should_be_terminated_when_schedule_allocation_is_more_than_ assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); - Currencies::unreserve_named(&NamedReserveId::get(), HDX, &ALICE, ONE / 2); + Currencies::unreserve_named(&NamedReserveId::get(), HDX, &ALICE, 5000 * ONE); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let schedule_id = 0; @@ -2258,7 +2230,7 @@ fn sell_schedule_should_be_completed_when_remainder_is_less_than_20_transaction_ proceed_to_blocknumber(1, 500); let remainder = 20 * SELL_DCA_FEE_IN_NATIVE - 1; - let total_amount = ONE + SELL_DCA_FEE_IN_NATIVE + remainder; + let total_amount = 2 * (ONE + SELL_DCA_FEE_IN_NATIVE) + remainder; let amount_to_sell = ONE; let schedule = ScheduleBuilder::new() @@ -2280,7 +2252,8 @@ fn sell_schedule_should_be_completed_when_remainder_is_less_than_20_transaction_ assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert let schedule_id = 0; @@ -2329,12 +2302,12 @@ fn schedules_are_purged_when_the_block_is_over() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); - assert_number_of_executed_sell_trades!(3); set_to_blocknumber(502); + assert_number_of_executed_sell_trades!(3); + set_to_blocknumber(503); //Assert - let scheduled_ids_for_next_block = DCA::schedule_ids_per_block(501); + let scheduled_ids_for_next_block = DCA::schedule_ids_per_block(502); assert_eq!( scheduled_ids_for_next_block, create_bounded_vec_with_schedule_ids(vec![]) @@ -2352,7 +2325,7 @@ fn sell_schedule_should_be_replanned_when_remainder_is_equal_to_20_transaction_f proceed_to_blocknumber(1, 500); let remainder = 20 * SELL_DCA_FEE_IN_NATIVE; - let total_amount = ONE + SELL_DCA_FEE_IN_NATIVE + remainder; + let total_amount = 2 * (ONE + SELL_DCA_FEE_IN_NATIVE) + remainder; let amount_to_sell = ONE; let schedule = ScheduleBuilder::new() @@ -2374,14 +2347,15 @@ fn sell_schedule_should_be_replanned_when_remainder_is_equal_to_20_transaction_f assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert let schedule_id = 0; expect_events(vec![DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 702, } .into()]); }); @@ -2397,7 +2371,7 @@ fn sell_schedule_should_be_replanned_when_more_than_20_transaction_fee_left_for_ proceed_to_blocknumber(1, 500); let remainder = 20 * SELL_DCA_FEE_IN_NATIVE + 1; - let total_amount = ONE + SELL_DCA_FEE_IN_NATIVE + remainder; + let total_amount = 2 * (ONE + SELL_DCA_FEE_IN_NATIVE) + remainder; let amount_to_sell = ONE; let schedule = ScheduleBuilder::new() @@ -2419,14 +2393,15 @@ fn sell_schedule_should_be_replanned_when_more_than_20_transaction_fee_left_for_ assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert let schedule_id = 0; expect_events(vec![DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 702, } .into()]); }); @@ -2445,7 +2420,7 @@ fn dca_should_complete_when_remainder_is_smaller_than_min_trading_limit() { proceed_to_blocknumber(1, 500); let remainder = min_trade_limit - 1; - let total_amount = ONE + SELL_DCA_FEE_IN_NATIVE + remainder; + let total_amount = 2 * (ONE + SELL_DCA_FEE_IN_NATIVE) + remainder; let amount_to_sell = ONE; let schedule = ScheduleBuilder::new() @@ -2467,7 +2442,8 @@ fn dca_should_complete_when_remainder_is_smaller_than_min_trading_limit() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert let schedule_id = 0; @@ -2485,7 +2461,7 @@ fn dca_should_continue_when_remainder_is_equal_to_min_trading_limit() { //Arrange proceed_to_blocknumber(1, 500); - let total_amount = ONE + SELL_DCA_FEE_IN_NATIVE + min_trade_limit; + let total_amount = 2 * (ONE + SELL_DCA_FEE_IN_NATIVE) + min_trade_limit; let amount_to_sell = ONE; let schedule = ScheduleBuilder::new() @@ -2507,14 +2483,15 @@ fn dca_should_continue_when_remainder_is_equal_to_min_trading_limit() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); + set_to_blocknumber(602); //Assert let schedule_id = 0; expect_events(vec![DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 702, } .into()]); }); @@ -2553,7 +2530,7 @@ fn execution_is_still_successful_when_no_parent_hash_present() { set_parent_hash(None); //Act - set_to_blocknumber(501); + set_to_blocknumber(502); //Assert let schedule_id = 0; @@ -2568,7 +2545,7 @@ fn execution_is_still_successful_when_no_parent_hash_present() { DcaEvent::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 601, + block: 602, } .into(), ]); @@ -2602,7 +2579,7 @@ fn dca_schedule_should_still_take_fee_when_order_fails() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act and assert - set_to_blocknumber(501); + set_to_blocknumber(502); assert_number_of_executed_buy_trades!(0); assert_balance!(TreasuryAccount::get(), HDX, BUY_DCA_FEE_IN_NATIVE); }); diff --git a/pallets/dca/src/tests/schedule.rs b/pallets/dca/src/tests/schedule.rs index fe2f12c86..921439575 100644 --- a/pallets/dca/src/tests/schedule.rs +++ b/pallets/dca/src/tests/schedule.rs @@ -21,13 +21,11 @@ use crate::tests::mock::*; use crate::tests::{create_bounded_vec, ScheduleBuilder}; use crate::{Error, Event, Order}; use frame_support::{assert_noop, assert_ok}; -use frame_system::pallet_prelude::BlockNumberFor; use hydradx_traits::router::{PoolType, Trade}; use orml_traits::NamedMultiReservableCurrency; use pretty_assertions::assert_eq; use sp_runtime::DispatchError::BadOrigin; use std::ops::RangeInclusive; -use test_case::test_case; #[test] fn schedule_should_reserve_all_total_amount_as_named_reserve() { @@ -156,7 +154,7 @@ fn schedule_should_compound_named_reserve_for_multiple_schedules() { } #[test] -fn schedule_should_store_schedule_for_next_block_when_no_blocknumber_specified() { +fn schedule_should_store_schedule_with_2_blocks_delay_when_no_blocknumber_specified() { ExtBuilder::default() .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) .build() @@ -174,8 +172,8 @@ fn schedule_should_store_schedule_for_next_block_when_no_blocknumber_specified() assert_eq!(stored_schedule, ScheduleBuilder::new().build()); //Check if schedule ids are stored - let schedule_ids = DCA::schedule_ids_per_block(501); - assert!(!DCA::schedule_ids_per_block(501).is_empty()); + let schedule_ids = DCA::schedule_ids_per_block(502); + assert!(!DCA::schedule_ids_per_block(502).is_empty()); let expected_scheduled_ids_for_next_block = create_bounded_vec_with_schedule_ids(vec![schedule_id]); assert_eq!(schedule_ids, expected_scheduled_ids_for_next_block); @@ -209,7 +207,7 @@ fn schedule_should_work_when_multiple_schedules_stored() { assert!(DCA::schedules(schedule_id).is_some()); assert!(DCA::schedules(schedule_id_2).is_some()); - let scheduled_ids_for_next_block = DCA::schedule_ids_per_block(501); + let scheduled_ids_for_next_block = DCA::schedule_ids_per_block(502); let expected_scheduled_ids_for_next_block = create_bounded_vec_with_schedule_ids(vec![schedule_id, schedule_id_2]); @@ -273,7 +271,7 @@ fn schedule_should_emit_necessary_events() { Event::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 501, + block: 502, } .into(), Event::Scheduled { @@ -288,6 +286,39 @@ fn schedule_should_emit_necessary_events() { }); } +#[test] +fn schedule_first_execution_cannot_be_next_block_manually() { + ExtBuilder::default() + .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) + .build() + .execute_with(|| { + //Arrange + let schedule = ScheduleBuilder::new().build(); + + //Act + set_block_number(500); + assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule.clone(), Some(501))); + + //Assert + expect_events(vec![ + Event::ExecutionPlanned { + id: 0, + who: ALICE, + block: 505, + } + .into(), + Event::Scheduled { + id: 0, + who: ALICE, + period: schedule.period, + total_amount: schedule.total_amount, + order: schedule.order, + } + .into(), + ]); + }); +} + #[test] fn schedule_should_emit_necessary_events_when_multiple_schedules_are_created() { ExtBuilder::default() @@ -311,7 +342,7 @@ fn schedule_should_emit_necessary_events_when_multiple_schedules_are_created() { Event::ExecutionPlanned { id: schedule_id, who: ALICE, - block: 501, + block: 502, } .into(), Event::Scheduled { @@ -440,39 +471,6 @@ fn buy_schedule_should_throw_error_when_total_budget_is_smaller_than_amount_in_p }); } -#[test] -fn buy_schedule_should_work_when_total_budget_is_equal_to_calculated_amount_in_plus_fee() { - ExtBuilder::default() - .with_endowed_accounts(vec![(ALICE, HDX, 100 * ONE)]) - .build() - .execute_with(|| { - //Arrange - let budget = CALCULATED_AMOUNT_IN_FOR_OMNIPOOL_BUY + BUY_DCA_FEE_IN_NATIVE; - - let schedule = ScheduleBuilder::new() - .with_total_amount(budget) - .with_period(ONE_HUNDRED_BLOCKS) - .with_order(Order::Buy { - asset_in: HDX, - asset_out: BTC, - amount_out: 10 * ONE, - max_amount_in: budget + BUY_DCA_FEE_IN_NATIVE, - route: create_bounded_vec(vec![Trade { - pool: PoolType::Omnipool, - asset_in: HDX, - asset_out: BTC, - }]), - }) - .build(); - - //Act - set_block_number(500); - - //Assert - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None),); - }); -} - #[test] fn schedule_should_fail_when_not_called_by_user() { ExtBuilder::default().build().execute_with(|| { @@ -484,28 +482,8 @@ fn schedule_should_fail_when_not_called_by_user() { }); } -#[test_case(1)] -#[test_case(499)] -#[test_case(500)] -fn schedule_should_fail_when_specified_next_block_is_not_greater_than_current_block(block: BlockNumberFor) { - ExtBuilder::default() - .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) - .build() - .execute_with(|| { - //Arrange - let schedule = ScheduleBuilder::new().build(); - set_block_number(500); - - //Act and assert - assert_noop!( - DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(block)), - Error::::BlockNumberIsNotInFuture - ); - }); -} - #[test] -fn schedule_should_schedule_for_consequent_block_when_next_block_is_full() { +fn schedule_should_schedule_for_consequent_block_when_specified_block_is_full() { ExtBuilder::default() .with_endowed_accounts(vec![(ALICE, HDX, 1000000 * ONE)]) .build() @@ -515,19 +493,27 @@ fn schedule_should_schedule_for_consequent_block_when_next_block_is_full() { for _ in RangeInclusive::new(1, 20) { let schedule = ScheduleBuilder::new().build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); } //Act let schedule = ScheduleBuilder::new().build(); let schedule_id = 20; - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); //Assert - let actual_schedule_ids = DCA::schedule_ids_per_block(501); + let actual_schedule_ids = DCA::schedule_ids_per_block(1000); assert_eq!(20, actual_schedule_ids.len()); - assert_scheduled_ids!(502, vec![schedule_id]); + assert_scheduled_ids!(1001, vec![schedule_id]); }); } @@ -542,16 +528,24 @@ fn schedule_should_schedule_for_after_consequent_block_when_both_next_block_and_ for _ in RangeInclusive::new(1, 40) { let schedule = ScheduleBuilder::new().build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); } //Act let schedule = ScheduleBuilder::new().build(); let schedule_id = 40; - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); //Assert - let block = 501; + let block = 1000; let actual_schedule_ids = DCA::schedule_ids_per_block(block); assert_eq!(20, actual_schedule_ids.len()); @@ -576,11 +570,15 @@ fn schedule_should_fail_when_there_is_no_free_consquent_blocks() { for _ in RangeInclusive::new(1, 220) { let schedule = ScheduleBuilder::new().build(); - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); + assert_ok!(DCA::schedule( + RuntimeOrigin::signed(ALICE), + schedule, + Option::Some(1000) + )); } //Check if all the blocks within radiuses are fully filled - let next_block = 501; + let next_block = 1000; let mut next_block_with_radius = next_block; for radius in GENERATED_SEARCH_RADIUSES { next_block_with_radius += radius; @@ -591,7 +589,7 @@ fn schedule_should_fail_when_there_is_no_free_consquent_blocks() { //Act and assert let schedule = ScheduleBuilder::new().build(); assert_noop!( - DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None), + DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::Some(1000)), Error::::NoFreeBlockFound ); }); @@ -759,14 +757,14 @@ fn schedule_should_fail_when_trade_amount_is_less_than_min_trading_limit() { } #[test] -fn sell_schedule_should_work_when_total_amount_is_equal_to_amount_in_plus_fee() { +fn sell_schedule_should_work_when_total_amount_is_equal_to_2times_amount_in_plus_fee() { ExtBuilder::default() .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) .build() .execute_with(|| { //Arrange let amount_in = ONE; - let total_amount = amount_in + SELL_DCA_FEE_IN_NATIVE; + let total_amount = 2 * (amount_in + SELL_DCA_FEE_IN_NATIVE); let schedule = ScheduleBuilder::new() .with_total_amount(total_amount) .with_order(Order::Sell { @@ -882,7 +880,7 @@ fn schedule_should_be_created_when_no_routes_specified() { } #[test] -fn thousands_of_dcas_should_be_schedules_on_a_specific_block_because_of_salt_added_to_block_search_randomness() { +fn thousands_of_dcas_can_be_scheduled_on_a_specific_block_because_of_salt_added_to_block_search_randomness() { ExtBuilder::default() .with_endowed_accounts(vec![(ALICE, HDX, 100000000000 * ONE)]) .build() diff --git a/pallets/dca/src/tests/terminate.rs b/pallets/dca/src/tests/terminate.rs index 7fb62ab3a..de29a0a97 100644 --- a/pallets/dca/src/tests/terminate.rs +++ b/pallets/dca/src/tests/terminate.rs @@ -49,31 +49,6 @@ fn terminate_should_remove_schedule_from_storage() { }); } -#[test] -fn terminate_should_terminate_schedule_planned_in_next_block_when_no_block_specified() { - ExtBuilder::default() - .with_endowed_accounts(vec![(ALICE, HDX, 10000 * ONE)]) - .build() - .execute_with(|| { - //Arrange - set_block_number(500); - let schedule = ScheduleBuilder::new().build(); - let schedule_id = 0; - assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); - - //Act - assert_ok!(DCA::terminate(RuntimeOrigin::root(), schedule_id, Option::None)); - - //Assert - expect_events(vec![Event::Terminated { - id: 0, - who: ALICE, - error: Error::::ManuallyTerminated.into(), - } - .into()]); - }); -} - #[test] fn terminate_should_unreserve_all_named_reserved() { ExtBuilder::default() @@ -206,7 +181,7 @@ fn terminate_should_pass_when_called_by_owner() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act and assert - assert_ok!(DCA::terminate(RuntimeOrigin::signed(ALICE), schedule_id, Some(501))); + assert_ok!(DCA::terminate(RuntimeOrigin::signed(ALICE), schedule_id, Some(502))); }); } @@ -260,7 +235,7 @@ fn terminate_should_pass_when_called_by_technical_origin() { assert_ok!(DCA::schedule(RuntimeOrigin::signed(ALICE), schedule, Option::None)); //Act and assert - assert_ok!(DCA::terminate(RuntimeOrigin::root(), schedule_id, Some(501))); + assert_ok!(DCA::terminate(RuntimeOrigin::root(), schedule_id, Some(502))); }); } diff --git a/pallets/democracy/Cargo.toml b/pallets/democracy/Cargo.toml index e81a14033..b8a90367d 100644 --- a/pallets/democracy/Cargo.toml +++ b/pallets/democracy/Cargo.toml @@ -14,13 +14,13 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ - "derive", + "derive", ] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0.188", features = ["derive"], optional = true } log = { workspace = true } -frame-benchmarking = { workspace = true, optional = true} +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-io = { workspace = true } @@ -34,36 +34,36 @@ pallet-scheduler = { workspace = true } pallet-preimage = { workspace = true } [features] -default = [ "std" ] +default = ["std"] std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-preimage/std", - "pallet-scheduler/std", - "scale-info/std", - "serde", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-preimage/std", + "pallet-scheduler/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-preimage/runtime-benchmarks", - "pallet-scheduler/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-preimage/try-runtime", - "pallet-scheduler/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-preimage/try-runtime", + "pallet-scheduler/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/pallets/democracy/src/lib.rs b/pallets/democracy/src/lib.rs index 5e6e37b47..c8f1797d2 100644 --- a/pallets/democracy/src/lib.rs +++ b/pallets/democracy/src/lib.rs @@ -181,6 +181,7 @@ pub mod weights; use crate::traits::DemocracyHooks; pub use conviction::Conviction; pub use pallet::*; +use sp_std::vec; pub use types::{ Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus, Tally, UnvoteScope, }; diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index f30b7aedf..eeafc0f93 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "248.0.0" +version = "249.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" @@ -62,7 +62,7 @@ pallet-multisig = { workspace = true } pallet-democracy = { workspace = true } pallet-elections-phragmen = { workspace = true } pallet-uniques = { workspace = true } -pallet-message-queue= { workspace = true } +pallet-message-queue = { workspace = true } pallet-state-trie-migration = { workspace = true } # Warehouse dependencies @@ -116,7 +116,7 @@ pallet-xcm = { workspace = true } polkadot-xcm = { workspace = true } xcm-executor = { workspace = true } xcm-builder = { workspace = true } -polkadot-runtime-common= { workspace = true } +polkadot-runtime-common = { workspace = true } # Substrate dependencies frame-benchmarking = { workspace = true, optional = true } @@ -143,7 +143,7 @@ sp-transaction-pool = { workspace = true } sp-version = { workspace = true } sp-trie = { workspace = true } sp-io = { workspace = true } -primitive-types = { workspace = true } +primitive-types = { workspace = true } # Frontier fp-rpc = { workspace = true } @@ -324,7 +324,7 @@ std = [ "polkadot-runtime-common/std", "pallet-state-trie-migration/std", ] -try-runtime= [ +try-runtime = [ "frame-try-runtime", "frame-executive/try-runtime", "frame-system/try-runtime", diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 03eda657b..af43db7b7 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -42,7 +42,7 @@ use primitives::constants::{ currency::{NATIVE_EXISTENTIAL_DEPOSIT, UNITS}, time::DAYS, }; -use sp_runtime::{traits::Zero, DispatchError, DispatchResult, FixedPointNumber}; +use sp_runtime::{traits::Zero, DispatchError, DispatchResult, FixedPointNumber, Percent}; use core::ops::RangeInclusive; use frame_support::{ @@ -738,6 +738,9 @@ parameter_types! { pub MinBudgetInNativeCurrency: Balance = 1000 * UNITS; pub MaxSchedulesPerBlock: u32 = 20; pub MaxPriceDifference: Permill = Permill::from_rational(15u32, 1000u32); + pub MaxConfigurablePriceDifference: Permill = Permill::from_percent(5); + pub MinimalPeriod: u32 = 5; + pub BumpChance: Percent = Percent::from_percent(17); pub NamedReserveId: NamedReserveIdentifier = *b"dcaorder"; pub MaxNumberOfRetriesOnError: u8 = 3; pub DCAOraclePeriod: OraclePeriod = OraclePeriod::Short; @@ -773,6 +776,9 @@ impl pallet_dca::Config for Runtime { type RouteExecutor = pallet_route_executor::DummyRouter; type RouteProvider = Router; type MaxPriceDifferenceBetweenBlocks = MaxPriceDifference; + type MaxConfigurablePriceDifferenceBetweenBlocks = MaxConfigurablePriceDifference; + type MinimalPeriod = MinimalPeriod; + type BumpChance = BumpChance; type MaxSchedulePerBlock = MaxSchedulesPerBlock; type MaxNumberOfRetriesOnError = MaxNumberOfRetriesOnError; type NativeAssetId = NativeAssetId; diff --git a/runtime/hydradx/src/benchmarking/dca.rs b/runtime/hydradx/src/benchmarking/dca.rs index 9f5153d84..02af52cc6 100644 --- a/runtime/hydradx/src/benchmarking/dca.rs +++ b/runtime/hydradx/src/benchmarking/dca.rs @@ -65,7 +65,7 @@ fn schedule_fake( ) -> Schedule { let schedule1: Schedule = Schedule { owner, - period: 3u32, + period: 5u32, total_amount: 1100 * ONE, max_retries: None, stability_threshold: None, @@ -97,7 +97,7 @@ fn schedule_buy_fake( ) -> Schedule { let schedule1: Schedule = Schedule { owner, - period: 3u32, + period: 5u32, total_amount: 2000 * ONE, max_retries: None, stability_threshold: None, @@ -125,7 +125,7 @@ fn schedule_sell_fake( ) -> Schedule { let schedule1: Schedule = Schedule { owner, - period: 3u32, + period: 5u32, total_amount: 2000 * ONE, max_retries: None, stability_threshold: None, @@ -197,7 +197,7 @@ runtime_benchmarks! { fund_treasury()?; let schedule1 = schedule_buy_fake(seller.clone(), HDX, DAI, amount_buy); - let execution_block = 1001u32; + let execution_block = 1005u32; assert_ok!(DCA::schedule(RawOrigin::Signed(seller.clone()).into(), schedule1.clone(), Option::Some(execution_block))); @@ -211,7 +211,7 @@ runtime_benchmarks! { //Make sure that we have other schedules planned in the block where the benchmark schedule is planned, leading to worst case //We leave only one slot - let schedule_period = 3; + let schedule_period = 5; let next_block_to_replan = execution_block + schedule_period; let number_of_all_schedules = MaxSchedulesPerBlock::get() + MaxSchedulesPerBlock::get() * RETRY_TO_SEARCH_FOR_FREE_BLOCK - 1; for i in 0..number_of_all_schedules { @@ -239,7 +239,7 @@ runtime_benchmarks! { fund_treasury()?; //Fund treasury with some HDX to prevent BelowMinimum issue due to low fee let schedule1 = schedule_sell_fake(seller.clone(), HDX, DAI, amount_sell); - let execution_block = 1001u32; + let execution_block = 1005u32; assert_ok!(DCA::schedule(RawOrigin::Signed(seller.clone()).into(), schedule1.clone(), Option::Some(execution_block))); @@ -253,7 +253,7 @@ runtime_benchmarks! { //Make sure that we have other schedules planned in the block where the benchmark schedule is planned, leading to worst case //We leave only one slot - let schedule_period = 3; + let schedule_period = 5; let next_block_to_replan = execution_block + schedule_period; let number_of_all_schedules = MaxSchedulesPerBlock::get() + MaxSchedulesPerBlock::get() * RETRY_TO_SEARCH_FOR_FREE_BLOCK - 1; for i in 0..number_of_all_schedules { @@ -345,7 +345,7 @@ runtime_benchmarks! { let schedule1: Schedule = Schedule { owner:caller.clone() , - period: 3u32, + period: 5u32, total_amount: 1100 * ONE, max_retries: None, stability_threshold: None, @@ -363,7 +363,7 @@ runtime_benchmarks! { }, }; - let execution_block = 100u32; + let execution_block = 105u32; //We fill blocks with schedules leaving only one place let schedule_2 = schedule_fake(caller.clone(), HDX, DAI, amount_sell); @@ -397,7 +397,7 @@ runtime_benchmarks! { let execution_block = 100u32; assert_ok!(DCA::schedule(RawOrigin::Signed(caller).into(), schedule1, Option::Some(execution_block))); - }: _(RawOrigin::Root, schedule_id, None) + }: _(RawOrigin::Root, schedule_id, Some(105)) verify { assert!(>::get::(schedule_id).is_none()); } diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 2e34909ec..a1c2b9bf0 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -113,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 248, + spec_version: 249, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1,