Skip to main content
This guide walks through building a Next.js application that uses the Newton SDK to evaluate transaction intents against your policy, then executes them through your deployed PolicyClient contract.

Prerequisites

Step 1: Create the Project

npx create-next-app@latest newton-sdk-app \
  --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd newton-sdk-app
npm install @magicnewton/newton-protocol-sdk@0.3.15 viem

Step 2: Environment Variables

Create .env.local:
# Alchemy RPC URLs
NEXT_PUBLIC_SEPOLIA_ALCHEMY_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
NEXT_PUBLIC_SEPOLIA_ALCHEMY_WS_URL=wss://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY

# Newton API key
NEXT_PUBLIC_NEWTON_API_KEY=your_newton_api_key

# Newton Policy Contract address (fixed on Sepolia)
NEXT_PUBLIC_POLICY_CONTRACT_ADDRESS=0x698C687f86Bc2206AC7C06eA68AC513A2949abA6

# YOUR deployed wallet address
NEXT_PUBLIC_POLICY_WALLET_ADDRESS=0xYourDeployedWallet

# Signer private key for client-side signing
NEXT_PUBLIC_SIGNER_PRIVATE_KEY=0xYourPrivateKey
NEXT_PUBLIC_POLICY_CONTRACT_ADDRESS is the Newton Policy contract (fixed on Sepolia). NEXT_PUBLIC_POLICY_WALLET_ADDRESS is YOUR deployed PolicyClient. These are different contracts.

Step 3: Create Configuration

Create src/const/config.ts:
import { Hex } from "viem";

export const SEPOLIA_ALCHEMY_URL = process.env.NEXT_PUBLIC_SEPOLIA_ALCHEMY_URL!;
export const SEPOLIA_ALCHEMY_WS_URL = process.env.NEXT_PUBLIC_SEPOLIA_ALCHEMY_WS_URL!;
export const NEWTON_API_KEY = process.env.NEXT_PUBLIC_NEWTON_API_KEY!;
export const POLICY_WALLET_ADDRESS = process.env.NEXT_PUBLIC_POLICY_WALLET_ADDRESS as Hex;
export const POLICY_CONTRACT_ADDRESS = process.env.NEXT_PUBLIC_POLICY_CONTRACT_ADDRESS as Hex;
export const SIGNER_PRIVATE_KEY = process.env.NEXT_PUBLIC_SIGNER_PRIVATE_KEY as Hex;

Step 4: Create the Newton Client

Create src/lib/use-newton-client.ts:
"use client";

import { useMemo } from "react";
import { createWalletClient, webSocket, Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { newtonWalletClientActions } from "@magicnewton/newton-protocol-sdk";
import { SEPOLIA_ALCHEMY_WS_URL, NEWTON_API_KEY } from "@/const/config";

export const useNewtonClient = (privateKey: Hex) => {
  const client = useMemo(() => {
    const account = privateKeyToAccount(privateKey);
    const walletClient = createWalletClient({
      account,
      chain: sepolia,
      transport: webSocket(SEPOLIA_ALCHEMY_WS_URL),
    }).extend(newtonWalletClientActions({ apiKey: NEWTON_API_KEY }));

    return { walletClient, account };
  }, [privateKey]);

  return client;
};

Step 5: Submit an Evaluation Request

Create src/lib/evaluation-request.ts:
import { Hex } from "viem";
import { sepolia } from "viem/chains";
import { POLICY_WALLET_ADDRESS } from "@/const/config";

function stringToHexBytes(str: string): Hex {
  const encoder = new TextEncoder();
  const bytes = encoder.encode(str);
  return ("0x" + Array.from(bytes)
    .map((byte) => byte.toString(16).padStart(2, "0"))
    .join("")) as Hex;
}

export type EvaluationRequestParams = {
  signerAddress: Hex;
  targetAddress: Hex;
  value: bigint;
  data: Hex;
  wasmArgs: Record<string, unknown>;
};

export const createEvaluationRequest = ({
  signerAddress,
  targetAddress,
  value,
  data,
  wasmArgs,
}: EvaluationRequestParams) => {
  const functionSignature = stringToHexBytes(
    "validateAndExecuteDirect(address,uint256,bytes,(bytes32,address,uint32,uint32,(address,address,uint256,bytes,uint256,bytes),bytes,bytes,bytes),(bytes32,address,bytes32,address,(address,address,uint256,bytes,uint256,bytes),bytes,bytes,(bytes32,address,bytes,(bytes,bytes,bytes,address,uint32)[]),(bytes,uint32)),bytes)"
  );

  return {
    policyClient: POLICY_WALLET_ADDRESS,
    intent: {
      from: signerAddress,
      to: targetAddress,
      value: `0x${value.toString(16)}` as Hex,
      data: data,
      chainId: sepolia.id,
      functionSignature: functionSignature,
    },
    wasmArgs: stringToHexBytes(JSON.stringify(wasmArgs)),
    timeout: 60,
  };
};

Step 6: Execute an Attested Transaction

Use evaluateIntentDirect to get an attestation, then submit the attested transaction on-chain:
// Evaluate intent — returns attestation data
const evalResponse = await walletClient.evaluateIntentDirect(evalRequest);
const { evaluationResult, task, taskResponse, blsSignature } = evalResponse.result;

if (!evaluationResult) {
  throw new Error("Policy evaluation failed — transaction blocked");
}

// Execute on-chain with the attestation
const functionData = encodeFunctionData({
  abi: newtonPolicyWalletAbi,
  functionName: "validateAndExecuteDirect",
  args: [to, value, data, task, taskResponse, blsSignature],
});

const txHash = await walletClient.sendTransaction({
  to: POLICY_WALLET_ADDRESS,
  data: functionData,
});

Step 7: Run the Application

npm run dev
Open http://localhost:3000 in your browser.

Troubleshooting

IssueCauseFix
GATEWAY_ERRORInvalid API key or network issueVerify NEWTON_API_KEY and network connectivity
InvalidAttestation on-chainTask Manager mismatch or expired attestationVerify contract was initialized with correct Task Manager address
WebSocket connection failedWrong WS URL or Alchemy plan limitsVerify SEPOLIA_ALCHEMY_WS_URL format (wss://...)
TIMEOUTOperator network slow or unavailableIncrease timeout or retry

Next Steps

SDK Reference

Full TypeScript SDK documentation

RPC API

Underlying JSON-RPC methods