Skip to content

Smart Contract Accounts (Gnosis Safe)

Use a Gnosis Safe (or similar smart-contract wallet) as the owner of your Synthetix master account. The Safe holds custody onchain; a separate manager EOA signs trading API requests because the Safe cannot produce EIP-712 signatures directly.

This is different from sub-account delegation (addDelegatedSigner), which grants session or delegate permissions on a specific subaccount. A manager is an account-level role granted onchain.

Roles

RoleWallet typeHow it is grantedTypical use
OwnerSafe (smart contract)First deposit from the SafeCustody, onchain deposits
ManagerEOASafe calls PermissionsRegistry.addManagerSign API requests for account admin actions
Delegate / sessionEOAOwner, manager, or sub-account delegate calls addDelegatedSignerOn-exchange trading keys

End-to-end flow

1. Deposit from the Safe

Deposit collateral from the Safe to SynthetixDepositContract using a standard multisig transaction. See Deposits & Account Creation.

The Safe address becomes the owner wallet for the master account (sub_accounts.wallet_address). Query IDs with getSubAccountIds.

2. Assign a manager onchain

The Safe must grant a normal EOA permission to act on its behalf:

// Called by the Safe (msg.sender = Safe address)
IPermissionsRegistry(PERMISSIONS_REGISTRY).addManager(managerEOA);
NetworkPermissionsRegistry (Proxy)
Mainnet0x45F91031b33Da2585932c8f1cdFF0faa6cD329ae

The relayer indexes PermissionGranted / PermissionRevoked events; the backend updates manager authorization within a few blocks. To revoke: revokeManager(managerEOA).

3. Operate the account with the manager EOA

The manager signs EIP-712 requests with its own private key (same domain and flow as EIP-712 Signing). Use the target subaccount's subAccountId in each signed request.

Manager can sign (owner-or-manager actions):
ActionNotes
withdrawCollateraldestination must be the owner Safe address
transferCollateralSource and destination must belong to the same owner wallet (managers cannot transfer across different Safes)
createSubaccountCreates subaccounts under the Safe owner; send params.subAccountId as an existing managed ID — EIP-712 signs it as masterSubAccountId (see endpoint docs)
updateSubAccountNameRename subaccounts
removeAllDelegatedSignersClear all delegates on a subaccount
voluntaryCollateralExchangeCollateral conversion under owner account
addDelegatedSigner / removeManager tier equals owner for delegation admin
Manager cannot sign without extra setup:
ActionWhy
placeOrders, placeIsolatedOrder, cancelOrders, cancelAllOrders, modifyOrder, modifyOrderBatch, scheduleCancelRequires sub-account session (or delegate) delegation — assign via addDelegatedSigner
Withdraw to an arbitrary addressBlocked — managers may only withdraw to the owner wallet

4. Enable on-exchange trading (optional)

To trade on Synthetix from an EOA without the Safe signing each order:

  1. The manager EOA calls addDelegatedSigner with permissions: ["session"] for a trading key EOA on the target subaccount (after onchain addManager).
  2. The session EOA signs placeOrders and related trading actions.

Alternatively, the manager EOA can hold session delegation on the subaccount and sign trades directly.

5. Discover managed workspaces

If your wallet is a pure manager (no owned accounts), the default getSubAccountIds response is an empty array. Use this bootstrap sequence:

  1. Call unsigned getSubAccountIds with includeDelegations: true and read managedSubAccountIds. Retry after the onchain addManager transaction confirms if the list is still empty.
  2. Pick one managed ID and use it as subAccountId when signing trade requests (including getSubAccounts).
  3. Call signed getSubAccounts. The response has two complementary views:
    • subAccounts — all sibling subaccounts under the same master as the authenticated ID (for a manager signing with a Safe subaccount ID, this is that Safe's workspace).
    • managedAccounts — every owner wallet this manager can access, grouped by owner address (useful when you manage multiple Safes in one call).

For a single Safe, subAccounts and managedAccounts[safeAddress] overlap; use managedSubAccountIds only to pick an ID to authenticate, not as a substitute for signed reads.

managedSubAccountIds is discovery-only; signed trade calls always require a concrete subAccountId in the EIP-712 envelope.

Example: manager withdrawal to Safe

{
  "params": {
    "action": "withdrawCollateral",
    "subAccountId": "1867542890123456789",
    "symbol": "USDT",
    "amount": "1000.0",
    "destination": "0xYourSafeAddress..."
  },
  "nonce": 1704067200000,
  "signature": { "...": "signed by manager EOA" },
  "expiresAfter": 1704067300
}

If destination is not the Safe owner address, the API returns HTTP 403: "Managers may only withdraw to the owner's wallet address".

Related