From x402fhe
Zama-native ACL delegation for x402fhe — share read access to encrypted cUSDC balances (or other FHE handles) with a chosen Ethereum address, revoke that access, or use a delegation that someone else granted you to decrypt their balance. Use when the user asks to grant view, share encrypted balance, let someone read their balance, give read access, delegate decryption, revoke a delegation, take back read access, or use a previously granted delegation to read another address's encrypted cUSDC balance. Also use when a server needs to authorize a facilitator to decrypt stored payment-amount handles on the X402PaymentVerifier contract via the --contract override. Do NOT use for direct payments, wrapping or unwrapping cUSDC, escrow job lifecycle, agent identity registration, reputation feedback, or HTTP-402 paid-request orchestration — those are handled by sibling skills (fhe-payment-basics, fhe-payment-unwrap, fhe-escrow, fhe-agent-identity, x402-demo-orchestrators).
How this skill is triggered — by the user, by Claude, or both
Slash command
/x402fhe:fhe-delegationThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Three OpenClaw commands wrap Zama's native SDK delegation surface so one wallet can authorize another to decrypt encrypted FHE handles: `grant-view`, `revoke-view`, and `view-as`. The default target is cUSDC balance sharing, but the same plumbing applies to any FHE-protected handle stored on a contract that the delegator has ACL access over (notably `X402PaymentVerifier.paymentAmounts` for faci...
Three OpenClaw commands wrap Zama's native SDK delegation surface so one wallet can authorize another to decrypt encrypted FHE handles: grant-view, revoke-view, and view-as. The default target is cUSDC balance sharing, but the same plumbing applies to any FHE-protected handle stored on a contract that the delegator has ACL access over (notably X402PaymentVerifier.paymentAmounts for facilitator amount audits). All three scripts call native Token / ReadonlyToken methods on @zama-fhe/sdk; none of them work in stub mode.
view-as --delegator X means: "I (the calling wallet) decrypt X's encrypted balance because X granted my address access". It does NOT mean "switch wallets to become X" or "impersonate X for one call".
This is enforced by the script. Read view-as.ts line 13:
const delegateAddress = await wallet.getAddress();
The delegate address is always the address of whatever wallet your env variables (USER_PRIVATE_KEY, DFNS_WALLET_ID, LEDGER_BRIDGE_URL) currently authenticate. There is no --as, --as-wallet, --from, or --impersonate flag. There is no way to feed a different address into the delegateAddress slot. The only knob is the env-level wallet selection, which the script reads once at startup.
A grants view access to B (A runs grant-view --delegate B). To then read A's encrypted balance:
view-as --delegator A. The script authenticates as B (env vars), asks the gateway "does A allow B to decrypt? yes — give me the plaintext".view-as --delegator A — that is a self-delegation read which the gateway rejects.view-as --delegator B — A was the delegator, not the delegate. The grant only goes one direction.view-as --delegator A (where C is some unrelated third party) — the gateway will return "no delegation exists".| User says | What it sounds like | What it actually requires |
|---|---|---|
| "show me Bob's balance as if I were him" | impersonate Bob | impossible from a fixed wallet — Bob has to run view-as --delegator <whoever-shared-with-Bob> from his own env |
| "I just granted Bob view, now verify it worked" | run view-as from my own wallet to test the grant | impossible — the grant flows from you to Bob, so only Bob can exercise it. Ask Bob to run view-as --delegator <your-address> from his env |
| "switch to Bob's wallet temporarily and read his balance" | swap signers for one call | not supported — wallet selection is process-level via env vars, not per-call |
| "let me see what Bob sees" | impersonate Bob's view | impossible — you can only see what addresses have delegated to YOU |
Even when the agent feels strong pressure to "make it work", these excuses do not change the script's hardcoded behaviour:
| Rationalization | Why it is wrong |
|---|---|
| "The script must support impersonation somewhere" | It does not. delegateAddress = await wallet.getAddress() is hardcoded on line 13. There is no override path. |
"I will just pass --as-wallet 0xBob" | There is no such flag. parseCliArgs runs strict-false, so it will be silently dropped and you will get a misleading downstream error. |
| "It is the only way to verify the grant worked" | No — the grant is verifiable on-chain via the tx receipt and via isDelegated(), both of which revoke-view and view-as's preflight call. The user does not need to test the gateway round-trip from A's wallet. |
| "The user clearly meant impersonate, so I should figure out a way" | The user is using natural language for a concept that does not map to the protocol. The correct response is to disambiguate, not to invent a code path. Explain: "Only Bob's wallet can exercise the grant; if you want to see what Bob sees, Bob runs the command from his env." |
"I will just temporarily set USER_PRIVATE_KEY to Bob's key" | You almost certainly do not have Bob's private key, and even if you did, swapping it mid-session is a destructive env mutation that affects every subsequent script call. Never propose this. |
| "The grant must be testable end-to-end before I report success" | The grant tx is on-chain. Reporting "delegation tx mined, propagation takes 1-2 min, only Bob's wallet can exercise it" IS the success report. |
If the user says "as if I were them", "switch to their wallet", "impersonate", "pretend to be X", "test the grant from my side", or "see what they see" — STOP. Do not run view-as. Do not invent a flag. Reply with a one-paragraph explanation of the direction rule: "view-as runs from YOUR wallet and only reads what addresses have explicitly delegated to YOU. To exercise a grant you just made, the recipient has to run view-as --delegator <your-address> from their own env."
After grant-view mines, there is a real propagation lag between the on-chain ACL state and the Zama gateway. The asymmetry matters:
| Layer | Update timing after grant-view mines |
|---|---|
On-chain ACL (read by isDelegated() and getDelegationExpiry()) | Immediate — the moment the tx is included, both preflight calls return the new state. |
Zama gateway / KMS (consumed by decryptBalanceAs()) | 1-2 minutes lag — the gateway needs time to ingest the on-chain event before it will honor decrypt requests. |
Practical consequence: immediately after grant-view mines, view-as's on-chain preflight will pass (it sees the delegation), then the actual decryptBalanceAs() call may 500 with DelegationNotPropagated. The script catches this and remaps it:
"Delegation may not have propagated to the Zama gateway yet. Wait 1-2 minutes after grant-view and try again."
Do not run view-as within 60 seconds of grant-view. Wait at least 90 seconds, ideally a full 2 minutes.
Always warn the user about the propagation window in your reply to a successful grant-view. Quote the note field from the JSON verbatim. The recipient (delegate) needs to know they should wait before testing.
If view-as fails with the propagation message, do NOT auto-retry, do NOT re-run grant-view (that wastes gas and does not help — the original delegation is already on-chain). Tell the user "wait 1-2 minutes and re-run the same view-as command".
Error mapping (already implemented in view-as.ts lines 85-90 — recognise these in any raw error you might surface):
DELEGATION_NOT_PROPAGATED (SDK error code)DelegationNotPropagatedmsg.includes('500'))All three remap to the same "wait 1-2 minutes" message.
session.createToken() and session.createReadonlyToken() both throw immediately if _isStub === true:
if (this._isStub) {
throw new Error('Cannot create Token in stub mode. Install @zama-fhe/sdk.');
}
grant-view, revoke-view, and view-as ALL hit one of these factories before any other work. There is no graceful fallback — the script returns {"ok": false, "error": "Cannot create Token in stub mode. Install @zama-fhe/sdk."} and exits.
If you see this error from any of the three commands, the fix is environmental, not in the command. Direct the user to ../../references/wallet-setup.md for live @zama-fhe/sdk installation. Do not retry, do not paper over with a workaround — none of the delegation commands have a stub path.
Five rules that apply to every command in this skill:
{"ok": true, "action": "<grant_view|revoke_view|view_as>", ...} or {"ok": false, "error": "..."}. The canonical check is parsed.ok === true. Bash exit code is unreliable — a script that returns {"ok": false, ...} still exits 0. The action field is informational, never use it as a second success signal.grant-view, revoke-view). Both submit signed on-chain transactions. A retry is a second tx, a second gas spend, and on grant-view it reseeds the expiry timer to a NEW window — you can quietly extend a delegation the user did not ask to extend. Read-only view-as is safe to re-run, but only after the propagation window."execution reverted", contract-specific revert messages, or SDK error codes. The exact text is the diagnostic — agents downstream of you may pattern-match on it.view-as failure where the message contains "No delegation exists" or "Delegation expired" is a fast preflight reject (zero KMS calls — see the next section). A failure containing "may not have propagated" is a real KMS round-trip that hit the lag window. The user-facing remediation is different: the first needs a fresh grant-view; the second just needs time.The Zama SDK splits delegation into two classes; the scripts pick the right one for each operation:
| Class | Used by | Operations | On-chain mutation? |
|---|---|---|---|
Token (write-capable) | grant-view, revoke-view | delegateDecryption(), revokeDelegation() | Yes — submits a signed tx, returns a txHash |
ReadonlyToken (read-only) | view-as, revoke-view's preflight | decryptBalanceAs(), isDelegated(), getDelegationExpiry() | No — pure reads, no gas, no signature |
Both factories live on ZamaFheSession:
await session.createToken(wallet, contractAddress) — for any writeawait session.createReadonlyToken(wallet, contractAddress) — for any readBoth gate on the live @zama-fhe/sdk (see the stub-mode rule above). revoke-view.ts actually constructs both in one run: a ReadonlyToken for the isDelegated() preflight, then a Token to submit the revoke tx. view-as.ts only ever needs a ReadonlyToken — there is no on-chain mutation in a delegated read.
This split matters when reading errors: a "Cannot create Token in stub mode" error from view-as is impossible (it never constructs a Token); the equivalent is "Cannot create ReadonlyToken in stub mode". Match the error message to the right class.
view-as.ts calls two preflight methods on ReadonlyToken BEFORE the KMS round-trip, so missing or expired delegations cost zero gateway calls. From the script:
Lines 22-29 — readonlyToken.isDelegated({ delegatorAddress, delegateAddress }). If false, the script returns:
"No delegation exists from <addr> to your wallet for cUSDC. Ask the delegator to run: grant-view --delegate <your-address>"
The call is wrapped in try/catch — if the preflight itself errors, the script falls through and lets the main path surface the real issue.
Lines 38-54 — readonlyToken.getDelegationExpiry({ delegatorAddress, delegateAddress }). If expiry > 0n && expiry <= nowSec, the script returns:
"Delegation from <addr> expired at <ISO timestamp>. Ask the delegator to run grant-view again."
Also try/catch wrapped. expiry === 0n is treated as "permanent" (the SDK uses uint64.max for permanent grants, but on-chain "no expiry set" can also surface as 0).
Only after both preflights pass does view-as call decryptBalanceAs({ delegatorAddress }), which is the actual gateway round-trip.
Operational consequence: a view-as failure that quotes the preflight messages above is fast and free — it never hit the KMS. A view-as failure that quotes "may not have propagated" is the only failure mode that costs a real gateway call. Rely on this distinction when deciding whether to wait (propagation) or to escalate to the delegator (preflight).
--contract override (facilitator amount verification)grant-view.ts line 16:
const contractAddress = args.contract ?? cUSDCAddress;
By default, grant-view delegates ACL access for the cUSDC token contract — that is the "share my balance" use case. Pass --contract <address> to delegate ACL access for a different contract whose handles the delegator has FHE ACL over.
X402PaymentVerifier.paymentAmounts[nonce] stores a euint64 handle of the post-clamped transferred amount, and during onConfidentialTransferReceived the verifier grants persistent ACL access to the recipient via FHE.allow(amount, payTo). So the recipient (payTo, typically a server) holds ACL rights over those handles.
If the server wants a third-party facilitator to audit transferred amounts (verify decryptedAmount >= minPrice per CLAUDE.md), the server runs:
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts \
--delegate <facilitator-address> \
--contract <X402PaymentVerifier-address>
After propagation, the facilitator can call delegatedUserDecrypt on any paymentAmounts[nonce] handle stored in the verifier and confirm the transferred amount meets the minimum. This is opt-in and requires FACILITATOR_PRIVATE_KEY set in the facilitator's env.
--contract <verifier-address> — server-side amount audits via a third-party facilitator. Pass the deployed X402PaymentVerifier address, NOT the cUSDC address.--contract <other-fhe-contract> — any other contract where the delegator holds ACL rights over an FHE handle they want a delegate to decrypt.The --contract value is regex-validated as a 0x-prefixed 40-hex-char address. Wrong format returns "--contract must be a valid Ethereum address".
You need one wallet mode plus an RPC URL set in env. See ../../references/wallet-setup.md for the three modes (user, dfns, ledger-bridge), ../../references/env-vars.md for the full variable list, and ../../references/output-schema.md for the canonical ok / fail wrapper shape. Minimum dev setup:
export WALLET_MODE=user
export USER_PRIVATE_KEY=0x... # 64 hex chars
export RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
When RPC_URL points to Sepolia (chain ID 11155111), SEPOLIA_ADDRESSES are auto-selected — you do not need to override CUSDC_ADDRESS. For the facilitator audit pattern, you also need VERIFIER_ADDRESS resolved (check via the sibling info command in fhe-payment-basics).
Stub-mode reminder: this entire skill requires a live @zama-fhe/sdk install. If you are not sure, run info from fhe-payment-basics first — if that returns OK but any delegation command fails with "Cannot create [Token|ReadonlyToken] in stub mode", the SDK is missing.
When to use: the user asks to share their encrypted balance with another address, give read access, let X read their cUSDC, delegate decryption, or authorize a facilitator (--contract override) to audit a payment-amount handle.
Invocation (default — share own cUSDC balance, 24-hour expiry):
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts --delegate 0xBob...
Invocation (custom expiry in hours):
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts --delegate 0xBob... --hours 168
Invocation (permanent — never expires, SDK uses uint64.max):
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts --delegate 0xBob... --permanent true
Invocation (facilitator amount audit on the verifier contract):
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts \
--delegate 0xFacilitator... \
--contract 0xX402PaymentVerifier...
Required env: RPC_URL, one wallet mode, ETH for one tx (~150-300k gas).
Required flags: --delegate <0x...> (the address that will gain decrypt rights).
Optional flags:
--hours N — expiry window in hours, default 24. Must be a positive integer >= 1.--permanent true — no expiry. Mutually exclusive with --hours (permanent wins). Also accepted as --permanent with no value.--contract <0x...> — override the default cUSDC address with another FHE-holding contract (canonical: the X402PaymentVerifier address for facilitator audits).Returns:
{
"ok": true,
"action": "grant_view",
"delegator": "0x...",
"delegate": "0x...",
"contract": "0x...",
"expiresIn": "24 hours",
"txHash": "0x...",
"note": "Delegation takes 1-2 minutes to propagate to the Zama gateway before view-as works."
}
expiresIn is one of: "<N> hours", "permanent (never expires)". The note field is always populated — quote it verbatim in your reply so the user (and the delegate, if they're the next actor) knows to wait before exercising the grant.
Common errors:
--delegate is required — forgot the flag.--delegate must be a valid Ethereum address — wrong format. Must be 0x + exactly 40 hex chars.--contract must be a valid Ethereum address — same rule for the override.--hours must be a positive integer >= 1 — non-integer or zero. Use 1, 24, 168, etc.Cannot delegate to yourself. The delegate must be a different address. — caught from the SDK's DelegationSelfNotAllowedError and remapped. The delegate cannot equal the delegator.Cannot create Token in stub mode. Install @zama-fhe/sdk. — see the stub-mode CRITICAL section above.execution reverted / insufficient funds for gas — standard tx failures. Check ETH balance, contract addresses (run info from fhe-payment-basics).When to use: the user asks to revoke view access, take back read access, end a delegation, stop sharing their balance with X, or undo a previous grant-view. Operates only on cUSDC by default — there is no --contract flag on revoke-view.ts, so to revoke a non-cUSDC delegation, the script needs a code change (out of scope for this skill).
Invocation:
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/revoke-view.ts --delegate 0xBob...
Required env: RPC_URL, one wallet mode, ETH for one tx.
Required flags: --delegate <0x...> (the address to revoke).
Preflight behaviour: revoke-view.ts constructs a ReadonlyToken first and calls isDelegated({ delegatorAddress, delegateAddress }). If no active delegation exists, the script short-circuits BEFORE submitting any tx and returns:
"No active delegation exists from <delegator> to <delegate> for cUSDC."
This protects the user from spending gas on a no-op revoke. If the preflight passes, the script then constructs a Token and calls revokeDelegation({ delegateAddress }).
Returns:
{
"ok": true,
"action": "revoke_view",
"delegator": "0x...",
"delegate": "0x...",
"contract": "0x...",
"txHash": "0x..."
}
contract is always the cUSDC address (no override on this command).
Common errors:
--delegate is required — forgot the flag.--delegate must be a valid Ethereum address — wrong format.No active delegation exists from <X> to <Y> for cUSDC. — preflight short-circuit. Either the user already revoked, or it never existed, or the --delegate value is a typo. Confirm with the user before re-running.Cannot create ReadonlyToken in stub mode. / Cannot create Token in stub mode. — stub mode. See CRITICAL section.execution reverted — rare on revoke; usually means the verifier/ACL contract changed state mid-flight or the env points at a wrong contract.When to use: the user has previously been GRANTED view access by another address (the delegator), and now wants to read THAT delegator's encrypted cUSDC balance using their own (the calling wallet's) keys. The user must be the DELEGATE — see the CRITICAL "view-as direction" section above before invoking.
Invocation:
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/view-as.ts --delegator 0xAlice...
Required env: RPC_URL, one wallet mode (this wallet must be the delegate that Alice authorized).
Required flags: --delegator <0x...> (the address that GRANTED you access — typically NOT your own address).
Preflight behaviour: see the IMPORTANT "view-as preflight short-circuits" section above. The script checks isDelegated() and getDelegationExpiry() on a ReadonlyToken BEFORE any KMS round-trip, so missing/expired delegations are reported instantly with no gateway cost. Only after both pass does it call decryptBalanceAs().
Returns:
{
"ok": true,
"action": "view_as",
"delegator": "0xAlice...",
"delegate": "0xYourAddress...",
"decryptedBalance": "12.345678",
"decryptedBalanceRaw": "12345678",
"delegationExpiry": "1745452800"
}
decryptedBalance is the human-readable USDC value (6-decimal). decryptedBalanceRaw is the raw bigint as a string. delegationExpiry is a Unix-second timestamp string, or "unknown" if the SDK could not return it (preflight catch).
Common errors:
--delegator is required — forgot the flag, OR (more likely) typed --delegate (the wrong direction word). The flag is --delegator because it names the address that granted the delegation.--delegator must be a valid Ethereum address — wrong format.No delegation exists from <X> to your wallet for cUSDC. Ask the delegator to run: grant-view --delegate <your-address> — preflight fast-reject. The grant has not been issued (or was issued to a different address than your current wallet). Quote this message back to the user with the exact <your-address> shown so they can forward it to the delegator.Delegation from <X> expired at <ISO>. Ask the delegator to run grant-view again. — preflight expiry reject. The delegator needs to re-issue.Delegation may not have propagated to the Zama gateway yet. Wait 1-2 minutes after grant-view and try again. — KMS round-trip hit the propagation lag. Wait, do not retry immediately, do not re-run grant-view.Cannot create ReadonlyToken in stub mode. — stub mode.User: "Let 0xBob1234567890abcdef1234567890ABCDEF123456 read my encrypted cUSDC balance."
Run:
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/grant-view.ts --delegate 0xBob1234567890abcdef1234567890ABCDEF123456
Parse the JSON. If ok: true and action: "grant_view", reply:
Granted view access for your cUSDC balance to
0xBob1234567890abcdef1234567890ABCDEF123456. Tx:0x<txHash>. Expires in 24 hours.Wait 1-2 minutes before that delegation works. Quoting the script note verbatim: "Delegation takes 1-2 minutes to propagate to the Zama gateway before view-as works." Bob will hit a propagation error if he tries to read your balance any sooner.
When Bob is ready, he runs from his own wallet env:
view-as --delegator <your-address>. He cannot see what you see by running anything from your wallet — the grant only flows in one direction.
Note that the reply (a) reports the on-chain success, (b) warns about the propagation window, (c) tells the user what Bob does next, and (d) preemptively addresses the wallet-direction confusion before the user can ask "now show me what Bob sees".
Continuing from Example 1 — 30 seconds later, user says: "Now show me Bob's view of my balance — switch to his wallet for one call."
DO NOT run any script. DO NOT invent a flag. DO NOT swap env vars. Reply:
I cannot do that —
view-asalways runs from YOUR wallet (whatever your env vars currently authenticate), and there is no flag to swap signers per call. Specifically,view-as.tsline 13 hardcodesdelegateAddress = await wallet.getAddress()so the calling wallet is always the delegate.What you just granted Bob is the right to decrypt YOUR balance from HIS wallet. To exercise it, Bob runs this from his own env (
USER_PRIVATE_KEYset to Bob's key, or his Dfns wallet, etc.):pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/view-as.ts --delegator <your-address>The grant tx I just submitted is verifiable on-chain right now. Bob's read will work as soon as the Zama gateway finishes propagating (1-2 minutes after the grant tx mined). You do not need to (and cannot) test it from your side.
This is the canonical disambiguation reply. The user's mental model ("show me what they see") does not map to the protocol; explain why, and redirect to what is actually possible.
User: "Alice delegated read access to my wallet earlier today. Show me her cUSDC balance — her address is 0xAlice000000000000000000000000000000000001."
This is the correct direction: the user is the DELEGATE, Alice is the DELEGATOR.
Run:
pnpm --filter @x402fhe/openclaw-skill exec tsx scripts/view-as.ts --delegator 0xAlice000000000000000000000000000000000001
Parse the JSON. Possible outcomes:
ok: true, action: "view_as" — reply with decryptedBalance (human-readable, e.g. "12.345678 cUSDC") and the delegationExpiry if it is meaningful (convert the Unix-second string to ISO if the user wants a date).
ok: false with "No delegation exists from 0xAlice... to your wallet" — preflight reject, no KMS cost. Reply: "Alice has not granted view access to this wallet (<your-address>). Possibilities: the grant was issued to a different address, the grant was already revoked, or it was never issued. Ask Alice to run grant-view --delegate <your-address> from her wallet."
ok: false with "Delegation from 0xAlice... expired at ..." — preflight expiry reject. Reply: "Your delegation from Alice expired at <ISO>. Ask her to run grant-view --delegate <your-address> again."
ok: false with "may not have propagated" — propagation lag. Reply: "The gateway hasn't ingested the delegation yet. Wait 1-2 minutes and re-run the same command. Don't ask Alice to grant again — her tx is already on-chain."
In every case, surface the raw error verbatim alongside your interpretation so the user can pattern-match if they hit something unfamiliar.
| Error text (regex / substring) | Where it surfaces | Cause | Remediation |
|---|---|---|---|
--delegate is required | grant-view, revoke-view | Missing required flag | Add --delegate 0x... |
--delegator is required | view-as | Missing required flag (likely typed --delegate instead) | Use --delegator for view-as — names the granting party |
--delegate must be a valid Ethereum address / --delegator must be... / --contract must be... | all | Wrong format | Must be 0x + exactly 40 hex chars |
--hours must be a positive integer >= 1 | grant-view | Non-integer, zero, or negative --hours | Use a positive integer like 1, 24, 168 |
Cannot delegate to yourself. The delegate must be a different address. | grant-view | --delegate equals the calling wallet's address (caught from SDK DelegationSelfNotAllowedError) | Use a different address. There is no concept of self-delegation. |
No delegation exists from <X> to your wallet for cUSDC. Ask the delegator to run: grant-view --delegate <your-address> | view-as | Preflight isDelegated() returned false — fast reject, zero KMS cost | Forward the message verbatim to the user, including the <your-address> suffix so they know what to ask the delegator for |
Delegation from <X> expired at <ISO>. Ask the delegator to run grant-view again. | view-as | Preflight getDelegationExpiry() returned a past timestamp | Delegator must re-issue with a fresh expiry |
Delegation may not have propagated to the Zama gateway yet. Wait 1-2 minutes after grant-view and try again. | view-as | KMS round-trip hit the 1-2 min lag window (mapped from DELEGATION_NOT_PROPAGATED, DelegationNotPropagated, or generic 500) | Wait 90-120 seconds, re-run the same view-as command. Do NOT re-run grant-view. |
No active delegation exists from <X> to <Y> for cUSDC. | revoke-view | Preflight isDelegated() returned false — nothing to revoke, no tx submitted | Confirm with the user; either it was already revoked, or --delegate is a typo |
Cannot create Token in stub mode. Install @zama-fhe/sdk. | grant-view, revoke-view | Stub-mode session, _isStub === true | Install live @zama-fhe/sdk. See ../../references/wallet-setup.md. None of the delegation commands have a stub fallback. |
Cannot create ReadonlyToken in stub mode. Install @zama-fhe/sdk. | view-as, revoke-view (preflight) | Stub-mode session | Same as above |
execution reverted (raw RPC) | grant-view, revoke-view | Contract-level revert during the on-chain tx — usually wrong contract address, paused contract, or signer mismatch | Run info from fhe-payment-basics to confirm contract addresses are non-zero and chain auto-detect picked Sepolia |
insufficient funds for gas | grant-view, revoke-view | Wallet has no Sepolia ETH | Fund the address shown by info |
could not detect network | all | Bad RPC_URL or rate-limited | Verify the URL and quota |
| Bash exit != 0 with stack trace, NOT a JSON line | all | Script crashed before returning JSON — _wallet.ts init error | Check env vars, pnpm install from repo root |
This skill covers ONLY grant-view, revoke-view, and view-as. Do not use it for:
info / balance / wrap / pay (direct cUSDC operations) — see fhe-payment-basics. Note that balance --of <address> is a DELEGATED read that uses the same plumbing under the hood, but it is a sibling affordance exposed by the basics skill, not a fourth command in this one.unwrap / finalize-unwrap (redeeming cUSDC back to USDC) — see fhe-payment-unwrapcreate-job / fund-job / submit / complete-job (encrypted escrow lifecycle) — see fhe-escrowregister-agent / identity / metadata (ERC-8004 NFT identity) — see fhe-agent-identityresearch-and-visualize / review-and-rate / HTTP-402 paid-request orchestration — see x402-demo-orchestratorsnpx claudepluginhub zama-ai/confidential-agentic-payment-stack --plugin x402fheProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.