Skip to content

Commit

Permalink
add configurable origins and unlock extrinsic
Browse files Browse the repository at this point in the history
  • Loading branch information
Roznovjak committed Jul 20, 2023
1 parent bc29fba commit 2a6f8e6
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 2 deletions.
32 changes: 31 additions & 1 deletion pallets/bonds/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ pub mod pallet {
/// Min number of blocks for maturity.
type MinMaturity: Get<Moment>;

/// The origin which can issue new bonds.
type IssueOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// The origin which can issue new bonds.
type UnlockOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Protocol Fee for
type ProtocolFee: Get<Permill>;

Expand Down Expand Up @@ -147,6 +153,8 @@ pub mod pallet {
bond_id: T::AssetId,
amount: T::Balance,
},
/// Bonds were unlocked
BondsUnlocked { bond_id: T::AssetId },
}

#[pallet::error]
Expand Down Expand Up @@ -190,7 +198,7 @@ pub mod pallet {
// not covered in the tests. Create an asset with empty name should always work
let bond_asset_id = T::AssetRegistry::create_bond_asset(&vec![], asset_details.existential_deposit)?;

let fee = T::ProtocolFee::get().mul_ceil(amount); // TODO
let fee = T::ProtocolFee::get().mul_ceil(amount); // TODO: check
let amount_without_fee = amount.checked_sub(&fee).ok_or(ArithmeticError::Overflow)?;
let pallet_account = Self::account_id();

Expand Down Expand Up @@ -254,6 +262,28 @@ pub mod pallet {

Ok(())
}

///
#[pallet::call_index(2)]
#[pallet::weight(<T as Config>::WeightInfo::redeem())]
pub fn unlock(origin: OriginFor<T>, bond_id: T::AssetId) -> DispatchResult {
T::UnlockOrigin::ensure_origin(origin)?;

RegisteredBonds::<T>::try_mutate_exists(bond_id, |maybe_bond_data| -> DispatchResult {
let bond_data = maybe_bond_data.as_mut().ok_or(Error::<T>::BondNotRegistered)?;

let now = T::TimestampProvider::now();
// do nothing if the bonds are already mature
if bond_data.maturity > now {
bond_data.maturity = now;

Self::deposit_event(Event::BondsUnlocked { bond_id });
}
Ok(())
})?;

Ok(())
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion pallets/bonds/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::*;

use frame_support::traits::{ConstU128, Everything, GenesisBuild};
use frame_support::{construct_runtime, parameter_types, traits::ConstU32};
use frame_system::{EnsureRoot, EnsureSigned};
use hydradx_traits::{BondRegistry, Registry};
use orml_traits::parameter_type_with_key;
use sp_core::H256;
Expand Down Expand Up @@ -88,6 +89,8 @@ impl Config for Test {
type TimestampProvider = DummyTimestampProvider<Test>;
type PalletId = BondsPalletId;
type MinMaturity = MinMaturity;
type IssueOrigin = EnsureSigned<AccountId>;
type UnlockOrigin = EnsureRoot<AccountId>;
type ProtocolFee = ProtocolFee;
type FeeReceiver = TreasuryAccount;
type WeightInfo = ();
Expand Down Expand Up @@ -122,7 +125,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type MaxConsumers = ConstU32<16>;
}

impl pallet_balances::Config for Test {
Expand Down
1 change: 1 addition & 0 deletions pallets/bonds/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod issue;
pub mod mock;
mod redeem;
mod unlock;
43 changes: 43 additions & 0 deletions pallets/bonds/src/tests/redeem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,49 @@ fn fully_redeem_bonds_should_work_when_with_non_zero_fee() {
});
}

#[test]
fn redeem_bonds_should_work_when_redeemed_from_non_issuer_account() {
ExtBuilder::default().build().execute_with(|| {
// Arrange
let now = DummyTimestampProvider::<Test>::now();
let maturity = now.checked_add(MONTH).unwrap();
let amount = ONE;
let redeem_amount = ONE.checked_div(4).unwrap();
let bond_id = next_asset_id();

// Act
assert_ok!(Bonds::issue(RuntimeOrigin::signed(ALICE), HDX, amount, maturity));
assert_ok!(Tokens::transfer(
RuntimeOrigin::signed(ALICE),
BOB,
bond_id,
redeem_amount
));

System::set_block_number(2 * MONTH);

assert_ok!(Bonds::redeem(RuntimeOrigin::signed(BOB), bond_id, redeem_amount));

// Assert
expect_events(vec![Event::BondsRedeemed {
who: BOB,
bond_id,
amount: redeem_amount,
}
.into()]);

assert_eq!(Tokens::free_balance(HDX, &BOB), redeem_amount);
assert_eq!(Tokens::free_balance(bond_id, &BOB), 0);

assert_eq!(Tokens::free_balance(HDX, &<Test as Config>::FeeReceiver::get()), 0);

assert_eq!(
Tokens::free_balance(HDX, &Bonds::account_id()),
amount.checked_sub(redeem_amount).unwrap()
);
});
}

#[test]
fn redeem_bonds_should_fail_when_bond_not_exists() {
ExtBuilder::default().build().execute_with(|| {
Expand Down
90 changes: 90 additions & 0 deletions pallets/bonds/src/tests/unlock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// This file is part of HydraDX.

// Copyright (C) 2020-2022 Intergalactic, Limited (GIB).
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::tests::mock::*;
use crate::*;
use frame_support::{assert_noop, assert_ok, assert_storage_noop};
pub use pretty_assertions::{assert_eq, assert_ne};
use sp_runtime::DispatchError::BadOrigin;

#[test]
fn unlock_should_work_when_bonds_are_not_mature() {
ExtBuilder::default().build().execute_with(|| {
// Arrange
let now = DummyTimestampProvider::<Test>::now();
let maturity = now.checked_add(MONTH).unwrap();
let amount = ONE;
let bond_id = next_asset_id();

// Act
assert_ok!(Bonds::issue(RuntimeOrigin::signed(ALICE), HDX, amount, maturity,));

System::set_block_number(2 * WEEK);
let now = DummyTimestampProvider::<Test>::now();

assert_ok!(Bonds::unlock(RuntimeOrigin::root(), bond_id));

// Assert
expect_events(vec![Event::BondsUnlocked { bond_id }.into()]);

assert_eq!(
Bonds::bonds(bond_id).unwrap(),
Bond {
maturity: now,
asset_id: HDX,
amount,
}
);
});
}

#[test]
fn unlock_should_be_storage_noop_if_bonds_are_already_mature() {
ExtBuilder::default().build().execute_with(|| {
// Arrange
let now = DummyTimestampProvider::<Test>::now();
let maturity = now.checked_add(MONTH).unwrap();
let amount = ONE;
let bond_id = next_asset_id();

// Act
assert_ok!(Bonds::issue(RuntimeOrigin::signed(ALICE), HDX, amount, maturity,));

System::set_block_number(2 * MONTH);

// Assert
assert_storage_noop!(Bonds::unlock(RuntimeOrigin::root(), bond_id).unwrap());
});
}

#[test]
fn unlock_should_fail_when_called_from_wrong_origin() {
ExtBuilder::default().build().execute_with(|| {
// Arrange
let now = DummyTimestampProvider::<Test>::now();
let maturity = now.checked_add(MONTH).unwrap();
let amount = ONE;
let bond_id = next_asset_id();

// Act
assert_ok!(Bonds::issue(RuntimeOrigin::signed(ALICE), HDX, amount, maturity,));

System::set_block_number(2 * MONTH);

assert_noop!(Bonds::unlock(RuntimeOrigin::signed(ALICE), bond_id), BadOrigin);
});
}
9 changes: 9 additions & 0 deletions pallets/bonds/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use sp_std::marker::PhantomData;
pub trait WeightInfo {
fn issue() -> Weight;
fn redeem() -> Weight;
fn unlock() -> Weight;
}

/// Weights for pallet_otc using the hydraDX node and recommended hardware.
Expand All @@ -62,6 +63,10 @@ impl<T: frame_system::Config> WeightInfo for HydraWeight<T> {
fn redeem() -> Weight {
Weight::zero()
}

fn unlock() -> Weight {
Weight::zero()
}
}

// For backwards compatibility and tests
Expand All @@ -73,4 +78,8 @@ impl WeightInfo for () {
fn redeem() -> Weight {
Weight::zero()
}

fn unlock() -> Weight {
Weight::zero()
}
}

0 comments on commit 2a6f8e6

Please sign in to comment.