Policy examples
Access control
Allow a specific user to create wallets
{
"policyName": "Allow user <USER_ID> to create wallets",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "activity.resource == 'WALLET' && activity.action == 'CREATE'"
}
Allow users with a specific tag to create users
{
"policyName": "Allow user_tag <USER_TAG_ID> to create users",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.tags.contains('<USER_TAG_ID>'))",
"condition": "activity.resource == 'USER' && activity.action == 'CREATE'"
}
Require two users with a specific tag to add policies
{
"policyName": "Require two users with user_tag <USER_TAG_ID> to create policies",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.filter(user, user.tags.contains('<USER_TAG_ID>')).count() >= 2",
"condition": "activity.resource == 'POLICY' && activity.action == 'CREATE'"
}
Deny all delete actions for users with a specific tag
{
"policyName": "Only user_tag <USER_TAG_ID> can take actions",
"effect": "EFFECT_DENY",
"consensus": "approvers.any(user, user.tags.contains('<USER_TAG_ID>'))",
"condition": "activity.action == 'DELETE'"
}
Allow a specific user (e.g. API-only user) to create a sub-org
{
"policyName": "Allow user <USER_ID> to create a sub-org",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<YOUR_API_USER_ID>')",
"condition": "activity.resource == 'ORGANIZATION' && activity.action == 'CREATE'"
}
Allow a specific user to initiate user email recovery
{
"policyName": "Allow user <USER_ID> to initiate user email recovery",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<YOUR_API_USER_ID>')",
"condition": "activity.resource == 'RECOVERY' && activity.action == 'CREATE'"
}
Allow a specific user to initiate email auth
{
"policyName": "Allow user <USER_ID> to initiate email auth",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<YOUR_API_USER_ID>')",
"condition": "activity.resource == 'AUTH' && activity.action == 'CREATE'"
}
Signing control
Allow a specific user to sign transactions with a specific wallet
{
"policyName": "Allow <USER_ID> to sign transactions with <WALLET_ID>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "wallet.id == '<WALLET_ID>'"
}
Allow a specific user to sign transactions with a specific private key
{
"policyName": "Allow <USER_ID> to sign transactions with <PRIVATE_KEY_ID>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "private_key.id == '<PRIVATE_KEY_ID>'"
}
Ethereum (EVM)
Note: see the language section for more details.
Allow ERC-20 transfers for a specific token smart contract
{
"policyName": "Enable ERC-20 transfers for <CONTRACT_ADDRESS>",
"effect": "EFFECT_ALLOW",
"condition": "eth.tx.to == '<CONTRACT_ADDRESS>' && eth.tx.data[0..10] == '0xa9059cbb'"
}
Allow anyone to sign transactions for testnet (Sepolia)
{
"policyName": "Allow signing ethereum sepolia transactions",
"effect": "EFFECT_ALLOW",
"condition": "eth.tx.chain_id == 11155111"
}
Allow ETH transactions with a specific nonce range
{
"policyName": "Allow signing Ethereum transactions with an early nonce",
"effect": "EFFECT_ALLOW",
"condition": "eth.tx.nonce <= 3"
}
Solana
Note: see the language section for various approaches on writing Solana policies.
Allow Solana transactions that include a transfer from one specific sender
{
"policyName": "Enable transactions with a transfer sent by <SENDER_ADDRESS>",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.transfers.all(transfer, transfer.from == '<SENDER_ADDRESS>')"
}
Allow Solana transactions that include a transfer to only one specific recipient
{
"policyName": "Enable transactions with a single transfer sent to <RECIPIENT_ADDRESS>",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.transfers.count == 1 && solana.tx.transfers[0].to == '<RECIPIENT_ADDRESS>'"
}
Allow Solana transactions that have exactly one transfer, to one specific recipient
{
"policyName": "Enable transactions with a transfer sent to <RECIPIENT_ADDRESS>",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.transfers.all(transfer, transfer.to == '<RECIPIENT_ADDRESS>')"
}
Allow Solana transactions that only use the Solana System Program
{
"policyName": "Enable transactions that only use the system program",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.program_keys.all(p, p == '11111111111111111111111111111111')"
}
Deny all Solana transactions transferring to an undesired address
{
"policyName": "Reject transactions with a transfer sent to <BAD_ADDRESS>",
"effect": "EFFECT_DENY",
"condition": "solana.tx.transfers.any(transfer, transfer.to == '<BAD_ADDRESS>')"
}
Allow Solana transactions with specific expected instruction data
{
"policyName": "Enable transactions where the first instruction has precisely <HEX BYTES>",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.instructions[0].instruction_data_hex == '<HEX BYTES>'"
}
Allow Solana transactions whose first instruction involves a specific address
{
"policyName": "Enable transactions where the first instruction has a first account involving <ADDRESS>",
"effect": "EFFECT_ALLOW",
"condition": "solana.tx.instructions[0].accounts[0].account_key == '<ADDRESS>'"
}
Solana -- SPL token transfers
Turnkey’s policy engine supports policies for SPL token transfers. Specifically, we support creating policies for the Transfer
, TransferChecked
and TransferCheckedWithFee
instructions across both the Solana Token Program and the Solana Token 2022 Program.
Some important context for using SPL token policies with Turnkey:
Token Addresses For policies related to the receiving token address of an SPL transfer, the token address receiving the tokens will have to be used, NOT the wallet address that is the owner for the receiving token address. This is because, while both the owning wallet address and the receiving token address are specified in the transfer instruction, the owning wallet address of the recipient token address is not specified. For this we highly recommend using the convention of “associated token addresses” to set policies that, for example, allow SPL token transfers to a particular wallet address.
For further context on associated token addresses check out Solana’s documentation on it: https://spl.solana.com/associated-token-account
An example implementation of using a policy to allow transfers to the associated token address of the intended recipient wallet address can be found in our SDK examples here.
Mint Address Accessibility
The mint account address of the token will only be accessible when the transaction is constructed using instructions that specify the mint address – TransferChecked
and TransferCheckedWithFee
. For transactions constructed using the simple Transfer
method, the mint account will be considered empty.
Example SPL transfer policies
Allow a user to sign Solana transactions that include a single instruction which is an SPL token transfer from a particular sending token address
{
"policyName": "Allow user <USER_ID> to sign Solana transactions that include only a single SPL Transfer FROM <ADDRESS>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "solana.tx.instructions.count() == 1 && solana.tx.spl_transfers.any(transfer, transfer.from == '<ADDRESS>')"
}
Allow a user to sign Solana transactions only if ALL of the instructions are SPL transfers TO a particular token address
{
"policyName": "Allow user <USER_ID> to sign Solana transactions only if ALL of the instructions are SPL transfers TO <ADDRESS>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "solana.tx.instructions.count() == solana.tx.spl_transfers.count() && solana.tx.spl_transfers.all(transfer, transfer.to == '<ADDRESS>')"
}
Allow users with a specific tag to sign Solana transactions only if ALL of the instructions are SPL token transfers with a specific address as the owner of the sending token address
{
"policyName": "Allow users with <USER_TAG> to sign Solana transactions only if ALL of the instructions are SPL token transfers with <ADDRESS> as the owner of the sending token address",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.tags.contains('<USER_TAG_ID>'))",
"condition": "solana.tx.instructions.count() == solana.tx.spl_transfers.count() && solana.tx.spl_transfers.any(transfer, transfer.owner == '<ADDRESS>')"
}
Allow a user to sign Solana transactions that include a single instruction which is an SPL token transfer where the atomic units of the transfer are less than a threshold amount
{
"policyName": "Allow user <USER_ID> to sign Solana transactions that include a single instruction which is an SPL token transfer where the atomic units of the transfer are less than <AMOUNT>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "solana.tx.instructions.count() == 1 && solana.tx.spl_transfers.any(transfer, transfer.amount <AMOUNT>)"
}
Allow a user to sign Solana transactions only if ALL of the instructions are SPL token transfers where the token mint address is a particular address
{
"policyName": "Allow <USER_ID> to sign a Solana transaction only if ALL of the instructions are SPL token transfers where the token mint address is <TOKEN_MINT_ADDRESS>",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "solana.tx.instructions.count() == solana.tx.spl_transfers.count() && solana.tx.spl_transfers.any(transfer, transfer.token_mint == '<TOKEN_MINT_ADDRESS>')"
}
Allow a user to sign Solana transactions that includes a single instruction which is an SPL token transfer where one of the multisig signers of the owner is a particular address
{
"policyName": "Allow <USER_ID> to sign a Solana transaction only if ALL of it's instructions are SPL token transfers where one of the multisig signers of the owner is a particular address",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(user, user.id == '<USER_ID>')",
"condition": "solana.tx.instructions.count() == 1 && solana.tx.spl_transfers.any(transfer, transfer.signers.any(s, s == '<ADDRESS>'))"
}