@zkproofport-ai/sdk 0.2.17 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -154
- package/dist/actionApproval.d.ts +51 -0
- package/dist/actionApproval.d.ts.map +1 -0
- package/dist/actionApproval.js +216 -0
- package/dist/actionApproval.js.map +1 -0
- package/dist/attestation.d.ts +6 -1
- package/dist/attestation.d.ts.map +1 -1
- package/dist/attestation.js +41 -4
- package/dist/attestation.js.map +1 -1
- package/dist/cdp.d.ts +3 -3
- package/dist/circuits.d.ts +1 -3
- package/dist/circuits.d.ts.map +1 -1
- package/dist/circuits.js +0 -4
- package/dist/circuits.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -10
- package/dist/constants.js.map +1 -1
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +5 -7
- package/dist/extract.js.map +1 -1
- package/dist/flow.d.ts +2 -2
- package/dist/flow.d.ts.map +1 -1
- package/dist/flow.js +42 -30
- package/dist/flow.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -12
- package/dist/index.js.map +1 -1
- package/dist/inputs.d.ts +4 -3
- package/dist/inputs.d.ts.map +1 -1
- package/dist/inputs.js +4 -7
- package/dist/inputs.js.map +1 -1
- package/dist/payment.d.ts +7 -9
- package/dist/payment.d.ts.map +1 -1
- package/dist/payment.js +20 -43
- package/dist/payment.js.map +1 -1
- package/dist/typedAction.js +1 -1
- package/dist/typedAction.js.map +1 -1
- package/dist/types.d.ts +12 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/dist/usdc.d.ts +6 -10
- package/dist/usdc.d.ts.map +1 -1
- package/dist/usdc.js +6 -11
- package/dist/usdc.js.map +1 -1
- package/dist/wallets.d.ts +8 -34
- package/dist/wallets.d.ts.map +1 -1
- package/dist/wallets.js +10 -87
- package/dist/wallets.js.map +1 -1
- package/package.json +2 -12
- package/dist/arcWallet.d.ts +0 -50
- package/dist/arcWallet.d.ts.map +0 -1
- package/dist/arcWallet.js +0 -155
- package/dist/arcWallet.js.map +0 -1
- package/dist/delegation.d.ts +0 -107
- package/dist/delegation.d.ts.map +0 -1
- package/dist/delegation.js +0 -126
- package/dist/delegation.js.map +0 -1
- package/dist/nanopayment.d.ts +0 -71
- package/dist/nanopayment.d.ts.map +0 -1
- package/dist/nanopayment.js +0 -77
- package/dist/nanopayment.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# @zkproofport-ai/sdk
|
|
2
2
|
|
|
3
|
-
Client SDK for ZKProofport zero-knowledge proofs, including experimental exact-action authorization on
|
|
3
|
+
Client SDK for ZKProofport zero-knowledge proofs, including experimental exact-action authorization with `giwa_attestation` on GIWA Sepolia.
|
|
4
4
|
|
|
5
|
-
The shared `hashTypedAction` helper is available from **SDK 0.2.12**.
|
|
5
|
+
The shared `hashTypedAction` helper is available from **SDK 0.2.12**. GIWA optional actions require **SDK 0.2.14 or later**; MCP stepwise input preparation requires **MCP 0.2.15 or later**. Release Please owns the versions. Confirm the resolved npm versions before using this path.
|
|
6
|
+
|
|
7
|
+
Human action approval requires SDK **0.3.0 or later** and a compatible AI service with the approval API enabled. For the CLI/MCP flow, use MCP **0.3.0 or later** as well. Earlier releases that support action hashing do not implement this approval lifecycle. Action requests now pause for human approval; handle `ActionApprovalRequiredError` and resume as shown below.
|
|
6
8
|
|
|
7
9
|
## Overview
|
|
8
10
|
|
|
@@ -15,7 +17,7 @@ ZKProofport has a TEE-based proving architecture. Enclave encryption and hardwar
|
|
|
15
17
|
A service tells you what it charges. Ask for a proof and the answer is either
|
|
16
18
|
the proof or a `402` listing the chains it takes USDC on — you pick one.
|
|
17
19
|
|
|
18
|
-
For supported x402 offers, the payer signs an authorization and the payment rail settles it.
|
|
20
|
+
For supported x402 offers, the payer signs an authorization and the payment rail settles it. Wallet, chain and signing-domain compatibility must match the actual offer.
|
|
19
21
|
|
|
20
22
|
Supported payment-wallet adapters:
|
|
21
23
|
|
|
@@ -24,9 +26,7 @@ import {
|
|
|
24
26
|
generateProof,
|
|
25
27
|
walletFromPrivateKey, // a raw key
|
|
26
28
|
walletFromCdp, // a Coinbase CDP server wallet
|
|
27
|
-
|
|
28
|
-
walletFromArcAgent, // an existing Circle Agent Wallet through Circle CLI
|
|
29
|
-
walletFor, // walletFor('arc') selects the same adapter from environment
|
|
29
|
+
walletFor, // walletFor('key' | 'cdp') builds one from environment
|
|
30
30
|
} from '@zkproofport-ai/sdk';
|
|
31
31
|
|
|
32
32
|
const proof = await generateProof(
|
|
@@ -62,14 +62,13 @@ never quietly pays on a different one.
|
|
|
62
62
|
From the command line:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
zkproofport-prove coinbase_kyc --pay-with
|
|
65
|
+
zkproofport-prove coinbase_kyc --pay-with key --pay-on <chain>
|
|
66
66
|
zkproofport-prove coinbase_kyc --pay-with cdp
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
`--pay-with cdp` needs `CDP_API_KEY_ID`, `CDP_API_KEY_SECRET` and
|
|
70
|
-
`CDP_WALLET_SECRET`; `--pay-with
|
|
71
|
-
|
|
72
|
-
`PAYMENT_PRIVATE_KEY`. For the existing Circle Agent Wallet, explicitly select `--pay-with arc`; Circle CLI owns the login, and `ARC_AGENT_WALLET` selects its wallet. With exactly one key/CDP/developer-controlled wallet configured you can omit
|
|
70
|
+
`CDP_WALLET_SECRET`; `--pay-with key` needs `PAYMENT_PRIVATE_KEY`. These are
|
|
71
|
+
the only two payment wallets. With exactly one of them configured you can omit
|
|
73
72
|
the flag. With more than one, naming it is required — a wallet is money, and
|
|
74
73
|
guessing which account it leaves is not a default worth having.
|
|
75
74
|
|
|
@@ -77,7 +76,7 @@ guessing which account it leaves is not a default worth having.
|
|
|
77
76
|
|
|
78
77
|
When the selected endpoint supplies a supported attested `teePublicKey`, the SDK encrypts inputs with X25519 ECDH + AES-256-GCM. The host relays ciphertext to the enclave; Nitro mode rejects plaintext inputs. Validate the returned attestation against the required trust policy.
|
|
79
78
|
|
|
80
|
-
An endpoint without that key receives readable circuit inputs over HTTPS and provides no hardware-attestation guarantee. The current
|
|
79
|
+
An endpoint without that key receives readable circuit inputs over HTTPS and provides no hardware-attestation guarantee. The current GCP Cloud Run deployments use that mode. HTTPS transport encryption does not make the prover a blind relay. A valid public proof can hide the credential-holder address from the relying dApp without establishing enclave execution. Only claim hardware-attested proving when the actual attestation supports it.
|
|
81
80
|
|
|
82
81
|
## Installation
|
|
83
82
|
|
|
@@ -85,7 +84,7 @@ An endpoint without that key receives readable circuit inputs over HTTPS and pro
|
|
|
85
84
|
npm install @zkproofport-ai/sdk@latest ethers
|
|
86
85
|
```
|
|
87
86
|
|
|
88
|
-
Use **SDK 0.2.14 or later** and **MCP 0.2.15 or later** for both
|
|
87
|
+
Use **SDK 0.2.14 or later** and **MCP 0.2.15 or later** for both GIWA signature modes, including stepwise preparation. Install from npm and check the resolved version. Release Please manages package versions and the release workflow publishes them; repository source changes alone do not update `@latest`.
|
|
89
88
|
|
|
90
89
|
## Prerequisites
|
|
91
90
|
|
|
@@ -93,15 +92,15 @@ Use **SDK 0.2.14 or later** and **MCP 0.2.15 or later** for both Arc/GIWA signat
|
|
|
93
92
|
|
|
94
93
|
1. **Coinbase account with KYC verification** — Complete identity verification on [Coinbase](https://www.coinbase.com/)
|
|
95
94
|
2. **Coinbase KYC EAS attestation on Base** — Obtain an attestation via [Coinbase Verifications](https://www.coinbase.com/onchain-verify). This creates an on-chain EAS attestation on Base linked to your wallet address.
|
|
96
|
-
3. **Credential signer**
|
|
95
|
+
3. **Credential signer for proofs without an action** — A local signer for the wallet holding the EAS attestation. The CLI uses `ATTESTATION_KEY`; programmatic callers can supply a compatible `ProofportSigner`. A GIWA action instead requires the credential holder to connect a wallet and sign through the approval page. Keep key material outside model context.
|
|
97
96
|
|
|
98
97
|
**For OIDC circuits** (`oidc_domain`): No wallet or attestation needed — just a JWT `id_token` from your OIDC provider.
|
|
99
98
|
|
|
100
|
-
**For GIWA** (`giwa_attestation`): an attestation from GIWA's attester on **GIWA Sepolia**, and
|
|
99
|
+
**For GIWA** (`giwa_attestation`): an attestation from GIWA's attester on **GIWA Sepolia**, and control of that wallet. Without an action, supply its local signer; with an action, the person connects that wallet on the approval page. The attestation must belong to the wallet signing the proof request on the chain the circuit reads.
|
|
101
100
|
|
|
102
|
-
**For the
|
|
101
|
+
**For the circuit that can bind an action** (`giwa_attestation`): the GIWA attestation above. What differs is what the wallet signs, and the action is **optional** — see below.
|
|
103
102
|
|
|
104
|
-
**One wallet has a different nullifier per circuit, and for
|
|
103
|
+
**One wallet has a different nullifier per circuit, and for `giwa_attestation` it does not depend on what you signed.** `giwa_attestation` derives it from your address, the circuit's own identifier and the scope, so the same wallet in the same scope gives the same nullifier whether or not you bind an action. The Coinbase circuits derive theirs from the signed challenge instead. Nothing to configure — the SDK picks the rule the circuit expects, and refuses a circuit it has no rule for rather than guessing one. Requires **0.2.14 or later**; earlier versions sent the Coinbase rule for every circuit, which a GIWA proof rejects as a nullifier mismatch.
|
|
105
104
|
|
|
106
105
|
## Quick Start
|
|
107
106
|
|
|
@@ -126,16 +125,15 @@ console.log('Valid:', verification.valid);
|
|
|
126
125
|
|
|
127
126
|
> **Experimental. Testnet only.**
|
|
128
127
|
>
|
|
129
|
-
>
|
|
128
|
+
> One circuit can bind an action, on a testnet and not generally available:
|
|
130
129
|
>
|
|
131
130
|
> | Circuit | Chain | Verifier |
|
|
132
131
|
> |---|---|---|
|
|
133
|
-
> | `arc_eligibility` | Arc Testnet, 5042002 | `0x2aEB66292f631ceb6225ffA2439B1f2b4b15e44a` |
|
|
134
132
|
> | `giwa_attestation` | GIWA Sepolia, 91342 | `0x5Da234546874304F8c51BBEed00fC632938211c1` |
|
|
135
133
|
>
|
|
136
|
-
>
|
|
134
|
+
> Public-input layouts and deployments may change; do not put it in front of real funds. Generally available: `coinbase_kyc`, `coinbase_country`, `oidc_domain`.
|
|
137
135
|
>
|
|
138
|
-
> **The action is optional
|
|
136
|
+
> **The action is optional.** Send one and the SDK pauses for a human to connect a wallet and sign the exact typed data. Send none and the supplied credential signer signs the request's signal hash as before. Supplying `ATTESTATION_KEY` does not bypass action approval.
|
|
139
137
|
|
|
140
138
|
### The problem it solves
|
|
141
139
|
|
|
@@ -154,22 +152,25 @@ wallet and scope no matter what the proof is later used for, so:
|
|
|
154
152
|
|
|
155
153
|
### What changes
|
|
156
154
|
|
|
157
|
-
Pass an `action` and
|
|
158
|
-
typed structure
|
|
159
|
-
|
|
155
|
+
Pass an `action` and a human wallet signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712)
|
|
156
|
+
typed structure through the AI service's approval page. You define the structure;
|
|
157
|
+
the SDK does not invent action fields. No proof challenge or payment starts while
|
|
158
|
+
approval is pending. The agent's payment wallet remains separately configured.
|
|
160
159
|
|
|
161
160
|
```typescript
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
import {
|
|
162
|
+
ActionApprovalRequiredError, generateProof, getActionApprovalStatus,
|
|
163
|
+
type ActionApproval, type ClientConfig, type PaymentWallet, type ProofParams,
|
|
164
|
+
} from '@zkproofport-ai/sdk';
|
|
165
|
+
|
|
166
|
+
const params: ProofParams = {
|
|
167
|
+
circuit: 'giwa_attestation',
|
|
167
168
|
scope: 'my-app',
|
|
168
169
|
action: {
|
|
169
170
|
domain: {
|
|
170
171
|
name: 'MyVault',
|
|
171
172
|
version: '1',
|
|
172
|
-
chainId:
|
|
173
|
+
chainId: 91342,
|
|
173
174
|
verifyingContract: vaultAddress, // binds the signature to ONE contract
|
|
174
175
|
},
|
|
175
176
|
types: {
|
|
@@ -180,19 +181,60 @@ const result = await generateProof(
|
|
|
180
181
|
],
|
|
181
182
|
},
|
|
182
183
|
primaryType: 'Deposit',
|
|
183
|
-
message: { amount:
|
|
184
|
+
message: { amount: '100000', nonce: '1', expiry: String(deadline) },
|
|
184
185
|
},
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// For a CLI or backend with an appropriate request lifetime. An MCP tool should
|
|
189
|
+
// return the URL promptly and resume in a later call instead of waiting here.
|
|
190
|
+
async function waitForHumanApproval(config: ClientConfig, approval: ActionApproval) {
|
|
191
|
+
while (Date.now() < Date.parse(approval.expiresAt)) {
|
|
192
|
+
const status = await getActionApprovalStatus(config, approval);
|
|
193
|
+
if (status.status === 'approved') return;
|
|
194
|
+
if (status.status !== 'pending') throw new Error(`Action approval is ${status.status}`);
|
|
195
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
196
|
+
}
|
|
197
|
+
throw new Error('Action approval expired');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function generateWithHumanApproval(config: ClientConfig, payment: PaymentWallet, params: ProofParams) {
|
|
201
|
+
try {
|
|
202
|
+
return await generateProof(config, { payment }, params);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (!(error instanceof ActionApprovalRequiredError)) throw error;
|
|
205
|
+
const approval = error.approval;
|
|
206
|
+
console.error('Review and sign:', approval.approvalUrl);
|
|
207
|
+
await waitForHumanApproval(config, approval);
|
|
208
|
+
// Consume once. Do not retry this call automatically after a failure.
|
|
209
|
+
return generateProof(config, { payment }, { ...params, actionApproval: approval });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result = await generateWithHumanApproval(config, payment, params);
|
|
187
214
|
```
|
|
188
215
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
216
|
+
`ActionApprovalRequiredError` is a pause, not a failed proof. Its `.approval`
|
|
217
|
+
handle contains `{approvalId, approvalUrl, requesterToken, expiresAt}`. Keep
|
|
218
|
+
`requesterToken` in private application storage; only show the approval URL to
|
|
219
|
+
the person. Status replies contain no signature. `resolveActionApproval`
|
|
220
|
+
checks the original circuit, scope, full action and recovered signer before
|
|
221
|
+
preparing inputs. Rejected, expired and consumed sessions cannot proceed.
|
|
222
|
+
|
|
223
|
+
The session expires after ten minutes and can be consumed once. If a response
|
|
224
|
+
is lost after consumption, inspect the previous proof/payment outcome before
|
|
225
|
+
requesting fresh approval; never automatically pay again. Session expiry does
|
|
226
|
+
not expire the EIP-712 signature on chain. The relying contract must enforce
|
|
227
|
+
the action's own nonce and deadline. This flow supports EOA credential wallets;
|
|
228
|
+
contract-wallet signature verification is outside the current circuit.
|
|
229
|
+
|
|
230
|
+
`giwa_attestation` uses the credential wallet's GIWA Sepolia attestation. The
|
|
231
|
+
action domain names the relying contract's execution chain;
|
|
232
|
+
it is independent of the chain used for attestation lookup or proof payment.
|
|
233
|
+
The approval page asks the wallet to use the action's declared chain.
|
|
192
234
|
|
|
193
235
|
The wallet renders `amount`, `nonce` and `expiry` by name for the person to
|
|
194
236
|
read. Two 32-byte hashes reach the circuit — the EIP-712 domain separator and
|
|
195
|
-
the hash of the action — and the circuit proves the
|
|
237
|
+
the hash of the action — and the circuit proves the attested key signed them. It
|
|
196
238
|
never learns what the action was, so nothing about vaults or USDC is baked into
|
|
197
239
|
it: a grant of authority or an agreement in prose works the same way.
|
|
198
240
|
|
|
@@ -232,65 +274,29 @@ one byte each:
|
|
|
232
274
|
| `scope` | 128–159 |
|
|
233
275
|
| `nullifier` | 160–191 |
|
|
234
276
|
|
|
235
|
-
`signal_hash` is
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
277
|
+
`signal_hash` is zero when an action is bound. The nullifier derives from the
|
|
278
|
+
wallet, the circuit id and the scope, not from the signed action: deriving it
|
|
279
|
+
from the action would give a different nullifier per action and destroy the
|
|
280
|
+
one-per-person property it exists for.
|
|
239
281
|
|
|
240
282
|
### Omitting `action`
|
|
241
283
|
|
|
242
|
-
`
|
|
284
|
+
`giwa_attestation` accepts an omitted action and retains its local `personal_sign` path. Coinbase circuits reject `action` and retain their existing signing path; OIDC rejects `action` and uses its JWT flow without wallet signing. For action proofs, the application must verify the exact target, actor, amount, nonce and deadline as well as the proof.
|
|
243
285
|
|
|
244
|
-
|
|
286
|
+
### Approving the exact payment
|
|
245
287
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
npm install -g @circle-fin/cli
|
|
250
|
-
circle wallet list --chain ARC-TESTNET --type agent
|
|
251
|
-
# Set ARC_AGENT_WALLET to the user's existing, approved wallet B address.
|
|
252
|
-
circle gateway balance --address "$ARC_AGENT_WALLET" --chain ARC-TESTNET --output json
|
|
253
|
-
# Only when the user has approved this funding amount:
|
|
254
|
-
circle gateway deposit --amount 0.1 --address "$ARC_AGENT_WALLET" --chain ARC-TESTNET --method direct
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
For the live Gateway batched offer, SDK `signPayment` uses `@circle-fin/x402-batching` and the Circle CLI wallet adapter. It signs the Gateway authorization against the offer’s Gateway signing domain and spends the existing funded Gateway balance. The payment adapter uses the agent smart-wallet address for direct USDC payments (`arc-testnet`, verified through ERC-1271) and Circle's backing EOA for Gateway authorization (`arc-testnet-nano`). `wallet.address` is the on-chain owner; `wallet.gatewayAddress` identifies the separate Gateway depositor, and `signPayment` selects the correct owner for the chosen offer. The operational smart-wallet address remains Wallet B for the action and stake; do not substitute the backing EOA into the action's `delegate` field. `walletFromArcAgent({address: process.env.ARC_AGENT_WALLET, chain: 'ARC-TESTNET'})` and `walletFor('arc')` provide the same payment path.
|
|
258
|
-
|
|
259
|
-
After the user reviews the action and the live x402 offer, save those exact approved objects locally as `approved-action.json` and `approved-payment.json`:
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
import { readFile } from 'node:fs/promises';
|
|
263
|
-
import {
|
|
264
|
-
createConfig, fromPrivateKey, generateProof, verifyProof, walletFor,
|
|
265
|
-
type ProofParams, type ApprovedPayment,
|
|
266
|
-
} from '@zkproofport-ai/sdk';
|
|
267
|
-
|
|
268
|
-
const config = createConfig({baseUrl: 'https://stg-ai.zkproofport.app'});
|
|
269
|
-
const action: NonNullable<ProofParams['action']> = JSON.parse(await readFile('approved-action.json', 'utf8'));
|
|
270
|
-
const approvedPayment: ApprovedPayment = JSON.parse(await readFile('approved-payment.json', 'utf8'));
|
|
271
|
-
const result = await generateProof(config, {
|
|
272
|
-
attestation: fromPrivateKey(process.env.ATTESTATION_KEY!), // local credential holder A
|
|
273
|
-
payment: await walletFor('arc'), // existing Circle wallet B
|
|
274
|
-
}, {
|
|
275
|
-
circuit: 'arc_eligibility', scope: 'ledger-house', action,
|
|
276
|
-
payOn: 'arc-testnet-nano', maxPayment: '0.001', approvedPayment,
|
|
277
|
-
});
|
|
278
|
-
const verification = await verifyProof(result);
|
|
279
|
-
if (!verification.valid) throw new Error('Arc verifier rejected the proof');
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
`approvedPayment` pins the selected live offer's `network`, `scheme`, `amount`, `asset`, `payTo`, and signing domain `extra: {name, version, verifyingContract}`. For direct `exact` EIP-3009 payments (Base, Ethereum and Arc), set the approved `extra.verifyingContract` to the offer's `asset`: the standard offer may omit `extra.verifyingContract` because the token is the signing contract. For Circle Gateway (`GatewayWalletBatched`, version `1`), copy the required `extra.verifyingContract` from the offer; never substitute the token address. Missing or conflicting domain information and unsupported transfer methods such as Permit2 are rejected. `amount` is an integer string in USDC base units (`"1000"` is 0.001 USDC); `maxPayment` is a decimal USDC string (`"0.001"`). A changed fee, recipient, asset, network or signing domain is rejected before payment signing. Approve the actual offer; do not manufacture its recipient or domain from a documentation example.
|
|
288
|
+
Pass `maxPayment` and `approvedPayment` in `ProofParams`. `approvedPayment` pins the selected live offer's `network`, `scheme`, `amount`, `asset`, `payTo`, and signing domain `extra: {name, version, verifyingContract}`. For `exact` EIP-3009 payments (Base and Ethereum), set the approved `extra.verifyingContract` to the offer's `asset`: the standard offer may omit `extra.verifyingContract` because the token is the signing contract. Missing or conflicting domain information, Circle Gateway batched domains and unsupported transfer methods such as Permit2 are rejected. `amount` is an integer string in USDC base units (`"1000"` is 0.001 USDC); `maxPayment` is a decimal USDC string (`"0.001"`). A changed fee, recipient, asset, network or signing domain is rejected before payment signing. Approve the actual offer; do not manufacture its recipient or domain from a documentation example.
|
|
283
289
|
|
|
284
290
|
### Action object: caller-owned types, keys and values
|
|
285
291
|
|
|
286
|
-
Both `generateProof`/`generate_proof` and
|
|
292
|
+
Both `generateProof`/`generate_proof` and stepwise input preparation use this complete `TypedAction` shape:
|
|
287
293
|
|
|
288
294
|
| Field | Type | Meaning |
|
|
289
295
|
|---|---|---|
|
|
290
296
|
| `domain` | object | EIP-712 signing context; all four fields below are required. |
|
|
291
297
|
| `domain.name` | non-empty string | Application/domain name agreed with the verifier. |
|
|
292
298
|
| `domain.version` | non-empty string | Signing-domain version. |
|
|
293
|
-
| `domain.chainId` | integer number | Target chain ID, such as `
|
|
299
|
+
| `domain.chainId` | integer number | Target chain ID, such as `91342`. |
|
|
294
300
|
| `domain.verifyingContract` | string | 20-byte `0x` contract address bound by the signature. |
|
|
295
301
|
| `types` | object | Map from caller-chosen struct names to arrays of field declarations. Omit `EIP712Domain`; ethers derives it from `domain`. |
|
|
296
302
|
| `types[structName]` | array of objects | Ordered fields; each declaration has `name` and `type`. Include referenced nested structs. |
|
|
@@ -299,51 +305,10 @@ Both `generateProof`/`generate_proof` and Arc input preparation use this complet
|
|
|
299
305
|
| `primaryType` | non-empty string | The declared top-level struct; it must equal the single root inferred by the encoder. |
|
|
300
306
|
| `message` | object | Values matching the declared field types, including nested objects and arrays. |
|
|
301
307
|
|
|
302
|
-
No SDK action registry restricts struct names, field names or
|
|
308
|
+
No SDK action registry restricts business-specific struct names, field names or values. For example, `Instruction` can contain `terms: Terms` and `labels: string[]`, where `Terms` contains `quantity: uint256` and `memo: string`. The circuit binds their hashes; each relying application decides which structures it can execute. Declare every approval-critical field in `types`; the approval API rejects extra unsigned message keys. Invalid shape, missing declared fields (including nested structs/arrays), non-boolean `bool` values, malformed field declarations/encoded values, ambiguous roots and a mismatched `primaryType` are rejected before human signing. The API also bounds request size, nesting, array length and declaration count.
|
|
303
309
|
|
|
304
310
|
Use decimal **integer strings** for JSON integer message values, especially `uint256` (for example `"10000000"`); JSON cannot represent `bigint`, and large JavaScript numbers can lose precision. Addresses/bytes are `0x` strings, booleans remain JSON booleans, and nested values follow their declared types. `domain.chainId` remains an integer JSON number.
|
|
305
311
|
|
|
306
|
-
### Full 10-USDC Ledger House Gate action
|
|
307
|
-
|
|
308
|
-
This valid JSON shows the Gate's exact five-field schema. The example delegate is a public placeholder for Wallet B: replace it with the approved operational smart-wallet address, choose a future approved Unix-seconds deadline and a fresh nonce, then review the whole object. Never include the private credential-holder A's address or key in it.
|
|
309
|
-
|
|
310
|
-
```json
|
|
311
|
-
{
|
|
312
|
-
"domain": {
|
|
313
|
-
"name": "Ledger House Staking",
|
|
314
|
-
"version": "1",
|
|
315
|
-
"chainId": 5042002,
|
|
316
|
-
"verifyingContract": "0xD0F3eE648386B59B484157332E736388Fcc41F47"
|
|
317
|
-
},
|
|
318
|
-
"types": {
|
|
319
|
-
"CredentialDelegation": [
|
|
320
|
-
{ "name": "delegate", "type": "address" },
|
|
321
|
-
{ "name": "action", "type": "string" },
|
|
322
|
-
{ "name": "amount", "type": "uint256" },
|
|
323
|
-
{ "name": "expiresAt", "type": "uint256" },
|
|
324
|
-
{ "name": "nonce", "type": "string" }
|
|
325
|
-
]
|
|
326
|
-
},
|
|
327
|
-
"primaryType": "CredentialDelegation",
|
|
328
|
-
"message": {
|
|
329
|
-
"delegate": "0x0000000000000000000000000000000000000001",
|
|
330
|
-
"action": "stake",
|
|
331
|
-
"amount": "10000000",
|
|
332
|
-
"expiresAt": "2000000000",
|
|
333
|
-
"nonce": "replace-with-fresh-user-approved-nonce"
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
`"10000000"` means **10 USDC** (6 decimals). The proof fee is separate: **0.001 USDC**, or `"1000"` base units. `delegate` and `CredentialDelegation` are established wire names for exact-action authorization: A authorizes this operation by B; B does not inherit A's identity or KYC credential. User approval covers the exact action and live proof-payment terms, followed by separate approval of the actual stake transaction.
|
|
339
|
-
|
|
340
|
-
The exported SDK `buildDelegationAction` helper is a legacy generic four-field schema (`delegate`, `action`, `expiresAt`, `nonce`). It **does not include `amount` and is not compatible with this staking Gate**. Its existing wire hash remains unchanged. Build the explicit five-field `TypedAction` above for the Gate; arbitrary custom actions remain valid SDK inputs but require a verifier that understands them.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
Generating and verifying a proof does not submit a stake. After a separate stake approval, the operational wallet calls the dApp gate, which verifies the proof and enforces its policy, exact action, unused nonce and deadline atomically. Check the receipt before reporting a position change.
|
|
344
|
-
|
|
345
|
-
The MCP `gateway_balance` and `deposit_to_gateway` helpers currently require `PAYMENT_PRIVATE_KEY`; they do not operate the Circle Agent Wallet. Use the Circle CLI commands above for that wallet. SDK `gatewayBalance` / `ensureGatewayBalance` likewise accept the private-key `NanopaymentWallet` shape. This path documents the supported Arc Testnet offer, not a guarantee for every wallet on every chain.
|
|
346
|
-
|
|
347
312
|
## Configuration
|
|
348
313
|
|
|
349
314
|
```typescript
|
|
@@ -441,58 +406,62 @@ if (verification.valid) {
|
|
|
441
406
|
|
|
442
407
|
## Step-by-Step API
|
|
443
408
|
|
|
444
|
-
For
|
|
409
|
+
For GIWA actions, obtain human approval before preparing inputs. `prepareInputs` needs the camelCase hashes to recover A's public key from the approved EIP-712 digest; it does not add those hashes to its returned object. Attach the snake_case fields for submission. `signal_hash` is still computed internally for nullifier derivation and is not the action signing message. Low-level witness APIs accept signatures but cannot establish who operated the signing wallet.
|
|
445
410
|
|
|
446
411
|
Prepared inputs are **private witness data**: they include A's public key, signature and attestation data. Keep them inside trusted local code and send them only to the chosen prover (encrypted when a supported TEE key is provided). Do not forward them to the dApp, model, UI or logs. The `generateProof` public result is the boundary for sharing a proof; step callbacks and `proofport://config` can contain sensitive signer data and must not be forwarded wholesale.
|
|
447
412
|
|
|
448
413
|
```typescript
|
|
449
414
|
import { readFile } from 'node:fs/promises';
|
|
450
415
|
import {
|
|
451
|
-
createConfig,
|
|
416
|
+
createConfig, createActionApproval, resolveActionApproval, hashTypedAction, prepareInputs,
|
|
452
417
|
requestChallenge, signPayment, submitProof, submitEncryptedProof,
|
|
453
|
-
encryptForTee,
|
|
418
|
+
encryptForTee, walletFromEnv, CIRCUIT_IDS,
|
|
454
419
|
type TypedAction, type ApprovedPayment,
|
|
455
420
|
} from '@zkproofport-ai/sdk';
|
|
456
421
|
|
|
457
422
|
const config = createConfig({ baseUrl: 'https://stg-ai.zkproofport.app' });
|
|
458
423
|
const action: TypedAction = JSON.parse(await readFile('approved-action.json', 'utf8'));
|
|
459
424
|
const approvedPayment: ApprovedPayment = JSON.parse(await readFile('approved-payment.json', 'utf8'));
|
|
460
|
-
const
|
|
461
|
-
const scope = 'ledger-house';
|
|
425
|
+
const scope = 'myapp:membership';
|
|
462
426
|
|
|
463
|
-
// 1.
|
|
427
|
+
// 1. Create an immutable request, then let the person connect and sign.
|
|
464
428
|
const { domainSeparator, actionHash } = hashTypedAction(action);
|
|
465
|
-
const
|
|
429
|
+
const request = { circuit: 'giwa_attestation' as const, scope, action };
|
|
430
|
+
const approval = await createActionApproval(config, request);
|
|
431
|
+
console.error('Review and sign:', approval.approvalUrl);
|
|
432
|
+
await waitForHumanApproval(config, approval); // bounded helper from the example above
|
|
433
|
+
const approved = await resolveActionApproval(config, request, approval);
|
|
466
434
|
|
|
467
|
-
// 2.
|
|
435
|
+
// 2. The signature has been verified and consumed. Build the private witness.
|
|
468
436
|
const prepared = await prepareInputs(config, {
|
|
469
|
-
circuitId: CIRCUIT_IDS.
|
|
470
|
-
userAddress:
|
|
437
|
+
circuitId: CIRCUIT_IDS.GIWA_ATTESTATION,
|
|
438
|
+
userAddress: approved.address, userSignature: approved.signature, scope,
|
|
471
439
|
domainSeparator, actionHash,
|
|
472
440
|
});
|
|
473
441
|
const inputs = { ...prepared, domain_separator: domainSeparator, action_hash: actionHash };
|
|
474
442
|
|
|
475
443
|
// 3. Fetch a fresh nonce/offer/TEE key without sending the witness yet.
|
|
476
|
-
const challenge = await requestChallenge(config, '
|
|
444
|
+
const challenge = await requestChallenge(config, 'giwa_attestation');
|
|
477
445
|
const paymentHeaders = challenge.requiresPayment
|
|
478
|
-
? (await signPayment(challenge, await
|
|
479
|
-
network:
|
|
446
|
+
? (await signPayment(challenge, await walletFromEnv(), {
|
|
447
|
+
network: approvedPayment.network, maxPayment: '0.001', approvedPayment,
|
|
480
448
|
})).headers
|
|
481
449
|
: undefined;
|
|
482
450
|
|
|
483
451
|
// 4. Submit with the same nonce and approved payment; preserve TEE encryption.
|
|
484
452
|
const result = challenge.teePublicKey
|
|
485
453
|
? await submitEncryptedProof(config, {
|
|
486
|
-
circuit: '
|
|
454
|
+
circuit: 'giwa_attestation', nonce: challenge.nonce, paymentHeaders,
|
|
487
455
|
encryptedPayload: encryptForTee(
|
|
488
|
-
JSON.stringify({ circuitId: CIRCUIT_IDS.
|
|
456
|
+
JSON.stringify({ circuitId: CIRCUIT_IDS.GIWA_ATTESTATION, inputs }),
|
|
489
457
|
challenge.teePublicKey.publicKey,
|
|
490
458
|
),
|
|
491
459
|
})
|
|
492
460
|
: await submitProof(config, {
|
|
493
|
-
circuit: '
|
|
461
|
+
circuit: 'giwa_attestation', inputs, nonce: challenge.nonce, paymentHeaders,
|
|
494
462
|
});
|
|
495
|
-
// Share only the public result
|
|
463
|
+
// Share only the public result and verify it. Any application transaction
|
|
464
|
+
// that relies on the proof needs its own approval.
|
|
496
465
|
```
|
|
497
466
|
|
|
498
467
|
`hashTypedAction(action)` returns `{domainSeparator: string, actionHash: string}` as 32-byte `0x` hashes. For Coinbase KYC/country, sign `computeSignalHash(address, scope, circuitId)` with `signMessage`, call `prepareInputs` without action hashes, then use the same challenge/payment/submission sequence. OIDC uses `prepareOidcPayload` and no attestation-wallet signature. The all-in-one `generateProof` already handles these distinctions.
|
|
@@ -616,8 +585,8 @@ The proof contains 148 public input fields (32 bytes each):
|
|
|
616
585
|
**Circuit Types:**
|
|
617
586
|
|
|
618
587
|
```typescript
|
|
619
|
-
type CircuitName = 'coinbase_kyc' | 'coinbase_country' | 'oidc_domain' | '
|
|
620
|
-
type CircuitId = 'coinbase_attestation' | 'coinbase_country_attestation' | 'oidc_domain_attestation' | '
|
|
588
|
+
type CircuitName = 'coinbase_kyc' | 'coinbase_country' | 'oidc_domain' | 'giwa_attestation';
|
|
589
|
+
type CircuitId = 'coinbase_attestation' | 'coinbase_country_attestation' | 'oidc_domain_attestation' | 'giwa_attestation';
|
|
621
590
|
```
|
|
622
591
|
|
|
623
592
|
**Configuration:**
|
|
@@ -650,7 +619,8 @@ function fromSigner(signer: ethers.Signer): ProofportSigner;
|
|
|
650
619
|
```typescript
|
|
651
620
|
interface ProofParams {
|
|
652
621
|
circuit: CircuitName;
|
|
653
|
-
action?: { domain: { name: string; version: string; chainId: number; verifyingContract: string }; types: Record<string, Array<{name: string; type: string}>>; primaryType: string; message: Record<string, unknown> }; // optional for
|
|
622
|
+
action?: { domain: { name: string; version: string; chainId: number; verifyingContract: string }; types: Record<string, Array<{name: string; type: string}>>; primaryType: string; message: Record<string, unknown> }; // optional for giwa_attestation
|
|
623
|
+
actionApproval?: ActionApproval; // private handle; resume the original action after human approval
|
|
654
624
|
payOn?: string;
|
|
655
625
|
maxPayment?: string; // decimal USDC
|
|
656
626
|
approvedPayment?: ApprovedPayment;
|
|
@@ -760,21 +730,17 @@ npm run test:e2e:published -- --sdk-version 0.2.16 --mcp-version 0.2.16
|
|
|
760
730
|
```
|
|
761
731
|
|
|
762
732
|
It installs those exact npm versions outside the workspace, resolves SDK imports
|
|
763
|
-
and the MCP process to that installation, and removes it afterwards.
|
|
764
|
-
|
|
765
|
-
subprocess PATH without changing the global installation. Optional CDP/x402 and
|
|
766
|
-
Circle developer wallet dependencies are installed at tested exact versions,
|
|
733
|
+
and the MCP process to that installation, and removes it afterwards. Optional CDP/x402
|
|
734
|
+
wallet dependencies are installed at tested exact versions,
|
|
767
735
|
and their imports are verified before paid tests start. A normal
|
|
768
736
|
workspace test run exercises local packages and is not published-package evidence.
|
|
769
737
|
The full suite creates paid testnet proofs. It requires the attestation and payer
|
|
770
738
|
credentials loaded by `tests/setup.ts`; GIWA uses `GIWA_ATTESTATION_KEY`, and OIDC
|
|
771
739
|
uses `E2E_OIDC_JWT` or an authenticated gcloud account. The payment matrix checks
|
|
772
|
-
Base Sepolia
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
existing Circle Agent Wallet for both Arc cases; the supported Circle CLI,
|
|
777
|
-
on-chain USDC for immediate settlement, and Gateway USDC for nano are required. The runner never deposits or transfers funds.
|
|
740
|
+
Base Sepolia and Ethereum Sepolia separately, so each needs a funded payer on its
|
|
741
|
+
selected network. Optional `E2E_PAYER_KEY_<NETWORK>` overrides use uppercase names
|
|
742
|
+
with underscores, for example `E2E_PAYER_KEY_ETHEREUM_SEPOLIA`. The runner never
|
|
743
|
+
transfers funds.
|
|
778
744
|
|
|
779
745
|
For a non-paying package/discovery check, append
|
|
780
|
-
`-t 'should list all
|
|
746
|
+
`-t 'should list all 4 circuits'`. This checks discovery only, not proof generation.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { CircuitName, ClientConfig, TypedAction } from './types.js';
|
|
2
|
+
/** Requester capability. Keep this handle private; show only approvalUrl to the person. */
|
|
3
|
+
export interface ActionApproval {
|
|
4
|
+
approvalId: string;
|
|
5
|
+
approvalUrl: string;
|
|
6
|
+
requesterToken: string;
|
|
7
|
+
/** Fixed session expiry, as an ISO timestamp. This is not an on-chain signature deadline. */
|
|
8
|
+
expiresAt: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ActionApprovalRequest {
|
|
11
|
+
circuit: CircuitName;
|
|
12
|
+
scope: string;
|
|
13
|
+
action: TypedAction;
|
|
14
|
+
expectedSigner?: string;
|
|
15
|
+
}
|
|
16
|
+
export type ActionApprovalState = 'pending' | 'approved' | 'rejected' | 'consumed' | 'expired';
|
|
17
|
+
export interface ActionApprovalStatus extends ActionApprovalRequest {
|
|
18
|
+
approvalId: string;
|
|
19
|
+
status: ActionApprovalState;
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
address?: string;
|
|
22
|
+
}
|
|
23
|
+
export type ActionApprovalErrorCode = 'ACTION_APPROVAL_INVALID' | 'ACTION_APPROVAL_REJECTED' | 'ACTION_APPROVAL_CONSUMED' | 'ACTION_APPROVAL_EXPIRED' | 'ACTION_APPROVAL_UNAVAILABLE';
|
|
24
|
+
export declare class ActionApprovalError extends Error {
|
|
25
|
+
readonly code: ActionApprovalErrorCode;
|
|
26
|
+
constructor(code: ActionApprovalErrorCode, message: string);
|
|
27
|
+
}
|
|
28
|
+
/** A resumable pause before proof work or payment, not a failed proof. */
|
|
29
|
+
export declare class ActionApprovalRequiredError extends Error {
|
|
30
|
+
readonly code = "ACTION_APPROVAL_REQUIRED";
|
|
31
|
+
readonly approval: ActionApproval;
|
|
32
|
+
constructor(approval: ActionApproval);
|
|
33
|
+
}
|
|
34
|
+
/** Create a pending session; this does not sign, prepare a proof, or pay. */
|
|
35
|
+
export declare function createActionApproval(config: ClientConfig, request: ActionApprovalRequest): Promise<ActionApproval>;
|
|
36
|
+
/** Status never returns a raw signature or a requester credential. */
|
|
37
|
+
export declare function getActionApprovalStatus(config: ClientConfig, approval: ActionApproval): Promise<ActionApprovalStatus>;
|
|
38
|
+
/**
|
|
39
|
+
* Consume once and cryptographically check the returned signature against the
|
|
40
|
+
* caller's frozen action. A lost response must be inspected, never auto-retried.
|
|
41
|
+
*/
|
|
42
|
+
export declare function consumeActionApproval(config: ClientConfig, request: ActionApprovalRequest, approval: ActionApproval): Promise<{
|
|
43
|
+
address: string;
|
|
44
|
+
signature: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** Resume an approved action, or yield a handle for human review before any proof work. */
|
|
47
|
+
export declare function resolveActionApproval(config: ClientConfig, request: ActionApprovalRequest, approval?: ActionApproval): Promise<{
|
|
48
|
+
address: string;
|
|
49
|
+
signature: string;
|
|
50
|
+
}>;
|
|
51
|
+
//# sourceMappingURL=actionApproval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionApproval.d.ts","sourceRoot":"","sources":["../src/actionApproval.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,2FAA2F;AAC3F,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAC/F,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,uBAAuB,GAC/B,yBAAyB,GACzB,0BAA0B,GAC1B,0BAA0B,GAC1B,yBAAyB,GACzB,6BAA6B,CAAC;AAElC,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,IAAI,EAAE,uBAAuB;gBAA7B,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM;CAI3E;AAED,0EAA0E;AAC1E,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,SAAiB,QAAQ,EAAE,cAAc,CAAC;gBAE9B,QAAQ,EAAE,cAAc;CAMrC;AAiGD,6EAA6E;AAC7E,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC,CAOxH;AAED,sEAAsE;AACtE,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAa3H;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB3K;AAED,2FAA2F;AAC3F,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB5K"}
|