Skip to content

Commit

Permalink
Merge pull request #117 from starknet-id/feat/store_times_db
Browse files Browse the repository at this point in the history
fix: add deployed timestamp in db, create endpoint & update seniority…
  • Loading branch information
Th0rgal authored Oct 28, 2023
2 parents c134da0 + 6e5d171 commit f45c47f
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 45 deletions.
56 changes: 56 additions & 0 deletions src/common/has_deployed_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::sync::Arc;

use crate::utils::DeployedTimesTrait;
use crate::{
models::{AppState, DeployedTime},
utils::to_hex,
};
use mongodb::{bson::doc, Collection};
use starknet::core::types::FieldElement;

pub async fn execute_has_deployed_time(
state: Arc<AppState>,
addr: &FieldElement,
) -> Result<u32, String> {
// Check if we have already a result in the db
let deployed_times_collection: Collection<DeployedTime> = state.db.collection("deployed_times");
let filter = doc! { "addr": to_hex(*addr) };
if let Ok(Some(document)) = deployed_times_collection.find_one(filter, None).await {
return Ok(document.timestamp);
}

// If not we fetch it from the API and store it in the db
let url = format!(
"https://api.starkscan.co/api/v0/transactions?from_block=1&limit=1&contract_address={}&order_by=asc",
to_hex(*addr)
);
let client = reqwest::Client::new();
match client
.get(&url)
.header("accept", "application/json")
.header("x-api-key", state.conf.starkscan.api_key.clone())
.send()
.await
{
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => {
if let Some(timestamp) = json["data"][0]["timestamp"].as_i64() {
match state
.upsert_deployed_timestamp(*addr, timestamp as u32)
.await
{
Ok(_) => Ok(timestamp as u32),
Err(e) => Err(format!("{}", e)),
}
} else {
Err("Wallet not deployed.".to_string())
}
}
Err(e) => Err(format!(
"Failed to get JSON response while fetching user transaction data: {}",
e
)),
},
Err(e) => Err(format!("Failed to fetch user transactions from API: {}", e)),
}
}
1 change: 1 addition & 0 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod has_deployed_time;
pub mod verify_has_nft;
pub mod verify_has_root_domain;
pub mod verify_has_root_or_braavos_domain;
Expand Down
67 changes: 22 additions & 45 deletions src/endpoints/achievements/verify_seniority.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::Arc;

use crate::{
common::has_deployed_time::execute_has_deployed_time,
models::{AppState, VerifyAchievementQuery},
utils::{get_error, to_hex, AchievementsTrait},
utils::{get_error, AchievementsTrait},
};
use axum::{
extract::{Query, State},
Expand All @@ -28,51 +29,27 @@ pub async fn handler(
return get_error("Invalid achievement id".to_string());
}

let url = format!(
"https://api.starkscan.co/api/v0/transactions?from_block=1&limit=1&contract_address={}&order_by=asc",
to_hex(addr)
);
let client = reqwest::Client::new();
match client
.get(&url)
.header("accept", "application/json")
.header("x-api-key", state.conf.starkscan.api_key.clone())
.send()
.await
{
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => {
if let Some(timestamp) = json["data"][0]["timestamp"].as_i64() {
let dt = NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap();
let current_time = Utc::now().naive_utc();
let duration = current_time - dt;
let days_passed = duration.num_days();

if (achievement_id == 14 && days_passed >= 90)
|| (achievement_id == 15 && days_passed >= 180)
|| (achievement_id == 16 && days_passed >= 365)
{
match state
.upsert_completed_achievement(addr, achievement_id)
.await
{
Ok(_) => {
(StatusCode::OK, Json(json!({"achieved": true}))).into_response()
}
Err(e) => get_error(format!("{}", e)),
}
} else {
get_error("Your wallet is too recent".to_string())
}
} else {
get_error("No value found for this address".to_string())
match execute_has_deployed_time(state.clone(), &query.addr).await {
Ok(timestamp) => {
let dt = NaiveDateTime::from_timestamp_opt(timestamp as i64, 0).unwrap();
let current_time = Utc::now().naive_utc();
let duration = current_time - dt;
let days_passed = duration.num_days();
if (achievement_id == 14 && days_passed >= 90)
|| (achievement_id == 15 && days_passed >= 180)
|| (achievement_id == 16 && days_passed >= 365)
{
match state
.upsert_completed_achievement(addr, achievement_id)
.await
{
Ok(_) => (StatusCode::OK, Json(json!({"achieved": true}))).into_response(),
Err(e) => get_error(format!("{}", e)),
}
} else {
get_error("Your wallet is too recent".to_string())
}
Err(e) => get_error(format!(
"Failed to get JSON response from Starkscan api: {}",
e
)),
},
Err(e) => get_error(format!("Failed to fetch Starkscan api: {}", e)),
}
Err(e) => get_error(e),
}
}
29 changes: 29 additions & 0 deletions src/endpoints/get_deployed_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::sync::Arc;

use crate::models::AppState;
use crate::{common::has_deployed_time::execute_has_deployed_time, utils::get_error};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use starknet::core::types::FieldElement;

#[derive(Debug, Serialize, Deserialize)]

pub struct GetDeployedTimeQuery {
addr: FieldElement,
}

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<GetDeployedTimeQuery>,
) -> impl IntoResponse {
match execute_has_deployed_time(state, &query.addr).await {
Ok(timestamp) => (StatusCode::OK, Json(json!({ "timestamp": timestamp }))).into_response(),
Err(e) => get_error(e),
}
}
1 change: 1 addition & 0 deletions src/endpoints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod achievements;
pub mod get_completed_quests;
pub mod get_deployed_time;
pub mod get_quest;
pub mod get_quest_participants;
pub mod get_quests;
Expand Down
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ async fn main() {
get(endpoints::get_trending_quests::handler),
)
.route("/get_tasks", get(endpoints::get_tasks::handler))
.route(
"/get_deployed_time",
get(endpoints::get_deployed_time::handler),
)
.route(
"/quests/verify_quiz",
post(endpoints::quests::verify_quiz::handler),
Expand Down
5 changes: 5 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,8 @@ pub_struct!(Debug, Deserialize, Serialize; BuildingDocument {
level: u32,
img_url: String,
});

pub_struct!(Deserialize, Debug; DeployedTime {
addr: String,
timestamp: u32,
});
29 changes: 29 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,32 @@ impl AchievementsTrait for AppState {
result
}
}

#[async_trait]
pub trait DeployedTimesTrait {
async fn upsert_deployed_timestamp(
&self,
addr: FieldElement,
timestamp: u32,
) -> Result<UpdateResult, mongodb::error::Error>;
}

#[async_trait]
impl DeployedTimesTrait for AppState {
async fn upsert_deployed_timestamp(
&self,
addr: FieldElement,
timestamp: u32,
) -> Result<UpdateResult, mongodb::error::Error> {
let deployed_times_collection: Collection<CompletedTasks> =
self.db.collection("deployed_times");
let filter = doc! { "addr": addr.to_string() };
let update = doc! { "$setOnInsert": { "addr": to_hex(addr), "timestamp": timestamp } };
let options = UpdateOptions::builder().upsert(true).build();

let result = deployed_times_collection
.update_one(filter, update, options)
.await;
result
}
}

0 comments on commit f45c47f

Please sign in to comment.