Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2837bbb
Merge pull request #132 from akiraonstarknet/dev
akiraonstarknet Mar 19, 2025
d7a2f77
Merge pull request #133 from akiraonstarknet/dev
akiraonstarknet Mar 21, 2025
7a4b570
Merge pull request #137 from akiraonstarknet/dev
akiraonstarknet Mar 28, 2025
02ce26b
Merge pull request #138 from akiraonstarknet/dev
akiraonstarknet Mar 28, 2025
31ee0ba
Merge pull request #139 from akiraonstarknet/dev
akiraonstarknet Mar 30, 2025
9efa001
add cache to actions UI
akiraonstarknet Apr 3, 2025
302dd64
Merge branch 'dev' of github.com:akiraonstarknet/starkfarm-client int…
akiraonstarknet Apr 3, 2025
e1c40ea
Merge pull request #140 from akiraonstarknet/dev
akiraonstarknet Apr 3, 2025
cb9c343
add Ekubo Vault
akiraonstarknet Apr 5, 2025
9fcca0f
Merge pull request #141 from akiraonstarknet/feat/cl_vault
akiraonstarknet Apr 5, 2025
3a6e833
comment
akiraonstarknet Apr 5, 2025
7adee30
Merge branch 'feat/cl_vault' into dev
akiraonstarknet Apr 5, 2025
c708b7c
fix build
akiraonstarknet Apr 5, 2025
447b484
fix form reset bug, cache issue, stats processing
akiraonstarknet Apr 6, 2025
6f9a61b
fixing cache issues
akiraonstarknet Apr 6, 2025
f4965a4
add revalidate API
akiraonstarknet Apr 7, 2025
70fb8b1
add CRON secret support in reval
akiraonstarknet Apr 7, 2025
e2c1a63
fix revalidate
akiraonstarknet Apr 7, 2025
4379234
debug
akiraonstarknet Apr 7, 2025
e383b90
debug[2]
akiraonstarknet Apr 7, 2025
e792fc7
add upstash redis support
akiraonstarknet Apr 7, 2025
a785c6f
add strats api in crons
akiraonstarknet Apr 7, 2025
ca6f0f1
update yield to 16000 blocks in cl vault
akiraonstarknet Apr 8, 2025
1e4340a
update @strkfarm/sdk
akiraonstarknet Apr 11, 2025
4fb07bd
bump @strkfarm/sdk - vesu api fix
akiraonstarknet Apr 11, 2025
2f95fdd
Merge pull request #142 from akiraonstarknet/dev
akiraonstarknet Apr 14, 2025
f4948e5
add rewards api
akiraonstarknet Apr 15, 2025
feacc10
ekubo: improve deposit UX
akiraonstarknet Apr 15, 2025
6b486e9
sdk bump 1.0.32
akiraonstarknet Apr 15, 2025
a851fe0
Merge pull request #143 from akiraonstarknet/dev
akiraonstarknet Apr 15, 2025
55cfa2d
Merge branch 'dev' into rewards-api
akiraonstarknet Apr 15, 2025
8bf0946
update rewards APY
akiraonstarknet Apr 15, 2025
f3c63bf
fix strats sorting bug
akiraonstarknet Apr 16, 2025
e956b16
update rewards apy
akiraonstarknet Apr 16, 2025
466c89f
Merge pull request #144 from akiraonstarknet/dev
akiraonstarknet Apr 16, 2025
b7b3942
@strkfarm/sdk tokens fix
akiraonstarknet Apr 16, 2025
c25ee98
Merge pull request #145 from akiraonstarknet/dev
akiraonstarknet Apr 16, 2025
136a271
convert rewards API to dynamic
akiraonstarknet Apr 17, 2025
bb4dd26
improve APY tooltips
akiraonstarknet Apr 17, 2025
19169be
Merge pull request #146 from akiraonstarknet/dev
akiraonstarknet Apr 17, 2025
4e787c5
update strategies route
akiraonstarknet Apr 18, 2025
3631c6a
update strategies route [2]
akiraonstarknet Apr 18, 2025
afb1d82
add ekubo docs
akiraonstarknet Apr 18, 2025
89d49eb
Merge pull request #147 from akiraonstarknet/dev
akiraonstarknet Apr 18, 2025
ab47350
update rewards
akiraonstarknet Apr 18, 2025
e8af322
Merge branch 'dev' into prod
akiraonstarknet Apr 18, 2025
d752a9e
fix usdt logo
akiraonstarknet Apr 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ CRON_SECRET=
# Note: Not everything is supported on sepolia
# Default: mainnet
NEXT_PUBLIC_NETWORK=mainnet

# To enable API caching
VK_REDIS_KV_URL=
VK_REDIS_KV_REST_API_READ_ONLY_TOKEN=
VK_REDIS_REDIS_URL=
VK_REDIS_KV_REST_API_TOKEN=
VK_REDIS_KV_REST_API_URL=
VK_REDIS_PREFIX="strkfarm::beta"
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@
"@prisma/client": "5.18.0",
"@starknet-react/chains": "3.0.0",
"@starknet-react/core": "3.0.1",
"@strkfarm/sdk": "^1.0.28",
"@strkfarm/sdk": "^1.0.35",
"@tanstack/query-core": "5.28.0",
"@types/mixpanel-browser": "2.49.0",
"@types/mustache": "4.2.5",
"@upstash/redis": "^1.34.7",
"@vercel/analytics": "1.2.2",
"@vercel/speed-insights": "1.0.12",
"@xyflow/react": "^12.4.4",
Expand All @@ -53,6 +54,7 @@
"graphql": "16.9.0",
"jotai": "2.6.4",
"jotai-tanstack-query": "0.8.5",
"lodash.debounce": "^4.0.8",
"mixpanel": "^0.18.0",
"mixpanel-browser": "2.49.0",
"mustache": "4.2.0",
Expand All @@ -75,6 +77,7 @@
"wonka": "6.3.4"
},
"devDependencies": {
"@types/lodash.debounce": "^4.0.9",
"@types/node": "20",
"@types/react": "18",
"@types/react-dom": "18",
Expand Down
151 changes: 151 additions & 0 deletions src/app/api/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { STRKFarmStrategyAPIResult } from '@/store/strkfarm.atoms';
import { Redis } from '@upstash/redis';
import { Contract, RpcProvider, uint256 } from 'starknet';

const kvRedis = new Redis({
url: process.env.VK_REDIS_KV_REST_API_URL,
token: process.env.VK_REDIS_KV_REST_API_TOKEN,
});

export async function getDataFromRedis(
key: string,
url: string,
revalidate: number,
) {
if (url.includes('no_cache=true')) {
// force no cache
return null;
}
const cacheData: any = await kvRedis.get(key);
if (
cacheData &&
new Date().getTime() - new Date(cacheData.lastUpdated).getTime() <
revalidate * 1000
) {
console.log(`Cache hit for ${key}`);
return cacheData;
}

return null;
}

export default kvRedis;

export const getRewardsInfo = async (
strategies: Pick<STRKFarmStrategyAPIResult, 'id' | 'tvlUsd' | 'contract'>[],
) => {
const funder =
'0x02D6cf6182259ee62A001EfC67e62C1fbc0dF109D2AA4163EB70D6d1074F0173';
const allowedStrats = [
{
id: 'vesu_fusion_eth',
// ! consider exchange rate of vToken
maxRewardsPerDay: 0.047, // in token units
maxAPY: 200, // in percent
underlyingTokenName: 'ETH',
decimals: 18,
rewardToken:
'0x021fe2ca1b7e731e4a5ef7df2881356070c5d72db4b2d19f9195f6b641f75df0',
},
];

const provider = new RpcProvider({
nodeUrl: process.env.RPC_URL!,
});

const rewardsInfo: {
id: string;
reward: number;
tvlUsd: number;
rewardAPY: number;
rewardDecimals: number;
maxRewardsPerDay: number;
rewardToken: string;
funder: string;
receiver: string;
}[] = [];
for (const strat of strategies) {
const stratId = strat.id;
const stratAllowed = allowedStrats.find(
(allowedStrat) => allowedStrat.id === stratId,
);
if (stratAllowed) {
// Fetch the price of the underlying token
const priceResponse = await fetch(
`${process.env.HOSTNAME}/api/price/${stratAllowed.underlyingTokenName}`,
);
const priceData = await priceResponse.json();
// consider token price of vToken
const clsVToken = await provider.getClassAt(stratAllowed.rewardToken);
const tokenContractVToken = new Contract(
clsVToken.abi,
stratAllowed.rewardToken,
provider,
);
const shareValue = await tokenContractVToken.call('convert_to_assets', [
uint256.bnToUint256((1e18).toString()),
]);
console.log(`shareValue::${stratId}::${shareValue}`);
const tokenPrice =
(priceData.price *
Number(
(BigInt(shareValue.toString()) * BigInt(10000)) /
BigInt((1e18).toString()),
)) /
10000;
console.log(
`RewardCalc::${stratId}::tokenPrice::${tokenPrice}, underlyingTokenPrice::${priceData.price}`,
);

const tvlUsd = strat.tvlUsd;
console.log(`RewardCalc::${stratId}::tvlUsd::${tvlUsd}`);

// Calculate the hourly reward based on TVL and token price
const rewardBasedOnTVL =
(tvlUsd * stratAllowed.maxAPY) / (100 * 365 * 24 * tokenPrice);
console.log(
`RewardCalc::${stratId}::ewardBasedOnTVL::${rewardBasedOnTVL}`,
);
console.log(`RewardCalc::${stratId}::tvl::${tvlUsd}`);

// Ensure the reward does not exceed max rewards per day
let finalReward = Math.min(
rewardBasedOnTVL,
stratAllowed.maxRewardsPerDay / 24,
);
console.log(`RewardCalc::${stratId}::finalReward::${finalReward}`);

// if less bal available, use the available balance
const rewardToken = stratAllowed.rewardToken;
const cls = await provider.getClassAt(rewardToken);
const tokenContract = new Contract(cls.abi, rewardToken, provider);
const available = await tokenContract.balanceOf(funder);
const availableBal =
Number(
BigInt(available.toString()) /
BigInt(10 ** (stratAllowed.decimals - 4)),
) / 10000;
console.log(
`RewardCalc::${stratId}::availableBal::${availableBal.toString()}`,
);

finalReward = Math.min(finalReward, availableBal);
console.log(`RewardCalc::${stratId}::finalReward::${finalReward}`);

// Calculate the reward APY
rewardsInfo.push({
id: stratId,
reward: finalReward,
rewardDecimals: stratAllowed.decimals,
tvlUsd,
rewardAPY: ((finalReward * 24 * 365) / (tvlUsd / tokenPrice)) * 100,
maxRewardsPerDay: stratAllowed.maxRewardsPerDay,
rewardToken: stratAllowed.rewardToken,
funder,
receiver: strat.contract[0].address,
});
}
}

return rewardsInfo;
};
1 change: 1 addition & 0 deletions src/app/api/price/[name]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NextResponse } from 'next/server';
import { getMainnetConfig, PricerRedis } from '@strkfarm/sdk';

export const revalidate = 300; // 5 mins
export const dynamic = 'force-dynamic';

// only meant for backend calls
async function initRedis() {
Expand Down
157 changes: 157 additions & 0 deletions src/app/api/raffle/_route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// import { NextResponse } from 'next/server';

// import { db } from '@/db';
// import { getStrategies } from '@/store/strategies.atoms';
// import { standariseAddress } from '@/utils';

// export async function POST(req: Request) {
// const { address, type } = await req.json();

// if (!address || !type) {
// return NextResponse.json({
// success: false,
// message: 'address not found',
// user: null,
// });
// }

// if (!type || !['SHARED_ON_X', 'ACTIVE_DEPOSITS', 'REGISTER'].includes(type)) {
// return NextResponse.json({
// success: false,
// message: 'Invalid type',
// });
// }

// // standardised address
// let parsedAddress = address;
// try {
// parsedAddress = standariseAddress(address);
// } catch (e) {
// throw new Error('Invalid address');
// }

// const user = await db.user.findFirst({
// where: {
// address: parsedAddress,
// },
// });

// if (!user) {
// return NextResponse.json({
// success: false,
// message: 'User not found',
// user: null,
// });
// }

// if (type === 'REGISTER') {
// const raffleUser = await db.raffle.findFirst({
// where: {
// userId: user.id,
// },
// });

// if (raffleUser) {
// return NextResponse.json({
// success: false,
// message: 'User already registered',
// user: raffleUser,
// });
// }

// const createdUser = await db.raffle.create({
// data: {
// isRaffleParticipant: true,
// User: {
// connect: {
// address: parsedAddress,
// },
// },
// },
// });

// return NextResponse.json({
// success: true,
// message: 'User registered for raffle successfully',
// user: createdUser,
// });
// }

// const raffleUser = await db.raffle.findFirst({
// where: {
// userId: user.id,
// isRaffleParticipant: true,
// },
// });

// if (!raffleUser) {
// return NextResponse.json({
// success: false,
// message: 'Registered user not found',
// user: null,
// });
// }

// if (type === 'SHARED_ON_X') {
// const updatedUser = await db.raffle.update({
// where: {
// raffleId: raffleUser.raffleId,
// },
// data: {
// sharedOnX: true,
// },
// });

// return NextResponse.json({
// success: true,
// message: 'Shared on X successfully',
// user: updatedUser,
// });
// }

// if (type === 'ACTIVE_DEPOSITS') {
// const strategies = getStrategies();

// const values = strategies.map(async (strategy) => {
// const balanceInfo = await strategy.getUserTVL(parsedAddress);
// return {
// id: strategy.id,
// usdValue: balanceInfo.usdValue,
// tokenInfo: {
// name: balanceInfo.tokenInfo.name,
// symbol: balanceInfo.tokenInfo.name,
// logo: balanceInfo.tokenInfo.logo,
// decimals: balanceInfo.tokenInfo.decimals,
// displayDecimals: balanceInfo.tokenInfo.displayDecimals,
// },
// amount: balanceInfo.amount.toEtherStr(),
// };
// });

// const result = await Promise.all(values);
// const sum = result.reduce((acc, item) => acc + item.usdValue, 0);

// if (sum > 10) {
// const createdUser = await db.raffle.update({
// where: {
// raffleId: raffleUser.raffleId,
// },
// data: {
// activeDeposits: true,
// },
// });

// return NextResponse.json({
// success: true,
// message: 'Active deposits found',
// user: createdUser,
// });
// }

// return NextResponse.json({
// success: false,
// message: 'No active deposits found',
// user: null,
// });
// }
// }
Loading
Loading