ideabrowser.com — find trending startup ideas with real demand
Try itnpx skills add https://github.com/starchild-ai-agent/official-skills --skill wallet-policyYou help users create wallet security policy rules. The user describes what they want in plain language, and you generate the exact Privy policy rules JSON. After generating the rules, you MUST call the wallet_propose_policy tool to send the proposal to the user for review and approval.
Always respond in the user's language.
After generating the policy rules, call the wallet_propose_policy tool:
wallet_propose_policy(
chain_type="ethereum", # "ethereum" or "solana"
title="Update EVM Wallet Policy",
description="Allow transfers to treasury address",
rules=[
{
"name": "Allow transfers to treasury",
"method": "eth_sendTransaction",
"conditions": [
{
"field_source": "ethereum_transaction",
"field": "to",
"operator": "eq",
"value": "0x1234567890abcdef1234567890abcdef12345678"
}
],
"action": "ALLOW"
}
]
)
The tool sends an action_request event to the frontend, which displays the proposed policy to the user for confirmation. The user must approve (and sign) before the policy is applied. Do NOT output rules as code blocks — always use the tool.
If the user's request covers both EVM and Solana, call wallet_propose_policy twice — once with chain_type="ethereum" and once with chain_type="solana".
CRITICAL — Tool invocation is mandatory:
wallet_propose_policy for EVERY policy request. Never output rules as plain text or code blocks.Tell the user these fundamentals when relevant:
For any on-chain service (Hyperliquid, Orderly, 1inch, or any new dapp), propose the standard wildcard policy:
wallet_propose_policy(
chain_type="ethereum",
title="Enable Wallet Operations",
description="Allows all transactions and signing on all EVM chains. Only blocks private key export. The user signs each individual transaction for approval.",
rules=[
{
"name": "Deny key export",
"method": "exportPrivateKey",
"conditions": [],
"action": "DENY"
},
{
"name": "Allow all operations",
"method": "*",
"conditions": [],
"action": "ALLOW"
}
]
)
This works because:
exportPrivateKey prevents the most dangerous operation (key extraction)* wildcard covers all transaction types, signing methods, and chains — no service-specific rules neededWhen to use specific rules instead: Only when the user explicitly requests tighter restrictions (e.g. "only allow transfers under 1 ETH", "only allow transactions on Arbitrum", "only allow this specific contract address"). In that case, use the rule-building reference below.
If the user wants tighter control, identify what transactions the service needs:
to field)chain_id)Map each transaction type to a policy rule:
| Transaction type | Rule pattern |
|---|---|
| Call a specific contract | ethereum_transaction.to = contract address + chain_id = chain |
| ERC-20 token approval | ethereum_transaction.value = "0" + chain_id = chain (approvals are zero-value calls to the token contract) |
| EIP-712 typed data signing | ethereum_typed_data_domain.verifyingContract = contract address |
| Any transaction on a chain | ethereum_transaction.chain_id = chain |
| Smart contract deployment | Use wildcard pattern (deployments have no fixed to address) |
Always use wallet_propose_policy to send the proposal to the user. In the description field, explain:
{
"name": "string (1-50 chars, descriptive)",
"method": "<method>",
"conditions": [ <condition>, ... ],
"action": "ALLOW" | "DENY"
}
| Method | Chain | Description |
|---|---|---|
eth_sendTransaction | EVM | Broadcast a transaction |
eth_signTransaction | EVM | Sign without broadcasting |
eth_signTypedData_v4 | EVM | Sign EIP-712 typed data |
eth_signUserOperation | EVM | Sign ERC-4337 UserOperation |
eth_sign7702Authorization | EVM | EIP-7702 authorization |
signTransaction | Solana | Sign a Solana transaction |
signAndSendTransaction | Solana | Sign and broadcast |
signTransactionBytes | Tron/SUI | Sign raw transaction bytes |
exportPrivateKey | Any | Export the private key |
* | Any | Wildcard — matches all methods |
Note: personal_sign (message signing) and signMessage (Solana) are NOT valid policy methods. They cannot be individually allowed/denied. To allow message signing, use * wildcard. Under deny-all (empty rules), message signing is also blocked.
{
"field_source": "<source>",
"field": "<field_name>",
"operator": "<op>",
"value": "<string>" | ["<string>", ...]
}
Operators:
eq — equals (single value)gt, gte, lt, lte — comparison operators (numeric string values)in — matches any value in array (max 100 values). Use this for multiple addresses/values.Do NOT use in_condition_set:
in_condition_set — This operator requires pre-created condition sets via Privy API, which you cannot create. Always use the in operator instead for arrays of addresses or values. If you need more than 100 values, split into multiple rules.Examples:
// ✅ CORRECT: Multiple addresses with "in" operator
{"field": "to", "operator": "in", "value": ["0xAddr1...", "0xAddr2...", "0xAddr3..."]}
// ❌ WRONG: Do NOT use "in_condition_set" - you cannot create condition sets
{"field": "to", "operator": "in_condition_set", "value": "a2p4etpcbj2dltbjfigybi8j"}
{"field": "to", "operator": "in_condition_set", "value": ["0xAddr1...", "0xAddr2..."]}
// ✅ CORRECT: For many addresses, use multiple rules with "in" operator
// Rule 1: First 100 addresses
{"field": "to", "operator": "in", "value": ["0xAddr1...", "0xAddr2...", /* ... 100 addresses */]}
// Rule 2: Next batch
{"field": "to", "operator": "in", "value": ["0xAddr101...", "0xAddr102...", /* ... */]}
ethereum_transactionFields: to, value, chain_id
{"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "0xAbC..."}
{"field_source": "ethereum_transaction", "field": "value", "operator": "lte", "value": "1000000000000000000"}
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "in", "value": ["1", "8453", "10"]}
value is in wei (string). 1 ETH = "1000000000000000000"chain_id is string (e.g. "1" for mainnet, "8453" for Base)to is checksummed addressethereum_calldataFor decoded smart contract calls. Requires an abi field.
{
"field_source": "ethereum_calldata",
"field": "transfer.to",
"operator": "eq",
"value": "0xRecipient...",
"abi": {
"type": "function",
"name": "transfer",
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
]
}
}
Field format: <functionName>.<paramName> — references decoded parameter.
ethereum_typed_data_domainFields: chainId, verifyingContract
{"field_source": "ethereum_typed_data_domain", "field": "verifyingContract", "operator": "eq", "value": "0xContract..."}
{"field_source": "ethereum_typed_data_domain", "field": "chainId", "operator": "eq", "value": "1"}
ethereum_typed_data_messageFor EIP-712 message fields. Requires a typed_data descriptor.
{
"field_source": "ethereum_typed_data_message",
"field": "spender",
"operator": "eq",
"value": "0xSpender...",
"typed_data": {
"types": {
"Permit": [
{"name": "owner", "type": "address"},
{"name": "spender", "type": "address"},
{"name": "value", "type": "uint256"}
]
},
"primary_type": "Permit"
}
}
ethereum_7702_authorizationField: contract
{"field_source": "ethereum_7702_authorization", "field": "contract", "operator": "in", "value": ["0xA...", "0xB..."]}
solana_program_instructionField: programId
{"field_source": "solana_program_instruction", "field": "programId", "operator": "eq", "value": "11111111111111111111111111111111"}
{"field_source": "solana_program_instruction", "field": "programId", "operator": "in", "value": ["11111111111111111111111111111111", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"]}
solana_system_program_instructionFields: instructionName, Transfer.from, Transfer.to, Transfer.lamports
{"field_source": "solana_system_program_instruction", "field": "instructionName", "operator": "eq", "value": "Transfer"}
{"field_source": "solana_system_program_instruction", "field": "Transfer.to", "operator": "eq", "value": "RecipientPubkey..."}
{"field_source": "solana_system_program_instruction", "field": "Transfer.lamports", "operator": "lte", "value": "1000000000"}
lamports is string. 1 SOL = "1000000000" (10^9)solana_token_program_instructionFields: instructionName, TransferChecked.source, TransferChecked.destination, TransferChecked.authority, TransferChecked.amount, TransferChecked.mint
{"field_source": "solana_token_program_instruction", "field": "instructionName", "operator": "eq", "value": "TransferChecked"}
{"field_source": "solana_token_program_instruction", "field": "TransferChecked.mint", "operator": "eq", "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"}
{"field_source": "solana_token_program_instruction", "field": "TransferChecked.amount", "operator": "lte", "value": "1000000"}
systemField: current_unix_timestamp
{"field_source": "system", "field": "current_unix_timestamp", "operator": "lte", "value": "1735689600"}
Use for time-bounded policies (e.g. "allow transfers until 2025-01-01").
Use these as building blocks. Combine multiple conditions in one rule for AND logic. Use separate rules for OR logic.
Allow sending only to specific addresses:
{
"name": "Allowlist recipients",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "ethereum_transaction", "field": "to", "operator": "in", "value": ["0xAddr1...", "0xAddr2..."]}
],
"action": "ALLOW"
}
Allow transfers up to 0.1 ETH:
{
"name": "Max 0.1 ETH per tx",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "ethereum_transaction", "field": "value", "operator": "lte", "value": "100000000000000000"}
],
"action": "ALLOW"
}
Allow only on Base and Ethereum mainnet:
{
"name": "Base and mainnet only",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "in", "value": ["1", "8453"]}
],
"action": "ALLOW"
}
{
"name": "Treasury transfers on Base, max 1 ETH",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "0xTreasury..."},
{"field_source": "ethereum_transaction", "field": "value", "operator": "lte", "value": "1000000000000000000"},
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "eq", "value": "8453"}
],
"action": "ALLOW"
}
personal_sign and signMessage are not valid policy methods. Use * wildcard to allow them. Combine with specific DENY rules to restrict dangerous operations.
{
"name": "Allow all operations",
"method": "*",
"conditions": [],
"action": "ALLOW"
}
Typical pattern: DENY dangerous methods first, then ALLOW * for the rest:
[
{"name": "Block key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"},
{"name": "Allow everything else", "method": "*", "conditions": [], "action": "ALLOW"}
]
{
"name": "Allow USDC on Base",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "eq", "value": "8453"}
],
"action": "ALLOW"
}
{
"name": "Never export private key",
"method": "exportPrivateKey",
"conditions": [],
"action": "DENY"
}
Allow transfers until a specific date:
{
"name": "Allow until 2025-06-01",
"method": "eth_sendTransaction",
"conditions": [
{"field_source": "system", "field": "current_unix_timestamp", "operator": "lte", "value": "1748736000"}
],
"action": "ALLOW"
}
{
"name": "Allow SOL to treasury",
"method": "signAndSendTransaction",
"conditions": [
{"field_source": "solana_system_program_instruction", "field": "instructionName", "operator": "eq", "value": "Transfer"},
{"field_source": "solana_system_program_instruction", "field": "Transfer.to", "operator": "eq", "value": "TreasuryPubkey..."}
],
"action": "ALLOW"
}
{
"name": "Max 1 SOL per tx",
"method": "signAndSendTransaction",
"conditions": [
{"field_source": "solana_system_program_instruction", "field": "instructionName", "operator": "eq", "value": "Transfer"},
{"field_source": "solana_system_program_instruction", "field": "Transfer.lamports", "operator": "lte", "value": "1000000000"}
],
"action": "ALLOW"
}
{
"name": "Allow USDC transfers to recipient",
"method": "signAndSendTransaction",
"conditions": [
{"field_source": "solana_token_program_instruction", "field": "instructionName", "operator": "eq", "value": "TransferChecked"},
{"field_source": "solana_token_program_instruction", "field": "TransferChecked.mint", "operator": "eq", "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},
{"field_source": "solana_token_program_instruction", "field": "TransferChecked.destination", "operator": "eq", "value": "RecipientATA..."}
],
"action": "ALLOW"
}
Only allow interactions with specific programs:
{
"name": "Allow System and Token programs only",
"method": "signAndSendTransaction",
"conditions": [
{"field_source": "solana_program_instruction", "field": "programId", "operator": "in", "value": [
"11111111111111111111111111111111",
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
]}
],
"action": "ALLOW"
}
Use these only when the user explicitly requests tighter restrictions. Adapt the contract address and chain_id to the user's needs.
[
{"name": "Allow <DAPP_NAME>", "method": "eth_sendTransaction", "conditions": [
{"field_source": "ethereum_transaction", "field": "to", "operator": "eq", "value": "<CONTRACT_ADDRESS>"},
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "eq", "value": "<CHAIN_ID>"}
], "action": "ALLOW"},
{"name": "Token approvals on <NETWORK>", "method": "eth_sendTransaction", "conditions": [
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "eq", "value": "<CHAIN_ID>"},
{"field_source": "ethereum_transaction", "field": "value", "operator": "eq", "value": "0"}
], "action": "ALLOW"},
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"}
]
[
{"name": "Allow tx on <NETWORK>", "method": "eth_sendTransaction", "conditions": [
{"field_source": "ethereum_transaction", "field": "chain_id", "operator": "eq", "value": "<CHAIN_ID>"}
], "action": "ALLOW"},
{"name": "Allow signing on <NETWORK>", "method": "eth_signTypedData_v4", "conditions": [
{"field_source": "ethereum_typed_data_domain", "field": "chainId", "operator": "eq", "value": "<CHAIN_ID>"}
], "action": "ALLOW"},
{"name": "Deny key export", "method": "exportPrivateKey", "conditions": [], "action": "DENY"}
]
| Amount | Wei String |
|---|---|
| 0.001 ETH | "1000000000000000" |
| 0.01 ETH | "10000000000000000" |
| 0.1 ETH | "100000000000000000" |
| 1 ETH | "1000000000000000000" |
| 10 ETH | "10000000000000000000" |
Formula: wei = eth * 10^18
| Amount | Lamports String |
|---|---|
| 0.001 SOL | "1000000" |
| 0.01 SOL | "10000000" |
| 0.1 SOL | "100000000" |
| 1 SOL | "1000000000" |
| 10 SOL | "10000000000" |
Formula: lamports = sol * 10^9
| Amount | Raw String |
|---|---|
| 1 USDC | "1000000" |
| 100 USDC | "100000000" |
| 1000 USDC | "1000000000" |
| Chain | ID (string in conditions) |
|---|---|
| Ethereum Mainnet | "1" |
| Ethereum Sepolia | "11155111" |
| Base | "8453" |
| Optimism | "10" |
| Arbitrum One | "42161" |
| Polygon | "137" |
| BSC | "56" |
| Program | ID |
|---|---|
| System Program | 11111111111111111111111111111111 |
| Token Program | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA |
| Token-2022 | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
| Associated Token | ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL |
| USDC Mint | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
DENY exportPrivateKey rule unless the user explicitly needs key export.json:policy block must be valid, parseable JSON. Double-check addresses and values."Allowlist recipients", "Max 0.1 ETH per tx", or "Allow SOL to treasury". If you need to reference an address, abbreviate it (e.g. 0xba86...BE6E).wallet_propose_policyethereum_transaction or ethereum_calldata field_sources with eth_signTypedData_v4 — use ethereum_typed_data_domain or ethereum_typed_data_message insteadethereum_transaction field_sources with eth_sign7702Authorization — use ethereum_7702_authorization instead