From d1eb80be12a4848e94579ae242d702d8c8f72d4e Mon Sep 17 00:00:00 2001 From: Andrew Min Date: Tue, 27 Jan 2026 18:48:08 -0500 Subject: [PATCH 1/3] update policy language to add activity.params.type --- concepts/policies/examples/access-control.mdx | 2 +- concepts/policies/examples/bitcoin.mdx | 2 +- concepts/policies/examples/ethereum.mdx | 2 +- .../policies/examples/signing-control.mdx | 2 +- concepts/policies/examples/solana.mdx | 2 +- concepts/policies/language.mdx | 43 ++++++++++++++++++- 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/concepts/policies/examples/access-control.mdx b/concepts/policies/examples/access-control.mdx index e5d57332..a1585e28 100644 --- a/concepts/policies/examples/access-control.mdx +++ b/concepts/policies/examples/access-control.mdx @@ -1,6 +1,6 @@ --- title: "Access control" -description: "This page provides examples of policies governing access." +description: "This page provides examples of policies governing access generally." sidebarTitle: "Access control" --- diff --git a/concepts/policies/examples/bitcoin.mdx b/concepts/policies/examples/bitcoin.mdx index 4a91e97f..be35016a 100644 --- a/concepts/policies/examples/bitcoin.mdx +++ b/concepts/policies/examples/bitcoin.mdx @@ -1,6 +1,6 @@ --- title: "Bitcoin" -description: "This page provides examples of policies governing signing." +description: "This page provides examples of policies governing Bitcoin signing." sidebarTitle: "Bitcoin" --- diff --git a/concepts/policies/examples/ethereum.mdx b/concepts/policies/examples/ethereum.mdx index 952e18b2..5375fd5a 100644 --- a/concepts/policies/examples/ethereum.mdx +++ b/concepts/policies/examples/ethereum.mdx @@ -1,6 +1,6 @@ --- title: "Ethereum (EVM)" -description: "This page provides examples of policies governing signing." +description: "This page provides examples of policies governing Ethereum (EVM) signing." sidebarTitle: "Ethereum (EVM)" --- diff --git a/concepts/policies/examples/signing-control.mdx b/concepts/policies/examples/signing-control.mdx index df9e9949..3d659273 100644 --- a/concepts/policies/examples/signing-control.mdx +++ b/concepts/policies/examples/signing-control.mdx @@ -1,6 +1,6 @@ --- title: "Signing control" -description: "This page provides examples of policies governing signing." +description: "This page provides examples of policies governing signing generally. See network-specific guides for more." sidebarTitle: "Signing control" --- diff --git a/concepts/policies/examples/solana.mdx b/concepts/policies/examples/solana.mdx index eec936dd..0c609b07 100644 --- a/concepts/policies/examples/solana.mdx +++ b/concepts/policies/examples/solana.mdx @@ -1,6 +1,6 @@ --- title: "Solana" -description: "This page provides examples of policies governing signing." +description: "This page provides examples of policies governing Solana signing." sidebarTitle: "Solana" --- diff --git a/concepts/policies/language.mdx b/concepts/policies/language.mdx index e00882fe..c33112ee 100644 --- a/concepts/policies/language.mdx +++ b/concepts/policies/language.mdx @@ -83,7 +83,7 @@ The language is strongly typed which makes policies easy to author and maintain. | **Activity** | type | string | The type of the activity (e.g. ACTIVITY_TYPE_SIGN_TRANSACTION_V2) | | | resource | string | The resource type the activity targets: `USER`, `PRIVATE_KEY`, `POLICY`, `WALLET`, `ORGANIZATION`, `INVITATION`, `CREDENTIAL`, `CONFIG`, `**RECOVERY`, `AUTH`, `OTP`, `PAYMENT_METHOD`, `SUBSCRIPTION` | | | action | string | The action of the activity: `CREATE`, `UPDATE`, `DELETE`, `SIGN`, `EXPORT`, `IMPORT` | -| | params | string | The parameters of the activity: `SIGN_RAW_PAYLOADS`, `SIGN_RAW_PAYLOAD_V2` - `hash_function`, `encoding` | +| | params | struct | The parameters of the activity. See [here](#activity-parameters) for more details. | | **Wallet** | id | string | The identifier of the wallet | | | imported | bool | Boolean indicating whether or not this wallet has been imported | | | exported | bool | Boolean indicating whether or not this wallet has been exported | @@ -295,6 +295,47 @@ Note that our policy engine does not short circuit during evaluation. In practic For example, suppose you're looking to construct a policy with the condition `wallet.id == '\' || private_key.id == '\'. This condition will _always_ error out during evaluation, because only one of the two clauses can ever be valid: an activity cannot target both a wallet and private key. That means if you're trying to, say, sign with a private key, then the wallet clause will fail (because a wallet isn't being passed into the policy evaluation). Conversely, if you're trying to sign with a wallet, the private key clause will fail for the same reason. +### Activity parameters + +The `activity.params` field exposes specific parameters based on the activity type. Most activities do not expose any parameters, but certain activities expose fields that can be used in policy conditions. + +| Category | Activity Type | Field | Type | Description | +|--------------------|------------------------------------|---------------|-------------------|------------------------------------------------------------------------| +| Signing | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD` | `hash_function` | string | The hash function used (e.g., `HASH_FUNCTION_NO_OP`, `HASH_FUNCTION_KECCAK256`) | +| | | `encoding` | string | The payload encoding (e.g., `PAYLOAD_ENCODING_HEXADECIMAL`, `PAYLOAD_ENCODING_EIP712`) | +| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2`| `hash_function` | string | The hash function used | +| | | `encoding` | string | The payload encoding | +| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOADS` | `hash_function` | string | The hash function used | +| | | `encoding` | string | The payload encoding | +| | `ACTIVITY_TYPE_SIGN_TRANSACTION_V2`| `type` | string | The transaction type (e.g., `TRANSACTION_TYPE_ETHEREUM, TRANSACTION_TYPE_SOLANA, TRANSACTION_TYPE_BITCOIN, TRANSACTION_TYPE_TRON, TRANSACTION_TYPE_TEMPO`) | +| User Management | `ACTIVITY_TYPE_DELETE_USERS` | `user_ids` | list | The list of user IDs being deleted | +| Wallet Management | `ACTIVITY_TYPE_UPDATE_WALLET` | `wallet_id` | string | The ID of the wallet being updated | +| Authentication | `ACTIVITY_TYPE_INIT_OTP_AUTH_V2` | `otp_length` | int | The length of the OTP code | +| | `ACTIVITY_TYPE_INIT_OTP_AUTH_V3` | `otp_length` | int | The length of the OTP code | + +#### Example usage + +To deny signing raw payloads with the NO_OP hash function (except for EIP-712): + +```json +{ + "policyName": "Deny NO_OP hash signing", + "effect": "EFFECT_DENY", + "condition": "activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2' && activity.params.hash_function == 'HASH_FUNCTION_NO_OP' && activity.params.encoding != 'PAYLOAD_ENCODING_EIP712'" +} +``` + +To allow a user to delete only themselves: + +```json +{ + "policyName": "Allow self-deletion", + "effect": "EFFECT_ALLOW", + "consensus": "approvers.any(user, user.id == '')", + "condition": "activity.type == 'ACTIVITY_TYPE_DELETE_USERS' && activity.params.user_ids.count() == 1 && '' in activity.params.user_ids" +} +``` + ### Root quorum activities There are a select few activities that are not governed by policies, but rather by an organization's [root quorum](/concepts/users/root-quorum). These activities are: `ACTIVITY_TYPE_UPDATE_ROOT_QUORUM`, `ACTIVITY_TYPE_SET_ORGANIZATION_FEATURE`, `ACTIVITY_TYPE_REMOVE_ORGANIZATION_FEATURE`. For example, if a policy is added that allows a specific non-root user to perform `ACTIVITY_TYPE_SET_ORGANIZATION_FEATURE` activities, these requests will still fail as they are subject specifically to root quorum. From 12cfc4d9232dc9a4d1fef6048517c3051d64ff04 Mon Sep 17 00:00:00 2001 From: Andrew Min Date: Tue, 27 Jan 2026 18:49:05 -0500 Subject: [PATCH 2/3] wip: tempo docs --- concepts/policies/examples/tempo.mdx | 91 ++++++++++++ concepts/policies/language.mdx | 207 ++++++++++++++------------- docs.json | 3 +- 3 files changed, 198 insertions(+), 103 deletions(-) create mode 100644 concepts/policies/examples/tempo.mdx diff --git a/concepts/policies/examples/tempo.mdx b/concepts/policies/examples/tempo.mdx new file mode 100644 index 00000000..985f01e1 --- /dev/null +++ b/concepts/policies/examples/tempo.mdx @@ -0,0 +1,91 @@ +--- +title: "Tempo" +description: "This page provides examples of policies governing Tempo signing." +sidebarTitle: "Tempo" +--- + +Note: see the [language section](/concepts/policies/language#ethereum) for more details. + +#### Allow ABI-specific contract call parameters + +See [here](../../../concepts/policies/smart-contract-interfaces) for more information and examples. + +#### Allow ERC-20 transfers for a specific token smart contract + +```json +{ + "policyName": "Enable ERC-20 transfers for ", + "effect": "EFFECT_ALLOW", + "condition": "eth.tx.to == '' && eth.tx.data[0..10] == '0xa9059cbb'" +} +``` + +#### Allow anyone to sign transactions for testnet (Sepolia) + +```json +{ + "policyName": "Allow signing ethereum sepolia transactions", + "effect": "EFFECT_ALLOW", + "condition": "eth.tx.chain_id == 11155111" +} +``` + +#### Allow ETH transactions with a specific nonce range + +```json +{ + "policyName": "Allow signing Ethereum transactions with an early nonce", + "effect": "EFFECT_ALLOW", + "condition": "eth.tx.nonce <= 3" +} +``` + +#### Allow signing of EIP-712 payloads for Hyperliquid `ApproveAgent` operations + +```json +{ + "policyName": "Allow signing of EIP-712 Payloads for Hyperliquid `ApproveAgent` operations", + "effect": "EFFECT_ALLOW", + "condition": "eth.eip_712.domain.name == 'HyperliquidSignTransaction' && eth.eip_712.primary_type == 'HyperliquidTransaction:ApproveAgent' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" +} +``` + +### Deny signing of `NO_OP` keccak256 payloads + +```json +{ + "policyName": "Deny NO_OP hash signing", + "effect": "EFFECT_DENY", + "condition": "activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2' && activity.params.hash_function == 'HASH_FUNCTION_NO_OP' && activity.params.encoding != 'PAYLOAD_ENCODING_EIP712'" +} +``` + +#### Allow signing of EIP-712 payloads for EIP-3009 Transfers + +```json +{ + "policyName": "Allow signing of EIP-712 payloads for EIP-3009 Transfers for USD Coin", + "effect": "EFFECT_ALLOW", + "condition": "eth.eip_712.domain.name == 'USD Coin' && eth.eip_712.primary_type == 'TransferWithAuthorization' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" +} +``` + +#### Allow signing of EIP-712 payloads for EIP-2612 Permits for USD Coin + +```json +{ + "policyName": "Allow signing of EIP-712 payloads for EIP-2612 Permits for USD Coin", + "effect": "EFFECT_ALLOW", + "condition": "eth.eip_712.domain.name == 'USD Coin' && eth.eip_712.primary_type == 'Permit' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" +} +``` + +#### Allow signing of EIP-7702 Authorizations + +```json +{ + "policyName": "Allow signing of EIP-7702 Authorizations", + "effect": "EFFECT_ALLOW", + "condition": "eth.eip_7702_authorization.address == '
' && eth.eip_7702_authorization.chain_id == '' && eth.eip_7702_authorization.nonce == '' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" +} +``` diff --git a/concepts/policies/language.mdx b/concepts/policies/language.mdx index c33112ee..b0ca143e 100644 --- a/concepts/policies/language.mdx +++ b/concepts/policies/language.mdx @@ -38,19 +38,19 @@ Keywords are reserved words that are dynamically interchanged for real values at ### Condition -| Keyword | Type | Description | -| ------------------------------ | -------------------- | ---------------------------------------------------------------------------- | -| **activity** | Activity | The activity metadata of the request | -| **eth.tx** | EthereumTransaction | The parsed Ethereum transaction payload (see Appendix below) | -| **eth.eip_712** | Eip712TypedData | EIP-712 Typed Data (see Appendix below) | -| **eth.eip_7702_authorization** | Eip7702Authorization | EIP-7702 Authorization (see Appendix below) | -| **solana.tx** | SolanaTransaction | The parsed Solana transaction payload (see Appendix below) | -| **tron.tx** | TronTransaction | The parsed Tron transaction payload (see Appendix below) | -| **bitcoin.tx** | BitcoinTransaction | The parsed Bitcoin transaction payload (see Appendix below) | -| **wallet** | Wallet | The target wallet used in sign + export requests | -| **wallets** | list\ | The target wallets associated with requests involving with multiple wallets | -| **private_key** | PrivateKey | The target private key used in sign + export requests | -| **wallet_account** | WalletAccount | The target wallet account used in sign + export requests | +| Keyword | Type | Description | +| ------------------------------ | -------------------- | --------------------------------------------------------------------------- | +| **activity** | Activity | The activity metadata of the request | +| **eth.tx** | EthereumTransaction | The parsed Ethereum transaction payload (see Appendix below) | +| **eth.eip_712** | Eip712TypedData | EIP-712 Typed Data (see Appendix below) | +| **eth.eip_7702_authorization** | Eip7702Authorization | EIP-7702 Authorization (see Appendix below) | +| **solana.tx** | SolanaTransaction | The parsed Solana transaction payload (see Appendix below) | +| **tron.tx** | TronTransaction | The parsed Tron transaction payload (see Appendix below) | +| **bitcoin.tx** | BitcoinTransaction | The parsed Bitcoin transaction payload (see Appendix below) | +| **wallet** | Wallet | The target wallet used in sign + export requests | +| **wallets** | list\ | The target wallets associated with requests involving with multiple wallets | +| **private_key** | PrivateKey | The target private key used in sign + export requests | +| **wallet_account** | WalletAccount | The target wallet account used in sign + export requests | ## Types @@ -81,7 +81,7 @@ The language is strongly typed which makes policies easy to author and maintain. | | credential_id | string | The credential ID of a passkey. Note: this is only populated for passkeys (also known as Authenticators within Turnkey resources), not API keys | | | public_key | string | The public key of the credential that approved the request | | **Activity** | type | string | The type of the activity (e.g. ACTIVITY_TYPE_SIGN_TRANSACTION_V2) | -| | resource | string | The resource type the activity targets: `USER`, `PRIVATE_KEY`, `POLICY`, `WALLET`, `ORGANIZATION`, `INVITATION`, `CREDENTIAL`, `CONFIG`, `**RECOVERY`, `AUTH`, `OTP`, `PAYMENT_METHOD`, `SUBSCRIPTION` | +| | resource | string | The resource type the activity targets: `USER`, `PRIVATE_KEY`, `POLICY`, `WALLET`, `ORGANIZATION`, `INVITATION`, `CREDENTIAL`, `CONFIG`, `**RECOVERY`, `AUTH`, `OTP`, `PAYMENT_METHOD`, `SUBSCRIPTION` | | | action | string | The action of the activity: `CREATE`, `UPDATE`, `DELETE`, `SIGN`, `EXPORT`, `IMPORT` | | | params | struct | The parameters of the activity. See [here](#activity-parameters) for more details. | | **Wallet** | id | string | The identifier of the wallet | @@ -134,78 +134,83 @@ The language is strongly typed which makes policies easy to author and maintain. | | outputs | list\ | All outputs created by this Bitcoin transaction | | | locktime | BitcoinTxLocktime | The locktime of this bitcoin transaction | - The `ContractArgument` type, used in documentation for ABI an IDL arguments represents an enum indicating this type could be any one of the string, number, array or struct types listed in our Primitives section. + + {" "} + The `ContractArgument` type, used in documentation for ABI an IDL arguments + represents an enum indicating this type could be any one of the string, + number, array or struct types listed in our Primitives section.{" "} + #### Nested Structs -| Struct | Field | Type | Description | -| ------------------------------- | ----------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Eip712Domain** | name | string | The name | -| | version | string | The version | -| | chain_id | uint | The chain ID | -| | verifying_contract | string (case insensitive) | The address of the verifying contract | -| **Instruction** | program_key | string | The program (public key) involved in the instruction | -| | accounts | list\ | A list of Accounts involved in the instruction | -| | instruction_data_hex | string | Raw hex bytes corresponding to instruction data | -| | address_table_lookups | list\ | A list of AddressTableLookups used in the instruction. | -| | parsed_instruction_data | Option\ | IDL related field specifying all additional information for an instruction calling a program for which an IDL has been uploaded | -| **Transfer** | from | string | A Solana account (public key) representing the sender of the transfer | -| | to | string | A Solana account (public key) representing the recipient of the transfer | -| | amount | int | The native SOL amount for the transfer (lamports). Only transfers executed by direct calls to system programs are recognized, transfers performed indirectly inside other programs or via unsupported System Program instructions are not included. | -| **SPLTransfer** | from | string | A Solana account (public key) representing the token account that is sending tokens in this SPL transfer | -| | to | string | A Solana account (public key) representing the token account that is receiving tokens in this SPL transfer | +| Struct | Field | Type | Description | +| ------------------------------- | ----------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Eip712Domain** | name | string | The name | +| | version | string | The version | +| | chain_id | uint | The chain ID | +| | verifying_contract | string (case insensitive) | The address of the verifying contract | +| **Instruction** | program_key | string | The program (public key) involved in the instruction | +| | accounts | list\ | A list of Accounts involved in the instruction | +| | instruction_data_hex | string | Raw hex bytes corresponding to instruction data | +| | address_table_lookups | list\ | A list of AddressTableLookups used in the instruction. | +| | parsed_instruction_data | Option\ | IDL related field specifying all additional information for an instruction calling a program for which an IDL has been uploaded | +| **Transfer** | from | string | A Solana account (public key) representing the sender of the transfer | +| | to | string | A Solana account (public key) representing the recipient of the transfer | +| | amount | int | The native SOL amount for the transfer (lamports). Only transfers executed by direct calls to system programs are recognized, transfers performed indirectly inside other programs or via unsupported System Program instructions are not included. | +| **SPLTransfer** | from | string | A Solana account (public key) representing the token account that is sending tokens in this SPL transfer | +| | to | string | A Solana account (public key) representing the token account that is receiving tokens in this SPL transfer | | | amount | int | The amount (noted in raw atomic units) of this SPL transfer. Only parsed for top-level SPL transfers using `Transfer`, `TransferChecked`, or `TransferCheckedWithFee`. Transfers performed through other Token / Token-2022 instructions are not included. | -| | owner | string | A Solana account (public key) representing the owner of the sending token account for this SPL transfer | -| | signers | list\ | A list of Solana accounts (public keys) representing the multisig signers (if they exist) for this SPL transfer | -| | token_mint | string | A Solana account (public key) representing the token mint of the token being transferred in this SPL transfer | -| **Account** | account_key | string | A Solana account (public key) | -| | signer | boolean | An indicator of whether or not the account is a signer | -| | writable | boolean | An indicator of whether or not the account can perform a write operation | -| **AddressTableLookup** | address_table_key | string | A Solana address (public key) corresponding to the address table | -| | writable_indexes | list\ | Indexes corresponding to accounts that can perform writes | -| | readonly_indexes | list\ | Indexes corresponding to accounts that can only perform reads | -| **SolanaParsedInstructionData** | instruction_name | string | IDL related field specifying the name of the instruction being called | -| | discriminator | string | IDL related field specifying the byte discriminator denoting which instruction is being called by the instruction call data | -| | named_account | map\ | IDL related field specifying a mapping of account names to the account string, with the names as defined by the program IDL | -| | program_call_args | map\ | IDL related field specifying a mapping of account names to the account string, with the names as defined by the program IDL | -| **TronContract** | type | string | The contract type, a complete list can be found in the Tron Protocol Documentation | -| | permission_id | int | The transaction permission type | -| | owner_address | string | The address of the caller of the transaction | -| | to_address | string | The address of the recipient (Only available for TransferContract's) | -| | amount | int | The amount of TRX to send (Only available for TransferContract's) | -| | contract_address | string | The address of the contract being called (Only available for TriggerSmartContract's) | -| | call_value | int | The amount of TRX passed to the contract (Only available for TriggerSmartContract's) | -| | data | string | The function selector, and the functions parameters of the contract (Only available for TriggerSmartContract's) | -| | call_token_value | int | The amount of a TRC-10 token passed to the contract (Only available for TriggerSmartContract's) | -| | token_id | int | The TRC-10 token id (Only available for TriggerSmartContract's) | -| | resource | string | The resource to delegate/undelegate will be "ENERGY" or "BANDWIDTH" (Only available for Delegate, UnDelegate, FreezeBalanceV2, UnfreezeBalanceV2 contract's) | -| | balance | int | The amount of sun (1,000,000 sun = 1 TRX) staked for resources to be delegated (Only available for DelegateContract and UnDelegateContract) | -| | receiver_address | string | The resource receiver address (Only available for DelegateContract and UnDelegateContract) | -| | lock | bool | Indicates if the delegated resources are locked or not. If true resources cannot be undelegated within the lock_period (Only available for DelegateContract's) | -| | lock_period | int | The time, in blocks, of how long the delegation is locked, only valid when lock is true (Only available for DelegateContract's) | -| | frozen_balance | int | The amount of sun (1,000,000 sun = 1 TRX) to be frozen (Only available for FreezeBalanceV2Contract's) | -| | unfreeze_balance | int | The amount of sun (1,000,000 sun = 1 TRX) to unfreeze (Only available for UnfreezeBalanceV2Contract's) | -| | owner | TronPermission | The owner permission of the account (Only available for AccountPermissionUpdateContract's) | -| | witness | TronPermission | The witness permission of the account (Only available for AccountPermissionUpdateContract's) | -| | actives | list\ | A list of active permissions for the account (Only available for AccountPermissionUpdateContract's) | -| **TronPermission** | type | string | The permission type either "Owner", "Witness", or "Active" | -| | id | int | The permission id Owner = 0, Witness = 1, Active = 2+n where n is the 0 indexed active permission number | -| | permission_name | string | The name of the permission | -| | threshold | int | The operation is allowed only when the sum of the weights of the participating signatures exceeds the domain value. Requires a maximum value less than the Long type (int64). | -| | parent_id | int | The parent id, currently always 0 | -| | operations | String | Hex encoded 32 bytes (256 bits), each bit represents the authority of a contract, a 1 means the authority to own the contract | -| | keys | TronKey | A list of address's and weight's that jointly own the permission can be up to 5 keys. | -| **TronKey** | address | string | The address authorized for a specific TronPermission | -| | weight | int | The weight of this address's signature for this permission, used to reach "threshold" in a TronPermission | -| **BitcoinTxInput** | tx_id | string | The transaction id of the Bitcoin transaction that created the output that is being spent by this input | -| | vout | int | The index in the output array on the Bitcoin transaction that created the output being spent by this input | -| | sequence | int | The sequence field on this input which is set whether the transaction can be replaced or when it can be mined | -| **BitcoinTxOutput** | value | int | The value of this output in Satoshis | -| | script_pubkey | string | The locking code for this transaction output | -| | address | string | The on chain address representation for this transaction output | -| | address_type | string (case insensitive) | The address derivation type of the address for this transaction output | -| **BitcoinTxLocktime** | amount | int | The amount represented in this transaction's locktime | -| | type | string | The type of locktime represented (either 'Seconds' or 'Blocks') | +| | owner | string | A Solana account (public key) representing the owner of the sending token account for this SPL transfer | +| | signers | list\ | A list of Solana accounts (public keys) representing the multisig signers (if they exist) for this SPL transfer | +| | token_mint | string | A Solana account (public key) representing the token mint of the token being transferred in this SPL transfer | +| **Account** | account_key | string | A Solana account (public key) | +| | signer | boolean | An indicator of whether or not the account is a signer | +| | writable | boolean | An indicator of whether or not the account can perform a write operation | +| **AddressTableLookup** | address_table_key | string | A Solana address (public key) corresponding to the address table | +| | writable_indexes | list\ | Indexes corresponding to accounts that can perform writes | +| | readonly_indexes | list\ | Indexes corresponding to accounts that can only perform reads | +| **SolanaParsedInstructionData** | instruction_name | string | IDL related field specifying the name of the instruction being called | +| | discriminator | string | IDL related field specifying the byte discriminator denoting which instruction is being called by the instruction call data | +| | named_account | map\ | IDL related field specifying a mapping of account names to the account string, with the names as defined by the program IDL | +| | program_call_args | map\ | IDL related field specifying a mapping of account names to the account string, with the names as defined by the program IDL | +| **TronContract** | type | string | The contract type, a complete list can be found in the Tron Protocol Documentation | +| | permission_id | int | The transaction permission type | +| | owner_address | string | The address of the caller of the transaction | +| | to_address | string | The address of the recipient (Only available for TransferContract's) | +| | amount | int | The amount of TRX to send (Only available for TransferContract's) | +| | contract_address | string | The address of the contract being called (Only available for TriggerSmartContract's) | +| | call_value | int | The amount of TRX passed to the contract (Only available for TriggerSmartContract's) | +| | data | string | The function selector, and the functions parameters of the contract (Only available for TriggerSmartContract's) | +| | call_token_value | int | The amount of a TRC-10 token passed to the contract (Only available for TriggerSmartContract's) | +| | token_id | int | The TRC-10 token id (Only available for TriggerSmartContract's) | +| | resource | string | The resource to delegate/undelegate will be "ENERGY" or "BANDWIDTH" (Only available for Delegate, UnDelegate, FreezeBalanceV2, UnfreezeBalanceV2 contract's) | +| | balance | int | The amount of sun (1,000,000 sun = 1 TRX) staked for resources to be delegated (Only available for DelegateContract and UnDelegateContract) | +| | receiver_address | string | The resource receiver address (Only available for DelegateContract and UnDelegateContract) | +| | lock | bool | Indicates if the delegated resources are locked or not. If true resources cannot be undelegated within the lock_period (Only available for DelegateContract's) | +| | lock_period | int | The time, in blocks, of how long the delegation is locked, only valid when lock is true (Only available for DelegateContract's) | +| | frozen_balance | int | The amount of sun (1,000,000 sun = 1 TRX) to be frozen (Only available for FreezeBalanceV2Contract's) | +| | unfreeze_balance | int | The amount of sun (1,000,000 sun = 1 TRX) to unfreeze (Only available for UnfreezeBalanceV2Contract's) | +| | owner | TronPermission | The owner permission of the account (Only available for AccountPermissionUpdateContract's) | +| | witness | TronPermission | The witness permission of the account (Only available for AccountPermissionUpdateContract's) | +| | actives | list\ | A list of active permissions for the account (Only available for AccountPermissionUpdateContract's) | +| **TronPermission** | type | string | The permission type either "Owner", "Witness", or "Active" | +| | id | int | The permission id Owner = 0, Witness = 1, Active = 2+n where n is the 0 indexed active permission number | +| | permission_name | string | The name of the permission | +| | threshold | int | The operation is allowed only when the sum of the weights of the participating signatures exceeds the domain value. Requires a maximum value less than the Long type (int64). | +| | parent_id | int | The parent id, currently always 0 | +| | operations | String | Hex encoded 32 bytes (256 bits), each bit represents the authority of a contract, a 1 means the authority to own the contract | +| | keys | TronKey | A list of address's and weight's that jointly own the permission can be up to 5 keys. | +| **TronKey** | address | string | The address authorized for a specific TronPermission | +| | weight | int | The weight of this address's signature for this permission, used to reach "threshold" in a TronPermission | +| **BitcoinTxInput** | tx_id | string | The transaction id of the Bitcoin transaction that created the output that is being spent by this input | +| | vout | int | The index in the output array on the Bitcoin transaction that created the output being spent by this input | +| | sequence | int | The sequence field on this input which is set whether the transaction can be replaced or when it can be mined | +| **BitcoinTxOutput** | value | int | The value of this output in Satoshis | +| | script_pubkey | string | The locking code for this transaction output | +| | address | string | The on chain address representation for this transaction output | +| | address_type | string (case insensitive) | The address derivation type of the address for this transaction output | +| **BitcoinTxLocktime** | amount | int | The amount represented in this transaction's locktime | +| | type | string | The type of locktime represented (either 'Seconds' or 'Blocks') | ## Activity Breakdown @@ -230,7 +235,7 @@ The language is strongly typed which makes policies easy to author and maintain. | | IMPORT | ACTIVITY_TYPE_IMPORT_WALLET | | | DELETE | ACTIVITY_TYPE_DELETE_WALLETS | | | UPDATE | ACTIVITY_TYPE_UPDATE_WALLET | -| | DELETE | ACTIVITY_TYPE_DELETE_WALLET_ACCOUNTS | +| | DELETE | ACTIVITY_TYPE_DELETE_WALLET_ACCOUNTS | | **PRIVATE_KEY** | CREATE | ACTIVITY_TYPE_CREATE_PRIVATE_KEYS_V2 | | | CREATE | ACTIVITY_TYPE_CREATE_PRIVATE_KEY_TAG | | | UPDATE | ACTIVITY_TYPE_UPDATE_PRIVATE_KEY_TAG | @@ -265,7 +270,7 @@ The language is strongly typed which makes policies easy to author and maintain. | | DELETE | ACTIVITY_TYPE_DELETE_PAYMENT_METHOD | | **SUBSCRIPTION** | CREATE | ACTIVITY_TYPE_ACTIVATE_BILLING_TIER | | **CONFIG** | UPDATE | ACTIVITY_TYPE_UPDATE_ALLOWED_ORIGINS | -| ****RECOVERY** | CREATE | ACTIVITY_TYPE_INIT_USER_EMAIL_RECOVERY_V2 | +| \***\*RECOVERY** | CREATE | ACTIVITY_TYPE_INIT_USER_EMAIL_RECOVERY_V2 | | **AUTH** | CREATE | ACTIVITY_TYPE_EMAIL_AUTH_V3 | | | CREATE | ACTIVITY_TYPE_INIT_OTP_AUTH_V3 | | | CREATE | ACTIVITY_TYPE_OTP_AUTH | @@ -283,7 +288,6 @@ The language is strongly typed which makes policies easy to author and maintain. | **AUTH_PROXY** | CREATE | ACTIVITY_TYPE_ENABLE_AUTH_PROXY | | | DELETE | ACTIVITY_TYPE_DISABLE_AUTH_PROXY | | | UPDATE | ACTIVITY_TYPE_UPDATE_AUTH_PROXY_CONFIG | - ** Legacy features, deprecated in the latest SDKs. @@ -293,27 +297,27 @@ The language is strongly typed which makes policies easy to author and maintain. Note that our policy engine does not short circuit during evaluation. In practice, this means that if any clause within the `condition` field of a policy results in an error, the evaluated `outcome` for that policy will be an error. In such cases, consider breaking up complex policies into separate policies. -For example, suppose you're looking to construct a policy with the condition `wallet.id == '\' || private_key.id == '\'. This condition will _always_ error out during evaluation, because only one of the two clauses can ever be valid: an activity cannot target both a wallet and private key. That means if you're trying to, say, sign with a private key, then the wallet clause will fail (because a wallet isn't being passed into the policy evaluation). Conversely, if you're trying to sign with a wallet, the private key clause will fail for the same reason. +For example, suppose you're looking to construct a policy with the condition `wallet.id == '\' || private*key.id == '\'. This condition will \_always* error out during evaluation, because only one of the two clauses can ever be valid: an activity cannot target both a wallet and private key. That means if you're trying to, say, sign with a private key, then the wallet clause will fail (because a wallet isn't being passed into the policy evaluation). Conversely, if you're trying to sign with a wallet, the private key clause will fail for the same reason. ### Activity parameters The `activity.params` field exposes specific parameters based on the activity type. Most activities do not expose any parameters, but certain activities expose fields that can be used in policy conditions. -| Category | Activity Type | Field | Type | Description | -|--------------------|------------------------------------|---------------|-------------------|------------------------------------------------------------------------| -| Signing | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD` | `hash_function` | string | The hash function used (e.g., `HASH_FUNCTION_NO_OP`, `HASH_FUNCTION_KECCAK256`) | -| | | `encoding` | string | The payload encoding (e.g., `PAYLOAD_ENCODING_HEXADECIMAL`, `PAYLOAD_ENCODING_EIP712`) | -| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2`| `hash_function` | string | The hash function used | -| | | `encoding` | string | The payload encoding | -| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOADS` | `hash_function` | string | The hash function used | -| | | `encoding` | string | The payload encoding | -| | `ACTIVITY_TYPE_SIGN_TRANSACTION_V2`| `type` | string | The transaction type (e.g., `TRANSACTION_TYPE_ETHEREUM, TRANSACTION_TYPE_SOLANA, TRANSACTION_TYPE_BITCOIN, TRANSACTION_TYPE_TRON, TRANSACTION_TYPE_TEMPO`) | -| User Management | `ACTIVITY_TYPE_DELETE_USERS` | `user_ids` | list | The list of user IDs being deleted | -| Wallet Management | `ACTIVITY_TYPE_UPDATE_WALLET` | `wallet_id` | string | The ID of the wallet being updated | -| Authentication | `ACTIVITY_TYPE_INIT_OTP_AUTH_V2` | `otp_length` | int | The length of the OTP code | -| | `ACTIVITY_TYPE_INIT_OTP_AUTH_V3` | `otp_length` | int | The length of the OTP code | - -#### Example usage +| Category | Activity Type | Field | Type | Description | +| ----------------- | ----------------------------------- | --------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Signing | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD` | `hash_function` | string | The hash function used (e.g., `HASH_FUNCTION_NO_OP`, `HASH_FUNCTION_KECCAK256`) | +| | | `encoding` | string | The payload encoding (e.g., `PAYLOAD_ENCODING_HEXADECIMAL`, `PAYLOAD_ENCODING_EIP712`) | +| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2` | `hash_function` | string | The hash function used | +| | | `encoding` | string | The payload encoding | +| | `ACTIVITY_TYPE_SIGN_RAW_PAYLOADS` | `hash_function` | string | The hash function used | +| | | `encoding` | string | The payload encoding | +| | `ACTIVITY_TYPE_SIGN_TRANSACTION_V2` | `type` | string | The transaction type (one of `[TRANSACTION_TYPE_ETHEREUM, TRANSACTION_TYPE_SOLANA, TRANSACTION_TYPE_BITCOIN, TRANSACTION_TYPE_TRON, TRANSACTION_TYPE_TEMPO`] | +| User Management | `ACTIVITY_TYPE_DELETE_USERS` | `user_ids` | list\ | The list of user IDs being deleted | +| Wallet Management | `ACTIVITY_TYPE_UPDATE_WALLET` | `wallet_id` | string | The ID of the wallet being updated | +| Authentication | `ACTIVITY_TYPE_INIT_OTP_AUTH_V2` | `otp_length` | int | The length of the OTP code | +| | `ACTIVITY_TYPE_INIT_OTP_AUTH_V3` | `otp_length` | int | The length of the OTP code | + +#### Examples To deny signing raw payloads with the NO_OP hash function (except for EIP-712): @@ -364,7 +368,6 @@ Here are some approaches you might take to govern transfers: - _Just one_ transfer needs to match the policy condition. Useful for blocklists ([example](/concepts/policies/examples/solana#deny-all-solana-transactions-transferring-to-an-undesired-address)) - Only match if there is a _single_ transfer in the transaction, _and_ that transfer meets the criteria ([example](/concepts/policies/examples/solana#allow-solana-transactions-that-have-exactly-one-transfer,-to-one-specific-recipient)). This is the most secure approach, and thus most restrictive. - Address table lookups: Solana transactions can reference onchain address lookup tables. Turnkey currently surfaces any address pulled from a lookup table as the literal string `ADDRESS_TABLE_LOOKUP` in Solana address fields (`account_keys`, `program_keys`, instruction accounts, `transfers`, and `spl_transfers`). The `solana.tx.address_table_lookups` array indicates when lookups are present, but the specific addresses are not resolved. If you rely on allowlists or denylists of addresses, add a guard for this placeholder (for example, require `solana.tx.address_table_lookups.count == 0` before comparing addresses, or explicitly deny when `ADDRESS_TABLE_LOOKUP` appears with something like `solana.tx.transfers.any(t, t.to == 'ADDRESS_TABLE_LOOKUP')`) so that dynamic lookups cannot bypass or unexpectedly fail your policy. See the [address table lookup examples](/concepts/policies/examples/solana#deny-all-address-table-lookups) for examples on how to enforce this. See the [Solana policy examples](/concepts/policies/examples/solana) for sample scenarios. diff --git a/docs.json b/docs.json index 968feb5f..0afd6149 100644 --- a/docs.json +++ b/docs.json @@ -262,7 +262,8 @@ "concepts/policies/examples/ethereum", "concepts/policies/examples/solana", "concepts/policies/examples/tron", - "concepts/policies/examples/bitcoin" + "concepts/policies/examples/bitcoin", + "concepts/policies/examples/tempo" ] }, { From 9a5cbe1dc90d7421786d33d52eb12b995970972f Mon Sep 17 00:00:00 2001 From: Andrew Min Date: Wed, 4 Feb 2026 23:48:14 -0500 Subject: [PATCH 3/3] other changes --- concepts/policies/examples/tempo.mdx | 65 +++++++++++++--------------- concepts/policies/language.mdx | 27 ++++++++++++ 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/concepts/policies/examples/tempo.mdx b/concepts/policies/examples/tempo.mdx index 985f01e1..ef02b9e9 100644 --- a/concepts/policies/examples/tempo.mdx +++ b/concepts/policies/examples/tempo.mdx @@ -4,88 +4,85 @@ description: "This page provides examples of policies governing Tempo signing." sidebarTitle: "Tempo" --- -Note: see the [language section](/concepts/policies/language#ethereum) for more details. +Note: see the [language section](/concepts/policies/language#tempo) for more details. -#### Allow ABI-specific contract call parameters +## Overview -See [here](../../../concepts/policies/smart-contract-interfaces) for more information and examples. +Tempo transactions have limited policy support compared to other blockchain types. Unlike Ethereum, Solana, Bitcoin, and Tron, Tempo does not expose transaction-specific fields (such as `tempo.tx`) in the policy engine. The primary way to govern Tempo transactions is through the `activity.params.type` field and other general policy attributes. -#### Allow ERC-20 transfers for a specific token smart contract +## Policy Examples + +#### Allow Tempo transactions ```json { - "policyName": "Enable ERC-20 transfers for ", + "policyName": "Allow Tempo transactions", "effect": "EFFECT_ALLOW", - "condition": "eth.tx.to == '' && eth.tx.data[0..10] == '0xa9059cbb'" + "condition": "activity.type == 'ACTIVITY_TYPE_SIGN_TRANSACTION_V2' && activity.params.type == 'TRANSACTION_TYPE_TEMPO'" } ``` -#### Allow anyone to sign transactions for testnet (Sepolia) +#### Deny Tempo transactions ```json { - "policyName": "Allow signing ethereum sepolia transactions", - "effect": "EFFECT_ALLOW", - "condition": "eth.tx.chain_id == 11155111" + "policyName": "Deny Tempo transactions", + "effect": "EFFECT_DENY", + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO'" } ``` -#### Allow ETH transactions with a specific nonce range +#### Allow Tempo transactions for a specific wallet ```json { - "policyName": "Allow signing Ethereum transactions with an early nonce", + "policyName": "Allow Tempo transactions for specific wallet", "effect": "EFFECT_ALLOW", - "condition": "eth.tx.nonce <= 3" + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO' && wallet.id == ''" } ``` -#### Allow signing of EIP-712 payloads for Hyperliquid `ApproveAgent` operations +#### Allow Tempo transactions for a specific private key ```json { - "policyName": "Allow signing of EIP-712 Payloads for Hyperliquid `ApproveAgent` operations", + "policyName": "Allow Tempo transactions for specific private key", "effect": "EFFECT_ALLOW", - "condition": "eth.eip_712.domain.name == 'HyperliquidSignTransaction' && eth.eip_712.primary_type == 'HyperliquidTransaction:ApproveAgent' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO' && private_key.id == ''" } ``` -### Deny signing of `NO_OP` keccak256 payloads +#### Require specific approver for Tempo transactions ```json { - "policyName": "Deny NO_OP hash signing", - "effect": "EFFECT_DENY", - "condition": "activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2' && activity.params.hash_function == 'HASH_FUNCTION_NO_OP' && activity.params.encoding != 'PAYLOAD_ENCODING_EIP712'" + "policyName": "Require specific approver for Tempo transactions", + "effect": "EFFECT_ALLOW", + "consensus": "approvers.any(user, user.id == '')", + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO'" } ``` -#### Allow signing of EIP-712 payloads for EIP-3009 Transfers +#### Allow Tempo transactions for wallets with a specific label ```json { - "policyName": "Allow signing of EIP-712 payloads for EIP-3009 Transfers for USD Coin", + "policyName": "Allow Tempo transactions for wallets with specific label", "effect": "EFFECT_ALLOW", - "condition": "eth.eip_712.domain.name == 'USD Coin' && eth.eip_712.primary_type == 'TransferWithAuthorization' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO' && wallet.label == 'tempo-production'" } ``` -#### Allow signing of EIP-712 payloads for EIP-2612 Permits for USD Coin +#### Allow Tempo transactions for private keys with a specific tag ```json { - "policyName": "Allow signing of EIP-712 payloads for EIP-2612 Permits for USD Coin", + "policyName": "Allow Tempo transactions for private keys with tag", "effect": "EFFECT_ALLOW", - "condition": "eth.eip_712.domain.name == 'USD Coin' && eth.eip_712.primary_type == 'Permit' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO' && private_key.tags.contains('tempo-enabled')" } ``` -#### Allow signing of EIP-7702 Authorizations +## Legacy Ethereum Transactions on Tempo -```json -{ - "policyName": "Allow signing of EIP-7702 Authorizations", - "effect": "EFFECT_ALLOW", - "condition": "eth.eip_7702_authorization.address == '
' && eth.eip_7702_authorization.chain_id == '' && eth.eip_7702_authorization.nonce == '' && activity.type == 'ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2'" -} -``` +During Tempo's testnet period, [legacy Ethereum transactions](https://docs.tempo.xyz/quickstart/evm-compatibility#transaction-differences) are also supported on Tempo and can be governed using standard Ethereum policy syntax. See the [Ethereum policy examples](/concepts/policies/examples/ethereum) for more details on how to write policies for these transaction types. diff --git a/concepts/policies/language.mdx b/concepts/policies/language.mdx index b0ca143e..70f19f22 100644 --- a/concepts/policies/language.mdx +++ b/concepts/policies/language.mdx @@ -395,3 +395,30 @@ NOTE: While our `SIGN TRANSACTION` endpoint takes in a Partially Signed Bitcoin For further reference on how Turnkey handles Bitcoin transactions in our policy-enabled transaction signing flow, check out this section in our [Bitcoin Network Support](/networks/bitcoin#policy-enabled-bitcoin-transaction-signing) page. See the [Bitcoin policy examples](/concepts/policies/examples/bitcoin) for sample Bitcoin policies. + +### Tempo + +Tempo has a limited set of applicable policies. Unlike other blockchain types (Ethereum, Solana, Bitcoin, Tron), Tempo transactions do not have their own namespace keyword (such as `tempo.tx`) or exposed transaction fields for policy evaluation. + +The primary way to govern Tempo transactions is by targeting the transaction type itself using the `activity.params.type` field: + +```json +{ + "policyName": "Allow Tempo transactions", + "effect": "EFFECT_ALLOW", + "condition": "activity.type == 'ACTIVITY_TYPE_SIGN_TRANSACTION_V2' && activity.params.type == 'TRANSACTION_TYPE_TEMPO'" +} +``` + +Or to deny Tempo transactions: + +```json +{ + "policyName": "Deny Tempo transactions", + "effect": "EFFECT_DENY", + "condition": "activity.params.type == 'TRANSACTION_TYPE_TEMPO'" +} +``` + +Since Tempo transactions don't expose granular transaction details in the policy engine, governance must rely on other available policy fields such as targeting specific wallets, private keys, or requiring specific approvers through consensus rules. +