Skip to content

Commit

Permalink
fix: added private rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
k0beLeenders authored and chambaz committed Oct 1, 2024
1 parent 4000222 commit aae8ca5
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 46 deletions.
5 changes: 4 additions & 1 deletion apps/marginfi-v2-trading/src/pages/api/lut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

// Ensure groupAddress is a valid Marginfi account
try {
const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE!, "confirmed");
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "",
"confirmed"
);
const wallet = NodeWallet.local();
const config = await getConfig("production");
const client = await MarginfiClient.fetch(
Expand Down
55 changes: 41 additions & 14 deletions apps/marginfi-v2-trading/src/pages/api/oracle/price.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Bank, BankConfig, BankRaw, findOracleKey, MARGINFI_IDL, MarginfiIdlType, MarginfiProgram, OraclePrice, OracleSetup, parseOracleSetup, parsePriceInfo } from "@mrgnlabs/marginfi-client-v2";
import { CrossbarSimulatePayload, decodeSwitchboardPullFeedData, FeedResponse } from "@mrgnlabs/marginfi-client-v2/dist/vendor";
import {
Bank,
BankConfig,
BankRaw,
findOracleKey,
MARGINFI_IDL,
MarginfiIdlType,
MarginfiProgram,
OraclePrice,
OracleSetup,
parseOracleSetup,
parsePriceInfo,
} from "@mrgnlabs/marginfi-client-v2";
import {
CrossbarSimulatePayload,
decodeSwitchboardPullFeedData,
FeedResponse,
} from "@mrgnlabs/marginfi-client-v2/dist/vendor";
import { chunkedGetRawMultipleAccountInfoOrdered, median, Wallet } from "@mrgnlabs/mrgn-common";
import { Connection, PublicKey } from "@solana/web3.js";
import BigNumber from "bignumber.js";
Expand Down Expand Up @@ -31,13 +47,15 @@ interface OraclePriceString {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const requestedBanksRaw = req.query.banks;

if (!(requestedBanksRaw) || typeof requestedBanksRaw !== 'string') {
return res.status(400).json({ error: 'Invalid input: expected an array of bank base58-encoded addresses.' });
if (!requestedBanksRaw || typeof requestedBanksRaw !== "string") {
return res.status(400).json({ error: "Invalid input: expected an array of bank base58-encoded addresses." });
}

const requestedBanks = requestedBanksRaw.split(',').map((bankAddress) => bankAddress.trim());
const requestedBanks = requestedBanksRaw.split(",").map((bankAddress) => bankAddress.trim());

const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "");
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const idl = { ...MARGINFI_IDL, address: config.mfiConfig.programId.toBase58() } as unknown as MarginfiIdlType;
const provider = new AnchorProvider(connection, {} as Wallet, {
...AnchorProvider.defaultOptions(),
Expand All @@ -57,10 +75,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

const host = extractHost(req.headers.origin) || extractHost(req.headers.referer);
if (!host) {
return res.status(400).json({ error: 'Invalid input: expected a valid host.' });
return res.status(400).json({ error: "Invalid input: expected a valid host." });
}
const feedIdMapRaw: Record<string, string> = await fetch(`${host}/api/oracle/pythFeedMap`).then((response) => response.json());
const feedIdMap: Map<string, PublicKey> = new Map(Object.entries(feedIdMapRaw).map(([key, value]) => [key, new PublicKey(value)]));
const feedIdMapRaw: Record<string, string> = await fetch(`${host}/api/oracle/pythFeedMap`).then((response) =>
response.json()
);
const feedIdMap: Map<string, PublicKey> = new Map(
Object.entries(feedIdMapRaw).map(([key, value]) => [key, new PublicKey(value)])
);

const requestedOraclesData = banksMap.map((b) => ({
oracleKey: findOracleKey(BankConfig.fromAccountParsed(b.data.config), feedIdMap).toBase58(),
Expand All @@ -84,7 +106,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

// If on-chain data is recent enough, use it even for SwitchboardPull oracles
if (oracleData.oracleSetup === OracleSetup.SwitchboardPull && isStale) {
swbPullOraclesStale.push({ data: oracleData, feedHash: Buffer.from(decodeSwitchboardPullFeedData(priceDataRaw.data).feed_hash).toString("hex") });
swbPullOraclesStale.push({
data: oracleData,
feedHash: Buffer.from(decodeSwitchboardPullFeedData(priceDataRaw.data).feed_hash).toString("hex"),
});
continue;
}

Expand All @@ -96,7 +121,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const feedHashes = swbPullOraclesStale.map((value) => value.feedHash);
const crossbarPrices = await fetchCrossbarPrices(feedHashes);

for (const { data: { oracleKey }, feedHash } of swbPullOraclesStale) {
for (const {
data: { oracleKey },
feedHash,
} of swbPullOraclesStale) {
const crossbarPrice = crossbarPrices.get(feedHash);
if (!crossbarPrice) {
throw new Error(`Crossbar didn't return data for ${feedHash}`);
Expand All @@ -106,15 +134,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}

const updatedOraclePricesSorted = requestedOraclesData.map(value => updatedOraclePrices.get(value.oracleKey)!);
const updatedOraclePricesSorted = requestedOraclesData.map((value) => updatedOraclePrices.get(value.oracleKey)!);

res.setHeader("Cache-Control", "s-maxage=20, stale-while-revalidate=59");
return res.status(200).json(updatedOraclePricesSorted.map(stringifyOraclePrice));
} catch (error) {
console.error("Error:", error);
return res.status(500).json({ error: "Error fetching data" });
}

}

async function fetchCrossbarPrices(feedHashes: string[]): Promise<Map<string, OraclePrice>> {
Expand All @@ -137,7 +164,7 @@ async function fetchCrossbarPrices(feedHashes: string[]): Promise<Map<string, Or
if (!response.ok) {
throw new Error("Network response was not ok");
}
const payload = await response.json() as CrossbarSimulatePayload;
const payload = (await response.json()) as CrossbarSimulatePayload;

return crossbarPayloadToOraclePricePerFeedHash(payload);
} catch (error) {
Expand Down
25 changes: 17 additions & 8 deletions apps/marginfi-v2-trading/src/pages/api/oracle/pythFeedMap.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Bank, BankRaw, buildFeedIdMap, MARGINFI_IDL, MarginfiIdlType, MarginfiProgram } from "@mrgnlabs/marginfi-client-v2";
import {
Bank,
BankRaw,
buildFeedIdMap,
MARGINFI_IDL,
MarginfiIdlType,
MarginfiProgram,
} from "@mrgnlabs/marginfi-client-v2";
import { chunkedGetRawMultipleAccountInfoOrdered, Wallet } from "@mrgnlabs/mrgn-common";
import { Connection, PublicKey } from "@solana/web3.js";
import { NextApiRequest, NextApiResponse } from "next";
import config from "~/config/marginfi";

export default async function handler(_req: NextApiRequest, res: NextApiResponse) {
try {
const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "");
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const idl = { ...MARGINFI_IDL, address: config.mfiConfig.programId.toBase58() } as unknown as MarginfiIdlType;
const provider = new AnchorProvider(connection, {} as Wallet, {
...AnchorProvider.defaultOptions(),
commitment: connection.commitment ?? AnchorProvider.defaultOptions().commitment,
});
const program = new Program(idl, provider) as any as MarginfiProgram;

let bankAddresses = (await connection.getProgramAccounts(config.mfiConfig.programId, {
filters: [
{ memcmp: { offset: 8 + 32 + 1, bytes: config.mfiConfig.groupPk.toBase58() } },
], dataSlice: { length: 0, offset: 0 }
})).map((bank) => bank.pubkey.toBase58());
let bankAddresses = (
await connection.getProgramAccounts(config.mfiConfig.programId, {
filters: [{ memcmp: { offset: 8 + 32 + 1, bytes: config.mfiConfig.groupPk.toBase58() } }],
dataSlice: { length: 0, offset: 0 },
})
).map((bank) => bank.pubkey.toBase58());

const banksAis = await chunkedGetRawMultipleAccountInfoOrdered(connection, bankAddresses);
let banksMap: { address: PublicKey; data: BankRaw }[] = banksAis.map((account, index) => ({
Expand All @@ -38,7 +48,6 @@ export default async function handler(_req: NextApiRequest, res: NextApiResponse
console.error("Error:", error);
return res.status(500).json({ error: "Error fetching data" });
}

}

function stringifyFeedIdMap(feedIdMap: Map<string, PublicKey>) {
Expand Down
55 changes: 41 additions & 14 deletions apps/marginfi-v2-ui/src/pages/api/oracle/price.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Bank, BankConfig, BankRaw, findOracleKey, MARGINFI_IDL, MarginfiIdlType, MarginfiProgram, OraclePrice, OracleSetup, parseOracleSetup, parsePriceInfo } from "@mrgnlabs/marginfi-client-v2";
import { CrossbarSimulatePayload, decodeSwitchboardPullFeedData, FeedResponse } from "@mrgnlabs/marginfi-client-v2/dist/vendor";
import {
Bank,
BankConfig,
BankRaw,
findOracleKey,
MARGINFI_IDL,
MarginfiIdlType,
MarginfiProgram,
OraclePrice,
OracleSetup,
parseOracleSetup,
parsePriceInfo,
} from "@mrgnlabs/marginfi-client-v2";
import {
CrossbarSimulatePayload,
decodeSwitchboardPullFeedData,
FeedResponse,
} from "@mrgnlabs/marginfi-client-v2/dist/vendor";
import { chunkedGetRawMultipleAccountInfoOrdered, median, Wallet } from "@mrgnlabs/mrgn-common";
import { Connection, PublicKey } from "@solana/web3.js";
import BigNumber from "bignumber.js";
Expand Down Expand Up @@ -31,13 +47,15 @@ interface OraclePriceString {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const requestedBanksRaw = req.query.banks;

if (!(requestedBanksRaw) || typeof requestedBanksRaw !== 'string') {
return res.status(400).json({ error: 'Invalid input: expected an array of bank base58-encoded addresses.' });
if (!requestedBanksRaw || typeof requestedBanksRaw !== "string") {
return res.status(400).json({ error: "Invalid input: expected an array of bank base58-encoded addresses." });
}

const requestedBanks = requestedBanksRaw.split(',').map((bankAddress) => bankAddress.trim());
const requestedBanks = requestedBanksRaw.split(",").map((bankAddress) => bankAddress.trim());

const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "");
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const idl = { ...MARGINFI_IDL, address: config.mfiConfig.programId.toBase58() } as unknown as MarginfiIdlType;
const provider = new AnchorProvider(connection, {} as Wallet, {
...AnchorProvider.defaultOptions(),
Expand All @@ -57,10 +75,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

const host = extractHost(req.headers.origin) || extractHost(req.headers.referer);
if (!host) {
return res.status(400).json({ error: 'Invalid input: expected a valid host.' });
return res.status(400).json({ error: "Invalid input: expected a valid host." });
}
const feedIdMapRaw: Record<string, string> = await fetch(`${host}/api/oracle/pythFeedMap`).then((response) => response.json());
const feedIdMap: Map<string, PublicKey> = new Map(Object.entries(feedIdMapRaw).map(([key, value]) => [key, new PublicKey(value)]));
const feedIdMapRaw: Record<string, string> = await fetch(`${host}/api/oracle/pythFeedMap`).then((response) =>
response.json()
);
const feedIdMap: Map<string, PublicKey> = new Map(
Object.entries(feedIdMapRaw).map(([key, value]) => [key, new PublicKey(value)])
);

const requestedOraclesData = banksMap.map((b) => ({
oracleKey: findOracleKey(BankConfig.fromAccountParsed(b.data.config), feedIdMap).toBase58(),
Expand All @@ -84,7 +106,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

// If on-chain data is recent enough, use it even for SwitchboardPull oracles
if (oracleData.oracleSetup === OracleSetup.SwitchboardPull && isStale) {
swbPullOraclesStale.push({ data: oracleData, feedHash: Buffer.from(decodeSwitchboardPullFeedData(priceDataRaw.data).feed_hash).toString("hex") });
swbPullOraclesStale.push({
data: oracleData,
feedHash: Buffer.from(decodeSwitchboardPullFeedData(priceDataRaw.data).feed_hash).toString("hex"),
});
continue;
}

Expand All @@ -96,7 +121,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const feedHashes = swbPullOraclesStale.map((value) => value.feedHash);
const crossbarPrices = await fetchCrossbarPrices(feedHashes);

for (const { data: { oracleKey }, feedHash } of swbPullOraclesStale) {
for (const {
data: { oracleKey },
feedHash,
} of swbPullOraclesStale) {
const crossbarPrice = crossbarPrices.get(feedHash);
if (!crossbarPrice) {
throw new Error(`Crossbar didn't return data for ${feedHash}`);
Expand All @@ -106,15 +134,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}

const updatedOraclePricesSorted = requestedOraclesData.map(value => updatedOraclePrices.get(value.oracleKey)!);
const updatedOraclePricesSorted = requestedOraclesData.map((value) => updatedOraclePrices.get(value.oracleKey)!);

res.setHeader("Cache-Control", "s-maxage=20, stale-while-revalidate=59");
return res.status(200).json(updatedOraclePricesSorted.map(stringifyOraclePrice));
} catch (error) {
console.error("Error:", error);
return res.status(500).json({ error: "Error fetching data" });
}

}

async function fetchCrossbarPrices(feedHashes: string[]): Promise<Map<string, OraclePrice>> {
Expand All @@ -137,7 +164,7 @@ async function fetchCrossbarPrices(feedHashes: string[]): Promise<Map<string, Or
if (!response.ok) {
throw new Error("Network response was not ok");
}
const payload = await response.json() as CrossbarSimulatePayload;
const payload = (await response.json()) as CrossbarSimulatePayload;

return crossbarPayloadToOraclePricePerFeedHash(payload);
} catch (error) {
Expand Down
25 changes: 17 additions & 8 deletions apps/marginfi-v2-ui/src/pages/api/oracle/pythFeedMap.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Bank, BankRaw, buildFeedIdMap, MARGINFI_IDL, MarginfiIdlType, MarginfiProgram } from "@mrgnlabs/marginfi-client-v2";
import {
Bank,
BankRaw,
buildFeedIdMap,
MARGINFI_IDL,
MarginfiIdlType,
MarginfiProgram,
} from "@mrgnlabs/marginfi-client-v2";
import { chunkedGetRawMultipleAccountInfoOrdered, Wallet } from "@mrgnlabs/mrgn-common";
import { Connection, PublicKey } from "@solana/web3.js";
import { NextApiRequest, NextApiResponse } from "next";
import config from "~/config/marginfi";

export default async function handler(_req: NextApiRequest, res: NextApiResponse) {
try {
const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "");
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const idl = { ...MARGINFI_IDL, address: config.mfiConfig.programId.toBase58() } as unknown as MarginfiIdlType;
const provider = new AnchorProvider(connection, {} as Wallet, {
...AnchorProvider.defaultOptions(),
commitment: connection.commitment ?? AnchorProvider.defaultOptions().commitment,
});
const program = new Program(idl, provider) as any as MarginfiProgram;

let bankAddresses = (await connection.getProgramAccounts(config.mfiConfig.programId, {
filters: [
{ memcmp: { offset: 8 + 32 + 1, bytes: config.mfiConfig.groupPk.toBase58() } },
], dataSlice: { length: 0, offset: 0 }
})).map((bank) => bank.pubkey.toBase58());
let bankAddresses = (
await connection.getProgramAccounts(config.mfiConfig.programId, {
filters: [{ memcmp: { offset: 8 + 32 + 1, bytes: config.mfiConfig.groupPk.toBase58() } }],
dataSlice: { length: 0, offset: 0 },
})
).map((bank) => bank.pubkey.toBase58());

const banksAis = await chunkedGetRawMultipleAccountInfoOrdered(connection, bankAddresses);
let banksMap: { address: PublicKey; data: BankRaw }[] = banksAis.map((account, index) => ({
Expand All @@ -38,7 +48,6 @@ export default async function handler(_req: NextApiRequest, res: NextApiResponse
console.error("Error:", error);
return res.status(500).json({ error: "Error fetching data" });
}

}

function stringifyFeedIdMap(feedIdMap: Map<string, PublicKey>) {
Expand Down
3 changes: 2 additions & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"NEXT_PUBLIC_GROUPS_MAP",
"NEXT_PUBLIC_BANKS_MAP",
"NEXT_PUBLIC_TOKENS_MAP",
"NEXT_RUNTIME"
"NEXT_RUNTIME",
"PRIVATE_RPC_ENDPOINT_OVERRIDE"
]
}

0 comments on commit aae8ca5

Please sign in to comment.