diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 5bd717b15..6fb3e1603 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -30,6 +30,10 @@ services: image: parallelfinance/stake-client:latest command: sync ledger --relay-ws=ws://relaychain-bob:9944 --para-ws=ws://parachain-${PARA_ID}-0:9944 --derivative-index 1 restart: always + stake-client-fast-match-unstake: + image: parallelfinance/stake-client:latest + command: fast-match-unstake --para-ws=ws://parachain-${PARA_ID}-0:9944 --batch-size 30 + restart: always liquidation-client: image: parallelfinance/liquidation-client:latest command: --endpoint ws://parachain-${PARA_ID}-0:9944 diff --git a/pallets/streaming/src/lib.rs b/pallets/streaming/src/lib.rs index 9425de4bd..4f2e666b6 100644 --- a/pallets/streaming/src/lib.rs +++ b/pallets/streaming/src/lib.rs @@ -83,6 +83,14 @@ pub mod pallet { #[pallet::constant] type MaxFinishedStreamsCount: Get; + /// Currency id of the native token + #[pallet::constant] + type NativeCurrencyId: Get>; + + /// The essential balance for an existed account + #[pallet::constant] + type NativeExistentialDeposit: Get; + /// The Unix time type UnixTime: UnixTime; @@ -335,7 +343,7 @@ pub mod pallet { pub fn withdraw( origin: OriginFor, stream_id: StreamId, - amount: BalanceOf, + mut amount: BalanceOf, ) -> DispatchResultWithPostInfo { let recipient = ensure_signed(origin)?; @@ -349,6 +357,12 @@ pub mod pallet { Error::::InsufficientStreamBalance ); + if stream.asset_id == T::NativeCurrencyId::get() + && amount.saturating_add(T::NativeExistentialDeposit::get()) >= recipient_balance + { + amount = recipient_balance + } + stream.try_deduct(amount)?; stream.try_complete()?; Streams::::insert(stream_id, stream.clone()); diff --git a/pallets/streaming/src/mock.rs b/pallets/streaming/src/mock.rs index 5bb6ff48c..53efe25e7 100644 --- a/pallets/streaming/src/mock.rs +++ b/pallets/streaming/src/mock.rs @@ -90,7 +90,7 @@ impl pallet_timestamp::Config for Test { } parameter_types! { - pub const ExistentialDeposit: Balance = 1; + pub const ExistentialDeposit: Balance = 1_000; pub const MaxLocks: u32 = 50; } @@ -158,6 +158,8 @@ impl Config for Test { type Assets = CurrencyAdapter; type UpdateOrigin = EnsureRoot; type WeightInfo = (); + type NativeCurrencyId = NativeCurrencyId; + type NativeExistentialDeposit = ExistentialDeposit; } pub fn dollar(d: u128) -> u128 { diff --git a/pallets/streaming/src/tests.rs b/pallets/streaming/src/tests.rs index 1edd6ebc1..99b1bc17a 100644 --- a/pallets/streaming/src/tests.rs +++ b/pallets/streaming/src/tests.rs @@ -223,14 +223,14 @@ fn withdraw_with_slower_rate_works() { assert_eq!(stream.sender_balance().unwrap(), 7769230769240); assert_eq!(stream.recipient_balance().unwrap(), 93230769230760); - // Bob withdraw all available balance (93230769230759 + 1 = 93230769230760) + // Bob withdraw all available balance (93230769229759 + 1 + 1000 = 93230769230760) assert_ok!(Streaming::withdraw( Origin::signed(BOB), stream_id_0, - 93230769230759 + 93230769229759 )); // withdraw a small value should be ok - assert_ok!(Streaming::withdraw(Origin::signed(BOB), stream_id_0, 1)); + assert_ok!(Streaming::withdraw(Origin::signed(BOB), stream_id_0, 1001)); stream = Streams::::get(stream_id_0).unwrap(); assert_eq!(stream.sender_balance().unwrap(), 7769230769240); @@ -249,10 +249,10 @@ fn withdraw_with_slower_rate_works() { assert_ok!(Streaming::withdraw( Origin::signed(BOB), stream_id_0, - 7769230769239 + 7769230768239 )); - assert_ok!(Streaming::withdraw(Origin::signed(BOB), stream_id_0, 1)); + assert_ok!(Streaming::withdraw(Origin::signed(BOB), stream_id_0, 1001)); // Stream is removed as balance goes zero assert_eq!( @@ -262,6 +262,92 @@ fn withdraw_with_slower_rate_works() { }); } +#[test] +fn withdraw_under_ed_works() { + new_test_ext().execute_with(|| { + assert_ok!(Streaming::set_minimum_deposit( + Origin::root(), + HKO, + dollar(10) + )); + let before_bob = ::Assets::balance(HKO, &BOB); + assert_eq!(TimestampPallet::now(), 6000); + // Alice creates stream 101 dollars to Bob + let stream_id_0 = NextStreamId::::get(); + assert_ok!(Streaming::create( + Origin::signed(ALICE), + BOB, + dollar(101), + HKO, + 6, + 19, + true, + )); + // rate_per_secs: 101 / (19-6) = 7769230769230 + let stream = Streams::::get(stream_id_0).unwrap(); + assert_eq!( + stream, + Stream::new(dollar(101), HKO, 7769230769230, ALICE, BOB, 6, 19, true,) + ); + + // Dave cannot access + assert_err!( + Streaming::withdraw(Origin::signed(DAVE), 0, 1), + Error::::NotTheRecipient + ); + + // passed 11 seconds + TimestampPallet::set_timestamp(17000); + assert_eq!(stream.delta_of(), Ok(11)); + // Should be 15538461538460, but add 10(amount) dut to accuracy loss + assert_eq!(stream.sender_balance().unwrap(), 15538461538470); + // per_rate_secs * 11 = 85461538461530 + assert_eq!(stream.recipient_balance().unwrap(), 85461538461530); + + // passed 12 seconds + TimestampPallet::set_timestamp(18000); + let mut stream = Streams::::get(stream_id_0).unwrap(); + // delta of should only increase until end_time + assert_eq!(stream.delta_of(), Ok(12)); + // Should be 7769230769230, but add 10(amount) dut to accuracy loss + assert_eq!(stream.sender_balance().unwrap(), 7769230769240); + assert_eq!(stream.recipient_balance().unwrap(), 93230769230760); + + // Bob withdraw balance + let ed = ::NativeExistentialDeposit::get(); + assert_ok!(Streaming::withdraw( + Origin::signed(BOB), + stream_id_0, + 93230769230760 - ed + 1 + )); + + stream = Streams::::get(stream_id_0).unwrap(); + assert_eq!(stream.sender_balance().unwrap(), 7769230769240); + assert_eq!(stream.recipient_balance().unwrap(), 0); + + // passed 14 seconds + TimestampPallet::set_timestamp(20000); + stream = Streams::::get(stream_id_0).unwrap(); + assert_eq!(stream.delta_of(), Ok(13)); + assert_eq!(stream.sender_balance().unwrap(), 0); + // Reaches the end_time, returned amount should contains the accuracy loss(10) + // recipient_balance = 7769230769230 + 10 + assert_eq!(stream.recipient_balance().unwrap(), 7769230769240); + + // Bob withdraw remaining_balance + assert_ok!(Streaming::withdraw( + Origin::signed(BOB), + stream_id_0, + 7769230769240 - ed + 1 + )); + // Stream is removed as balance goes zero + assert_eq!( + ::Assets::balance(HKO, &BOB) - before_bob, + dollar(101) + ); + }); +} + #[test] fn cancel_works_with_withdrawal() { new_test_ext().execute_with(|| { diff --git a/runtime/heiko/src/lib.rs b/runtime/heiko/src/lib.rs index d42a5e7dc..e77e544ca 100644 --- a/runtime/heiko/src/lib.rs +++ b/runtime/heiko/src/lib.rs @@ -1994,6 +1994,8 @@ impl pallet_streaming::Config for Runtime { type UnixTime = Timestamp; type UpdateOrigin = EnsureRootOrMoreThanHalfGeneralCouncil; type WeightInfo = weights::pallet_streaming::WeightInfo; + type NativeCurrencyId = NativeCurrencyId; + type NativeExistentialDeposit = ExistentialDeposit; } parameter_types! { diff --git a/runtime/kerria/src/lib.rs b/runtime/kerria/src/lib.rs index 97983b893..4e712c82b 100644 --- a/runtime/kerria/src/lib.rs +++ b/runtime/kerria/src/lib.rs @@ -2137,6 +2137,8 @@ impl pallet_streaming::Config for Runtime { type UnixTime = Timestamp; type UpdateOrigin = EnsureRootOrMoreThanHalfGeneralCouncil; type WeightInfo = weights::pallet_streaming::WeightInfo; + type NativeCurrencyId = NativeCurrencyId; + type NativeExistentialDeposit = ExistentialDeposit; } parameter_types! { diff --git a/runtime/parallel/src/lib.rs b/runtime/parallel/src/lib.rs index cfbc99534..b700036de 100644 --- a/runtime/parallel/src/lib.rs +++ b/runtime/parallel/src/lib.rs @@ -1985,6 +1985,8 @@ impl pallet_streaming::Config for Runtime { type UnixTime = Timestamp; type UpdateOrigin = EnsureRootOrMoreThanHalfGeneralCouncil; type WeightInfo = weights::pallet_streaming::WeightInfo; + type NativeCurrencyId = NativeCurrencyId; + type NativeExistentialDeposit = ExistentialDeposit; } parameter_types! { diff --git a/runtime/vanilla/src/lib.rs b/runtime/vanilla/src/lib.rs index db9a212ba..a596aabdf 100644 --- a/runtime/vanilla/src/lib.rs +++ b/runtime/vanilla/src/lib.rs @@ -2176,6 +2176,8 @@ impl pallet_streaming::Config for Runtime { type UnixTime = Timestamp; type UpdateOrigin = EnsureRootOrMoreThanHalfGeneralCouncil; type WeightInfo = weights::pallet_streaming::WeightInfo; + type NativeCurrencyId = NativeCurrencyId; + type NativeExistentialDeposit = ExistentialDeposit; } parameter_types! {