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

# MPC Wallets

> Restrict what an MPC wallet can do on Derive

Most MPC services (Fordefi, Fireblocks, and similar) can permission over three types of actions:

* **Smart contract calls** — which contract, which function, and often which argument values.
  Fordefi expresses these as
  [ABI conditions](https://docs.fordefi.com/user-guide/policies/policy-rules-conditions-and-actions#abi),
  matching on `address`, integer, boolean, `bytes`, array, and tuple arguments.
* **Personal messages** (`personal_sign`, e.g. EIP-191) — see
  [personal messages and messages on non-EVM chains](https://docs.fordefi.com/user-guide/policies/message-policy#personal-messages-and-messages-on-non-evm-chains).
* **Typed data** (EIP-712) — see
  [typed data messages](https://docs.fordefi.com/user-guide/policies/message-policy#typed-data-messages), where you can deny signatures that match certain fields of the EIP-712 domain or message.

Derive uses all three paths for different purposes:

| Path                                                 | Medium    | Scheme      | Reaches                                                               |
| ---------------------------------------------------- | --------- | ----------- | --------------------------------------------------------------------- |
| [`OnchainActionManager`](/getting-started/contracts) | Onchain   | transaction | deposit, withdraw, add/remove session keys                            |
| [Signed actions](/authentication/action-signing)     | HTTP / WS | EIP-712     | all of the above, **and trading**                                     |
| [Session login](/authentication/session-login)       | HTTP / WS | EIP-191     | **read-only** — authenticates an HTTP request or WebSocket connection |

Because the EIP-712 path can do strictly more than the contract-call path, the tightest setup is
to close it entirely and operate through contract calls only.

## Recommended policy

<Steps>
  <Step title="Block all EIP-712 signing on the MPC wallet">
    Every state-changing Derive action taken over the API is an EIP-712 signed action. Denying
    EIP-712 outright means the MPC wallet can never trade, transfer, or withdraw through the
    API — no allow-list to maintain and no new action type can slip through later.

    In Fordefi this is a [typed data message](https://docs.fordefi.com/user-guide/policies/message-policy#typed-data-messages)
    rule. Fordefi surfaces an EIP-712 message's **recipient as its `verifyingContract`**, so target
    the rule at Derive's action domain:

    | Domain field        | Value                                                             |
    | ------------------- | ----------------------------------------------------------------- |
    | `name`              | `Matching`                                                        |
    | `version`           | `1.0`                                                             |
    | `verifyingContract` | `0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD`                      |
    | `chainId`           | the settlement chain's id — the only field that varies by network |

    Every Derive signed action uses this same `verifyingContract` on every network, so one deny
    rule on that address covers mainnet and testnet alike.
  </Step>

  <Step title="Whitelist deposits">
    On `OnchainActionManager`, allow the two deposit entrypoints:

    ```solidity theme={null}
    function deposit(address asset, uint256 amount, uint64 subaccountId, address fallbackRecipient);
    function depositToNewSubaccount(address asset, uint256 amount, uint32 managerId, address owner);
    ```

    Pin `owner` and `fallbackRecipient` to the MPC wallet address. `owner` is what makes the MPC
    wallet the account owner; `fallbackRecipient` is where a deposit lands if it cannot be applied
    to its intended subaccount.
  </Step>

  <Step title="Whitelist session-key registration, with the scopes pinned">
    Session keys are registered from L1 through the generic entrypoint:

    ```solidity theme={null}
    function submit(uint256 actionType, bytes calldata data)
    ```

    ### Example (trade and withdraw only session key)

    In this example we're going to create a policy that will only allow your MPC wallet to create Session keys with the `trade:orderbook:perp` and `withdraw` protocol scopes. Because only `admin` scoped keys can withdraw to any recipient, this policy will only allow withdrawals to the MPC wallet's own subaccounts.

    Use the TypeScript SDK's
    [session-key codec](https://github.com/derivexyz/derive-ts/blob/master/src/codecs/sessionKey.ts),
    to produce the bytes you'll use to create an MPC policy.

    ```typescript theme={null}
    import { encodeSetSessionKeyActionData } from '@derivexyz/derive-ts/codecs';
    import { ProtocolScopeCode } from '@derivexyz/derive-ts';
    import { Interface } from 'ethers';

    const data = encodeSetSessionKeyActionData({
      sessionKey: '0x9f000000000000000000000000000000000000be',
      expirySec: 1793491200, // the KEY's lifetime; 0 deletes it
      // Exactly two scopes, in this order — the policy pins both.
      scopes: [ProtocolScopeCode.Withdraw, ProtocolScopeCode.TradeOrderbookPerp], // [1, 5]
      subaccountIds: [], // empty = all current and future subaccounts
    });

    const manager = new Interface([
      'function submit(uint256 actionType, bytes data) payable returns (uint256)',
    ]);
    // 51 = Set Session Key. `data` is the 256 bytes broken down below.
    console.log(manager.encodeFunctionData('submit', [51, data]));
    ```

    ### Which bytes to pin in your MPC policy

    | Param           | Contents                             | Pin to           |
    | --------------- | ------------------------------------ | ---------------- |
    | `actionType`    | Set Session Key action type          | `51`             |
    | `data[0:32]`    | `sessionKey` address                 | free — rotates   |
    | `data[32:64]`   | `expirySec`                          | free — rotates   |
    | `data[64:96]`   | offset → `scopes`                    | from console.log |
    | `data[96:128]`  | offset → `subaccountIds`             | from console.log |
    | `data[128:160]` | `scopes.length`                      | from console.log |
    | `data[160:192]` | `scopes[0]` = `withdraw`             | from console.log |
    | `data[192:224]` | `scopes[1]` = `trade:orderbook:perp` | from console.log |
    | `data[224:256]` | `subaccountIds.length` = all         | from console.log |

    You can modify what you pin depending on your needs.
  </Step>
</Steps>

<Note>
  Leave [personal messages (EIP191)](https://docs.fordefi.com/user-guide/policies/message-policy#personal-messages-and-messages-on-non-evm-chains)
  allowed so [session login](/authentication/session-login) keeps working and the MPC wallet retains
  read-only account access through UX and API.
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Access scopes" icon="lock" href="/authentication/access-scopes">
    The full protocol and off-chain scope catalog.
  </Card>

  <Card title="Session keys" icon="key" href="/authentication/session-keys">
    What a session key is and how its lifecycle works.
  </Card>

  <Card title="Withdrawals" icon="money-bill-transfer" href="/trading/transfers-withdrawals">
    The withdrawal action and its recipient semantics.
  </Card>
</CardGroup>


## Related topics

- [Contracts](/getting-started/contracts.md)
- [Wallet rfqs](/api-reference/channels/walletrfqs.md)
- [public/get_wallets_from_session_key](/api-reference/session-keys/publicget_wallets_from_session_key.md)
