#![allow(clippy::option_if_let_else)]
pub use crate::merkle::AccountInclusionProof;
use crate::serde_utils::{
hex_signature, hex_u32, hex_u32_opt, hex_u64, hex_u256, hex_u256_opt, hex_u256_opt_vec,
hex_u256_vec,
};
use alloy_primitives::{Address, Signature};
use ruint::aliases::U256;
use serde::{Deserialize, Serialize};
use strum::EnumString;
#[cfg(feature = "openapi")]
use utoipa::{IntoParams, ToSchema};
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateAccountRequest {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub recovery_address: Option<Address>,
#[cfg_attr(feature = "openapi", schema(value_type = Vec<String>, format = "hex"))]
pub authenticator_addresses: Vec<Address>,
#[serde(with = "hex_u256_vec")]
#[cfg_attr(feature = "openapi", schema(value_type = Vec<String>, format = "hex"))]
pub authenticator_pubkeys: Vec<U256>,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub offchain_signer_commitment: U256,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateAuthenticatorRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub old_authenticator_address: Address,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_authenticator_address: Address,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub old_offchain_signer_commitment: U256,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_offchain_signer_commitment: U256,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
#[serde(with = "hex_u32")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub pubkey_id: u32,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_authenticator_pubkey: U256,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct InsertAuthenticatorRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_authenticator_address: Address,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub old_offchain_signer_commitment: U256,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_offchain_signer_commitment: U256,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
#[serde(with = "hex_u32")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub pubkey_id: u32,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_authenticator_pubkey: U256,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct RemoveAuthenticatorRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub authenticator_address: Address,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub old_offchain_signer_commitment: U256,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_offchain_signer_commitment: U256,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
#[serde(default, with = "hex_u32_opt")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<String>, format = "hex"))]
pub pubkey_id: Option<u32>,
#[serde(default, with = "hex_u256_opt")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<String>, format = "hex"))]
pub authenticator_pubkey: Option<U256>,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateRecoveryAgentRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_recovery_agent: Address,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct ExecuteRecoveryAgentUpdateRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct CancelRecoveryAgentUpdateRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct RecoverAccountRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub leaf_index: u64,
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_authenticator_address: Address,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub old_offchain_signer_commitment: U256,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub new_offchain_signer_commitment: U256,
#[serde(with = "hex_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub signature: Signature,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub nonce: U256,
#[serde(default, with = "hex_u256_opt")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<String>, format = "hex"))]
pub new_authenticator_pubkey: Option<U256>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(transparent)]
pub struct GatewayRequestId(String);
impl GatewayRequestId {
pub fn new(id: impl Into<String>) -> Self {
Self(format!("gw_{}", id.into()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn as_str_without_prefix(&self) -> &str {
self.0.strip_prefix("gw_").unwrap_or(&self.0)
}
}
impl std::fmt::Display for GatewayRequestId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for GatewayRequestId {
fn as_ref(&self) -> &str {
&self.0
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct GatewayStatusResponse {
pub request_id: GatewayRequestId,
pub kind: GatewayRequestKind,
pub status: GatewayRequestState,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum GatewayRequestKind {
CreateAccount,
UpdateAuthenticator,
InsertAuthenticator,
RemoveAuthenticator,
UpdateRecoveryAgent,
CancelRecoveryAgentUpdate,
ExecuteRecoveryAgentUpdate,
RecoverAccount,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(tag = "state", rename_all = "snake_case")]
pub enum GatewayRequestState {
Queued,
Batching,
Submitted {
tx_hash: String,
},
Finalized {
tx_hash: String,
},
Failed {
error: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
error_code: Option<GatewayErrorCode>,
},
}
impl GatewayRequestState {
pub fn failed(error: impl Into<String>, error_code: Option<GatewayErrorCode>) -> Self {
Self::Failed {
error: error.into(),
error_code,
}
}
#[must_use]
pub fn failed_from_code(code: GatewayErrorCode) -> Self {
Self::Failed {
error: code.to_string(),
error_code: Some(code),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerPackedAccountRequest {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"))]
pub authenticator_address: Address,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerPackedAccountResponse {
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x1"))]
pub packed_account_data: U256,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerQueryRequest {
#[serde(with = "hex_u64")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x1"))]
pub leaf_index: u64,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerSignatureNonceResponse {
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x0"))]
pub signature_nonce: U256,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerRecoveryAgentResponse {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x0"))]
pub recovery_agent: Address,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerPendingRecoveryAgentResponse {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x0"))]
pub pending_recovery_agent: Address,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex", example = "0x0"))]
pub execute_after: U256,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct IndexerAuthenticatorPubkeysResponse {
#[serde(with = "hex_u256_opt_vec")]
#[cfg_attr(feature = "openapi", schema(value_type = Vec<Option<String>>, format = "hex"))]
pub authenticator_pubkeys: Vec<Option<U256>>,
#[serde(with = "hex_u256")]
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub offchain_signer_commitment: U256,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct HealthResponse {
pub success: bool,
}
#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "openapi", derive(IntoParams, ToSchema))]
pub struct IsValidRootQuery {
#[cfg_attr(feature = "openapi", schema(value_type = String, format = "hex"))]
pub root: String,
}
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[derive(Debug, Serialize, Deserialize)]
pub struct IsValidRootResponse {
pub valid: bool,
}
#[derive(Debug, Clone, EnumString, Serialize, Deserialize, strum::Display)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum IndexerErrorCode {
InternalServerError,
NotFound,
InvalidLeafIndex,
Locked,
AccountDoesNotExist,
RequestTimeout,
}
#[derive(Debug, Clone, Deserialize, Serialize, strum::Display)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum GatewayErrorCode {
InternalServerError,
NotFound,
BadRequest,
BatcherUnavailable,
AuthenticatorAlreadyExists,
AuthenticatorDoesNotExist,
MismatchedSignatureNonce,
PubkeyIdInUse,
PubkeyIdOutOfBounds,
AuthenticatorDoesNotBelongToAccount,
TransactionReverted,
ConfirmationError,
DuplicateRequestInFlight,
RateLimitExceeded,
RequestTimeout,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ServiceApiError<T>
where
T: Clone,
{
pub code: T,
pub message: String,
}
impl<T> ServiceApiError<T>
where
T: Clone,
{
pub const fn new(code: T, message: String) -> Self {
Self { code, message }
}
}
#[derive(serde::Serialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct AccountInclusionProofSchema {
#[cfg_attr(
feature = "openapi",
schema(value_type = String, format = "hex", example = "0x1a2b3c4d5e6f7890")
)]
pub root: String,
#[cfg_attr(
feature = "openapi",
schema(value_type = String, format = "hex", example = "0x2a")
)]
pub leaf_index: String,
#[cfg_attr(feature = "openapi", schema(value_type = Vec<String>, format = "hex"))]
pub siblings: Vec<String>,
#[cfg_attr(feature = "openapi", schema(value_type = Vec<String>, format = "hex"))]
pub authenticator_pubkeys: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_authenticator_request_signature_serializes_as_hex_string() {
let request = InsertAuthenticatorRequest {
leaf_index: 42,
new_authenticator_address: Address::from([0x11; 20]),
old_offchain_signer_commitment: U256::from(0x1234_u64),
new_offchain_signer_commitment: U256::from(0x5678_u64),
signature: Signature::new(U256::from(0xdead_u64), U256::from(0xbeef_u64), false),
nonce: U256::from(0x9abc_u64),
pubkey_id: 7,
new_authenticator_pubkey: U256::from(0xdef0_u64),
};
let json = serde_json::to_string(&request).unwrap();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(
value["new_authenticator_address"]
.as_str()
.expect("new_authenticator_address should be a string"),
"0x1111111111111111111111111111111111111111"
);
assert_eq!(
value["old_offchain_signer_commitment"]
.as_str()
.expect("old_offchain_signer_commitment should be a string"),
"0x1234"
);
assert_eq!(
value["new_offchain_signer_commitment"]
.as_str()
.expect("new_offchain_signer_commitment should be a string"),
"0x5678"
);
assert_eq!(
value["signature"]
.as_str()
.expect("signature should be a string"),
"0x000000000000000000000000000000000000000000000000000000000000dead000000000000000000000000000000000000000000000000000000000000beef1b"
);
assert_eq!(
value["nonce"].as_str().expect("nonce should be a string"),
"0x9abc"
);
assert_eq!(
value["new_authenticator_pubkey"]
.as_str()
.expect("new_authenticator_pubkey should be a string"),
"0xdef0"
);
let roundtripped: InsertAuthenticatorRequest = serde_json::from_str(&json).unwrap();
assert_eq!(roundtripped.leaf_index, request.leaf_index);
assert_eq!(
roundtripped.new_authenticator_address,
request.new_authenticator_address
);
assert_eq!(
roundtripped.old_offchain_signer_commitment,
request.old_offchain_signer_commitment
);
assert_eq!(
roundtripped.new_offchain_signer_commitment,
request.new_offchain_signer_commitment
);
assert_eq!(roundtripped.signature, request.signature);
assert_eq!(roundtripped.nonce, request.nonce);
assert_eq!(roundtripped.pubkey_id, request.pubkey_id);
assert_eq!(
roundtripped.new_authenticator_pubkey,
request.new_authenticator_pubkey
);
}
}