From 7b27d0b1e7d96617a303432f051b78e5693ac97c Mon Sep 17 00:00:00 2001 From: Krzysztof Spisak-Spisacki Date: Thu, 21 Aug 2025 14:50:35 +0200 Subject: [PATCH 1/4] doc: updated Solver Authentication and Winner Enforcment v3 docs --- docs/integration/7683/solver-integration.md | 106 +++++++++++++------- 1 file changed, 72 insertions(+), 34 deletions(-) diff --git a/docs/integration/7683/solver-integration.md b/docs/integration/7683/solver-integration.md index 4e00b843..52f0fdea 100644 --- a/docs/integration/7683/solver-integration.md +++ b/docs/integration/7683/solver-integration.md @@ -43,9 +43,9 @@ The settler contract implementation is `T1ERC7683.sol`. 3 - t1 catches the open event on source chain settler contract and runs an auction to compute what is the best price available. -4 - Considering the best price is at least the `minAmountOut`, t1 commits the winning bid `amountOut` and solver's settlement receiver address on source settler contracts, and notifies winner solver it can fill over the websocket connection (with relevant data). +4 - Considering the best price is at least the `minAmountOut`, t1 notifies winner solver it can fill over the websocket connection (with relevant data, which includes winning bid `amountOut` and solver's settlement receiver address and signature of a trusted party on that data). -5 - Solver fills on destination chain settler contract in respect of the pricing it commited to. +5 - Solver fills on destination chain settler contract in respect of the pricing it commited to. While solving he uses the t1 trusted party signature asseting that he is the auction winner. This signature is verified by the settler contract (`T1ERC7683.sol`) . 6 - Solver calls a verify function on source chain settler contract that will create the Proof of Read that will check the fill on destination settler contract. @@ -53,36 +53,45 @@ The settler contract implementation is `T1ERC7683.sol`. 8 - Solver calls t1 offchain API to get the merkle proof data for the Proof of Read. -9 - Solver calls settlement function on source chain settler contract by providing the merkle proof, and get repaid if the fill settlement receiver address and amountOut verified by Proof of Read match with the commited winning bid. +9 - Solver calls settlement function on source chain settler contract by providing the merkle proof, and get repaid if Proof of Read confirms that the winning bid was settled on the target chain settler contract. ## Integrating the auction Here is how you can integrate from step 1 to 5. -### 1 - Solver logins to t1 backend and stream its prices +### 1 - Solver logins to t1 backend and streams its prices -Connect to `wss://localhost:{port}` by providing a `username` data field. +Connect to `wss://api.v07.t1protocol.com` by providing following authentication headers: ```typescript - const socket = new WebSocket(`ws://localhost:{port}/`, { - headers: { Authorization: "username" } + const socket = new WebSocket(`wss://api.v07.t1protocol.com/`, { + headers: { + "X-Auth-Blob": { + username: 'your-username-for-t1-stats-recording-only', + nonce: 'nonce-string-retrieved-from-t1-api' + }, + "X-Auth-Signature": 'signature-of-x-auth-blob' + } }); ``` +- `"X-Auth-Blob".username` is any string that you would like your Solver to be identified by +- `"X-Auth-Blob".nonce` is a unique nonce tied to your login attempt. you can acquire be calling `https://t1.api.url/api/currentNonce?username=your-username` . This nonce is needed for security of your login - should any part intercept your signature, they are not able to use it to login. +- `"X-Auth-Signature"` is a signature of a stringified `"X-Auth-Blob"` . Make sure to use the private key that will also be used to fill intents! -Then stream you price list in the following format to the socket. +Then stream your price list in the following format to the socket. Best frequency is 1s. ```typescript - type PriceListItem = { +type PriceListItem = { direction: Direction; intervals: Interval[]; - srcTokenAddresses: string[]; - dstTokenAddress: string[]; - solverAddress: string; + srcTokenAddresses: `0x${string}`[]; + dstTokenAddresses: `0x${string}`[]; + settlementReceiverAddress: `0x${string}`; } - enum Direction { +enum Direction { "Base_USDC->Arbitrum_USDC", "Base_USDC->Arbitrum_WETH", "Arbitrum_USDC->Base_USDC", @@ -96,39 +105,63 @@ enum Token { Arbitrum_WETH = "Arbitrum_WETH", } - type Interval = { +type TokenWithDecimal = { + token: Token; + decimal: bigint; +} + +type Interval = { range: { min: bigint; // must be an integer representing full tokens max: bigint; // must be an integer representing full tokens }; - rangeUnit: Token; + rangeUnit: TokenWithDecimal; price: bigint; // must be an integer representing the smallest denomination of the token - priceUnit: Token; + priceUnit: TokenWithDecimal; }; - socket.send(JSON.stringify(priceList)); +const priceList: PriceListItem[] = YOUR_PRICE_LIST_ITEMS; +socket.send(JSON.stringify(priceList)); ``` ### 4 - t1 notifies winner solver it can fill -When an intent is opened and you win to auction, t1 backend will notify you over websocket the following way. +When an intent is opened and an auction winner is chosen, t1 backend will notify you over websocket the following way. -You will receive a JSON stringify version of the following data on your socket connection. +You will receive a stringified JSON of the following data on your socket connection: ```typescript interface AuctionResult { - winner: string; - price: bigint; - orderId: string; - resolvedOrder: string; + type: "winning-bid"; + orderId: string; // orderId created when Intent was opened on the source chain + settlementReceiverAddress: string; // winning solver + amountOut: bigint; // auctioned amountOut + signature: string; // a EIP-712 signature of a trusted t1 EOA attesting about the auction result } ``` +A EIP-712 signature tied to every `AuctionResult` is created as follows: + +```solidity +// DOMAIN SEPARATOR can be fetched on contract by calling `domainSeparator()` method +bytes32 FILL_AUTHORIZATION_TYPEHASH = keccak256("FillAuthorization(bytes32 orderId,address filler,uint256 amountOut)"); + +bytes32 structHash = keccak256(abi.encode(FILL_AUTHORIZATION_TYPEHASH, orderId, solverAddress, amountOut)); +bytes32 digest = ECDSA.toTypedDataHash(DOMAIN_SEPARATOR, structHash); +``` + +A sample message could look like this: +``` +[0xa3e5e908868e2d881e70c304c18636a2f43e933f] won auction on chain [84532] : {"type":"winning-bid","settlementReceiverAddress":"0xa3e5e908868e2d881e70c304c18636a2f43e933f","amountOut":"97","orderId":"0x7498f9b0ac58664036824d927a04b36b85f2ec219e18889686badc30e0eece75","signature":"0x33dea4f2cec07b0ff373bbf2450056073abda4a301f27965c3d17f233ccd81a33be819c24d8004a3a28d1c709ac7370f6e8b649f58c59ae9e4faf1977e02bbb91b"} +``` + +Do note that all connected Solvers will receive such message, even if they did not win the auction. Therefore, before filling an intent, make sure that your address won the auction. + ### 5 - Solver fills on destination chain -You can then use the received data to call the `fill` function on destination settler. +You can then use the received data to call the `fill` function on destination chain settler contract. ```solidity @@ -139,6 +172,13 @@ You can then use the received data to call the `fill` function on destination se function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external payable; ``` +The `fillerData` should be encoded in a following way: + +```solidity +/// @param authorization Signature provided by auction backend +abi.encode(uint256 bidAmountOut, address sourceChainSettlementReceiver, bytes authorization) +``` + ## Settlement integration @@ -185,7 +225,7 @@ Location: offchain Timing: once a new `ProofOfReadRootCommitted` event has been emitted -Base URL: `https://api.v06.t1protocol.com` +Base URL: `https://api.v07.t1protocol.com` Endpoint: `/api/read-proofs` @@ -193,19 +233,17 @@ Method: `GET` **Query Parameters:** -| Parameter | Type | Optional | Description | Value suggested | - -|-----------|------|----------|-------------|---------| - -| `address` | string | No | Address that called `verifySettlement` | - | -| `direction` | string | Yes | Direction of the read: `"ARB_TO_BASE"` \| `"BASE_TO_ARB"` | - | -| `page` | number | Yes | Page number to fetch | `1` | -| `page_size` | number | Yes | Items per page | `100` | +| Parameter | Type | Optional | Description | Value suggested | +|-----------|------|----------|-------------|------------------------------------| +| `address` | string | No | Address that called `verifySettlement` | - | +| `direction` | string | Yes | Direction of the read | `"ARB_TO_BASE"` or `"BASE_TO_ARB"` | +| `page` | number | Yes | Page number to fetch | `1` | +| `page_size` | number | Yes | Items per page | `100` | **Example Request:** ```bash -curl "https://api.v06.t1protocol.com/api/read-proofs?address=0x123...&direction=ARB_TO_BASE&page=1&page_size=100" +curl "https://api.v07.t1protocol.com/api/read-proofs?address=0x123...&direction=ARB_TO_BASE&page=1&page_size=100" ``` The HTTP call will return the following structure as a response : From a87c2a10c742e8894cb25c940d8177bc58f4b995 Mon Sep 17 00:00:00 2001 From: Krzysztof Spisak-Spisacki Date: Thu, 21 Aug 2025 15:08:16 +0200 Subject: [PATCH 2/4] doc: added v07 api url instead of a sample one --- docs/integration/7683/solver-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integration/7683/solver-integration.md b/docs/integration/7683/solver-integration.md index 52f0fdea..a839ccfb 100644 --- a/docs/integration/7683/solver-integration.md +++ b/docs/integration/7683/solver-integration.md @@ -75,7 +75,7 @@ Connect to `wss://api.v07.t1protocol.com` by providing following authentication }); ``` - `"X-Auth-Blob".username` is any string that you would like your Solver to be identified by -- `"X-Auth-Blob".nonce` is a unique nonce tied to your login attempt. you can acquire be calling `https://t1.api.url/api/currentNonce?username=your-username` . This nonce is needed for security of your login - should any part intercept your signature, they are not able to use it to login. +- `"X-Auth-Blob".nonce` is a unique nonce tied to your login attempt. you can acquire be calling `https://api.v07.t1protocol.com/api/currentNonce?username=your-username` . This nonce is needed for security of your login - should any part intercept your signature, they are not able to use it to login. - `"X-Auth-Signature"` is a signature of a stringified `"X-Auth-Blob"` . Make sure to use the private key that will also be used to fill intents! Then stream your price list in the following format to the socket. From 7871e8abc1a318e100bcfcf57e7af288155dc785 Mon Sep 17 00:00:00 2001 From: Krzysztof Spisak-Spisacki Date: Thu, 21 Aug 2025 16:37:48 +0200 Subject: [PATCH 3/4] doc: made it explicitly obvious that data that we send over WebSocket is solver filler address --- docs/integration/7683/solver-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integration/7683/solver-integration.md b/docs/integration/7683/solver-integration.md index a839ccfb..e32e9ac4 100644 --- a/docs/integration/7683/solver-integration.md +++ b/docs/integration/7683/solver-integration.md @@ -43,7 +43,7 @@ The settler contract implementation is `T1ERC7683.sol`. 3 - t1 catches the open event on source chain settler contract and runs an auction to compute what is the best price available. -4 - Considering the best price is at least the `minAmountOut`, t1 notifies winner solver it can fill over the websocket connection (with relevant data, which includes winning bid `amountOut` and solver's settlement receiver address and signature of a trusted party on that data). +4 - Considering the best price is at least the `minAmountOut`, t1 notifies winner solver it can fill over the websocket connection (with relevant data, which includes winning bid `amountOut` and solver's filler address and signature of a trusted party on that data). 5 - Solver fills on destination chain settler contract in respect of the pricing it commited to. While solving he uses the t1 trusted party signature asseting that he is the auction winner. This signature is verified by the settler contract (`T1ERC7683.sol`) . From 6fbf6997fe1d6e834d3a31b0b29980b59e785bbf Mon Sep 17 00:00:00 2001 From: Krzysztof Spisak-Spisacki Date: Thu, 21 Aug 2025 16:40:59 +0200 Subject: [PATCH 4/4] doc: removed redundant information --- docs/integration/7683/solver-integration.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/integration/7683/solver-integration.md b/docs/integration/7683/solver-integration.md index e32e9ac4..29f7d5bf 100644 --- a/docs/integration/7683/solver-integration.md +++ b/docs/integration/7683/solver-integration.md @@ -142,16 +142,6 @@ interface AuctionResult { } ``` -A EIP-712 signature tied to every `AuctionResult` is created as follows: - -```solidity -// DOMAIN SEPARATOR can be fetched on contract by calling `domainSeparator()` method -bytes32 FILL_AUTHORIZATION_TYPEHASH = keccak256("FillAuthorization(bytes32 orderId,address filler,uint256 amountOut)"); - -bytes32 structHash = keccak256(abi.encode(FILL_AUTHORIZATION_TYPEHASH, orderId, solverAddress, amountOut)); -bytes32 digest = ECDSA.toTypedDataHash(DOMAIN_SEPARATOR, structHash); -``` - A sample message could look like this: ``` [0xa3e5e908868e2d881e70c304c18636a2f43e933f] won auction on chain [84532] : {"type":"winning-bid","settlementReceiverAddress":"0xa3e5e908868e2d881e70c304c18636a2f43e933f","amountOut":"97","orderId":"0x7498f9b0ac58664036824d927a04b36b85f2ec219e18889686badc30e0eece75","signature":"0x33dea4f2cec07b0ff373bbf2450056073abda4a301f27965c3d17f233ccd81a33be819c24d8004a3a28d1c709ac7370f6e8b649f58c59ae9e4faf1977e02bbb91b"}