← Digital Assets

EVM signing policies

Set signing rules for contract calls, typed authorizations, and account delegation.

Contract methods

Choose which contract methods your key may authorize. Check decoded arguments, chain ID, gas limits, and attached ETH.

Integration guide
Edict exampletransfer-limits.edict
authority "transfer-limits" {
    intent = "evm.transaction"
    version = "1"

    config {
        chainId = 1
    }

    contract usdc: evm {
        address = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"

        fn transfer(to: address, amount: uint256)

        fn approve(spender: address, amount: uint256)
    }

    let USDC = 1000000
    let router = "0xe592427a0aece92de3edee1f18e0157c05861564"

    approver treasury {
        type = "reference"
        id = "treasury"
        ref = "treasury@sha256:<digest>"
    }

    approver security {
        type = "reference"
        id = "security"
        ref = "security@sha256:<digest>"
    }

    policy "token-spending" {
        allow "transfer" when {
            usdc.transfer != null
            usdc.transfer.amount > 0
            usdc.transfer.amount <= 10000 * USDC
            transaction.value == 0
            transaction.gasLimit <= 100000
        }

        allow "bounded-allowance" when {
            usdc.approve != null
            usdc.approve.spender == router
            usdc.approve.amount > 0
            usdc.approve.amount <= 1000 * USDC
            transaction.value == 0
        }

        allow "revoke-allowance" when {
            usdc.approve != null
            usdc.approve.amount == 0
            transaction.value == 0
        }

        require 2 of [treasury, security] if (
            usdc.transfer != null and usdc.transfer.amount > 1000 * USDC
        )
        require security if (usdc.approve != null and usdc.approve.amount > 0)
    }
}

Typed token authorizations

Set amount and expiry limits for ERC-3009 transfers and ERC-2612 permits. Restrict the token contract and decide when a request needs approval.

Integration guide
Edict exampleshowcase-transfer-authorization.edict
authority "showcase-transfer-authorization" {
    intent = "evm.3009"
    version = "1"

    config {
        chainId = 1
    }

    let USDC = 1000000

    approver treasury {
        type = "reference"
        id = "treasury"
        ref = "treasury@sha256:<digest>"
    }

    approver security {
        type = "reference"
        id = "security"
        ref = "security@sha256:<digest>"
    }

    policy "relayed-usdc-payment" {
        let now = Time.now().toSeconds()

        allow when {
            operation == "transferWithAuthorization"
            domain.name == "USD Coin"
            domain.version == "2"
            domain.verifyingContract == "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
            transfer != null
            transfer.value > 0
            transfer.value <= 10000 * USDC
            transfer.validAfter <= now
            transfer.validBefore > now
            transfer.validBefore <= now + 300
        }

        require 2 of [treasury, security] if (
            transfer != null and transfer.value > 1000 * USDC
        )
    }
}

Structured call arguments

Declare structs and arrays in a contract interface. Write rules that compare decoded fields with each other or with values such as the current time.

Integration guide
Edict exampleshowcase-swap.edict
authority "showcase-swap" {
    intent = "evm.transaction"
    version = "1"

    config {
        chainId = 1
    }

    contract router: evm {
        address = "0xe592427a0aece92de3edee1f18e0157c05861564"

        struct ExactOutput {
            tokenIn: address
            tokenOut: address
            fee: uint24
            recipient: address
            deadline: uint256
            amountOut: uint256
            amountInMaximum: uint256
            sqrtPriceLimitX96: uint160
        }

        fn exactOutputSingle(params: ExactOutput)
    }

    let WETH = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
    let USDC = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"

    approver treasury {
        type = "reference"
        id = "treasury"
        ref = "treasury@sha256:<digest>"
    }

    approver security {
        type = "reference"
        id = "security"
        ref = "security@sha256:<digest>"
    }

    policy "reviewed-swap" {
        let swap = router.exactOutputSingle.params

        allow when {
            swap.tokenIn == WETH
            swap.tokenOut == USDC
            swap.fee == 3000
            swap.amountOut > 0
            swap.amountInMaximum > 0
            swap.amountInMaximum <= 1000000000000000000
            swap.deadline > Time.now().toSeconds()
            swap.deadline <= Time.now().toSeconds() + 300
            transaction.value == 0
            transaction.gasLimit <= 250000
        }

        require 2 of [treasury, security]
    }
}

EIP-7702 delegation

Choose which contracts may act for an account. Restrict the chain and nonce, and require approval before signing a delegation.

Integration guide
Edict exampledelegation-control.edict
authority "delegation-control" {
    intent = "evm.7702"
    version = "1"

    config {
        chainId = 1
    }

    approver security {
        type = "reference"
        id = "security"
        ref = "security@sha256:<digest>"
    }

    policy "delegation-control" {
        allow when {
            chainId == 1
            address == "0x1111111111111111111111111111111111111111"
            nonce == 7
        }

        require security
    }
}