From af3abfb1999c4a85ce4f42b9660b298371cdc997 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Tue, 28 May 2024 14:27:05 -0400 Subject: [PATCH] feat: enable usage of chains without pre-existing deployments in axiom-std --- build/axiom-std-cli-build.js | 18 +++++- cli/prove.ts | 18 +++++- env.example | 3 +- foundry.toml | 1 + src/AxiomCli.sol | 2 +- test/HashiCrosschain.t.sol | 114 +++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 test/HashiCrosschain.t.sol diff --git a/build/axiom-std-cli-build.js b/build/axiom-std-cli-build.js index 813a679..2f41988 100644 --- a/build/axiom-std-cli-build.js +++ b/build/axiom-std-cli-build.js @@ -255,7 +255,11 @@ var require_prove = __commonJS({ if (!targetChainId) { throw new Error("`targetChainId` is required for blockhash oracle bridge type"); } - axiomV2QueryAddress = (0, address_1.getAxiomV2QueryBlockhashOracleAddress)(sourceChainId, targetChainId); + if (sourceChainId == "1" && targetChainId == "8453") { + axiomV2QueryAddress = (0, address_1.getAxiomV2QueryBlockhashOracleAddress)(sourceChainId, targetChainId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } else if (broadcaster) { if (!targetChainId) { throw new Error("`targetChainId` is required for broadcaster bridge type"); @@ -263,9 +267,17 @@ var require_prove = __commonJS({ if (!bridgeId) { throw new Error("`bridgeId` is required for broadcaster bridge type"); } - axiomV2QueryAddress = (0, address_1.getAxiomV2QueryBroadcasterAddress)(sourceChainId, targetChainId, bridgeId); + if (sourceChainId == "1" && targetChainId == "8453") { + axiomV2QueryAddress = (0, address_1.getAxiomV2QueryBroadcasterAddress)(sourceChainId, targetChainId, bridgeId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } else { - axiomV2QueryAddress = (0, address_1.getAxiomV2QueryAddress)(sourceChainId); + if (sourceChainId in ["1", "11155111", "8453", "84532"]) { + axiomV2QueryAddress = (0, address_1.getAxiomV2QueryAddress)(sourceChainId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } let target; if (blockhashOracle || broadcaster) { diff --git a/cli/prove.ts b/cli/prove.ts index 624421e..91f69db 100644 --- a/cli/prove.ts +++ b/cli/prove.ts @@ -71,7 +71,11 @@ export const prove = async ( if (!targetChainId) { throw new Error("`targetChainId` is required for blockhash oracle bridge type"); } - axiomV2QueryAddress = getAxiomV2QueryBlockhashOracleAddress(sourceChainId, targetChainId); + if (sourceChainId == "1" && targetChainId == "8453") { + axiomV2QueryAddress = getAxiomV2QueryBlockhashOracleAddress(sourceChainId, targetChainId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } else if (broadcaster) { if (!targetChainId) { throw new Error("`targetChainId` is required for broadcaster bridge type"); @@ -79,9 +83,17 @@ export const prove = async ( if (!bridgeId) { throw new Error("`bridgeId` is required for broadcaster bridge type"); } - axiomV2QueryAddress = getAxiomV2QueryBroadcasterAddress(sourceChainId, targetChainId, bridgeId); + if (sourceChainId == "1" && targetChainId == "8453") { + axiomV2QueryAddress = getAxiomV2QueryBroadcasterAddress(sourceChainId, targetChainId, bridgeId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } else { - axiomV2QueryAddress = getAxiomV2QueryAddress(sourceChainId); + if (sourceChainId in ["1", "11155111", "8453", "84532"]) { + axiomV2QueryAddress = getAxiomV2QueryAddress(sourceChainId); + } else { + axiomV2QueryAddress = "0xdEaDBEefDeaDbEefDeAdbeefDeAdbEEfAAaaAAaA"; + } } let target; diff --git a/env.example b/env.example index 5b257a4..7a1a45e 100644 --- a/env.example +++ b/env.example @@ -2,4 +2,5 @@ PROVIDER_URI_SEPOLIA= PROVIDER_URI_MAINNET= PROVIDER_URI_BASE_SEPOLIA= -PROVIDER_URI_BASE= \ No newline at end of file +PROVIDER_URI_BASE= +PROVIDER_URI_GNOSIS= \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 9357b7b..9869147 100644 --- a/foundry.toml +++ b/foundry.toml @@ -11,6 +11,7 @@ sepolia = "${PROVIDER_URI_SEPOLIA}" mainnet = "${PROVIDER_URI_MAINNET}" base-sepolia = "${PROVIDER_URI_BASE_SEPOLIA}" base = "${PROVIDER_URI_BASE}" +gnosis = "${PROVIDER_URI_GNOSIS}" [fmt] bracket_spacing = true diff --git a/src/AxiomCli.sol b/src/AxiomCli.sol index c6d12d9..cb2a546 100644 --- a/src/AxiomCli.sol +++ b/src/AxiomCli.sol @@ -3,5 +3,5 @@ pragma solidity ^0.8.0; library AxiomCli { /// @dev The SHA256 hash of the Axiom CLI binary - bytes public constant CLI_SHASUM = hex"58b095c3ba0363609d2106a126b4a12afab6ca5ce60691602c7fdaac9aa14e00"; + bytes public constant CLI_SHASUM = hex"98c0d3d033ec4510144a338c7d35dfd639a1e000a27bc0b61464a628f6ab320e"; } diff --git a/test/HashiCrosschain.t.sol b/test/HashiCrosschain.t.sol new file mode 100644 index 0000000..3534159 --- /dev/null +++ b/test/HashiCrosschain.t.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "../src/AxiomTest.sol"; +import { AxiomV2Client } from "@axiom-crypto/v2-periphery/client/AxiomV2Client.sol"; + +contract MockClient is AxiomV2Client { + /// @dev The chain ID of the chain whose data the callback is expected to be called from. + uint64 public callbackSourceChainId; + + /// @dev The query schema of the query the client is expecting. + bytes32 public immutable QUERY_SCHEMA; + + /// @dev Error returned if the `sourceChainId` does not match. + error SourceChainIdDoesNotMatch(); + + /// @dev Error returned if the `querySchema` does not match. + error QuerySchemaDoesNotMatch(); + + /// @notice Emitted after callback is made. + /// @param queryId The unique ID identifying the query. + /// @param axiomResults The results of the query. + /// @param extraData Additional data passed to the callback. + event MockClientEvent(uint256 indexed queryId, bytes32[] axiomResults, bytes extraData); + + /// @notice Construct a new MockClient contract. + /// @param _axiomV2QueryAddress The address of the AxiomV2Query contract. + /// @param _callbackSourceChainId The ID of the chain the query reads from. + /// @param querySchema The query schema the client is expecting. + constructor(address _axiomV2QueryAddress, uint64 _callbackSourceChainId, bytes32 querySchema) + AxiomV2Client(_axiomV2QueryAddress) + { + callbackSourceChainId = _callbackSourceChainId; + QUERY_SCHEMA = querySchema; + } + + /// @inheritdoc AxiomV2Client + function _validateAxiomV2Call( + AxiomCallbackType callbackType, + uint64 sourceChainId, + address caller, + bytes32 querySchema, + uint256 queryId, + bytes calldata extraData + ) internal override { + if (sourceChainId != callbackSourceChainId) { + revert SourceChainIdDoesNotMatch(); + } + + if (querySchema != QUERY_SCHEMA) { + revert QuerySchemaDoesNotMatch(); + } + } + + /// @inheritdoc AxiomV2Client + function _axiomV2Callback( + uint64 sourceChainId, + address caller, + bytes32 querySchema, + uint256 queryId, + bytes32[] calldata axiomResults, + bytes calldata extraData + ) internal override { + emit MockClientEvent(queryId, axiomResults, extraData); + } +} + +contract GnosisHashiTest is AxiomTest { + using Axiom for Query; + + struct AxiomInput { + uint64[] blockNumbers; + uint256[] slots; + address _address; + } + + AxiomInput public input; + bytes32 public querySchema; + + MockClient public client; + + function setUp() public { + _createSelectForkAndSetupAxiomCrosschain("gnosis", "mainnet", 3_500_000, 1, true, 0); + + uint64[] memory blockNumbers = new uint64[](3); + uint256[] memory slots = new uint256[](3); + for (uint256 i = 0; i < 3; i++) { + blockNumbers[i] = 4_205_938; + slots[i] = i; + } + input = AxiomInput({ + blockNumbers: blockNumbers, + slots: slots, + _address: address(0x8018fe32fCFd3d166E8b4c4E37105318A84BA11b) + }); + + querySchema = axiomVm.readCircuit("test/circuit/array.circuit.ts"); + client = new MockClient(axiomV2QueryAddress, MAINNET_CHAIN_ID, querySchema); + } + + /// @dev Simple demonstration of testing an Axiom client contract using Axiom cheatcodes + function test_hashi_crosschain() public { + // create a query into Axiom with default parameters + Query memory q = query(querySchema, abi.encode(input), address(client)); + + // send the query to Axiom + q.send(); + + // prank fulfillment of the query, returning the Axiom results + bytes32[] memory results = q.prankFulfill(); + + bytes32[] memory expectedResults = q.peekResults(); + } +}