Skip to main content
Note: see the language section for more details.

Allow ABI-specific contract call parameters

For contract interactions, use Smart Contract Interfaces (ABI upload) rather than raw calldata slicing. ABI-based policies use named arguments (eth.tx.contract_call_args['arg_name']) instead of byte offsets — they’re more readable, less error-prone, and won’t silently break if the contract encoding changes. Raw eth.tx.data[...] slicing is a fallback for contracts where no ABI is available. Restrict a transfer call to a maximum amount and a specific recipient:
Restrict by function name or selector (also requires an ABI upload):

Iterating over contract call arguments

When a contract function takes an array parameter, use .count(), .all(), and .any() to enforce conditions across every element, rather than checking a single index. Syntax:
  • Array element count: eth.tx.contract_call_args['arrayArg'].count()
  • All elements match: eth.tx.contract_call_args['arrayArg'].all(item, <condition>)
  • Any element matches: eth.tx.contract_call_args['arrayArg'].any(item, <condition>)
For functions that take a tuple array (e.g. executeBatch((address,uint256,bytes)[] calls)), tuple fields are currently accessed by position rather than name: call[0] (first field), call[1] (second field), and so on. The positions correspond to the order of fields as defined in your ABI — substitute the correct indices for your own struct. Named access such as call['target'] is not yet supported and produces OUTCOME_ERROR.
Enforce exact batch size Restrict signing to transactions containing exactly two calls:
Whitelist all call targets in a batch Allow signing only when every call targets a specific contract and sends zero ETH. call[0] is the target address and call[1] is the value, based on the field order in the executeBatch ABI:
Combine batch size and target restrictions
Whitelist recipients in a flat address array For functions that take a plain address[] parameter — such as a disperse-style multi-send contract — use in to restrict signing to a known set of addresses:
See Smart Contract Interfaces for the full upload walkthrough and Solana IDL support.

Allow ERC-20 transfers for a specific token smart contract (raw calldata fallback)

Use this pattern only when an ABI is unavailable. The selector 0xa9059cbb is the 4-byte keccak256 hash of transfer(address,uint256).

Allow anyone to sign transactions for testnet (Sepolia)

Allow ETH transactions with a specific nonce range

Allow signing of EIP-712 payloads for Hyperliquid ApproveAgent operations

Inspect nested fields in EIP-712 message payloads

The eth.eip_712.message map supports nested field access using bracket notation and array iteration operators, allowing policies to inspect and enforce conditions across typed data contents, beyond just the domain and primary type. Syntax:
  • Nested struct fields: eth.eip_712.message['outerField']['innerField']
  • Array element fields: eth.eip_712.message['arrayField'][0]['innerField']
  • Array iteration: eth.eip_712.message['arrayField'].all(item, <condition>)
  • Array length: eth.eip_712.message['arrayField'].count()
Example: Restrict Permit2 batch token approvals by token address Uniswap’s Permit2 PermitBatchTransferFrom type lets users sign a single message authorizing multiple token transfers. The message contains a permitted array of TokenPermissions structs, each with a token address and an amount.
To allow signing only when the first permitted token is USDC:
Array elements can be accessed by index ([0], [1], etc.). The condition message['permitted'][0]['token'] == '0xA0b8...' checks the first entry. You can also check specific positions explicitly (for example, message['permitted'][0]['token'] == '0xA0b8...' && message['permitted'][1]['token'] == '0xA0b8...'). Use .all() when you want to apply the same token check across the entire permitted array. A .all() example is included below.

Iterating over array fields

Whitelist a specific token across all approvals with .all():
Allow Permit2 batches with two or fewer token permissions:
Allow token permissions below 100 USDC:
in works for integer fields. For string fields such as token addresses, use || instead.

Deny signing of NO_OP keccak256 payloads

Allow signing of EIP-712 payloads for EIP-3009 transfers

Allow signing of EIP-712 payloads for EIP-2612 permits for USD Coin

Allow signing of EIP-7702 authorizations

Allow only the delegate contracts that you trust, on your specific chains:
To allow one authorization only, also set the nonce:
Notes:
  • Write each address in the EIP-55 checksummed (mixed-case) form, because the comparison is case-sensitive.
  • Write chain_id and nonce as numbers (without quotes). Turnkey rejects a policy that compares a number to a quoted value.
  • To also allow ACTIVITY_TYPE_SIGN_RAW_PAYLOAD, use activity.type in ['ACTIVITY_TYPE_SIGN_RAW_PAYLOAD', 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'].
  • For the activities that populate eth.eip_7702_authorization, see EIP-7702.

Deny signing of EIP-7702 authorizations for all chains

An authorization with chain_id 0 is valid on all EVM chains. To deny these authorizations in the activities that populate eth.eip_7702_authorization, you can use a policy like the following:

Deny batch signing of EIP-7702 authorizations

ACTIVITY_TYPE_SIGN_RAW_PAYLOADS does not populate eth.eip_7702_authorization. To deny EIP-7702 authorizations in this activity, use the encoding. This policy blocks only PAYLOAD_ENCODING_EIP7702_AUTHORIZATION. A hexadecimal or UTF-8 payload can also be an authorization, so if a key must not sign authorizations, allow raw payload signing only with PAYLOAD_ENCODING_EIP712 (see EIP-7702).

Deny signing of Type 4 transactions

The policy engine does not expose the authorization list of a Type 4 (EIP-7702) transaction. To block Type 4 transactions, deny them by type. This policy covers the transactions that you sign with ACTIVITY_TYPE_SIGN_TRANSACTION and ACTIVITY_TYPE_SIGN_TRANSACTION_V2.
Side note: Type 4 transactions that Turnkey builds for ACTIVITY_TYPE_ETH_SEND_TRANSACTION_V2 and ACTIVITY_TYPE_ETH_UNDELEGATE_7702 have a different eth.tx.type, so this policy does not apply to them.