Skip to main content

SDK

The Execution Escrow SDK is a TypeScript framework for secure blockchain transaction processing with multi-signature escrow. The SDK provides independent arbiter verification for AI agents and applications.

Installation

npm install @navalabs/execution-escrow-core
# or
pnpm add @navalabs/execution-escrow-core

The core package provides everything needed for transaction verification and execution.

Key Generation

Generate the required cryptographic keys using the CLI tool included with the adapter package.

  • PGP Key Pair - Required for Type 1 and Type 2 (encrypts transaction manifests for arbiter review)
  • P-256 Key Pair - Only required for Type 2 (AGENT_EXECUTED) where the agent signs transactions automatically

Install the Adapter

npm install @navalabs/execution-escrow-adapter
# or
pnpm add @navalabs/execution-escrow-adapter

Generate Keys

Run the key generation tool directly after installation:

# pnpm
pnpm generate-escrow-keys <walletType> <pgpPassphrase>

# npm
npx generate-escrow-keys <walletType> <pgpPassphrase>

Arguments

ArgumentRequiredDescription
walletTypeYesWallet integration: privy
pgpPassphraseYesPassphrase to encrypt your PGP private key

Examples

# Generate keys for Privy wallet
pnpm generate-escrow-keys privy "my-secure-passphrase"

Fireblocks Integration: We support Fireblocks wallet integration. Please contact us to set up Fireblocks for your deployment.

What Gets Generated

The tool generates and displays:

  1. PGP Key Pair - For encrypting transaction manifests (required for Type 1 and Type 2)
    • Public key is needed for agent registration
  2. P-256 Key Pair - For agent transaction signing (required for Type 2 only)
    • Base64-encoded for Privy (PRIVY_EXECUTION_PRIVATE_KEY)
    • Not needed for Type 1 where you sign transactions yourself

Security Notes

  • Keys are displayed in terminal only (copy them securely)
  • Never commit private keys or mcp-config.json to version control
  • Store private keys in a secure password manager or secrets vault
  • Use environment variables for sensitive configuration in production
  • Rotate keys regularly for production deployments

Quick Start

This example demonstrates the recommended Type 1 (USER_EXECUTED) flow where the arbiter verifies your transaction. Once verified, you or your agent can proceed with execution however you choose.

import {
ExecutionEscrowCore,
Configuration,
getNavaInboxAddress,
} from '@navalabs/execution-escrow-core';

// Configuration - use environment variables in production
const NAVA_API_KEY = process.env.NAVA_API_KEY!;
const ESCROW_ADDRESS = process.env.ESCROW_ADDRESS as `0x${string}`;
const RPC_URL = process.env.RPC_URL!;
const PGP_EXECUTOR_PRIVATE_KEY = process.env.PGP_EXECUTOR_PRIVATE_KEY!;
const PGP_EXECUTOR_PASSPHRASE = process.env.PGP_EXECUTOR_PASSPHRASE!;
const CHAIN_ID = '11155111'; // Sepolia

async function main() {
// Initialize with Type 1 (USER_EXECUTED) - arbiter reviews, you sign
const config = Configuration.fromObject({
navaApiKey: NAVA_API_KEY,
chainId: CHAIN_ID,
execution: {
pgpExecutorPrivateKey: PGP_EXECUTOR_PRIVATE_KEY,
pgpExecutorPassphrase: PGP_EXECUTOR_PASSPHRASE,
escrowAddress: ESCROW_ADDRESS,
rpcUrl: RPC_URL,
executionType: 1, // USER_EXECUTED (recommended)
},
});

const escrow = new ExecutionEscrowCore(config);

// Submit transaction for arbiter verification
const transaction = {
to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' as `0x${string}`,
value: '0.001',
data: '0x' as `0x${string}`,
chainId: parseInt(CHAIN_ID, 10),
};

const agentContext = {
request: 'Transfer 0.001 ETH to test arbiter verification',
timestamp: Date.now(),
metadata: { source: 'quickstart' },
};

const result = await escrow.requestVerification(
transaction,
agentContext,
ESCROW_ADDRESS
);

console.log('Request submitted:', result.success);

// After arbiter approval, proceed with execution as needed
}

main();

Full Example: See the minimal-verification cookbook for a complete, runnable example with polling and status display.

Checking Verification Status

When you submit a transaction for verification using requestVerification(), a requestHash is returned in the response. You can use this hash to check whether the arbiter has approved the request.

// Submit for verification
const result = await escrow.requestVerification(
transaction,
agentContext,
ESCROW_ADDRESS
);

// Get the request hash from the response
const requestHash = result.data.requestHash;

// Check approval status
const status = await escrow.checkVerificationStatus(requestHash);

if (status.approved) {
// Proceed with execution
}

Integration Examples

For framework-specific integration examples, see:

Configuration

Environment Variables

# Required for all execution types
NAVA_API_KEY=nava_live_...
ESCROW_ADDRESS=0x...
CHAIN_ID=11155111

# Required for Type 1 and Type 2 (arbiter-reviewed flows)
PGP_EXECUTOR_PRIVATE_KEY=-----BEGIN PGP PRIVATE KEY BLOCK-----\n...\n-----END PGP PRIVATE KEY BLOCK-----
PGP_EXECUTOR_PASSPHRASE=your-passphrase
RPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-key

# Optional (SDK provides defaults)
# NAVA_BASE_URL=https://api.nava.dev
# NAVA_INBOX_ADDRESS=0x... (derived from CHAIN_ID)

Execution Types

The Execution Escrow SDK supports three distinct execution types. The executionType field determines which workflow is used and which configuration fields are required.

Choosing an Execution Type

TypeWho Signs?Who Executes?Best For
Type 1: USER_EXECUTEDUserUserInteractive apps where users want control over final execution
Type 2: AGENT_EXECUTEDAgentArbiterAutonomous agents that need to operate without user intervention
Type 0: DIRECT_USERUserUserTrusted environments with no oversight needed

Type 1 (Recommended) is best for most use cases:

  • Arbiter verifies the transaction before you proceed
  • User or agent maintains control over final execution
  • Flexibility to execute however you choose after verification

Type 2 is for fully autonomous agents:

  • Agent signs transactions automatically
  • Arbiter reviews AND executes approved transactions
  • Requires Privy wallet credentials
  • Use when building agents that must operate 24/7 without user interaction

Arbiter reviews and verifies transactions, but the user executes them through their own wallet after approval.

Use Case: Balance between user control and oversight. Arbiter can block malicious transactions while user maintains execution control.

Required Configuration:

  • executionType: 1
  • escrowAddress
  • chainId
  • navaApiKey
  • pgpExecutorPrivateKey - For encrypting transaction manifests
  • pgpExecutorPassphrase - For PGP key decryption

Example:

const config = Configuration.fromObject({
navaApiKey: NAVA_API_KEY,
chainId: '11155111',
execution: {
executionType: 1,
escrowAddress: '0x...',
pgpExecutorPrivateKey: '-----BEGIN PGP PRIVATE KEY BLOCK-----\n...',
pgpExecutorPassphrase: 'your_passphrase',
rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/...',
},
});

Type 2: AGENT_EXECUTED

Arbiter reviews and verifies transactions, and the agent automatically executes approved transactions.

Use Case: Fully automated agents that can operate autonomously after arbiter approval.

Required Configuration:

  • executionType: 2
  • escrowAddress
  • chainId
  • navaApiKey
  • pgpExecutorPrivateKey - For encrypting transaction manifests
  • pgpExecutorPassphrase - For PGP key decryption
  • rpcUrl - For blockchain interaction
  • Privy wallet credentials: privyWalletId + privyExecutionPrivateKey

Example:

const config = Configuration.fromObject({
navaApiKey: NAVA_API_KEY,
chainId: '11155111',
execution: {
executionType: 2,
escrowAddress: '0x...',
pgpExecutorPrivateKey: '-----BEGIN PGP PRIVATE KEY BLOCK-----\n...',
pgpExecutorPassphrase: 'your_passphrase',
rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/...',
walletProvider: 'privy',
privy: {
walletId: 'did:privy:...',
executionPrivateKey: 'MIGHAgEAMBMG...',
},
},
});

Fireblocks Integration: We support Fireblocks wallet integration for Type 2 execution. Please contact us to set up Fireblocks for your deployment.

Type 0: DIRECT_USER

User signs and executes transactions directly through their own wallet. No arbiter review or verification process.

Use Case: Minimal overhead for trusted environments where users want direct control without oversight.

Required Configuration:

  • executionType: 0
  • escrowAddress
  • chainId
  • navaApiKey

Example:

const config = Configuration.fromObject({
navaApiKey: NAVA_API_KEY,
chainId: '11155111',
execution: {
executionType: 0,
escrowAddress: '0x...',
rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/...',
},
});

Configuration Requirements Table

FieldType 1Type 2Type 0
executionType
escrowAddress
chainId
navaApiKey
pgpExecutorPrivateKey
pgpExecutorPassphrase
rpcUrl
Wallet credentials

SDK Defaults (Optional Fields)

The following fields have SDK-provided defaults and can be omitted unless you need to override them:

  • navaBaseUrl - Defaults to https://api.nava.dev
  • navaInboxAddress - Defaults to official Nava Inbox contract address (derived from chainId)
  • pgpArbiterPublicKey - Defaults to official arbiter public key
  • walletProvider - Defaults to 'privy'
  • privyAppId - Defaults to Nava Privy application ID

API Reference

Core Classes

ExecutionEscrowCore

The main class that handles the escrow logic.

class ExecutionEscrowCore {
constructor(config: Configuration);
async requestVerification(
transaction: TransactionRequest,
context: AgentContext,
escrowAddress: `0x${string}`
): Promise<ExecutionResult>;
async checkRequestStatus(
requestHash: `0x${string}`,
navaInbox: `0x${string}`
): Promise<RequestStatus>;
async getApprovalStatus(
requestHash: `0x${string}`
): Promise<ApprovalStatus>;
}

Types

interface TransactionRequest {
to: `0x${string}`;
data: `0x${string}`;
value: string;
chainId: number;
}

interface AgentContext {
request: string;
timestamp: number;
metadata?: Record<string, unknown>;
}

interface ExecutionResult {
success: boolean;
data: {
userMessage?: string;
nextSteps?: string;
transactionHash?: string;
error?: string;
};
}

Development

Building

git clone https://github.com/navalabs-dev/execution-escrow.git
cd execution-escrow
pnpm install
pnpm build

Development Commands

# Build all packages
pnpm build

# Development with watch mode
pnpm dev

# Run tests
pnpm test

# Lint all packages
pnpm lint