This is an example of using Wormhole Queries to bridge the World ID state root from Ethereum to Solana.
Enable cross-chain World ID verification so that protocols can verify their users’ identities on Solana. This is accomplished in two parts:
- Read, authenticate, and propagate the World ID state root from Ethereum to Solana.
- Allow for protocols to authenticate users’ World IDs on-chain on Solana using those roots.
Currently, the World ID state is managed on Ethereum in a privacy-preserving manner via an on-chain representation of the Semaphore set.
On Ethereum, on-chain verification of World IDs can be performed by calling verifyProof on the World ID Identity Manager contract (example) with a proof provided by the Tree Availability Service.
On-chain verification can be made available on other blockchains and is currently available on some Layer 2 EVMs, such as Polygon, Optimism, and Base, via their native bridges, the World ID State Bridge contracts, and the State Bridge Service. Integrators can use the bridged OpWorldID and PolygonWorldID contracts as they would the World ID Identity Manager contract on Ethereum.
Wormhole Queries is a service that allows applications, developers, and users to access cross-chain data on-demand in an efficient and inexpensive manner. Queries leverages the Wormhole Guardians (some of the largest proof of stake validators in the blockchain ecosystem) to attest to cross-chain reads, enabling sub-second, authenticated cross-chain data retrieval. It currently supports querying, parsing, and verifying on all connected Wormhole EVM networks and Solana. Read more about Queries in the docs.
💡 The Queries feature relevant for WorldID is support for requesting a designated
eth_callfrom a given contract on Ethereum and the ability to parse and verify the response on Solana.
The Ethereum-to-Solana State Bridge Service is responsible for monitoring the World ID contract on Ethereum for state root changes and propagating the root to Solana. It can do this by performing the following steps:
- Subscribe to
TreeChangedevents or poll forlatestRoot.event TreeChanged(uint256 indexed preRoot, TreeChange indexed kind, uint256 indexed postRoot);
- When the root has updated, issue a Wormhole Query request for the
latestRooton Ethereum. - Submit the Query response to the SolanaWorldID program on Solana.
This is akin to the EVM L2 State Bridge Service
The bridge service provided in /app is a TypeScript program designed to be run either in a scheduled lambda / cloud function setting or as a service. If the SLEEP environment variable is set, it will run as a service, sleeping for SLEEP seconds between calls and will not exit on errors, otherwise it will simply run once, throwing on errors. When running as a service, the CLEANUP environment variable may also be set, which works the same as SLEEP but for cleaning up expired roots, reclaiming their rent.
Optionally, change the following line in solana-world-id-program.ts from it to it.only:
it(fmtTest("initialize", "Successfully initializes"), async () => {Run the following to start a local validator:
anchor test --detachFinally, run
NETWORK=localnet npm startRunning NETWORK=localnet npm start subsequent times will update the root again, as necessary.
Akin to the above but
anchor test --detach -- --no-default-features --features testnetand
NETWORK=testnet MOCK=true SOLANA_RPC_URL="http://127.0.0.1:8899" WALLET="../tests/keys/pFCBP4bhqdSsrWUVTgqhPsLrfEdChBK17vgFM7TxjxQ.json" npm startNETWORK=testnet WALLET=~/.config/solana/your-key.json QUERY_API_KEY=your-wormhole-query-api-key npm startThis program serves two purposes:
- Parse, verify, and manage the bridged World ID state root.
- Provide the equivalent functionality of
verifyProofto on-chain integrators.
This is akin to the World ID State Bridge contracts for EVM L2s and should be compatible with existing inclusion proofs served by the Tree Availability Service.
- Config stores the program configuration. There is only one.
- LatestRoot stores the most recent verified root metadata and hash. There is one per
Rootverification mechanism (e.g. Query with Guardian signatures). - GuardianSignatures stores unverified guardian signatures for subsequent verification. These are created with
post_signaturesin service of verifying a root via Queries and closed when that root is verified withupdate_root_with_queryor can be explicitly closed withclose_signaturesby the initial payer. - Root stores the metadata and expiry for a verified root. These can be closed with
clean_up_rootafter the root has expired.
- initialize sets the initial config and creates the LatestRoot account. It must be signed by the deployer.
- post_signatures posts unverified guardian signatures for verification during
update_root_with_query. - update_root_with_query with a Query response and
GuardianSignaturesaccount, verifies the signatures against an active guardian set and updates thelatestRootfrom the World ID Identity Manager contract on Ethereum. - clean_up_root closes a
Rootaccount which has expired, reimbursing the rent to the initial payer. - close_signatures allows the initial payer to close a
GuardianSignaturesaccount in case the query was invalid. - transfer_ownership is the first of a two-step ownership transfer process which sets the
pending_ownerand locks the ability to upgrade. - claim_ownership is the second step of the ownership transfer process, signed by either the
pending_owner(to accept) or the existingowner(to cancel). - set_root_expiry sets the
root_expiryfield. Theownermust sign. - set_allowed_update_staleness sets the
allowed_update_stalenessfield. Theownermust sign. - verify_groth16_proof verifies a proof against an active root and inputs. Intended to be called via CPI by on-chain integrators, though it can be called directly as well.
anchor testanchor build --verifiable -- --no-default-features --features testnetanchor build --verifiableanchor deploy --provider.cluster devnet --provider.wallet ~/.config/solana/your-key.json
NETWORK=testnet WALLET=~/.config/solana/your-key.json npx tsx app/init.tsanchor deploy --provider.cluster mainnet --provider.wallet ~/.config/solana/your-key.json
NETWORK=mainnet WALLET=~/.config/solana/your-key.json npx tsx app/init.tsanchor upgrade --provider.cluster <network> --provider.wallet ~/.config/solana/your-key.json --program-id <PROGRAM_ID> target/deploy/solana_world_id_program.so
If you get an error like this
Error: Deploying program failed: RPC response error -32002: Transaction simulation failed: Error processing Instruction 0: account data too small for instruction [3 log messages]
Don't fret! Just extend the program size.
solana program -u <network> -k ~/.config/solana/w7-testnet.json extend <PROGRAM_ID> <ADDITIONAL_BYTES>
You can view the current program size with solana program -u <network> show <PROGRAM_ID>.