-
Notifications
You must be signed in to change notification settings - Fork 0
Docs/solver integration #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0c54683
docs: add fetch and verify proof example script
aharvet 5e8a311
docs: document solver integration
aharvet 149c0da
Update docs/7683/solver-integration.md
aharvet 5a5ad0e
Update docs/7683/solver-integration.md
aharvet 72bf531
Update solver-integration.md
aharvet 69fab55
Update docs/7683/solver-integration.md
aharvet 17a1c0e
Update docs/7683/solver-integration.md
aharvet ab35794
Update docs/7683/solver-integration.md
aharvet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| # t1 intents protocol: solver integration | ||
|
|
||
| Our intent protocol implements the ERC 7683 specification. | ||
|
|
||
| The workflow is the same for intents opening, filling and settlement. | ||
|
|
||
|
|
||
| There is only a difference in the process for settlement of solvers on origin chain. | ||
|
|
||
| Solvers need to require a so called Proof of Read, and then use a merkle proof to get repaid. | ||
|
|
||
| ## Workflow | ||
|
|
||
| Here is the full workflow from intent open to settlement (t1 specific steps with 🔴). | ||
|
|
||
| The settler contract implementation is `T1ERC7683.sol`. | ||
|
|
||
| 1 - Intent is opened on source chain settler contract whether on chain by user or gasslessly by solver | ||
|
|
||
| 2 - Solver listens for open event on source chain settler contract and fill on destination chain settler contract | ||
|
|
||
| 🔴 3 - Solver calls a verify function on source chain settler contract | ||
|
|
||
| 🔴 4 - Solver listens for new Proof of Read commitment event on source chain XChainReader contract | ||
|
|
||
| 🔴 5 - Solver calls t1 offchain API to get the merkle proof data | ||
|
|
||
| 🔴 6 - Solver calls settlement function on source chain settler contract and get repaid | ||
|
|
||
| ## Implementation | ||
|
|
||
| Here is how you integrate from step 3. | ||
|
|
||
| ### 3 - Solver call a verify function on source chain settler contract | ||
|
|
||
| Location: source chain | ||
|
|
||
| Contract: T1ERC7683 (see deployment file for address and ABI) | ||
|
|
||
| Timing: should be called once intent is filled on destination chain | ||
|
|
||
| ```solidity | ||
| /// @param destinationDomain The chain id of the destination chain | ||
| /// @param gasLimit The gas limit for the read operation | ||
| /// @param orderId The ID of the order to verify fillment | ||
| /// @return requestId The ID of the read request | ||
| function verifySettlement( | ||
| uint32 destinationDomain, | ||
| uint256 gasLimit, | ||
| bytes32 orderId | ||
| ) | ||
| payable | ||
| returns (bytes32 requestId); | ||
| ``` | ||
|
|
||
| ### 4 - Solver listen for new Proof of Read commitment event on source chain XChainReader contract | ||
|
|
||
| Location: source chain | ||
|
|
||
| Contract: T1XChainReader (see deployment file for address and ABI) | ||
|
|
||
| Timing: should be listened to once `verifySettlement` has been called | ||
|
|
||
| ```solidity | ||
| /// @param batchIndex The index where the Proof of Read batch has been committed | ||
| event ProofOfReadRootCommitted(uint256 batchIndex); | ||
| ``` | ||
|
|
||
| ### 5 - Solver call t1 offchain API to get the merkle proof data | ||
|
|
||
| Location: offchain | ||
|
|
||
| Timing: once a new `ProofOfReadRootCommitted` event has been emitted | ||
|
|
||
| Base URL: `https://api.v05.t1protocol.com` | ||
|
|
||
| Endpoint: `/api/read-proofs` | ||
|
|
||
| Method: `GET` | ||
|
|
||
| **Query Parameters:** | ||
| | Parameter | Type | Optional | Description | Value suggested | | ||
| |-----------|------|----------|-------------|---------| | ||
| | `address` | string | No | Address that called `verifySettlement` | - | | ||
| | `direction` | string | Yes | Direction of the read: `"L1_TO_L2"` \| `"L2_TO_L1"` | - | | ||
| | `page` | number | Yes | Page number to fetch | `1` | | ||
| | `page_size` | number | Yes | Items per page | `100` | | ||
|
|
||
| **Example Request:** | ||
| ```bash | ||
| curl "https://api.v05.t1protocol.com/api/read-proofs?address=0x123...&direction=L1_TO_L2&page=1&page_size=100" | ||
| ``` | ||
|
|
||
| The HTTP call will return the following structure as a response : | ||
|
|
||
| ```typescript | ||
| interface ReadProofsResponse { | ||
| results: Result[]; | ||
| page: number; | ||
| page_size: number; | ||
| total: number; | ||
| } | ||
|
|
||
| interface Result { | ||
| source_tx_hash: string; | ||
| message_type: number; | ||
| block_number: number; | ||
| message_hash: string; | ||
| tx_sender: string; | ||
| direction: Direction; | ||
| claim_info: ClaimInfo; | ||
| } | ||
|
|
||
| enum Direction { | ||
| L1_TO_L2 = 'L1_TO_L2', | ||
| L2_TO_L1 = 'L2_TO_L1', | ||
| } | ||
|
|
||
| interface ClaimInfo { | ||
| from: string; | ||
| to: string; | ||
| value: string; | ||
| nonce: string; | ||
| message: string; | ||
| x_chain_read_result: string; | ||
| request_id: string; | ||
| handle_read_result_with_proof_calldata: string; | ||
| proof: { | ||
| batch_index: string; | ||
| merkle_proof: string; | ||
| }; | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| Solver will then iterate on the `results` array and find the one that matches its request. | ||
|
|
||
| Solver can check values like `source_tx_hash` or `tx_sender` for that. | ||
|
|
||
| Solver will then get the value in `claim_info.handle_read_result_with_proof_calldata` from the matching result for next step. | ||
|
|
||
| See more: https://docs.t1protocol.com/api/xChainRead/overview | ||
|
|
||
| ### 6 - Solver call settlement function on source chain settler contract and get repaid | ||
|
|
||
| Location: source chain | ||
|
|
||
| Contract: T1ERC7683 (see deployment file for address and ABI) | ||
|
|
||
| Timing: should be called once new `ProofOfReadRootCommitted` event has been emitted and merkle proof has been fetched from API | ||
|
|
||
| ```solidity | ||
| /// @param encodedProofOfRead The encoded proof of read which is formatted as following: | ||
| /// abi.encode(uint256 batchIndex, bytes32 requestId, uint256 position, bytes result, bytes proof) | ||
| /// This is the value returned in previous step as `handle_read_result_with_proof_calldata` | ||
| function handleReadResultWithProof(bytes encodedProofOfRead); | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| /** | ||
| * t1 Protocol - Proof of Read Verification Example Script | ||
| * | ||
| * This script demonstrates how to fetch and verify cross-chain read proofs using the t1 Protocol. | ||
| * It retrieves Merkle proofs for cross-chain read operations from t1's API and verifies them on-chain via t1 XChainReader contract. | ||
| * | ||
| * Before running this script, ensure you have: | ||
| * 1. Node.js and npm installed | ||
| * 2. Installed dependencies (ethers.js) | ||
| * 3. A valid Ethereum address that has performed cross-chain reads through t1 Protocol | ||
| * | ||
| * Usage: node fetchAndVerifyProofOfRead <requester_address> <direction> | ||
| * - requester_address: The Ethereum address that initiated cross-chain reads | ||
| * - direction: Either "ARB_TO_BASE" or "BASE_TO_ARB" for the cross-chain direction | ||
| * | ||
| * Example: node fetchAndVerifyProofOfRead 0x1234567890123456789012345678901234567890 ARB_TO_BASE | ||
| */ | ||
|
|
||
| import { ethers } from 'ethers'; | ||
|
|
||
| enum Direction { | ||
| ARB_TO_BASE = 'L1_TO_L2', | ||
| BASE_TO_ARB = 'L2_TO_L1', | ||
| } | ||
|
|
||
| interface ClaimInfo { | ||
| from: string; | ||
| to: string; | ||
| value: string; | ||
| nonce: string; | ||
| message: string; | ||
| x_chain_read_result: string; | ||
| request_id: string; | ||
| handle_read_result_with_proof_calldata: string; | ||
| proof: { | ||
| batch_index: string; | ||
| merkle_proof: string; | ||
| }; | ||
| } | ||
|
|
||
| interface Result { | ||
| source_tx_hash: string; | ||
| message_type: number; | ||
| block_number: number; | ||
| message_hash: string; | ||
| tx_sender: string; | ||
| direction: Direction; | ||
| claim_info: ClaimInfo; | ||
| } | ||
|
|
||
| interface ReadProofsResponse { | ||
| results: Result[]; | ||
| page: number; | ||
| page_size: number; | ||
| total: number; | ||
| } | ||
|
|
||
| interface VerifiedProofReturn { | ||
| requestId: string; | ||
| result: string; | ||
| } | ||
|
|
||
| const XCHAINREADER_ABI = [ | ||
| 'function verifyProofOfRead(bytes encodedProofOfRead) external view returns (bytes32, bytes)', | ||
| ]; | ||
| const API_BASE_URL = 'https://api.v05.t1protocol.com/api'; | ||
| const ARB_RPC_URL = 'https://arbitrum-sepolia-rpc.publicnode.com'; | ||
| const BASE_RPC_URL = 'https://base-sepolia-rpc.publicnode.com'; | ||
| const ARB_XCHAINREADER_ADDRESS = '0x42d389A9007e446b92C0ce7bd8F42Ea10292881B'; | ||
| const BASE_XCHAINREADER_ADDRESS = '0x3821b214B4c9D053fa744dc2B355E2039696dFb7'; | ||
|
|
||
| async function getMerkleProofs( | ||
| address: string, | ||
| direction: Direction, | ||
| page: number = 1, | ||
| pageSize: number = 100, | ||
| ): Promise<ReadProofsResponse> { | ||
| const url = `${API_BASE_URL}/read-proofs?address=${address}&direction=${direction}&page=${page}&page_size=${pageSize}`; | ||
|
|
||
| console.log(`Fetching Merkle proofs from: ${url}`); | ||
|
|
||
| const response = await fetch(url); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error( | ||
| `Failed to fetch proofs: ${response.status} ${response.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| const { data } = await response.json(); | ||
| return data; | ||
| } | ||
|
|
||
| async function verifyProofOfRead( | ||
| direction: Direction, | ||
| proofCalldata: string, | ||
| ): Promise<VerifiedProofReturn | undefined> { | ||
| const provider = new ethers.JsonRpcProvider( | ||
| direction === Direction.ARB_TO_BASE ? ARB_RPC_URL : BASE_RPC_URL, | ||
| ); | ||
| const contract = new ethers.Contract( | ||
| direction === Direction.ARB_TO_BASE | ||
| ? ARB_XCHAINREADER_ADDRESS | ||
| : BASE_XCHAINREADER_ADDRESS, | ||
| XCHAINREADER_ABI, | ||
| provider, | ||
| ); | ||
|
|
||
| try { | ||
| const response = await contract.verifyProofOfRead(proofCalldata); | ||
| return { requestId: response[0], result: response[1] }; | ||
| } catch (error) { | ||
| console.error('Error verifying proof:', error); | ||
| return undefined; | ||
| } | ||
| } | ||
|
|
||
| async function main() { | ||
| const address = process.argv[2]; | ||
| const direction = process.argv[3]; | ||
|
|
||
| if ( | ||
| !address || | ||
| (direction !== 'ARB_TO_BASE' && direction !== 'BASE_TO_ARB') | ||
| ) { | ||
| console.error( | ||
| 'Usage: npm run dev <requester_address> <direction: "ARB_TO_BASE" | "BASE_TO_ARB">', | ||
| ); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const parsedDirection = | ||
| direction === 'ARB_TO_BASE' ? Direction.ARB_TO_BASE : Direction.BASE_TO_ARB; | ||
|
|
||
| try { | ||
| console.log(`Getting Merkle proofs for address: ${address}`); | ||
|
|
||
| const response = await getMerkleProofs(address, parsedDirection); | ||
|
|
||
| if (response.results.length === 0) { | ||
| console.log('No proofs found for this address.'); | ||
| return; | ||
| } | ||
|
|
||
| console.log(`Found ${response.results.length} proof(s)`); | ||
|
|
||
| for (let i = 0; i < response.results.length; i++) { | ||
| const item = response.results[i]; | ||
|
|
||
| console.log(`\nVerifying proof ${i + 1}/${response.results.length}:`); | ||
| console.log(`- Request ID: ${item.claim_info.request_id}`); | ||
| console.log(`- Source TX Hash: ${item.source_tx_hash}`); | ||
| console.log(`- Batch Index: ${item.claim_info.proof.batch_index}`); | ||
|
|
||
| const res = await verifyProofOfRead( | ||
| parsedDirection, | ||
| item.claim_info.handle_read_result_with_proof_calldata, | ||
| ); | ||
|
|
||
| if (!res) { | ||
| console.log('Proof verification FAILED'); | ||
| } else { | ||
| console.log('Proof verification SUCCESSFUL'); | ||
| } | ||
| } | ||
|
|
||
| console.log('\nProof verifications completed'); | ||
| } catch (error) { | ||
| console.error('Error:', error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
|
|
||
| main().catch(console.error); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.