Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- ShadowVerifier
- Optimization enabled
- true
- Compiler version
- v0.8.33+commit.64118f21
- Optimization runs
- 200
- EVM Version
- shanghai
- Verified at
- 2026-02-24T05:53:27.525582Z
Constructor Arguments
0x0000000000000000000000001670130000000000000000000000000000010001000000000000000000000000c989429b6c3f3e870fdb825735c00d2f4ebcd559
Arg [0] (address) : 0x1670130000000000000000000000000000010001
Arg [1] (address) : 0xc989429b6c3f3e870fdb825735c00d2f4ebcd559
src/impl/ShadowVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {IAnchor} from "../iface/IAnchor.sol";
import {ICircuitVerifier} from "../iface/ICircuitVerifier.sol";
import {IShadow} from "../iface/IShadow.sol";
import {IShadowVerifier} from "../iface/IShadowVerifier.sol";
import {ShadowPublicInputs} from "../lib/ShadowPublicInputs.sol";
/// @title ShadowVerifier
/// @notice Verifies Shadow claim proofs using TaikoAnchor for block hash validation.
/// @dev The ZK proof commits to a blockHash. The stateRoot is derived in-circuit from
/// the RLP-encoded block header. We verify the blockHash is canonical via TaikoAnchor.
/// @custom:security-contact security@taiko.xyz
contract ShadowVerifier is IShadowVerifier {
ICircuitVerifier public immutable circuitVerifier;
IAnchor public immutable anchor;
constructor(address _anchor, address _circuitVerifier) {
require(_anchor != address(0), ZeroAddress());
require(_circuitVerifier != address(0), ZeroAddress());
anchor = IAnchor(_anchor);
circuitVerifier = ICircuitVerifier(_circuitVerifier);
}
/// @notice Verifies a proof and its public inputs.
/// @dev Fetches the canonical blockHash from TaikoAnchor and builds public inputs.
/// The ZK proof verifies: keccak256(block_header_rlp) == blockHash, then derives
/// stateRoot from the header and verifies the account balance against it.
function verifyProof(bytes calldata _proof, IShadow.PublicInput calldata _input)
external
view
returns (bool _isValid_)
{
require(_input.blockNumber > 0, BlockHashNotFound(_input.blockNumber));
// Get canonical block hash from TaikoAnchor
bytes32 blockHash = anchor.blockHashes(_input.blockNumber);
require(blockHash != bytes32(0), BlockHashNotFound(_input.blockNumber));
uint256[] memory publicInputs = ShadowPublicInputs.toArray(_input, blockHash);
bool ok = circuitVerifier.verifyProof(_proof, publicInputs);
require(ok, ProofVerificationFailed());
_isValid_ = true;
}
}
src/iface/IAnchor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @custom:security-contact security@taiko.xyz
interface IAnchor {
function blockHashes(uint256 _blockNumber) external view returns (bytes32 _blockHash_);
}
src/iface/ICircuitVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @custom:security-contact security@taiko.xyz
interface ICircuitVerifier {
/// @notice Verifies a proof against public inputs.
function verifyProof(bytes calldata _proof, uint256[] calldata _publicInputs)
external
view
returns (bool _isValid_);
}
src/iface/IShadow.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @custom:security-contact security@taiko.xyz
interface IShadow {
struct PublicInput {
uint64 blockNumber;
uint256 chainId;
uint256 amount;
address recipient;
bytes32 nullifier;
}
event Claimed(bytes32 indexed nullifier, address indexed recipient, uint256 amount);
error ChainIdMismatch(uint256 expected, uint256 actual);
error InvalidAmount(uint256 amount);
error InvalidRecipient(address recipient);
error NullifierAlreadyConsumed(bytes32 nullifier);
error ProofVerificationFailed();
/// @notice Submits a proof and public inputs to mint ETH via the configured minter hook.
/// @dev The Shadow implementation applies a 0.1% claim fee (`amount / 1000`) to an immutable feeRecipient.
function claim(bytes calldata _proof, PublicInput calldata _input) external;
}
src/iface/IShadowVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {IShadow} from "./IShadow.sol";
/// @custom:security-contact security@taiko.xyz
interface IShadowVerifier {
error BlockHashNotFound(uint64 blockNumber);
error ProofVerificationFailed();
error ZeroAddress();
/// @notice Verifies a proof and its public inputs.
function verifyProof(bytes calldata _proof, IShadow.PublicInput calldata _input)
external
view
returns (bool _isValid_);
}
src/lib/ShadowPublicInputs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import {IShadow} from "../iface/IShadow.sol";
/// @title ShadowPublicInputs
/// @notice Encodes public inputs for Shadow ZK proof verification.
/// @dev Layout matches the prover's ClaimJournal:
/// - Index 0: blockNumber (u64)
/// - Index 1-32: blockHash (32 bytes, each byte as uint256)
/// - Index 33: chainId (u64)
/// - Index 34: amount (u128)
/// - Index 35-54: recipient (20 bytes)
/// - Index 55-86: nullifier (32 bytes)
/// @custom:security-contact security@taiko.xyz
library ShadowPublicInputs {
uint256 private constant _PUBLIC_INPUTS_LEN = 87;
uint256 private constant _IDX_BLOCK_NUMBER = 0;
uint256 private constant _IDX_BLOCK_HASH = 1;
uint256 private constant _IDX_CHAIN_ID = 33;
uint256 private constant _IDX_AMOUNT = 34;
uint256 private constant _IDX_RECIPIENT = 35;
uint256 private constant _IDX_NULLIFIER = 55;
/// @notice Converts a PublicInput struct to a uint256 array for circuit verification.
/// @dev blockHash is fetched on-chain from TaikoAnchor, so it is not part of
/// `IShadow.PublicInput` calldata.
function toArray(IShadow.PublicInput calldata _input, bytes32 _blockHash)
internal
pure
returns (uint256[] memory inputs_)
{
inputs_ = new uint256[](_PUBLIC_INPUTS_LEN);
inputs_[_IDX_BLOCK_NUMBER] = _input.blockNumber;
_writeBytes32(inputs_, _IDX_BLOCK_HASH, _blockHash);
inputs_[_IDX_CHAIN_ID] = _input.chainId;
inputs_[_IDX_AMOUNT] = _input.amount;
_writeAddress(inputs_, _IDX_RECIPIENT, _input.recipient);
_writeBytes32(inputs_, _IDX_NULLIFIER, _input.nullifier);
}
function _writeBytes32(uint256[] memory _inputs, uint256 _offset, bytes32 _value) private pure {
for (uint256 i = 0; i < 32;) {
_inputs[_offset + i] = uint256(uint8(_value[i]));
unchecked {
++i;
}
}
}
function _writeAddress(uint256[] memory _inputs, uint256 _offset, address _value) private pure {
for (uint256 i = 0; i < 20;) {
_inputs[_offset + i] = uint256(uint8(bytes20(_value)[i]));
unchecked {
++i;
}
}
}
}
Compiler Settings
{"viaIR":true,"remappings":["forge-std/=node_modules/forge-std/src/","@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/","@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","risc0-ethereum/=lib/risc0-ethereum/contracts/src/","openzeppelin/contracts/=node_modules/@openzeppelin/contracts/"],"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"shanghai"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_anchor","internalType":"address"},{"type":"address","name":"_circuitVerifier","internalType":"address"}]},{"type":"error","name":"BlockHashNotFound","inputs":[{"type":"uint64","name":"blockNumber","internalType":"uint64"}]},{"type":"error","name":"ProofVerificationFailed","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAnchor"}],"name":"anchor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ICircuitVerifier"}],"name":"circuitVerifier","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"_isValid_","internalType":"bool"}],"name":"verifyProof","inputs":[{"type":"bytes","name":"_proof","internalType":"bytes"},{"type":"tuple","name":"_input","internalType":"struct IShadow.PublicInput","components":[{"type":"uint64","name":"blockNumber","internalType":"uint64"},{"type":"uint256","name":"chainId","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"bytes32","name":"nullifier","internalType":"bytes32"}]}]}]
Contract Creation Code
0x60c0346100b657601f6105f438819003918201601f19168301916001600160401b038311848410176100ba5780849260409485528339810103126100b657610052602061004b836100ce565b92016100ce565b6001600160a01b039091169081156100a7576001600160a01b03169081156100a75760a05260805260405161051190816100e38239608051818181609701526102d8015260a051818181604f01526101740152f35b63d92e233d60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100b65756fe60806040526004361015610011575f80fd5b5f3560e01c8063ca167509146100c6578063d0ea9ff0146100825763d3fb73b41461003a575f80fd5b3461007e575f36600319011261007e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5f80fd5b3461007e575f36600319011261007e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461007e5760c036600319011261007e5760043567ffffffffffffffff811161007e573660238201121561007e57806004013567ffffffffffffffff811161007e57366024828401011161007e5760a036602319011261007e5761014567ffffffffffffffff610134610455565b16151561013f610455565b9061046c565b61014d610455565b6040516334cdf78d60e01b815267ffffffffffffffff9190911660048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561036b575f91610423575b506101c06101b8610455565b82151561046c565b610b0091604051916101d28484610491565b605783526020830193601f190136853767ffffffffffffffff6101f3610455565b168351156103eb5784525f5b602081106103ff5750508151602110156103eb576044356104408301528151602210156103eb576064356104608301526084356001600160a01b038116810361007e5760601b6bffffffffffffffffffffffff19165f5b601481106103c757505060a4355f5b6020811061038f57602486868660848780604051968796631e8e1e1360e01b8852604060048901528260448901520160648701375f60648287010152601f801991011684016064810192600319606487840301016024870152518093520191905f5b8181106103765784602081808703817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561036b575f91610330575b501561032157602060405160018152f35b631ac2386360e31b5f5260045ffd5b90506020813d602011610363575b8161034b60209383610491565b8101031261007e5751801515810361007e5781610310565b3d915061033e565b6040513d5f823e3d90fd5b82518452859450602093840193909201916001016102c7565b8060370190816037116103b3576001916103ac84831a91876104c7565b5201610265565b634e487b7160e01b5f52601160045260245ffd5b8060230190816023116103b3576001916103e484831a91876104c7565b5201610256565b634e487b7160e01b5f52603260045260245ffd5b8060010190816001116103b35760019161041c84831a91876104c7565b52016101ff565b90506020813d60201161044d575b8161043e60209383610491565b8101031261007e5751836101ac565b3d9150610431565b60243567ffffffffffffffff8116810361007e5790565b156104745750565b67ffffffffffffffff906328e89c8b60e01b5f521660045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176104b357604052565b634e487b7160e01b5f52604160045260245ffd5b80518210156103eb5760209160051b01019056fea26469706673582212205e31ce7a58695ed70aeb299ab7d44bf75540f4e30db65eee511a2105d2c9d03e64736f6c634300082100330000000000000000000000001670130000000000000000000000000000010001000000000000000000000000c989429b6c3f3e870fdb825735c00d2f4ebcd559
Deployed ByteCode
0x60806040526004361015610011575f80fd5b5f3560e01c8063ca167509146100c6578063d0ea9ff0146100825763d3fb73b41461003a575f80fd5b3461007e575f36600319011261007e576040517f00000000000000000000000016701300000000000000000000000000000100016001600160a01b03168152602090f35b5f80fd5b3461007e575f36600319011261007e576040517f000000000000000000000000c989429b6c3f3e870fdb825735c00d2f4ebcd5596001600160a01b03168152602090f35b3461007e5760c036600319011261007e5760043567ffffffffffffffff811161007e573660238201121561007e57806004013567ffffffffffffffff811161007e57366024828401011161007e5760a036602319011261007e5761014567ffffffffffffffff610134610455565b16151561013f610455565b9061046c565b61014d610455565b6040516334cdf78d60e01b815267ffffffffffffffff9190911660048201526020816024817f00000000000000000000000016701300000000000000000000000000000100016001600160a01b03165afa90811561036b575f91610423575b506101c06101b8610455565b82151561046c565b610b0091604051916101d28484610491565b605783526020830193601f190136853767ffffffffffffffff6101f3610455565b168351156103eb5784525f5b602081106103ff5750508151602110156103eb576044356104408301528151602210156103eb576064356104608301526084356001600160a01b038116810361007e5760601b6bffffffffffffffffffffffff19165f5b601481106103c757505060a4355f5b6020811061038f57602486868660848780604051968796631e8e1e1360e01b8852604060048901528260448901520160648701375f60648287010152601f801991011684016064810192600319606487840301016024870152518093520191905f5b8181106103765784602081808703817f000000000000000000000000c989429b6c3f3e870fdb825735c00d2f4ebcd5596001600160a01b03165afa90811561036b575f91610330575b501561032157602060405160018152f35b631ac2386360e31b5f5260045ffd5b90506020813d602011610363575b8161034b60209383610491565b8101031261007e5751801515810361007e5781610310565b3d915061033e565b6040513d5f823e3d90fd5b82518452859450602093840193909201916001016102c7565b8060370190816037116103b3576001916103ac84831a91876104c7565b5201610265565b634e487b7160e01b5f52601160045260245ffd5b8060230190816023116103b3576001916103e484831a91876104c7565b5201610256565b634e487b7160e01b5f52603260045260245ffd5b8060010190816001116103b35760019161041c84831a91876104c7565b52016101ff565b90506020813d60201161044d575b8161043e60209383610491565b8101031261007e5751836101ac565b3d9150610431565b60243567ffffffffffffffff8116810361007e5790565b156104745750565b67ffffffffffffffff906328e89c8b60e01b5f521660045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176104b357604052565b634e487b7160e01b5f52604160045260245ffd5b80518210156103eb5760209160051b01019056fea26469706673582212205e31ce7a58695ed70aeb299ab7d44bf75540f4e30db65eee511a2105d2c9d03e64736f6c63430008210033