Skip to main content

Smart Contract Trading

The Delegate Signer feature is currently only available on EVM chains. Solana programs have a different architecture and do not require this pattern.
On EVM chains, there is a distinction between Externally Owned Accounts (EOAs) and Smart Contract accounts. An EOA cannot have code deployed to it, and a Smart Contract account cannot have a private key that is owned by a user. Since Orderly uses EIP-712 off-chain signatures for trading, which are sent to our backend services, a Smart Contract account cannot trade using this method. To allow Smart Contracts to trade on our platform, we introduce the “Delegate Signer” feature, which allows delegation of trading for a Smart Contract account to be executed by an EOA. Delegate Signer allows Smart Contracts to assign an EOA to sign on behalf of them for actions requiring the EIP-712 signature. Smart Contracts assign a Delegate Signer through Orderly’s Vault Smart Contract function called delegateSigner prior to interacting with Orderly.

Limitations

  • There can only be one EOA per Smart Contract account. If a new Delegate Signer is announced, the old one will be deleted. - Smart Contract accounts are single-chain — they can only deposit/withdraw from the chain where the Delegate Signer call was made. - A Smart Contract account will always need an EOA to withdraw funds.

Implementation

1

Create delegate signer link

Your Smart Contract needs to call the delegateSigner function on the Orderly Vault contract. The interface is described below:
function delegateSigner(VaultTypes.VaultDelegate calldata data) external;

library VaultTypes {
    struct VaultDelegate {
        bytes32 brokerHash;
        address delegateSigner;
    }
}
EVM contract ABI files can be found on our GitHub. All contracts are verified, so you can also see the ABI on their respective explorers.
An example Smart Contract is deployed on Arbitrum Sepolia, however it implements an insecure execAction function, which allows arbitrary code execution.A minimal implementation of a Smart Contract will look like this:
pragma solidity ^0.8.18;

import "./IVault.sol";

contract DelegateContract {
    function delegate(address vault, VaultTypes.VaultDelegate calldata data) public {
        IVault(vault).delegateSigner(data);
    }

    // ...
    // implement any other functions your SC might need
}
After calling the function on your Smart Contract for the linking, you need to remember this transaction hash.
2

Accept the link from the Delegate Signer EOA

Confirm the link by signing the EIP-712 with your Delegate Signer’s wallet and send the information via delegate_signer API. You will also need to send the transaction hash from previous step.This step will optionally create the account for the Smart Contract, if it does not yet exist. If it already exists it will just update the linked EOA account.
3

Add Orderly Key

Add Delegate Orderly Key via Add Delegate Signer Orderly Key API (calling POST /v1/delegate_orderly_key). Note that normal Orderly Key endpoints cannot be used for smart contract accounts.
4

Deploy funds

You can either implement a function in your Smart Contract to call the deposit function of the Vault contract or you can call the depositTo function from any address and deposit into your Smart Contract’s account wallet. For the latter you will also require the account_id from the registration step.
5

Start Trading

Start trading via regular trading APIs authenticated with above Orderly Key.
6

Settle PnL/Withdraw

Settle PnL and withdraw via special Delegate Signer endpoints. Sign EIP-712 domains with Delegate Signer wallet.

Code Examples

The following examples demonstrate how an EOA wallet delegates authentication for a Smart Contract account.
import { ethers } from "ethers";

const types = {
  DelegateSigner: [
    { name: "delegateContract", type: "address" },
    { name: "brokerId", type: "string" },
    { name: "chainId", type: "uint256" },
    { name: "timestamp", type: "uint64" },
    { name: "registrationNonce", type: "uint256" },
    { name: "txHash", type: "bytes32" }
  ],
  DelegateAddOrderlyKey: [
    { name: "delegateContract", type: "address" },
    { name: "brokerId", type: "string" },
    { name: "chainId", type: "uint256" },
    { name: "orderlyKey", type: "string" },
    { name: "scope", type: "string" },
    { name: "timestamp", type: "uint64" },
    { name: "expiration", type: "uint64" }
  ]
};

const OFF_CHAIN_DOMAIN = {
  name: "Orderly",
  version: "1",
  chainId: 421614,
  verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
};

const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!); // Delegate EOA Wallet
const smartContractAddress = "0xYourSmartContractAddress";

// Step 2: Accept the link via API
async function linkDelegate(txHash: string, registrationNonce: string) {
  const timestamp = Date.now();
  const message = {
    delegateContract: smartContractAddress,
    brokerId: "woofi_dex",
    chainId: 421614,
    timestamp,
    registrationNonce,
    txHash // ethers.js automatically parses hex string to bytes32
  };

  const signature = await wallet.signTypedData(
    OFF_CHAIN_DOMAIN,
    { DelegateSigner: types.DelegateSigner },
    message
  );

  const response = await fetch("https://testnet-api.orderly.org/v1/delegate_signer", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      message,
      signature,
      userAddress: wallet.address
    })
  });
  console.log(await response.json());
}

// Step 3: Add Orderly Key for the Smart Contract Account
async function addDelegateKey(orderlyKeyStr: string) {
  const timestamp = Date.now();
  const message = {
    delegateContract: smartContractAddress,
    brokerId: "woofi_dex",
    chainId: 421614,
    orderlyKey: orderlyKeyStr,
    scope: "read,trading",
    timestamp,
    expiration: timestamp + 1000 * 60 * 60 * 24 * 365 // 1 year
  };

  const signature = await wallet.signTypedData(
    OFF_CHAIN_DOMAIN,
    { DelegateAddOrderlyKey: types.DelegateAddOrderlyKey },
    message
  );

  const response = await fetch("https://testnet-api.orderly.org/v1/delegate_orderly_key", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      message,
      signature,
      userAddress: wallet.address
    })
  });
  console.log(await response.json());
}

Additional Resources