Tracking loan events on Famous Fox Citrus

In this article, we will explore one of the most convenient methods of tracking loans taken on Citrus Protocol, powered by SHYFT APIs.

Khac Vy
Shyft.to

--

The ability to understand when and what events happened on a blockchain is a core part of web3 or decentralized applications. These events serve as triggers that propel updates and interactions within applications, contributing to their dynamic functionality. The Solana blockchain maintains a comprehensive event log, capturing transaction details within blocks, thereby enabling external access to valuable event-specific data. However, applications often grapple with a significant hurdle: translating this intricate event data into human-readable formats. This challenge of extracting user-friendly insights from blockchain events has prompted the emergence of solutions like Shyft, aimed at bridging this information gap and enhancing the usability of applications.

In this article, we will guide you on using the Shyft API to listen for the “lend NFT” event in Citrus. Citrus stands as a lending platform presented by the well-known Fox Federation team. It provides a secure lending experience through its smart contracts that work without needing special permission. These smart contracts directly link NFT holders with those who have liquidity to provide. Does this sound intriguing? Let’s delve deeper to learn more.

Before getting started

To get started, we will need a few things.

Register your Shyft account and get your own Shyft API key.

x-api-key is an authentication parameter, which gives you access to Shyft APIs. You can get your own API Key from the Shyft website. Just signup with your email here and you can get it for free. If you already have an Shyft API Key, please skip this step.

Create Supabase account

We require a place to store our callback data, which the front-end can subsequently query and showcase on the website. Throughout this tutorial, we will employ Supabase for this purpose, although you are free to opt for any database of your preference. If you’re interested in setting up Supabase with a Next.js project, you can find detailed instructions in this link.

Our tutorial will be divided into three steps:

  • Make use of the Shyft API to register a callback that will inform the application when the event takes place.
  • Establish an API to listen for the callback from Shyft. This API will be responsible for receiving callback data from Shyft and storing it in Supabase once received.
  • Retrieve the data from the database and render it on the front-end.

You can clone the full source code of this article from this repo.

Tracking loan events on Famous Fox Citrus

Register callback API

Shyft allows us to set up callbacks for particular addresses. This implies that whenever there are on-chain events involving these addresses, Shyft will quickly inform us with the important event details. In our situation, we’ll set up a callback for the Citrus smart contract address. This approach ensures that if events like listing a new NFT for lending or borrowing a new NFT occur, Shyft will promptly notify us.

To register the callback, you have the option to either manually call the Shyft API or utilize the Shyft JS SDK by supplying the necessary data. In our tutorial, we will opt for the Shyft JS SDK for added convenience. For additional details regarding the callback API, you can find further information at this link.

import shyftClient from "@/lib/shyft"
import { Network, TxnAction } from "@shyft-to/js"
import { NextApiRequest, NextApiResponse } from "next"

export type BaseResponse = {
success: boolean
data?: Object
error?: string
}

const CITRUS_PROGRAM_ID = "JCFRaPv7852ESRwJJGRy2mysUMydXZgVVhrMLmExvmVp"

export default async function handler(req: NextApiRequest, res: NextApiResponse<BaseResponse>) {
if (req.method === "POST") {
try {
await shyftClient.callback.register({
network: Network.Mainnet,
addresses: [CITRUS_PROGRAM_ID],
callbackUrl: `${process.env.NEXT_PUBLIC_CALLBACK_BASE_URL!}/api/callback`,
events: [TxnAction.TAKE_LOAN],
})

res.status(200).json({ success: true, data: "Registered successfuly" })
} catch (err: any) {
console.error(err)

res.status(500).json({ success: false, error: err?.message })
return
}
} else {
return res.status(405).json({ success: false, error: "Method not allowed" })
}
}

Setting API to receive the callback

When registering a callback, it’s necessary to provide Shyft with a callback URL. This URL is where the API resides to handle the callback data. This API uses the standard POST method. When an event takes place, Shyft will make a call to this API and transmit the corresponding data to us. Our responsibility involves examining this data and then saving it in a database. You have the flexibility to decide which parts of the data to store; this choice depends on your application’s logic. In our tutorial’s case, as we are only monitoring the TAKE_LOAN event, we will store specific details such as lender, borrower, nft_address, amount, and apy. If you wish to gain a deeper understanding of how the callback data is structured, you can refer to this link.

Sample callback data

{
"timestamp": "2023-08-29T19:38:19.000Z",
"fee": 0.00001,
"fee_payer": "GRr4z2jjXT9JVHZod94gn2McBxQWqqCUVUi5649MWpZ1",
"signers": [
"GRr4z2jjXT9JVHZod94gn2McBxQWqqCUVUi5649MWpZ1",
"FiyCTJ4hY7NxfDbyZxmSLdFXpXg4bTsM1k21bJFDjhj4"
],
"signatures": [
"4jhBwko8fvCqPaGyTea5CrBRHghi5P8NpqcJapMox3euYdBdx6AZNWGdDVZvxQhBEDJLMX3DUYYeaZTkjm4jTvmA",
"4XMsA2opv2qAJe9XKyaupyMZvG6KVWzxVpr3btkST8DXdtk1vnZCRjPZm8xCpPqdjqUMa3428LfBbVQQTEE9jShB"
],
"protocol": {
"address": "JCFRaPv7852ESRwJJGRy2mysUMydXZgVVhrMLmExvmVp",
"name": "FOXY_CITRUS"
},
"type": "TAKE_LOAN",
"status": "Success",
"actions": [
{
"info": {
"lender": "6YLKPcv3yKiEE2jdxcFW6DWtHYcXv7zpkk41SaG6L9X1",
"borrower": "GRr4z2jjXT9JVHZod94gn2McBxQWqqCUVUi5649MWpZ1",
"currency": "So11111111111111111111111111111111111111112",
"amount": "30.805500000",
"nft_address": "uJeP9d5NQQxi26mKWVVjeABxqfP7T4UaCKGf82RCwKu",
"loan": "",
"escrow": "",
"escrow_token_account": "",
"order_book": "",
"apy": 180,
"loan_duration_seconds": 259200,
"discount": null,
"transfer_to_borrower": "30.805500000"
},
"source_protocol": {
"address": "JCFRaPv7852ESRwJJGRy2mysUMydXZgVVhrMLmExvmVp",
"name": "FOXY_CITRUS"
},
"type": "TAKE_LOAN"
},
{
"info": {
"sender": "EnCzTdpp7ifJssa64sVk5e8dzi9bbryQWMNxjZk9V4AF",
"receiver": "6YLKPcv3yKiEE2jdxcFW6DWtHYcXv7zpkk41SaG6L9X1",
"amount": "1.194500000"
},
"source_protocol": {
"address": "11111111111111111111111111111111",
"name": "SYSTEM_PROGRAM"
},
"type": "SOL_TRANSFER",
"parent_protocol": "JCFRaPv7852ESRwJJGRy2mysUMydXZgVVhrMLmExvmVp"
},
{
"info": {
"sender": "EnCzTdpp7ifJssa64sVk5e8dzi9bbryQWMNxjZk9V4AF",
"receiver": "GRr4z2jjXT9JVHZod94gn2McBxQWqqCUVUi5649MWpZ1",
"amount": "30.805500000"
},
"source_protocol": {
"address": "11111111111111111111111111111111",
"name": "SYSTEM_PROGRAM"
},
"type": "SOL_TRANSFER",
"parent_protocol": "JCFRaPv7852ESRwJJGRy2mysUMydXZgVVhrMLmExvmVp"
}
],
"accounts": []
}
import shyftClient from "@/lib/shyft"
import supabase from "@/lib/supabase"
import { CallbackType } from "@/types"
import { Network } from "@shyft-to/js"
import { NextApiRequest, NextApiResponse } from "next"

type BaseResponse = {
success: boolean
data?: Object
error?: string
}

export type RequestBody = {
tree: string
}

interface ExtendedNextApiRequest extends NextApiRequest {
body: CallbackType
}

export default async function handler(req: ExtendedNextApiRequest, res: NextApiResponse<BaseResponse>) {
if (req.method === "POST") {
try {
const callbackData = req.body

if (callbackData && callbackData.type === "TAKE_LOAN" && callbackData.actions) {
const type = callbackData.type
const action = callbackData.actions.find((action) => action.type === type)
if (!action) {
return res.status(400).json({ success: true, error: "Invalid callback data" })
}

// fetch the detail of the NFT
const nft = await shyftClient.nft.getNftByMint({ mint: action.info.nft_address, network: Network.Mainnet })

await supabase.from("shyft_citrus_activities").insert({
...action.info,
info: action.info,
nft,
})

res.status(200).json({ success: true, error: "Success" })
} else {
return res.status(400).json({ success: true, error: "Invalid callback data" })
}
} catch (err: any) {
console.error(err)

res.status(500).json({ success: false, error: err?.message })
return
}
} else {
return res.status(405).json({ success: false, error: "Method not allowed" })
}
}

Display the data on the front-end.

All’s going well up to this point. We’ve wrapped up the backend work. Now, it’s time to display the stored data on the front-end. You have the freedom to choose how you want to show the data. In our tutorial, we’ll present the activity in a card format that contains details like the lender, borrower, borrowed NFT, amount, APY, and lending duration.

Conclusion

Understanding blockchain events is crucial for decentralized apps. Events trigger updates, making apps dynamic. While blockchains like Solana maintain logs, translating event data remains challenging. Shyft addresses this, enhancing app usability.

We explored Shyft’s API to track “lend NFT” events in Citrus, a lending platform by Fox Federation. It connects NFT holders and liquidity providers securely. We covered registering callbacks, setting up APIs, and showing data on the front-end.

Shyft empowers developers to decode intricate blockchain events, improving decentralized apps. In an evolving blockchain landscape, tools like Shyft bridge user-app interaction gaps.

If you enjoyed reading about Shyft callback API in this article, we invite you to explore more of our content. Discover our other articles on topics like How to parse raw transactions on Solana and Working with compressed NFTs on Solana. We hope these resources will enrich your experience in building with Shyft.

Resources

--

--