From 5d3d5b03ab1e71126177f9b96713c4653625135a Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Sun, 20 Sep 2026 20:45:52 -0700 Subject: [PATCH 1/4] docs(world-id): fix on-chain action hash to use field-reduced value - world-id/idkit/onchain-verification.mdx: change the minimal-mapping guidance for computing verify()/verifyAndExecute() args from raw action = keccak256(action) to the field-reduced action = hash_to_field(action) (keccak256(action) >> 8), and add a warning that a raw hash exceeds the BN254 scalar field and reverts with PublicInputNotInField --- world-id/idkit/onchain-verification.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/world-id/idkit/onchain-verification.mdx b/world-id/idkit/onchain-verification.mdx index 4d16d9d..0a45d0b 100644 --- a/world-id/idkit/onchain-verification.mdx +++ b/world-id/idkit/onchain-verification.mdx @@ -167,7 +167,7 @@ contract VerifyUniquenessV4 { Minimal mapping from IDKit result: - `nullifier` = `responses[i].nullifier` -- `action` = `keccak256(action)` as `uint256` +- `action` = `hash_to_field(action)` (i.e. `keccak256(action) >> 8`, the same field-reduction used for [RP signatures](/world-id/idkit/signatures#algorithm)) as `uint256` - `rpId` = numeric form of your `rp_context.rp_id` (the `rp_`-prefixed string from your RP context, not the result) - `nonce` = top-level `nonce` - `signalHash` = `responses[i].signal_hash` @@ -175,3 +175,7 @@ Minimal mapping from IDKit result: - `issuerSchemaId` = `responses[i].issuer_schema_id` - `credentialGenesisIssuedAtMin` = the request's `genesis_issued_at_min` constraint (`0` if unconstrained) — not returned in `responses[i]` - `proof` = `responses[i].proof` (`uint256[5]`) + + + Do not pass a raw, unreduced `keccak256(action)` — it is a 256-bit value that generally exceeds the BN254 scalar field and will cause the proof verification to revert with `PublicInputNotInField`. Always reduce it with `hash_to_field` (`keccak256(action) >> 8`) first. + From 738be27df81e3a500ecbb2ca183677a1158c54b2 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Mon, 21 Sep 2026 10:19:14 -0700 Subject: [PATCH 2/4] polish: tighten wording on hash_to_field mapping, match house style --- world-id/idkit/onchain-verification.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/world-id/idkit/onchain-verification.mdx b/world-id/idkit/onchain-verification.mdx index 0a45d0b..0150abb 100644 --- a/world-id/idkit/onchain-verification.mdx +++ b/world-id/idkit/onchain-verification.mdx @@ -167,7 +167,7 @@ contract VerifyUniquenessV4 { Minimal mapping from IDKit result: - `nullifier` = `responses[i].nullifier` -- `action` = `hash_to_field(action)` (i.e. `keccak256(action) >> 8`, the same field-reduction used for [RP signatures](/world-id/idkit/signatures#algorithm)) as `uint256` +- `action` = `hash_to_field(action)` as `uint256` (same field-reduction as [RP signatures](/world-id/idkit/signatures#algorithm)) - `rpId` = numeric form of your `rp_context.rp_id` (the `rp_`-prefixed string from your RP context, not the result) - `nonce` = top-level `nonce` - `signalHash` = `responses[i].signal_hash` @@ -177,5 +177,5 @@ Minimal mapping from IDKit result: - `proof` = `responses[i].proof` (`uint256[5]`) - Do not pass a raw, unreduced `keccak256(action)` — it is a 256-bit value that generally exceeds the BN254 scalar field and will cause the proof verification to revert with `PublicInputNotInField`. Always reduce it with `hash_to_field` (`keccak256(action) >> 8`) first. + Never pass a raw, unreduced `keccak256(action)` — it exceeds the BN254 scalar field and reverts with `PublicInputNotInField`. Reduce it with `hash_to_field` (`keccak256(action) >> 8`) first. From 4820e778b3c1978cbda603bb63e49a66e110c040 Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Tue, 22 Sep 2026 18:47:19 -0700 Subject: [PATCH 3/4] docs(world-id): clarify unreduced action hash failures --- world-id/idkit/onchain-verification.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world-id/idkit/onchain-verification.mdx b/world-id/idkit/onchain-verification.mdx index 0150abb..0d96a3f 100644 --- a/world-id/idkit/onchain-verification.mdx +++ b/world-id/idkit/onchain-verification.mdx @@ -177,5 +177,5 @@ Minimal mapping from IDKit result: - `proof` = `responses[i].proof` (`uint256[5]`) - Never pass a raw, unreduced `keccak256(action)` — it exceeds the BN254 scalar field and reverts with `PublicInputNotInField`. Reduce it with `hash_to_field` (`keccak256(action) >> 8`) first. + Use `hash_to_field(action)` (`keccak256(action) >> 8`). An unreduced hash can exceed the BN254 scalar field and revert with `PublicInputNotInField`; even when it fits, it does not match the field-reduced action used by the proof. From 79654aa655f0f2151c032f5e4c269e986622d91f Mon Sep 17 00:00:00 2001 From: Soam Desai Date: Wed, 23 Sep 2026 15:00:44 -0700 Subject: [PATCH 4/4] docs(world-id): name the bytes hashed for the on-chain action - action mapping: hash_to_field(utf8(action)) over the UTF-8 bytes of the top-level action string, matching how IDKit builds the proof's action - warning: give the Solidity form, uint256(keccak256(bytes(action))) >> 8 --- world-id/idkit/onchain-verification.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/world-id/idkit/onchain-verification.mdx b/world-id/idkit/onchain-verification.mdx index 0d96a3f..67c35cd 100644 --- a/world-id/idkit/onchain-verification.mdx +++ b/world-id/idkit/onchain-verification.mdx @@ -167,7 +167,7 @@ contract VerifyUniquenessV4 { Minimal mapping from IDKit result: - `nullifier` = `responses[i].nullifier` -- `action` = `hash_to_field(action)` as `uint256` (same field-reduction as [RP signatures](/world-id/idkit/signatures#algorithm)) +- `action` = `hash_to_field(utf8(action))` as `uint256`, over the UTF-8 bytes of the top-level `action` string (same field-reduction as [RP signatures](/world-id/idkit/signatures#algorithm)) - `rpId` = numeric form of your `rp_context.rp_id` (the `rp_`-prefixed string from your RP context, not the result) - `nonce` = top-level `nonce` - `signalHash` = `responses[i].signal_hash` @@ -177,5 +177,5 @@ Minimal mapping from IDKit result: - `proof` = `responses[i].proof` (`uint256[5]`) - Use `hash_to_field(action)` (`keccak256(action) >> 8`). An unreduced hash can exceed the BN254 scalar field and revert with `PublicInputNotInField`; even when it fits, it does not match the field-reduced action used by the proof. + Use `hash_to_field(utf8(action))`, which in Solidity is `uint256(keccak256(bytes(action))) >> 8`. An unreduced hash can exceed the BN254 scalar field and revert with `PublicInputNotInField`; even when it fits, it does not match the field-reduced action used by the proof.