From 484cca8cac97de3c1cceb8a98c4922b5d9b08f45 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:40 +0300 Subject: [PATCH 01/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/README.md | 198 +++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 demos/asterpay-kya/README.md diff --git a/demos/asterpay-kya/README.md b/demos/asterpay-kya/README.md new file mode 100644 index 0000000..dc4cf2f --- /dev/null +++ b/demos/asterpay-kya/README.md @@ -0,0 +1,198 @@ +# ACK-ID: AsterPay KYA Trust Score Demo + +This demo shows how [AsterPay](https://asterpay.io) KYA (Know Your Agent) Trust Score tokens integrate with ACK-ID's identity infrastructure. AsterPay provides a 5-layer trust verification framework for AI agent payments: VERIFY → SCREEN → SCORE → ATTEST → COMPLY. + +## Getting started + +Before starting, please follow the [Getting Started](../../README.md#getting-started) guide at the root of this monorepo. + +### Running the demo + +You can use the demo by running the following command from the root of this repository: + +```sh +pnpm run demo:asterpay-kya +``` + +Alternatively, you can run the demo from this directory (`./demos/asterpay-kya`) with: + +```sh +pnpm run demo +``` + +## What is ACK-ID? + +ACK-ID is a protocol built on W3C Standards that provides verifiable, secure identity infrastructure for agents. It uses DIDs (Decentralized Identifiers) and Verifiable Credentials to establish trust and enable agent-to-agent commerce. ACK-ID serves as the core identity layer that other agent systems can build upon. + +## What is AsterPay KYA? + +AsterPay's Know Your Agent (KYA) framework provides trust scoring and verification for AI agents. KYA tokens are JWT-based credentials that contain: + +- **Trust Score** (0-100): Composite score from 7 on-chain and off-chain signals +- **Tier Classification**: Open, Verified, Trusted, or Enterprise +- **Score Components**: Wallet Age, Transaction Activity, Sanctions Screening, ERC-8004 Identity, Operator KYB, Payment History, Trust Bond +- **InsumerAPI Attestations**: Third-party ES256-signed attestations for Coinbase KYC, country verification, Gitcoin Passport, and USDC balance +- **Sanctions Screening**: Chainalysis-powered sanctions check +- **Cryptographic Proof**: ES256 JWT signature from AsterPay's infrastructure + +## How AsterPay KYA Works with ACK-ID + +This demo demonstrates how KYA Trust Score tokens leverage ACK-ID's infrastructure: + +1. **Native Compatibility**: KYA JWTs convert to standard W3C Verifiable Credentials +2. **Cryptographic Integrity**: Bidirectional conversion preserves original signatures with perfect fidelity +3. **Trust-Score-Aware Verification**: ACK-ID verifiers can set minimum trust score thresholds +4. **ERC-8183 Integration**: IACPHook gates agent commerce jobs using trust score verification + +## Demo Flow + +### 1. KYA Trust Score Token Creation + +- Creates an AsterPay KYA token with trust score, 7 scoring components, InsumerAPI attestations, and sanctions screening result +- Simulates what AsterPay's API returns at `GET /v1/agent/trust-score/:address` + +### 2. JWT to Verifiable Credential Conversion + +- Converts the KYA JWT to a W3C Verifiable Credential +- Preserves all original JWT data and cryptographic signatures +- Generates an ACK-ID compatible DID: `did:web:api.asterpay.io:agent:{address}` +- Extracts trust score, tier, components, and attestation data + +### 3. Bidirectional Conversion + +- Demonstrates perfect fidelity conversion back to original JWT format +- Proves cryptographic integrity is maintained throughout the process +- Verifies `original JWT === reconstructed JWT` + +### 4. ACK-ID Verification with Trust Score Gate + +- Verification with configurable minimum trust score thresholds +- Automatic sanctions status checking +- Trusted issuer validation +- Shows PASS/BLOCK scenarios for different thresholds + +### 5. ERC-8183 IACPHook Simulation + +- Simulates an ERC-8183 Agentic Commerce Protocol job +- Runs 5-shield verification: VERIFY → SCREEN → SCORE → ATTEST → COMPLY +- Demonstrates how trust scores gate agent access to commerce jobs + +## Technical Implementation + +### Verifiable Credential Structure + +The demo creates a Verifiable Credential containing the full KYA trust data: + +```json +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://agentcommercekit.com/contexts/asterpay-kya/v1" + ], + "type": ["VerifiableCredential", "AsterPayKYACredential", "AgentTrustScoreCredential"], + "issuer": { "id": "did:web:api.asterpay.io" }, + "credentialSubject": { + "id": "did:web:api.asterpay.io:agent:0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", + "trustScore": 82, + "tier": "trusted", + "components": { + "walletAge": 12, + "transactionActivity": 11, + "sanctionsScreening": 15, + "ercIdentity": 15, + "operatorKyb": 10, + "paymentHistory": 9, + "trustBond": 10 + }, + "insumerAttestation": { + "coinbaseKyc": { "met": true }, + "coinbaseCountry": { "met": true, "country": "EU" }, + "gitcoinPassport": { "met": true, "minScore": 20 }, + "tokenBalance": { "met": true, "chain": "base", "token": "USDC", "minBalance": "100" } + }, + "sanctioned": false + } +} +``` + +### DID Generation + +The system generates a DID for the agent identity: + +- **Agent DID**: `did:web:api.asterpay.io:agent:{address}` + +### Trust Score Components + +| Component | Max Score | Description | +|-----------|-----------|-------------| +| Wallet Age | 15 | How long the wallet has existed | +| Transaction Activity | 15 | On-chain transaction history | +| Sanctions Screening | 15 | Chainalysis sanctions clearance | +| ERC-8004 Identity | 15 | Registered agent identity on-chain | +| Operator KYB | 15 | Know Your Business verification | +| Payment History | 15 | Historical payment reliability | +| Trust Bond | 10 | Staked collateral for trust | + +### Verification Flow + +1. **JWT Verification**: Validates the KYA token signature using AsterPay's JWKS +2. **Trust Check**: Ensures AsterPay is in the trusted issuers list +3. **Expiration Check**: Validates token hasn't expired +4. **Sanctions Check**: Verifies agent is not sanctioned +5. **Trust Score Gate**: Checks score meets minimum threshold +6. **Attestation Check**: Validates Coinbase KYC attestation + +## Sample Output + +```txt +KYA Trust Score × ACK-ID Demo + +✨ === AsterPay KYA Trust Score → ACK-ID Integration Demo === ✨ + +1. Creating AsterPay KYA Trust Score token... +✓ KYA token created + +2. Converting KYA JWT to ACK-ID Verifiable Credential... +✓ Verifiable Credential created + Trust Score: 82 / 100 + Tier: trusted + +3. Demonstrating bidirectional conversion... +✓ Successfully converted VC back to JWT + Original JWT matches reconstructed: true + +4. Running ACK-ID verification with trust score gate... + 4a. minTrustScore=50: ✓ PASSED + 4b. minTrustScore=90: ✗ BLOCKED + 4c. Untrusted issuer: ✗ BLOCKED + +5. Simulating ERC-8183 IACPHook with ACK-ID... + ✅ VERIFY: ERC-8004 identity confirmed + ✅ SCREEN: Chainalysis sanctions clear + ✅ SCORE: Trust score 82 ≥ 50 minimum + ✅ ATTEST: InsumerAPI — KYC, Country, Passport, USDC + ✅ COMPLY: Tier "trusted" authorized + ✓ IACPHook: APPROVED — Agent may fund the job + +🎉 Demo complete +``` + +## Production Considerations + +For production deployment: + +1. **JWKS Endpoint**: Fetch AsterPay's current JWKS from `https://api.asterpay.io/.well-known/jwks.json` +2. **Trust Configuration**: Configure AsterPay as a trusted issuer in ACK-ID systems +3. **Score Thresholds**: Set appropriate minimum trust scores per use case +4. **Attestation Validation**: Verify InsumerAPI attestation signatures independently +5. **Sanctions Monitoring**: Implement real-time sanctions screening updates + +## Learn More + +- [Agent Commerce Kit](https://www.agentcommercekit.com) Documentation +- [ACK-ID](https://www.agentcommercekit.com/ack-id) Documentation +- [W3C Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) Specification +- [AsterPay](https://asterpay.io) — The Trust Layer for AI Agent Payments +- [InsumerAPI](https://insumermodel.com) — Third-party attestation provider +- [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) — Agent Identity Standard +- [ERC-8183](https://eips.ethereum.org/EIPS/eip-8183) — Agentic Commerce Protocol From 408f04961cea9389ae9cefc4c524571f5ad75e50 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:41 +0300 Subject: [PATCH 02/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/package.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 demos/asterpay-kya/package.json diff --git a/demos/asterpay-kya/package.json b/demos/asterpay-kya/package.json new file mode 100644 index 0000000..2e6b2ba --- /dev/null +++ b/demos/asterpay-kya/package.json @@ -0,0 +1,33 @@ +{ + "name": "@demos/asterpay-kya", + "version": "0.0.1", + "private": true, + "homepage": "https://github.com/agentcommercekit/ack#readme", + "bugs": "https://github.com/agentcommercekit/ack/issues", + "license": "MIT", + "author": { + "name": "AsterPay", + "url": "https://asterpay.io" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/agentcommercekit/ack.git", + "directory": "demos/asterpay-kya" + }, + "type": "module", + "scripts": { + "check:types": "tsc --noEmit", + "clean": "git clean -fdX .turbo", + "demo": "tsx ./src/index.ts", + "test": "vitest" + }, + "dependencies": { + "@repo/cli-tools": "workspace:*", + "agentcommercekit": "workspace:*", + "jose": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@repo/typescript-config": "workspace:*" + } +} From 3bd358ea57977f0bf27c09e1fef1b435f7b4fe29 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:41 +0300 Subject: [PATCH 03/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/src/asterpay-kya-ack-id.ts | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 demos/asterpay-kya/src/asterpay-kya-ack-id.ts diff --git a/demos/asterpay-kya/src/asterpay-kya-ack-id.ts b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts new file mode 100644 index 0000000..ee94cfa --- /dev/null +++ b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts @@ -0,0 +1,220 @@ +import { + type DidUri, + type DidWebUri, + type JwtString, + type Verifiable, + type W3CCredential, +} from "agentcommercekit" +import * as jose from "jose" + +import type { AsterPayKyaJwtPayload } from "./kya-token" + +export interface AsterPayKyaCredentialSubject { + id: DidUri + agentAddress: string + agentId?: string + trustScore: number + tier: string + components: AsterPayKyaJwtPayload["components"] + insumerAttestation: AsterPayKyaJwtPayload["insumerAttestation"] + sanctioned: boolean + jti: string +} + +export async function convertAsterPayKyaToVerifiableCredential( + jwks: jose.JSONWebKeySet, + kyaToken: JwtString, +): Promise>> { + const verifier = jose.createLocalJWKSet(jwks) + const { payload } = await jose.jwtVerify( + kyaToken, + verifier, + { + issuer: "https://api.asterpay.io/", + typ: "kya+JWT", + }, + ) + + const jwtParts = kyaToken.split(".") + if (jwtParts.length !== 3) { + throw new Error("Invalid JWT format") + } + const jwtSignature = jwtParts[2] + + if (!payload.iat || !payload.exp || !payload.jti || !payload.sub) { + throw new Error("Invalid JWT payload: missing required standard claims") + } + + if ( + typeof payload.trustScore !== "number" || + typeof payload.sanctioned !== "boolean" || + !payload.agentAddress || + !payload.tier || + !payload.components || + !payload.insumerAttestation + ) { + throw new Error( + "Invalid JWT payload: missing trust-critical claims (trustScore, sanctioned, agentAddress, tier, components, insumerAttestation)", + ) + } + + const agentDid: DidWebUri = + `did:web:api.asterpay.io:agent:${payload.sub}` as DidWebUri + + const syntheticVC: Verifiable> = { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://agentcommercekit.com/contexts/asterpay-kya/v1", + ], + id: `urn:uuid:${payload.jti}`, + type: [ + "VerifiableCredential", + "AsterPayKYACredential", + "AgentTrustScoreCredential", + ], + issuer: { id: "did:web:api.asterpay.io" }, + issuanceDate: new Date(payload.iat * 1000).toISOString(), + expirationDate: new Date(payload.exp * 1000).toISOString(), + credentialSubject: { + id: agentDid, + agentAddress: payload.agentAddress, + agentId: payload.agentId, + trustScore: payload.trustScore, + tier: payload.tier, + components: payload.components, + insumerAttestation: payload.insumerAttestation, + sanctioned: payload.sanctioned, + jti: payload.jti, + }, + proof: { + type: "JsonWebSignature2020", + created: new Date(payload.iat * 1000).toISOString(), + verificationMethod: "did:web:api.asterpay.io#kya-key-1", + proofPurpose: "assertionMethod", + jws: `${jwtParts[0]}..${jwtSignature}`, + originalPayload: jwtParts[1], + }, + } + + return syntheticVC +} + +export function getAgentDidFromVC( + vc: Verifiable>, +): DidWebUri { + return vc.credentialSubject.id as DidWebUri +} + +export function getTrustScoreFromVC( + vc: Verifiable>, +): number { + return vc.credentialSubject.trustScore +} + +export function getTierFromVC( + vc: Verifiable>, +): string { + return vc.credentialSubject.tier +} + +export function getInsumerAttestationFromVC( + vc: Verifiable>, +): AsterPayKyaCredentialSubject["insumerAttestation"] { + return vc.credentialSubject.insumerAttestation +} + +export function isSanctionedFromVC( + vc: Verifiable>, +): boolean { + return vc.credentialSubject.sanctioned +} + +export async function verifyAsterPayKyaAsAckId( + jwks: jose.JSONWebKeySet, + kyaToken: JwtString, + trustedIssuers: string[], + minTrustScore = 0, +): Promise<{ + valid: boolean + reason?: string + trustScore?: number + tier?: string +}> { + try { + const vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) + + if (!trustedIssuers.includes("did:web:api.asterpay.io")) { + return { valid: false, reason: "AsterPay not in trusted issuers" } + } + + if (vc.expirationDate && new Date() > new Date(vc.expirationDate)) { + return { valid: false, reason: "KYA token expired" } + } + + if (vc.credentialSubject.sanctioned) { + return { + valid: false, + reason: "Agent is sanctioned (Chainalysis)", + trustScore: vc.credentialSubject.trustScore, + tier: vc.credentialSubject.tier, + } + } + + if (vc.credentialSubject.trustScore < minTrustScore) { + return { + valid: false, + reason: `Trust score ${vc.credentialSubject.trustScore} below minimum ${minTrustScore}`, + trustScore: vc.credentialSubject.trustScore, + tier: vc.credentialSubject.tier, + } + } + + const att = vc.credentialSubject.insumerAttestation + if (!att.coinbaseKyc.met) { + return { + valid: false, + reason: "Coinbase KYC attestation not met", + trustScore: vc.credentialSubject.trustScore, + tier: vc.credentialSubject.tier, + } + } + + return { + valid: true, + trustScore: vc.credentialSubject.trustScore, + tier: vc.credentialSubject.tier, + } + } catch (error) { + return { + valid: false, + reason: `Verification error: ${(error as Error).message}`, + } + } +} + +export function convertVerifiableCredentialToAsterPayKya( + vc: Verifiable>, +): JwtString { + if (!vc.proof.jws) { + throw new Error("No JWS signature found in VC proof") + } + + const jwsParts = (vc.proof.jws as string).split("..") + if (jwsParts.length !== 2) { + throw new Error("Invalid JWS format in VC proof") + } + + const originalHeader = jwsParts[0] + const originalSignature = jwsParts[1] + + const originalPayload = (vc.proof as Record) + .originalPayload as string | undefined + + if (!originalPayload) { + throw new Error( + "No originalPayload found in VC proof — cannot reconstruct JWT losslessly", + ) + } + + return `${originalHeader}.${originalPayload}.${originalSignature}` as JwtString +} From f5129e81d3db261ca4b1d5dd3f36ac106727905d Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:42 +0300 Subject: [PATCH 04/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/src/index.ts | 315 ++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 demos/asterpay-kya/src/index.ts diff --git a/demos/asterpay-kya/src/index.ts b/demos/asterpay-kya/src/index.ts new file mode 100644 index 0000000..c435952 --- /dev/null +++ b/demos/asterpay-kya/src/index.ts @@ -0,0 +1,315 @@ +import { + colors, + errorMessage, + log, + logJson, + successMessage, + waitForEnter, +} from "@repo/cli-tools" +import type { JwtString, Verifiable, W3CCredential } from "agentcommercekit" +import type * as jose from "jose" + +import { generateJwks } from "./jwk-keys" +import { createMockAsterPayKyaToken } from "./kya-token" +import { + convertAsterPayKyaToVerifiableCredential, + convertVerifiableCredentialToAsterPayKya, + getAgentDidFromVC, + getInsumerAttestationFromVC, + getTierFromVC, + getTrustScoreFromVC, + isSanctionedFromVC, + verifyAsterPayKyaAsAckId, + type AsterPayKyaCredentialSubject, +} from "./asterpay-kya-ack-id" + +async function runDemo() { + const { jwks, keypair } = await generateJwks() + + log(` + _ _ ____ + / \\ ___| |_ ___ _ __| _ \\ __ _ _ _ + / _ \\ / __| __/ _ \\ '__| |_) / _\` | | | | + / ___ \\\\__ \\ || __/ | | __/ (_| | |_| | + /_/ \\_\\___/\\__\\___|_| |_| \\__,_|\\__, | + |___/ + KYA Trust Score × ACK-ID Demo + `) + + log("✨ === AsterPay KYA Trust Score → ACK-ID Integration Demo === ✨\n") + + log(`📝 Overview: + AsterPay's Know Your Agent (KYA) framework provides a 5-layer trust + verification system for AI agents: VERIFY → SCREEN → SCORE → ATTEST → COMPLY. + + The Trust Score (0-100) combines 7 on-chain and off-chain signals including + cryptographically signed attestations from InsumerAPI (Coinbase KYC, + country verification, Gitcoin Passport, USDC balance). + + This demo shows how AsterPay KYA tokens integrate with ACK-ID: + • AsterPay Trust Score JWT → W3C Verifiable Credential conversion + • Bidirectional conversion with full cryptographic integrity + • ACK-ID verification with trust score thresholds and sanctions checks + • ERC-8183 IACPHook gate simulation using ACK-ID infrastructure +`) + await waitForEnter("Press Enter to start the demo...") + + // Step 1: Create KYA token + log( + `In this first step, we create an AsterPay KYA token containing the agent's +trust score, 7 scoring components, InsumerAPI attestations (4 conditions), +and sanctions screening result. This is what AsterPay's API returns at +GET /v1/agent/trust-score/:address\n`, + ) + await waitForEnter() + + log("1. Creating AsterPay KYA Trust Score token...\n") + + const kyaToken = await createMockAsterPayKyaToken(keypair) + log(successMessage("\nKYA token created")) + log(colors.dim(kyaToken), { wrap: false }) + + // Step 2: Convert to ACK-ID VC + log( + `\nNext, we convert the AsterPay KYA JWT into an ACK-ID compatible +Verifiable Credential. This creates a W3C standard VC that any ACK-compatible +service can verify — without contacting AsterPay directly.\n`, + ) + await waitForEnter() + + log("2. Converting KYA JWT to ACK-ID Verifiable Credential...\n") + + let vc: Verifiable> + try { + vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) + log(successMessage("Verifiable Credential created:")) + logJson(vc) + + const att = getInsumerAttestationFromVC(vc) + log(` +Details: + Agent DID: ${colors.magenta(getAgentDidFromVC(vc))} + Trust Score: ${colors.magenta(String(getTrustScoreFromVC(vc)))} / 100 + Tier: ${colors.magenta(getTierFromVC(vc))} + Sanctioned: ${colors.magenta(String(isSanctionedFromVC(vc)))} + + InsumerAPI Attestations (3rd-party, ES256-signed): + ✅ Coinbase KYC: ${att.coinbaseKyc.met ? "verified" : "not verified"} + ✅ Country: ${att.coinbaseCountry.country} (${att.coinbaseCountry.met ? "verified" : "not verified"}) + ✅ Gitcoin Passport: score ≥ ${att.gitcoinPassport.minScore} (${att.gitcoinPassport.met ? "met" : "not met"}) + ✅ USDC Balance: ≥ ${att.tokenBalance.minBalance} on ${att.tokenBalance.chain} (${att.tokenBalance.met ? "met" : "not met"}) + + Trust Score Components: + • Wallet Age: ${vc.credentialSubject.components.walletAge}/15 + • Transaction Activity: ${vc.credentialSubject.components.transactionActivity}/15 + • Sanctions Screening: ${vc.credentialSubject.components.sanctionsScreening}/15 + • ERC-8004 Identity: ${vc.credentialSubject.components.ercIdentity}/15 + • Operator KYB: ${vc.credentialSubject.components.operatorKyb}/15 + • Payment History: ${vc.credentialSubject.components.paymentHistory}/15 + • Trust Bond: ${vc.credentialSubject.components.trustBond}/10 +`) + } catch (error: unknown) { + log(errorMessage("Conversion failed")) + log(colors.dim((error as Error).toString())) + return + } + + // Step 3: Bidirectional conversion + log( + `Now we demonstrate that the conversion maintains full cryptographic +integrity by converting the VC back to the original JWT format.\n`, + ) + await waitForEnter() + + log("3. Demonstrating bidirectional conversion...\n") + + try { + const reconstructedJwt = convertVerifiableCredentialToAsterPayKya(vc) + log(`${successMessage("Successfully converted VC back to JWT:")} + + Original JWT matches reconstructed: ${colors.magenta(kyaToken === reconstructedJwt ? "true" : "false")}`) + log(` Reconstructed JWT:`) + log(colors.dim(reconstructedJwt), { wrap: false }) + } catch (error: unknown) { + log(errorMessage("Bidirectional conversion failed")) + log(colors.dim((error as Error).toString())) + return + } + + // Step 4: ACK-ID verification with trust score gate + log( + `\nNext, we verify the KYA token using ACK-ID's verification infrastructure. +AsterPay adds trust-score-aware verification: the verifier can set a minimum +score threshold and the system automatically checks sanctions status.\n`, + ) + await waitForEnter() + + log("4. Running ACK-ID verification with trust score gate...\n") + + log(" 4a. Verification with minTrustScore=50 (should PASS)...") + const result1 = await verifyAsterPayKyaAsAckId( + jwks, + kyaToken, + ["did:web:api.asterpay.io"], + 50, + ) + if (result1.valid) { + log( + successMessage( + ` PASSED — Score: ${result1.trustScore}, Tier: ${result1.tier}`, + ), + ) + } else { + log(errorMessage(` FAILED — ${result1.reason}`)) + } + + log("\n 4b. Verification with minTrustScore=90 (should FAIL)...") + const result2 = await verifyAsterPayKyaAsAckId( + jwks, + kyaToken, + ["did:web:api.asterpay.io"], + 90, + ) + if (result2.valid) { + log(successMessage(` PASSED — Score: ${result2.trustScore}`)) + } else { + log( + errorMessage( + ` BLOCKED — ${result2.reason} (Score: ${result2.trustScore}, Tier: ${result2.tier})`, + ), + ) + } + + log( + "\n 4c. Verification with untrusted issuer (should FAIL)...", + ) + const result3 = await verifyAsterPayKyaAsAckId( + jwks, + kyaToken, + ["did:web:some-other-provider.xyz"], + 0, + ) + if (result3.valid) { + log(successMessage(` PASSED`)) + } else { + log(errorMessage(` BLOCKED — ${result3.reason}`)) + } + + // Step 5: ERC-8183 IACPHook simulation + log( + `\nFinally, we simulate how an ERC-8183 Agentic Commerce Protocol job uses +ACK-ID with AsterPay KYA to gate agent access. The IACPHook checks the +agent's trust score before allowing job funding or provider assignment.\n`, + ) + await waitForEnter() + + log("5. Simulating ERC-8183 IACPHook with ACK-ID...\n") + + await simulateIACPHook(jwks, kyaToken) + + log(` +🎉 Demo complete + +📋 Summary: + • AsterPay KYA tokens convert to W3C Verifiable Credentials for ACK-ID + • Trust Score (0-100) with 7 components + InsumerAPI attestations + • Bidirectional JWT ↔ VC conversion with full cryptographic integrity + • ACK-ID verification with configurable trust score thresholds + • Sanctions screening (Chainalysis) integrated into verification + • ERC-8183 IACPHook can use ACK-ID to gate agent commerce + • AsterPay serves as a Trust Provider in the ACK ecosystem + +🔗 Links: + • AsterPay: https://asterpay.io + • KYA API: https://api.asterpay.io/v1/agent/trust-score/:address + • ERC-8183 KYA Hook: https://github.com/AsterPay/erc8183-kya-hook + • InsumerAPI: https://insumermodel.com + • ACK: https://agentcommercekit.com +`) +} + +async function simulateIACPHook( + jwks: jose.JSONWebKeySet, + kyaToken: JwtString, +) { + log(" 📋 ERC-8183 Job: 'Analyze Q4 market data' (budget: 50 USDC)") + log(" 🤖 Agent requests to fund the job...") + log(" 🔒 IACPHook.beforeAction(fund) triggered\n") + + log(" → Resolving agent identity via ACK-ID...") + + const minScore = 50 + const verification = await verifyAsterPayKyaAsAckId( + jwks, + kyaToken, + ["did:web:api.asterpay.io"], + minScore, + ) + + if (!verification.valid) { + log( + `\n ${errorMessage(`IACPHook: BLOCKED — ${verification.reason}`)}`, + ) + log(` → Emitting ReputationNegative event`) + return + } + + const vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) + + log( + ` → Agent: ${colors.magenta(vc.credentialSubject.agentAddress)}`, + ) + log( + ` → ERC-8004 ID: ${colors.magenta(vc.credentialSubject.agentId ?? "none")}`, + ) + log( + ` → Trust Score: ${colors.magenta(String(vc.credentialSubject.trustScore))} / 100`, + ) + log( + ` → Tier: ${colors.magenta(vc.credentialSubject.tier)}`, + ) + + log("\n → Running 5-shield verification:") + + const hasIdentity = !!vc.credentialSubject.agentId + log( + ` ${hasIdentity ? "✅" : "❌"} VERIFY: ERC-8004 identity ${hasIdentity ? `confirmed (${vc.credentialSubject.agentId})` : "missing"}`, + ) + + const notSanctioned = !vc.credentialSubject.sanctioned + log( + ` ${notSanctioned ? "✅" : "❌"} SCREEN: Chainalysis sanctions ${notSanctioned ? "clear" : "FLAGGED"} (sanctioned=${vc.credentialSubject.sanctioned})`, + ) + + const scorePass = vc.credentialSubject.trustScore >= minScore + log( + ` ${scorePass ? "✅" : "❌"} SCORE: Trust score ${vc.credentialSubject.trustScore} ${scorePass ? "≥" : "<"} ${minScore} minimum`, + ) + + const att = vc.credentialSubject.insumerAttestation + const attestPass = + att.coinbaseKyc.met && att.gitcoinPassport.met && att.tokenBalance.met + log( + ` ${attestPass ? "✅" : "❌"} ATTEST: InsumerAPI — KYC=${att.coinbaseKyc.met}, Country=${att.coinbaseCountry.country}, Passport=${att.gitcoinPassport.met}, USDC=${att.tokenBalance.met}`, + ) + + const allPassed = hasIdentity && notSanctioned && scorePass && attestPass + const tierAuthorized = allPassed + log( + ` ${tierAuthorized ? "✅" : "❌"} COMPLY: Tier "${vc.credentialSubject.tier}" ${tierAuthorized ? "authorized" : "denied"} for job budget`, + ) + + if (allPassed) { + log( + `\n ${successMessage("IACPHook: APPROVED — Agent may fund the job")}`, + ) + log(` → Emitting ReputationPositive event for ${vc.credentialSubject.agentAddress}`) + } else { + log( + `\n ${errorMessage("IACPHook: REJECTED — Agent failed verification gates")}`, + ) + log(` → Emitting ReputationNegative event for ${vc.credentialSubject.agentAddress}`) + } +} + +runDemo().catch(console.error) From 4358b60bdac242dfaf4b6ef6a8d6b66f0f8485e6 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:43 +0300 Subject: [PATCH 05/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/src/jwk-keys.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 demos/asterpay-kya/src/jwk-keys.ts diff --git a/demos/asterpay-kya/src/jwk-keys.ts b/demos/asterpay-kya/src/jwk-keys.ts new file mode 100644 index 0000000..f2ce4b0 --- /dev/null +++ b/demos/asterpay-kya/src/jwk-keys.ts @@ -0,0 +1,17 @@ +import { generateKeypair, publicKeyBytesToJwk } from "agentcommercekit" + +export async function generateJwks() { + const keypair = await generateKeypair("secp256r1") + const publicKeyJwk = { + ...publicKeyBytesToJwk(keypair.publicKey, "secp256r1"), + crv: "P-256", + use: "sig", + kid: "asterpay-kya-key-1", + alg: "ES256", + } + + return { + jwks: { keys: [publicKeyJwk] }, + keypair, + } +} From aeb0816e6cc410f006a4204dbbaa7fcce1c1b398 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:44 +0300 Subject: [PATCH 06/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/src/kya-token.ts | 121 ++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 demos/asterpay-kya/src/kya-token.ts diff --git a/demos/asterpay-kya/src/kya-token.ts b/demos/asterpay-kya/src/kya-token.ts new file mode 100644 index 0000000..58aa756 --- /dev/null +++ b/demos/asterpay-kya/src/kya-token.ts @@ -0,0 +1,121 @@ +import { log, logJson } from "@repo/cli-tools" +import { + createJwt, + createJwtSigner, + type JwtString, + type Keypair, +} from "agentcommercekit" +import { jwtPayloadSchema } from "agentcommercekit/schemas/zod/v4" +import { decodeJwt } from "jose" +import * as z from "zod/v4" + +export const trustScoreComponentSchema = z.object({ + walletAge: z.number().min(0).max(15), + transactionActivity: z.number().min(0).max(15), + sanctionsScreening: z.number().min(0).max(15), + ercIdentity: z.number().min(0).max(15), + operatorKyb: z.number().min(0).max(15), + paymentHistory: z.number().min(0).max(15), + trustBond: z.number().min(0).max(10), +}) + +export const insumerAttestationSchema = z.object({ + tokenBalance: z.object({ + met: z.boolean(), + chain: z.string(), + token: z.string(), + minBalance: z.string(), + }), + coinbaseKyc: z.object({ + met: z.boolean(), + schema: z.string(), + }), + coinbaseCountry: z.object({ + met: z.boolean(), + country: z.string(), + }), + gitcoinPassport: z.object({ + met: z.boolean(), + minScore: z.number(), + }), +}) + +export const asterPayKyaJwtPayloadSchema = z.object({ + ...jwtPayloadSchema.shape, + agentAddress: z.string(), + agentId: z.string().optional(), + trustScore: z.number().min(0).max(100), + tier: z.enum(["open", "verified", "trusted", "enterprise"]), + components: trustScoreComponentSchema, + insumerAttestation: insumerAttestationSchema, + sanctioned: z.boolean(), + jti: z.string(), +}) + +export type AsterPayKyaJwtPayload = z.output< + typeof asterPayKyaJwtPayloadSchema +> + +export async function createMockAsterPayKyaToken( + keypair: Keypair, +): Promise { + const payload: AsterPayKyaJwtPayload = { + sub: "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", + aud: "erc8183-acp-provider", + agentAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", + agentId: "ERC-8004 #16850", + trustScore: 82, + tier: "trusted", + components: { + walletAge: 12, + transactionActivity: 11, + sanctionsScreening: 15, + ercIdentity: 15, + operatorKyb: 10, + paymentHistory: 9, + trustBond: 10, + }, + insumerAttestation: { + tokenBalance: { + met: true, + chain: "base", + token: "USDC", + minBalance: "100", + }, + coinbaseKyc: { + met: true, + schema: "coinbase_account_attestation", + }, + coinbaseCountry: { + met: true, + country: "EU", + }, + gitcoinPassport: { + met: true, + minScore: 20, + }, + }, + sanctioned: false, + jti: crypto.randomUUID(), + } + + const jwt = await createJwt( + payload, + { + issuer: "https://api.asterpay.io/", + signer: createJwtSigner(keypair), + expiresIn: 1800, // 30 min aligned to InsumerAPI JWT TTL + }, + { + // @ts-expect-error - custom typ for AsterPay KYA + typ: "kya+JWT", + alg: "ES256", + }, + ) + + const parsed = decodeJwt(jwt) + log("🔑 AsterPay KYA Trust Score Token:") + logJson(parsed) + + return jwt +} From e6f8b8c9c74e20bffc4e2392ce91e822c53f45e9 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:45 +0300 Subject: [PATCH 07/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/tsconfig.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 demos/asterpay-kya/tsconfig.json diff --git a/demos/asterpay-kya/tsconfig.json b/demos/asterpay-kya/tsconfig.json new file mode 100644 index 0000000..85d2481 --- /dev/null +++ b/demos/asterpay-kya/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@repo/typescript-config/typescript-library.json", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["."], + "exclude": ["node_modules", "dist"] +} From 9dd3c29a0e3995b0c55bd92928b6bf2fbbfa68cc Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:46 +0300 Subject: [PATCH 08/15] feat: add AsterPay KYA Trust Score demo --- demos/asterpay-kya/vitest.config.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 demos/asterpay-kya/vitest.config.ts diff --git a/demos/asterpay-kya/vitest.config.ts b/demos/asterpay-kya/vitest.config.ts new file mode 100644 index 0000000..7972f77 --- /dev/null +++ b/demos/asterpay-kya/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + passWithNoTests: true, + watch: false, + }, +}) From 8a04b8f3f3d10ad8ff30dad02ce570091eb0fc1b Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:56 +0300 Subject: [PATCH 09/15] feat: add AsterPay KYA demo script --- demos/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/demos/README.md b/demos/README.md index 2050198..82895e7 100644 --- a/demos/README.md +++ b/demos/README.md @@ -36,6 +36,16 @@ pnpm demo:e2e [View the code](./e2e) +### AsterPay KYA Trust Score demo + +A demo showing how AsterPay KYA Trust Score tokens integrate with ACK-ID for trust-gated agent commerce via ERC-8183. + +```sh +pnpm demo:asterpay-kya +``` + +[View the code](./asterpay-kya) + ## Note These demos are designed as interactive walkthroughs of various ACK flows. The source code is designed with this in mind, and may not be suitable for production environments. From b75959f6ccc734980f0611cb16e2ab2c1ff78fa6 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 16:57:58 +0300 Subject: [PATCH 10/15] feat: add AsterPay KYA demo script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 56d1b74..4a08cae 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "demo:identity": "pnpm --filter ./demos/identity demo", "demo:identity-a2a": "pnpm --filter ./demos/identity-a2a demo", "demo:payments": "pnpm --filter ./demos/payments demo", + "demo:asterpay-kya": "pnpm --filter ./demos/asterpay-kya demo", "demo:skyfire-kya": "pnpm --filter ./demos/skyfire-kya demo", "dev:docs": "pnpm --filter ./docs docs", "dev:examples": "turbo dev", From cbd7f58c2a77638be04358ec42d701ec5ea29803 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 17:34:35 +0300 Subject: [PATCH 11/15] fix: use JwtProof2020, dynamic issuer verification, country check --- demos/asterpay-kya/src/asterpay-kya-ack-id.ts | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/demos/asterpay-kya/src/asterpay-kya-ack-id.ts b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts index ee94cfa..96811a2 100644 --- a/demos/asterpay-kya/src/asterpay-kya-ack-id.ts +++ b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts @@ -87,12 +87,11 @@ export async function convertAsterPayKyaToVerifiableCredential( jti: payload.jti, }, proof: { - type: "JsonWebSignature2020", + type: "JwtProof2020", created: new Date(payload.iat * 1000).toISOString(), verificationMethod: "did:web:api.asterpay.io#kya-key-1", proofPurpose: "assertionMethod", - jws: `${jwtParts[0]}..${jwtSignature}`, - originalPayload: jwtParts[1], + jwt: kyaToken, }, } @@ -143,8 +142,9 @@ export async function verifyAsterPayKyaAsAckId( try { const vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) - if (!trustedIssuers.includes("did:web:api.asterpay.io")) { - return { valid: false, reason: "AsterPay not in trusted issuers" } + const issuerDid = typeof vc.issuer === "string" ? vc.issuer : vc.issuer.id + if (!trustedIssuers.includes(issuerDid)) { + return { valid: false, reason: `Issuer ${issuerDid} not in trusted issuers` } } if (vc.expirationDate && new Date() > new Date(vc.expirationDate)) { @@ -179,6 +179,15 @@ export async function verifyAsterPayKyaAsAckId( } } + if (!att.coinbaseCountry.met) { + return { + valid: false, + reason: `Country verification not met (${att.coinbaseCountry.country})`, + trustScore: vc.credentialSubject.trustScore, + tier: vc.credentialSubject.tier, + } + } + return { valid: true, trustScore: vc.credentialSubject.trustScore, @@ -192,29 +201,26 @@ export async function verifyAsterPayKyaAsAckId( } } +/** + * Reconstructs the original KYA JWT from a synthetic VC. + * Only works with VCs created by `convertAsterPayKyaToVerifiableCredential` + * as it relies on the `jwt` field in the JwtProof2020 proof. + */ export function convertVerifiableCredentialToAsterPayKya( vc: Verifiable>, ): JwtString { - if (!vc.proof.jws) { - throw new Error("No JWS signature found in VC proof") - } - - const jwsParts = (vc.proof.jws as string).split("..") - if (jwsParts.length !== 2) { - throw new Error("Invalid JWS format in VC proof") - } - - const originalHeader = jwsParts[0] - const originalSignature = jwsParts[1] + const jwt = (vc.proof as Record).jwt as string | undefined - const originalPayload = (vc.proof as Record) - .originalPayload as string | undefined - - if (!originalPayload) { + if (!jwt) { throw new Error( - "No originalPayload found in VC proof — cannot reconstruct JWT losslessly", + "No jwt field found in VC proof — expected JwtProof2020 format", ) } - return `${originalHeader}.${originalPayload}.${originalSignature}` as JwtString + const parts = jwt.split(".") + if (parts.length !== 3) { + throw new Error("Invalid JWT format in VC proof") + } + + return jwt as JwtString } From af69b3a4fe61abf134c9b6d9c6258d2f2b9f2df3 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 17:34:46 +0300 Subject: [PATCH 12/15] fix: proper error propagation, add country check to IACPHook --- demos/asterpay-kya/src/index.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/demos/asterpay-kya/src/index.ts b/demos/asterpay-kya/src/index.ts index c435952..a61e026 100644 --- a/demos/asterpay-kya/src/index.ts +++ b/demos/asterpay-kya/src/index.ts @@ -110,8 +110,8 @@ Details: `) } catch (error: unknown) { log(errorMessage("Conversion failed")) - log(colors.dim((error as Error).toString())) - return + log(colors.dim(String(error))) + throw error } // Step 3: Bidirectional conversion @@ -132,8 +132,8 @@ integrity by converting the VC back to the original JWT format.\n`, log(colors.dim(reconstructedJwt), { wrap: false }) } catch (error: unknown) { log(errorMessage("Bidirectional conversion failed")) - log(colors.dim((error as Error).toString())) - return + log(colors.dim(String(error))) + throw error } // Step 4: ACK-ID verification with trust score gate @@ -288,7 +288,10 @@ async function simulateIACPHook( const att = vc.credentialSubject.insumerAttestation const attestPass = - att.coinbaseKyc.met && att.gitcoinPassport.met && att.tokenBalance.met + att.coinbaseKyc.met && + att.coinbaseCountry.met && + att.gitcoinPassport.met && + att.tokenBalance.met log( ` ${attestPass ? "✅" : "❌"} ATTEST: InsumerAPI — KYC=${att.coinbaseKyc.met}, Country=${att.coinbaseCountry.country}, Passport=${att.gitcoinPassport.met}, USDC=${att.tokenBalance.met}`, ) @@ -312,4 +315,7 @@ async function simulateIACPHook( } } -runDemo().catch(console.error) +runDemo().catch((error) => { + console.error(error) + process.exitCode = 1 +}) From aa2ad3a38af867aea1fa96cd2af32660624b582e Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 18:26:15 +0300 Subject: [PATCH 13/15] fix: remove unused jwtSignature, return VC from verifier, add verification result type --- demos/asterpay-kya/src/asterpay-kya-ack-id.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/demos/asterpay-kya/src/asterpay-kya-ack-id.ts b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts index 96811a2..bc02428 100644 --- a/demos/asterpay-kya/src/asterpay-kya-ack-id.ts +++ b/demos/asterpay-kya/src/asterpay-kya-ack-id.ts @@ -39,7 +39,6 @@ export async function convertAsterPayKyaToVerifiableCredential( if (jwtParts.length !== 3) { throw new Error("Invalid JWT format") } - const jwtSignature = jwtParts[2] if (!payload.iat || !payload.exp || !payload.jti || !payload.sub) { throw new Error("Invalid JWT payload: missing required standard claims") @@ -128,17 +127,20 @@ export function isSanctionedFromVC( return vc.credentialSubject.sanctioned } +export type AsterPayVerificationResult = { + valid: boolean + reason?: string + trustScore?: number + tier?: string + vc?: Verifiable> +} + export async function verifyAsterPayKyaAsAckId( jwks: jose.JSONWebKeySet, kyaToken: JwtString, trustedIssuers: string[], minTrustScore = 0, -): Promise<{ - valid: boolean - reason?: string - trustScore?: number - tier?: string -}> { +): Promise { try { const vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) @@ -157,6 +159,7 @@ export async function verifyAsterPayKyaAsAckId( reason: "Agent is sanctioned (Chainalysis)", trustScore: vc.credentialSubject.trustScore, tier: vc.credentialSubject.tier, + vc, } } @@ -166,6 +169,7 @@ export async function verifyAsterPayKyaAsAckId( reason: `Trust score ${vc.credentialSubject.trustScore} below minimum ${minTrustScore}`, trustScore: vc.credentialSubject.trustScore, tier: vc.credentialSubject.tier, + vc, } } @@ -176,6 +180,7 @@ export async function verifyAsterPayKyaAsAckId( reason: "Coinbase KYC attestation not met", trustScore: vc.credentialSubject.trustScore, tier: vc.credentialSubject.tier, + vc, } } @@ -185,6 +190,7 @@ export async function verifyAsterPayKyaAsAckId( reason: `Country verification not met (${att.coinbaseCountry.country})`, trustScore: vc.credentialSubject.trustScore, tier: vc.credentialSubject.tier, + vc, } } @@ -192,6 +198,7 @@ export async function verifyAsterPayKyaAsAckId( valid: true, trustScore: vc.credentialSubject.trustScore, tier: vc.credentialSubject.tier, + vc, } } catch (error) { return { From 721f6ec70459d889a448bbecad6d5cfcd8914039 Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 18:26:25 +0300 Subject: [PATCH 14/15] fix: reuse VC from verifier, add real tier/budget COMPLY check --- demos/asterpay-kya/src/index.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/demos/asterpay-kya/src/index.ts b/demos/asterpay-kya/src/index.ts index a61e026..fbfde89 100644 --- a/demos/asterpay-kya/src/index.ts +++ b/demos/asterpay-kya/src/index.ts @@ -21,6 +21,7 @@ import { isSanctionedFromVC, verifyAsterPayKyaAsAckId, type AsterPayKyaCredentialSubject, + type AsterPayVerificationResult, } from "./asterpay-kya-ack-id" async function runDemo() { @@ -228,11 +229,19 @@ agent's trust score before allowing job funding or provider assignment.\n`, `) } +const TIER_BUDGET_LIMITS: Record = { + open: 1, + verified: 1_000, + trusted: 10_000, + enterprise: Infinity, +} + async function simulateIACPHook( jwks: jose.JSONWebKeySet, kyaToken: JwtString, ) { - log(" 📋 ERC-8183 Job: 'Analyze Q4 market data' (budget: 50 USDC)") + const jobBudget = 50 + log(` 📋 ERC-8183 Job: 'Analyze Q4 market data' (budget: ${jobBudget} USDC)`) log(" 🤖 Agent requests to fund the job...") log(" 🔒 IACPHook.beforeAction(fund) triggered\n") @@ -246,7 +255,7 @@ async function simulateIACPHook( minScore, ) - if (!verification.valid) { + if (!verification.valid || !verification.vc) { log( `\n ${errorMessage(`IACPHook: BLOCKED — ${verification.reason}`)}`, ) @@ -254,7 +263,7 @@ async function simulateIACPHook( return } - const vc = await convertAsterPayKyaToVerifiableCredential(jwks, kyaToken) + const vc = verification.vc log( ` → Agent: ${colors.magenta(vc.credentialSubject.agentAddress)}`, @@ -296,13 +305,13 @@ async function simulateIACPHook( ` ${attestPass ? "✅" : "❌"} ATTEST: InsumerAPI — KYC=${att.coinbaseKyc.met}, Country=${att.coinbaseCountry.country}, Passport=${att.gitcoinPassport.met}, USDC=${att.tokenBalance.met}`, ) - const allPassed = hasIdentity && notSanctioned && scorePass && attestPass - const tierAuthorized = allPassed + const tierLimit = TIER_BUDGET_LIMITS[vc.credentialSubject.tier] ?? 0 + const tierAuthorized = hasIdentity && notSanctioned && scorePass && attestPass && jobBudget <= tierLimit log( - ` ${tierAuthorized ? "✅" : "❌"} COMPLY: Tier "${vc.credentialSubject.tier}" ${tierAuthorized ? "authorized" : "denied"} for job budget`, + ` ${tierAuthorized ? "✅" : "❌"} COMPLY: Tier "${vc.credentialSubject.tier}" (limit: ${tierLimit === Infinity ? "unlimited" : `$${tierLimit}`}) ${tierAuthorized ? "authorized" : "denied"} for $${jobBudget} job budget`, ) - if (allPassed) { + if (tierAuthorized) { log( `\n ${successMessage("IACPHook: APPROVED — Agent may fund the job")}`, ) From 7d232cc3ad796c4933a2d9c3460432f1ec87c13e Mon Sep 17 00:00:00 2001 From: Petteri Date: Sat, 4 Apr 2026 18:26:35 +0300 Subject: [PATCH 15/15] fix: align mock tier to enterprise for trustScore 82 --- demos/asterpay-kya/src/kya-token.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/asterpay-kya/src/kya-token.ts b/demos/asterpay-kya/src/kya-token.ts index 58aa756..c1b1205 100644 --- a/demos/asterpay-kya/src/kya-token.ts +++ b/demos/asterpay-kya/src/kya-token.ts @@ -65,7 +65,7 @@ export async function createMockAsterPayKyaToken( agentAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", agentId: "ERC-8004 #16850", trustScore: 82, - tier: "trusted", + tier: "enterprise", components: { walletAge: 12, transactionActivity: 11,