> ## Documentation Index
> Fetch the complete documentation index at: https://staging-docs.orderly.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Delegate Signer

> How smart contracts trade on Orderly using the delegate signer pattern to bypass EOA-only signing.

## Smart Contract Trading

<Note>
  The Delegate Signer feature is currently only available on EVM chains. Solana programs have a
  different architecture and do not require this pattern.
</Note>

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](/build-on-omnichain/user-flows/wallet-authentication) 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](/build-on-omnichain/addresses) function called `delegateSigner` prior to interacting with Orderly.

### Limitations

<Warning>
  * 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.
</Warning>

### Implementation

<Steps>
  <Step title="Create delegate signer link">
    Your Smart Contract needs to call the `delegateSigner` function on the Orderly Vault contract.
    The interface is described below:

    ```sol theme={null}
    function delegateSigner(VaultTypes.VaultDelegate calldata data) external;

    library VaultTypes {
        struct VaultDelegate {
            bytes32 brokerHash;
            address delegateSigner;
        }
    }
    ```

    <Note>
      EVM contract ABI files can be found on our [GitHub](https://github.com/OrderlyNetwork/contract-evm-abi). All contracts are verified, so you can also see the ABI on their respective explorers.
    </Note>

    An example Smart Contract is deployed on [Arbitrum Sepolia](https://sepolia.arbiscan.io/address/0xa4394b62261061c629800c6d86d153a9f38f0cbb#code), however it implements an insecure `execAction` function, which allows arbitrary code execution.

    A minimal implementation of a Smart Contract will look like this:

    <CodeGroup>
      ```sol DelegateContract.sol theme={null}
      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
      }
      ```

      ```sol IVault.sol theme={null}
      pragma solidity ^0.8.18;

      library VaultTypes {
          struct VaultDelegate {
              bytes32 brokerHash;
              address delegateSigner;
          }
      }

      interface IVault {
          function delegateSigner(VaultTypes.VaultDelegate calldata data) external;
      }
      ```
    </CodeGroup>

    After calling the function on your Smart Contract for the linking, you need to remember this transaction hash.
  </Step>

  <Step title="Accept the link from the Delegate Signer EOA">
    Confirm the link by signing the [EIP-712](/build-on-omnichain/user-flows/wallet-authentication#message-types) with your Delegate Signer's wallet and send the information via [`delegate_signer` API](/build-on-omnichain/restful-api/public/delegate-signer). 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.
  </Step>

  <Step title="Add Orderly Key">
    Add Delegate Orderly Key via [Add Delegate Signer Orderly Key API](/build-on-omnichain/restful-api/public/add-delegate-signer-orderly-key) (calling `POST /v1/delegate_orderly_key`). Note that normal Orderly Key endpoints cannot be used for smart contract accounts.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Start Trading">
    Start trading via regular trading APIs authenticated with above Orderly Key.
  </Step>

  <Step title="Settle PnL/Withdraw">
    [Settle PnL](/build-on-omnichain/restful-api/private/delegate-signer-settle-pnl) and [withdraw](/build-on-omnichain/restful-api/private/delegate-signer-withdraw-request) via special Delegate Signer endpoints. Sign [EIP-712](/build-on-omnichain/user-flows/wallet-authentication) domains with Delegate Signer wallet.
  </Step>
</Steps>

### Code Examples

The following examples demonstrate how an EOA wallet delegates authentication for a Smart Contract account.

<CodeGroup>
  ```ts TypeScript theme={null}
  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());
  }
  ```

  ```py Python theme={null}
  import time
  import requests
  from eth_account import Account, messages

  MESSAGE_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"}
      ]
  }

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

  account = Account.from_key("eoa_private_key")
  sc_address = "0xYourSmartContractAddress"

  # Step 2: Accept the link via API
  def link_delegate(tx_hash, registration_nonce):
      timestamp = int(time.time() * 1000)
      message = {
          "delegateContract": sc_address,
          "brokerId": "woofi_dex",
          "chainId": 421614,
          "timestamp": timestamp,
          "registrationNonce": int(registration_nonce),
          "txHash": bytes.fromhex(tx_hash[2:]) # Convert hex string to bytes for bytes32
      }

      encoded_data = messages.encode_typed_data(
          domain_data=OFF_CHAIN_DOMAIN,
          message_types={"DelegateSigner": MESSAGE_TYPES["DelegateSigner"]},
          message_data=message
      )
      signature = account.sign_message(encoded_data).signature.hex()

      # Send API Request (txHash in the API body must be string)
      payload = {
          "message": {
              "delegateContract": message["delegateContract"],
              "brokerId": message["brokerId"],
              "chainId": message["chainId"],
              "timestamp": message["timestamp"],
              "registrationNonce": message["registrationNonce"],
              "txHash": tx_hash
          },
          "signature": signature,
          "userAddress": account.address
      }
      res = requests.post("https://testnet-api.orderly.org/v1/delegate_signer", json=payload)
      print(res.json())
  ```
</CodeGroup>

### Additional Resources

* A full frontend reference implementation can be found in [this Github repository](https://github.com/OrderlyNetwork/broker-registration).
* A deployed demonstration is available [here](https://orderlynetwork.github.io/broker-registration/).
