Skip to main content

Documentation Index

Fetch the complete documentation index at: https://turnkey-0e7c1f5b-graham-docs-revamp.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers the key implementation decisions for securing smart contract operations, then walks through the contract lifecycle end-to-end: permissioned deployment, function-level interaction policies, and minimizing risk during upgrades. For basic signing, start with the Quickstart.

Powered by Turnkey

Teams managing high-value contracts use Turnkey for policy-enforced signing at every stage:
  • Polymarket transitioned to Turnkey for secure contract interactions at scale, including Safe deployment and gasless trading infrastructure.
  • Brale uses Turnkey’s policy engine to constrain stablecoin minting and transfer operations to an allowlist of approved contracts.
Turnkey also fits existing contract deployments. You don’t need to redeploy to start managing signing through Turnkey.

Key implementation decisions

DecisionWhat to consider
Role isolationSeparate wallets for deployer, operator, and upgrade owner so each role has its own keys and scoped policies
Deployment permissionsPolicy scoping the deployer to a specific wallet and network
Function-level controlsPolicies targeting specific contract addresses and function signatures, or upload the ABI for full parsing
Value thresholdsAutomated approval for routine operations (small mints), multi-party consensus for large ones
Upgrade controlsGrant upgrade permissions only when needed via temporary policies, then revoke immediately after

Example: stablecoins and RWAs

Stablecoins and tokenized Real-World Assets often secure billions of dollars in value. These contracts typically involve minting, burning, pausing, and upgrades, each requiring different levels of authorization.
NeedHow Turnkey solves it
Minting must be constrained to approved limitsPolicies can bound mint amounts and require multi-party approval above a threshold via smart contract interfaces
Only authorized addresses can receive new supplyPolicies target specific contract addresses and function arguments, restricting who the mint recipient can be
Must support emergency pauseA dedicated pause operator wallet with a scoped policy for the pause function, separate from day-to-day minting
Upgrades must not introduce unreviewed logicTemporary upgrade policies with multi-party consensus, revoked immediately after execution
Full audit trail of every operationEvery signing action is an activity in Turnkey with a complete audit log of who approved what and when
rbac

Implementation steps

This guide assumes you’ve completed the Quickstart and have a Turnkey client initialized. If not, start there first.
1

Permissioned contract deployment

Deployment is strictly permissioned. A designated deployer user must have a policy that explicitly grants them permission to sign the deployment transaction for a specific wallet and network.
{
  "policyName": "Allow deployer to deploy on Base",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(user, user.id == '<DEPLOYER_USER_ID>')",
  "condition": "activity.action == 'SIGN' && wallet.id == '<DEPLOYER_WALLET_ID>' && eth.tx.to == '' && eth.tx.chain_id == '8453'"
}
The eth.tx.to == '' condition matches contract creation transactions (where the to field is empty). Combined with a chain ID constraint, this ensures the deployer can only deploy on the intended network.
2

Function-level interaction policies

Once deployed, interactions like minting new token supply are governed by precise policies. For example, a minting policy can be configured to:
  • Allow a specific operator user to sign
  • Require the transaction to originate from a designated wallet
  • Target the deployed contract’s exact address
  • Call only the mint function
{
  "policyName": "Allow minter to mint tokens",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(user, user.id == '<MINTER_USER_ID>')",
  "condition": "activity.action == 'SIGN' && wallet.id == '<MINTER_WALLET_ID>' && eth.tx.to == '<CONTRACT_ADDRESS>' && eth.tx.data[0..10] == '0x40c10f19'"
}
The 0x40c10f19 value is the function selector for mint(address,uint256). For production use, the recommended approach is to upload the contract ABI so policies can reference function names and constrain arguments directly (e.g., bounding mint amounts or requiring multi-party approval above a threshold).
3

Safe contract upgrades

Upgrading a contract redirects the underlying implementation logic, making it one of the most sensitive operations. Turnkey minimizes the risk window:
  1. Designate a specific upgrade owner wallet (separate from the operator wallet).
  2. Create a temporary, highly restrictive policy only when an upgrade is needed, granting the upgrade owner permission to sign the upgrade transaction.
  3. Once the upgrade is complete, remove the policy. The wallet remains dormant and secured until the next upgrade.
{
  "policyName": "Temporary: allow upgrade owner to upgrade proxy",
  "effect": "EFFECT_ALLOW",
  "consensus": "approvers.any(user, user.id == '<UPGRADE_OWNER_ID>')",
  "condition": "activity.action == 'SIGN' && wallet.id == '<UPGRADE_WALLET_ID>' && eth.tx.to == '<PROXY_ADDRESS>' && eth.tx.data[0..10] == '0x3659cfe6'"
}
The 0x3659cfe6 selector matches upgradeTo(address). After the upgrade completes, delete this policy to close the window.
This lifecycle is further explored in the Smart Contract Management demo on GitHub.

Next steps