Skip to content

Commit

Permalink
Merge pull request #201 from starknet-id/ayush/nostra
Browse files Browse the repository at this point in the history
feat: nostra quest staking
  • Loading branch information
Th0rgal authored Mar 13, 2024
2 parents f1b5a6b + cf0be5c commit c293a20
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 9 deletions.
41 changes: 40 additions & 1 deletion config.template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ api_key = "xxxxxx"
[quests.nostra]
utils_contract = "0xXXXXXXXXXXXX"
pairs = ["0xXXXXXXXXXXXX"]
staking_contract="0xXXXXXXXXXXXX"

[rhino]
api_endpoint = "xxxxx"
Expand Down Expand Up @@ -1045,4 +1046,42 @@ options = [
"LUSD 750",
"LUSD 100"
]
correct_answers = [*]
correct_answers = [*]


[quizzes.nostra2]
name = "Nostra Quiz"
desc = "Take part in our Quiz to test your knowledge about Nostra, and you'll have a chance to win 250 STRK."
intro = "Starknet Quest Quiz Rounds, a quiz series designed to make Starknet ecosystem knowledge accessible and enjoyable for all. Test your understanding of the workings of Nostra, enjoy the experience, and earn an exclusive NFT reward by testing your knowledge about Starknet Ecosystem projects!"

[[quizzes.nostra2.questions]]
kind = "text_choice"
layout = "default"
question = "What is nstSTRK?"
options = [
"The first liquid staking token on Starknet",
"A stablecoin on Nostra Money Market",
"A governance token for Nostra",
]
correct_answers = [*]

[[quizzes.nostra2.questions]]
kind = "text_choice"
layout = "default"
question = "Will users be able to use their Nostra Staked STRK (nstSTRK) in DeFi protocols on Starknet?"
options = [
"Yes - those who integrate with nstSTRK",
"No - it’s impossible",
]
correct_answers = [*]

[[quizzes.nostra2.questions]]
kind = "text_choice"
layout = "default"
question = "What is the total value of idle STRK tokens on Starknet waiting to be staked on Nostra?"
options = [
"$951 million",
"$100 million",
"$500 million",
]
correct_answers = [*]
8 changes: 7 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ pub_struct!(Clone, Deserialize; StarknetId {
account_id: String,
});

pub_struct!(Clone, Deserialize; Nostra {
utils_contract: FieldElement,
pairs : Vec<FieldElement>,
staking_contract: FieldElement,
});

pub_struct!(Clone, Deserialize; Pairs {
utils_contract: FieldElement,
pairs : Vec<FieldElement>,
Expand Down Expand Up @@ -68,7 +74,7 @@ pub_struct!(Clone, Deserialize; Quests {
myswap: Contract,
braavos: Braavos,
element: Element,
nostra: Pairs,
nostra: Nostra,
carbonable: Contract,
});

Expand Down
1 change: 0 additions & 1 deletion src/endpoints/get_quiz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ pub async fn handler(
Query(query): Query<GetQuizQuery>,
) -> impl IntoResponse {
let quizzes_from_config = &state.conf.quizzes;

match quizzes_from_config.get(&query.id) {
Some(quiz) => {
let questions: Vec<QuizQuestionResp> = quiz
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct ClaimableQuery {
#[route(
get,
"/quests/nostra/claimable",
crate::endpoints::quests::nostra::claimable
crate::endpoints::quests::nostra::liquidity_quest::claimable
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ pub struct Guild {
#[route(
get,
"/quests/nostra/discord_fw_callback",
crate::endpoints::quests::nostra::discord_fw_callback
crate::endpoints::quests::nostra::liquidity_quest::discord_fw_callback
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
Expand Down
3 changes: 3 additions & 0 deletions src/endpoints/quests/nostra/liquidity_quest/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod claimable;
pub mod discord_fw_callback;
pub mod verify_added_liquidity;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use starknet::{
#[route(
get,
"/quests/nostra/verify_added_liquidity",
crate::endpoints::quests::nostra::verify_added_liquidity
crate::endpoints::quests::nostra::liquidity_quest::verify_added_liquidity
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
Expand Down
5 changes: 2 additions & 3 deletions src/endpoints/quests/nostra/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
pub mod claimable;
pub mod discord_fw_callback;
pub mod verify_added_liquidity;
pub mod liquidity_quest;
pub mod staking_quest;
107 changes: 107 additions & 0 deletions src/endpoints/quests/nostra/staking_quest/claimable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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 axum_auto_routes::route;
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 = 27;
const TASK_IDS: &[u32] = &[132, 133, 134];
const LAST_TASK: u32 = TASK_IDS[2];
const NFT_LEVEL: u32 = 39;

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

#[route(
get,
"/quests/nostra/staking_quest/claimable",
crate::endpoints::quests::nostra::staking_quest::claimable
)]
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()),
}
}
3 changes: 3 additions & 0 deletions src/endpoints/quests/nostra/staking_quest/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod claimable;
pub mod verify_twitter_tw;
pub mod verify_stake;
79 changes: 79 additions & 0 deletions src/endpoints/quests/nostra/staking_quest/verify_stake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::sync::Arc;

use crate::{
models::{AppState, VerifyQuery},
utils::{get_error, CompletedTasksTrait},
};
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use axum_auto_routes::route;
use serde_json::json;
use starknet::{
core::types::{BlockId, BlockTag, FieldElement, FunctionCall},
macros::selector,
providers::Provider,
};

#[route(
get,
"/quests/nostra/staking_quest/verify_stake",
crate::endpoints::quests::nostra::staking_quest::verify_stake
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 133;
let addr = &query.addr;
let balance_calldata = vec![*addr];
let balance_result = state
.provider
.call(
FunctionCall {
contract_address: state.conf.quests.nostra.staking_contract,
entry_point_selector: selector!("balance_of"),
calldata: balance_calldata,
},
BlockId::Tag(BlockTag::Latest),
)
.await;

let user_balance = match &balance_result {
Ok(result) => result[0],
Err(e) => return get_error(format!("{}", e)),
};

if user_balance == FieldElement::ZERO {
return get_error("You didn't stake any STRK.".to_string());
}

let call_result = state
.provider
.call(
FunctionCall {
contract_address: state.conf.quests.nostra.staking_contract,
entry_point_selector: selector!("convert_to_assets"),
calldata: balance_result.unwrap().to_vec(),
},
BlockId::Tag(BlockTag::Latest),
)
.await;

match call_result {
Ok(result) => {
if result[0] < FieldElement::from_dec_str("10").unwrap() {
get_error("You need to stake atleast 10 STRK".to_string())
} else {
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)),
}
}
}
Err(e) => get_error(format!("{}", e)),
}
}
30 changes: 30 additions & 0 deletions src/endpoints/quests/nostra/staking_quest/verify_twitter_tw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::sync::Arc;

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

#[route(
get,
"/quests/nostra/staking_quest/verify_twitter_tw",
crate::endpoints::quests::nostra::staking_quest::verify_twitter_tw
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
Query(query): Query<VerifyQuery>,
) -> impl IntoResponse {
let task_id = 134;
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)),
}
}
11 changes: 11 additions & 0 deletions src/endpoints/quests/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,17 @@ pub async fn handler(
}),
).into_response(),

Some(39) => (
StatusCode::OK,
Json(TokenURI {
name: "Nostra - Mafia Boss Cigar NFT".into(),
description: "A Nostra - Mafia Boss Cigar NFT won for successfully finishing the Quest".into(),
image: format!("{}/nostra/cigar.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 @@ -33,6 +33,7 @@ fn get_task_id(quiz_name: &str) -> Option<u32> {
"braavos" => Some(98),
"rhino" => Some(100),
"nimbora" => Some(89),
"nostra2" => Some(132),
_ => None,
}
}
Expand Down

0 comments on commit c293a20

Please sign in to comment.