Getting Started

The page describes the steps for protocols to integrate with Ferum.

This page hasn't been updated yet and might be out of date.

Ferum is currently deployed on testnet to 0x09c94d95f00a30b11aa18052c0276ff890d6ae754d5214cc85c714fa5b7c4133

High-Level Steps

  1. First, you need to get the latest Ferum module address from the Ferum Contract. This will be used to call entry functions and reference all test coins.

  2. Create an account, fund it with Aptos coins through the faucet, and mint some test USDF coins for testing purposes — see Creating Your First Order to get started.

  3. Decide if you want to integrate with Ferum through a Smart Contract (i.e. Move Module) or through the Aptos SDK (i.e. TypeScript).

    1. Public Functions are useful if integrating through Move Modules

    2. Public Entry Functions are useful if utilizing Aptos SDK or CLIs.

  4. Optionally, to create a new market, talk to the Ferum team. They will need to manually call init_market_entry to allow trading brand-new coin pairs. Ferum will allow permissionless market creation soon.

  5. Call add_order_entry to place your orders.

  6. In case you want to add additional, custom metadata to your order, pass in clientOrderID.

  7. To get the status of a particular order or market state, use the Ferum Indexer.

  8. Trading fees will follow the schedule outlined in Fees.

Integrating Through Smart Contract

To integrate Ferum into your Aptos contract, you will need to include Ferum as a move dependency to your Move.toml file like below:

[dependencies]
AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-framework/", rev = "testnet" }
Ferum = { git = "https://github.com/ferum-dex/ferum.git", rev = "testnet", subdir = "contract" }

Move caches modules so you might run into errors if the upstream module is updated but the cache version is stale. To fix this run aptos move clean.

Integrating Through Aptos SDK

Ferum will release an SDK in many popular languages as a part of Milestone Beta. In the meantime, the Ferum contract can be called through Aptos SDKs. An example Ferum contract call using the TypeScript SDK is shown below (sdk version = 1.3.13):

import {
  AptosAccount,
  AptosClient,
  TxnBuilderTypes,
} from "aptos";

// Update to the latest by checking Ferum's Move.toml file:
// https://github.com/ferum-dex/ferum/blob/testnet/contract/Move.toml
const FERUM = "0x7e9d4ebb1ac454c759719fc87b7f13b116d2f226c76d838970bf80e6aaea9000";
const NODE_URL = "https://fullnode.devnet.aptoslabs.com/v1";
const Client = new AptosClient(NODE_URL);

// Shown for illustration purposes only. You should probably be reading this
// from the environment variable or a secret store in a production setting.
const PRIVATE_KEY = "<ACCOUNT_PRIVATE_KEY>";
const Account = account = new AptosAccount(Uint8Array.from(Buffer.from(PRIVATE_KEY)));

// Helper function to send signed transactions.
export async function sendSignedTransactionWithAccount(
  signerAccount: AptosAccount,
  entryFunction: TxnBuilderTypes.EntryFunction,
) {
  const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(entryFunction);

  // Ge the latest sequence number and chain id.
  const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
    client.getAccount(signerAccount.address()),
    client.getChainId(),
  ]);

  const rawTxn = new TxnBuilderTypes.RawTransaction(
      // Transaction sender account address
      TxnBuilderTypes.AccountAddress.fromHex(signerAccount.address()),
      BigInt(sequenceNumber),
      entryFunctionPayload,
      // Max gas unit to spend
      BigInt(2000),
      // Gas price per unit
      BigInt(100),
      // Expiration timestamp. The transaction is discarded if it is not 
      // executed within 10 seconds from now.
      BigInt(Math.floor(Date.now() / 1000) + 10),
      new TxnBuilderTypes.ChainId(chainId),
  );
  const signedTxn = await client.signTransaction(signerAccount, rawTxn);
  const pendingTxn = await client.submitSignedBCSTransaction(signedTxn);
  return pendingTxn.hash;
}

// Helper function to create limit order.
async function addOrder(
  signerAccount: AptosAccount,
  instrumentCoin: string,
  quoteCoin: string,
  side: 'buy' | 'sell',
  typ: 'resting' | 'post' | 'ioc' | 'fok',
  price: number,
  quantity: number,
) {
  let parsedTyp = 0;
  if (typ === 'resting') {
    parsedTyp = 1;
  } else if (typ === 'post') {
    parsedTyp = 2;
  } else if (typ === 'ioc') {
    parsedTyp = 3;
  } else if (typ === 'fok') {
    parsedTyp = 4;
  }
  const entryFunction = TxnBuilderTypes.EntryFunction.natural(
    `${FERUM}::market`,
    "add_order_entry",
    coinTypeTags(instrumentCoin, quoteCoin),
    [
      BCS.bcsSerializeU8(side === 'buy' ? 2 : 1), // Side
      BCS.bcsSerializeU8(parsedTyp), // Type
      BCS.bcsSerializeUint64(price), // Price
      BCS.bcsSerializeUint64(quantity), // Quantity
      BCS.bcsSerializeStr(""), // ClientOrderID
    ]
  );
  return await sendSignedTransactionWithAccount(signerAccount, entryFunction);
}

async function main() {
  // Places a standard limit SELL for 1 APT with a limit price of 1 USDF. 

  const price = "1000";
  const quantity = "1000";
  const side = "sell";
  const typ = "resting";
  const quoteCoinType = `${FERUM}::test_coins::USDF`;
  const instrumentCoinType = "0x1::aptos_coin::AptosCoin";

  const txHash = await addOrder(account, instrumentCoinType, quoteCoinType, side, typ, price, quantity)
  const txResult = await client.waitForTransactionWithResult(txHash);
  console.log(txResult);
}

main();

Last updated