diff --git a/pallets/common/src/traits/settlement.rs b/pallets/common/src/traits/settlement.rs index 138083a82..a09ec3215 100644 --- a/pallets/common/src/traits/settlement.rs +++ b/pallets/common/src/traits/settlement.rs @@ -66,7 +66,7 @@ decl_event!( /// (did, venue_id, instruction_id, settlement_type, trade_date, value_date, legs, memo) InstructionCreated( IdentityId, - VenueId, + Option, InstructionId, SettlementType, Option, diff --git a/pallets/runtime/tests/src/asset_pallet/base_transfer.rs b/pallets/runtime/tests/src/asset_pallet/base_transfer.rs index 2aaeca008..d5386fe13 100644 --- a/pallets/runtime/tests/src/asset_pallet/base_transfer.rs +++ b/pallets/runtime/tests/src/asset_pallet/base_transfer.rs @@ -219,7 +219,7 @@ fn base_transfer_locked_asset() { )); assert_ok!(Settlement::add_and_affirm_instruction( alice.origin(), - VenueId(0), + Some(VenueId(0)), SettlementType::SettleOnAffirmation, None, None, diff --git a/pallets/runtime/tests/src/asset_pallet/controller_transfer.rs b/pallets/runtime/tests/src/asset_pallet/controller_transfer.rs index 8d152d241..a7dd0ecc3 100644 --- a/pallets/runtime/tests/src/asset_pallet/controller_transfer.rs +++ b/pallets/runtime/tests/src/asset_pallet/controller_transfer.rs @@ -56,7 +56,7 @@ fn controller_transfer_locked_asset() { )); assert_ok!(Settlement::add_instruction( alice.origin(), - VenueId(0), + Some(VenueId(0)), SettlementType::SettleManual(System::block_number() + 1), None, None, diff --git a/pallets/runtime/tests/src/asset_test.rs b/pallets/runtime/tests/src/asset_test.rs index e1c5c99c4..5e34a2375 100644 --- a/pallets/runtime/tests/src/asset_test.rs +++ b/pallets/runtime/tests/src/asset_test.rs @@ -1938,7 +1938,7 @@ fn controller_transfer_locked_asset() { )); assert_ok!(Settlement::add_instruction( alice.origin(), - VenueId(0), + Some(VenueId(0)), SettlementType::SettleManual(System::block_number() + 1), None, None, diff --git a/pallets/runtime/tests/src/portfolio.rs b/pallets/runtime/tests/src/portfolio.rs index ac6e157ab..a373b5aa5 100644 --- a/pallets/runtime/tests/src/portfolio.rs +++ b/pallets/runtime/tests/src/portfolio.rs @@ -678,7 +678,7 @@ fn delete_portfolio_with_locked_nfts() { }]; assert_ok!(Settlement::add_and_affirm_instruction( alice.origin(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, diff --git a/pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs b/pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs index 642b3639f..145c4b062 100644 --- a/pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs +++ b/pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs @@ -61,7 +61,10 @@ fn execute_instruction_storage_pruning() { // Asserts all storage have been pruned assert_eq!(InstructionAffirmsPending::get(instruction_id), 0); - assert_eq!(VenueInstructions::iter_prefix_values(venue_id).next(), None); + assert_eq!( + VenueInstructions::iter_prefix_values(venue_id.unwrap()).next(), + None + ); assert_eq!( InstructionLegs::iter_prefix_values(instruction_id).next(), None diff --git a/pallets/runtime/tests/src/settlement_pallet/setup.rs b/pallets/runtime/tests/src/settlement_pallet/setup.rs index 7b7b8112b..54524ba18 100644 --- a/pallets/runtime/tests/src/settlement_pallet/setup.rs +++ b/pallets/runtime/tests/src/settlement_pallet/setup.rs @@ -12,7 +12,7 @@ type Nft = pallet_nft::Module; type Settlement = pallet_settlement::Module; /// Calls [`create_and_issue_sample_asset`] and creates a venue for `asset_owner`. -pub fn create_and_issue_sample_asset_with_venue(asset_owner: &User) -> (AssetID, VenueId) { +pub fn create_and_issue_sample_asset_with_venue(asset_owner: &User) -> (AssetID, Option) { let asset_id = create_and_issue_sample_asset(&asset_owner); let venue_id = Settlement::venue_counter(); @@ -23,5 +23,5 @@ pub fn create_and_issue_sample_asset_with_venue(asset_owner: &User) -> (AssetID, VenueType::Other )); - (asset_id, venue_id) + (asset_id, Some(venue_id)) } diff --git a/pallets/runtime/tests/src/settlement_test.rs b/pallets/runtime/tests/src/settlement_test.rs index 6988359a6..11087d5bd 100644 --- a/pallets/runtime/tests/src/settlement_test.rs +++ b/pallets/runtime/tests/src/settlement_test.rs @@ -484,7 +484,10 @@ fn token_swap() { assert_instruction_details(instruction_id, instruction_details); assert_affirms_pending(instruction_id, 2); - assert_eq!(venue_instructions(venue_counter), vec![instruction_id]); + assert_eq!( + venue_instructions(venue_counter.unwrap()), + vec![instruction_id] + ); alice.assert_all_balances_unchanged(); bob.assert_all_balances_unchanged(); @@ -614,7 +617,10 @@ fn settle_on_block() { ); assert_affirms_pending(instruction_id, 2); - assert_eq!(venue_instructions(venue_counter), vec![instruction_id]); + assert_eq!( + venue_instructions(venue_counter.unwrap()), + vec![instruction_id] + ); alice.assert_all_balances_unchanged(); bob.assert_all_balances_unchanged(); @@ -746,7 +752,10 @@ fn failed_execution() { instruction_details ); assert_affirms_pending(instruction_id, 2); - assert_eq!(venue_instructions(venue_counter), vec![instruction_id]); + assert_eq!( + venue_instructions(venue_counter.unwrap()), + vec![instruction_id] + ); // Ensure balances have not changed. alice.assert_all_balances_unchanged(); @@ -864,7 +873,7 @@ fn venue_filtering() { assert_ok!(Settlement::allow_venues( alice.origin(), asset_id, - vec![venue_counter] + vec![venue_counter.unwrap()] )); assert_ok!(Settlement::add_and_affirm_instruction( alice.origin(), @@ -886,7 +895,7 @@ fn venue_filtering() { assert_ok!(Settlement::disallow_venues( alice.origin(), asset_id, - vec![venue_counter] + vec![venue_counter.unwrap()] )); next_block(); // Second instruction fails to settle due to venue being not whitelisted @@ -992,7 +1001,7 @@ fn basic_fuzzing() { } assert_ok!(Settlement::add_instruction( alice.origin(), - venue_counter, + Some(venue_counter), SettlementType::SettleOnBlock(block_number), None, None, @@ -2275,7 +2284,7 @@ fn basic_settlement_with_memo() { fn create_instruction( alice: &User, bob: &User, - venue_counter: VenueId, + venue_counter: Option, asset_id: AssetID, amount: u128, ) -> InstructionId { @@ -2677,7 +2686,7 @@ fn add_and_affirm_nft_instruction() { }]; assert_ok!(Settlement::add_and_affirm_instruction( alice.origin(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -2786,7 +2795,7 @@ fn add_and_affirm_nft_not_owned() { assert_noop!( Settlement::add_and_affirm_instruction( alice.origin(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -2854,7 +2863,7 @@ fn add_same_nft_different_legs() { assert_noop!( Settlement::add_and_affirm_instruction( alice.origin(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -2910,7 +2919,7 @@ fn add_and_affirm_with_receipts_nfts() { }]; assert_ok!(Settlement::add_instruction( alice.origin(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -2964,7 +2973,7 @@ fn add_instruction_unexpected_offchain_asset() { assert_noop!( Settlement::add_instruction( alice.origin(), - venue_counter, + Some(venue_counter), SettlementType::SettleOnAffirmation, None, None, @@ -2983,7 +2992,7 @@ fn add_instruction_unexpected_offchain_asset() { assert_noop!( Settlement::add_instruction( alice.origin(), - venue_counter, + Some(venue_counter), SettlementType::SettleOnAffirmation, None, None, @@ -3091,7 +3100,7 @@ fn affirm_offchain_asset_without_receipt() { }]; assert_ok!(Settlement::add_instruction( alice.origin(), - venue, + Some(venue), SettlementType::SettleOnAffirmation, None, None, diff --git a/pallets/settlement/src/benchmarking.rs b/pallets/settlement/src/benchmarking.rs index 898ff77dc..88df935a7 100644 --- a/pallets/settlement/src/benchmarking.rs +++ b/pallets/settlement/src/benchmarking.rs @@ -209,7 +209,7 @@ where ); Module::::add_instruction_with_mediators( sender.origin.clone().into(), - venue_id, + Some(venue_id), settlement_type, None, None, @@ -436,7 +436,7 @@ benchmarks! { let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); Module::::add_instruction( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -489,7 +489,7 @@ benchmarks! { let venue_id = create_venue_::(alice.did(), vec![alice.account()]); let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); - }: _(alice.origin, venue_id, settlement_type, None, None, parameters.legs, memo) + }: _(alice.origin, Some(venue_id), settlement_type, None, None, parameters.legs, memo) add_and_affirm_instruction { // Number of fungible, non-fungible and offchain LEGS in the instruction @@ -504,7 +504,7 @@ benchmarks! { let venue_id = create_venue_::(alice.did(), vec![alice.account()]); let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); - }: _(alice.origin, venue_id, settlement_type, None, None, parameters.legs, parameters.portfolios.sdr_portfolios, memo) + }: _(alice.origin, Some(venue_id), settlement_type, None, None, parameters.legs, parameters.portfolios.sdr_portfolios, memo) affirm_instruction { // Number of fungible and non-fungible assets in the portfolios @@ -519,7 +519,7 @@ benchmarks! { let parameters = setup_legs::(&alice, &bob, f, n, T::MaxNumberOfOffChainAssets::get(), false, false); Module::::add_instruction( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -613,7 +613,7 @@ benchmarks! { let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); Module::::add_instruction( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -650,7 +650,7 @@ benchmarks! { let parameters = setup_legs::(&alice, &bob, f, n, T::MaxNumberOfOffChainAssets::get(), false, false); Module::::add_instruction( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -692,7 +692,7 @@ benchmarks! { let mediators: BTreeSet = (0..m).map(|i| IdentityId::from(i as u128)).collect(); let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); - }: _(alice.origin, venue_id, settlement_type, None, None, parameters.legs, memo, mediators.try_into().unwrap()) + }: _(alice.origin, Some(venue_id), settlement_type, None, None, parameters.legs, memo, mediators.try_into().unwrap()) add_and_affirm_with_mediators { // Number of fungible, non-fungible, offchain legs and mediators @@ -709,7 +709,7 @@ benchmarks! { let mediators: BTreeSet = (0..m).map(|i| IdentityId::from(i as u128)).collect(); let parameters = setup_legs::(&alice, &bob, f, n, o, false, false); - }: _(alice.origin, venue_id, settlement_type, None, None, parameters.legs, parameters.portfolios.sdr_portfolios, memo, mediators.try_into().unwrap()) + }: _(alice.origin, Some(venue_id), settlement_type, None, None, parameters.legs, parameters.portfolios.sdr_portfolios, memo, mediators.try_into().unwrap()) affirm_instruction_as_mediator { let bob = UserBuilder::::default().generate_did().build("Bob"); @@ -730,7 +730,7 @@ benchmarks! { ); Module::::add_instruction_with_mediators( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, @@ -760,7 +760,7 @@ benchmarks! { ); Module::::add_instruction_with_mediators( alice.origin.clone().into(), - venue_id, + Some(venue_id), SettlementType::SettleOnAffirmation, None, None, diff --git a/pallets/settlement/src/lib.rs b/pallets/settlement/src/lib.rs index 8eda8f1ab..13700d35d 100644 --- a/pallets/settlement/src/lib.rs +++ b/pallets/settlement/src/lib.rs @@ -522,7 +522,7 @@ decl_module! { /// Adds a new instruction. /// /// # Arguments - /// * `venue_id`: The [`VenueId`] of the venue this instruction belongs to. + /// * `venue_id`: The optional [`VenueId`] of the venue this instruction belongs to. /// * `settlement_type`: The [`SettlementType`] specifying when the instruction should be settled. /// * `trade_date`: Optional date from which people can interact with this instruction. /// * `value_date`: Optional date after which the instruction should be settled (not enforced). @@ -531,7 +531,7 @@ decl_module! { #[weight = ::WeightInfo::add_instruction_legs(legs)] pub fn add_instruction( origin, - venue_id: VenueId, + venue_id: Option, settlement_type: SettlementType, trade_date: Option, value_date: Option, @@ -567,7 +567,7 @@ decl_module! { #[weight = ::WeightInfo::add_and_affirm_instruction_legs(legs)] pub fn add_and_affirm_instruction( origin, - venue_id: VenueId, + venue_id: Option, settlement_type: SettlementType, trade_date: Option, value_date: Option, @@ -769,7 +769,7 @@ decl_module! { #[weight = ::WeightInfo::add_instruction_with_mediators_legs(legs, mediators.len() as u32)] pub fn add_instruction_with_mediators( origin, - venue_id: VenueId, + venue_id: Option, settlement_type: SettlementType, trade_date: Option, value_date: Option, @@ -807,7 +807,7 @@ decl_module! { #[weight = ::WeightInfo::add_and_affirm_with_mediators_legs(legs, mediators.len() as u32)] pub fn add_and_affirm_with_mediators( origin, - venue_id: VenueId, + venue_id: Option, settlement_type: SettlementType, trade_date: Option, value_date: Option, @@ -941,7 +941,7 @@ impl Module { pub fn base_add_instruction( did: IdentityId, - venue_id: VenueId, + venue_id: Option, settlement_type: SettlementType, trade_date: Option, value_date: Option, @@ -966,7 +966,9 @@ impl Module { } // Ensure venue exists & sender is its creator. - Self::venue_for_management(venue_id, did)?; + if let Some(venue_id) = venue_id { + Self::venue_for_management(venue_id, did)?; + } // Verifies if all legs are valid. let mut instruction_info = Self::ensure_valid_legs(&legs, &venue_id)?; @@ -1020,7 +1022,9 @@ impl Module { if let Some(ref memo) = memo { InstructionMemos::insert(instruction_id, &memo); } - VenueInstructions::insert(venue_id, instruction_id, ()); + if let Some(venue_id) = venue_id { + VenueInstructions::insert(venue_id, instruction_id, ()); + } Self::deposit_event(RawEvent::InstructionCreated( did, @@ -1066,7 +1070,7 @@ impl Module { /// See also: [`Module::ensure_valid_fungible_leg`], [`Module::ensure_valid_nft_leg`] and [`Module::ensure_valid_off_chain_leg`]. fn ensure_valid_legs( legs: &[Leg], - venue_id: &VenueId, + venue_id: &Option, ) -> Result { // Tracks the number of fungible, non-fungible and offchain assets across the legs let mut instruction_asset_count = AssetCount::default(); @@ -1280,7 +1284,9 @@ impl Module { ) { Ok(_) => { // Remove remaning storage - VenueInstructions::remove(instruction_details.venue_id, instruction_id); + if let Some(venue_id) = instruction_details.venue_id { + VenueInstructions::remove(venue_id, instruction_id); + } let _ = InstructionLegStatus::::clear_prefix( instruction_id, instruction_legs.len() as u32, @@ -1456,7 +1462,9 @@ impl Module { /// [`InstructionStatus::Rejected`]. fn prune_rejected_instruction(instruction_id: InstructionId) { let instruction_details = InstructionDetails::::take(&instruction_id); - VenueInstructions::remove(instruction_details.venue_id, instruction_id); + if let Some(venue_id) = instruction_details.venue_id { + VenueInstructions::remove(venue_id, instruction_id); + } InstructionAffirmsPending::remove(instruction_id); let _ = InstructionMediatorsAffirmations::::clear_prefix( instruction_id, @@ -1964,13 +1972,15 @@ impl Module { /// in the venue allowed list. fn ensure_allowed_venue( instruction_legs: &[(LegId, Leg)], - venue_id: VenueId, + venue_id: Option, ) -> DispatchResult { - // Avoids reading the storage multiple times for the same asset_id - let mut tickers: BTreeSet = BTreeSet::new(); - for (_, leg) in instruction_legs { - if let Some(asset_id) = leg.asset_id() { - Self::ensure_venue_filtering(&mut tickers, *asset_id, &venue_id)?; + if let Some(_) = venue_id { + // Avoids reading the storage multiple times for the same asset_id + let mut tickers: BTreeSet = BTreeSet::new(); + for (_, leg) in instruction_legs { + if let Some(asset_id) = leg.asset_id() { + Self::ensure_venue_filtering(&mut tickers, *asset_id, &venue_id)?; + } } } Ok(()) @@ -1980,13 +1990,15 @@ impl Module { fn ensure_venue_filtering( tickers: &mut BTreeSet, asset_id: AssetID, - venue_id: &VenueId, + venue_id: &Option, ) -> DispatchResult { - if tickers.insert(asset_id) && Self::venue_filtering(asset_id) { - ensure!( - Self::venue_allow_list(asset_id, venue_id), - Error::::UnauthorizedVenue - ); + if let Some(venue_id) = venue_id { + if tickers.insert(asset_id) && Self::venue_filtering(asset_id) { + ensure!( + Self::venue_allow_list(asset_id, venue_id), + Error::::UnauthorizedVenue + ); + } } Ok(()) } @@ -2007,7 +2019,7 @@ impl Module { /// See also: [`Module::ensure_valid_fungible_leg`], [`Module::ensure_valid_nft_leg`] and [`Module::ensure_valid_off_chain_leg`]. fn ensure_valid_leg( leg: &Leg, - venue_id: &VenueId, + venue_id: &Option, tickers: &mut BTreeSet, instruction_asset_count: &mut AssetCount, ) -> DispatchResult { @@ -2058,7 +2070,7 @@ impl Module { tickers: &mut BTreeSet, asset_id: AssetID, amount: Balance, - venue_id: &VenueId, + venue_id: &Option, ) -> DispatchResult { ensure!(amount > 0, Error::::ZeroAmount); ensure!( @@ -2075,7 +2087,7 @@ impl Module { fn ensure_valid_nft_leg( tickers: &mut BTreeSet, nfts: &NFTs, - venue_id: &VenueId, + venue_id: &Option, ) -> DispatchResult { ensure!( Self::is_on_chain_asset(nfts.asset_id()), @@ -2152,12 +2164,22 @@ impl Module { } None => { // If the caller is not the venue creator, they should be a counter party in an offchain leg - if Self::venue_for_management(instruction_details.venue_id, caller_did).is_err() { - ensure!( - Self::is_offchain_party(&instruction_legs, &caller_did), - Error::::Unauthorized - ); - }; + match instruction_details.venue_id { + Some(venue_id) => { + if Self::venue_for_management(venue_id, caller_did).is_err() { + ensure!( + Self::is_offchain_party(&instruction_legs, &caller_did), + Error::::Unauthorized + ); + }; + } + None => { + ensure!( + Self::is_offchain_party(&instruction_legs, &caller_did), + Error::::Unauthorized + ); + } + } } } @@ -2219,7 +2241,7 @@ impl Module { /// if the receipt has not been used before, if the receipt's `leg_id` and `instruction_id` are referencing the /// correct instruction/leg and if its signature is valid. fn ensure_valid_receipts_details( - venue_id: VenueId, + venue_id: Option, instruction_id: InstructionId, receipts_details: &[ReceiptDetails], ) -> DispatchResult { @@ -2239,10 +2261,14 @@ impl Module { unique_legs.insert(receipt_details.leg_id()), Error::::MultipleReceiptsForOneLeg ); - ensure!( - Self::venue_signers(venue_id, receipt_details.signer()), - Error::::UnauthorizedSigner - ); + + if let Some(venue_id) = venue_id { + ensure!( + Self::venue_signers(venue_id, receipt_details.signer()), + Error::::UnauthorizedSigner + ); + } + ensure!( !Self::receipts_used(receipt_details.signer(), &receipt_details.uid()), Error::::ReceiptAlreadyClaimed diff --git a/pallets/settlement/src/migrations.rs b/pallets/settlement/src/migrations.rs index 6840791d5..4519e5a54 100644 --- a/pallets/settlement/src/migrations.rs +++ b/pallets/settlement/src/migrations.rs @@ -36,6 +36,17 @@ mod v2 { pub ids: Vec, } + #[derive(Encode, Decode, TypeInfo)] + #[derive(Default, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] + pub struct Instruction { + pub instruction_id: InstructionId, + pub venue_id: VenueId, + pub settlement_type: SettlementType, + pub created_at: Option, + pub trade_date: Option, + pub value_date: Option, + } + decl_storage! { trait Store for Module as Settlement { // This storage changed the Ticker key to AssetID. @@ -50,6 +61,9 @@ mod v2 { pub(crate) InstructionLegs get(fn instruction_legs): double_map hasher(twox_64_concat) InstructionId, hasher(twox_64_concat) LegId => Option; + pub(crate) InstructionDetails get(fn instruction_details): + map hasher(twox_64_concat) InstructionId => Instruction; + } } @@ -95,6 +109,19 @@ impl From for Leg { } } +impl From> for Instruction { + fn from(v2_instruction: v2::Instruction) -> Instruction { + Instruction { + instruction_id: v2_instruction.instruction_id, + venue_id: Some(v2_instruction.venue_id), + settlement_type: v2_instruction.settlement_type, + created_at: v2_instruction.created_at, + trade_date: v2_instruction.trade_date, + value_date: v2_instruction.value_date, + } + } +} + pub(crate) fn migrate_to_v3() { RuntimeLogger::init(); let mut ticker_to_asset_id = BTreeMap::new(); @@ -130,4 +157,12 @@ pub(crate) fn migrate_to_v3() { InstructionLegs::insert(instruction_id, leg_id, Leg::from(leg)); }); log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the InstructionDetails storage"); + v2::InstructionDetails::::drain().for_each(|(id, inst)| { + count += 1; + InstructionDetails::::insert(id, Instruction::from(inst)); + }); + log::info!("{:?} items migrated", count); } diff --git a/pallets/sto/src/lib.rs b/pallets/sto/src/lib.rs index 2b0b37be2..a8a07c00a 100644 --- a/pallets/sto/src/lib.rs +++ b/pallets/sto/src/lib.rs @@ -488,7 +488,7 @@ decl_module! { let instruction_id = Settlement::::base_add_instruction( fundraiser.creator, - fundraiser.venue_id, + Some(fundraiser.venue_id), SettlementType::SettleOnAffirmation, None, None, diff --git a/primitives/src/settlement.rs b/primitives/src/settlement.rs index d59184e65..4461c9e23 100644 --- a/primitives/src/settlement.rs +++ b/primitives/src/settlement.rs @@ -146,7 +146,7 @@ pub struct Instruction { /// Unique instruction id. It is an auto incrementing number pub instruction_id: InstructionId, /// Id of the venue this instruction belongs to - pub venue_id: VenueId, + pub venue_id: Option, /// Type of settlement used for this instruction pub settlement_type: SettlementType, /// Date at which this instruction was created