Skip to content

Commit

Permalink
Merge pull request #1067 from mrgnlabs/feat/arena-client
Browse files Browse the repository at this point in the history
feat(mfi-trading): introduction of arena client
  • Loading branch information
k0beLeenders authored Feb 17, 2025
2 parents f403f8a + b8e1792 commit 16f1860
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ import { Button } from "~/components/ui/button";
import { useExtendedPool } from "~/hooks/useExtendedPools";
import { ArenaPoolV2, GroupStatus } from "~/types/trade-store.types";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";

export const PoolTradeHeader = ({ activePool }: { activePool: ArenaPoolV2 }) => {
const router = useRouter();
const { connected, wallet } = useWallet();

const extendedPool = useExtendedPool(activePool);
const client = useMarginfiClient({ groupPk: activePool.groupPk });
const client = useArenaClient({ groupPk: activePool.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: activePool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ActionBox, ActionBoxProvider } from "~/components/action-box-v2";
import { Button } from "~/components/ui/button";
import { ArenaPoolV2Extended } from "~/types/trade-store.types";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";

type LpActionButtonsProps = {
activePool: ArenaPoolV2Extended;
Expand All @@ -26,7 +26,7 @@ export const LpActionButtons = ({ size = "sm", activePool }: LpActionButtonsProp
setDisplaySettings: state.setDisplaySettings,
}));
const [refreshGroup, nativeSolBalance] = useTradeStoreV2((state) => [state.refreshGroup, state.nativeSolBalance]);
const client = useMarginfiClient({ groupPk: activePool.groupPk });
const client = useArenaClient({ groupPk: activePool.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: activePool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PositionActionButtons } from "~/components/common/Portfolio";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/components/ui/tooltip";

import { ArenaPoolV2Extended, GroupStatus } from "~/types/trade-store.types";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { usePositionsData } from "~/hooks/usePositionsData";
import { PnlBadge, PnlLabel } from "~/components/common/pnl-display";
Expand All @@ -34,7 +34,7 @@ export const PositionCard = ({ size = "lg", arenaPool }: PositionCardProps) => {
const [showQuotePrice, setShowQuotePrice] = React.useState(false);

const positionData = usePositionsData({ groupPk: arenaPool.groupPk });
const client = useMarginfiClient({ groupPk: arenaPool.groupPk });
const client = useArenaClient({ groupPk: arenaPool.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: arenaPool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { dynamicNumeralFormatter } from "@mrgnlabs/mrgn-common";

import { ArenaPoolV2Extended, GroupStatus } from "~/types/trade-store.types";
import { useLeveragedPositionDetails } from "~/hooks/arenaHooks";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { usePositionsData } from "~/hooks/usePositionsData";

Expand All @@ -23,7 +23,7 @@ interface props {
export const PositionListItem = ({ arenaPool }: props) => {
const router = useRouter();
const positionData = usePositionsData({ groupPk: arenaPool.groupPk });
const client = useMarginfiClient({ groupPk: arenaPool.groupPk });
const client = useArenaClient({ groupPk: arenaPool.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: arenaPool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ActiveBankInfo } from "@mrgnlabs/marginfi-v2-ui-state";
import { Button } from "~/components/ui/button";
import { ArenaBank, ArenaPoolPositions, ArenaPoolV2Extended } from "~/types/trade-store.types";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useConnection } from "~/hooks/use-connection";
import { useTradeStoreV2, useUiStore } from "~/store";
import { useWallet } from "~/components/wallet-v2/hooks";
Expand All @@ -38,7 +38,7 @@ export const ClosePosition = ({ arenaPool, positionsByGroupPk, depositBanks, bor
const [multiStepToast, setMultiStepToast] = React.useState<MultiStepToastHandle | null>(null);
const [actionTxns, setActionTxns] = React.useState<ClosePositionActionTxns | null>(null);

const client = useMarginfiClient({ groupPk: arenaPool.groupPk });
const client = useArenaClient({ groupPk: arenaPool.groupPk });
const { wrappedAccount } = useWrappedAccount({
client,
groupPk: arenaPool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { ActionBox, ActionBoxProvider } from "@mrgnlabs/mrgn-ui";

import { ArenaBank, ArenaPoolV2, ArenaPoolV2Extended, GroupStatus } from "~/types/trade-store.types";
import { useExtendedPool } from "~/hooks/useExtendedPools";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { useWallet } from "~/components/wallet-v2/hooks";

Expand Down Expand Up @@ -145,7 +145,7 @@ const YieldItem = ({
}, [bank]);

const [nativeSolBalance, refreshGroup] = useTradeStoreV2((state) => [state.nativeSolBalance, state.refreshGroup]);
const client = useMarginfiClient({ groupPk: pool.groupPk });
const client = useArenaClient({ groupPk: pool.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: pool.groupPk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Card, CardContent, CardHeader } from "~/components/ui/card";
import { useTradeStoreV2, useUiStore } from "~/store";
import { useWallet, useWalletStore } from "~/components/wallet-v2";
import { useExtendedPool } from "~/hooks/useExtendedPools";
import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { useAmountDebounce } from "~/hooks/useAmountDebounce";

Expand Down Expand Up @@ -110,7 +110,7 @@ export const TradeBoxV2 = ({ activePool, side = "long" }: TradeBoxV2Props) => {

// Hooks
const activePoolExtended = useExtendedPool(activePool);
const client = useMarginfiClient({ groupPk: activePoolExtended.groupPk });
const client = useArenaClient({ groupPk: activePoolExtended.groupPk });
const { accountSummary, wrappedAccount } = useWrappedAccount({
client,
groupPk: activePoolExtended.groupPk,
Expand Down
4 changes: 2 additions & 2 deletions apps/marginfi-v2-trading/src/hooks/useActionBoxProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { PublicKey } from "@solana/web3.js";

import { DEFAULT_ACCOUNT_SUMMARY } from "@mrgnlabs/marginfi-v2-ui-state";

import { useMarginfiClient } from "~/hooks/useMarginfiClient";
import { useArenaClient } from "~/hooks/useArenaClient";
import { useWrappedAccount } from "~/hooks/useWrappedAccount";
import { ArenaBank } from "~/types/trade-store.types";

export function useActionBoxProps(groupPk: PublicKey, banks: ArenaBank[]) {
const marginfiClient = useMarginfiClient({ groupPk });
const marginfiClient = useArenaClient({ groupPk });
const { wrappedAccount, accountSummary } = useWrappedAccount({ client: marginfiClient, groupPk, banks });

return { marginfiClient, wrappedAccount, accountSummary: accountSummary ?? DEFAULT_ACCOUNT_SUMMARY };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
feedIdToString,
getConfig,
MARGINFI_IDL,
MarginfiClient,
ArenaClient,
MarginfiConfig,
MarginfiIdlType,
MarginfiProgram,
Expand All @@ -28,7 +28,7 @@ type UseMarginfiClientProps = {

const defaultConfig = getConfig();

export function useMarginfiClient({
export function useArenaClient({
groupPk,
clientOptions,
clientConfig = {
Expand Down Expand Up @@ -105,7 +105,7 @@ export function useMarginfiClient({
};
});

const client = new MarginfiClient(
const client = new ArenaClient(
{ groupPk, ...clientConfig },
program,
wallet,
Expand All @@ -120,8 +120,7 @@ export function useMarginfiClient({
bankMetadataByBankPk,
clientOptions?.bundleSimRpcEndpoint,
clientOptions?.processTransactionStrategy,
lookupTables,
true // Add arena tag to transactions
lookupTables
);

return client;
Expand Down
45 changes: 45 additions & 0 deletions apps/marginfi-v2-trading/src/pages/api/processTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import cookie from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
import { fetchAuthToken } from "~/utils";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.MARGINFI_API_URL) {
return res.status(500).json({ error: "API URL is not set" });
}

const cookies = cookie.parse(req.headers.cookie || "");
let token = cookies.jwt;

// If the token is missing, fetch a new one
if (!token) {
try {
token = await fetchAuthToken(req);
} catch (error) {
console.error("Error fetching new JWT:", error);
return res.status(401).json({ error: "Unauthorized: Unable to fetch token" });
}
}

try {
const response = await fetch(`${process.env.MARGINFI_API_URL}/arena/process-transactions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({}),
});

if (!response.ok) {
throw new Error(`API responded with status: ${response.status}`);
}

return res.status(200).json({ message: "Transaction processed successfully" });
} catch (error) {
console.error("Error processing transaction:", error);
return res.status(500).json({
error: "Internal server error",
message: process.env.NODE_ENV === "development" ? error : undefined,
});
}
}
87 changes: 87 additions & 0 deletions packages/marginfi-client-v2/src/clients/arena-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { AddressLookupTableAccount, PublicKey, TransactionSignature } from "@solana/web3.js";

import { BankMetadataMap, SolanaTransaction, TransactionOptions, Wallet } from "@mrgnlabs/mrgn-common";

import { MarginfiGroup } from "../models/group";
import { MarginfiConfig, MarginfiProgram } from "../types";
import { PythPushFeedIdMap } from "../utils";
import { ProcessTransactionStrategy, ProcessTransactionsClientOpts } from "../services";
import { MarginfiClient, BankMap, OraclePriceMap, MintDataMap } from "..";

class ArenaClient extends MarginfiClient {
constructor(
config: MarginfiConfig,
program: MarginfiProgram,
wallet: Wallet,
isReadOnly: boolean,
group: MarginfiGroup,
banks: BankMap,
priceInfos: OraclePriceMap,
mintDatas: MintDataMap,
feedIdMap: PythPushFeedIdMap,
addressLookupTables?: AddressLookupTableAccount[],
preloadedBankAddresses?: PublicKey[],
bankMetadataMap?: BankMetadataMap,
bundleSimRpcEndpoint?: string,
processTransactionStrategy?: ProcessTransactionStrategy,
lookupTablesAddresses?: PublicKey[]
) {
super(
config,
program,
wallet,
isReadOnly,
group,
banks,
priceInfos,
mintDatas,
feedIdMap,
addressLookupTables,
preloadedBankAddresses,
bankMetadataMap,
bundleSimRpcEndpoint,
processTransactionStrategy,
lookupTablesAddresses
);
}

async processTransaction(
transaction: SolanaTransaction,
processOpts?: ProcessTransactionsClientOpts,
txOpts?: TransactionOptions
): Promise<TransactionSignature> {
try {
await fetch("/api/processTransaction", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error("Error processing transaction to the backend:", error);
}

return super.processTransaction(transaction, { ...processOpts, addArenaTxTag: true }, txOpts);
}

async processTransactions(
transactions: SolanaTransaction[],
processOpts?: ProcessTransactionsClientOpts,
txOpts?: TransactionOptions
): Promise<TransactionSignature[]> {
try {
await fetch("/api/processTransaction", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error("Error processing transaction to the backend:", error);
}

return super.processTransactions(transactions, { ...processOpts, addArenaTxTag: true }, txOpts);
}
}

export default ArenaClient;
19 changes: 3 additions & 16 deletions packages/marginfi-client-v2/src/clients/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
SendTransactionError,
Signer,
Transaction,
TransactionInstruction,
TransactionMessage,
Expand All @@ -22,7 +20,6 @@ import instructions from "../instructions";
import { MarginRequirementType } from "../models/account";
import {
addTransactionMetadata,
BankMetadata,
BankMetadataMap,
chunkedGetRawMultipleAccountInfoOrdered,
DEFAULT_COMMITMENT,
Expand Down Expand Up @@ -56,13 +53,8 @@ import {
OracleSetup,
} from "..";
import { MarginfiAccountWrapper } from "../models/account/wrapper";
import {
ProcessTransactionError,
ProcessTransactionErrorType,
parseErrorFromLogs,
parseTransactionError,
} from "../errors";
import { findOracleKey, makePriorityFeeIx, PythPushFeedIdMap, buildFeedIdMap } from "../utils";
import { ProcessTransactionError, ProcessTransactionErrorType, parseTransactionError } from "../errors";
import { findOracleKey, PythPushFeedIdMap, buildFeedIdMap } from "../utils";
import {
ProcessTransactionOpts,
ProcessTransactionStrategy,
Expand Down Expand Up @@ -120,7 +112,6 @@ class MarginfiClient {
public processTransactionStrategy?: ProcessTransactionStrategy;
private preloadedBankAddresses?: PublicKey[];
private bundleSimRpcEndpoint: string;
private addArenaTxTag: boolean;

// --------------------------------------------------------------------------
// Factories
Expand All @@ -141,8 +132,7 @@ class MarginfiClient {
readonly bankMetadataMap?: BankMetadataMap,
bundleSimRpcEndpoint?: string,
processTransactionStrategy?: ProcessTransactionStrategy,
lookupTablesAddresses?: PublicKey[],
addArenaTxTag?: boolean
lookupTablesAddresses?: PublicKey[]
) {
this.group = group;
this.banks = banks;
Expand All @@ -154,7 +144,6 @@ class MarginfiClient {
this.bundleSimRpcEndpoint = bundleSimRpcEndpoint ?? program.provider.connection.rpcEndpoint;
this.processTransactionStrategy = processTransactionStrategy;
this.lookupTablesAddresses = lookupTablesAddresses ?? [];
this.addArenaTxTag = addArenaTxTag ?? false;
}

/**
Expand Down Expand Up @@ -945,7 +934,6 @@ class MarginfiClient {
programId: this.program.programId,
bundleSimRpcEndpoint: this.bundleSimRpcEndpoint,
dynamicStrategy: processOpts?.dynamicStrategy ?? this.processTransactionStrategy,
addArenaTxTag: this.addArenaTxTag,
};

console.log("processOpts", processOpts);
Expand Down Expand Up @@ -984,7 +972,6 @@ class MarginfiClient {
programId: this.program.programId,
bundleSimRpcEndpoint: this.bundleSimRpcEndpoint,
dynamicStrategy: processOpts?.dynamicStrategy ?? this.processTransactionStrategy,
addArenaTxTag: this.addArenaTxTag,
};

const [signature] = await processTransactions({
Expand Down
Loading

0 comments on commit 16f1860

Please sign in to comment.