Skip to content

Commit

Permalink
feat: add brine quest
Browse files Browse the repository at this point in the history
  • Loading branch information
ayushtom committed Jan 21, 2024
1 parent a370ca5 commit be34fe3
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ pub enum QuizQuestionType {

impl<'de> Deserialize<'de> for QuizQuestionType {
fn deserialize<D>(deserializer: D) -> Result<QuizQuestionType, D::Error>
where
D: Deserializer<'de>,
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
Expand Down Expand Up @@ -167,6 +167,7 @@ pub_struct!(Clone, Deserialize; Config {
rhino: PublicApi,
rango: Api,
pyramid: ApiEndpoint,
brine: Api
});

pub fn load() -> Config {
Expand Down
101 changes: 101 additions & 0 deletions src/endpoints/quests/brine/claimable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::models::{AppState, CompletedTaskDocument, Reward, RewardResponse};
use crate::utils::{get_error, get_nft};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use futures::StreamExt;
use mongodb::bson::doc;
use serde::Deserialize;
use starknet::{
core::types::FieldElement,
signers::{LocalWallet, SigningKey},
};
use std::sync::Arc;

const QUEST_ID: u32 = 25;
const TASK_IDS: &[u32] = &[114, 115, 116, 117];
const LAST_TASK: u32 = TASK_IDS[3];
const NFT_LEVEL: u32 = 34;

#[derive(Deserialize)]
pub struct ClaimableQuery {
addr: FieldElement,
}

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<ClaimableQuery>,
) -> impl IntoResponse {
let collection = state
.db
.collection::<CompletedTaskDocument>("completed_tasks");

let pipeline = vec![
doc! {
"$match": {
"address": &query.addr.to_string(),
"task_id": { "$in": TASK_IDS },
},
},
doc! {
"$lookup": {
"from": "tasks",
"localField": "task_id",
"foreignField": "id",
"as": "task",
},
},
doc! {
"$match": {
"task.quest_id": QUEST_ID,
},
},
doc! {
"$group": {
"_id": "$address",
"completed_tasks": { "$push": "$task_id" },
},
},
doc! {
"$match": {
"completed_tasks": { "$all": TASK_IDS },
},
},
];

let completed_tasks = collection.aggregate(pipeline, None).await;
match completed_tasks {
Ok(mut tasks_cursor) => {
if tasks_cursor.next().await.is_none() {
return get_error("User hasn't completed all tasks".into());
}

let signer = LocalWallet::from(SigningKey::from_secret_scalar(
state.conf.nft_contract.private_key,
));

let mut rewards = vec![];

let Ok((token_id, sig)) = get_nft(QUEST_ID, LAST_TASK, &query.addr, NFT_LEVEL, &signer).await else {
return get_error("Signature failed".into());
};

rewards.push(Reward {
task_id: LAST_TASK,
nft_contract: state.conf.nft_contract.address.clone(),
token_id: token_id.to_string(),
sig: (sig.r, sig.s),
});

if rewards.is_empty() {
get_error("No rewards found for this user".into())
} else {
(StatusCode::OK, Json(RewardResponse { rewards })).into_response()
}
}
Err(_) => get_error("Error querying rewards".into()),
}
}
4 changes: 4 additions & 0 deletions src/endpoints/quests/brine/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod verify_twitter_fw;
pub mod verify_deposit;
pub mod verify_trade;
pub mod claimable;
67 changes: 67 additions & 0 deletions src/endpoints/quests/brine/verify_deposit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::sync::Arc;

use crate::models::VerifyQuery;
use crate::utils::{to_hex, CompletedTasksTrait};
use crate::{models::AppState, utils::get_error};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 115;
let address_hex = to_hex(query.addr);

// make get request to pyramid api for verification
let url = format!(
"{}/has-deposited-starknet?starknet_address={}",
state.conf.brine.api_endpoint,
to_hex(query.addr)
);

let res = make_brine_request(url.as_str(), &state.conf.brine.api_key, &address_hex).await;

let response = match res {
Ok(response) => response,
Err(_) => return get_error(format!("Try again later")),
};

if let Some(_) = response.get("status") {
let data = response.get("payload").unwrap();
if let Some(res) = data.get("has_deposited") {
if res.as_bool().unwrap() {
return match state.upsert_completed_task(query.addr, task_id).await {
Ok(_) => (StatusCode::OK, Json(json!({"res": true}))).into_response(),
Err(e) => get_error(format!("{}", e)),
};
}
}
}
get_error("Not yet deposited".to_string())
}

async fn make_brine_request(
endpoint: &str,
api_key: &str,
addr: &str,
) -> Result<serde_json::Value, String> {
let client = reqwest::Client::new();
match client
.get(endpoint)
.header("X-API-Key", api_key)
.send()
.await
{
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => Ok(json),
Err(_) => Err(format!("Funds not deposited")),
},
Err(_) => Err(format!("Funds not bridged")),
}
}
65 changes: 65 additions & 0 deletions src/endpoints/quests/brine/verify_trade.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::sync::Arc;

use crate::models::VerifyQuery;
use crate::utils::{to_hex, CompletedTasksTrait};
use crate::{models::AppState, utils::get_error};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 116;
let address_hex = to_hex(query.addr);
let threshold_vol = 297;
// make get request to pyramid api for verification
let url = format!(
"{}/has-traded-beyond-threshold?eth_address={}&threshold_vol={}",
state.conf.brine.api_endpoint,
to_hex(query.addr),
threshold_vol
);

let res = make_brine_request(url.as_str(), &state.conf.brine.api_key, &address_hex).await;

let response = match res {
Ok(response) => response,
Err(_) => return get_error(format!("Try again later")),
};

if let Some(res) = response.get("status") {
if res.as_str().unwrap() == "success" {
return match state.upsert_completed_task(query.addr, task_id).await {
Ok(_) => (StatusCode::OK, Json(json!({"res": true}))).into_response(),
Err(e) => get_error(format!("{}", e)),
};
}
}
get_error("Not yet traded".to_string())
}

async fn make_brine_request(
endpoint: &str,
api_key: &str,
addr: &str,
) -> Result<serde_json::Value, String> {
let client = reqwest::Client::new();
match client
.get(endpoint)
.header("X-API-Key", api_key)
.send()
.await
{
Ok(response) => match response.json::<serde_json::Value>().await {
Ok(json) => Ok(json),
Err(_) => Err(format!("Funds not deposited")),
},
Err(_) => Err(format!("Funds not bridged")),
}
}
24 changes: 24 additions & 0 deletions src/endpoints/quests/brine/verify_twitter_fw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyQuery},
utils::{get_error, CompletedTasksTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;

pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 114;
match state.upsert_completed_task(query.addr, task_id).await {
Ok(_) => (StatusCode::OK, Json(json!({"res": true}))).into_response(),
Err(e) => get_error(format!("{}", e)),
}
}
1 change: 1 addition & 0 deletions src/endpoints/quests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ pub mod verify_quiz;
pub mod zklend;
pub mod rhino;
pub mod rango;
pub mod brine;
10 changes: 10 additions & 0 deletions src/endpoints/quests/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,16 @@ pub async fn handler(
}),
).into_response(),

Some(34) => (
StatusCode::OK,
Json(TokenURI {
name: "Starknet Pro Score x zkLend Quest NFT".into(),
description: "This Starknet commemorative Non-Fungible Token represents the first steps into the Starknet universe. By using zkLend, the native money-market protocol on Starknet, you are building solid foundations for your Starknet experience.".into(),
image: format!("{}/brine/brine.webp", state.conf.variables.app_link),
attributes: None,
}),
).into_response(),

_ => get_error("Error, this level is not correct".into()),
}
}
1 change: 1 addition & 0 deletions src/endpoints/quests/verify_quiz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn get_task_id(quiz_name: &str) -> Option<u32> {
"rango" => Some(95),
"braavos" => Some(98),
"rhino" => Some(100),
"brine" => Some(117),
_ => None,
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,22 @@ async fn main() {
"/quests/rango/claimable",
get(endpoints::quests::rango::claimable::handler),
)
.route(
"/quests/brine/verify_twitter_fw",
get(endpoints::quests::brine::verify_twitter_fw::handler),
)
.route(
"/quests/brine/claimable",
get(endpoints::quests::brine::claimable::handler),
)
.route(
"/quests/brine/verify_deposit",
get(endpoints::quests::brine::verify_deposit::handler),
)
.route(
"/quests/brine/verify_trade",
get(endpoints::quests::brine::verify_trade::handler),
)
.route(
"/achievements/verify_default",
get(endpoints::achievements::verify_default::handler),
Expand Down

0 comments on commit be34fe3

Please sign in to comment.