@okkult-dev/sdk
v0.3.0
Published
Okkult Protocol — Zero-Knowledge Privacy SDK for Ethereum
Maintainers
Readme
@okkult-dev/sdk
Zero-Knowledge Privacy SDK for the Okkult Protocol
Shield assets. Vote privately. Prove identity. Verify emails. All on Ethereum.
Install
npm install @okkult-dev/sdk ethers
# or
pnpm add @okkult-dev/sdk ethers
# or
yarn add @okkult-dev/sdk ethersQuick Start
import { OkkultSDK } from '@okkult-dev/sdk'
import { ethers } from 'ethers'
const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com')
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider)
// Mainnet — OkkultShield deployed at 0xDAB93E727B2382972C0863a7Ac22e8b80bfBbC88
const sdk = new OkkultSDK({
provider,
signer,
network: 'mainnet', // uses deployed contract addresses automatically
})Deployed Contracts (Mainnet)
| Contract | Address | Etherscan |
|----------|---------|-----------|
| OkkultShield | 0xDAB93E727B2382972C0863a7Ac22e8b80bfBbC88 | View ↗ |
| ShieldVerifier | 0xCe5ac79201892C60EaaeD69607354721219b5737 | View ↗ |
| UnshieldVerifier | 0xFD7888c3C0f47bd8D3C8699075Cf15b487373482 | View ↗ |
| TransferVerifier | 0x20dE927843cdc0CEfd3c82510cF835982c4313EE | View ↗ |
Modules
Shield — Private UTXO Pool
import { ShieldModule } from '@okkult-dev/sdk/shield'
const shield = new ShieldModule(config)
// Deposit 1000 USDC into the private pool
const result = await shield.shield(USDC_ADDRESS, 1_000_000_000n)
// → { utxo, txHash, leafIndex, fee }
// Transfer 400 USDC to a 0zk address (private, no on-chain link)
await shield.transfer(USDC_ADDRESS, 400_000_000n, recipientZkAddress, merkleProof)
// Withdraw 600 USDC to any public address
await shield.unshield(USDC_ADDRESS, 600_000_000n, recipient, merkleProof)
// Check shielded balance
const balance = await shield.getBalance(ownerAddress, USDC_ADDRESS)Vote — Private Governance
import { VoteModule } from '@okkult-dev/sdk/vote'
const vote = new VoteModule(config)
// Create a poll (0.01 ETH fee)
const { pollId } = await vote.createPoll(
'Treasury Allocation Q3 2026',
'Approve 500k USDC for protocol development.',
voterMerkleRoot,
startTime, endTime,
)
// Cast encrypted vote (1 = YES, 0 = NO)
await vote.castVote(pollId, voterAddress, voterSecret, 1, merkleProof)
// Coordinator tallies after poll closes
const result = await vote.tallyResults(pollId)
// → { totalYes: 312n, totalNo: 88n }ID — ZK Identity Credentials
import { IdentityModule, CredType } from '@okkult-dev/sdk/identity'
const id = new IdentityModule(config)
// Prove age >= 18 without revealing exact age
const proof = await id.proveAgeCredential(25, credSecret, 18, validIssuer)
// Prove balance >= 1 ETH (accredited investor)
const thresholdProof = await id.proveThreshold(
actualBalance, credSecret, ethers.parseEther('1')
)
// Verify on-chain
await id.verifyOnChain(userAddress, proof, CredType.AGE)
// Supported types: AGE, KYC, RESIDENCY, THRESHOLD, HUMANITY, DAO_MEMBERMail — Email ZK Proofs
import { MailModule } from '@okkult-dev/sdk/mail'
const mail = new MailModule(config)
// Generate proof from .eml file
const proof = await mail.generateEmailProof(rawEmail, {
type: 'KYC_APPROVED',
domain: 'coinbase.com',
})
// Submit claim on-chain (valid 90 days)
const { claimHash, validUntil } = await mail.submitClaim(proof)
// Check claim validity
const isValid = await mail.hasValidClaim(userAddress, claimHash)Relay — Gas Abstraction
import { RelayModule } from '@okkult-dev/sdk/relay'
const relay = new RelayModule(config)
// Get available relayers
const relayers = await relay.getActiveRelayers()
// Get fee quote
const fee = await relay.getFeeQuote(targetContract, calldata)
// Submit relayed transaction (no ETH needed by sender)
const { txHash, feePaid } = await relay.submitRequest(
{ target, calldata, maxFee: fee, deadline },
proof_a, proof_b, proof_c,
)ZK Utilities
import {
poseidonHash,
commitmentHash,
nullifierHash,
generateProof,
proofToCalldata,
} from '@okkult-dev/sdk'
// Poseidon hash (ZK-optimised, used by OkkultShield)
const hash = await poseidonHash([BigInt(address), amount])
// Generate Groth16 proof (browser + Node.js)
const { proof, publicSignals } = await generateProof(inputs, wasmPath, zkeyPath)
// Convert to Solidity calldata format
const { proof_a, proof_b, proof_c } = proofToCalldata(proof)Configuration
const sdk = new OkkultSDK({
provider, // ethers Provider
signer, // ethers Signer (required for write operations)
network: 'mainnet', // 'mainnet' | 'sepolia' | 'polygon' | 'arbitrum' | 'base'
circuitBaseUrl: 'https://circuits.okkult.io/v1', // circuit file CDN (optional)
// Override individual contract addresses (optional — defaults used from network)
contracts: {
okkultShield: '0xDAB93E727B2382972C0863a7Ac22e8b80bfBbC88',
okkultVote: '0x...',
okkultID: '0x...',
okkultMail: '0x...',
okkultRelay: '0x...',
dkimRegistry: '0x...',
},
})Circuit Files
Circuits are fetched from circuits.okkult.io by default. For self-hosting, set circuitBaseUrl:
| File | Description |
|------|-------------|
| shield.wasm + shield_final.zkey | Shield deposit |
| unshield.wasm + unshield_final.zkey | Unshield withdraw |
| transfer.wasm + transfer_final.zkey | Private transfer |
| vote.wasm + vote_final.zkey | Private voting |
| tally.wasm + tally_final.zkey | Vote tally |
| identity.wasm + identity_final.zkey | Identity base |
| ageProof.wasm + ageProof_final.zkey | Age proof |
| thresholdProof.wasm + thresholdProof_final.zkey | Threshold proof |
| emailProof.wasm + emailProof_final.zkey | Email ZK proof |
Changelog
v0.2.0 (2026-04-22)
- Mainnet deployment —
OkkultShieldlive at0xDAB93E727B2382972C0863a7Ac22e8b80bfBbC88 CONTRACT_ADDRESSES.mainnet.okkultShieldnow set to deployed address- All 4 contracts (Shield + 3 Groth16 Verifiers) source-verified on Etherscan
v0.1.0 (initial release)
- Core modules: Shield, Vote, Identity, Mail, Relay
- Groth16 proof generation via snarkjs
- Poseidon hashing via circomlibjs
License
MIT © Okkult Protocol
