From a1551dccdd9ac26f5627fcb8ecb4538400b1902b Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 27 Mar 2024 13:17:20 +0100 Subject: [PATCH 1/4] fix: panic if no response from api & detect the right erc20 contract --- bot/src/bot.rs | 47 ++++++++++++++++++--------- bot/src/config.rs | 65 +++++++++++++++++++++++++++++++++++-- bot/src/models.rs | 24 ++++++++++++-- bot/src/pipelines.rs | 25 +++++++++----- bot/src/starknetid_utils.rs | 4 +-- config.template.toml | 9 +++++ 6 files changed, 142 insertions(+), 32 deletions(-) diff --git a/bot/src/bot.rs b/bot/src/bot.rs index 4c74baa..8a7ea35 100644 --- a/bot/src/bot.rs +++ b/bot/src/bot.rs @@ -28,6 +28,7 @@ use crate::models::{AggregateResult, AggregateResults, DomainAggregateResult, Me use crate::pipelines::{get_auto_renewal_altcoins_data, get_auto_renewal_data}; use crate::starknet_utils::check_pending_transactions; use crate::starknetid_utils::{get_altcoin_quote, get_balances, get_renewal_price_eth}; +use crate::utils::to_hex; use crate::utils::{from_uint256, hex_to_bigdecimal, to_uint256}; use crate::{config::Config, models::AppState}; @@ -66,7 +67,18 @@ pub async fn get_domains_ready_for_renewal( // Fetch balances for all renewers let renewer_and_erc20: Vec<(String, String)> = results .iter() - .map(|result| (result.renewer_address.clone(), result.erc20_addr.clone())) + .map(|result| { + ( + result.renewer_address.clone(), + // get the erc20 address for the given auto_renew_contract + to_hex( + *config + .altcoins_mapping + .get(&result.auto_renew_contract) + .unwrap(), + ), + ) + }) .collect(); let balances = get_balances(config, renewer_and_erc20.clone()).await; @@ -85,9 +97,7 @@ pub async fn get_domains_ready_for_renewal( balances_iter.next().expect("Expected high not found"), ); let mut outer_map = dynamic_balances.lock().unwrap(); - let inner_map = outer_map - .entry(address.clone()) - .or_default(); + let inner_map = outer_map.entry(address.clone()).or_default(); inner_map.insert(erc20.clone(), balance); } @@ -111,16 +121,18 @@ pub async fn get_domains_ready_for_renewal( if FieldElement::from_hex_be(erc20).unwrap() == config.contract.erc20 { renewal_price_eth } else { - match get_altcoin_quote(config, result.erc20_addr.clone()).await { + match get_altcoin_quote(config, erc20.to_string()).await { Ok(quote) => { (quote * renewal_price_eth) / BigInt::from_str("1000000000000000000").unwrap() } Err(e) => { - println!("Error while fetching quote: {:?}", e); - // this case can happen if the quote is not in the right range - // we return 0 and won't renew this domain - BigInt::from(0) + // in case get_altcoin_quote endpoint returns an error we panic with the error + logger.severe(format!( + "Error while fetching quote on starknetid server: {:?}", + e + )); + panic!("Error while fetching quote on starknetid server: {:?}", e) } } }; @@ -130,6 +142,7 @@ pub async fn get_domains_ready_for_renewal( logger, BigDecimal::from(balance.to_owned()), BigDecimal::from(renewal_price.to_owned()), + erc20.clone(), ) .await; @@ -138,7 +151,7 @@ pub async fn get_domains_ready_for_renewal( dynamic_balances .lock() .unwrap() - .entry(result.erc20_addr) + .entry(erc20.to_string()) .or_default() .insert(address.to_owned(), new_balance); }; @@ -186,6 +199,7 @@ async fn process_aggregate_result( _logger: &Logger, balance: BigDecimal, renewal_price: BigDecimal, + erc20_addr: String, ) -> Option { // Skip the rest if auto-renewal is not enabled if !result.enabled || result.allowance.is_none() { @@ -193,18 +207,19 @@ async fn process_aggregate_result( } let renewer_addr = FieldElement::from_hex_be(&result.renewer_address).unwrap(); - let erc20_allowance = if let Some(approval_value) = result.approval_value { + // map the vec of approval_values to get tha approval_value for the erc20_addr selected + let erc20_allowance = if let Some(approval_value) = result + .approval_values + .iter() + .find(|&data| data.erc20_addr == erc20_addr) + .map(|data| data.approval_value.clone()) + { hex_to_bigdecimal(&approval_value).unwrap() } else { BigDecimal::from(0) }; let allowance = hex_to_bigdecimal(&result.allowance.unwrap()).unwrap(); - // if renewal_price is 0, we don't renew the domain - if renewal_price == BigDecimal::from(0) { - return None; - } - // Check user meta hash let mut tax_price = BigDecimal::from(0); let mut meta_hash = FieldElement::ZERO; diff --git a/bot/src/config.rs b/bot/src/config.rs index 9b19009..3f8b1e1 100644 --- a/bot/src/config.rs +++ b/bot/src/config.rs @@ -1,6 +1,11 @@ +use serde::de::MapAccess; +use serde::de::Visitor; use serde::Deserialize; +use serde::Deserializer; use starknet::core::types::FieldElement; +use std::collections::HashMap; use std::env; +use std::fmt; use std::fs; macro_rules! pub_struct { @@ -65,17 +70,73 @@ pub_struct!(Clone, Deserialize; Server { starknetid_api: String, }); -pub_struct!(Clone, Deserialize; Config { +pub_struct!(Clone, Deserialize; Altcoin { + address: FieldElement, + renewal_contract: FieldElement, +}); + +pub_struct!(Clone; Config { contract: Contract, database: Database, account: MyAccount, - renewals : Renewals, + renewals: Renewals, indexer_server: IndexerServer, rpc: Rpc, watchtower: Watchtower, server: Server, + altcoins_mapping: HashMap, }); +impl<'de> Deserialize<'de> for Config { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct OuterConfig { + contract: Contract, + database: Database, + account: MyAccount, + renewals: Renewals, + indexer_server: IndexerServer, + rpc: Rpc, + watchtower: Watchtower, + server: Server, + altcoins: HashMap, + } + + let OuterConfig { + contract, + database, + account, + renewals, + indexer_server, + rpc, + watchtower, + server, + altcoins, + } = OuterConfig::deserialize(deserializer)?; + + // Build atcoins mapping + let altcoins_mapping = altcoins + .into_values() + .map(|val| (val.renewal_contract, val.address)) + .collect(); + + Ok(Config { + contract, + database, + account, + renewals, + indexer_server, + rpc, + watchtower, + server, + altcoins_mapping, + }) + } +} + pub fn load() -> Config { let args: Vec = env::args().collect(); let config_path = if args.len() <= 1 { diff --git a/bot/src/models.rs b/bot/src/models.rs index abd7acb..bce43f8 100644 --- a/bot/src/models.rs +++ b/bot/src/models.rs @@ -41,19 +41,39 @@ pub struct Cursor { pub from: Option, } +// #[derive(Clone, Debug, Deserialize, Serialize)] +// pub struct DomainAggregateResult { +// pub domain: String, +// pub expiry: Option, +// pub renewer_address: String, +// pub enabled: bool, +// pub approval_value: Option, +// pub allowance: Option, +// pub last_renewal: Option, +// pub meta_hash: Option, +// pub _cursor: Cursor, +// pub erc20_addr: String, +// pub auto_renew_contract: FieldElement, +// } + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AutoRenewAllowance { + pub approval_value: String, + pub erc20_addr: String, +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DomainAggregateResult { pub domain: String, pub expiry: Option, pub renewer_address: String, pub enabled: bool, - pub approval_value: Option, pub allowance: Option, pub last_renewal: Option, pub meta_hash: Option, pub _cursor: Cursor, - pub erc20_addr: String, pub auto_renew_contract: FieldElement, + pub approval_values: Vec, } pub struct AggregateResult { diff --git a/bot/src/pipelines.rs b/bot/src/pipelines.rs index ba8a021..a796200 100644 --- a/bot/src/pipelines.rs +++ b/bot/src/pipelines.rs @@ -58,7 +58,6 @@ pub async fn get_auto_renewal_data( }}, doc! { "$unwind": { "path": "$approval_info", "preserveNullAndEmptyArrays": true } }, doc! { "$addFields": { - "erc20_addr": erc20_addr, "auto_renew_contract": auto_renew_contract, }}, doc! { "$group": { @@ -66,13 +65,17 @@ pub async fn get_auto_renewal_data( "expiry": { "$first": "$domain_info.expiry" }, "renewer_address": { "$first": "$renewer_address" }, "enabled": { "$first": "$enabled" }, - "approval_value": { "$first": { "$ifNull": [ "$approval_info.allowance", "0x0" ] } }, "allowance": { "$first": "$allowance" }, "last_renewal": { "$first": "$last_renewal" }, "meta_hash": { "$first": "$meta_hash" }, "_cursor": { "$first": "$_cursor" }, - "erc20_addr": { "$first": "$erc20_addr" }, "auto_renew_contract": { "$first": "$auto_renew_contract" }, + "approval_values": { + "$push": { + "approval_value": "$approval_info.allowance", + "erc20_addr": erc20_addr + } + }, }}, doc! { "$project": { "_id": 0, @@ -80,13 +83,12 @@ pub async fn get_auto_renewal_data( "expiry": 1, "renewer_address": 1, "enabled": 1, - "approval_value": 1, "allowance": 1, "last_renewal": 1, "meta_hash": 1, "_cursor": 1, - "erc20_addr": 1, "auto_renew_contract": 1, + "approval_values": 1, }}, ]; @@ -154,13 +156,17 @@ pub async fn get_auto_renewal_altcoins_data( "expiry": { "$first": "$domain_info.expiry" }, "renewer_address": { "$first": "$renewer_address" }, "enabled": { "$first": "$enabled" }, - "approval_value": { "$first": { "$ifNull": [ "$approval_info.allowance", "0x0" ] } }, "allowance": { "$first": "$allowance" }, "last_renewal": { "$first": "$last_renewal" }, "meta_hash": { "$first": "$meta_hash" }, "_cursor": { "$first": "$_cursor" }, - "erc20_addr": { "$first": "$approval_info.erc20_addr" }, "auto_renew_contract": { "$first": "$auto_renew_contract" }, + "approval_values": { + "$push": { + "approval_value": "$approval_info.allowance", + "erc20_addr": "$approval_info.erc20_addr" + } + }, }}, doc! { "$project": { "_id": 0, @@ -168,13 +174,12 @@ pub async fn get_auto_renewal_altcoins_data( "expiry": 1, "renewer_address": 1, "enabled": 1, - "approval_value": 1, "allowance": 1, "last_renewal": 1, "meta_hash": 1, "_cursor": 1, - "erc20_addr": 1, "auto_renew_contract": 1, + "approval_values": 1, }}, ]; @@ -190,5 +195,7 @@ pub async fn get_auto_renewal_altcoins_data( // Check if the conversion was successful let results = results?; + println!("results: {:?}", results); + Ok(results) } diff --git a/bot/src/starknetid_utils.rs b/bot/src/starknetid_utils.rs index 8eb47e1..1df3f16 100644 --- a/bot/src/starknetid_utils.rs +++ b/bot/src/starknetid_utils.rs @@ -49,9 +49,7 @@ pub async fn get_altcoin_quote(config: &Config, erc20: String) -> Result match client.get(&url).send().await { Ok(response) => match response.text().await { Ok(text) => match serde_json::from_str::(&text) { - Ok(results) => { - Ok(BigInt::from_str(&results.quote).unwrap()) - } + Ok(results) => Ok(BigInt::from_str(&results.quote).unwrap()), Err(err) => Err(anyhow!("Error parsing response: {:?}", err)), }, Err(err) => Err(anyhow!("Error fetching response: {:?}", err)), diff --git a/config.template.toml b/config.template.toml index 4c624ec..41e7eb1 100644 --- a/config.template.toml +++ b/config.template.toml @@ -38,3 +38,12 @@ rpc_url = "https://starknet-goerli.g.alchemy.com/v2/xxxxxxx" [server] starknetid_api = "https://api.starknet.id" + +[altcoins] +[altcoins.ETH] +address = "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" +renewal_contract = "0x020379Ba14750ECEE8dde204D0649808BCf6D32E9fDe81Ca952Ab0360cdC0937" + +[altcoins.STRK] +address = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" +renewal_contract = "0x078F63fcD145Ddc6ca932E562b466AFbfD7c9E882C9aa70f3e5b2ce05cD892eA" \ No newline at end of file From 7fa77959e1b80232fa0aae88275fe9d588689c08 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 27 Mar 2024 13:18:28 +0100 Subject: [PATCH 2/4] fix: remove comments --- bot/src/models.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/bot/src/models.rs b/bot/src/models.rs index bce43f8..8435fec 100644 --- a/bot/src/models.rs +++ b/bot/src/models.rs @@ -41,21 +41,6 @@ pub struct Cursor { pub from: Option, } -// #[derive(Clone, Debug, Deserialize, Serialize)] -// pub struct DomainAggregateResult { -// pub domain: String, -// pub expiry: Option, -// pub renewer_address: String, -// pub enabled: bool, -// pub approval_value: Option, -// pub allowance: Option, -// pub last_renewal: Option, -// pub meta_hash: Option, -// pub _cursor: Cursor, -// pub erc20_addr: String, -// pub auto_renew_contract: FieldElement, -// } - #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AutoRenewAllowance { pub approval_value: String, From 889997405b54df184742e6f856d723104cd455db Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 27 Mar 2024 13:36:37 +0100 Subject: [PATCH 3/4] fix: price for domain len = 3 & 4 --- bot/src/starknetid_utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/src/starknetid_utils.rs b/bot/src/starknetid_utils.rs index 1df3f16..939682e 100644 --- a/bot/src/starknetid_utils.rs +++ b/bot/src/starknetid_utils.rs @@ -13,8 +13,8 @@ use crate::{config::Config, starknet_utils::create_jsonrpc_client}; lazy_static::lazy_static! { static ref PRICE_DOMAIN_LEN_1: BigInt = BigInt::from_u128(1068493150684932 * 365).unwrap(); static ref PRICE_DOMAIN_LEN_2: BigInt = BigInt::from_u128(657534246575343 * 365).unwrap(); - static ref PRICE_DOMAIN_LEN_3: BigInt = BigInt::from_u128(410958904109590 * 365).unwrap(); - static ref PRICE_DOMAIN_LEN_4: BigInt = BigInt::from_u128(232876712328767 * 365).unwrap(); + static ref PRICE_DOMAIN_LEN_3: BigInt = BigInt::from_u128(200000000000000 * 365).unwrap(); + static ref PRICE_DOMAIN_LEN_4: BigInt = BigInt::from_u128(73972602739726 * 365).unwrap(); static ref PRICE_DOMAIN: BigInt = BigInt::from_u128(24657534246575 * 365).unwrap(); } From d097316fa5a8c2152ec384acb07056e62d0f5f30 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 27 Mar 2024 15:35:09 +0100 Subject: [PATCH 4/4] fix: hardcode max fee to 10$ --- bot/src/bot.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/src/bot.rs b/bot/src/bot.rs index 8a7ea35..632a2d5 100644 --- a/bot/src/bot.rs +++ b/bot/src/bot.rs @@ -481,7 +481,13 @@ pub async fn send_transaction( .fee_estimate_multiplier(5.0f64); match execution.estimate_fee().await { - Ok(fee) => match execution.nonce(nonce).max_fee(fee.overall_fee).send().await { + Ok(_) => match execution + .nonce(nonce) + // harcode max fee to 10$ = 0.0028 ETH + .max_fee(FieldElement::from(2800000000000000_u64)) + .send() + .await + { Ok(tx_result) => Ok(tx_result.transaction_hash), Err(e) => { let error_message = format!("An error occurred while renewing domains: {}", e);