Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/rpc proxy #955

Merged
merged 12 commits into from
Nov 15, 2024
4 changes: 3 additions & 1 deletion apps/marginfi-landing-page/src/lib/useProtocolStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
import * as solanaStakePool from "@solana/spl-stake-pool";
import { MarginfiClient, getConfig, Bank, OraclePrice, getPriceWithConfidence } from "@mrgnlabs/marginfi-client-v2";
import { loadBankMetadatas, nativeToUi } from "@mrgnlabs/mrgn-common";
import { generateEndpoint } from "~/rpc.utils";

type UseProtocolStats = {
stats: Stats;
Expand Down Expand Up @@ -33,7 +34,8 @@ export const useProtocolStats = (): UseProtocolStats => {

React.useEffect(() => {
const init = async () => {
const connection = new Connection(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE!);
const rpcEndpoint = await generateEndpoint(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE ?? "");
const connection = new Connection(rpcEndpoint);
const [bankMetadataMap] = await Promise.all([loadBankMetadatas()]);
const bankAddresses = Object.keys(bankMetadataMap).map((address) => new PublicKey(address));

Expand Down
25 changes: 25 additions & 0 deletions apps/marginfi-landing-page/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NextRequest, NextResponse } from "next/server";
import { generateEndpoint } from "~/rpc.utils";

const allowedOrigins = [
"https://www.marginfi.com",
"https://marginfi-landing-page-v2.vercel.app",
"http://localhost:3005",
];

export async function middleware(req: NextRequest) {
const fullRpcProxy = await generateEndpoint(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE ?? "");

if (req.nextUrl.toString() === fullRpcProxy) {
const origin = req.headers.get("origin") ?? "";

if (!allowedOrigins.includes(origin)) {
return new Response("Access Denied", {
status: 403,
statusText: "Forbidden",
});
}

return NextResponse.rewrite(new URL(process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE ?? ""));
}
}
2 changes: 1 addition & 1 deletion apps/marginfi-landing-page/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"esModuleInterop": true,
"downlevelIteration": true,
"paths": {
"~/*": ["./src/*"]
"~/*": ["./src/*", "../../packages/mrgn-ui/src/*", "../../packages/mrgn-utils/src/*"]
},
"plugins": [
{
Expand Down
73 changes: 46 additions & 27 deletions apps/marginfi-v2-trading/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,61 @@
import { NextRequest, NextResponse } from "next/server";
import { generateEndpoint } from "~/rpc.utils";

export const config = {
matcher: ["/", "/index", "/yield", "/portfolio", "/trade/:path*"],
matcher: ["/", "/index", "/yield", "/portfolio", "/trade/:path*", "/api/proxy/:path*"],
};

const restrictedCountries = ["US", "VE", "CU", "IR", "KP", "SY"];

export function middleware(req: NextRequest) {
const basicAuth = req.headers.get("authorization");
const url = req.nextUrl;
const response = NextResponse.next();

if (
!process.env.GEOBLOCK_DISABLED &&
req.geo &&
req.geo.country &&
restrictedCountries.includes(req.geo.country) &&
!req.nextUrl.pathname.startsWith("/blocked")
) {
return NextResponse.redirect("https://www.thearena.trade/blocked");
}
const allowedOrigins = ["https://www.thearena.trade", "https://staging.thearena.trade", "http://localhost:3006"];

if (process.env.AUTHENTICATION_DISABLED === "true") {
return response;
}
export async function middleware(req: NextRequest) {
const fullRpcProxy = await generateEndpoint(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE ?? "");

if (req.nextUrl.toString() === fullRpcProxy) {
const origin = req.headers.get("origin") ?? "";

if (basicAuth) {
const authValue = basicAuth.split(" ")[1];
const [providedUser, providedPassword] = atob(authValue).split(":");
if (!allowedOrigins.includes(origin)) {
return new Response("Access Denied", {
status: 403,
statusText: "Forbidden",
});
}

const expectedUser = process.env.AUTHENTICATION_USERNAME || "admin";
const expectedPassword = process.env.AUTHENTICATION_PASSWORD || "admin";
// Handle simple requests
return NextResponse.rewrite(new URL(process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE ?? ""));
} else {
const basicAuth = req.headers.get("authorization");
const url = req.nextUrl;
const response = NextResponse.next();

if (
!process.env.GEOBLOCK_DISABLED &&
req.geo &&
req.geo.country &&
restrictedCountries.includes(req.geo.country) &&
!req.nextUrl.pathname.startsWith("/blocked")
) {
return NextResponse.redirect("https://www.thearena.trade/blocked");
}

if (providedUser === expectedUser && providedPassword === expectedPassword) {
if (process.env.AUTHENTICATION_DISABLED === "true") {
return response;
}
}
url.pathname = "/api/auth";

return NextResponse.rewrite(url);
if (basicAuth) {
const authValue = basicAuth.split(" ")[1];
const [providedUser, providedPassword] = atob(authValue).split(":");

const expectedUser = process.env.AUTHENTICATION_USERNAME || "admin";
const expectedPassword = process.env.AUTHENTICATION_PASSWORD || "admin";

if (providedUser === expectedUser && providedPassword === expectedPassword) {
return response;
}
}
url.pathname = "/api/auth";

return NextResponse.rewrite(url);
}
}
16 changes: 12 additions & 4 deletions apps/marginfi-v2-trading/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Analytics } from "@vercel/analytics/react";
import { BankMetadataRaw } from "@mrgnlabs/mrgn-common";
import { DEFAULT_MAX_CAP, Desktop, Mobile, init as initAnalytics } from "@mrgnlabs/mrgn-utils";
import { ActionProvider, AuthDialog } from "@mrgnlabs/mrgn-ui";
import { generateEndpoint } from "~/rpc.utils";

import config from "~/config";
import { useUiStore } from "~/store";
Expand All @@ -38,6 +39,7 @@ type MrgnAppProps = { path: string; bank: BankMetadataRaw | null };
export default function MrgnApp({ Component, pageProps, path, bank }: AppProps & MrgnAppProps) {
const { query, isReady } = useRouter();
const [ready, setReady] = React.useState(false);
const [rpcEndpoint, setRpcEndpoint] = React.useState("");

const [broadcastType, priorityType, maxCap, maxCapType] = useUiStore((state) => [
state.broadcastType,
Expand All @@ -47,15 +49,21 @@ export default function MrgnApp({ Component, pageProps, path, bank }: AppProps &
]);

React.useEffect(() => {
setReady(true);
initAnalytics();
const init = async () => {
const rpcEndpoint = await generateEndpoint(config.rpcEndpoint);
setRpcEndpoint(rpcEndpoint);
setReady(true);
initAnalytics();
};

init();
}, []);

return (
<>
<Meta path={path} bank={bank} />
{ready && (
<ConnectionProvider endpoint={config.rpcEndpoint}>
{ready && rpcEndpoint && (
<ConnectionProvider endpoint={rpcEndpoint}>
<TipLinkWalletAutoConnect isReady={isReady} query={query}>
<WalletProvider wallets={WALLET_ADAPTERS} autoConnect={true}>
<MrgnWalletProvider>
Expand Down
5 changes: 1 addition & 4 deletions apps/marginfi-v2-trading/src/pages/api/lut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

// Ensure groupAddress is a valid Marginfi account
try {
const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || "",
"confirmed"
);
const connection = new Connection(process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || "", "confirmed");
const wallet = NodeWallet.local();
const config = await getConfig("production");
const client = await MarginfiClient.fetch(
Expand Down
6 changes: 2 additions & 4 deletions apps/marginfi-v2-trading/src/pages/api/oracle/price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

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

const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const connection = new Connection(process.env.PRIVATE_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 Down Expand Up @@ -192,7 +190,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)

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

res.setHeader("Cache-Control", "s-maxage=5, stale-while-revalidate=59");
res.setHeader("Cache-Control", "s-maxage=10, stale-while-revalidate=59");
return res.status(200).json(updatedOraclePricesSorted.map(stringifyOraclePrice));
} catch (error) {
console.error("Error:", error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(400).json({ error: "Invalid input: expected a groupPk string." });
}

const connection = new Connection(
process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE || ""
);
const connection = new Connection(process.env.PRIVATE_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 Down

This file was deleted.

This file was deleted.

2 changes: 0 additions & 2 deletions apps/marginfi-v2-ui/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const environment = process.env.NEXT_PUBLIC_MARGINFI_ENVIRONMENT;
const rpcEndpointOverride = process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE;
const dialectDappAddress = process.env.NEXT_PUBLIC_DIALECT_DAPP_ADDRESS;

let rpcEndpoint;
switch (environment) {
Expand All @@ -22,7 +21,6 @@ switch (environment) {

const config = {
rpcEndpoint,
dialectDappAddress,
};

export default config;
71 changes: 47 additions & 24 deletions apps/marginfi-v2-ui/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,66 @@
import { NextRequest, NextResponse } from "next/server";
import { generateEndpoint } from "~/rpc.utils";

export const config = {
matcher: ["/", "/index", "/stake", "/swap", "/bridge", "/earn", "/points", "/looper"],
matcher: ["/", "/index", "/stake", "/swap", "/bridge", "/earn", "/points", "/looper", "/api/proxy/:path*"],
};

const restrictedCountries = ["VE", "CU", "IR", "KP", "SY"];
const leverageRestrictedCountries = ["US"];
const restrictedRoute = "/looper";

export function middleware(req: NextRequest) {
const basicAuth = req.headers.get("authorization");
const url = req.nextUrl;
const response = NextResponse.next();
const country = req.geo?.country;
const allowedOrigins = [
"https://app.marginfi.com",
"https://marginfi-v2-ui-git-staging-mrgn.vercel.app",
"http://localhost:3004",
];

if (country && restrictedCountries.includes(country)) {
return NextResponse.redirect("https://www.marginfi.com");
}
export async function middleware(req: NextRequest) {
const fullRpcProxy = await generateEndpoint(process.env.NEXT_PUBLIC_MARGINFI_RPC_ENDPOINT_OVERRIDE ?? "");

if (req.nextUrl.pathname.startsWith(restrictedRoute) && country && leverageRestrictedCountries.includes(country)) {
return NextResponse.redirect(new URL("/not-allowed", req.url));
}
if (req.nextUrl.toString() === fullRpcProxy) {
const origin = req.headers.get("origin") ?? "";

if (process.env.AUTHENTICATION_DISABLED === "true") {
return response;
}
if (!allowedOrigins.includes(origin)) {
return new Response("Access Denied", {
status: 403,
statusText: "Forbidden",
});
}

if (basicAuth) {
const authValue = basicAuth.split(" ")[1];
const [providedUser, providedPassword] = atob(authValue).split(":");
// Handle simple requests
return NextResponse.rewrite(new URL(process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE ?? ""));
} else {
const basicAuth = req.headers.get("authorization");
const url = req.nextUrl;
const response = NextResponse.next();
const country = req.geo?.country;

const expectedUser = process.env.AUTHENTICATION_USERNAME || "admin";
const expectedPassword = process.env.AUTHENTICATION_PASSWORD || "admin";
if (country && restrictedCountries.includes(country)) {
return NextResponse.redirect("https://www.marginfi.com");
}

if (req.nextUrl.pathname.startsWith(restrictedRoute) && country && leverageRestrictedCountries.includes(country)) {
return NextResponse.redirect(new URL("/not-allowed", req.url));
}

if (providedUser === expectedUser && providedPassword === expectedPassword) {
if (process.env.AUTHENTICATION_DISABLED === "true") {
return response;
}
}
url.pathname = "/api/auth";

return NextResponse.rewrite(url);
if (basicAuth) {
const authValue = basicAuth.split(" ")[1];
const [providedUser, providedPassword] = atob(authValue).split(":");

const expectedUser = process.env.AUTHENTICATION_USERNAME || "admin";
const expectedPassword = process.env.AUTHENTICATION_PASSWORD || "admin";

if (providedUser === expectedUser && providedPassword === expectedPassword) {
return response;
}
}
url.pathname = "/api/auth";

return NextResponse.rewrite(url);
}
}
Loading
Loading