Skip to main content
Newton VaultKit sits beside the vault SDK you already use. Your app prepares the same manager action it would normally send to a vault, then routes execution through a Shield that requires a Newton attestation. This guide shows the common VaultKit setup and the typed Morpho path most curators should start with: defineComposite(...), createShield(...), grant the clone its manager role, then call shield.morpho.reallocate(...). For Morpho-specific curator guidance, including Base Sepolia setup and MetaMorpho role requirements, see Morpho. For Euler-specific typed overlays, see Euler and Euler Earn. For lower-level examples, see Examples.

Prerequisites

  • Node 22 or newer
  • pnpm 10 or newer, or the package manager your app already uses
  • A funded curator EOA or Safe-controlled signer on the target chain
  • A Newton Gateway API key from the dashboard
  • The vault protocol SDKs your integration uses

Install Packages

VaultKit integrations use a core SDK package, a shared policy-pack helper package, one or more individual policy packs, and optionally a vendor SDK.

Core Packages

Install these for every VaultKit integration:
pnpm add @newton-xyz/vaultkit @newton-xyz/policy-pack-shared viem zod
PackageWhen you need it
@newton-xyz/vaultkitAlways. Provides createShield, the Shield client, typed vendor overlays, generic sendCall, and SDK errors.
@newton-xyz/policy-pack-sharedAlways when using published policy packs. Provides helpers such as defineComposite(...) for combining one or more policy modules into the pack object passed to createShield.
viemAlways. VaultKit uses Viem clients for chain reads, signing, and transaction submission.
zodUsually. Policy packs use Zod schemas to validate typed params and secrets.

Individual Policy Packs

Install each policy pack your Shield should enforce. For the Vaults.fyi example in this guide:
pnpm add @newton-xyz/policy-pack-vaultsfyi
For the currently documented vault policy packs:
pnpm add @newton-xyz/policy-pack-vaultsfyi @newton-xyz/policy-pack-chainalysis @newton-xyz/policy-pack-redstone @newton-xyz/policy-pack-webacy
PackageWhen you need it
@newton-xyz/policy-pack-vaultsfyiUse when enforcing Vaults.fyi vault-risk checks such as risk score, APY movement, TVL drawdown, critical flags, or corrupted data.
@newton-xyz/policy-pack-chainalysisUse when enforcing Chainalysis sanctions, AML, or high-risk address screening.
@newton-xyz/policy-pack-redstoneUse when enforcing RedStone oracle-divergence checks for stale, divergent, or sustained price-feed issues.
@newton-xyz/policy-pack-webacyUse when enforcing Webacy depeg-risk checks for pegged assets.
Other policy packsAdd only when your policy uses another data source or guardrail.

Vendor SDKs

Install vendor SDKs only when your app uses them directly. The typed Morpho overlay uses Morpho’s published SDK under the hood, so Morpho integrations install the Morpho peer dependency:
pnpm add @morpho-org/blue-sdk-viem
If you use shield.sendCall(...) and encode calldata yourself, you do not need a vendor SDK. Prefer typed vendor overlays when VaultKit ships one for your protocol.

1. Create Viem Clients

VaultKit reads chain state through a publicClient and submits transactions through a walletClient.
import { createPublicClient, createWalletClient, http } from 'viem'
import { base } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount(process.env.CURATOR_PRIVATE_KEY as `0x${string}`)

const publicClient = createPublicClient({
  chain: base,
  transport: http(process.env.BASE_RPC_URL),
})

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(process.env.BASE_RPC_URL),
})
For a Safe multisig owner, use the Safe-aware signer path your application already supports. Shield ownership is independent of the transaction transport.

2. Build a Policy Pack

Policy packs define the typed params, secrets, and per-call wasmArgs used during Newton operator evaluation. For composite policies, wrap the modules you want to enforce:
import { defineComposite } from '@newton-xyz/policy-pack-shared'
import { vaultsfyi } from '@newton-xyz/policy-pack-vaultsfyi'

const pack = await defineComposite({
  modules: [vaultsfyi],
  chainId: '8453',
  env: 'prod',
  publicClient,
  policyAddress: '0xPolicyAddress',
})

3. Create or Attach to a Shield

import { createShield } from '@newton-xyz/vaultkit'
import { morphoActions } from '@newton-xyz/vaultkit/vendors/morpho'

const shield = (await createShield({
  apiKey: process.env.NEWTON_API_KEY!,
  walletClient,
  rpc: process.env.BASE_RPC_URL!,
  pack,
  vault: '0xMetaMorphoVaultAddress',
  bypassDelaySeconds: 7n * 24n * 60n * 60n,
})).extend(morphoActions)
createShield is idempotent. It predicts the deterministic Shield address from the curator, vault, and version. If a clone exists with compatible configuration, the SDK attaches to it; otherwise it deploys a new clone through ShieldFactory. If you expect an existing clone, pass the known address and expected params:
const shield = await createShield({
  apiKey: process.env.NEWTON_API_KEY!,
  walletClient,
  rpc: process.env.BASE_RPC_URL!,
  policyClientAddress: '0xExpectedShieldAddress',
  pack,
  vault: '0xMetaMorphoVaultAddress',
  version: 1n,
  expectedParams: {
    vaultsfyi: {
      apy_z_max: 3,
      risk_score_floor: 80,
      tvl_drawdown_24h_max_pct: 25,
      tvl_drawdown_7d_max_pct: 40,
      deny_on_allocation_change: true,
      deny_on_critical_flag: true,
      deny_on_corrupted: true,
    },
  },
})
If the onchain policy address or params differ from expectedParams, the SDK throws a typed configuration error instead of silently using the wrong Shield.

4. Configure Policy Params and Secrets

await shield.setParams({
  vaultsfyi: {
    apy_z_max: 3,
    risk_score_floor: 80,
    tvl_drawdown_24h_max_pct: 25,
    tvl_drawdown_7d_max_pct: 40,
    deny_on_allocation_change: true,
    deny_on_critical_flag: true,
    deny_on_corrupted: true,
  },
})

await shield.uploadSecrets({
  vaultsfyi: {
    VAULTS_FYI_API_KEY: process.env.VAULTSFYI_API_KEY!,
  },
})
setParams writes typed policy params onchain through the Shield’s bound Newton policy. uploadSecrets encrypts API secrets in the SDK and stores them with Newton Gateway so operators can evaluate policies without exposing credentials onchain.

5. Grant the Shield Its Roles

execute and executeDirect are gated by Shield’s approved-delegate map. The initial owner is approved at clone initialization, but any hot key or Safe module that will submit actions through the Shield must be explicitly approved:
await walletClient.writeContract({
  address: shield.policyClientAddress,
  abi: shieldAbi,
  functionName: 'setApprovedDelegate',
  args: [account.address, true],
})
The Shield clone also needs the vendor-side manager role required by the action. For MetaMorpho reallocate, grant the clone allocator authority, or make it the curator:
await metaMorpho.write.setIsAllocator([shield.policyClientAddress, true])

6. Execute a Morpho Action

For Morpho, use the typed overlay. It builds the MetaMorpho calldata through Morpho’s SDK, lets the composite pack derive wasmArgs, requests the Newton attestation, and submits executeDirect.
const result = await shield.morpho.reallocate(
  '0xMetaMorphoVaultAddress',
  [{ marketParams, assets: 1_000_000n }],
  {
    prepareQueryOptions: {
      vaultsfyi: { previousAllocationHash: '0x...' },
    },
  },
)

const receipt = await publicClient.waitForTransactionReceipt({
  hash: result.transactionHash,
})
The SDK builds the exact Intent, signs it with the curator wallet, asks Newton operators to evaluate the configured policy, and submits the attested call through the Shield. If the policy denies the action, the SDK raises a typed policy error and the vault call is not forwarded. Use shield.sendCall(...) only for vendors or actions without a typed overlay. With sendCall, your integration owns the target, calldata, function signature, and any pack-specific per-call inputs.

7. Handle Policy Errors

import {
  AttestationTimeoutError,
  GatewayError,
  PolicyDeniedError,
} from '@newton-xyz/vaultkit'

try {
  await shield.morpho.reallocate('0xMetaMorphoVaultAddress', allocations)
} catch (error) {
  if (error instanceof PolicyDeniedError) {
    console.error('Vault action denied by policy', error)
    return
  }

  if (error instanceof AttestationTimeoutError || error instanceof GatewayError) {
    console.error('Retry with a fresh task after backoff', error)
    return
  }

  throw error
}
See Errors for retry guidance.

Next Steps

Concepts

Learn how Intent, wasmArgs, policy packs, and vendor modules fit together.

Examples

Copy patterns for idempotent deploys, generic calls, retries, and read-only inspection.

Reference

Review the SDK API surface.