Skip to main content
Before getting started, ensure that your EOA address is allowlisted with Newton Protocol. Please reach out to [email protected] to learn more.

Deploy a policy

The first step in using Newton Protocol is deploying a policy to the protocol.
A policy is a rule set applied to a data oracle that can be customized by an end-user with their own parameters.
Policies consist of a combination of a policy code along with a data oracle for calling data offchain.
Note: This step is optional — you may choose to leverage an existing policy that’s already deployed.
See this example.
  1. Writepolicy code in Rego. See this guide for what’s supported.
  2. Use the CLI tool to deploy your policy:
    https://github.com/newt-foundation/developer-policy-cli
  3. At this point, a policy address and IPFS hash will be returned.

Deploy a policy client

The next step in integration is using the policy as part of a policy client, a Solidity contract. The policy client represents the consumer of the policy proofs — where transactions will be gated by Newton Protocol. Write a Solidity contract to extend NewtonPolicyClient.
Initialize for Newton Protocol with the policy address you want to use (this can be in the constructor).
Steps:
  1. Call _initNewtonPolicyClient(policyTaskManager, policy, policyClientOwner); from NewtonPolicyClient in your implementation.
  2. Update your function to place the policy as a guard.
    For example, change
    function doPolicyGuaradedAction(address, uint256) external
    
    to
    function doPolicyGuaradedAction(NewtonMessage.Attestation attestation) external
    
    Inside this method, add your business logic.
  3. Deploy the contract.
  4. Call initialize with your policy address and owner.
    The proverTaskManager value is 0xae08eb5bb2d2debfa248ec6ec0355e6643791a91.
  5. Call setParameters with configuration parameters for the policy (via Etherscan or a client app). Note that this step is mandatory, even if the policy doesn’t require any parameters.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

// Find newton contracts here: https://github.com/newt-foundation/newton-contracts
// or forge install newt-foundation/newton-contracts
import {NewtonPolicyClient} from "newton-contracts/src/mixins/NewtonPolicyClient.sol";
import {INewtonPolicy} from "newton-contracts/src/interfaces/INewtonPolicy.sol";
import {NewtonMessage} from "newton-contracts/src/core/NewtonMessage.sol";

contract YourPolicyClient is NewtonPolicyClient {
    event Success();
    error InvalidAttestation();

    constructor() {}

    function initialize(
        address proverTaskManager,
        address policy, // refers to the policy address
        address owner
    ) external {
        _initNewtonPolicyClient(policyTaskManager, policy, owner);
    }
	
	//User customization of the policy is set here
    function setParameters(INewtonPolicy.PolicyConfig memory _config) external {
        _setPolicy(_config);
    }
	
	//This method will validate the Newton proof and gate execution
    function validateAndExecuteIntent(NewtonMessage.Attestation memory attestation) external {
        require(_validateAttestation(attestation), InvalidAttestation());
        emit Success();
    }
}

Task submission

Before getting started with this step, ensure that your EOA address is allowlisted with Newton Protocol. The EOA signer is represented by the variable signer in the snippet below. Please reach out to [email protected] to get the wallet allowlisted.
Once a policy is configured by an end-user, a task can be submitted.
Here, an intent representing a transaction is constructed and sent to Newton Protocol along with the corresponding policy client address.
This portion of the SDK should be executed server-side (since it requires a private key to sign messages).
The EOA used must be allowlisted.
Steps:
  1. Install the Newton Protocol SDK
    @magicnewton/newton-protocol-sdk
  2. Sign up with Alchemy (or another RPC provider) for a WebSocket URL.
  3. Implement the SDK per the below example.
// Code to be run server side
import { createWalletClient } from 'newton-sdk'

const signer = await getSigner(); // signer for agent, loaded with an approved private key

const agentNewtonWalletClient = createWalletClient({
  chain: sepolia,
  transport: websocket('wss://alchemyURL?apikey'),
  account: signer
}).extend(newtonWalletClientActions);

//transaction intent
const intent = {
  from: "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
  to: "0xb1ad5f82407bc0f19f42b2614fb9083035a36b69",
  value: "0x0",
  data: "0xdeadbeef",
  chainId: "0xaa36a7",
  functionSignature: stringToHex("transfer(address,uint256)")
};

//This code submits an intent to be evaluated
const pending: PendingTaskBuilder = await agentNewtonWalletClient.submitEvaluationRequest({
  policyClient: policyClientAddress,
  intent,
  timeout: 60
});

//A task ID can be queried in the newton explorer
const taskID = pending.taskId;

// This line of code provides the result of the task once its complete
const response = await pending.waitForTaskResponse({ timeoutMs: 20_000 });

// Check evaluation result. It will either be compliant or non-compliant
const evaluationResult = response.taskResponse.evaluationResult;

// Get attestation proof. This is used to submit a transaction
const attestation: Attestation = response.attestation;

Transaction submission

Finally, the proof from the previous step can be used in the policy client to perform a transaction.
//This code submits a transaction to the policy client, calling the method validateAndExecuteIntent with the proof from the previous step 
newtonWalletClient.writeContract({ 
  address: policyClientAddress,
  abi,
  functionName: 'validateAndExecuteIntent',
  args: [attestation]
})

Additional resources

If your app requires multiple users to have their own policy clients (e.g. trading vaults or guarded accounts), you can use the factory pattern to deploy a smart contract per user. Solidity example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {NewtonPolicyClient} from "../../mixins/NewtonPolicyClient.sol";
import {INewtonPolicy} from "../../interfaces/INewtonPolicy.sol";
import {NewtonMessage} from "../../core/NewtonMessage.sol";

contract YourPolicyClient is NewtonPolicyClient {
    constructor(
        address proverTaskManager,
        address policy,
        address policyClientOwner
    ) {
        _initNewtonPolicyClient(policyTaskManager, policy, policyClientOwner);
    }

    function setParameters(INewtonPolicy.PolicyConfig memory _config) external {
        _setPolicy(_config);
    }

    function swap(NewtonMessage.Attestation attestation) external {
        require(_validateAttestation(attestation), "Not authorized");
        // Business logic here
    }
}
Factory contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {YourPolicyClient} from "./YourPolicyClient.sol";
import {INewtonPolicy} from "../../interfaces/INewtonPolicy.sol";

contract NewtonClientFactory {
    YourPolicyClient public immutable clientImpl;

    constructor(YourPolicyClient _impl) {
        clientImpl = _impl;
    }

    function createClient(INewtonPolicy.PolicyConfig memory config)
        external
        returns (YourPolicyClient client)
    {
        client = YourPolicyClient(Clones.clone(client));
        client.setParameters(config);
    }
}
Frontend example
import { createWalletClient, encodeFunctionData } from 'viem'
import { newtonWalletClientActions } from 'newton-sdk'

const signer = Signer() // e.g. from wagmi or another wallet library

const walletClient = createWalletClient({
  chain: sepolia,
  transport: custom(window.ethereum),
  account: signer
});

const policyParamsJson = { spendLimitUsd: 1_000, allow: ['0xRouter'] };
const policyParamsHexEncoded = toHex(JSON.stringify(policyParamsJson));
const expireAfter = 7200;

const policyConfig: PolicyConfig = {
  policyParams: policyParamsHexEncoded,
  expireAfter
};

const factoryAbi = { ... };
const factoryAddress = "0x...";

const hash = await walletClient.sendTransaction({
  data: encodeFunctionData({
    abi: factoryAbi,
    functionName: "createClient",
    args: [policyConfig]
  }),
  to: factoryAddress
});