Skip to content

Commit

Permalink
Merge pull request #94 from starknet-id/feat/add_tvl_achievement
Browse files Browse the repository at this point in the history
feat: add tvl, seniority & Avnu achievements
  • Loading branch information
Th0rgal authored Oct 16, 2023
2 parents 77dbbbe + 0085343 commit f2e52f4
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 1 deletion.
2 changes: 1 addition & 1 deletion config.template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ question = "What is MEV protection, and how does AVNU's RFQ system provide it?"
options = [
"MEV protection is Maximum Earnings Volatility, and AVNU ensures it through trading volume.",
"MEV protection is Market Efficiency Verification, and AVNU uses it to verify liquidity sources.",
"MEV protection is Miner Extractable Value, and AVNU uses cryptographic signatures to prevent front-running and maintain a secure trading environment.",
"MEV protection is Maximal Extractable Value, and AVNU uses cryptographic signatures to prevent front-running and maintain a secure trading environment.",
"MEV protection is Monetary Exchange Value, and AVNU guarantees it through blockchain technology. "
]
correct_answers = [*]
Expand Down
3 changes: 3 additions & 0 deletions src/endpoints/achievements/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub mod fetch;
pub mod fetch_buildings;
pub mod verify_avnu;
pub mod verify_briq;
pub mod verify_default;
pub mod verify_has_domain;
pub mod verify_seniority;
pub mod verify_tvl;
pub mod verify_whitelisted;
63 changes: 63 additions & 0 deletions src/endpoints/achievements/verify_avnu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyAchievementQuery},
utils::{get_error, to_hex, AchievementsTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;
use starknet::core::types::FieldElement;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyAchievementQuery>,
) -> impl IntoResponse {
let addr = query.addr;
if addr == FieldElement::ZERO {
return get_error("Please connect your wallet first".to_string());
}

let achievement_id = query.id;
if !(17..=19).contains(&achievement_id) {
return get_error("Invalid achievement id".to_string());
}

let url = format!("https://starknet.api.avnu.fi/v1/takers/{}", to_hex(addr));
let client = reqwest::Client::new();
match client.get(url).send().await {
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => {
if let Some(volume) = json["volumeInUSD"].as_f64() {
if (achievement_id == 17 && volume >= 500.0)
|| (achievement_id == 18 && volume >= 5000.0)
|| (achievement_id == 19 && volume >= 50000.0)
{
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 volume on AVNU is too low".to_string())
}
} else {
get_error("No data found for this address".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)),
}
}
78 changes: 78 additions & 0 deletions src/endpoints/achievements/verify_seniority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyAchievementQuery},
utils::{get_error, to_hex, AchievementsTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use chrono::{NaiveDateTime, Utc};
use serde_json::json;
use starknet::core::types::FieldElement;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyAchievementQuery>,
) -> impl IntoResponse {
let addr = query.addr;
if addr == FieldElement::ZERO {
return get_error("Please connect your wallet first".to_string());
}

let achievement_id = query.id;
if !(14..=16).contains(&achievement_id) {
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 >= 30)
|| (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())
}
}
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)),
}
}
66 changes: 66 additions & 0 deletions src/endpoints/achievements/verify_tvl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyAchievementQuery},
utils::{get_error, to_hex, AchievementsTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;
use starknet::core::types::FieldElement;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyAchievementQuery>,
) -> impl IntoResponse {
let addr = query.addr;
if addr == FieldElement::ZERO {
return get_error("Please connect your wallet first".to_string());
}

let achievement_id = query.id;
if !(11..=13).contains(&achievement_id) {
return get_error("Invalid achievement id".to_string());
}

let url = format!(
"https://stack.starkendefi.xyz/public/aggregates/{}",
to_hex(addr)
);
let client = reqwest::Client::new();
match client.get(url).send().await {
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => {
if let Some(total_tvl_dollars) = json["total_tvl_dollars"].as_f64() {
if (achievement_id == 11 && total_tvl_dollars >= 100.0)
|| (achievement_id == 12 && total_tvl_dollars >= 1000.0)
|| (achievement_id == 13 && total_tvl_dollars >= 10000.0)
{
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 TVL is too low".to_string())
}
} else {
get_error("total_tvl_dollars not found or not a float".to_string())
}
}
Err(e) => get_error(format!(
"Failed to get JSON response from Starkendefi: {}",
e
)),
},
Err(e) => get_error(format!("Failed to fetch Starkendefi: {}", e)),
}
}
12 changes: 12 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@ async fn main() {
"/achievements/verify_briq",
get(endpoints::achievements::verify_briq::handler),
)
.route(
"/achievements/verify_tvl",
get(endpoints::achievements::verify_tvl::handler),
)
.route(
"/achievements/verify_seniority",
get(endpoints::achievements::verify_seniority::handler),
)
.route(
"/achievements/verify_avnu",
get(endpoints::achievements::verify_avnu::handler),
)
.route(
"/achievements/verify_has_domain",
get(endpoints::achievements::verify_has_domain::handler),
Expand Down

0 comments on commit f2e52f4

Please sign in to comment.