Dynamic Pre-sign Simulation

This guide uses to @shield3/react-sdk to simulate transactions before signing. This allows your app to easily screen transactions before signing with the embedded wallet or sending to the wallet client

This integration is best suited for apps using Dynamic with both embedded and external wallets (like Metamask) enabled. If your app only uses Dynamic's embedded wallet, we recommend Dynamic RPC Substitution, as it is simpler integration and does not require as many changes to your existing code.

Integration Plan

Test out the integration here!

For the final code, please visit our working example app repository. This guide will show you how to start a new project to use verifications provided by Shield3 by guiding you through how we integrated with Privy's auth-demo app. Pre-existing projects should be able to find relevant information here as well.

Before starting, make sure to have your Project ID (you can get it here) and your Shield3 API Key (found here). You should also navigate to the workflows section and enable the policy for blocking unknown contract calls.

Warning, you should remove all mentions DynamicWagmiConnector as it will cause dependency conflicts, I recommend dropping it from the package.json, then npm install and delete all the highlighted errors.

Setting up

To set up, we will need to do the following:

  1. Clone the nextjs-viem-wagmi-v2 repository
  2. Configure env variables
  3. Install packages
# 1. Cloning the repository
git clone https://github.com/0xShield3/examples/tree/main/Dynamic/nextjs-viem-wagmi-v2

# 2. Copy the .env.example.local file into .env.local
cd auth-demo
cp .env.example.local .env.local
# Now set NEXT_PUBLIC_SHIELD3_API_KEY and NEXT_PUBLIC_PRIVY_APP_ID

3. Install Packages
npm i

Integrating and testing

  1. Install the Shield3 React SDK
# 1. Install the Shield3 React SDK
## Make sure you are at the root of the auth-demo repository
npm install @shield3/react-sdk
  1. Verifying transactions with custom component
"use client"
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import {useShield3Context} from "@shield3/react-sdk"
import type { RoutingDecision } from '@shield3/react-sdk/dist/shield3/simulate'
import React, { useState } from 'react'


interface Transaction {
    to: string;
    data?: string;
    nonce: number;
    value: number;
    chainId: number;
    gasLimit: number;
    [key: string]: unknown; // Allows any number of other unknown keys
}


const exampleFlaggedTx = {
    to: '0x6aabdd49a7f97f5242fd0fd6938987e039827666',
    data: '0xa9059cbb0000000000000000000000006aabdd49a7f97f5242fd0fd6938987e03982766600000000000000000000000000000000000000000000000001e32b4789740000',
    nonce: 1,
    value: 0,
    chainId: 1,
    gasLimit: 100000,
}

const Signer = () => {
    const { primaryWallet } = useDynamicContext()
    const { shield3Client } = useShield3Context()

    const [result, setResult] = useState<RoutingDecision| string | null>(null)
    const [response, setResponse] = useState("")

    const sign = async (isBlocked:boolean) => {
        setResult("Getting Results...")
        const connectedAccounts = await primaryWallet?.connector.getConnectedAccounts() ?? []
        const account = connectedAccounts[0]
        let transaction:Transaction
        if (isBlocked) transaction = exampleFlaggedTx
        else
            transaction = {
                to: account,
                nonce: 1,
                value: 0,
                chainId: 1,
                gasLimit: 100000,
            }

        console.log({ transaction, account })
        const results = await shield3Client.getPolicyResults(transaction, account as `0x${string}`)
        setResult(results?.decision ?? null)
        setResponse(JSON.stringify(results,null,2))
    }

    return (
        <div className='border border-white rounded-lg p-20 bg-black flex flex-col m-2'>
            <h1 className='m-2 text-white'>Get Policy Results</h1>
            <h2 className='m-2 text-white'>Result: {result}</h2>
            <button className="bg-white text-black p-5 m-2 rounded-lg transition duration-700 hover:bg-purple-500" type="button" onClick={() => sign(true)}>Try flagged transaction</button>
            <button className="bg-white text-black p-5 m-2 rounded-lg transition duration-700 hover:bg-purple-500" type="button" onClick={() => sign(false)}>Try allowed transaction</button>
            <div style={{ backgroundColor: '#282c34', padding: '20px', borderRadius: '8px', color: 'lightgreen' , width:'500px',height: '300px',overflow: 'auto'}}>
            <pre style={{
                whiteSpace: 'pre-wrap',       // Maintains whitespace
                wordWrap: 'break-word',       // Prevents long text from overflowing
                overflow: 'auto',
                borderRadius: '8px'              // Ensures scrollability within the pre tag
                }}>
                    <code>
                        {response}
                    </code>
                </pre>
            </div>
        </div>
    )
}

export default Signer

  1. Wrap your app and add the Signer component
"use client"
import { Shield3Provider } from "@shield3/react-sdk";
import { DynamicWidget } from "../lib/dynamic";
import Signer from "./Signer";


export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm lg:flex">
      <Shield3Provider apiKey={process.env.NEXT_PUBLIC_SHIELD3_API_KEY as string} chainId={1}>
        <div className="flex flex-col items-center">
        <DynamicWidget />
        <Signer/>
        </div>
      </Shield3Provider>
      </div>
    </main>
  );
}