Obtaining Muon Signatures

For sensitive functions like opening and closing a trade, Muon signatures are required to be passed and verified on the contracts to verify the user's upnl and the price of an asset. This is done through a series of processes. For a trading bot, we'll structure Muon queries into three main parts: HTTP utility functions, a base client for handling requests to the Muon network, and a specialized client for fetching quotes and signatures.

Making a HTTP Request

The HTTP utility module provides a basic functionality to make HTTP GET requests using axios, a promise-based HTTP client.

const axios = require('axios');

const makeHttpRequest = async function(url, options = {}) {
  try {
    const response = await axios.get(url, {
      headers: {
        'Cache-Control': 'no-cache',
      }
    });
    return response.data; 
  } catch (err) {
    console.error(`Error fetching ${url}: `, err.message); 
    return null; 
  }
};

module.exports = { makeHttpRequest };

This code snippet sends an HTTP GET request to a specified URL and returns the response data. We'll export this module and use it in the base client.

Base Client

Overview

MuonClient serves as the base class for making requests to the Muon network. It constructs URLs with appropriate parameters for the network requests. Here's how you can set up a base client, which will be used by extended classes to carry out specific queries to Muon.

Parameters:

  • options (Object): Configuration options for the Muon client. Includes APP_METHOD to specify the method for the Muon application.

const { makeHttpRequest } = require("../utils/http");

class MuonClient {
  constructor(options) {
    this.APP_METHOD = options.APP_METHOD;
  }
  //Code
}

Here's a full list of the App Methods you can use to query Muon:

  • 'uPnl_A': Returns the uPnl of partyA.

  • **'partyA_overview':**Returns an the prices of partyA's assets upon liquidation.

  • 'verify': Returns liquidation signatures.

  • **'uPnl_A_withSymbolPrice':**Returns partyA's uPnl with the price of a symbol.

  • **'uPnl_B':**Returns partyB's uPnl.

  • **'uPnl':**Returns the uPnl of both parties at a timestamp.

  • 'uPnlWithSymbolPrice': Returns both parties' uPnl with a symbol price.

  • 'price': Returns an array of prices from an array of quotes.

Sending a Request

The _sendRequest() function constructs a request URL with the given parameters and sends an HTTP GET request to the Muon network.

Parameters:

  • baseUrl (String): The base URL for the Muon network endpoint.

  • appName (String): The name of the application making the request ("symmio")

  • requestParams (Array): A list of parameters required for the request, structured as key-value pairs.

  async _sendRequest(baseUrl, appName, requestParams) {
    const MuonURL = new URL(baseUrl);
    MuonURL.searchParams.set("app", appName);
    MuonURL.searchParams.append("method", this.APP_METHOD);
    requestParams.forEach((param) => {
      MuonURL.searchParams.append(`params[${param[0]}]`, param[1]);
    });
    console.log('Request URL:', MuonURL.href);

    try {
      const response = await makeHttpRequest(MuonURL.href);
      return { result: response, success: true }; 
    } catch (error) {
      console.error(`Error during request to ${baseUrl}:`, error);
      return { success: false, error }; 
    }
  }
}

Returns: An object indicating the success of the request and containing the result or error information.

Finally we'll export this class:

module.exports = { MuonClient };

Creating the Quotes Client

The QuotesClient class extends MuonClient to interact with the Muon network for the purpose of fetching the price of a symbol with the upnl of partyA. In order to send a quote, the signature received must be verified on SYMM's contracts.

Constructor

Initializes the QuotesClient with a predefined application method.

class QuotesClient extends MuonClient {
  constructor() {
    super({ APP_METHOD: "uPnl_A_withSymbolPrice" });
  }
  • APP_METHOD: Set to "uPnl_A_withSymbolPrice", specifying the Muon network method to be used for fetching quotes and signatures related to unrealized profit and loss (uPnl) and symbol price.

Creating an Instance

  static createInstance(isEnabled) {
    if (isEnabled) {
      return new QuotesClient();
    }
    return null;
  }

A static factory method to conditionally create an instance of QuotesClient.

Parameters:

  • isEnabled (Boolean): A flag to determine whether an instance should be created.

Returns:

  • An instance of QuotesClient if isEnabled is true.

  • null if isEnabled is false.

Obtaining the Muon Signature

The getMuonSig() function fetches a cryptographic signature from the Muon network based on provided parameters. It formats the generated signature so it can be passed to the contract without causing errors.

  async getMuonSig(
    account, 
    appName, 
    urls, 
    chainId, 
    contractAddress, 
    marketId) {
    try {
      //creating the request
      const requestParams = this._getRequestParams(account, chainId, contractAddress, marketId);
      if (requestParams instanceof Error) throw requestParams;
      let result, success;
      for (const url of urls) {
        try {
          const res = await this._sendRequest(url, appName, requestParams);
          if (res && res.success) {
            result = res.result;
            success = true;
            break; 
          }
        } catch (error) {
          console.log("Retrying with the next URL...");
        }
      }

      if (success) {
        const reqId = result.result.reqId;
        const timestamp = result.result.data.timestamp ? BigInt(result.result.data.timestamp) : BigInt(0);
        console.log("timestamp: ", timestamp);
        const upnl = result.result.data.result.uPnl ? BigInt(result.result.data.result.uPnl) : BigInt(0);
        const price = result.result.data.result.price ? BigInt(result.result.data.result.price) : BigInt(0);
        const gatewaySignature = result.result.nodeSignature;
        const signature = result.result.signatures[0].signature ? BigInt(result.result.signatures[0].signature) : BigInt(0);
        const owner = result.result.signatures[0].owner;
        const nonce = result.result.data.init.nonceAddress;

        //formatting the generatedSignature
        const generatedSignature = {
          reqId,
          timestamp,
          upnl,
          price: price ? price : toWei(0),
          gatewaySignature,
          sigs: { signature, owner, nonce },
        };
        //returning the signature
        return { success: true, signature: generatedSignature };
        
      } else {
        throw new Error("Muon request unsuccessful");
      }
    } catch (error) {
      console.error(error);
      return { success: false, error };
    }
  }

Parameters:

  • account (String): The subAccount address.

  • appName (String): The name of the Muon app ("symmio")

  • urls (Array): An array of URLs for the Muon network endpoints to try in sequence.

  • chainId (Number): The blockchain chain ID.

  • contractAddress (String): The address of the Diamond contract.

  • marketId (Number): A unique identifier for the market.

Returns: A promise that resolves to an object with two properties:

  • success (Boolean): Indicates whether the request was successful.

  • signature (Object): The signature data, available only if success is true.

  • error (String): An error message, available only if success is false.

Getting Request Params

This is a helper function to make sure all fields for the Muon request are filled. It constructs the request parameters required by the Muon network.

_getRequestParams(account, chainId, contractAddress, marketId) {
    if (!account) return new Error("Param `account` is missing.");
    if (!chainId) return new Error("Param `chainId` is missing.");
    if (!contractAddress) return new Error("Param `contractAddress` is missing.");
    if (!marketId) return new Error("Param `marketId` is missing.");

    return [
      ["partyA", account],
      ["chainId", chainId.toString()],
      ["symmio", contractAddress],
      ["symbolId", marketId.toString()],
    ];
  }

Parameters:

  • The same as for getMuonSig.

Returns:

  • An array of key-value pairs representing the request parameters.

  • An Error object if any parameter is missing.

Finally we'll export the QuotesClient so it can be used in our main code:

module.exports = QuotesClient;

Implementing for Sending a Quote

Here's how you can implement the getMuonSig() function in the main bot (using pre-defined values).

async function getMuonSigImplementation(botAddress) {
  const quotesClient = QuotesClient.createInstance(true);

  if (!quotesClient) {
      console.log('QuotesClient is not enabled or failed to instantiate.');
      return { success: false, error: 'QuotesClient initialization failed' };
  }

  const account = botAddress;
  const appName = 'symmio';
  const urls = [process.env.MUON_URL];
  const chainId = 137;
  const contractAddress = config.DIAMOND_ADDRESS;
  const marketId = 4;

  try {
    const result = await quotesClient.getMuonSig(account, appName, urls, chainId, contractAddress, marketId);

    if (result.success) {
      console.log('Successfully retrieved Muon signature:', result.signature);
      return { success: true, signature: result.signature };
    } else {
      throw new Error(result.error || 'Unknown error');
    }
  } catch (error) {
    console.error('Error getting Muon signature:', error);
    return { success: false, error: error.toString() };
  }
}

Last updated

Logo

All rights to the people (c) 2023 Symmetry Labs A.G.