Skip to content

Remove Delegated Signer (WebSocket)

Remove a delegated signer from a subaccount through the WebSocket connection, revoking their ability to perform trading actions on behalf of the subaccount. This immediately terminates the trading permission previously granted to the specified wallet address.

Endpoint

ws.send() wss://api.synthetix.io/v1/ws/trade

Request

Request Format

{
  "id": "delegate-remove-1",
  "method": "post",
  "params": {
    "action": "removeDelegatedSigner",
    "subAccountId": "1867542890123456789",
    "delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
    "nonce": 1735689600000,
    "expiresAfter": 1735689900,
    "signature": {
      "v": 28,
      "r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
      "s": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
    }
  }
}

Parameters

Request Parameters

ParameterTypeRequiredDescription
idstringYesClient-generated unique request identifier
methodstringYesMust be "post"
paramsobjectYesContains all parameters for the request

Params Object

ParameterTypeRequiredDescription
actionstringYesMust be "removeDelegatedSigner"
subAccountIdstringYesSubaccount identifier
delegateAddressstringYesEthereum wallet address of the delegated signer to remove (42-character hex format)
nonceintegerYesIncrementing nonce (Unix ms timestamp as number)
expiresAfterintegerNoOptional request expiration timestamp in seconds
signatureobjectYesEIP-712 signature

Important: Only the master account owner can remove delegated signers.

EIP-712 Type Definition

EIP-712 Type Definitions for Delegation

AddDelegatedSigner

const AddDelegatedSignerTypes = {
  AddDelegatedSigner: [
    { name: "delegateAddress", type: "address" },
    { name: "subAccountId", type: "uint256" },
    { name: "nonce", type: "uint256" },
    { name: "expiresAfter", type: "uint256" },
    { name: "expiresAt", type: "uint256" },
    { name: "permissions", type: "string[]" }
  ]
}

GetDelegatedSigners

Uses the standard SubAccountAction type (same as other read operations like getTrades, getPositions):

const SubAccountActionTypes = {
  SubAccountAction: [
    { name: "subAccountId", type: "uint256" },
    { name: "action", type: "string" },
    { name: "expiresAfter", type: "uint256" }
  ]
}
 
// Message example:
const message = {
  subAccountId: "1867542890123456789",
  action: "getDelegatedSigners",
  expiresAfter: 0  // Optional, use 0 if not expiring
}

RemoveDelegatedSigner

const RemoveDelegatedSignerTypes = {
  RemoveDelegatedSigner: [
    { name: "delegateAddress", type: "address" },
    { name: "subAccountId", type: "uint256" },
    { name: "nonce", type: "uint256" },
    { name: "expiresAfter", type: "uint256" }
  ]
}

Example Typed Data for AddDelegatedSigner

{
  "types": {
    "EIP712Domain": [
      { "name": "name", "type": "string" },
      { "name": "version", "type": "string" },
      { "name": "chainId", "type": "uint256" },
      { "name": "verifyingContract", "type": "address" }
    ],
    "AddDelegatedSigner": [
      { "name": "delegateAddress", "type": "address" },
      { "name": "subAccountId", "type": "uint256" },
      { "name": "nonce", "type": "uint256" },
      { "name": "expiresAfter", "type": "uint256" },
      { "name": "expiresAt", "type": "uint256" },
      { "name": "permissions", "type": "string[]" }
    ]
  },
  "primaryType": "AddDelegatedSigner",
  "domain": {
    "name": "Synthetix",
    "version": "1",
    "chainId": 1,
    "verifyingContract": "0x0000000000000000000000000000000000000000"
  },
  "message": {
    "delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
    "subAccountId": "1867542890123456789",
    "nonce": 1735689600000,
    "expiresAfter": 1735689900000,
    "expiresAt": 0,
    "permissions": ["trading"]
  }
}

Important Notes:

  • Field order matters for EIP-712 - Fields must be in the exact order shown above for signature verification
  • delegateAddress is the wallet address being granted delegation permissions
  • expiresAfter is the request expiration timestamp (when the request itself expires)
  • expiresAt is when the delegation permission expires (use 0 for no expiration)
  • The nonce field must be monotonically increasing per subaccount
  • All delegation operations must be signed by the master account owner

Response Format

Success Response

{
  "id": "delegate-remove-1",
  "status": 200,
  "result": {
    "subAccountId": "1867542890123456789",
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590"
  }
}

Response Fields

FieldTypeDescription
subAccountIdstringThe subaccount ID the signer was removed from
walletAddressstringThe removed delegated signer's wallet address

Error Response

{
  "id": "delegate-remove-1",
  "status": 404,
  "result": null,
  "error": {
    "code": 404,
    "message": "Delegated signer not found"
  }
}

Implementation Example

import { ethers } from 'ethers';
 
async function removeDelegatedSigner(ws, signer, subAccountId, delegateAddress) {
  const nonce = Date.now();
  const expiresAfter = Math.floor(Date.now() / 1000) + 300; // 5 minutes
 
  const domain = {
    name: "Synthetix",
    version: "1",
    chainId: 1,
    verifyingContract: "0x0000000000000000000000000000000000000000"
  };
 
  const types = {
    RemoveDelegatedSigner: [
      { name: "subAccountId", type: "uint256" },
      { name: "delegateAddress", type: "address" },
      { name: "nonce", type: "uint256" },
      { name: "expiresAfter", type: "uint256" }
    ]
  };
 
  const message = {
    subAccountId: BigInt(subAccountId),
    delegateAddress,
    nonce: BigInt(nonce),
    expiresAfter: BigInt(expiresAfter)
  };
 
  const sig = await signer.signTypedData(domain, types, message);
  const signature = ethers.Signature.from(sig);
 
  ws.send(JSON.stringify({
    id: `delegate-remove-${Date.now()}`,
    method: "post",
    params: {
      action: "removeDelegatedSigner",
      subAccountId,
      delegateAddress,
      nonce,
      expiresAfter,
      signature: { v: signature.v, r: signature.r, s: signature.s }
    }
  }));
}
 
// Usage: Remove a trading bot's delegation
await removeDelegatedSigner(
  ws,
  signer,
  "1867542890123456789",
  "0x742d35Cc6634C0532925a3b844Bc9e7595f89590"
);

Code Examples

Remove Trading Bot Access

{
  "id": "remove-bot",
  "method": "post",
  "params": {
    "action": "removeDelegatedSigner",
    "subAccountId": "1867542890123456789",
    "delegateAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f89590",
    "nonce": 1735689600000,
    "expiresAfter": 1735689900,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Revoke Team Member Access

{
  "id": "remove-team-member",
  "method": "post",
  "params": {
    "action": "removeDelegatedSigner",
    "subAccountId": "1867542890123456789",
    "delegateAddress": "0x9C4b8E7F0A2D3B6C5E8A1F3D5B7C9E1A3F5D7B9E",
    "nonce": 1735689600001,
    "expiresAfter": 1735689900,
    "signature": { "v": 28, "r": "0x...", "s": "0x..." }
  }
}

Implementation Notes

  • Immediate Effect: Removal takes effect immediately upon successful execution
  • Access Requirements: Only master account owners can remove delegated signers
  • No Self-Removal: Delegated signers cannot remove themselves (must be done by master account)
  • Idempotent: Attempting to remove a non-existent delegation returns an error
  • Active Sessions: Any active sessions or connections for the removed signer should be terminated
  • Pending Operations: Any pending operations initiated by the removed signer remain valid
  • Audit Trail: All removal actions are logged for security and compliance

Effect on Active Operations

Operation TypeEffect of Removal
Open OrdersRemain active (can be cancelled by master account)
Pending WithdrawalsContinue processing
Active SessionsShould be terminated
API KeysShould be invalidated
SubscriptionsShould be cancelled

Use Cases

  • Security Response: Immediately revoke access when a delegated signer is compromised
  • Team Changes: Remove access when team members leave or change roles
  • Bot Decommission: Remove trading bot access when no longer needed
  • Access Rotation: Regular removal and re-addition of signers for security
  • Emergency Lockdown: Quick removal of all delegated signers in security events

Common Errors

Error CodeMessageDescription
404Delegated signer not foundAddress is not delegated for this subaccount
401Only master account can remove delegated signersMust be signed by master account owner
404Subaccount not foundInvalid subaccount ID
ErrorDescription
Invalid signatureEIP-712 signature validation failed
Invalid market symbolMarket symbol not recognized
Nonce already usedNonce must be greater than previous value
Rate limit exceededToo many requests in time window
Request expiredexpiresAfter timestamp has passed

Next Steps