lfglabs-dev / api.starknet.quest

starknet.quest rust backend
7 stars 28 forks source link

Add GET Route /defi/rewards to Centralize DeFi Spring Reward Collection #279

Closed Marchand-Nicolas closed 3 weeks ago

Marchand-Nicolas commented 1 month ago

Add GET Route /defi/rewards to Centralize DeFi Spring Reward Collection

Issue Summary

We need to implement a new GET route /defi/rewards that accepts an addr parameter (user address) and returns:

The primary goal is to centralize the collection of DeFi Spring rewards, enabling users to execute all claim calls in a single multicall transaction.

Background and Motivation

Currently, users have to interact with multiple protocols individually to claim their DeFi Spring rewards. By aggregating this process into a single API route, we can streamline reward collection, improve user experience, and reduce transaction overhead.

Protocols to Integrate

We need to fetch reward data from the following protocols:

  1. ZkLend
  2. Nostra
  3. Nimbora
  4. Ekubo

Each protocol provides an API to retrieve reward information and the necessary data to construct claim calls.


1. ZkLend

2. Nostra

3. Nimbora

4. Ekubo


Proposed Implementation

1. Create the GET Route

2. Route Handler Logic

3. Example Response

{
  "rewards": [
    {
      "protocol": "ZkLend",
      "amount": "0x201aeb1ca717e5e47",
      "token": "STRK",
      "contract": "0x01a9acedc9a858ec3a2b6e5b2540aab67c6fab3376c5d56b44dfc3c18d727663",
      "claimId": 381,
      "claimed": false,
      "proof": ["0x3fe2045d6ab41b5981b14742ce2c7db0...", "..."]
    },
    ...
  ],
  "calls": [
    {
      "entryPoint": "claim",
      "contract": "0x01a9acedc9a858ec3a2b6e5b2540aab67c6fab3376c5d56b44dfc3c18d727663",
      "callData": [
        "381", // claim_id for ZkLend
        "{address}",
        "0x201aeb1ca717e5e47", // amount
        ["0x3fe2045d6ab41b5981b14742ce2c7db0...", "..."] // proof array
      ]
    },
    ...
  ]
}

Implementation Steps

  1. Setup Route

    Add the new route to the API using the existing framework (e.g., Axum for Rust).

    #[route(get, "/defi/rewards")]
    pub async fn get_defi_rewards(
       State(state): State<Arc<AppState>>,
       Query(params): Query<HashMap<String, String>>,
    ) -> impl IntoResponse {
       // Handler logic here
    }
  2. Validate Input

    • Ensure the addr parameter is provided and is a valid address format.
  3. Fetch Data from Protocols

    • ZkLend:

      let zklend_url = format!("https://app.zklend.com/api/reward/all/{}", addr);
      let zklend_response = client.get(&zklend_url).send().await?;
      let zklend_rewards = zklend_response.json::<Vec<ZkLendReward>>().await?;
    • Nostra:

      let nostra_request_body = serde_json::json!({
       "dataSource": "nostra-production",
       "database": "prod-a-nostra-db",
       "collection": "rewardProofs",
       "filter": { "account": addr.to_lowercase() }
      });
      let nostra_response = client
       .post("https://us-east-2.aws.data.mongodb-api.com/app/data-yqlpb/endpoint/data/v1/action/find")
       .json(&nostra_request_body)
       .send()
       .await?;
      let nostra_rewards = nostra_response.json::<NostraResponse>().await?;
    • Nimbora:

      let nimbora_url = format!("https://strk-dist-backend.nimbora.io/get_calldata?address={}", addr);
      let nimbora_response = client.get(&nimbora_url).send().await?;
      let nimbora_reward = nimbora_response.json::<NimboraReward>().await?;
    • Ekubo:

      let ekubo_url = format!(
       "https://mainnetapi.ekubo.org/airdrops/{}?token={}",
       addr, ekubo_token_address
      );
      let ekubo_response = client.get(&ekubo_url).send().await?;
      let ekubo_rewards = ekubo_response.json::<Vec<EkuboReward>>().await?;
  4. Construct Rewards

    • Array containing one element per protocol

    • Each element correspond to one line of the following modal:

      image
    • In each element, just set the field required by the modal (protocol name, image, reward amounts & tokens).

  5. Construct Calls

    • For each protocol, map the reward data to a call object.

    • Example for ZkLend:

      let zklend_calls: Vec<Call> = zklend_rewards
       .iter()
       .filter(|reward| !reward.claimed)
       .map(|reward| Call {
           entry_point: "claim".to_string(),
           contract: reward.claim_contract.clone(),
           call_data: vec![
               reward.claim_id.to_string(),
               addr.clone(),
               reward.amount.value.clone(),
               serde_json::to_string(&reward.proof).unwrap(),
           ],
       })
       .collect();
  6. Aggregate Results

    • Combine rewards and calls from all protocols.
    let all_rewards = vec![/* rewards from all protocols */];
    let all_calls = vec![/* calls from all protocols */];
  7. Return the Response

    let response = serde_json::json!({
       "rewards": all_rewards,
       "calls": all_calls
    });
    (StatusCode::OK, Json(response)).into_response()

Error Handling

Testing

Additional Notes

Example Code Snippet

#[route(get, "/defi/rewards")]
pub async fn get_defi_rewards(
    State(state): State<Arc<AppState>>,
    Query(params): Query<HashMap<String, String>>,
) -> impl IntoResponse {
    let addr = match params.get("addr") {
        Some(address) => address,
        None => return get_error("Missing 'addr' parameter".to_string()),
    };

    // Initialize HTTP client
    let client = reqwest::Client::new();

    // Fetch and process rewards from all protocols
    // ...

    // Aggregate rewards and calls
    let response = serde_json::json!({
        "rewards": all_rewards,
        "calls": all_calls
    });

    (StatusCode::OK, Json(response)).into_response()
}

Action Items

References

0xdevcollins commented 1 month ago

Hello @Marchand-Nicolas, I would like to work on this task. Could you please assign it to me?

Here’s how I’ll approach the issue:

Create the /defi/rewards GET Accept a user's wallet address (addr). Return STARK rewards and the necessary claim data for a multicall transaction.

Fetch rewards from four protocols: ZkLend: Retrieve rewards using the API and parse response. Nostra: Use a POST request to fetch reward proofs. Nimbora: Send a GET request to get calldata. Ekubo: Fetch airdrop rewards with token information.

Construct response: Build the rewards and calls objects with entry points, contract addresses, and call data for each protocol.

Handle errors: Validate input, retry failed API calls, and handle timeouts.

Test thoroughly: Use various addresses and edge cases, ensuring proper functionality across all integrated protocols.

Benjtalkshow commented 1 month ago

Hello @Marchand-Nicolas Please kindly assign this task to me. i will make sure a deliver a quality work. I am a Full Stack Developer with extensive experience in blockchain development, specializing in Next.js, TypeScript, Node.js, and Rust. With over 18 contributions across projects in the OnlyDust ecosystem, I’ve developed a strong proficiency in delivering high-quality solutions and tackling complex issues under tight deadlines. My experience spans frontend, backend, smart contract development, and a deep understanding of optimizing and maintaining scalable codebases.

The goal of this task is to implement a new GET route /defi/rewards that simplifies the process of claiming DeFi Spring rewards by centralizing everything into one call.

  1. Users currently need to interact with multiple protocols to claim their rewards, which can be time-consuming and inefficient. This route will accept a user address and fetch available rewards from various DeFi protocols like ZkLend, Nostra, Nimbora, and Ekubo.
  2. It will return both the detailed rewards data and a set of claim call objects, each containing the necessary contract information, function entry points, and call data. By bundling these claim calls into a single multicall transaction, users can execute all reward claims at once, improving the overall user experience and reducing transaction overhead. This centralized approach streamlines the reward collection process and provides a more efficient way for users to manage their DeFi Spring rewards.

How i plan to tackle it

The route will be added under the endpoints directory, and main.rs will be updated to register the route. Also, Helper functions will be placed in utils.rs for code reusability.

od-hunter commented 1 month ago

Can I be assigned this please @Marchand-Nicolas ? I’ve gone through the proposed implementation and I’m ready to do just that. Please

Ugo-X commented 1 month ago

@Marchand-Nicolas , I would love to work on this. I have experience working with this pools from issues i worked on, in starkfarm, and i will be able to send a PR in line with the time schedule.

GideonBature commented 1 month ago

I would love to work on this. I have some experience building APIs using Actix-web, I believe my knowledge of Rust and Actix-web is also applicable in Axum. I also made my first contribution to the Starknet Quest in the just concluded ODhack8.0

JoE11-y commented 1 month ago

Applying for this task. Would love to take it on.