Skip to content

Commit

Permalink
feat: update bot to new indexer
Browse files Browse the repository at this point in the history
  • Loading branch information
irisdv committed Sep 20, 2023
1 parent cf633f3 commit 8acf394
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 123 deletions.
3 changes: 2 additions & 1 deletion bot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
starknet = { git = "https://github.com/Th0rgal/starknet-rs.git", branch = "feat/starknet-id" }
mongodb = "2.1.0"
bson = "2.0.0"
chrono = "0.4"
Expand All @@ -22,4 +21,6 @@ serde_json = "1.0"
lazy_static = "1.4.0"
reqwest = "0.11.18"
num-integer = "0.1.45"
starknet = "0.6.0"
starknet-id = { git = "https://github.com/starknet-id/starknet-id.rs.git", branch = "master" }

154 changes: 67 additions & 87 deletions bot/src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,30 @@ use bson::{doc, Bson};
use chrono::{Duration, Utc};
use futures::TryStreamExt;
use mongodb::options::FindOneOptions;
use num_integer::Integer;
use starknet::accounts::Account;
use starknet::core::chain_id;
use starknet::core::types::{BlockTag, FunctionCall};
use starknet::{
accounts::{Call, SingleOwnerAccount},
core::types::{BlockId, CallFunction, FieldElement},
accounts::{Account, Call, SingleOwnerAccount},
core::types::{BlockId, FieldElement},
macros::selector,
providers::{Provider, SequencerGatewayProvider},
providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider},
signers::LocalWallet,
};
use url::Url;
use starknet_id::encode;

use crate::discord::log_msg_and_send_to_discord;
use crate::models::{
AggregateResult, AggregateResults, DomainAggregateResult, MetadataDoc, Unzip5,
};
use crate::starknet_utils::create_jsonrpc_client;
use crate::utils::{hex_to_bigdecimal, to_uint256};
use crate::{
config::Config,
models::{AppState, Domain},
};
use bigdecimal::{BigDecimal, FromPrimitive};
use bigdecimal::BigDecimal;

lazy_static::lazy_static! {
static ref RENEW_TIME: FieldElement = FieldElement::from_dec_str("365").unwrap();
static ref TWO_POW_128: BigInt = BigInt::from(2).pow(128);
}

pub fn get_provider(config: &Config) -> SequencerGatewayProvider {
if config.devnet_provider.is_devnet {
SequencerGatewayProvider::new(
Url::from_str(&config.devnet_provider.gateway).unwrap(),
Url::from_str(&config.devnet_provider.feeder_gateway).unwrap(),
)
} else if config.devnet_provider.is_testnet {
SequencerGatewayProvider::starknet_alpha_goerli()
} else {
SequencerGatewayProvider::starknet_alpha_mainnet()
}
}

pub fn get_chainid(config: &Config) -> FieldElement {
if config.devnet_provider.is_devnet || config.devnet_provider.is_testnet {
chain_id::TESTNET
} else {
chain_id::MAINNET
}
}

fn to_uint256(n: BigInt) -> (FieldElement, FieldElement) {
let (n_high, n_low) = n.div_rem(&TWO_POW_128);
let (_, low_bytes) = n_low.to_bytes_be();
let (_, high_bytes) = n_high.to_bytes_be();

(
FieldElement::from_byte_slice_be(&low_bytes).unwrap(),
FieldElement::from_byte_slice_be(&high_bytes).unwrap(),
)
}

pub async fn get_domains_ready_for_renewal(
Expand All @@ -74,17 +41,17 @@ pub async fn get_domains_ready_for_renewal(

// Define aggregate pipeline
let pipeline = vec![
doc! { "$match": { "_chain.valid_to": Bson::Null } },
doc! { "$match": { "expiry": { "$lt": Bson::Int32((min_expiry_date.timestamp_millis() / 1000).try_into().unwrap()) } } },
doc! { "$match": { "_cursor.to": null } },
doc! { "$match": { "expiry": { "$lt": Bson::Int64(min_expiry_date.timestamp_millis() / 1000) } } },
doc! { "$lookup": {
"from": "auto_renewals",
"from": "auto_renew_flows",
"let": { "domain_name": "$domain" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$domain", "$$domain_name" ] },
{ "$eq": [ "$_chain.valid_to", Bson::Null ] }
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
}
Expand All @@ -93,14 +60,14 @@ pub async fn get_domains_ready_for_renewal(
}},
doc! { "$unwind": "$renewal_info" },
doc! { "$lookup": {
"from": "approvals",
"from": "auto_renew_approvals",
"let": { "renewer_addr": "$renewal_info.renewer_address" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$renewer", "$$renewer_addr" ] },
{ "$eq": [ "$_chain.valid_to", Bson::Null ] }
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
}
Expand All @@ -112,12 +79,12 @@ pub async fn get_domains_ready_for_renewal(
"domain": 1,
"expiry": 1,
"renewer_address": "$renewal_info.renewer_address",
"auto_renewal_enabled": "$renewal_info.auto_renewal_enabled",
"approval_value": { "$ifNull": [ "$approval_info.value", "0" ] },
"auto_renewal_enabled": "$renewal_info.enabled",
"approval_value": { "$ifNull": [ "$approval_info.allowance", "0x0" ] },
"limit_price": "$renewal_info.limit_price",
"last_renewal": "$renewal_info.last_renewal",
"meta_hash": "$renewal_info.meta_hash",
"_chain": "$renewal_info._chain",
"_cursor": "$renewal_info._cursor",
}},
];

Expand Down Expand Up @@ -163,6 +130,7 @@ pub async fn get_domains_ready_for_renewal(
)
})
.unzip5();

Ok(AggregateResults {
domains,
renewers,
Expand All @@ -182,60 +150,72 @@ async fn process_aggregate_result(
return Ok(None);
}

let renewer_addr = FieldElement::from_str(&result.renewer_address)?;
let allowance = BigDecimal::from_str(&result.approval_value)?;
let limit_price = BigDecimal::from_str(&result.limit_price)?;
let renewer_addr = FieldElement::from_hex_be(&result.renewer_address)?;
let allowance = if let Some(approval_value) = result.approval_value {
hex_to_bigdecimal(&approval_value).unwrap()
} else {
BigDecimal::from(0)
};
// let allowance = hex_to_bigdecimal(&result.approval_value).unwrap();
let limit_price = hex_to_bigdecimal(&result.limit_price).unwrap();

// get renew price from contract
let provider = get_provider(&config);
let provider = create_jsonrpc_client(&config);
let domain_name = result
.domain
.strip_suffix(".stark")
.ok_or_else(|| anyhow::anyhow!("Invalid domain name: {:?}", result.domain))?;
let domain_encoded = starknet::id::encode(domain_name)
.map_err(|_| anyhow!("Failed to encode domain name"))
.context("Error occurred while encoding domain name")?;
let domain_len = domain_name.len();

let call_result = provider
.call_contract(
CallFunction {
.call(
FunctionCall {
contract_address: config.contract.pricing,
entry_point_selector: selector!("compute_renew_price"),
calldata: vec![domain_encoded, *RENEW_TIME],
calldata: vec![domain_len.into(), *RENEW_TIME],
},
BlockId::Latest,
BlockId::Tag(BlockTag::Latest),
)
.await;

match call_result {
Ok(res) => {
let renew_price = FieldElement::to_big_decimal(&res.result[1], 18);
Ok(price) => {
let renew_price = FieldElement::to_big_decimal(&price[1], 18);
// Check user has enough allowance & limit_price is not lower than renew_price
if renew_price <= allowance && renew_price <= limit_price {
if result.meta_hash != "0" {
let decimal_val =
BigInt::parse_bytes(result.meta_hash.as_str().as_bytes(), 10).unwrap();
let hex_meta_hash = decimal_val.to_str_radix(16);
let metadata_collection =
state.db_metadata.collection::<MetadataDoc>("metadata");
if let Some(document) = metadata_collection
.find_one(doc! {"meta_hash": hex_meta_hash}, FindOneOptions::default())
.await?
{
let tax_state = document.tax_state;
if let Some(state_info) = state.states.states.get(&tax_state) {
let tax_rate = (state_info.rate * 100.0).round() as i32;
let tax_price =
(renew_price * BigDecimal::from(tax_rate)) / BigDecimal::from(100);
return Ok(Some(AggregateResult {
domain: domain_encoded,
renewer_addr,
limit_price,
tax_price,
meta_hash: FieldElement::from_dec_str(&result.meta_hash)?,
}));
// encode domain name
let domain_encoded = encode(domain_name)
.map_err(|_| anyhow!("Failed to encode domain name"))
.context("Error occurred while encoding domain name")?;
if let Some(meta_hash) = result.meta_hash {
if meta_hash != "0" {
let decimal_meta_hash =
BigInt::parse_bytes(meta_hash.trim_start_matches("0x").as_bytes(), 16)
.unwrap();
let hex_meta_hash = decimal_meta_hash.to_str_radix(16);
let metadata_collection =
state.db_metadata.collection::<MetadataDoc>("metadata");
if let Some(document) = metadata_collection
.find_one(doc! {"meta_hash": hex_meta_hash}, FindOneOptions::default())
.await?
{
let tax_state = document.tax_state;
if let Some(state_info) = state.states.states.get(&tax_state) {
let tax_rate = (state_info.rate * 100.0).round() as i32;
let tax_price = (renew_price * BigDecimal::from(tax_rate))
/ BigDecimal::from(100);
return Ok(Some(AggregateResult {
domain: domain_encoded,
renewer_addr,
limit_price,
tax_price,
meta_hash: FieldElement::from_hex_be(&meta_hash)?,
}));
}
}
}
}

Ok(Some(AggregateResult {
domain: domain_encoded,
renewer_addr,
Expand Down Expand Up @@ -266,7 +246,7 @@ async fn process_aggregate_result(

pub async fn renew_domains(
config: &Config,
account: &SingleOwnerAccount<SequencerGatewayProvider, LocalWallet>,
account: &SingleOwnerAccount<JsonRpcClient<HttpTransport>, LocalWallet>,
mut aggregate_results: AggregateResults,
) -> Result<()> {
// If we have more than 400 domains to renew we make multiple transactions to avoid hitting the 2M steps limit
Expand Down Expand Up @@ -324,7 +304,7 @@ pub async fn renew_domains(

pub async fn send_transaction(
config: &Config,
account: &SingleOwnerAccount<SequencerGatewayProvider, LocalWallet>,
account: &SingleOwnerAccount<JsonRpcClient<HttpTransport>, LocalWallet>,
aggregate_results: AggregateResults,
) -> Result<()> {
let mut calldata: Vec<FieldElement> = Vec::new();
Expand Down
13 changes: 5 additions & 8 deletions bot/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ pub_struct!(Clone, Deserialize; Database {
connection_string_metadata: String,
});

pub_struct!(Clone, Deserialize; DevnetProvider {
is_devnet: bool,
is_testnet: bool,
gateway: String,
feeder_gateway: String,
});

pub_struct!(Clone, Deserialize; MyAccount {
private_key: FieldElement,
address: FieldElement,
Expand All @@ -60,15 +53,19 @@ pub_struct!(Clone, Deserialize; IndexerServer {
server_url: String,
});

pub_struct!(Clone, Deserialize; Rpc {
rpc_url: String,
});

pub_struct!(Clone, Deserialize; Config {
apibara: Apibara,
contract: Contract,
database: Database,
devnet_provider: DevnetProvider,
account: MyAccount,
discord: Discord,
renewals : Renewals,
indexer_server: IndexerServer,
rpc: Rpc,
});

pub fn load() -> Config {
Expand Down
3 changes: 2 additions & 1 deletion bot/src/discord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{Context, Result};
use serde_json::json;
use serenity::http::Http;
use serenity::model::channel::Message;
use starknet_id::decode;

use crate::config::Config;
use crate::models::AggregateResults;
Expand Down Expand Up @@ -52,7 +53,7 @@ pub async fn log_domains_renewed(
.domains
.iter()
.zip(aggregate_results.domains.iter())
.map(|(d, r)| format!("- `{}` by `{}`", &starknet::id::decode(*d), r))
.map(|(d, r)| format!("- `{}` by `{}`", &decode(*d), r))
.collect::<Vec<String>>()
.join(" \n")
);
Expand Down
18 changes: 12 additions & 6 deletions bot/src/indexer_status.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use anyhow::Result;
use starknet::{
core::types::{BlockId, BlockStatus},
core::types::{BlockId, BlockStatus, MaybePendingBlockWithTxHashes},
providers::Provider,
};

use crate::{bot::get_provider, config::Config};
use crate::{config::Config, starknet_utils::create_jsonrpc_client};

pub async fn check_block_status(conf: &Config, block_nb: u64) -> Result<bool> {
let provider = get_provider(&conf);
let provider = create_jsonrpc_client(&conf);

match provider.get_block(BlockId::Number(block_nb)).await {
Ok(block) => {
match provider
.get_block_with_tx_hashes(BlockId::Number(block_nb))
.await
{
Ok(MaybePendingBlockWithTxHashes::Block(block)) => {
if block.status == BlockStatus::AcceptedOnL2 || block.status == BlockStatus::Pending {
Ok(true)
} else {
Expand All @@ -21,6 +24,7 @@ pub async fn check_block_status(conf: &Config, block_nb: u64) -> Result<bool> {
Ok(false)
}
}
Ok(MaybePendingBlockWithTxHashes::PendingBlock(_)) => Ok(true),
Err(e) => {
println!(
"Error while fetching block status {} with error: {}",
Expand All @@ -31,6 +35,7 @@ pub async fn check_block_status(conf: &Config, block_nb: u64) -> Result<bool> {
}
}

// todo: update
pub async fn get_status_from_endpoint(conf: &Config) -> Result<u64, Box<dyn std::error::Error>> {
// Perform the request and get the response as a string
let raw_response = reqwest::get(format!(
Expand All @@ -48,7 +53,8 @@ pub async fn get_status_from_endpoint(conf: &Config) -> Result<u64, Box<dyn std:
let response: serde_json::Value = serde_json::from_str(&raw_response)?;

// Extract the "last_block" field as a usize
let last_block = response["last_block"].as_u64()
let last_block = response["last_block"]
.as_u64()
.ok_or("Failed to parse 'last_block' as usize")?;

Ok(last_block)
Expand Down
Loading

0 comments on commit 8acf394

Please sign in to comment.