Order Request

Typescript examples use ethersjs v6.

Full example script is available request_order.ts.

For an example using a separate approve transaction rather than permit, see request_order_approve.ts.

Direct Orders

This example submits a BUY order request to the OrderProcessor contract. This process involves two method calls bundled together into one transaction using the OrderProcessor's multicall utility.

The first step is to determine the fees to add to the desired order amount.

// ------------------ Configure Order ------------------

// order amount (1000 USDC)
const orderAmount = BigInt(1000_000_000);
// buy order (Change to true for Sell Order)
const sellOrder = false;
// market order
const orderType = Number(0);

// check the order precision doesn't exceed max decimals
// applicable to sell and limit orders only
if (sellOrder || orderType === 1) {
  const maxDecimals = await orderProcessor.maxOrderDecimals(assetTokenAddress);
  const assetTokenDecimals = await assetToken.decimals();
  const allowablePrecision = 10 ** (assetTokenDecimals - maxDecimals);
  if (Number(orderAmount) % allowablePrecision != 0) {
    throw new Error(`Order amount precision exceeds max decimals of ${maxDecimals}`);
  }
}

// get fees, fees will be added to buy order deposit or taken from sell order proceeds
const fees = await orderProcessor.estimateTotalFeesForOrder(signer.address, false, paymentTokenAddress, orderAmount);
const totalSpendAmount = orderAmount + fees;
console.log(`fees: ${ethers.formatUnits(fees, 6)}`);

The first method call - selfPermit - gives the OrderProcessor permission to spend the payment token by pulling the order amount + fees from the user's account.

// ------------------ Configure Permit ------------------

// permit nonce for user
const nonce = await paymentToken.nonces(signer.address);
// 5 minute deadline from current blocktime
const blockNumber = await provider.getBlockNumber();
const blockTime = (await provider.getBlock(blockNumber))?.timestamp;
if (!blockTime) throw new Error("no block time");
const deadline = blockTime + 60 * 5;

// unique signature domain for payment token
const permitDomain = {
  name: await paymentToken.name(),
  version: await getContractVersion(paymentToken),
  chainId: (await provider.getNetwork()).chainId,
  verifyingContract: paymentTokenAddress,
};

// permit message to sign
const permitMessage = {
  owner: signer.address,
  spender: orderProcessorAddress,
  value: totalSpendAmount,
  nonce: nonce,
  deadline: deadline
};

// sign permit to spend payment token
const permitSignatureBytes = await signer.signTypedData(permitDomain, permitTypes, permitMessage);
const permitSignature = ethers.Signature.from(permitSignatureBytes);

// create selfPermit call data
const selfPermitData = orderProcessor.interface.encodeFunctionData("selfPermit", [
  paymentTokenAddress,
  permitMessage.owner,
  permitMessage.value,
  permitMessage.deadline,
  permitSignature.v,
  permitSignature.r,
  permitSignature.s
]);

The second method call - requestOrder - submits the order request to be filled by the protocol. This is submitted with the spending permit.

Once the transaction is mined, the event logs from resulting transaction receipt can be unpacked to obtain the order recipient's order index. The recipient's order index can be used to query the OrderProcessor for the current state of the order or look up the event history for that order.

// ------------------ Submit Order ------------------

// create requestOrder call data
// see IOrderProcessor.Order struct for order parameters
const requestOrderData = orderProcessor.interface.encodeFunctionData("requestOrder", [[
  signer.address,
  assetToken,
  paymentTokenAddress,
  sellOrder,
  orderType,
  0, // Asset amount to sell. Ignored for buys. Fees will be taken from proceeds for sells.
  orderAmount, // Payment amount to spend. Ignored for sells. Fees will be added to this amount for buys.
  0, // Unused limit price
  1, // GTC
  ethers.ZeroAddress, // split recipient
  0, // split amount
]]);

// submit permit + request order multicall transaction
const tx = await orderProcessor.multicall([
  selfPermitData,
  requestOrderData,
]);
const receipt = await tx.wait();
console.log(tx.hash);

// get order id from event
const events = receipt.logs.map((log: any) => orderProcessor.interface.parseLog(log));
if (!events) throw new Error("no events");
const orderEvent = events.find((event: any) => event && event.name === "OrderRequested");
if (!orderEvent) throw new Error("no order event");
const orderId = orderEvent.args[0];
const orderAccount = orderEvent.args[1];
console.log(`Order ID: ${orderId}`);
console.log(`Order Account: ${orderAccount}`);

// use order id to get order status (ACTIVE, FULFILLED, CANCELLED)
const orderStatus = await orderProcessor.getOrderStatus(orderId);
console.log(`Order Status: ${orderStatus}`);

Last updated