-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: balance rpc call * feat: client funcs for rpc call * chore: tests * chore(balances): add default/skip to relevant options * feat: add util folder for general rpc utils * fix: deserialize string floats into f64 * chore: add chrono crate, des timestamp into chrono DateTime * chore: fmt
- Loading branch information
Showing
6 changed files
with
192 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use std::collections::HashMap; | ||
|
||
use chrono::{DateTime, Utc}; | ||
use ethers::types::{Address, U256}; | ||
use reqwest::Url; | ||
|
||
use crate::{client::ClientResult, rpc::util::string_as_f64, SafeClient}; | ||
|
||
/// Safe balances response | ||
pub type BalancesResponse = Vec<BalanceResponse>; | ||
|
||
/// Safe balances request | ||
#[derive(Debug, Clone, serde::Serialize)] | ||
pub struct BalancesRequest; | ||
|
||
impl BalancesRequest { | ||
/// Return the URL to which to dispatch this request | ||
pub fn url(root: &Url, safe_address: Address) -> Url { | ||
let mut url = root.clone(); | ||
url.set_path(&format!( | ||
"api/v1/safes/{}/balances/usd/", | ||
ethers::utils::to_checksum(&safe_address, None) | ||
)); | ||
url | ||
} | ||
} | ||
|
||
/// Safe Balances request filters | ||
#[derive(Clone, serde::Serialize)] | ||
pub struct BalancesFilters<'a> { | ||
#[serde(flatten)] | ||
pub(crate) filters: HashMap<&'static str, String>, | ||
#[serde(skip)] | ||
pub(crate) client: &'a SafeClient, | ||
} | ||
|
||
impl<'a> BalancesFilters<'a> { | ||
/// Dispatch the request to the API, querying safe balances from the API | ||
pub async fn query(self, safe_address: Address) -> ClientResult<BalancesResponse> { | ||
self.client | ||
.filtered_balances(safe_address, self.filters) | ||
.await | ||
} | ||
|
||
/// Return the URL to which to dispatch this request | ||
pub fn url(root: &Url, safe_address: Address) -> reqwest::Url { | ||
let path = format!( | ||
"api/v1/safes/{}/balances/usd/", | ||
ethers::utils::to_checksum(&safe_address, None) | ||
); | ||
let mut url = root.clone(); | ||
url.set_path(&path); | ||
url | ||
} | ||
|
||
/// Instantiate from a client | ||
pub(crate) fn new(client: &'a SafeClient) -> Self { | ||
Self { | ||
filters: Default::default(), | ||
client, | ||
} | ||
} | ||
|
||
/// Filter by allowing only trusted tokens to be returned or not. | ||
pub fn trusted(mut self, trusted: bool) -> Self { | ||
self.filters.insert("trusted", trusted.to_string()); | ||
self | ||
} | ||
|
||
/// Filter by allowing known spam tokens to be returned or not. | ||
pub fn exclude_spam(mut self, exclude_spam: bool) -> Self { | ||
self.filters | ||
.insert("exclude_spam", exclude_spam.to_string()); | ||
self | ||
} | ||
} | ||
|
||
/// The individual response for every Safe token balance. | ||
#[derive(Debug, Clone, serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct BalanceResponse { | ||
/// The address of the token (null for native tokens) | ||
pub token_address: Option<Address>, | ||
/// The token info (null for native tokens) | ||
pub token: Option<Erc20Info>, | ||
/// The balance of the safe for the token | ||
pub balance: U256, | ||
/// The value in eth of the token | ||
pub eth_value: String, | ||
/// The timestamp of when the conversion was made | ||
pub timestamp: DateTime<Utc>, | ||
/// The balance in USD of the token | ||
/// The conversion rate used to calculate the fiat balance | ||
#[serde(default, deserialize_with = "string_as_f64")] | ||
pub fiat_balance: f64, | ||
/// The conversion rate used to calculate the fiat balance | ||
#[serde(default, deserialize_with = "string_as_f64")] | ||
pub fiat_conversion: f64, | ||
/// The currency used to calculate the fiat balance | ||
pub fiat_code: String, | ||
} | ||
|
||
/// The info about the token, if it's an ERC20 token. | ||
#[derive(Debug, Clone, serde::Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Erc20Info { | ||
/// The name of the token | ||
pub name: String, | ||
/// The token symbol | ||
pub symbol: String, | ||
/// The token decimals | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub decimals: Option<u32>, | ||
/// The logo URI, if it exists. | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub logo_uri: Option<String>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use serde::{ | ||
de::{Error, Unexpected, Visitor}, | ||
Deserializer, | ||
}; | ||
use std::fmt; | ||
|
||
/// Small utility function to deserialize a string into a f64. | ||
pub(crate) fn string_as_f64<'de, D>(deserializer: D) -> Result<f64, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
deserializer.deserialize_str(F64Visitor) | ||
} | ||
|
||
struct F64Visitor; | ||
impl<'de> Visitor<'de> for F64Visitor { | ||
type Value = f64; | ||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
formatter.write_str("a string representation of a f64") | ||
} | ||
fn visit_str<E>(self, value: &str) -> Result<f64, E> | ||
where | ||
E: Error, | ||
{ | ||
value.parse::<f64>().map_err(|_err| { | ||
E::invalid_value(Unexpected::Str(value), &"a string representation of a f64") | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters