diff --git a/Cargo.lock b/Cargo.lock index a94097a..7adc042 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,21 +243,24 @@ dependencies = [ [[package]] name = "canton-api-client" -version = "3.3.0-0.1.1" +version = "3.6.0-0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3e641f161ca1dddca37296cfa43c14d28772864ca66631a38e3db79d3f3ddd" +checksum = "1702122d98d165e2c77a1c6532e8be8a80d5bf7f25eac71beed5587a61fcaf01" dependencies = [ - "reqwest", + "chrono", + "reqwest 0.13.3", "serde", "serde_json", "serde_repr", "serde_with", + "tokio", + "tokio-util", "url", ] [[package]] name = "cbtc" -version = "0.4.2" +version = "0.6.0" dependencies = [ "base64", "canton-api-client", @@ -271,7 +274,7 @@ dependencies = [ "ledger", "log", "registry", - "reqwest", + "reqwest 0.12.26", "semver", "serde", "serde_json", @@ -336,8 +339,8 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "common" -version = "0.5.0" -source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.5.0#e5b459f62715ee903d9cd8fdd0c4411252356e4e" +version = "0.6.0" +source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.6.0#83446199920966c322c525d6a3739d2cfd2863c7" dependencies = [ "canton-api-client", "rust_decimal", @@ -1165,29 +1168,31 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] [[package]] name = "keycloak" -version = "0.5.0" -source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.5.0#e5b459f62715ee903d9cd8fdd0c4411252356e4e" +version = "0.6.0" +source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.6.0#83446199920966c322c525d6a3739d2cfd2863c7" dependencies = [ "base64", - "reqwest", + "reqwest 0.12.26", "serde", "serde_json", ] [[package]] name = "ledger" -version = "0.5.0" -source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.5.0#e5b459f62715ee903d9cd8fdd0c4411252356e4e" +version = "0.6.0" +source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.6.0#83446199920966c322c525d6a3739d2cfd2863c7" dependencies = [ "base64", "canton-api-client", @@ -1195,7 +1200,7 @@ dependencies = [ "futures-util", "log", "rand", - "reqwest", + "reqwest 0.12.26", "serde", "serde_json", "tokio-tungstenite", @@ -1591,11 +1596,11 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "registry" -version = "0.5.0" -source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.5.0#e5b459f62715ee903d9cd8fdd0c4411252356e4e" +version = "0.6.0" +source = "git+ssh://git@github.com/DLC-link/canton-lib?tag=v0.6.0#83446199920966c322c525d6a3739d2cfd2863c7" dependencies = [ "common", - "reqwest", + "reqwest 0.12.26", "serde", "serde_json", ] @@ -1619,7 +1624,6 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -1631,6 +1635,43 @@ dependencies = [ "js-sys", "log", "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "reqwest" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "js-sys", + "log", "mime_guess", "native-tls", "percent-encoding", @@ -1642,12 +1683,14 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -2477,9 +2520,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -2491,22 +2534,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2514,9 +2554,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -2527,18 +2567,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 5ca43b8..30ab838 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cbtc" -version = "0.4.2" +version = "0.6.0" edition = "2024" autoexamples = false @@ -8,10 +8,10 @@ autoexamples = false uninlined_format_args = "allow" [dependencies] -ledger = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.5.0" } -keycloak = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.5.0" } -registry = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.5.0" } -common = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.5.0" } +ledger = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.6.0" } +keycloak = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.6.0" } +registry = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.6.0" } +common = { git = "ssh://git@github.com/DLC-link/canton-lib", tag = "v0.6.0" } tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] } serde_json = "1" chrono = "0.4.42" @@ -22,7 +22,7 @@ futures = "0.3" dotenvy = "0.15" base64 = "0.22" log = "0.4" -canton-api-client = "3.3.0-0.1.0" +canton-api-client = "3.6.0-0.1.0" reqwest = { version = "0.12.24", features = ["json"] } zip = "2" semver = "1" diff --git a/examples/integration_test.rs b/examples/integration_test.rs index 01ca4ad..3ef8afc 100644 --- a/examples/integration_test.rs +++ b/examples/integration_test.rs @@ -823,9 +823,14 @@ async fn main() -> Result<(), String> { .await .map_err(|e| format!("Failed to list holdings for split: {}", e))?; - match holdings.first() { + // Pick a CBTC holding. list_holdings returns every Holding-template contract the + // sender owns, which on devnet includes legacy `CBTCV0RC8` instruments alongside + // `CBTC`. Splitting a non-CBTC holding while asserting `instrument_id = "CBTC"` + // below would make the registry reject the request with 400 "Given holdings are + // invalid". + match holdings.iter().find(|h| h.instrument_id == "CBTC") { None => { - print_skip("(no holdings available to split)"); + print_skip("(no CBTC holdings available to split)"); passed += 1; } Some(holding) => { diff --git a/examples/list_incoming_offers.rs b/examples/list_incoming_offers.rs index 0a94884..be925c5 100644 --- a/examples/list_incoming_offers.rs +++ b/examples/list_incoming_offers.rs @@ -72,7 +72,7 @@ async fn main() -> Result<(), String> { println!(" Full ID: {}", contract_id); // Extract transfer details - if let Some(Some(create_arg)) = &transfer.created_event.create_argument { + if let Some(create_arg) = &transfer.created_event.create_argument { if let Some(transfer_data) = create_arg.get("transfer") { if let Some(sender) = transfer_data.get("sender") { println!(" From: {}", sender.as_str().unwrap_or("unknown")); diff --git a/examples/list_outgoing_offers.rs b/examples/list_outgoing_offers.rs index fee2f95..510ba4c 100644 --- a/examples/list_outgoing_offers.rs +++ b/examples/list_outgoing_offers.rs @@ -72,7 +72,7 @@ async fn main() -> Result<(), String> { println!(" Full ID: {}", contract_id); // Extract transfer details - if let Some(Some(create_arg)) = &transfer.created_event.create_argument { + if let Some(create_arg) = &transfer.created_event.create_argument { if let Some(transfer_data) = create_arg.get("transfer") { if let Some(receiver) = transfer_data.get("receiver") { println!(" To: {}", receiver.as_str().unwrap_or("unknown")); diff --git a/src/accept.rs b/src/accept.rs index 1050731..acfbbc2 100644 --- a/src/accept.rs +++ b/src/accept.rs @@ -241,7 +241,7 @@ pub async fn accept_all(params: AcceptAllParams) -> Result Result Result, fn parse_consolidate_response( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result, String> { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events")?; + let events = &response.transaction.events; let mut result_cids = Vec::new(); for event in events { if let Some(exercised) = crate::event_helpers::as_exercised_event(event) { - if let Some(result) = exercised.exercise_result.as_ref() { + if let Some(Some(result)) = exercised.exercise_result.as_ref() { // Extract receiverHoldingCids from the Daml-encoded payload if let Some(receiver_cids) = result["output"]["value"]["receiverHoldingCids"].as_array() @@ -499,10 +495,12 @@ mod parser_tests { #[test] fn missing_events_returns_err() { + // `events` is required on the wire now, so an empty list stands in for + // "missing events"; the parser falls through to its post-loop check. let response = transaction_response("tx-x", json!(null)); let err = parse_consolidate_response(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("Failed to extract result holding CIDs"), "unexpected error: {err}" ); } diff --git a/src/credentials.rs b/src/credentials.rs index 24879dd..34600da 100644 --- a/src/credentials.rs +++ b/src/credentials.rs @@ -41,13 +41,16 @@ impl CredentialOffer { pub fn from_active_contract(contract: &JsActiveContract) -> Result { let contract_id = contract.created_event.contract_id.clone(); let template_id = contract.created_event.template_id.clone(); - let created_event_blob = contract.created_event.created_event_blob.clone(); + let created_event_blob = contract + .created_event + .created_event_blob + .clone() + .unwrap_or_default(); let args = contract .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -116,7 +119,6 @@ impl UserCredential { .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -240,7 +242,6 @@ pub async fn list_credential_offers( .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .and_then(|args| args.get("holder")) .and_then(|v| v.as_str()) @@ -290,7 +291,6 @@ pub async fn list_credentials( .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .and_then(|args| args.get("holder")) .and_then(|v| v.as_str()) @@ -339,7 +339,6 @@ pub async fn find_user_service(params: FindUserServiceParams) -> Result a, @@ -431,11 +430,7 @@ pub async fn accept_credential_offer( fn parse_accept_credential_offer_response( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events in transaction")?; + let events = &response.transaction.events; for event in events { if let Some(created) = crate::event_helpers::as_created_event(event) { @@ -635,10 +630,12 @@ mod parser_tests { #[test] fn missing_events_returns_err() { + // `events` is required on the wire now, so an empty list stands in for + // "missing events"; the parser falls through to its post-loop check. let response = transaction_response("tx-x", json!(null)); let err = parse_accept_credential_offer_response(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("No Credential contract was created"), "unexpected error: {err}" ); } diff --git a/src/dar_check.rs b/src/dar_check.rs index c49fefd..658d42f 100644 --- a/src/dar_check.rs +++ b/src/dar_check.rs @@ -53,14 +53,7 @@ pub async fn check(params: Params) -> Result { .await .map_err(|e| format!("Failed to fetch packages from participant: {}", e))?; - let participant_packages: HashSet = response - .package_ids - .ok_or_else(|| { - "Failed to fetch packages from participant: response missing 'package_ids' field" - .to_string() - })? - .into_iter() - .collect(); + let participant_packages: HashSet = response.package_ids.into_iter().collect(); info!( "Fetched {} packages from participant", diff --git a/src/mint_redeem/mint.rs b/src/mint_redeem/mint.rs index af1440c..78df241 100644 --- a/src/mint_redeem/mint.rs +++ b/src/mint_redeem/mint.rs @@ -106,11 +106,7 @@ pub async fn list_deposit_accounts( fn parse_created_deposit_account_cid( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events in transaction")?; + let events = &response.transaction.events; for event in events { if let Some(created) = crate::event_helpers::as_created_event(event) { @@ -455,12 +451,13 @@ mod parser_tests { #[test] fn returns_err_when_events_missing() { - // Envelope is missing `transaction.events` entirely. + // `events` is required on the wire now; pass an empty list and verify + // the parser falls through to its post-loop check. let response = transaction_response("tx-3", json!(null)); let err = parse_created_deposit_account_cid(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("No DepositAccount was created"), "unexpected error message: {err}" ); } diff --git a/src/mint_redeem/models.rs b/src/mint_redeem/models.rs index 089287f..d197b21 100644 --- a/src/mint_redeem/models.rs +++ b/src/mint_redeem/models.rs @@ -96,7 +96,6 @@ impl DepositAccount { .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -189,13 +188,16 @@ impl WithdrawAccount { pub fn from_active_contract(contract: &JsActiveContract) -> Result { let contract_id = contract.created_event.contract_id.clone(); let template_id = contract.created_event.template_id.clone(); - let created_event_blob = contract.created_event.created_event_blob.clone(); + let created_event_blob = contract + .created_event + .created_event_blob + .clone() + .unwrap_or_default(); let args = contract .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -274,7 +276,6 @@ impl WithdrawRequest { .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -342,7 +343,6 @@ impl Holding { .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .ok_or("createArgument is not an object")?; @@ -382,7 +382,6 @@ impl Holding { .created_event .create_argument .as_ref() - .and_then(|opt| opt.as_ref()) .and_then(|v| v.as_object()) .and_then(|args| args.get("lock")) .is_some_and(|lock| !lock.is_null()) diff --git a/src/mint_redeem/redeem.rs b/src/mint_redeem/redeem.rs index b6878f1..e0ec17b 100644 --- a/src/mint_redeem/redeem.rs +++ b/src/mint_redeem/redeem.rs @@ -129,11 +129,7 @@ pub async fn list_withdraw_accounts( fn parse_created_withdraw_account_cid( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events in transaction")?; + let events = &response.transaction.events; for event in events { if let Some(created) = crate::event_helpers::as_created_event(event) { @@ -155,11 +151,7 @@ fn parse_created_withdraw_account_cid( fn parse_submit_withdraw_response( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events in transaction")?; + let events = &response.transaction.events; for event in events { if let Some(created) = crate::event_helpers::as_created_event(event) { @@ -757,10 +749,12 @@ mod parser_tests { #[test] fn parse_created_withdraw_account_cid_missing_events() { + // `events` is required on the wire now; pass an empty list and verify + // the parser falls through to its post-loop check. let response = transaction_response("tx-x", json!(null)); let err = parse_created_withdraw_account_cid(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("No WithdrawAccount was created"), "unexpected error: {err}" ); } @@ -816,10 +810,12 @@ mod parser_tests { #[test] fn parse_submit_withdraw_response_missing_events() { + // `events` is required on the wire now; pass an empty list and verify + // the parser falls through to its post-loop check. let response = transaction_response("tx-x", json!(null)); let err = parse_submit_withdraw_response(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("No updated WithdrawAccount was found"), "unexpected error: {err}" ); } diff --git a/src/split.rs b/src/split.rs index 1fb8769..adc8bb5 100644 --- a/src/split.rs +++ b/src/split.rs @@ -136,16 +136,12 @@ async fn split_once( fn parse_split_response( response: &JsSubmitAndWaitForTransactionResponse, ) -> Result<(String, Vec), String> { - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events")?; + let events = &response.transaction.events; let mut exercise_result = None; for event in events { if let Some(exercised) = crate::event_helpers::as_exercised_event(event) { - if let Some(result) = exercised.exercise_result.as_ref() { + if let Some(Some(result)) = exercised.exercise_result.as_ref() { if result.is_object() { exercise_result = Some(result); break; @@ -339,10 +335,12 @@ mod parser_tests { #[test] fn missing_events_returns_err() { + // `events` is required on the wire now; pass an empty list and verify + // the parser falls through to its post-loop check. let response = transaction_response("tx-x", json!(null)); let err = parse_split_response(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("Failed to find ExercisedEvent"), "unexpected error: {err}" ); } diff --git a/src/transfer.rs b/src/transfer.rs index e89022a..95f5b6b 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -615,11 +615,7 @@ fn parse_transfer_response_value( return Err("Failed to find updateId in response".to_string()); } - let events = response - .transaction - .events - .as_ref() - .ok_or("Failed to find events in response")?; + let events = &response.transaction.events; // Find the ExercisedEvent with TransferFactory_Transfer choice let mut sender_change_cids = None; @@ -628,7 +624,7 @@ fn parse_transfer_response_value( for event in events { if let Some(exercised) = crate::event_helpers::as_exercised_event(event) { if exercised.choice == "TransferFactory_Transfer" { - if let Some(result) = exercised.exercise_result.as_ref() { + if let Some(Some(result)) = exercised.exercise_result.as_ref() { // Extract senderChangeCids if let Some(change_array) = result["senderChangeCids"].as_array() { sender_change_cids = Some( @@ -810,11 +806,13 @@ mod parser_tests { #[test] fn malformed_envelope_missing_events() { + // `events` is required on the wire now; pass an empty list and verify + // the parser falls through to its post-loop check. let response = transaction_response("tx-1", json!(null)); let err = parse_transfer_response_value(&response).unwrap_err(); assert!( - err.contains("Failed to find events"), + err.contains("Failed to find senderChangeCids"), "unexpected error: {err}" ); } diff --git a/src/utils.rs b/src/utils.rs index 8583868..6384ad1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -96,7 +96,7 @@ async fn fetch_transfers( let filtered: Vec = result .into_iter() .filter(|ac| { - if let Some(Some(create_arg)) = &ac.created_event.create_argument { + if let Some(create_arg) = &ac.created_event.create_argument { if let Some(transfer) = create_arg.get("transfer") { // Check if instrumentId is CBTC let is_cbtc = if let Some(instrument_id) = transfer.get("instrumentId") { @@ -183,6 +183,8 @@ pub(crate) mod test_fixtures { "observers": [], "createdAt": "1970-01-01T00:00:00Z", "packageName": "test-pkg", + "representativePackageId": "test-pkg", + "acsDelta": true, } }) } @@ -223,30 +225,32 @@ pub(crate) mod test_fixtures { "lastDescendantNodeId": 0_i32, "exerciseResult": exercise_result, "packageName": "test-pkg", + "acsDelta": true, } }) } /// Build a `JsSubmitAndWaitForTransactionResponse` from an updateId and - /// an `events` value (use `json!(null)` to omit events entirely). + /// an `events` value. Pass `json!(null)` to construct a response with an + /// empty events list (the typed model now treats `events` as required, so + /// "no events" is represented as `[]` rather than an absent field). /// Deserializes through the typed model so fixtures fail loudly when the /// shape diverges from canton-api-client's schema. pub fn transaction_response( update_id: &str, events: Value, ) -> JsSubmitAndWaitForTransactionResponse { - let mut transaction = json!({ + let events = if events.is_null() { json!([]) } else { events }; + let transaction = json!({ "updateId": update_id, "commandId": "", "workflowId": "", "effectiveAt": "1970-01-01T00:00:00Z", + "events": events, "offset": 1_i64, "synchronizerId": "test-synchronizer", "recordTime": "1970-01-01T00:00:00Z", }); - if !events.is_null() { - transaction["events"] = events; - } let envelope = json!({ "transaction": transaction }); serde_json::from_value(envelope).expect("test fixture is not a valid response") }