Minimum disclosure, maximum coordination.
Satisfy is a credential-aware policy layer for Uniswap v4-style markets. It gates participation on verifiable proofs, not wallet identity.
A DAO spins up a launch pool on Unichain Sepolia.
At first block, bot clusters start rotating wallets to farm rewards. Normally, each fresh address looks like a fresh participant. Satisfy changes the game: access is based on proof, not wallet cosmetics.
A real participant sends a proof bundle:
- World ID personhood proof (checked on-chain through verifier path)
- Self-derived eligibility attestation (bridged and stored in
SelfAttestationRegistry)
The hook calls policy in real time before market execution:
- valid and in-policy: swap/liquidity action continues
- stale/replayed/revoked/out-of-policy: transaction is rejected
When risk signals appear (revocations, signer compromise), Reactive automation can rotate epoch or pause enforcement, so governance doesn’t rely on a centralized ops desk to react first.
This is the Satisfy thesis in production terms:
- prove what matters
- reveal nothing extra
- coordinate at market speed
SatisfyPolicyEngine- Adapter/policy registry, predicate logic (
AND/OR), replay protection. - Global pause gate for emergency response.
- Adapter/policy registry, predicate logic (
SatisfyHook- Pool-to-policy routing and policy enforcement at
beforeSwap/beforeAddLiquidity. - Independent pause gate for market enforcement.
- Pool-to-policy routing and policy enforcement at
WorldIdAdapter- On-chain verifier call path (
IWorldIdVerifier) with strict domain-separated signal checks. - Freshness controls via
issuedAt,validUntil, andmaxProofAge.
- On-chain verifier call path (
SelfAttestationRegistry- Domain-separated signer attestations with nonce replay protection.
- Carries bridge reference metadata (
sourceChainId,sourceBridgeId,sourceTxHash,sourceLogIndex). - Revocation and trusted signer rotation.
SelfAdapter- Consumes only on-chain registry attestations.
- Context binding to
(chainId, adapter, user, policyCondition).
SatisfyAutomationModule- Role-gated control plane with reactive replay-protected jobs.
REACTIVE_EXECUTOR_ROLEis granted toSatisfyReactiveGateway, not an EOA.- Roles:
POLICY_MANAGER_ROLEADAPTER_MANAGER_ROLEHOOK_MANAGER_ROLEREACTIVE_EXECUTOR_ROLEEMERGENCY_ROLE
SatisfyReactiveGateway- On-chain ingress for hosted workers.
- Verifies signed
JobV1messages with chain/contract/automation domain separation. - Enforces worker nonce and digest replay protection before dispatching to automation.
SatisfyTimelock- Safe-compatible proposer/executor timelock.
- Intended to be role admin for governance hardening.
+----------------------+
| World ID Verifier |
+----------+-----------+
|
v
+-------------------+ proofs +------------+-------------+
| User Wallet + UI +----------->| SatisfyHook (Unichain) |
+-------------------+ +------------+-------------+
|
v
+------------+-------------+
| SatisfyPolicyEngine |
| (policy + replay checks) |
+------+--------------+-----+
| |
v v
+---------+--+ +-----+------------------+
|WorldIdAdapter| |SelfAdapter |
+---------+----+ +-----+-----------------+
| |
| v
| +---------+------------------+
| |SelfAttestationRegistry |
| |(bridged attestations) |
| +---------+-------------------+
| |
| events v
| +---------+-------------------+
| |Reactive Network (Lasna) |
| |SatisfyLasnaReactiveProcessor|
| +---------+-------------------+
| |
| callback v
| +---------+-------------------+
| |SatisfyReactiveCallbackReceiver|
| +---------+-------------------+
| |
| v
| +---------+-------------------+
| |SatisfyReactiveGateway |
| +---------+-------------------+
| |
| v
| +---------+-------------------+
+----->|SatisfyAutomationModule |
+---------+-------------------+
|
+---------+-------------------+
|Policy/Hook lifecycle updates|
+-----------------------------+
flowchart LR
U[User Wallet] --> FE[Frontend dApp]
FE --> H[SatisfyHook<br/>Unichain]
H --> PE[SatisfyPolicyEngine]
PE --> WA[WorldIdAdapter]
WA --> WV[World ID Verifier]
PE --> SA[SelfAdapter]
SA --> SR[SelfAttestationRegistry]
SELF[Self Provider / Bridge Relay] --> SR
SR -- AttestationRevoked / TrustedSignerUpdated --> LP[SatisfyLasnaReactiveProcessor<br/>Reactive Lasna]
LP --> CP[Reactive Callback Proxy<br/>Unichain]
CP --> CR[SatisfyReactiveCallbackReceiver]
CR --> RG[SatisfyReactiveGateway]
RG --> AM[SatisfyAutomationModule]
AM --> PE
AM --> H
PE --> DEC{Policy satisfied?}
DEC -- yes --> EXEC[Swap/Liquidity action executes]
DEC -- no --> REJ[Transaction reverts]
Prereqs:
- Foundry (
forge,cast,anvil) bash
Build and test:
forge build --offline
forge test --offlineRun local E2E (deploy + policy + proofs + hook execution + replay + epoch + pause):
./script/anvil_e2e.shcp script/.env.unichain.example .env.unichain
source .env.unichain
UNICHAIN_NETWORK=sepolia ./script/deploy_unichain.shSupported networks:
sepolia(chainId=1301)mainnet(chainId=130)
Deployment output includes:
- core contract addresses
- governance/timelock role config
- verifier + registry config
- deployment artifact JSON
Safe-first default:
- set
SAFE_ADDRESSin.env.unichainto use Safe as default for automation owner, timelock admin/proposer/executor, and emergency actor.
Core Satisfy contracts stay on Unichain. Reactive Network is used to watch emitted events and execute callbacks back into Unichain:
- Deploy core contracts to Unichain (
deploy_unichain.sh). - Deploy reactive integration contracts:
SatisfyReactiveCallbackReceiveron UnichainSatisfyLasnaReactiveProcessoron Lasna testnet
- Wire callback authorization on
SatisfyReactiveGateway.
source .env.unichain
DEPLOYER_PK=0x... \
LASNA_DEPLOYER_PK=0x... \
./script/deploy_reactive_pipeline.sh deployments/unichain-sepolia.jsonDefault Lasna RPC: https://lasna-rpc.rnk.dev.
Reference: docs/REACTIVE_NETWORK_LASNA.md
SelfAttestationRegistryemits lifecycle events on Unichain.SatisfyLasnaReactiveProcessor(Lasna) subscribes and emits Reactive callbacks.- Reactive callback proxy calls
SatisfyReactiveCallbackReceiveron Unichain. - Receiver dispatches into
SatisfyReactiveGateway.executeFromReactiveCallback(...). - Gateway enforces callback authorization + replay guards and executes automation action.
Unichain Sepolia (chainId=1301):
PolicyEngine:0x7d4A7CD841ADF25a6b044066Aa8cd5f16B326e6FHook:0xc7c1fBcCe7A0Bc8bE8bb4F58F1177F3B67343741SelfRegistry:0xD88C3EaC6DE6583218EA46862f8fEB5506E470f1ReactiveGateway:0xC70B4A3525c1Cd6eBef2715FE4ed942D79aCd38FReactiveCallbackReceiver:0x236baa6AEb458d7b91d7863ccE06FDd34020AecE
Reactive Lasna (chainId=5318007):
SatisfyLasnaReactiveProcessor:0x2Be10838793F745f0E3550193C4720e9870e9E76
Current callback proxy used for Unichain Sepolia:
destinationCallbackSender:0x4d7d194675E6844f7E23C1e830d6A03071DF4f4D
Post a testnet attestation into SelfAttestationRegistry using domain-separated signature + nonce protection:
RPC_URL=https://sepolia.unichain.org \
RELAYER_PK=0x... \
RELAY_SIGNER_PK=0x... \
SELF_REGISTRY=0x... \
SUBJECT=0x... \
CONTEXT=0x... \
./script/relay_self_attestation_mock.shThis outputs a ready-to-use VITE_SELF_PROOF_PAYLOAD value for frontend tests.
Run the worker loop that watches registry events and submits signed jobs to SatisfyReactiveGateway.execute(...):
source .env.unichain
export REACTIVE_WORKER_PK=0x...
export REACTIVE_RELAYER_PK=0x...
./script/reactive_event_executor.sh deployments/unichain-sepolia.jsonBehavior:
- revocation events can rotate epoch
- signer disable events can trigger emergency pause
- optional timer can rotate epoch
- all lifecycle mutations flow through gateway-verified worker signatures
Reference: docs/REACTIVE_EXECUTOR.md
After deployment, run governance + optional fixture replay checks:
./script/unichain_smoke.sh deployments/unichain-sepolia.jsonOptional fixture replay inputs:
SMOKE_USERWORLD_PROOF_PAYLOADSELF_ATTESTATION_PAYLOADSELF_ATTESTATION_SIGNATURESELF_PROOF_PAYLOAD
cp frontend/.env.example frontend/.env.local
npm --prefix frontend install
npm --prefix frontend run devThe UI supports:
satisfies()beforeSwap()- proof payload schema validation for
WorldIdProofV1andSelfAttestationProofV1
Copy deployment artifacts into frontend static assets:
./script/sync_frontend_artifact.sh deployments/unichain-sepolia.jsonThen set:
VITE_UNICHAIN_SEPOLIA_DEPLOYMENT_ARTIFACT=/deployments/unichain-sepolia.jsonCI runs:
- full Foundry tests
- local anvil E2E script
- frontend lint/build
- real-data replay lane
- optional Unichain smoke lane (when Unichain smoke secrets are configured)
Real-data lane replays recorded provider fixtures from CI secrets:
./script/ci_real_data_replay.shExpected secret:
REALDATA_FIXTURE_JSON_B64- optional:
UNICHAIN_SMOKE_DEPLOYMENT_B64UNICHAIN_SMOKE_RPC_URLUNICHAIN_SMOKE_USERUNICHAIN_SMOKE_WORLD_PROOF_PAYLOADUNICHAIN_SMOKE_SELF_ATTESTATION_PAYLOADUNICHAIN_SMOKE_SELF_ATTESTATION_SIGNATUREUNICHAIN_SMOKE_SELF_PROOF_PAYLOADUNICHAIN_SMOKE_RELAYER_PK
Fixture schema example: docs/real_data_fixture.example.json.
Encoding reference: docs/REAL_DATA_FIXTURE.md.
To build a fixture JSON + base64 value locally:
./script/build_realdata_fixture.shsrc/contractstest/unit + integration + replay testsscript/deploy_unichain.shUnichain deployment pipelinescript/deploy_reactive_pipeline.shdeploys Lasna reactive processor + Unichain callback receiverscript/anvil_e2e.shlocal full-path protocol testscript/relay_self_attestation_mock.shmock bridge relay submissionscript/ci_real_data_replay.shCI replay lanescript/ci_unichain_smoke.shCI smoke runner for deployed Unichain artifactscript/build_realdata_fixture.shfixture bundler for CI secret generationscript/unichain_smoke.shtestnet smoke assertions + fixture replayscript/reactive_event_executor.shhosted-worker compatible event daemon (submits signed jobs to gateway)frontend/React + Vite appdocs/runbooks and deployment docs
MIT