Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add deployed timestamp in db, create endpoint & update seniority… #117

Merged
merged 2 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}