Skip to content

Commit

Permalink
feat: add altcoin renewal support
Browse files Browse the repository at this point in the history
  • Loading branch information
irisdv committed Mar 12, 2024
1 parent c6156f0 commit 92e0880
Show file tree
Hide file tree
Showing 8 changed files with 451 additions and 290 deletions.
328 changes: 119 additions & 209 deletions bot/src/bot.rs

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions bot/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ pub_struct!(Clone, Deserialize; WatchtowerTypes {
severe: String,
});

pub_struct!(Clone, Deserialize; Server {
starknetid_api: String,
});

pub_struct!(Clone, Deserialize; Config {
contract: Contract,
database: Database,
Expand All @@ -68,6 +72,7 @@ pub_struct!(Clone, Deserialize; Config {
indexer_server: IndexerServer,
rpc: Rpc,
watchtower: Watchtower,
server: Server,
});

pub fn load() -> Config {
Expand Down
49 changes: 25 additions & 24 deletions bot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use starknet::{
providers::Provider,
signers::{LocalWallet, SigningKey},
};
use starknet_id::decode;
use starknet_utils::create_jsonrpc_client;
use tokio::time::sleep;

Expand All @@ -23,6 +22,7 @@ mod bot;
mod config;
mod logger;
mod models;
mod pipelines;
mod sales_tax;
mod starknet_utils;
mod starknetid_utils;
Expand Down Expand Up @@ -161,30 +161,31 @@ async fn main() {
println!("[bot] Checking domains to renew");
match bot::get_domains_ready_for_renewal(&conf, &shared_state, &logger).await {
Ok(aggregate_results) => {
//println!("[bot] checking domains to renew today");
if !aggregate_results.domains.is_empty() {
match renew_domains(&conf, &account, aggregate_results.clone(), &logger)
if !aggregate_results.is_empty() {
for (auto_renew_contract, result) in &aggregate_results {
match renew_domains(
&conf,
&account,
result.clone(),
auto_renew_contract,
&logger,
)
.await
{
Ok(_) => {
aggregate_results
.domains
.iter()
.zip(aggregate_results.renewers.iter())
.for_each(|(d, r)| {
logger.info(format!(
"- `Renewal: {}` by `{:#x}`",
&decode(*d),
r
))
});
}
Err(e) => {
logger.severe(format!("Unable to renew domains: {}", e));
if e.to_string().contains("request rate limited") {
continue;
} else {
break;
{
Ok(_) => {
logger.info(format!(
"`Renewed {} domains on auto renewal contract address {}",
result.domains.len(),
auto_renew_contract
));
}
Err(e) => {
logger.severe(format!("Unable to renew domains: {}", e));
if e.to_string().contains("request rate limited") {
continue;
} else {
break;
}
}
}
}
Expand Down
62 changes: 6 additions & 56 deletions bot/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bigdecimal::BigDecimal;
use bson::DateTime;
use mongodb::Database;
use serde::{Deserialize, Serialize};
use starknet::core::types::{FieldElement, TransactionExecutionStatus};
use starknet::core::types::FieldElement;

pub struct AppState {
pub db: Database,
Expand All @@ -19,6 +19,7 @@ pub struct Domain {
pub token_id: Option<String>,
pub creation_date: Option<DateTime>,
pub rev_addr: Option<String>,
pub auto_renew_contract: Option<String>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down Expand Up @@ -51,6 +52,8 @@ pub struct DomainAggregateResult {
pub last_renewal: Option<i64>,
pub meta_hash: Option<String>,
pub _cursor: Cursor,
pub erc20_addr: String,
pub auto_renew_contract: FieldElement,
}

pub struct AggregateResult {
Expand All @@ -59,6 +62,7 @@ pub struct AggregateResult {
pub domain_price: BigDecimal,
pub tax_price: BigDecimal,
pub meta_hash: FieldElement,
pub auto_renew_contract: FieldElement,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand All @@ -68,6 +72,7 @@ pub struct AggregateResults {
pub domain_prices: Vec<BigDecimal>,
pub tax_prices: Vec<BigDecimal>,
pub meta_hashes: Vec<FieldElement>,
pub auto_renew_contracts: Vec<FieldElement>,
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -97,58 +102,3 @@ pub struct TxResult {
pub revert_reason: Option<String>,
pub domains_renewed: usize,
}

pub trait Unzip5 {
type A;
type B;
type C;
type D;
type E;

fn unzip5(
self,
) -> (
Vec<Self::A>,
Vec<Self::B>,
Vec<Self::C>,
Vec<Self::D>,
Vec<Self::E>,
);
}

impl<T, A, B, C, D, E> Unzip5 for T
where
T: Iterator<Item = (A, B, C, D, E)>,
{
type A = A;
type B = B;
type C = C;
type D = D;
type E = E;

fn unzip5(
self,
) -> (
Vec<Self::A>,
Vec<Self::B>,
Vec<Self::C>,
Vec<Self::D>,
Vec<Self::E>,
) {
let mut a = Vec::new();
let mut b = Vec::new();
let mut c = Vec::new();
let mut d = Vec::new();
let mut e = Vec::new();

for (x, y, z, w, v) in self {
a.push(x);
b.push(y);
c.push(z);
d.push(w);
e.push(v);
}

(a, b, c, d, e)
}
}
194 changes: 194 additions & 0 deletions bot/src/pipelines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use anyhow::Result;
use bson::{doc, Bson};
use chrono::{Duration, Utc};
use futures::TryStreamExt;
use starknet::core::types::FieldElement;
use std::sync::Arc;

use crate::{
config::Config,
models::{AppState, Domain, DomainAggregateResult},
utils::to_hex,
};

pub async fn get_auto_renewal_data(
config: &Config,
state: &Arc<AppState>,
) -> Result<Vec<DomainAggregateResult>> {
let auto_renews_collection = state.db.collection::<Domain>("auto_renew_flows");
let min_expiry_date = Utc::now() + Duration::days(400);
let erc20_addr = to_hex(config.contract.erc20);
let auto_renew_contract = FieldElement::to_string(&config.contract.renewal);
println!("timestamp: {}", min_expiry_date.timestamp());
// Define aggregate pipeline
let pipeline = vec![
doc! { "$match": { "_cursor.to": null } },
doc! { "$match": { "enabled": true } },
doc! { "$lookup": {
"from": "domains",
"let": { "domain_name": "$domain" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$domain", "$$domain_name" ] },
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
},
],
"as": "domain_info",
}},
doc! { "$unwind": "$domain_info" },
doc! { "$match": { "domain_info.expiry": { "$lt": Bson::Int64(min_expiry_date.timestamp()) } } },
doc! { "$lookup": {
"from": "auto_renew_approvals",
"let": { "renewer_addr": "$renewer_address" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$renewer", "$$renewer_addr" ] },
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
}
],
"as": "approval_info",
}},
doc! { "$unwind": { "path": "$approval_info", "preserveNullAndEmptyArrays": true } },
doc! { "$addFields": {
"erc20_addr": erc20_addr,
"auto_renew_contract": auto_renew_contract,
}},
doc! { "$group": {
"_id": "$domain_info.domain",
"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" },
}},
doc! { "$project": {
"_id": 0,
"domain": "$_id",
"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,
}},
];

// Execute the pipeline
let cursor = auto_renews_collection.aggregate(pipeline, None).await?;
// Extract the results as Vec<bson::Document>
let bson_docs: Vec<bson::Document> = cursor.try_collect().await?;
// Convert each bson::Document into DomainAggregateResult
let results: Result<Vec<DomainAggregateResult>, _> = bson_docs
.into_iter()
.map(|doc| bson::from_bson(bson::Bson::Document(doc)))
.collect();
// Check if the conversion was successful
let results = results?;

Ok(results)
}

pub async fn get_auto_renewal_altcoins_data(
config: &Config,
state: &Arc<AppState>,
) -> Result<Vec<DomainAggregateResult>> {
let auto_renews_collection = state.db.collection::<Domain>("auto_renew_flows_altcoins");
let min_expiry_date = Utc::now() + Duration::days(400); // todo : change to 30 days

// Define aggregate pipeline
let pipeline = vec![
doc! { "$match": { "_cursor.to": null } },
doc! { "$match": { "enabled": true } },
doc! { "$lookup": {
"from": "domains",
"let": { "domain_name": "$domain" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$domain", "$$domain_name" ] },
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
},
],
"as": "domain_info",
}},
doc! { "$unwind": "$domain_info" },
doc! { "$match": { "domain_info.expiry": { "$lt": Bson::Int64(min_expiry_date.timestamp()) } } },
doc! { "$lookup": {
"from": "auto_renew_approvals_altcoins",
"let": { "renewer_addr": "$renewer_address" },
"pipeline": [
{ "$match":
{ "$expr":
{ "$and": [
{ "$eq": [ "$renewer", "$$renewer_addr" ] },
{ "$eq": [ { "$ifNull": [ "$_cursor.to", null ] }, null ] },
]}
}
}
],
"as": "approval_info",
}},
doc! { "$unwind": { "path": "$approval_info", "preserveNullAndEmptyArrays": true } },
doc! { "$group": {
"_id": "$domain_info.domain",
"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" },
}},
doc! { "$project": {
"_id": 0,
"domain": "$_id",
"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,
}},
];

// Execute the pipeline
let cursor = auto_renews_collection.aggregate(pipeline, None).await?;
// Extract the results as Vec<bson::Document>
let bson_docs: Vec<bson::Document> = cursor.try_collect().await?;
// Convert each bson::Document into DomainAggregateResult
let results: Result<Vec<DomainAggregateResult>, _> = bson_docs
.into_iter()
.map(|doc| bson::from_bson(bson::Bson::Document(doc)))
.collect();
// Check if the conversion was successful
let results = results?;

Ok(results)
}
Loading

0 comments on commit 92e0880

Please sign in to comment.