Callbacks

Sensitive functions, such as Trade, OpenPosition, and ClosePosition, are encapsulated within callback-style custom hooks. These hooks implement the core logic for each operation.

Before explaining the callbacks, it's worth understanding some of the common hooks used throughout these callback functions.

  • useActiveWagmi() is a hook obtains the current user's account and chainID.

  • preConstructCall() is a useCallback hook that prepares the transaction call, including all the necessary arguments by assessing whether any of the dependencies are missing.

  • useMultiAccountable() is a hook is designed to interact with with the smart contracts using the MultiAccount smart contract, which lets users create and trade via subaccounts.

  • useTransactionAdder() is a hook from a state management module, used to update the UI and state with details of recent transactions.

Below is a brief overview of the main callbacks used in the application.

Sending Quotes

useSendQuote.ts
typescript
export function useSentQuoteCallback(): {
  state: TransactionCallbackState;
  callback: null | (() => Promise<SendTransactionResult | undefined>);
  error: string | null;
}

This callback is used to send a quote, fetches all the appropriate values from the state using custom appSelector hooks, and returns state, callback and error . The Muon signature is obtained from the muon client and is necessary for the sendQuote function to assess partyA's uPnL, there are also checks in place to ensure the Open Interest cap is not exceeded before the quote is sent.

Canceling Quotes

useCancelQuote.ts
export function useCancelQuote(
  quote: Quote | null,
  closeQuote: CloseQuote | null
): {
  state: TransactionCallbackState;
  callback: null | (() => Promise<SendTransactionResult | undefined>);
  error: string | null;
} {

This takes two inputs, a Quote struct and a closeQuote enum, used to determine the type of close action to execute on the contract side. It returns the state, callback action and an error if there is one.

Using Sub-Accounts

useMultiAccount.ts
export function useAddAccountToContract(accountName: string): {
  state: TransactionCallbackState;
  callback: null | (() => Promise<SendTransactionResult | undefined>);
  error: string | null;
} {
  const { account, chainId } = useActiveWagmi();
  const addTransaction = useTransactionAdder();
  const isSupportedChainId = useSupportedChainId();
  const Contract = useMultiAccountContract();
  const addRecentTransaction = useAddRecentTransaction();

  const functionName = "addAccount";

  const constructCall = useCallback(async (): ConstructCallReturnType => {

The callback useAddAccountToContract() allows a user to create a new subaccount to trade on. All positions across an account are in CROSS, creating a new account for each trade allows for effectively ISOLATED positions. To create an account the user must provide an accountName

Closing Positions

useClosePosition.ts
export function useClosePosition(
  quote: Quote | null,
  orderType: OrderType,
  typedPrice: string,
  quantityToClose: string
): {
  state: TransactionCallbackState;
  callback: null | (() => Promise<SendTransactionResult | undefined>);
  error: string | null;
}

useClosePosition lets the partyA close a position. It requires the Quote struct, OrderType enum, the typedPrice and quantityToClose. The typedPrice is first formatted to a BigNumber and rounded down:

  const typedPriceBN = useMemo(
    () =>
      toWeiBN(toBN(typedPrice).toFixed(pricePrecision, RoundMode.ROUND_DOWN)),
    [typedPrice, pricePrecision]
  );

The slippage is also calculated depending on whether a valid object is returned from useMarket(quote?.marketId)

  const autoSlippage = market ? market.autoSlippage : MARKET_PRICE_COEFFICIENT;

The closePriceFinal is then calculated using this logic and used in txInfo to be executed on the Diamond Contract:

  const closePriceFinal = useMemo(() => {
    if (orderType === OrderType.LIMIT) return closePriceBN;

    if (slippage === "auto") {
      return positionType === PositionType.SHORT
        ? closePriceBN.times(autoSlippage)
        : closePriceBN.div(autoSlippage);
    }

    const spSigned =
      positionType === PositionType.SHORT ? slippage * -1 : slippage;
    const slippageFactored = toBN(100 - spSigned).div(100);
    return toBN(closePriceBN).times(slippageFactored);
  }, [orderType, closePriceBN, slippage, positionType, autoSlippage]);

For a SHORT position, the slippage value is negated. For a LONG position, slippage is applied directly. Slippage can be either a number the partyA has input, or auto

export function useSlippageTolerance(): number | "auto" {
  const userSlippageTolerance = useAppSelector(
    (state) => state.user.userSlippageTolerance
  );
  return userSlippageTolerance;
}

When slippage is automatic, the adjustments are as follows:

  • For LONG, the price is reduced by dividing closePriceBN by autoSlippage.

  • For SHORT, the price increases by multiplying closePriceBN by autoSlippage.

When the slippage isn't automatic, for example the user has input a 10% tolerance, it is adjusted accordingly:

  • If slippage is specified (e.g., 10%):

    • For a short position (PositionType.SHORT), you want the price to be less by the slippage amount to enter the position more favorably. So, spSigned becomes -10 (slippage * -1), making slippageFactored = (100 - (-10)) / 100 = 1.1. This means you intend to allow a price up to 10% less than the current price to sell high.

    • For a long position (PositionType.LONG), spSigned is 10 (slippage is positive), so slippageFactored = (100 - 10) / 100 = 0.9. You are willing to pay up to 10% more than the current price to buy low.

Delegating Access

This callback hook allows a user to delegate access to a specific subaccount, enabling another account to perform actions on their behalf. This is particularly useful for instant trading. It's implemented like this:

  1. Dependencies Check: The hook verifies if all necessary dependencies, such as account, chainId, and MULTI_ACCOUNT_ADDRESS, are present.

  2. Function Arguments: Constructs the arguments required for the delegateAccess function, which includes the addresses of the delegating and delegated accounts, and other required parameters.

  3. Transaction Callback: Returns a callback that initiates the transaction, ensuring it is correctly added to the application's transaction tracking system.

Minting Test Collateral

This callback hook allows a user to mint test collateral within the system for testing purposes.

Transferring Collateral

Collateral Management Actions

useTransferCollateral.ts
function useTransferCollateral(
  typedAmount: string,
  activeTab: TransferTab
): {
  state: TransactionCallbackState;
  callback: null | (() => Promise<SendTransactionResult | undefined>);
  error: string | null;
}

The actions taken by partyA in relation to funding an account are the following:

  • ALLOCATE: This action involves designating deposited collateral to a specific subaccount. It is useful for separating funds if the partyA wishes to trade in an isolated way.

  • DEPOSIT: This action allows users to deposit funds into their account. Additionally, it automatically allocates the deposited collateral to a specified subaccount in a single transaction, streamlining the process.

  • WITHDRAW: Through this action, users can remove collateral from their account. There is a cooldown period associated with this action.

  • DEALLOCATE: This action is the reverse of ALLOCATE. It involves removing collateral from a subaccount's total, potentially to be withdrawn or reallocated.

The methodName is determined by the useMemo() hook:

const methodName = useMemo(() => {
    return activeTab === TransferTab.DEPOSIT
      ? "depositAndAllocateForAccount"
      : activeTab === TransferTab.DEALLOCATE
      ? "deallocate"
      : activeTab === TransferTab.WITHDRAW
      ? "withdrawFromAccount"
      : activeTab === TransferTab.ALLOCATE
      ? "allocate"
      : "";
  }, [activeTab]);

Each case is then appropriately handled and passed to the diamond contract. In the case of deallocate the Muon signature must be obtained to check the user's uPnL, assessing if they have a sufficient balance to support the positions they have open.

Writing Signatures

This callback facilitates the process of signing a message with a user's wallet and storing this signature in a smart contract. Here's how it works:

export function useWriteSign(): {
  state: TransactionCallbackState;
  callback:
    | null
    | ((sign: string) => Promise<SendTransactionResult | undefined>);
  error: string | null;
} {

It specifies storeSignatureForCurrentVersion as the function name to be called on the smart contract.

  const functionName = "storeSignatureForCurrentVersion";
  • A transaction call is generated with the provided signature

  • createTransactionCallback is used to handle the transaction process, which includes sending the transaction, adding it to the transaction list for tracking.

  • addTransaction and addRecentTransaction hooks are returned for adding the transaction to the application's state and for UI feedback, respectively.

Last updated