Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions queries/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[profile.default]
src = "src"
out = "out"
libs = ["dependencies"]

[fs_permissions]
read = ["./dependencies/sxt-proof-of-sql-contracts-0.2.1/config"]

[dependencies]
forge-std = "1.9.7"
"@openzeppelin-contracts" = "5.2.0"
sxt-proof-of-sql-contracts = { version = "0.2.1", url = "https://github.com/spaceandtimefdn/sxt-proof-of-sql-contracts/releases/download/v0.2.1/posql-contracts.zip"}
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
3 changes: 3 additions & 0 deletions queries/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.2.0/
forge-std/=dependencies/forge-std-1.9.7/
sxt-proof-of-sql-contracts/=dependencies/sxt-proof-of-sql-contracts-0.2.1/
35 changes: 35 additions & 0 deletions queries/script/deployPoSQLCats.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {Script} from "forge-std/src/Script.sol";
import {stdJson} from "forge-std/src/StdJson.sol";
import {PoSQLCats} from "../src/PoSQLCats.sol";

/// @title Deploy PoSQLCats
/// @notice Reads per-chain payout from config/examples.addresses.json (or ENV override) and deploys.
///
/// ## How to Run
/// `forge script script/deployPoSQLCats.s.sol:DeployPoSQLCats --broadcast --rpc-url=$ETH_RPC_URL --private-key=$PRIVATE_KEY --verify -vvvvv`
contract DeployPoSQLCats is Script {
using stdJson for string;

function run() public {
// pick chain id from the RPC you pass to forge, or override via env
uint256 chainId = block.chainid;

string memory path = string.concat(vm.projectRoot(), "/config/examples.addresses.json");
/// forge-lint: disable-start(unsafe-cheatcode)
string memory json = vm.readFile(path);

// Build JSON pointer like ".11155111.QUERY_ROUTER_ADDRESS"
string memory base = string.concat(".", vm.toString(chainId));
address queryRouter = json.readAddress(string.concat(base, ".QUERY_ROUTER_ADDRESS"));
bytes32 version = json.readBytes32(string.concat(base, ".PROOF_OF_SQL_VERSION_HASH"));
address sxt = json.readAddress(string.concat(base, ".SXT_TOKEN_CONTRACT_ADDRESS"));
uint248 amount = uint248(uint256(json.readUint(string.concat(base, ".PAYMENT_AMOUNT"))));

vm.startBroadcast(); // uses PRIVATE_KEY from env or CLI
new PoSQLCats(queryRouter, version, sxt, amount);
vm.stopBroadcast();
}
}
35 changes: 35 additions & 0 deletions queries/script/deployPoSQLHelloWorld.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {Script} from "forge-std/src/Script.sol";
import {stdJson} from "forge-std/src/StdJson.sol";
import {PoSQLHelloWorld} from "../src/PoSQLHelloWorld.sol";

/// @title Deploy PoSQLHelloWorld
/// @notice Reads per-chain payout from config/examples.addresses.json (or ENV override) and deploys.
///
/// ## How to Run
/// `forge script script/deployPoSQLHelloWorld.s.sol:DeployPoSQLHelloWorld --broadcast --rpc-url=$ETH_RPC_URL --private-key=$PRIVATE_KEY --verify -vvvvv`
contract DeployPoSQLHelloWorld is Script {
using stdJson for string;

function run() public {
// pick chain id from the RPC you pass to forge, or override via env
uint256 chainId = block.chainid;

string memory path = string.concat(vm.projectRoot(), "/config/examples.addresses.json");
/// forge-lint: disable-start(unsafe-cheatcode)
string memory json = vm.readFile(path);

// Build JSON pointer like ".11155111.QUERY_ROUTER_ADDRESS"
string memory base = string.concat(".", vm.toString(chainId));
address queryRouter = json.readAddress(string.concat(base, ".QUERY_ROUTER_ADDRESS"));
bytes32 version = json.readBytes32(string.concat(base, ".PROOF_OF_SQL_VERSION_HASH"));
address sxt = json.readAddress(string.concat(base, ".SXT_TOKEN_CONTRACT_ADDRESS"));
uint248 amount = uint248(uint256(json.readUint(string.concat(base, ".PAYMENT_AMOUNT"))));

vm.startBroadcast(); // uses PRIVATE_KEY from env or CLI
new PoSQLHelloWorld(queryRouter, version, sxt, amount);
vm.stopBroadcast();
}
}
35 changes: 35 additions & 0 deletions queries/script/deployPoSQLTimeStampWithInequality.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {Script} from "forge-std/src/Script.sol";
import {stdJson} from "forge-std/src/StdJson.sol";
import {PoSQLTimeStampWithInequality} from "../src/PoSQLTimeStampWithInequality.sol";

/// @title Deploy PoSQLHelloWorld
/// @notice Reads per-chain payout from config/examples.addresses.json (or ENV override) and deploys.
///
/// ## How to Run
/// `forge script script/deployPoSQLTimeStampWithInequality.s.sol:DeployPoSQLTimeStampWithInequality --broadcast --rpc-url=$ETH_RPC_URL --private-key=$PRIVATE_KEY --verify -vvvvv`
contract DeployPoSQLTimeStampWithInequality is Script {
using stdJson for string;

function run() public {
// pick chain id from the RPC you pass to forge, or override via env
uint256 chainId = block.chainid;

string memory path = string.concat(vm.projectRoot(), "/config/examples.addresses.json");
/// forge-lint: disable-start(unsafe-cheatcode)
string memory json = vm.readFile(path);

// Build JSON pointer like ".11155111.QUERY_ROUTER_ADDRESS"
string memory base = string.concat(".", vm.toString(chainId));
address queryRouter = json.readAddress(string.concat(base, ".QUERY_ROUTER_ADDRESS"));
bytes32 version = json.readBytes32(string.concat(base, ".PROOF_OF_SQL_VERSION_HASH"));
address sxt = json.readAddress(string.concat(base, ".SXT_TOKEN_CONTRACT_ADDRESS"));
uint248 amount = uint248(uint256(json.readUint(string.concat(base, ".PAYMENT_AMOUNT"))));

vm.startBroadcast(); // uses PRIVATE_KEY from env or CLI
new PoSQLTimeStampWithInequality(queryRouter, version, sxt, amount);
vm.stopBroadcast();
}
}
97 changes: 97 additions & 0 deletions queries/src/PoSQLCats.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ParamsBuilder, ProofOfSqlTable} from "sxt-proof-of-sql-contracts/src/PoSQL.sol";
import {IQueryCallback} from "sxt-proof-of-sql-contracts/src/IQueryCallback.sol";
import {IQueryRouter} from "sxt-proof-of-sql-contracts/src/query-router/interfaces/IQueryRouter.sol";

/// @title PoSQLCats
/// @notice Example contract demonstrating a Proof of SQL query with parameterized filters.
/// @dev This contract uses QueryRouter to pay for and execute the query.
/// the query is a SQL query that returns cats filtered by age and gender.
/// note: make sure that the caller holds SXT enough to cover the query payment. Also this is a simple
/// example and does not include zero address checks and other security checks.

/// @dev to deploy this contract, use the DeployPoSQLCats script
contract PoSQLCats is IQueryCallback {
using SafeERC20 for IERC20;

/// @notice QueryRouter contract address
address public immutable QUERY_ROUTER;
/// @notice Proof of SQL version hash
bytes32 public immutable VERSION;
/// @notice SXT token address
address public immutable SXT;
/// @notice The ammount of SXT to pay for the query
uint256 public immutable PAYMENT_AMOUNT;

uint256 public constant MAX_GAS_PRICE = 1e6;
uint64 public constant GAS_LIMIT = 1e6;

/// @dev hex-serialized SQL query plan:
/// select * from CATS_C0A95B8A7EBCAFF89436492A205E98D2E3277B37.CATS WHERE age > $1 and is_female = $2
bytes public constant QUERY_PLAN =
hex"00000000000000010000000000000032434154535f433041393542384137454243414646383934333634393241323035453938443245333237374233372e434154530000000000000004000000000000000000000000000000044e414d450000000700000000000000000000000000000003414745000000050000000000000000000000000000000949535f46454d414c45000000000000000000000000000000000000000e4d4554415f5355424d49545445520000000b000000000000000400000000000000044e414d450000000000000003414745000000000000000949535f46454d414c45000000000000000e4d4554415f5355424d4954544552000000000000000000000000000000060000000a0000000000000000000000010000000b00000000000000000000000500000000020000000000000000000000020000000b0000000000000001000000000000000000000004000000000000000000000000000000000000000000000001000000000000000000000002000000000000000000000003";

constructor(address queryRouter, bytes32 version, address sxt, uint248 amount) {
QUERY_ROUTER = queryRouter;
VERSION = version;
SXT = sxt;
PAYMENT_AMOUNT = amount;
}

/// @notice Pay for and execute a query to get cats by age and gender.
/// @param minAge The minimum age for cats to return (age > minAge)
/// @param isFemale Whether to filter for female cats
function query(int64 minAge, bool isFemale) external {
// 0. pull SXT from the caller (must have approved this contract beforehand)
IERC20(SXT).safeTransferFrom(msg.sender, address(this), PAYMENT_AMOUNT);

// 1. approve payment to be spent by QueryRouter, that will cover PoSQLVerifier's fees and fulfillment callback gas.
// Note: make sure that the caller address holds at least `PAYMENT_AMOUNT` of SXT.
IERC20(SXT).forceApprove(QUERY_ROUTER, PAYMENT_AMOUNT); // use forceApprove to handle nonzero allowances

// 2. Assemble the SQL parameters using the `ParamsBuilder` library
bytes memory ageParam = ParamsBuilder.bigIntParam(minAge);
bytes memory genderParam = ParamsBuilder.boolParam(isFemale);
bytes[] memory queryParameters = new bytes[](2);
queryParameters[0] = ageParam;
queryParameters[1] = genderParam;
bytes memory serializedParams = ParamsBuilder.serializeParamArray(queryParameters);

// 3. Assemble the request needed to run the query
IQueryRouter.Query memory queryToRequest = IQueryRouter.Query({
version: VERSION, innerQuery: QUERY_PLAN, parameters: serializedParams, metadata: hex""
});

// 4. Assemble the information needed to call the callback
IQueryRouter.Callback memory callback = IQueryRouter.Callback({
maxGasPrice: MAX_GAS_PRICE,
gasLimit: GAS_LIMIT,
callbackContract: address(this),
selector: IQueryCallback.queryCallback.selector,
callbackData: ""
});

// 5. Execute the query.
IQueryRouter(QUERY_ROUTER) // aderyn-ignore unchecked-return
.requestQuery(queryToRequest, callback, PAYMENT_AMOUNT, uint64(block.timestamp + 1 hours));
}

/// @notice Example event emitting the query result.
event QueryFulfilled(uint256 catCount);

/// @inheritdoc IQueryCallback
/// @notice Handle the query result.
/// @dev This will be called once the query has been executed and verified on-chain.
function queryCallback(bytes32, bytes calldata queryResult, bytes calldata) external {
// Use the `ProofOfSqlTable` to deserialize and read data from the result
(, ProofOfSqlTable.Table memory tableResult) = ProofOfSqlTable.__deserializeFromBytes(queryResult);
// The query returns 4 columns: NAME, AGE, IS_FEMALE, META_SUBMITTER
uint256 catCount = ProofOfSqlTable.readVarCharColumn(tableResult, 0).length;
// Emit the count.
emit QueryFulfilled(catCount);
}
}
93 changes: 93 additions & 0 deletions queries/src/PoSQLHelloWorld.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ParamsBuilder, ProofOfSqlTable} from "sxt-proof-of-sql-contracts/src/PoSQL.sol";
import {IQueryCallback} from "sxt-proof-of-sql-contracts/src/IQueryCallback.sol";
import {IQueryRouter} from "sxt-proof-of-sql-contracts/src/query-router/interfaces/IQueryRouter.sol";

/// @title PoSQLHelloWorld
/// @notice Example "Hello World"-style contract of a Proof of SQL query.
/// @dev This contract uses QueryRouter to pay for and execute the query.
/// the query is a simple SQL query that returns the number of ethereum contracts created by msg.sender.
/// note: make sure that this contract holds SXT enough to cover the query payment. Also this is a simple
/// example and does not include zero address checks and other security checks.

/// @dev to deploy this contract, use the DeployPoSQLHelloWorld script:
/// forge script script/deployPoSQLHelloWorld.s.sol:DeployPoSQLHelloWorld --broadcast --rpc-url=$ETH_RPC_URL --private-key=$PRIVATE_KEY --verify -vvvvv
contract PoSQLHelloWorld is IQueryCallback {
using SafeERC20 for IERC20;

/// @notice QueryRouter contract address
address public immutable QUERY_ROUTER;
/// @notice Proof of SQL version hash
bytes32 public immutable VERSION;
/// @notice SXT token address
address public immutable SXT;
/// @notice The ammount of SXT to pay for the query
uint256 public immutable PAYMENT_AMOUNT;

uint256 public constant MAX_GAS_PRICE = 1e6;
uint64 public constant GAS_LIMIT = 1e6;

/// @dev hex-serialized SQL query plan:
/// SELECT BLOCK_NUMBER FROM ETHEREUM.CONTRACTS WHERE CONTRACT_CREATOR_ADDRESS=$1
bytes public constant QUERY_PLAN =
hex"00000000000000010000000000000012455448455245554d2e434f4e54524143545300000000000000020000000000000000000000000000000c424c4f434b5f4e554d4245520000000500000000000000000000000000000018434f4e54524143545f43524541544f525f414444524553530000000b0000000000000001000000000000000c424c4f434b5f4e554d424552000000000000000000000000000000020000000000000000000000010000000b00000000000000000000000b0000000000000001000000000000000000000000";

constructor(address queryRouter, bytes32 version, address sxt, uint248 amount) {
QUERY_ROUTER = queryRouter;
VERSION = version;
SXT = sxt;
PAYMENT_AMOUNT = amount;
}

/// @notice Pay for and execute a "Hello World"-style query.
function query() external {
// 0. pull SXT from the caller (must have approved this contract beforehand)
IERC20(SXT).safeTransferFrom(msg.sender, address(this), PAYMENT_AMOUNT);

// 1. approve payment to be spent by QueryRouter, that will cover PoSQLVerifier's fees and fulfillment callback gas.
// Note: make sure that the caller address holds at least `PAYMENT_AMOUNT` of SXT.
IERC20(SXT).forceApprove(QUERY_ROUTER, PAYMENT_AMOUNT); // use forceApprove to handle nonzero allowances

// 2. Assemble the SQL parameters using the `ParamsBuilder` library
bytes memory param = ParamsBuilder.varBinaryParam(abi.encodePacked(msg.sender));
bytes[] memory queryParameters = new bytes[](1);
queryParameters[0] = param;
bytes memory serializedParams = ParamsBuilder.serializeParamArray(queryParameters);

// 3. Assemble the request needed to run the query
IQueryRouter.Query memory queryToRequest = IQueryRouter.Query({
version: VERSION, innerQuery: QUERY_PLAN, parameters: serializedParams, metadata: hex""
});

// 4. Assemble the information needed to call the callback
IQueryRouter.Callback memory callback = IQueryRouter.Callback({
maxGasPrice: MAX_GAS_PRICE,
gasLimit: GAS_LIMIT,
callbackContract: address(this),
selector: IQueryCallback.queryCallback.selector,
callbackData: ""
});

// 5. Execute the query.
IQueryRouter(QUERY_ROUTER) // aderyn-ignore unchecked-return
.requestQuery(queryToRequest, callback, PAYMENT_AMOUNT, uint64(block.timestamp + 1 hours));
}

/// @notice Example event emitting the query result.
event QueryFulfilled(uint256 contractCount);

/// @inheritdoc IQueryCallback
/// @notice Handle the query result.
/// @dev This will be called once the query has been executed and verified on-chain.
function queryCallback(bytes32, bytes calldata queryResult, bytes calldata) external {
// Use the `ProofOfSqlTable` to deserialize and read data from the result
(, ProofOfSqlTable.Table memory tableResult) = ProofOfSqlTable.__deserializeFromBytes(queryResult);
uint256 contractCount = ProofOfSqlTable.readBigIntColumn(tableResult, 0).length;
// Emit the count.
emit QueryFulfilled(contractCount);
}
}
Loading