Description:
Smart contract deployed on Ethereum with Factory, Oracle features.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}
abstract contract ReentrancyGuard {
using StorageSlot for bytes32;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/**
* @dev A `view` only version of {nonReentrant}. Use to block view functions
* from being called, preventing reading from inconsistent contract state.
*
* CAUTION: This is a "view" modifier and does not change the reentrancy
* status. Use it only on view functions. For payable or non-payable functions,
* use the standard {nonReentrant} modifier instead.
*/
modifier nonReentrantView() {
_nonReentrantBeforeView();
_;
}
function _nonReentrantBeforeView() private view {
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
_nonReentrantBeforeView();
// Any calls to nonReentrant after this point will fail
_reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
}
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
return REENTRANCY_GUARD_STORAGE;
}
}
/**
* @title ShadowOracle
* @dev Shielded oracle queries: Agents commit encrypted payloads off-chain via Shadow SDK.
* Responses decrypted off-chain in TEE swarm, on-chain emits for fulfillment.
* Agent-first: Permissionless, lightweight—queries routed without exposure.
* Ties to ShadowSettlement: Paid data for x402; to ZKRegistry: Proved capabilities for custom oracles.
*/
interface IFeeGateway {
function payAndPass(address user, bytes32 nonce) external payable;
}
contract ShadowOracle is ReentrancyGuard, Ownable {
IFeeGateway public immutable FEE_GATEWAY = IFeeGateway(0x08Db140854AAb90463f6B61eB6F346C29BFB02EA);
// Supported oracles (owner-set, agents can propose via events)
mapping(uint8 => address) public oracleNetworks; // 0: Chainlink, 1: UMA, 2: PYTH
// Query commitments: agent => commitment => pending
mapping(address => mapping(bytes32 => bool)) public queryCommitments;
// Nullifiers for fulfilled queries (prevents replays)
mapping(bytes32 => bool) public nullifiers;
// Latest batch merkle root from Swarm
bytes32 public currentRoot;
// Timelock for root updates (5 mins = 300s cooldown per caller)
mapping(address => uint256) public lastUpdateTime;
event ShieldedQuery(address indexed agent, bytes32 indexed commitment, uint8 oracleType, bytes32 querySpecHash);
event QueryFulfilled(uint256 indexed requestId, address indexed agent, bytes32 nullifier, bytes32 responseHash);
event FeePaidAndPassed(bytes32 nonce);
event NullifierSpent(bytes32 indexed nullifier, address indexed spender);
event RootUpdated(bytes32 indexed newRoot, address indexed updater);
uint256 public constant UPDATE_COOLDOWN = 300; // 5 minutes
constructor() Ownable(msg.sender) {
// Defaults: Set Chainlink in setters if needed
}
/**
* @dev Submit shielded query: Commitment conceals payload (e.g. "BTC price?").
* Off-chain: SDK: commitment = Poseidon(encryptedPayload + vrfSeed + oracleType + oracleFeedAddr + secret).
* On-chain: Store commitment, emit event for Shadow Swarm to route/decrypt/fulfill (e.g. Chainlink.latestRoundData on feedAddr).
* VRF: Agent-seeded off-chain.
* @param oracleType Oracle ID (0: Chainlink, 1: UMA, 2: Pyth).
* @param oracleFeedAddr Specific feed contract (e.g. Chainlink ETH/USD aggregator).
* @param commitment Off-chain hash.
* @param nonce Fee pass.
*/
function shieldedQuery(
uint8 oracleType,
address oracleFeedAddr,
bytes32 commitment,
bytes32 nonce
) external payable nonReentrant {
// Pay fee
FEE_GATEWAY.payAndPass{value: msg.value}(msg.sender, nonce);
require(oracleFeedAddr != address(0), "Invalid feed address");
// Store commitment
require(!queryCommitments[msg.sender][commitment], "Commitment spent");
queryCommitments[msg.sender][commitment] = true;
// Emit for off-chain routing (API/Swarm decrypts payload, routes to appropriate oracle feed)
emit ShieldedQuery(msg.sender, commitment, oracleType, keccak256(abi.encodePacked(commitment, oracleType, oracleFeedAddr)));
emit FeePaidAndPassed(nonce);
}
// Merkle verify helper (gas-optimized, no lib needed)
function merkleVerify(bytes32 leaf, bytes32[] calldata proof, bytes32 root) internal pure returns (bool) {
bytes32 computed = leaf;
for (uint i = 0; i < proof.length; i++) {
computed = keccak256(abi.encodePacked(computed, proof[i]));
}
return computed == root;
}
/**
* @dev Swarm updates batch root (permissionless but rate-limited via timelock).
* @param newRoot Merkle root of fulfills.
*/
function updateRoot(bytes32 newRoot) external {
require(block.timestamp >= lastUpdateTime[msg.sender] + UPDATE_COOLDOWN, "Update cooldown active");
lastUpdateTime[msg.sender] = block.timestamp;
currentRoot = newRoot;
emit RootUpdated(newRoot, msg.sender);
}
/**
* @dev Fulfill query: Verifies Merkle inclusion for trustless batch auth.
* Off-chain: Swarm includes proof from tree.
* @param requestId Oracle ID.
* @param agent Querier.
* @param nullifier Off-chain hash.
* @param responseHash Decrypted response.
* @param proof Merkle proof array (siblings).
*/
function fulfillQuery(
uint256 requestId,
address agent,
bytes32 nullifier,
bytes32 responseHash,
bytes32[] calldata proof
) external {
// Merkle verify (anyone call if proof valid)
bytes32 leaf = keccak256(abi.encodePacked(requestId, agent, responseHash, block.timestamp));
require(merkleVerify(leaf, proof, currentRoot), "Invalid Merkle proof");
require(!nullifiers[nullifier], "Nullifier spent");
nullifiers[nullifier] = true;
emit QueryFulfilled(requestId, agent, nullifier, responseHash);
emit NullifierSpent(nullifier, msg.sender);
}
/**
* @dev Spend nullifier for query (agent confirms post-decrypt).
* @param nullifier Off-chain hash.
*/
function spendNullifier(bytes32 nullifier) external {
require(!nullifiers[nullifier], "Nullifier spent");
nullifiers[nullifier] = true;
emit NullifierSpent(nullifier, msg.sender);
}
/**
* @dev Owner sets oracle network (e.g. Chainlink default price feed per chain).
* @param oracleType: 0 = Chainlink, 1 = Umbra, 2 = Pyth
* Agents can override feed addresses in shieldedQuery so long as they provide the appropriate oracleType param.
* Off-chain listeners route via oracleType.
*/
function setOracleNetwork(uint8 oracleType, address oracleAddr) external onlyOwner {
oracleNetworks[oracleType] = oracleAddr;
}
receive() external payable {}
}
Submitted on: 2025-10-30 18:57:58
Comments
Log in to comment.
No comments yet.