use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ActionKind {
Order,
Cancel,
CancelByCloid,
Modify,
BatchModify,
TwapOrder,
TwapCancel,
UpdateLeverage,
UpdateIsolatedMargin,
ScheduleCancel,
Noop,
AgentSetAbstraction,
AgentSendAsset,
SetReferrer,
CreateSubAccount,
SubAccountTransfer,
SubAccountSpotTransfer,
VaultTransfer,
BorrowLend,
UserOutcome,
UsdClassTransfer,
UsdSend,
SpotSend,
SendAsset,
Withdraw3,
CDeposit,
CWithdraw,
TokenDelegate,
ApproveAgent,
ApproveBuilderFee,
UserSetAbstraction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
Main,
Agent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Signers {
Main,
Either,
}
impl Signers {
#[must_use]
pub const fn takes_agent(self) -> bool {
matches!(self, Signers::Either)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Why {
UserSigned,
WrongAccount,
}
impl Why {
#[must_use]
pub const fn words(self) -> &'static str {
match self {
Why::UserSigned => "an agent's signature recovers to the agent, which holds nothing",
Why::WrongAccount => "an agent's would open or drain the wrong account",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("{kind} is signed by the account's own key: {}", why.words())]
pub struct Refused {
pub kind: ActionKind,
pub why: Why,
}
pub fn may_sign(kind: ActionKind, key: Key) -> Result<(), Refused> {
match (kind.why(), key) {
(Some(why), Key::Agent) => Err(Refused { kind, why }),
_ => Ok(()),
}
}
impl ActionKind {
pub const ALL: [ActionKind; 31] = [
ActionKind::Order,
ActionKind::Cancel,
ActionKind::CancelByCloid,
ActionKind::Modify,
ActionKind::BatchModify,
ActionKind::TwapOrder,
ActionKind::TwapCancel,
ActionKind::UpdateLeverage,
ActionKind::UpdateIsolatedMargin,
ActionKind::ScheduleCancel,
ActionKind::Noop,
ActionKind::AgentSetAbstraction,
ActionKind::AgentSendAsset,
ActionKind::SetReferrer,
ActionKind::CreateSubAccount,
ActionKind::SubAccountTransfer,
ActionKind::SubAccountSpotTransfer,
ActionKind::VaultTransfer,
ActionKind::BorrowLend,
ActionKind::UserOutcome,
ActionKind::UsdClassTransfer,
ActionKind::UsdSend,
ActionKind::SpotSend,
ActionKind::SendAsset,
ActionKind::Withdraw3,
ActionKind::CDeposit,
ActionKind::CWithdraw,
ActionKind::TokenDelegate,
ActionKind::ApproveAgent,
ActionKind::ApproveBuilderFee,
ActionKind::UserSetAbstraction,
];
#[must_use]
pub const fn wire(self) -> &'static str {
match self {
ActionKind::Order => "order",
ActionKind::Cancel => "cancel",
ActionKind::CancelByCloid => "cancelByCloid",
ActionKind::Modify => "modify",
ActionKind::BatchModify => "batchModify",
ActionKind::TwapOrder => "twapOrder",
ActionKind::TwapCancel => "twapCancel",
ActionKind::UpdateLeverage => "updateLeverage",
ActionKind::UpdateIsolatedMargin => "updateIsolatedMargin",
ActionKind::ScheduleCancel => "scheduleCancel",
ActionKind::Noop => "noop",
ActionKind::AgentSetAbstraction => "agentSetAbstraction",
ActionKind::AgentSendAsset => "agentSendAsset",
ActionKind::SetReferrer => "setReferrer",
ActionKind::CreateSubAccount => "createSubAccount",
ActionKind::SubAccountTransfer => "subAccountTransfer",
ActionKind::SubAccountSpotTransfer => "subAccountSpotTransfer",
ActionKind::VaultTransfer => "vaultTransfer",
ActionKind::BorrowLend => "borrowLend",
ActionKind::UserOutcome => "userOutcome",
ActionKind::UsdClassTransfer => "usdClassTransfer",
ActionKind::UsdSend => "usdSend",
ActionKind::SpotSend => "spotSend",
ActionKind::SendAsset => "sendAsset",
ActionKind::Withdraw3 => "withdraw3",
ActionKind::CDeposit => "cDeposit",
ActionKind::CWithdraw => "cWithdraw",
ActionKind::TokenDelegate => "tokenDelegate",
ActionKind::ApproveAgent => "approveAgent",
ActionKind::ApproveBuilderFee => "approveBuilderFee",
ActionKind::UserSetAbstraction => "userSetAbstraction",
}
}
#[must_use]
pub fn from_wire(text: &str) -> Option<ActionKind> {
ActionKind::ALL.into_iter().find(|kind| kind.wire() == text)
}
#[must_use]
pub const fn is_user_signed(self) -> bool {
matches!(
self,
ActionKind::UsdClassTransfer
| ActionKind::UsdSend
| ActionKind::SpotSend
| ActionKind::SendAsset
| ActionKind::Withdraw3
| ActionKind::CDeposit
| ActionKind::CWithdraw
| ActionKind::TokenDelegate
| ActionKind::ApproveAgent
| ActionKind::ApproveBuilderFee
| ActionKind::UserSetAbstraction
)
}
#[must_use]
pub const fn signers(self) -> Signers {
match self.why() {
Some(_) => Signers::Main,
None => Signers::Either,
}
}
#[must_use]
pub const fn why(self) -> Option<Why> {
if self.is_user_signed() {
return Some(Why::UserSigned);
}
match self {
ActionKind::CreateSubAccount
| ActionKind::SubAccountTransfer
| ActionKind::SubAccountSpotTransfer
| ActionKind::UserOutcome => Some(Why::WrongAccount),
_ => None,
}
}
}
impl fmt::Display for ActionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.wire())
}
}
#[cfg(test)]
#[path = "tests/signers.rs"]
mod tests;