Advanced signals


are a powerful security feature of the ZKPs World ID uses. Usually the signal is just a simple string message. Under the hood, this string is hashed and encoded so the Worldcoin app can generate a ZKP that can be shared with the World ID smart contracts. However sometimes a simple string is not enough. Given the limitation of string manipulation in smart contracts, we offer this option so your signal can become any arbitrary number of parameters.

Addresses & numbers

Particularly if your signal is an address or a uint, the JS widget will do the proper handling automatically. This means it will treat the number/address as bytes and hash it directly. Strings conversely, are converted to bytes first before hashing. You can check out the utils.worldIDHash function in the JS widget (v0.6.0 onwards).



Your own advanced signal

Let's say you want to create an action that lets you vote on a proposal and indicate another address to receive an NFT that proves your vote. You should include both parameters (the vote, and the address) as part of the signal to prevent manipulation. Follow these steps:

  1. After defining your signal, you'll need to hash it using keccak256, be sure to use the proper parameter types.

    import { keccak256 } from '@ethersproject/solidity'
    const signalHash = keccak256(
      ['uint8', 'address'],
      [userVote, nftRecipientAddress]

    As you can see above, you can encode any number of parameters and any type of parameters as your signal.

  2. After hashing you'll need to right shift the hash 8 bits (to bring it into the polynomial field we use for the ZKP, all proofs use mod p, with p being ).

    const signal = BigInt(signalHash) >> BigInt(8)
  3. You can then pass this manually hashed and encoded signal to the JS widget and both the widget and the app will process it raw. Remember to set advanced_use_raw_signal to true.

      advanced_use_raw_signal: true,
  4. On your smart contract you'll need to hash and encode the signal in the same way.

    /// uint8 userVote is an input param of the function
    /// address nftRecipientAddress is an input param of the function
        abi.encodePacked(userVote, nftRecipientAddress).hashToField(),

Advanced Action ID

The same functionality available for advanced signals can be used for Action IDs. While we don't recommend it's usage right now (as you won't be able to fully customize the user's experience in the Worldcoin app), you can do so by following the same steps as above.