Skip to content

Commit df5ef0e

Browse files
Settlement Locking 2nd Approach (#1820)
* Add lock instruction; Allow manual execution for locked instructions; Add simplified transfer * Add lock_instruction common benchmark * Improve benchmarks * Add unit tests * Add sample weights * Add rpc calls * Remove useless test * Fix benchmarks
1 parent 6d1de4d commit df5ef0e

File tree

21 files changed

+2551
-971
lines changed

21 files changed

+2551
-971
lines changed

pallets/asset/src/lib.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,8 +3293,8 @@ impl<T: AssetConfig> Pallet<T> {
32933293
)?;
32943294

32953295
// Updates the balance in the asset pallet
3296-
let sender_new_balance = sender_current_balance - transfer_value;
3297-
let receiver_new_balance = receiver_current_balance + transfer_value;
3296+
let sender_new_balance = sender_current_balance.saturating_sub(transfer_value);
3297+
let receiver_new_balance = receiver_current_balance.saturating_add(transfer_value);
32983298
BalanceOf::<T>::insert(asset_id, sender_portfolio.did, sender_new_balance);
32993299
BalanceOf::<T>::insert(asset_id, receiver_portfolio.did, receiver_new_balance);
33003300

@@ -3461,6 +3461,39 @@ impl<T: AssetConfig> Pallet<T> {
34613461
}
34623462
})
34633463
}
3464+
3465+
/// Transfers `transfer_value` of `asset_id` from `sender_pid` to `receiver_pid`.
3466+
/// Note: This functions skips all compliance and statistics checks, only checking for balance.
3467+
pub fn simplified_fungible_transfer(
3468+
asset_id: AssetId,
3469+
sender_pid: PortfolioId,
3470+
receiver_pid: PortfolioId,
3471+
transfer_value: Balance,
3472+
inst_id: InstructionId,
3473+
inst_memo: Option<Memo>,
3474+
caller_did: IdentityId,
3475+
weight_meter: &mut WeightMeter,
3476+
) -> DispatchResult {
3477+
ensure!(
3478+
BalanceOf::<T>::get(&asset_id, &sender_pid.did) >= transfer_value,
3479+
Error::<T>::InsufficientBalance
3480+
);
3481+
Portfolio::<T>::ensure_portfolio_validity(&receiver_pid)?;
3482+
Portfolio::<T>::ensure_sufficient_balance(&sender_pid, &asset_id, transfer_value)?;
3483+
3484+
Self::unverified_transfer_asset(
3485+
sender_pid,
3486+
receiver_pid,
3487+
asset_id,
3488+
transfer_value,
3489+
Some(inst_id),
3490+
inst_memo,
3491+
caller_did,
3492+
weight_meter,
3493+
)?;
3494+
3495+
Ok(())
3496+
}
34643497
}
34653498

34663499
//==========================================================================

pallets/nft/src/lib.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,10 @@ impl<T: Config> Pallet<T> {
712712
// Update the balance of the sender and the receiver
713713
let transferred_amount = nfts.len() as u64;
714714
NumberOfNFTs::<T>::mutate(nfts.asset_id(), sender_portfolio.did, |balance| {
715-
*balance -= transferred_amount
715+
*balance = balance.saturating_sub(transferred_amount)
716716
});
717717
NumberOfNFTs::<T>::mutate(nfts.asset_id(), receiver_portfolio.did, |balance| {
718-
*balance += transferred_amount
718+
*balance = balance.saturating_add(transferred_amount)
719719
});
720720
// Update the portfolio of the sender and the receiver
721721
for nft_id in nfts.ids() {
@@ -870,6 +870,43 @@ impl<T: Config> Pallet<T> {
870870
}
871871
})
872872
}
873+
874+
/// Transfers all `nfts` from `sender_pid` to `receiver_pid`.
875+
/// Note: This functions skips all compliance checks and only checks for onwership.
876+
pub fn simplified_nft_transfer(
877+
sender_pid: PortfolioId,
878+
receiver_pid: PortfolioId,
879+
nfts: NFTs,
880+
inst_id: InstructionId,
881+
inst_memo: Option<Memo>,
882+
caller_did: IdentityId,
883+
) -> DispatchResult {
884+
Portfolio::<T>::ensure_portfolio_validity(&receiver_pid)?;
885+
Self::ensure_sender_owns_nfts(&sender_pid, &nfts)?;
886+
Self::unverified_nfts_transfer(&sender_pid, &receiver_pid, &nfts);
887+
Self::deposit_event(Event::NFTPortfolioUpdated(
888+
caller_did,
889+
nfts,
890+
Some(sender_pid),
891+
Some(receiver_pid),
892+
PortfolioUpdateReason::Transferred {
893+
instruction_id: Some(inst_id),
894+
instruction_memo: inst_memo,
895+
},
896+
));
897+
Ok(())
898+
}
899+
900+
/// Returns `Ok` if `sender_pid` holds all nfts.
901+
fn ensure_sender_owns_nfts(sender_pid: &PortfolioId, nfts: &NFTs) -> DispatchResult {
902+
for nft_id in nfts.ids() {
903+
ensure!(
904+
PortfolioNFT::<T>::contains_key(sender_pid, (nfts.asset_id(), nft_id)),
905+
Error::<T>::InvalidNFTTransferNFTNotOwned
906+
);
907+
}
908+
Ok(())
909+
}
873910
}
874911

875912
impl<T: Config> NFTTrait<T::RuntimeOrigin> for Pallet<T> {

pallets/runtime/common/src/runtime.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ macro_rules! misc_pallet_impls {
556556
type MaxNumberOfPortfolios = MaxNumberOfPortfolios;
557557
type MaxNumberOfVenueSigners = MaxNumberOfVenueSigners;
558558
type MaxInstructionMediators = MaxInstructionMediators;
559+
type MaximumLockPeriod = MaximumLockPeriod;
559560
}
560561

561562
impl pallet_sto::Config for Runtime {
@@ -735,17 +736,17 @@ macro_rules! runtime_apis {
735736
use frame_support::dispatch::result::Result as FrameResult;
736737
use node_rpc_runtime_api::asset as rpc_api_asset;
737738

738-
use pallet_identity::types::{AssetDidResult, CddStatus, RpcDidRecords, DidStatus, KeyIdentityData};
739+
use pallet_identity::types::{AssetDidResult, CddStatus, RpcDidRecords};
740+
use pallet_identity::types::{DidStatus, KeyIdentityData};
739741
use pallet_pips::{Vote, VoteCount};
740742
use pallet_protocol_fee_rpc_runtime_api::CappedFee;
741-
use polymesh_primitives::asset::AssetId;
742-
use polymesh_primitives::settlement::{InstructionId, ExecuteInstructionInfo, AffirmationCount};
743+
use polymesh_primitives::asset::{AssetId, CheckpointId};
744+
use polymesh_primitives::settlement::{ AssetCount, AffirmationCount};
745+
use polymesh_primitives::settlement::{InstructionId, ExecuteInstructionInfo};
743746
use polymesh_primitives::transfer_compliance::TransferCondition;
744747
use polymesh_primitives::compliance_manager::{AssetComplianceResult, ComplianceReport};
745-
use polymesh_primitives::{
746-
asset::CheckpointId, IdentityId, Index, NFTs,PortfolioId, Signatory, Ticker,
747-
WeightMeter, IdentityClaim
748-
};
748+
use polymesh_primitives::{IdentityId, Index, NFTs, PortfolioId};
749+
use polymesh_primitives::{Signatory, Ticker, WeightMeter, IdentityClaim};
749750

750751
/// The address format for describing accounts.
751752
pub type Address = <Indices as StaticLookup>::Source;
@@ -1161,9 +1162,9 @@ macro_rules! runtime_apis {
11611162
impl node_rpc_runtime_api::settlement::SettlementApi<Block> for Runtime {
11621163
#[inline]
11631164
fn get_execute_instruction_info(
1164-
instruction_id: &InstructionId
1165+
instruction_id: InstructionId
11651166
) -> Option<ExecuteInstructionInfo> {
1166-
Settlement::execute_instruction_info(instruction_id)
1167+
Settlement::manual_execution_weight(instruction_id)
11671168
}
11681169

11691170
#[inline]
@@ -1182,10 +1183,18 @@ macro_rules! runtime_apis {
11821183

11831184
#[inline]
11841185
fn get_execute_instruction_report(instruction_id: InstructionId) -> Vec<DispatchError> {
1185-
let mut weight_meter = WeightMeter::max_limit_no_minimum();
1186-
Settlement::execute_instruction_report(&instruction_id, &mut weight_meter)
1186+
Settlement::execute_instruction_report(&instruction_id)
11871187
}
11881188

1189+
#[inline]
1190+
fn lock_instruction_weight(instruction_id: InstructionId) -> Result<Weight, DispatchError> {
1191+
Settlement::lock_instruction_weight(instruction_id)
1192+
}
1193+
1194+
#[inline]
1195+
fn instruction_asset_count(instruction_id: InstructionId) -> AssetCount {
1196+
Settlement::instruction_asset_count(&instruction_id)
1197+
}
11891198
}
11901199

11911200
impl node_rpc_runtime_api::compliance::ComplianceApi<Block> for Runtime {

pallets/runtime/develop/src/runtime.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ parameter_types! {
9494
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
9595
pub const MaxNumberOfVenueSigners: u32 = 50;
9696
pub const MaxInstructionMediators: u32 = 4;
97+
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours
9798

9899
// Multisig
99100
pub const MaxMultiSigSigners: u32 = 50;

pallets/runtime/mainnet/src/runtime.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ parameter_types! {
9292
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
9393
pub const MaxNumberOfVenueSigners: u32 = 50;
9494
pub const MaxInstructionMediators: u32 = 4;
95+
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours
9596

9697
// Multisig
9798
pub const MaxMultiSigSigners: u32 = 50;

pallets/runtime/testnet/src/runtime.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ parameter_types! {
9595
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
9696
pub const MaxNumberOfVenueSigners: u32 = 50;
9797
pub const MaxInstructionMediators: u32 = 4;
98+
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours
9899

99100
// Multisig
100101
pub const MaxMultiSigSigners: u32 = 50;

pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs

Lines changed: 30 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,113 +3,79 @@ use sp_keyring::AccountKeyring;
33

44
use pallet_asset::BalanceOf;
55
use pallet_portfolio::PortfolioLockedAssets;
6-
use pallet_settlement::{
7-
AffirmsReceived, Error, Event, InstructionAffirmsPending, InstructionDetails,
8-
InstructionLegStatus, InstructionLegs, InstructionMediatorsAffirmations, InstructionStatuses,
9-
OffChainAffirmations, UserAffirmations, VenueInstructions,
10-
};
11-
use polymesh_primitives::settlement::{
12-
AffirmationStatus, Instruction, InstructionId, InstructionStatus, Leg, LegId, SettlementType,
13-
};
6+
use pallet_settlement::{AffirmsReceived, Error, Event, InstructionAffirmsPending};
7+
use pallet_settlement::{InstructionDetails, InstructionLegStatus, InstructionLegs};
8+
use pallet_settlement::{InstructionMediatorsAffirmations, InstructionStatuses};
9+
use pallet_settlement::{OffChainAffirmations, UserAffirmations, VenueInstructions};
10+
use polymesh_primitives::settlement::{AffirmationStatus, Instruction, InstructionId};
11+
use polymesh_primitives::settlement::{InstructionStatus, Leg, LegId, SettlementType};
1412
use polymesh_primitives::PortfolioId;
1513
use polymesh_primitives::SystematicIssuers::Settlement as SettlementDID;
1614

17-
use super::setup::create_and_issue_sample_asset_with_venue;
15+
use super::setup::{add_and_affirm_simple_instruction, create_and_issue_sample_asset_with_venue};
1816
use crate::asset_pallet::setup::create_and_issue_sample_asset;
1917
use crate::storage::{default_portfolio_btreeset, User};
2018
use crate::{next_block, ExtBuilder, TestStorage};
2119

2220
type Settlement = pallet_settlement::Pallet<TestStorage>;
2321
type System = frame_system::Pallet<TestStorage>;
22+
type Timestamp = pallet_timestamp::Pallet<TestStorage>;
23+
24+
type PortfolioError = pallet_portfolio::Error<TestStorage>;
2425

2526
#[test]
26-
fn execute_instruction_storage_pruning() {
27+
fn storage_pruning() {
2728
ExtBuilder::default().build().execute_with(|| {
28-
let instruction_id = InstructionId(0);
29+
let inst_id = InstructionId(0);
2930
let bob = User::new(AccountKeyring::Bob);
31+
let dave = User::new(AccountKeyring::Dave);
3032
let alice = User::new(AccountKeyring::Alice);
31-
let bob_default_portfolio = PortfolioId::default_portfolio(bob.did);
32-
let alice_default_portfolio = PortfolioId::default_portfolio(alice.did);
3333

34-
let (asset_id, venue_id) = create_and_issue_sample_asset_with_venue(&alice);
35-
let legs: Vec<Leg> = vec![Leg::Fungible {
36-
sender: PortfolioId::default_portfolio(alice.did),
37-
receiver: PortfolioId::default_portfolio(bob.did),
38-
asset_id,
39-
amount: 1_000,
40-
}];
41-
assert_ok!(Settlement::add_instruction(
42-
alice.origin(),
43-
venue_id,
34+
let _ = add_and_affirm_simple_instruction(
35+
alice,
36+
bob,
37+
dave,
4438
SettlementType::SettleOnAffirmation,
45-
None,
46-
None,
47-
legs.clone(),
48-
None,
49-
));
50-
assert_ok!(Settlement::affirm_instruction(
51-
alice.origin(),
52-
instruction_id,
53-
default_portfolio_btreeset(alice.did),
54-
));
55-
assert_ok!(Settlement::affirm_instruction(
56-
bob.origin(),
57-
instruction_id,
58-
default_portfolio_btreeset(bob.did),
59-
));
39+
);
6040
next_block();
6141

6242
// Asserts all storage have been pruned
43+
assert_eq!(InstructionAffirmsPending::<TestStorage>::get(inst_id), 0);
44+
assert_eq!(VenueInstructions::<TestStorage>::iter().next(), None);
6345
assert_eq!(
64-
InstructionAffirmsPending::<TestStorage>::get(instruction_id),
65-
0
66-
);
67-
assert_eq!(
68-
VenueInstructions::<TestStorage>::iter_prefix_values(venue_id.unwrap()).next(),
69-
None
70-
);
71-
assert_eq!(
72-
InstructionLegs::<TestStorage>::iter_prefix_values(instruction_id).next(),
46+
InstructionLegs::<TestStorage>::iter_prefix_values(inst_id).next(),
7347
None
7448
);
7549
assert_eq!(
76-
InstructionDetails::<TestStorage>::get(instruction_id),
50+
InstructionDetails::<TestStorage>::get(inst_id),
7751
Instruction::default()
7852
);
7953
assert_eq!(
80-
InstructionLegStatus::<TestStorage>::iter_prefix_values(instruction_id).next(),
54+
InstructionLegStatus::<TestStorage>::iter_prefix_values(inst_id).next(),
8155
None
8256
);
8357
assert_eq!(
84-
OffChainAffirmations::<TestStorage>::iter_prefix_values(instruction_id).next(),
58+
OffChainAffirmations::<TestStorage>::iter_prefix_values(inst_id).next(),
8559
None
8660
);
8761
assert_eq!(
88-
AffirmsReceived::<TestStorage>::iter_prefix_values(instruction_id).next(),
62+
AffirmsReceived::<TestStorage>::iter_prefix_values(inst_id).next(),
8963
None
9064
);
9165
assert_eq!(
92-
InstructionMediatorsAffirmations::<TestStorage>::iter_prefix_values(instruction_id)
93-
.next(),
66+
InstructionMediatorsAffirmations::<TestStorage>::iter_prefix_values(inst_id).next(),
9467
None
9568
);
69+
assert_eq!(UserAffirmations::<TestStorage>::iter().next(), None);
9670
assert_eq!(
97-
UserAffirmations::<TestStorage>::get(alice_default_portfolio, instruction_id),
98-
AffirmationStatus::Unknown
99-
);
100-
assert_eq!(
101-
UserAffirmations::<TestStorage>::get(bob_default_portfolio, instruction_id),
102-
AffirmationStatus::Unknown
103-
);
104-
assert_eq!(
105-
InstructionStatuses::<TestStorage>::get(instruction_id),
71+
InstructionStatuses::<TestStorage>::get(inst_id),
10672
InstructionStatus::Success(1)
10773
);
10874
});
10975
}
11076

11177
#[test]
112-
fn execute_instruction_storage_rollback() {
78+
fn storage_rollback() {
11379
ExtBuilder::default().build().execute_with(|| {
11480
System::set_block_number(1);
11581

@@ -188,7 +154,7 @@ fn execute_instruction_storage_rollback() {
188154
system_events.pop().unwrap().event,
189155
crate::storage::EventTest::Settlement(Event::FailedToExecuteInstruction(
190156
instruction_id,
191-
Error::<TestStorage>::FailedToReleaseLockOrTransferAssets.into()
157+
Error::<TestStorage>::FailedAssetTransferringConditions.into()
192158
))
193159
);
194160
assert_eq!(

0 commit comments

Comments
 (0)