Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ID_HUB_ADDR=
SELF_SCOPE=
SELF_CONFIG_ID=

NEXT_PUBLIC_SELF_APP_NAME=
NEXT_PUBLIC_SELF_SCOPE=
NEXT_PUBLIC_SELF_ENDPOINT=
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: CI Workflow

on:
workflow_dispatch:
push:
branches:
- "main"
pull_request:

env:
ID_HUB_ADDR: "0x68c931C9a534D37aa78094877F46fE46a49F1A51"
SELF_SCOPE: "8881796546691635711476357071947099946348416895373566002268674093031321389112"
SELF_CONFIG_ID: "0xc52f992ebee4435b00b65d2c74b12435e96359d1ccf408041528414e6ea687bc"
NEXT_PUBLIC_SELF_APP_NAME: "DHK dao Identity Verification"
NEXT_PUBLIC_SELF_SCOPE: "8881796546691635711476357071947099946348416895373566002268674093031321389112"
NEXT_PUBLIC_SELF_ENDPOINT: "https://burro-concise-regularly.ngrok-free.app/api/verify"

jobs:
ci-check:
runs-on: "ubuntu-latest"
steps:
- name: "Check out the repo"
uses: "actions/checkout@v4"

- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1.4.0"

- name: "Install NodeJS"
uses: actions/setup-node@v4
with:
node-version: 22

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false

- name: "Restore the cached build and the node modules"
if: ${{ inputs.restore-cache }}
uses: "actions/cache/restore@v4"
with:
key: "build-and-modules-${{ github.sha }}"
path: ${{ inputs.cache-path }}

- name: "Install the Node.js dependencies"
run: "pnpm install"

- name: "Build and test"
run: |
pnpm build
pnpm test

- name: "Summary"
run: |
echo "## Result" >> $GITHUB_STEP_SUMMARY
echo "✅ ci-check Passed" >> $GITHUB_STEP_SUMMARY
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# Self-app
# Self Prototype

DHK dao snapshot-self plugin prototype

- Self: https://self.xyz
- docs: https://docs.self.xyz/
- Self Developer Tools: https://tools.self.xyz/

- Snapshot: https://docs.snapshot.box/developer-guides/validation-strategy
- [DHK dao snapshot page](https://snapshot.box/#/s:dhkdao.eth)
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "@dhkdao/self-app",
"name": "@dhkdao-self/app",
"private": true,
"license": "MIT",
"version": "1.0.0",
"description": "Self.xyz plugin prototype",
Expand All @@ -24,5 +25,6 @@
"devDependencies": {
"commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0"
}
},
"packageManager": "pnpm@10.14.0"
}
2 changes: 1 addition & 1 deletion packages/contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
libs = ["node_modules"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 0 additions & 1 deletion packages/contracts/lib/forge-std
Submodule forge-std deleted from 8bbcf6
18 changes: 18 additions & 0 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@dhkdao-self/contracts",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "forge test"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.14.0",
"dependencies": {
"@selfxyz/contracts": "^1.2.0",
"forge-std": "github:foundry-rs/forge-std#v1.10.0",
"solady": "^0.1.24"
}
}
2 changes: 2 additions & 0 deletions packages/contracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
forge-std/=node_modules/forge-std/src/
solady/=node_modules/solady/src/
75 changes: 75 additions & 0 deletions packages/contracts/src/SelfVerification.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {SelfVerificationRoot} from "@selfxyz/contracts/contracts/abstract/SelfVerificationRoot.sol";
import {ISelfVerificationRoot} from "@selfxyz/contracts/contracts/interfaces/ISelfVerificationRoot.sol";
import {Ownable} from "solady/auth/Ownable.sol";

contract SelfVerification is SelfVerificationRoot, Ownable {
// Store verification status for each user
mapping(address => bool) public verifiedHumans;
bytes32 public verificationConfigId;
address public lastUserAddress;

// Event to track successful verifications
event VerificationCompleted(
ISelfVerificationRoot.GenericDiscloseOutputV2 output,
bytes userData
);

/**
* @notice Constructor for the contract
* @param _identityVerificationHubV2Address The address of the Identity Verification Hub V2
* @param _scope The scope of the contract
* @param _verificationConfigId The configuration ID for the contract
*/
constructor(
address _identityVerificationHubV2Address,
uint256 _scope,
bytes32 _verificationConfigId
) SelfVerificationRoot(_identityVerificationHubV2Address, _scope) {
verificationConfigId = _verificationConfigId;
_setOwner(msg.sender);
}

/**
* @notice Implementation of customVerificationHook
* @dev This function is called by onVerificationSuccess after hub address validation
* @param output The verification output from the hub
* @param userData The user data passed through verification
*/
function customVerificationHook(
ISelfVerificationRoot.GenericDiscloseOutputV2 memory output,
bytes memory userData
) internal override {
lastUserAddress = address(uint160(output.userIdentifier));
verifiedHumans[lastUserAddress] = true;

emit VerificationCompleted(output, userData);

// Add your custom logic here:
// - Mint NFT to verified user
// - Add to allowlist
// - Transfer tokens
// - etc.
}

function getConfigId(
bytes32 _destinationChainId,
bytes32 _userIdentifier,
bytes memory _userDefinedData
) public view override returns (bytes32) {
return verificationConfigId;
}

/**
* @notice Check if an address is a verified human
*/
function isVerifiedHuman(address _user) external view returns (bool) {
return verifiedHumans[_user];
}

function setConfigId(bytes32 _configId) external onlyOwner {
verificationConfigId = _configId;
}
}
28 changes: 28 additions & 0 deletions packages/contracts/test/SelfVerification.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import {Test, console} from "forge-std/Test.sol";
import {SelfVerification} from "../src/SelfVerification.sol";

contract SelfVerificationTest is Test {
SelfVerification public sv;

function setUp() public {
address idHubAddr = vm.envAddress("ID_HUB_ADDR");
uint256 scope = vm.envUint("SELF_SCOPE");
bytes32 configId = vm.envBytes32("SELF_CONFIG_ID");

sv = new SelfVerification(idHubAddr, scope, configId);
}

function test_ConfigId() public {
bytes32 chainId = bytes32(0);
bytes32 userId = bytes32(0);
bytes memory data = bytes("0x");

bytes32 expectedConfigId = vm.envBytes32("SELF_CONFIG_ID");

bytes32 configId = sv.getConfigId(chainId, userId, data);
assertEq(configId, expectedConfigId);
}
}
16 changes: 16 additions & 0 deletions packages/web/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const nextConfig = {
/* config options here */
reactStrictMode: true,
compiler: {
styledComponents: true,
},
webpack: (config, {isServer}) => {
if (isServer) {
config.externals = [...(config.externals || []), "@selfxyz/qrcode"];
}

return config;
},
};

export default nextConfig;
7 changes: 0 additions & 7 deletions packages/web/next.config.ts

This file was deleted.

32 changes: 19 additions & 13 deletions packages/web/package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
{
"name": "web",
"name": "@dhkdao-self/web",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.4.6"
"@selfxyz/common": "^0.0.7",
"@selfxyz/core": "^1.0.8",
"@selfxyz/qrcode": "^1.0.11",
"geist": "^1.4.2",
"next": "14.2.30",
"react": "^18",
"react-dom": "^18",
"viem": "^2.33.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^9",
"eslint-config-next": "15.4.6",
"@eslint/eslintrc": "^3"
}
"tailwindcss": "^4",
"typescript": "^5"
},
"packageManager": "pnpm@10.14.0"
}
86 changes: 86 additions & 0 deletions packages/web/src/app/api/verify/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { NextRequest, NextResponse } from "next/server";
import { countries, Country3LetterCode, SelfAppDisclosureConfig } from "@selfxyz/common";
import {
countryCodes,
SelfBackendVerifier,
AllIds,
DefaultConfigStore,
VerificationConfig
} from "@selfxyz/core";

export async function POST(req: NextRequest) {
console.log("Received request");
console.log(req);

try {
const body = await req.json();
const { attestationId, proof, publicSignals, userContextData } = body;

if (!attestationId || !proof || !publicSignals || !userContextData) {
return NextResponse.json({
message: "Proof, publicSignals, attestationId and userContextData are required",
}, {
status: 400
});
}

const requirements: VerificationConfig = {
minimumAge: 18,
ofac: false,
excludedCountries: [],
}

const configStore = new DefaultConfigStore(requirements);

const selfBackendVerifier = new SelfBackendVerifier(
process.env.NEXT_PUBLIC_SELF_SCOPE || "",
process.env.NEXT_PUBLIC_SELF_ENDPOINT || "",
false,
AllIds,
configStore,
"hex",
);

const result = await selfBackendVerifier.verify(
attestationId,
proof,
publicSignals,
userContextData
);

if (!result.isValidDetails.isValid) {
return NextResponse.json({
status: "error",
result: false,
message: "Verification failed",
details: result.isValidDetails,
}, { status: 500 });
}

const saveOptions = (await configStore.getConfig(
result.userData.userIdentifier
)) as unknown as SelfAppDisclosureConfig;

return NextResponse.json({
status: "success",
result: result.isValidDetails.isValid,
credentialSubject: result.discloseOutput,
verificationOptions: {
minimumAge: saveOptions.minimumAge,
ofac: saveOptions.ofac,
excludedCountries: saveOptions.excludedCountries?.map((countryName: string) => {
const entry = Object.entries(countryCodes).find(([_, name]) => name === countryName);
return entry ? entry[0] : countryName;
}),
}
});

} catch (error) {
console.error("Error verifying proof:", error);
return NextResponse.json({
status: "error",
result: false,
message: error instanceof Error ? error.message : "Unknown error",
});
}
}
Loading