Skip to content

Deployment

This guide covers deployment flexibility, deployed contract addresses, how to deploy SmartAgentKit modules to a new chain, gas costs, and contract verification.

Deployment Flexibility

SmartAgentKit is chain-agnostic and does not rely on centralized or "official" deployments. The SDK accepts arbitrary hook contract addresses and works with any deployed instance of the modules.

Key points:

  • No official deployments required. You can deploy your own instances of all hook contracts.
  • Built-in defaults are convenience only. The SDK ships with default addresses for Base Sepolia and Sepolia testnets. These are not privileged — they are the same contracts you would deploy yourself.
  • Any EVM chain is supported. Deploy the hook contracts to your target chain and pass the addresses to the SDK.
  • Production deployments should be your own. Deploy, audit, and verify your own hook instances for production use. Do not rely on testnet defaults.
typescript
// Deploy your own hooks to any chain, then pass the addresses
const client = new SmartAgentKitClient({
  chain: myChain,
  rpcUrl: "...",
  bundlerUrl: "...",
  moduleAddresses: {
    spendingLimitHook: "0xYourSpendingLimitHook",
    allowlistHook: "0xYourAllowlistHook",
    emergencyPauseHook: "0xYourEmergencyPauseHook",
    customModules: {
      "my-custom-hook": "0xYourCustomHook",
    },
  },
});

You can also set defaults on the plugin registry at runtime:

typescript
import { pluginRegistry } from "@smartagentkit/sdk";

pluginRegistry.setDefaultAddress("spending-limit", myChain.id, "0xYourSpendingLimitHook");
pluginRegistry.setDefaultAddress("allowlist", myChain.id, "0xYourAllowlistHook");
pluginRegistry.setDefaultAddress("emergency-pause", myChain.id, "0xYourEmergencyPauseHook");

Or install a policy with an explicit hook address, bypassing all address resolution:

typescript
await client.policies.install(wallet, {
  plugin: "allowlist",
  hookAddress: "0xYourDeployedHook",
  config: { type: "allowlist", mode: "allow", targets: [...] },
}, ownerKey);

Deployed Contracts

SmartAgentKit modules are deployed on the following chains.

Base Sepolia (Chain ID: 84532)

ContractAddress
SpendingLimitHook0x0ea97ef2fc52700d1628110a8f411fefb0c0aa8b
AllowlistHook0x61a2100072d03f66de6f7dd0dfc2f7aa5c91e777
EmergencyPauseHook0xb8fdc9ee56cfb4077e132eff631b546fe6e79fec
AutomationExecutor0x729c29b35c396b907ed118f00fbe4d4bcc3a7f46

Infrastructure Contracts (Same on All EVM Chains)

ContractAddress
EntryPoint v0.70x0000000071727De22E5E9d8BAf0edAc6f37da032
Safe7579 Module0x7579EE8307284F293B1927136486880611F20002
Safe7579 Launchpad0x7579011aB74c46090561ea277Ba79D510c6C00ff
Rhinestone Attester0x000000333034E9f539ce08819E12c1b8Cb29084d
HookMultiPlexer0xF6782ed057F95f334D04F0Af1Af4D14fb84DE549

Auto-Resolution

The SDK automatically resolves module addresses for deployed chains. You do not need to specify moduleAddresses when using Base Sepolia or Sepolia:

typescript
const client = new SmartAgentKitClient({
  chain: baseSepolia,
  rpcUrl: "...",
  bundlerUrl: "...",
  // moduleAddresses not needed -- auto-resolved
});

For other chains, deploy the modules first and pass the addresses explicitly:

typescript
import { optimism } from "viem/chains";

const client = new SmartAgentKitClient({
  chain: optimism,
  rpcUrl: "...",
  bundlerUrl: "...",
  moduleAddresses: {
    spendingLimitHook: "0x...",
    allowlistHook: "0x...",
    emergencyPauseHook: "0x...",
    automationExecutor: "0x...",
  },
});

Deploying to a New Chain

Prerequisites

  • Foundry installed
  • A funded deployer account on the target chain
  • An RPC URL for the target chain

Using Foundry

bash
cd packages/contracts

# Set environment
export PRIVATE_KEY=0x...
export RPC_URL=https://...

# Deploy all modules
forge script script/Deploy.s.sol:Deploy \
  --rpc-url $RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --verify

The deploy script deploys all four modules:

  • SpendingLimitHook
  • AllowlistHook
  • EmergencyPauseHook
  • AutomationExecutor

The infrastructure contracts (EntryPoint, Safe7579, HookMultiPlexer) are already deployed on all major EVM chains and do not need to be redeployed.

Gas Costs

Median gas costs from forge test --gas-report:

OperationGas
SpendingLimitHook.preCheck~54,000
AllowlistHook.preCheck~32,000
EmergencyPauseHook.preCheck~28,000
HookMultiPlexer.preCheck (3 hooks)~87,000

The total hook overhead is approximately 87,000 gas per UserOp when all three hooks are installed. This is the cost of the HookMultiPlexer routing to each sub-hook's preCheck function.

Note that the HookMultiPlexer cost is less than the sum of individual hooks because it batches the calls and avoids redundant storage reads.

Contract Verification

After deploying, verify contracts on the block explorer so that users can read the source code and interact with them directly:

bash
forge verify-contract \
  --chain-id 84532 \
  --compiler-version v0.8.29 \
  0xCONTRACT_ADDRESS \
  src/modules/SpendingLimitHook.sol:SpendingLimitHook

Repeat for each deployed contract, substituting the address and contract path:

bash
# AllowlistHook
forge verify-contract \
  --chain-id 84532 \
  --compiler-version v0.8.29 \
  0xCONTRACT_ADDRESS \
  src/modules/AllowlistHook.sol:AllowlistHook

# EmergencyPauseHook
forge verify-contract \
  --chain-id 84532 \
  --compiler-version v0.8.29 \
  0xCONTRACT_ADDRESS \
  src/modules/EmergencyPauseHook.sol:EmergencyPauseHook

# AutomationExecutor
forge verify-contract \
  --chain-id 84532 \
  --compiler-version v0.8.29 \
  0xCONTRACT_ADDRESS \
  src/modules/AutomationExecutor.sol:AutomationExecutor

If verification fails, ensure you are using the exact same compiler version and optimizer settings as the deployment. The project uses via_ir = true in foundry.toml, which must match during verification.

Released under the MIT License.