diff --git a/rs/rosetta-api/icrc1/src/common/storage/storage_operations/mod.rs b/rs/rosetta-api/icrc1/src/common/storage/storage_operations/mod.rs index 922019331feb..e2f3c2b9fb6e 100644 --- a/rs/rosetta-api/icrc1/src/common/storage/storage_operations/mod.rs +++ b/rs/rosetta-api/icrc1/src/common/storage/storage_operations/mod.rs @@ -7,6 +7,10 @@ use ic_ledger_core::tokens::Zero; use ic_ledger_core::tokens::{CheckedAdd, CheckedSub}; use icrc_ledger_types::icrc1::account::Account; use icrc_ledger_types::icrc107::schema::BTYPE_107; +use icrc_ledger_types::icrc123::schema::{ + BTYPE_123_FREEZE_ACCOUNT, BTYPE_123_FREEZE_PRINCIPAL, BTYPE_123_UNFREEZE_ACCOUNT, + BTYPE_123_UNFREEZE_PRINCIPAL, +}; use icrc_ledger_types::icrc124::schema::{ BTYPE_124_DEACTIVATE, BTYPE_124_PAUSE, BTYPE_124_UNPAUSE, }; @@ -470,7 +474,11 @@ pub fn update_account_balances( } crate::common::storage::types::IcrcOperation::Pause { .. } | crate::common::storage::types::IcrcOperation::Unpause { .. } - | crate::common::storage::types::IcrcOperation::Deactivate { .. } => { + | crate::common::storage::types::IcrcOperation::Deactivate { .. } + | crate::common::storage::types::IcrcOperation::FreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::UnfreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::FreezePrincipal { .. } + | crate::common::storage::types::IcrcOperation::UnfreezePrincipal { .. } => { // Does not affect balances } } @@ -660,6 +668,58 @@ pub fn store_blocks( None, None, ), + crate::common::storage::types::IcrcOperation::FreezeAccount { .. } => ( + BTYPE_123_FREEZE_ACCOUNT, + None, + None, + None, + None, + None, + None, + Nat::from(0_u64), + None, + None, + None, + ), + crate::common::storage::types::IcrcOperation::UnfreezeAccount { .. } => ( + BTYPE_123_UNFREEZE_ACCOUNT, + None, + None, + None, + None, + None, + None, + Nat::from(0_u64), + None, + None, + None, + ), + crate::common::storage::types::IcrcOperation::FreezePrincipal { .. } => ( + BTYPE_123_FREEZE_PRINCIPAL, + None, + None, + None, + None, + None, + None, + Nat::from(0_u64), + None, + None, + None, + ), + crate::common::storage::types::IcrcOperation::UnfreezePrincipal { .. } => ( + BTYPE_123_UNFREEZE_PRINCIPAL, + None, + None, + None, + None, + None, + None, + Nat::from(0_u64), + None, + None, + None, + ), }; // SQLite doesn't support unsigned 64-bit integers. We need to convert the timestamps to signed diff --git a/rs/rosetta-api/icrc1/src/common/storage/types.rs b/rs/rosetta-api/icrc1/src/common/storage/types.rs index 4b5820123ca7..098d1c5cbefc 100644 --- a/rs/rosetta-api/icrc1/src/common/storage/types.rs +++ b/rs/rosetta-api/icrc1/src/common/storage/types.rs @@ -8,6 +8,10 @@ use icrc_ledger_types::icrc::generic_metadata_value::MetadataValue; use icrc_ledger_types::icrc::metadata_key::MetadataKey; use icrc_ledger_types::icrc3::blocks::GenericBlock; use icrc_ledger_types::icrc107::schema::BTYPE_107; +use icrc_ledger_types::icrc123::schema::{ + BTYPE_123_FREEZE_ACCOUNT, BTYPE_123_FREEZE_PRINCIPAL, BTYPE_123_UNFREEZE_ACCOUNT, + BTYPE_123_UNFREEZE_PRINCIPAL, +}; use icrc_ledger_types::icrc124::schema::{ BTYPE_124_DEACTIVATE, BTYPE_124_PAUSE, BTYPE_124_UNPAUSE, }; @@ -144,7 +148,11 @@ impl RosettaBlock { IcrcOperation::FeeCollector { .. } | IcrcOperation::Pause { .. } | IcrcOperation::Unpause { .. } - | IcrcOperation::Deactivate { .. } => None, + | IcrcOperation::Deactivate { .. } + | IcrcOperation::FreezeAccount { .. } + | IcrcOperation::UnfreezeAccount { .. } + | IcrcOperation::FreezePrincipal { .. } + | IcrcOperation::UnfreezePrincipal { .. } => None, })) } @@ -399,6 +407,30 @@ pub enum IcrcOperation { mthd: Option, reason: Option, }, + FreezeAccount { + account: Account, + caller: Option, + mthd: Option, + reason: Option, + }, + UnfreezeAccount { + account: Account, + caller: Option, + mthd: Option, + reason: Option, + }, + FreezePrincipal { + principal: Principal, + caller: Option, + mthd: Option, + reason: Option, + }, + UnfreezePrincipal { + principal: Principal, + caller: Option, + mthd: Option, + reason: Option, + }, } impl TryFrom<(Option, BTreeMap)> for IcrcOperation { @@ -506,6 +538,54 @@ impl TryFrom<(Option, BTreeMap)> for IcrcOperation { reason, }) } + BTYPE_123_FREEZE_ACCOUNT => { + let account: Account = get_field(&map, FIELD_PREFIX, "account")?; + let caller: Option = get_opt_field(&map, FIELD_PREFIX, "caller")?; + let mthd: Option = get_opt_field(&map, FIELD_PREFIX, "mthd")?; + let reason: Option = get_opt_field(&map, FIELD_PREFIX, "reason")?; + Ok(Self::FreezeAccount { + account, + caller, + mthd, + reason, + }) + } + BTYPE_123_UNFREEZE_ACCOUNT => { + let account: Account = get_field(&map, FIELD_PREFIX, "account")?; + let caller: Option = get_opt_field(&map, FIELD_PREFIX, "caller")?; + let mthd: Option = get_opt_field(&map, FIELD_PREFIX, "mthd")?; + let reason: Option = get_opt_field(&map, FIELD_PREFIX, "reason")?; + Ok(Self::UnfreezeAccount { + account, + caller, + mthd, + reason, + }) + } + BTYPE_123_FREEZE_PRINCIPAL => { + let principal: Principal = get_field(&map, FIELD_PREFIX, "principal")?; + let caller: Option = get_opt_field(&map, FIELD_PREFIX, "caller")?; + let mthd: Option = get_opt_field(&map, FIELD_PREFIX, "mthd")?; + let reason: Option = get_opt_field(&map, FIELD_PREFIX, "reason")?; + Ok(Self::FreezePrincipal { + principal, + caller, + mthd, + reason, + }) + } + BTYPE_123_UNFREEZE_PRINCIPAL => { + let principal: Principal = get_field(&map, FIELD_PREFIX, "principal")?; + let caller: Option = get_opt_field(&map, FIELD_PREFIX, "caller")?; + let mthd: Option = get_opt_field(&map, FIELD_PREFIX, "mthd")?; + let reason: Option = get_opt_field(&map, FIELD_PREFIX, "reason")?; + Ok(Self::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + }) + } found => { bail!( "Expected field 'op' to be 'burn', 'mint', 'xfer' or 'approve' but found {found}" @@ -630,6 +710,52 @@ impl From for BTreeMap { map.insert("reason".to_string(), Value::text(reason)); } } + Op::FreezeAccount { + account, + caller, + mthd, + reason, + } + | Op::UnfreezeAccount { + account, + caller, + mthd, + reason, + } => { + map.insert("account".to_string(), Value::from(account)); + if let Some(caller) = caller { + map.insert("caller".to_string(), Value::from(caller)); + } + if let Some(mthd) = mthd { + map.insert("mthd".to_string(), Value::text(mthd)); + } + if let Some(reason) = reason { + map.insert("reason".to_string(), Value::text(reason)); + } + } + Op::FreezePrincipal { + principal, + caller, + mthd, + reason, + } + | Op::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + } => { + map.insert("principal".to_string(), Value::from(principal)); + if let Some(caller) = caller { + map.insert("caller".to_string(), Value::from(caller)); + } + if let Some(mthd) = mthd { + map.insert("mthd".to_string(), Value::text(mthd)); + } + if let Some(reason) = reason { + map.insert("reason".to_string(), Value::text(reason)); + } + } } map } @@ -779,14 +905,50 @@ where mthd, reason, }, - Op::FreezeAccount { .. } - | Op::UnfreezeAccount { .. } - | Op::FreezePrincipal { .. } - | Op::UnfreezePrincipal { .. } => { - // ICRC-123 freeze operations are not yet supported in Rosetta. - // Full support will be added in a follow-up PR. - unimplemented!("ICRC-123 freeze operations are not yet supported in Rosetta") - } + Op::FreezeAccount { + account, + caller, + mthd, + reason, + } => Self::FreezeAccount { + account, + caller, + mthd, + reason, + }, + Op::UnfreezeAccount { + account, + caller, + mthd, + reason, + } => Self::UnfreezeAccount { + account, + caller, + mthd, + reason, + }, + Op::FreezePrincipal { + principal, + caller, + mthd, + reason, + } => Self::FreezePrincipal { + principal, + caller, + mthd, + reason, + }, + Op::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + } => Self::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + }, } } } @@ -962,6 +1124,50 @@ mod tests { }) } + fn arb_freeze_account() -> impl Strategy { + (arb_account(), arb_management_action()).prop_map(|(account, (caller, mthd, reason))| { + IcrcOperation::FreezeAccount { + account, + caller, + mthd, + reason, + } + }) + } + + fn arb_unfreeze_account() -> impl Strategy { + (arb_account(), arb_management_action()).prop_map(|(account, (caller, mthd, reason))| { + IcrcOperation::UnfreezeAccount { + account, + caller, + mthd, + reason, + } + }) + } + + fn arb_freeze_principal() -> impl Strategy { + (arb_principal(), arb_management_action()).prop_map( + |(principal, (caller, mthd, reason))| IcrcOperation::FreezePrincipal { + principal, + caller, + mthd, + reason, + }, + ) + } + + fn arb_unfreeze_principal() -> impl Strategy { + (arb_principal(), arb_management_action()).prop_map( + |(principal, (caller, mthd, reason))| IcrcOperation::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + }, + ) + } + fn arb_op() -> impl Strategy { prop_oneof![ arb_approve(), @@ -972,6 +1178,10 @@ mod tests { arb_pause(), arb_unpause(), arb_deactivate(), + arb_freeze_account(), + arb_unfreeze_account(), + arb_freeze_principal(), + arb_unfreeze_principal(), ] } @@ -1018,6 +1228,18 @@ mod tests { IcrcOperation::Pause { .. } => Some(BTYPE_124_PAUSE.to_string()), IcrcOperation::Unpause { .. } => Some(BTYPE_124_UNPAUSE.to_string()), IcrcOperation::Deactivate { .. } => Some(BTYPE_124_DEACTIVATE.to_string()), + IcrcOperation::FreezeAccount { .. } => { + Some(BTYPE_123_FREEZE_ACCOUNT.to_string()) + } + IcrcOperation::UnfreezeAccount { .. } => { + Some(BTYPE_123_UNFREEZE_ACCOUNT.to_string()) + } + IcrcOperation::FreezePrincipal { .. } => { + Some(BTYPE_123_FREEZE_PRINCIPAL.to_string()) + } + IcrcOperation::UnfreezePrincipal { .. } => { + Some(BTYPE_123_UNFREEZE_PRINCIPAL.to_string()) + } _ => None, }, }, @@ -1032,6 +1254,10 @@ mod tests { IcrcOperation::Pause { .. } => Some(BTYPE_124_PAUSE.to_string()), IcrcOperation::Unpause { .. } => Some(BTYPE_124_UNPAUSE.to_string()), IcrcOperation::Deactivate { .. } => Some(BTYPE_124_DEACTIVATE.to_string()), + IcrcOperation::FreezeAccount { .. } => Some(BTYPE_123_FREEZE_ACCOUNT.to_string()), + IcrcOperation::UnfreezeAccount { .. } => Some(BTYPE_123_UNFREEZE_ACCOUNT.to_string()), + IcrcOperation::FreezePrincipal { .. } => Some(BTYPE_123_FREEZE_PRINCIPAL.to_string()), + IcrcOperation::UnfreezePrincipal { .. } => Some(BTYPE_123_UNFREEZE_PRINCIPAL.to_string()), _ => None, }; let actual_op = match IcrcOperation::try_from((btype, BTreeMap::from(op.clone()))) { @@ -1050,6 +1276,10 @@ mod tests { IcrcOperation::Pause { .. } => Some(BTYPE_124_PAUSE.to_string()), IcrcOperation::Unpause { .. } => Some(BTYPE_124_UNPAUSE.to_string()), IcrcOperation::Deactivate { .. } => Some(BTYPE_124_DEACTIVATE.to_string()), + IcrcOperation::FreezeAccount { .. } => Some(BTYPE_123_FREEZE_ACCOUNT.to_string()), + IcrcOperation::UnfreezeAccount { .. } => Some(BTYPE_123_UNFREEZE_ACCOUNT.to_string()), + IcrcOperation::FreezePrincipal { .. } => Some(BTYPE_123_FREEZE_PRINCIPAL.to_string()), + IcrcOperation::UnfreezePrincipal { .. } => Some(BTYPE_123_UNFREEZE_PRINCIPAL.to_string()), _ => None, }; let actual_tx = match IcrcTransaction::try_from((btype, Value::from(tx.clone()))) { @@ -1289,6 +1519,72 @@ mod tests { assert_eq!(mthd, r_mthd, "mthd"); assert_eq!(reason, r_reason, "reason"); } + ( + ic_icrc1::Operation::FreezeAccount { + account, + caller, + mthd, + reason, + }, + IcrcOperation::FreezeAccount { + account: r_account, + caller: r_caller, + mthd: r_mthd, + reason: r_reason, + }, + ) + | ( + ic_icrc1::Operation::UnfreezeAccount { + account, + caller, + mthd, + reason, + }, + IcrcOperation::UnfreezeAccount { + account: r_account, + caller: r_caller, + mthd: r_mthd, + reason: r_reason, + }, + ) => { + assert_eq!(account, r_account, "account"); + assert_eq!(caller, r_caller, "caller"); + assert_eq!(mthd, r_mthd, "mthd"); + assert_eq!(reason, r_reason, "reason"); + } + ( + ic_icrc1::Operation::FreezePrincipal { + principal, + caller, + mthd, + reason, + }, + IcrcOperation::FreezePrincipal { + principal: r_principal, + caller: r_caller, + mthd: r_mthd, + reason: r_reason, + }, + ) + | ( + ic_icrc1::Operation::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + }, + IcrcOperation::UnfreezePrincipal { + principal: r_principal, + caller: r_caller, + mthd: r_mthd, + reason: r_reason, + }, + ) => { + assert_eq!(principal, r_principal, "principal"); + assert_eq!(caller, r_caller, "caller"); + assert_eq!(mthd, r_mthd, "mthd"); + assert_eq!(reason, r_reason, "reason"); + } (l, r) => panic!( "Found different type of operations. Operation:{l:?} rosetta's Operation:{r:?}" ), diff --git a/rs/rosetta-api/icrc1/src/common/types.rs b/rs/rosetta-api/icrc1/src/common/types.rs index e056dbc59921..f5fdc76bf584 100644 --- a/rs/rosetta-api/icrc1/src/common/types.rs +++ b/rs/rosetta-api/icrc1/src/common/types.rs @@ -204,6 +204,10 @@ pub enum OperationType { Pause, Unpause, Deactivate, + FreezeAccount, + UnfreezeAccount, + FreezePrincipal, + UnfreezePrincipal, } #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] @@ -469,3 +473,85 @@ impl TryFrom for ManagementActionMetadata { .context("Could not parse ManagementActionMetadata from JSON object") } } + +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct FreezeAccountMetadata { + pub account: Account, + + #[serde(skip_serializing_if = "Option::is_none")] + pub caller: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub mthd: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, +} + +impl TryFrom for ObjectMap { + type Error = anyhow::Error; + fn try_from(d: FreezeAccountMetadata) -> Result { + match serde_json::to_value(d) { + Ok(v) => match v { + serde_json::Value::Object(ob) => Ok(ob), + _ => anyhow::bail!( + "Could not convert FreezeAccountMetadata to ObjectMap. Expected type Object but received: {:?}", + v + ), + }, + Err(err) => anyhow::bail!( + "Could not convert FreezeAccountMetadata to ObjectMap: {:?}", + err + ), + } + } +} + +impl TryFrom for FreezeAccountMetadata { + type Error = anyhow::Error; + fn try_from(o: ObjectMap) -> anyhow::Result { + serde_json::from_value(serde_json::Value::Object(o)) + .context("Could not parse FreezeAccountMetadata from JSON object") + } +} + +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct FreezePrincipalMetadata { + pub principal: Principal, + + #[serde(skip_serializing_if = "Option::is_none")] + pub caller: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub mthd: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, +} + +impl TryFrom for ObjectMap { + type Error = anyhow::Error; + fn try_from(d: FreezePrincipalMetadata) -> Result { + match serde_json::to_value(d) { + Ok(v) => match v { + serde_json::Value::Object(ob) => Ok(ob), + _ => anyhow::bail!( + "Could not convert FreezePrincipalMetadata to ObjectMap. Expected type Object but received: {:?}", + v + ), + }, + Err(err) => anyhow::bail!( + "Could not convert FreezePrincipalMetadata to ObjectMap: {:?}", + err + ), + } + } +} + +impl TryFrom for FreezePrincipalMetadata { + type Error = anyhow::Error; + fn try_from(o: ObjectMap) -> anyhow::Result { + serde_json::from_value(serde_json::Value::Object(o)) + .context("Could not parse FreezePrincipalMetadata from JSON object") + } +} diff --git a/rs/rosetta-api/icrc1/src/common/utils/utils.rs b/rs/rosetta-api/icrc1/src/common/utils/utils.rs index 41dd0d7c5b02..8317de12d281 100644 --- a/rs/rosetta-api/icrc1/src/common/utils/utils.rs +++ b/rs/rosetta-api/icrc1/src/common/utils/utils.rs @@ -1,6 +1,7 @@ use crate::common::storage::types::{IcrcOperation, RosettaBlock}; use crate::common::types::{ - FeeCollectorMetadata, FeeMetadata, FeeSetter, ManagementActionMetadata, + FeeCollectorMetadata, FeeMetadata, FeeSetter, FreezeAccountMetadata, FreezePrincipalMetadata, + ManagementActionMetadata, }; use crate::{ AppState, MultiTokenAppState, @@ -161,6 +162,10 @@ pub fn rosetta_core_operations_to_icrc1_operation( Pause, Unpause, Deactivate, + FreezeAccount, + UnfreezeAccount, + FreezePrincipal, + UnfreezePrincipal, } // A builder which helps depict the icrc1 Operation and allows for an arbitrary order of rosetta_core Operations @@ -178,6 +183,8 @@ pub fn rosetta_core_operations_to_icrc1_operation( caller: Option, mthd: Option, reason: Option, + freeze_account: Option, + freeze_principal: Option, } impl IcrcOperationBuilder { @@ -196,6 +203,8 @@ pub fn rosetta_core_operations_to_icrc1_operation( caller: None, mthd: None, reason: None, + freeze_account: None, + freeze_principal: None, } } @@ -264,8 +273,18 @@ pub fn rosetta_core_operations_to_icrc1_operation( self } + pub fn with_freeze_account(mut self, account: Account) -> Self { + self.freeze_account = Some(account); + self + } + + pub fn with_freeze_principal(mut self, principal: Principal) -> Self { + self.freeze_principal = Some(principal); + self + } + pub fn build(self) -> anyhow::Result { - Ok(match self.icrc_operation.context("Icrc Operation type must be one of Mint, Burn, Transfer, Approve, FeeCollector, Pause, Unpause, or Deactivate")? { + Ok(match self.icrc_operation.context("Icrc Operation type must be one of Mint, Burn, Transfer, Approve, FeeCollector, FreezeAccount, UnfreezeAccount, FreezePrincipal, UnfreezePrincipal, Pause, Unpause, or Deactivate")? { IcrcOperation::Mint => { if self.from.is_some() { bail!("From AccountIdentifier field is not allowed for Mint operation") @@ -327,6 +346,30 @@ pub fn rosetta_core_operations_to_icrc1_operation( mthd: self.mthd, reason: self.reason, }, + IcrcOperation::FreezeAccount => crate::common::storage::types::IcrcOperation::FreezeAccount{ + account: self.freeze_account.context("Account field needs to be populated for FreezeAccount operation")?, + caller: self.caller, + mthd: self.mthd, + reason: self.reason, + }, + IcrcOperation::UnfreezeAccount => crate::common::storage::types::IcrcOperation::UnfreezeAccount{ + account: self.freeze_account.context("Account field needs to be populated for UnfreezeAccount operation")?, + caller: self.caller, + mthd: self.mthd, + reason: self.reason, + }, + IcrcOperation::FreezePrincipal => crate::common::storage::types::IcrcOperation::FreezePrincipal{ + principal: self.freeze_principal.context("Principal field needs to be populated for FreezePrincipal operation")?, + caller: self.caller, + mthd: self.mthd, + reason: self.reason, + }, + IcrcOperation::UnfreezePrincipal => crate::common::storage::types::IcrcOperation::UnfreezePrincipal{ + principal: self.freeze_principal.context("Principal field needs to be populated for UnfreezePrincipal operation")?, + caller: self.caller, + mthd: self.mthd, + reason: self.reason, + }, }) } } @@ -477,6 +520,54 @@ pub fn rosetta_core_operations_to_icrc1_operation( .with_mthd(ma_metadata.mthd) .with_reason(ma_metadata.reason) } + OperationType::FreezeAccount => { + let metadata = operation + .metadata + .context("metadata should be set for freeze account operations")?; + let fa_metadata = FreezeAccountMetadata::try_from(metadata)?; + icrc1_operation_builder + .with_icrc_operation(IcrcOperation::FreezeAccount) + .with_freeze_account(fa_metadata.account) + .with_caller(fa_metadata.caller) + .with_mthd(fa_metadata.mthd) + .with_reason(fa_metadata.reason) + } + OperationType::UnfreezeAccount => { + let metadata = operation + .metadata + .context("metadata should be set for unfreeze account operations")?; + let fa_metadata = FreezeAccountMetadata::try_from(metadata)?; + icrc1_operation_builder + .with_icrc_operation(IcrcOperation::UnfreezeAccount) + .with_freeze_account(fa_metadata.account) + .with_caller(fa_metadata.caller) + .with_mthd(fa_metadata.mthd) + .with_reason(fa_metadata.reason) + } + OperationType::FreezePrincipal => { + let metadata = operation + .metadata + .context("metadata should be set for freeze principal operations")?; + let fp_metadata = FreezePrincipalMetadata::try_from(metadata)?; + icrc1_operation_builder + .with_icrc_operation(IcrcOperation::FreezePrincipal) + .with_freeze_principal(fp_metadata.principal) + .with_caller(fp_metadata.caller) + .with_mthd(fp_metadata.mthd) + .with_reason(fp_metadata.reason) + } + OperationType::UnfreezePrincipal => { + let metadata = operation + .metadata + .context("metadata should be set for unfreeze principal operations")?; + let fp_metadata = FreezePrincipalMetadata::try_from(metadata)?; + icrc1_operation_builder + .with_icrc_operation(IcrcOperation::UnfreezePrincipal) + .with_freeze_principal(fp_metadata.principal) + .with_caller(fp_metadata.caller) + .with_mthd(fp_metadata.mthd) + .with_reason(fp_metadata.reason) + } }; } icrc1_operation_builder.build() @@ -793,6 +884,98 @@ pub fn icrc1_operation_to_rosetta_core_operations( ), )); } + crate::common::storage::types::IcrcOperation::FreezeAccount { + account, + caller, + mthd, + reason, + } => { + operations.push(rosetta_core::objects::Operation::new( + 0, + OperationType::FreezeAccount.to_string(), + None, + None, + None, + Some( + FreezeAccountMetadata { + account, + caller, + mthd, + reason, + } + .try_into()?, + ), + )); + } + crate::common::storage::types::IcrcOperation::UnfreezeAccount { + account, + caller, + mthd, + reason, + } => { + operations.push(rosetta_core::objects::Operation::new( + 0, + OperationType::UnfreezeAccount.to_string(), + None, + None, + None, + Some( + FreezeAccountMetadata { + account, + caller, + mthd, + reason, + } + .try_into()?, + ), + )); + } + crate::common::storage::types::IcrcOperation::FreezePrincipal { + principal, + caller, + mthd, + reason, + } => { + operations.push(rosetta_core::objects::Operation::new( + 0, + OperationType::FreezePrincipal.to_string(), + None, + None, + None, + Some( + FreezePrincipalMetadata { + principal, + caller, + mthd, + reason, + } + .try_into()?, + ), + )); + } + crate::common::storage::types::IcrcOperation::UnfreezePrincipal { + principal, + caller, + mthd, + reason, + } => { + operations.push(rosetta_core::objects::Operation::new( + 0, + OperationType::UnfreezePrincipal.to_string(), + None, + None, + None, + Some( + FreezePrincipalMetadata { + principal, + caller, + mthd, + reason, + } + .try_into()?, + ), + )); + } }; Ok(operations) diff --git a/rs/rosetta-api/icrc1/src/construction_api/services.rs b/rs/rosetta-api/icrc1/src/construction_api/services.rs index a8e32b5d117e..2fe0c30834fd 100644 --- a/rs/rosetta-api/icrc1/src/construction_api/services.rs +++ b/rs/rosetta-api/icrc1/src/construction_api/services.rs @@ -554,7 +554,7 @@ mod tests { | ic_icrc1::Operation::UnfreezeAccount { .. } | ic_icrc1::Operation::FreezePrincipal { .. } | ic_icrc1::Operation::UnfreezePrincipal { .. } => { - panic!("Management operations not implemented") + panic!("Management and freeze operations not implemented") } }; let args = match arg_with_caller.arg { diff --git a/rs/rosetta-api/icrc1/src/construction_api/utils.rs b/rs/rosetta-api/icrc1/src/construction_api/utils.rs index a64e37f2256d..da256e1cbbfb 100644 --- a/rs/rosetta-api/icrc1/src/construction_api/utils.rs +++ b/rs/rosetta-api/icrc1/src/construction_api/utils.rs @@ -277,8 +277,12 @@ pub fn build_icrc1_ledger_canister_method_args( } crate::common::storage::types::IcrcOperation::Pause { .. } | crate::common::storage::types::IcrcOperation::Unpause { .. } - | crate::common::storage::types::IcrcOperation::Deactivate { .. } => { - bail!("ICRC-124 operations not supported") + | crate::common::storage::types::IcrcOperation::Deactivate { .. } + | crate::common::storage::types::IcrcOperation::FreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::UnfreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::FreezePrincipal { .. } + | crate::common::storage::types::IcrcOperation::UnfreezePrincipal { .. } => { + bail!("Management and freeze operations not supported") } } .context("Unable to encode canister method args") @@ -311,8 +315,12 @@ fn extract_caller_principal_from_icrc1_ledger_operation( } crate::common::storage::types::IcrcOperation::Pause { .. } | crate::common::storage::types::IcrcOperation::Unpause { .. } - | crate::common::storage::types::IcrcOperation::Deactivate { .. } => { - bail!("ICRC-124 operations not supported") + | crate::common::storage::types::IcrcOperation::Deactivate { .. } + | crate::common::storage::types::IcrcOperation::FreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::UnfreezeAccount { .. } + | crate::common::storage::types::IcrcOperation::FreezePrincipal { .. } + | crate::common::storage::types::IcrcOperation::UnfreezePrincipal { .. } => { + bail!("Management and freeze operations not supported") } }) } diff --git a/rs/rosetta-api/icrc1/src/data_api/services.rs b/rs/rosetta-api/icrc1/src/data_api/services.rs index 37ebb75d200d..0c34038ff142 100644 --- a/rs/rosetta-api/icrc1/src/data_api/services.rs +++ b/rs/rosetta-api/icrc1/src/data_api/services.rs @@ -1232,7 +1232,11 @@ mod test { IcrcOperation::FeeCollector { .. } | IcrcOperation::Pause { .. } | IcrcOperation::Unpause { .. } - | IcrcOperation::Deactivate { .. } => None, + | IcrcOperation::Deactivate { .. } + | IcrcOperation::FreezeAccount { .. } + | IcrcOperation::UnfreezeAccount { .. } + | IcrcOperation::FreezePrincipal { .. } + | IcrcOperation::UnfreezePrincipal { .. } => None, }; if search_transactions_request.account_identifier.is_some() { break; @@ -1289,7 +1293,11 @@ mod test { IcrcOperation::FeeCollector { .. } | IcrcOperation::Pause { .. } | IcrcOperation::Unpause { .. } - | IcrcOperation::Deactivate { .. } => false, + | IcrcOperation::Deactivate { .. } + | IcrcOperation::FreezeAccount { .. } + | IcrcOperation::UnfreezeAccount { .. } + | IcrcOperation::FreezePrincipal { .. } + | IcrcOperation::UnfreezePrincipal { .. } => false, }) .count();