Universal Protocol
  • Introduction to Universal
    • Introduction to Universal
    • Universal Protocol Overview
      • Key Roles
        • Custodian
        • Merchant
        • User
      • Issuance
      • Redemption
      • Universal Token Liquidity & Acquisition
    • Introduction to uAssets
    • Frequently Asked Questions
    • Proof of Reserves
  • DEVELOPERS
    • Building with uAssets
    • Integrate the Universal API
    • Contract Addresses
    • Partnerships
    • uAsset Logos
  • Community
    • Twitter/X
    • Discord
  • Links
    • Buy Universal Assets
    • Whitepaper
    • Disclaimer
    • Terms of Service
    • Universal Security Audit
Powered by GitBook
On this page
  • Typescript SDK Reference
  • API Reference
  1. DEVELOPERS

Integrate the Universal API

Integrate just-in-time mint/redeems of 80+ uAssets directly in your app or protocol.

PreviousBuilding with uAssetsNextContract Addresses

Last updated 1 month ago

The Universal API allows developers to seamlessly integrate uAssets into their applications. The API acts as an intermediary, connecting users with Merchants to facilitate buying, selling, and cross-chain transactions.

What is the Universal API?

DeFi typically relies on liquidity pools (LPs) to provide assets for swaps. However, LPs are capital inefficient because liquidity sits unused until needed.

Universal solves this with JIT (just-in-time) Liquidity via the Universal API meaning:

  • Instead of pre-depositing liquidity, Merchants mint and fulfill orders dynamically when demand arises.

  • Liquidity is provided on an as-needed basis, optimizing capital efficiency.

To obtain API access with higher rate limits, please reach out to the Universal team for an API key.

The user will need to approve the Permit2 contract address 0x000000000022D473030F116dDEE9F6B43aC78BA3 to spend USDC or token address to be used for purchase, and the uAsset address for selling.

Typescript SDK Reference

The compatible Typescript SDK is the simplest way to integrate with the Universal API.

Installation

Install the Universal SDK via npm:

npm install universal-sdk
Environment

For signing orders, you’ll need your private key. We strongly recommend storing private keys securely using environment variables. For example, create a .env file in your project root:

PRIVATE_KEY=your_private_key_here

Load the .env file in your code by installing the dotenv package:

npm install dotenv

Then, in your project:

import dotenv from "dotenv";
dotenv.config();
Quick Start Example

Below is a complete TypeScript example that shows you how to use the SDK to request a quote, generate EIP‑712 typed data, sign the data, and submit an order:

import {
  UniversalRelayerSDK,
  generateTypedData,
  QuoteRequest,
} from "universal-sdk";
import { privateKeyToAccount } from "viem/accounts";
import { parseUnits } from "viem";
import dotenv from "dotenv";

dotenv.config();

async function executeOrder() {
  // Initialize the SDK (pass your API key if required)
  const universal = new UniversalRelayerSDK();

  // Create an account from your private key
  const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

  // Configure your quote parameters
  const quoteRequest: QuoteRequest = {
    type: "BUY",
    token: "BTC",
    pair_token: "USDC",
    blockchain: "BASE",
    slippage_bips: 20,
    user_address: account.address,
    pair_token_amount: parseUnits("5", 6).toString(), // 5 USDC, using 6 decimals
  };

  // Request a quote from the API
  const quote = await universal.getQuote(quoteRequest);
  console.log("Quote response:", quote);

  // Generate typed data as per EIP‑712 for signing
  const { typedData } = await generateTypedData(quote);

  // Sign the typed data using your account
  const signature = await account.signTypedData(typedData);

  // Combine the quote with its signature to form an order request
  const orderRequest = {
    ...quote,
    signature,
  };

  // Submit the order
  const orderResponse = await universal.submitOrder(orderRequest);
  console.log("Order response:", orderResponse);
}

executeOrder()
  .then(() => console.log("Order executed successfully"))
  .catch((error) => console.error("Error executing order:", error));

API Reference

Basics

API Basics

Base URL

This is the base URL for all API requests.

https://relayer.universal.xyz/api (with an API key)
https://www.universal.xyz/api/v1 (without an API key)

Authentication

All API requests require authentication via a Bearer Token.

Authorization: Bearer <YOUR_API_KEY>

Ensure that your API key is included in every request to authenticate and authorize API usage.


Types
  • TokenName: BTC, SOL, XRP, DOGE, DOT, NEAR, LTC, ADA, BCH, ALGO, ...

  • PairTokenName: USDC

  • BlockchainName: BASE | ARBITRUM | POLYGON | WORLD

  • Quote

      type: "BUY" | "SELL";
      token: TokenName;
      token_amount: string; // wei
      pair_token: PairTokenName | string; // USDC or token address 
      slippage_bips: number; // 0-100
      blockchain: BlockchainName;
      deadline: string; // unix epoch
      pair_token_amount: string; // wei
      id: string;
      user_address: string; // 0x..
      merchant_address: string; // 0x..
      gas_fee_nominal: string; // wei
      gas_fee_dollars: number; // e.g. $1.23 = 1.23
      relayer_nonce: number;
      merchant_id: string; 
      mode: "DIRECT" | "BRIDGED"
Requesting a Quote

Send a Quote Request

To retrieve a price quote for a uAsset transaction, make a POST request to the following endpoint:

POST /quote

Request Body Parameters

  • type: Specifies if the user wants to BUY or SELL.

  • token: The uAsset being bought or sold.

  • token_amount: (Optional) The amount of the asset being transacted.

  • pair_token: The stable asset used for the trade (e.g., USDC).

  • pair_token_amount: (Optional) The amount of the paired asset.

  • slippage_bips: Slippage tolerance in basis points (0-100).

  • blockchain: The blockchain network where the transaction is executed.

  • user_address: The wallet address initiating the trade.

{
  type: "BUY" | "SELL";
  token: TokenName;
  token_amount?: string;
  pair_token_amount?: string;
  pair_token: PairTokenName;
  slippage_bips: number;
  blockchain: BlockchainName;
  user_address: string;
}

Response (200 OK)

Upon success, the API returns a quote containing:

  • Quoted amounts

  • Gas fees

  • Merchant details

  • A unique quote ID for later use

{
  ...Quote
}

Signing a Quote

User Signs the Quote

The user must sign the quote using EIP-712 signature standards before submitting the order.

Example Code (Signing a Quote with EIP-712)

import { DutchOrderBuilder } from "@uniswap/uniswapx-sdk";
import { BigNumber } from "ethers5"; // ethers v5

type TokenName = "BTC" | "SOL" | "XRP" | "DOGE" | "DOT" | "NEAR" | "LTC" | "ADA" | "BCH" | "ALGO";
type PairTokenName = "USDC";
type BlockchainName = "BASE";

interface Quote {
  type: "BUY" | "SELL";
  token: TokenName;
  token_amount: bigint;
  pair_token: PairTokenName;
  slippage_bips: number;
  blockchain: BlockchainName;
  deadline: bigint;
  pair_token_amount: bigint;
  id: string;
  user_address: string;
  merchant_address: string;
  gas_fee_nominal: bigint;
  gas_fee_dollars: number;
  relayer_nonce: number;
}

const CHAIN_ID: number = <CHAIN_ID>;
const REACTOR_ADDRESS: string = <REACTOR_ADDRESS>;
const PERMIT2_ADDRESS: string = <PERMIT2_ADDRESS>;

async function signTypedQuote(quote: Quote) {
  const builder = new DutchOrderBuilder(
    CHAIN_ID,
    REACTOR_ADDRESS,
    PERMIT2_ADDRESS
  );
  const TOKEN_ADDRESS = <TOKEN_ADDRESS>;
  const PAIR_TOKEN_ADDRESS = <PAIR_TOKEN_ADDRESS>;

  const order = builder
    .deadline(Number(quote.deadline))
    .decayEndTime(Number(quote.deadline))
    .decayStartTime(Number(quote.deadline) - 100)
    .nonce(BigNumber.from(quote.relayer_nonce))
    .input({
      token: quote.type == "BUY" ? PAIR_TOKEN_ADDRESS : TOKEN_ADDRESS,
      startAmount:
        quote.type == "BUY"
          ? BigNumber.from(quote.pair_token_amount.toString())
          : BigNumber.from(quote.token_amount.toString()),
      endAmount:
        quote.type == "BUY"
          ? BigNumber.from(quote.pair_token_amount.toString())
          : BigNumber.from(quote.token_amount.toString()),
    })
    .output({
      token: quote.type == "BUY" ? TOKEN_ADDRESS : PAIR_TOKEN_ADDRESS,
      startAmount:
        quote.type == "BUY"
          ? BigNumber.from(quote.token_amount.toString())
          : BigNumber.from(quote.pair_token_amount.toString()),
      endAmount:
        quote.type == "BUY"
          ? BigNumber.from(quote.token_amount.toString())
          : BigNumber.from(quote.pair_token_amount.toString()),
      recipient: quote.user_address,
    })
    .swapper(quote.user_address)
    .exclusiveFiller(quote.merchant_address, BigNumber.from(0))
    .build();

  const { domain, types, values } = order.permitData();
  const primaryType: "PermitWitnessTransferFrom" = "PermitWitnessTransferFrom";
  const typedData = {
    domain,
    types,
    primaryType,
    message: values,
  };
  const signer = <SIGNER>;
  const signature = await signer.signTypedData(typedData);
  return signature;
}
Submitting an Order

Submit a Signed Order

Once the quote is signed, submit it using the /order endpoint:

POST /order

Request Body Parameters

This request includes the signed quote along with the necessary parameters from the previous steps.

{
 ...Quote, 
 "signature": string, // Quote signed by the user
}

Response (200 OK)

Upon success, the API will return the transaction hash confirming the execution of the trade.Response

{
  "transaction_hash": string // Transaction hash of the fulfillment
  (optional) "transaction_hash_2": string // Transaction hash of the bridged fulfillment
}

Partnerships
viem