Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions crates/core/src/webhooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,45 @@ mod tests {
.unwrap();
}

// Wire-inspection regression: the API expects `eventHashes` (camelCase)
// and returns 500 if it sees `event_hashes`. Confirm the field reaches the
// wire under the camelCase key.
#[tokio::test]
async fn create_webhook_from_template_wire_body_uses_camelcase_event_hashes() {
use wiremock::matchers::body_partial_json;
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path_regex("/webhooks/template/evmContractEvents"))
.and(body_partial_json(serde_json::json!({
"templateArgs": {
"eventHashes": ["0xabcd"],
}
})))
.respond_with(ResponseTemplate::new(201).set_body_json(webhook_response_json()))
.mount(&server)
.await;
let sdk = make_sdk(format!("{}/", server.uri()));
let template_args = TemplateArgs::EvmContractEvents(EvmContractEventsTemplate {
contracts: vec!["0xa0b8".to_string()],
event_hashes: Some(vec!["0xabcd".to_string()]),
});
let params = CreateWebhookFromTemplateParams {
name: "test-webhook".to_string(),
network: "ethereum-mainnet".to_string(),
notification_email: None,
destination_attributes: WebhookDestinationAttributes {
url: "https://example.com/hook".to_string(),
security_token: None,
compression: None,
},
template_args,
};
sdk.webhooks
.create_webhook_from_template(&params)
.await
.unwrap();
}

#[tokio::test]
async fn update_webhook_template_api_error() {
let server = MockServer::start().await;
Expand Down
4 changes: 4 additions & 0 deletions crates/core/src/webhooks/webhook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl EvmWalletFilterTemplate {
#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
#[cfg_attr(feature = "node", napi(object))]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EvmContractEventsTemplate {
/// Contract addresses to watch for events.
pub contracts: Vec<String>,
Expand Down Expand Up @@ -547,6 +548,9 @@ mod template_args_tests {
});
let json = serde_json::to_string(&args).unwrap();
assert!(json.contains(r#""templateId":"evmContractEvents""#));
// API expects camelCase `eventHashes` — snake_case is silently rejected with a 500.
assert!(json.contains(r#""eventHashes":["0x1234"]"#));
assert!(!json.contains("event_hashes"));
let parsed: TemplateArgs = serde_json::from_str(&json).unwrap();
assert!(matches!(parsed, TemplateArgs::EvmContractEvents(_)));
}
Expand Down
Loading