use crate::client::{ApiError, Transport, info, info_request};
use crate::deploy::outcomes::actions::{Fraction, Settlement};
use crate::markets::outcomes::coin::Coin;
use crate::utils::{Address, Dec, OutcomeSide};
use serde::Deserialize;
#[must_use]
pub fn outcome_meta_request() -> String {
info_request("outcomeMeta", &[])
}
pub async fn outcome_meta(transport: &impl Transport) -> Result<OutcomeMeta, ApiError> {
info(transport, outcome_meta_request()).await
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OutcomeMeta {
#[serde(default)]
pub outcomes: Vec<Outcome>,
#[serde(default)]
pub questions: Vec<Question>,
#[serde(default)]
pub deployers: Vec<Deployer>,
#[serde(default)]
pub fee_scale: Option<Dec>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Outcome {
pub outcome: u32,
#[serde(default)]
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub side_specs: Vec<SideSpec>,
#[serde(default)]
pub quote_token: Option<String>,
#[serde(default)]
pub venue: Option<String>,
#[serde(default)]
pub deployer_fee_scale: Option<Dec>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct SideSpec {
#[serde(default)]
pub name: String,
}
impl Outcome {
#[must_use]
pub const fn coin(&self, side: OutcomeSide) -> Coin {
Coin::new(self.outcome, side)
}
#[must_use]
pub fn settlement(&self, fraction: Fraction) -> Option<Settlement> {
let [first, second] = self.side_specs.as_slice() else {
return None;
};
Some(Settlement {
outcome: self.outcome,
fraction,
name: self.name.clone(),
description: self.description.clone(),
sides: [first.name.clone(), second.name.clone()],
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Question {
pub question: u32,
#[serde(default)]
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub fallback_outcome: Option<u32>,
#[serde(default)]
pub named_outcomes: Vec<u32>,
#[serde(default)]
pub settled_named_outcomes: Vec<u32>,
}
impl Question {
pub fn unsettled(&self) -> impl Iterator<Item = u32> + '_ {
self.named_outcomes.iter().copied().filter(|outcome| !self.settled_named_outcomes.contains(outcome))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Deployer {
pub deployer: Address,
#[serde(default)]
pub venue: Option<String>,
#[serde(default)]
pub sub_deployers: Vec<(String, Vec<Address>)>,
}
impl OutcomeMeta {
#[must_use]
pub fn outcome(&self, outcome: u32) -> Option<&Outcome> {
self.outcomes.iter().find(|listed| listed.outcome == outcome)
}
#[must_use]
pub fn question(&self, question: u32) -> Option<&Question> {
self.questions.iter().find(|listed| listed.question == question)
}
#[must_use]
pub fn question_of(&self, outcome: u32) -> Option<&Question> {
self.questions
.iter()
.find(|question| question.fallback_outcome == Some(outcome) || question.named_outcomes.contains(&outcome))
}
#[must_use]
pub fn deployer(&self, venue: &str) -> Option<&Deployer> {
self.deployers.iter().find(|deployer| deployer.venue.as_deref() == Some(venue))
}
#[must_use]
pub fn venue_of(&self, who: &str) -> Option<&str> {
let who = who.trim();
match who.parse::<Address>() {
Ok(address) => self.deployers.iter().find(|one| one.deployer == address)?.venue.as_deref(),
Err(_) => self
.deployers
.iter()
.find_map(|one| one.venue.as_deref().filter(|venue| venue.eq_ignore_ascii_case(who))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubDeployer {
pub address: Address,
pub actions: Vec<String>,
}
impl SubDeployer {
#[must_use]
pub fn settles(&self) -> bool {
self.actions.iter().any(|action| action.starts_with("settle"))
}
#[must_use]
pub fn registers(&self) -> bool {
self.actions.iter().any(|action| action.starts_with("register"))
}
}
impl Deployer {
#[must_use]
pub fn keys(&self) -> Vec<SubDeployer> {
let mut found: Vec<SubDeployer> = Vec::new();
for (action, addresses) in &self.sub_deployers {
for address in addresses {
match found.iter_mut().find(|key| key.address == *address) {
Some(key) => key.actions.push(action.clone()),
None => found.push(SubDeployer { address: *address, actions: vec![action.clone()] }),
}
}
}
found.sort_by(|a, b| (b.actions.len(), b.address).cmp(&(a.actions.len(), a.address)));
found
}
}
#[cfg(test)]
#[path = "tests/meta.rs"]
mod tests;