diff --git a/src/content/docs/cross-cats/Becoming a Solver/btc-orders.mdx b/src/content/docs/cross-cats/Becoming a Solver/btc-orders.mdx
new file mode 100644
index 0000000..387cdfe
--- /dev/null
+++ b/src/content/docs/cross-cats/Becoming a Solver/btc-orders.mdx
@@ -0,0 +1,785 @@
+---
+title: "Initiating BTC Orders"
+description: "Cross Cats allows solvers to collect order flow to and from various VM chains and to and from Bitcoin. Compared to competing solution, capital hungry solvers can improve their capital turnaround by using the underwriting network to their advantage."
+sidebar:
+ order: 5
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+On VM chains, assets can be pulled from users via approvals – a feature not available on non-VM chains – instead the sequence of operation has been modified to allow the user to push assets to the solver. This section outlines how orders are handled when originating from a VM chain, while the section [From Bitcoin](#from-bitcoin) covers orders originating from Bitcoin.
+
+### Collecting Orders
+
+The order server stores orders in a dictionary format, allowing for straightforward parsing by integrators and provides a high level of transparency in the implementation.
+
+
+
+
+ ```typescript
+ // The type parameter (DutchAuctionData.type) should not be submitted on-chain but can be used to differentiate order types.
+ type DutchAuctionData = {
+ type: "DutchAuction"; // Not to be submitted
+ verificationContext: string;
+ verificationContract: string;
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ slopeStartingTime: number;
+ inputSlopes: string[];
+ outputSlopes: string[];
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ type LimitOrderData = {
+ type: "LimitOrder"; // Not to be submitted
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ // With the CrossChainOrder defined as such:
+ type CrossChainOrder = {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchAuctionData | LimitOrderData;
+ };
+
+ type OrderDto = {
+ order: CrossChainOrder;
+ quote: {
+ fromAsset: string;
+ toAsset: string;
+ toPrice: string;
+ fromPrice: string;
+ intermediary: "USD" | "EUR" | "BTC" | string; // explicit string types here are examples.
+ discount: string;
+ };
+ signature: string;
+ submitTime: number;
+ };
+
+ type PaginationMeta = {
+ total: number;
+ limit: number;
+ offset: number;
+ };
+
+ type GetOrdersResponse = {
+ data: OrderDto[];
+ pagination: PaginationMeta;
+ };
+
+ async function getOrders(): GetOrdersResponse {
+ const API_URI = "https://crosscats-api-staging.catalyst.exchange";
+ const API_KEY = "your_api_key_here";
+
+ const orderServerResponse = await fetch(
+ API_URI + "/orders",
+ {
+ headers: {
+ "x-api-key": API_KEY,
+ Accept: "application/json",
+ },
+ }
+ );
+ if (!orderServerResponse.ok) {
+ throw new Error(`HTTP error! status: ${orderServerResponse.status}`);
+ }
+
+ const fetchedOrders = await orderServerResponse.json();
+ return fetchedOrders;
+ }
+ ```
+
+
+
+
+ ```python
+ import requests
+
+ def get_orders():
+ response = requests.get(API_URL + "orders/")
+ fetched_orders = response.json()
+ return fetched_orders
+ ```
+
+
+
+
+#### Subscribe to orders
+
+Subscribing to orders allow you to listen directly to the order flow. This section outlines how to subscribe to new orders using a WebSocket connection, allowing for real-time updates without the need to continuously poll the order server. By leveraging a WebSocket, the Catalyst order server broadcasts new orders as they arrive, offering a significant reduction in latency but at the cost of increased complexity due to the need for a persistent connection and local filtering of incoming data.
+
+Instead of polling for new orders, you can establish a WebSocket connection to the Catalyst order server. The server provides a WebSocket endpoint that pushes new order data to subscribers in real time. However, unlike polling, the data received through WebSocket is not pre-filtered. This means every order event will be pushed to your application, and it’s up to your implementation to manage and filter these events locally.
+
+Below is a simplified implementation in pure JavaScript that demonstrates how to connect to the WebSocket server, handle incoming messages, respond to ping events, and automatically attempt to reconnect if the connection is lost.
+
+```typescript
+const WebSocket = require("ws");
+
+// Configuration variables
+const wsUri = process.env.ORDER_SERVER_WS_URI; // Set your WebSocket server URI
+const apiKey = process.env.ORDER_SERVER_API_KEY; // Set your API key
+const reconnectInterval = 5000; // Reconnect interval in milliseconds
+
+let ws;
+
+// Function to connect to the WebSocket server
+function connectToOrderServer() {
+ ws = new WebSocket(wsUri, {
+ headers: {
+ "x-api-key": apiKey,
+ },
+ });
+
+ ws.on("open", () => {
+ console.log("Connected to WebSocket server");
+ });
+
+ ws.on("message", (data) => {
+ try {
+ const parsedData = JSON.parse(data.toString());
+ console.log("Received message:", parsedData);
+
+ switch (parsedData.event) {
+ case "ping":
+ handleReceivePing();
+ break;
+ case "quote-request":
+ handleReceiveQuoteRequest(parsedData, ws);
+ break;
+ case "order":
+ handleReceiveOrder(parsedData, ws);
+ break;
+ default:
+ console.log("Unknown message type:", parsedData);
+ }
+ } catch (error) {
+ console.error("Error parsing JSON:", error);
+ }
+ });
+
+ ws.on("error", (error) => {
+ console.error("WebSocket error:", error);
+ });
+
+ ws.on("close", () => {
+ console.log("Disconnected from WebSocket");
+ reconnect();
+ });
+}
+
+// Function to handle ping messages, you will be automatically disconnected if you don't respond to ping messages
+function handleReceivePing() {
+ ws.send(JSON.stringify({ event: "pong" }));
+}
+
+// Function to handle quote requests
+function handleReceiveQuoteRequest(data, ws) {
+ console.log("Handling quote request:", data);
+ // Add your custom handling logic here
+}
+
+// Function to handle orders
+function handleReceiveOrder(data, ws) {
+ console.log("Handling order:", data);
+ // Add your custom handling logic here
+}
+
+// Function to attempt reconnection
+function reconnect() {
+ console.log("Attempting to reconnect...");
+ setTimeout(() => {
+ ws.close(); // Close any existing connection
+ connectToOrderServer(); // Attempt to reconnect
+ }, reconnectInterval);
+}
+
+// Start listening to the order server
+connectToOrderServer();
+```
+
+### Evaluate Orders
+
+After fetching an order, the solver must thoroughly evaluate it to determine its viability and potential execution. To facilitate this evaluation, several contextual pointers are available within the returned order data. Key aspects to consider include:
+
+1. **Quote Validation**: Use the `OrderDto.quote` field to access the price context, which provides the pricing details for the inputs and outputs of the order. If you trust the order server, you can primarily rely on this quote to validate the order’s pricing. However, it’s crucial to verify that the solver supports the specific origin chain (`OrderDto.order.originChainId`) and output chains (`OrderDto.order.orderData.outputs[...].chainId`) as well as their respective tokens (`input[].token` and `output[].token`). These parameters are guaranteed to be present across all order types.
+
+2. **Solver-Exclusive Orders**: Some orders may initially be restricted to specific solvers. This is indicated by the `OrderDto.order.orderData.verificationContract` field. If this field is defined and not equal to `address(0)`, the order is exclusive to the designated solver until the `slopeStartingTime` elapses, after which the order becomes available for anyone to fulfill.
+
+3. **Mutually Exclusive Orders**: Be aware of potential conflicts between orders. If you encounter two orders with the same `OrderDto.order.swapper` and `OrderDto.order.nonce`, these orders are mutually exclusive, meaning only one of them can be submitted on-chain. This mechanism prevents double submissions and ensures the integrity of the order processing.
+
+Evaluating orders carefully ensures that solvers can accurately determine the feasibility of executing an order, adhere to exclusivity rules, and avoid conflicts, thereby maintaining the integrity and efficiency of the order fulfillment process.
+
+### Initiate Orders
+
+Once an order has been fetched and validated, the next step is to submit it on-chain. Catalyst Orders are accompanied by a signature (`OrderDto.signature`) that serves a dual purpose:
+
+1. **Permit2 Signature**: This signature acts as a Permit2, authorizing the Catalyst contracts to withdraw the submitter's tokens directly. This streamlines the process by eliminating the need for separate approval transactions.
+
+2. **User Authorization**: The signature also confirms that the user has approved the order, ensuring consent and alignment with the terms of execution.
+
+Orders are processed on a first-come, first-served basis, emphasizing the importance of swift submission to secure the desired transaction. By leveraging the Permit2 signature mechanism, Catalyst simplifies the initiation process, reducing overhead and ensuring seamless order execution.
+
+
+
+
+```typescript
+// This tutorial uses ethersjs but you can easily replace it by similar libraries.
+import { ethers } from "ethers";
+
+const reactorAbi = "...";
+const signer = "ethers.signer...";
+
+async function initiateOrder() {
+ // Get an order
+ const orders = await getOrders();
+ const order = orders.orders[0];
+
+ // Define the reactor we will call. You can get the reactor address from the order
+ const reactorAddress = order.order.settlementContract;
+ const reactor = new ethers.Contract(reactorAddress, reactorAbi, signer);
+
+ // TODO: Set approvals for the reactorAddress for all inputs & collateral.
+
+ // The order arrives almost ready to use,
+ // we just need to remove the type from the orderdata.
+ const { type: _, ...cleanedOrderData } = order.order.orderData;
+ const cleanedOrder = { ...order.order, orderData: cleanedOrderData };
+ const signature = order.signature;
+ const fillerData = "0x"; // #custom-fillerdata--underwriting
+
+ // Call the reactor to initiate the order.
+ return reactor.initiate(cleanedOrder, signature, fillerData);
+}
+```
+
+
+
+
+```python
+from web3 import Web3
+
+rpc_url = ""
+web3 = Web3(Web3.HTTPProvider(eth_node_url))
+
+# Your ABI and signer details
+reactor_abi = "..."
+signer_private_key = "your_private_key_here"
+signer_address = web3.eth.account.from_key(signer_private_key).address
+
+def initiate_order():
+ # Get an order
+ orders = get_orders()
+ order = orders['orders'][0]
+
+ # Define the reactor we will call. You can get the reactor address from the order
+ reactor_address = order['order']['settlementContract']
+ reactor = web3.eth.contract(address=reactor_address, abi=reactor_abi)
+
+ # TODO: Set approvals for the reactorAddress for all inputs & collateral.
+ # This will depend on the specific ERC20 tokens you're using,
+ # you need to call approve() on the ERC20 token contracts
+
+ # Clean the order data by removing the type field
+ cleaned_order_data = order['order']['orderData'].copy()
+ cleaned_order_data.pop('type')
+ cleaned_order = {**order['order'], 'orderData': cleaned_order_data}
+ signature = order['signature']
+ filler_data = "0x" # #custom-fillerdata--underwriting
+
+ # Build the transaction
+ txn = reactor.functions.initiate(cleaned_order, signature, filler_data).build_transaction({
+ 'from': signer_address,
+ 'nonce': web3.eth.get_transaction_count(signer_address)
+ })
+ # Sign the transaction
+ signed_txn = web3.eth.account.sign_transaction(txn, private_key=signer_private_key)
+ # Send the transaction
+ tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
+ # Wait for the transaction receipt
+ receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
+ return receipt
+```
+
+
+
+
+#### Custom FillerData & Underwriting
+
+By default, if `fillerData` is not specified, the input assets (provided by the user) are sent directly to the caller. This behavior is generally suitable for most use cases, eliminating the need for additional customization.
+
+However, if there is a need to direct the input assets to another address or to enable underwriting, a customized `fillerData` must be utilized. Currently, only one custom version (`v1`) is supported. The `v1` structure includes:
+
+- **Version Byte (0x01)**: Identifies the custom data version.
+- **fillerAddress**: The address that will receive the input assets and collateral.
+- **orderPurchaseDeadline**: A timestamp that allows an alternative buyer to purchase the order before this time. "Buying" the order in this context means transferring all of the input assets and collateral to the `fillerAddress`.
+- **orderDiscount**: Provides buyers with a discount on the inputs, represented as a fraction of 2^16 - 1. For example, to offer a 1% discount, the value would be calculated as `0.01 * (2^16 - 1) = 655`. This feature is particularly useful for chains with slower block confirmations (e.g., Bitcoin), enabling the solver to be paid after 0-1 confirmations while assuring the user of higher finality (3-6 confirmations).
+
+
+
+
+```typescript
+const fillerDataVersion = "0x01";
+const fillerAddress = "0x....".replace("0x", "");
+// fillerAddress.length === 20*2;
+const orderPurchaseDeadline = Number(1723199919)
+ .toString(16)
+ .padStart("0", 4 * 2);
+//orderPurchaseDeadline.length === 4*2
+const orderDiscount = Math.floor(0.01 * (2 ** 16 - 1))
+ .toString(16)
+ .padStart("0", 2 * 2);
+// orderDiscount.length === 2*2
+
+const fillerData =
+ fillerDataVersion + fillerAddress + orderPurchaseDeadline + orderDiscount;
+```
+
+
+
+
+```python
+fillerDataVersion = "0x01";
+fillerAddress = '0x....'.replace("0x", "");
+# len(fillerAddress) === 20*2;
+orderPurchaseDeadline = hex(1723199919).replace("0x", "").zfill(4*2);
+# len(orderPurchaseDeadline) === 4*2
+const orderDiscount = hex(int(0.01*(2**16-1))).replace("0x", "").zfill(2*2);
+# len(orderDiscount) === 2*2
+
+fillerData = fillerDataVersion + fillerAddress + orderPurchaseDeadline + orderDiscount;
+```
+
+
+
+
+## Delivery
+
+The delivery of assets varies based on the destination type: VM chains or Bitcoin.
+
+### EVM deliveries
+
+For EVM (Ethereum Virtual Machine) chains, you must interact with the specified oracle on the destination chain. Use the following details from the order to make the necessary call:
+
+- **Oracle Address**: Found in `OrderDto.order.orderData.outputs[].remoteOracle`.
+- **Destination Chain**: Identified by `OrderDto.order.orderData.outputs[].chainId`.
+
+
+
+
+```typescript
+// It is assumed you are continuing from the above steps.
+import { ethers } from "ethers";
+
+const oracleAbi = "...";
+
+// The oracle allows filling multiple outputs from different orders
+// in a single transaction. They do have to go to the same chain.
+// For simplicity, this function assumes that all outputs goes to
+// the same chain but it may not be the case.
+async function fillSingleChainOrder(order: CrossChainOrder) {
+ let recordedChain;
+ let recordedOracle;
+ for (const output of order.orderData.outputs) {
+ if (recordedChain === undefined) recordedChain = output.chainId;
+ if (recodedOracle === undefined) recodedOracle = output.remoteOracle;
+ if (recordedChain !== output.chainId)
+ throw Error(
+ `Mixed ChainIds, seen ${recordedChain} and ${output.chainId}`
+ );
+ if (recodedOracle !== output.remoteOracle)
+ throw Error(
+ `Mixed Oracles, seen ${recodedOracle} and ${output.remoteOracle}`
+ );
+ }
+ const oracle = new ethers.Contract(recordedOracle, oracleAbi, signer);
+
+ // TODO: Set approvals for the oracleAddress for the value of the output.
+
+ // We need to provide fill times. These have to be set to proofTime.
+ // These are used to ensure you can't reuse fills.
+ const fillTimes = order.orderData.outputs.map(
+ (_) => order.orderData.proofDeadline
+ );
+
+ // Call the reactor to initiate the order.
+ return oracle.fill(outputs, fillTimes);
+}
+```
+
+
+
+
+```python
+# It is assumed you are continuing from the above steps.
+
+oracleAbi = "...";
+
+# The oracle allows filling multiple outputs from different orders in a single transaction.
+# They do have to go to the same chain.
+# For simplicity, this function assumes that all outputs goes to the same chain but it may not be the case.
+def fill_single_chain_order(order):
+ oracle_address = order['orderData']['remoteOracle']
+
+ recordedChain = "";
+ recordedOracle = "";
+ for (output in order['orderData']['outputs']):
+ if (recordedChain == ""):
+ recordedChain = output.chainId
+ if (recordedOracle == ""):
+ recordedOracle = output.remoteOracle
+ if (recordedChain != output.chainId):
+ raise Exception(f"Mixed ChainIds, seen {recordedChain} and {output.chainId}");
+ if (recordedOracle != output.remoteOracle):
+ raise Exception(f"Mixed Oracles, seen {recordedOracle} and {output.remoteOracle}");
+
+ oracle = web3.eth.contract(address=recordedOracle, abi=oracle_abi)
+
+ # TODO: Set approvals for the oracleAddress for the value of the output.
+
+ # We need to provide fill times. These have to be set to proofTime.
+ # These are used to ensure you can't reuse fills.
+ fillTimes = [order.orderData.proofDeadline for _ in order.orderData.outputs]
+
+ # Build the transaction
+ txn = oracle.functions.fill(order.orderData.outputs, fillTimes).build_transaction({
+ 'from': signer_address,
+ 'nonce': web3.eth.get_transaction_count(signer_address)
+ })
+ # Sign the transaction
+ signed_txn = web3.eth.account.sign_transaction(txn, private_key=signer_private_key)
+ # Send the transaction
+ tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
+ # Wait for the transaction receipt
+ receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
+ return receipt
+```
+
+
+
+
+### Bitcoin Deliveries
+
+To determine whether an order involves a Bitcoin transaction, check the `GetOrderData.order.orderData.outputs[].token` field. If the token indicates Bitcoin, ensure the following conditions are met:
+
+- The first 30 bytes of the token should be `0x000000000000000000000000BC0000000000000000000000000000000000`. The 13th byte is `0xBC`.
+- The 31st byte indicates the number of confirmations required before the order can be verified on-chain. For example:
+ - `0x00` and `0x01` represent 1 confirmation.
+ - `0x02` represents 2 confirmations.
+ - `0x03` represents 3 confirmations, and so on.
+- The 32nd byte contains an address version identifier, which should be decoded as `uint8`.
+
+If the transaction is directed to Bitcoin, the address (`GetOrderData.order.orderData.outputs[].recipient`) will contain a relevant destination hash or witness, not the address itself. This value must be used along with the address version identifier to decode the address.
+
+
+
+
+ Version |
+ Name |
+ Encoding Scheme |
+ Prefix |
+ Hash Length |
+
+
+
+
+ 0 |
+ Unknown |
+ Ignore |
+ |
+ |
+
+
+ 1 |
+ P2PKH |
+ Base58Check(00+PKH) |
+ 1* |
+ 20 |
+
+
+ 2 |
+ P2SH |
+ Base58Check(05+SH) |
+ 3* |
+ 20 |
+
+
+ 3 |
+ P2WPKH |
+ Bech32 |
+ bc1q** |
+ 20 |
+
+
+ 4 |
+ P2WSH |
+ Bech32 |
+ bc1q** |
+ 32 |
+
+
+ 5 |
+ P2TR |
+ Bech32m |
+ bc1p** |
+ 32 |
+
+
+
+\* Prefixes are determined by the encoding scheme.
+\
+\*\* Part of the prefix – 1q/1p – is determined by the encoding scheme.
+
+The following guidelines assume you are implementing this from the perspective of a solver. You need to convert the expected output script into a Bitcoin address that can be used with your wallet:
+
+- **P2PKH (Pay-to-PubKey-Hash)**:
+
+ - The recipient is the public key hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `00`, and encode with Base58Check.
+
+- **P2SH (Pay-to-Script-Hash)**:
+
+ - The recipient is the script hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `05`, and encode with Base58Check.
+
+- **P2WPKH (Pay-to-Witness-PubKey-Hash)**:
+
+ - The recipient is the witness. Encode the first 20 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2WSH (Pay-to-Witness-Script-Hash)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2TR (Pay-to-Taproot)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32m](https://en.bitcoin.it/wiki/BIP_0350#Bech32m). Prepend with `bc1p`.
+
+- **Nested Witness Addresses/Outputs**:
+ - These are **P2SH** addresses and should be treated like any other **P2SH** address.
+
+Once the address is generated, create a Bitcoin transaction with at least one output that **exactly** matches the described output from the initiated order. The transaction can have any number of inputs and outputs, as long as one output precisely matches the one specified by the order's output. This flexibility allows for batch filling, consolidation, and more. While having multiple matching outputs is permitted, it benefits only the user to ensure the order’s conditions are met.
+
+## From Bitcoin
+
+Bitcoin operates differently from VM chains in terms of script functionality. Bitcoin scripts only define spending conditions, meaning that, unlike VM chains, we cannot pull funds from a user after they have signed a message. Consequently, the order flow is reversed, and the solver is responsible for signing the VM order. While it's technically feasible to create unique conditional deposit addresses, most wallets do not support pulling from custom scripts.
+
+### Encoding a Bitcoin Address
+
+Your solver must be capable of generating a Bitcoin deposit address. Although it's possible to use the same address for every order, it is crucial to ensure that each order amount is unique if you choose this approach. It is generally recommended to use different addresses for each order. Bitcoin does not impose additional fees for collecting UTXOs from multiple addresses.
+
+Bitcoin addresses are encoded in two fields: `token` and `address`.
+
+- **`token`**: This field serves to differentiate VM tokens from Bitcoin orders and encode the address version. Refer to the [Bitcoin Deliveries](#bitcoin-deliveries) table for details on converting address types to versions.
+
+- **`address`**: This field encodes the public key hash, script hash, or witness hash. Use the encoding schemes listed in the [Bitcoin Deliveries](#bitcoin-deliveries) table for various address versions. For addresses involving hashes of 20 bytes (P2PKH, P2SH, and P2WPKH), ensure to pad the hashes with zeros on the right side (e.g., `0xabcdef...00000`).
+
+### Quote Open Order (WIP)
+
+For solvers handling BTC to VM conversions, quoting orders for comparison with other solvers is essential. To begin quoting:
+
+1. **Subscription**: Solvers must subscribe to quote requests from the order server.
+2. **Response**: When a quote request is received, the solver must respond with a quote that remains valid for at least 60 seconds. If a signed order is requested within 30 seconds of the quote, the solver should provide a signed order with a lifetime of 30 seconds.
+
+Failure to respond with a signed order that matches or improves upon the quote within the 30-second window may result in blacklisting of the solver.
+
+Ensure your quoting mechanism is robust and timely to maintain competitive standing and avoid potential blacklisting.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CatalystQuoteRequestData {
+ quoteRequestId: string;
+ fromChain: string;
+ toChain: string;
+ fromAsset: string;
+ toAsset: string;
+ expirationTime: string;
+ amount: string;
+}
+
+async function handleReceiveQuoteRequest(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const quote = await getSolverQuote(
+ parsedData.data.fromAsset,
+ parsedData.data.toAsset,
+ parsedData.data.amount
+ );
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-quote",
+ data: {
+ origin: "your_identifier", // required
+ quoteRequestId: parsedData.data.quoteRequestId, // required
+ ...quote,
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error simulating quote:", error);
+ }
+}
+```
+
+### Return Signed Orders (WIP)
+
+Once the Order Server has evaluated all quotes from solvers, it selects the most favorable quote. After the user's deposit is confirmed, the Order Server will request the selected solver to provide a signed order.
+
+### Responsibilities of the Solver:
+
+1. **Provide a Signed Order**: The solver must return a signed order that aligns with the quoted terms and remains valid within the specified expiry period.
+
+2. **Order Exclusivity**: It is crucial that the signed order is exclusive to the user and the Order Server’s executor. This exclusivity ensures that the order cannot be fulfilled by other solvers or reused.
+
+3. **Asset Delivery Assurance**: The Cross Cats Order Server guarantees asset delivery through a Bitcoin address controlled by the Order Server but owned by the user. This setup ensures that the assets are securely delivered as promised.
+
+Ensuring these conditions helps maintain the integrity and efficiency of the order fulfillment process. It is essential that solvers adhere strictly to these requirements to ensure smooth operations and avoid any potential issues.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CrossChainOrder {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchOrderData | LimitOrderData;
+}
+
+export interface QuoteContext {
+ toAsset: string;
+ toPrice: string;
+ discount: string;
+ fromAsset: string;
+ fromPrice: string;
+ intermediary: string;
+}
+
+export interface CatalystOrderData {
+ order: CrossChainOrder;
+ quote: QuoteContext;
+ signature: string;
+}
+
+async function signOrderRequest(signOrderRequest: any): any | undefined {
+ const evaluationResponse = orderEvaluations(signOrderRequest);
+ if (evaluationResponse === undefined) return undefined;
+ ...
+}
+
+async function handleReceiveOrder(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const signedOrder = await signOrderRequest(...);
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-order-signed",
+ data: {
+ origin: "your_identifier", // required
+ ...signedOrder, // TODO: determine shape of signed order
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error signing order:", error);
+ }
+}
+```
+
+Once the signedOrder is sent & it correctly matches the expected quote, the vast vast majority of orders will be filled.
+
+TODO: The Order Server may respond back with a transaction hash.
+
+## Prove Orders (WIP)
+
+If Solvers' orders get challenged, they need to prove delivery. In exchange for these proofs, Solvers collect the collateral provided by Challengers.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Challenger (Has to be moved)
+
+Challengers work to ensure that solvers stay honest. Solvers submit collateral when claiming transaction. If Solvers aren't honest, Challengers can claim the collateral if they prove that Solvers havn't done settled their claimed orders.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Broadcast Orders (Depreciated)
+
+The first step of any order is broadcasting it. For solvers this can be seen as the 0'th step, since it is required before solvers can collect the orders from the order server.
+
+:::tip[Proof of Work]
+Submitting orders can easily be used to DDoS the order server. As a protection, the order server requires submitting a piece of PoW to rate-limit submissions.
+:::
+
+
+
+
+```typescript
+async function submitOrder(): bool {
+ const order: CrossChainorder;
+ const signature: string;
+ const body = {
+ order,
+ signature,
+ };
+ const response = await fecth(API_URL + "orders/submit", {
+ method: "POST",
+ body,
+ });
+ const parsedResponse = (await response.json()) as { success: bool };
+
+ return parsedResponse.success;
+}
+```
+
+
+
+
+```python
+import requests
+import json
+
+API_URL = "https://crosscats-api-staging.catalyst.exchange/"
+
+def submit_order(): bool:
+ order = {}
+ signature = "0x..."
+ data = {
+ "order": order,
+ "signature": signature
+ }
+ response = requests.post(API_URL+ "orders/submit", json=data)
+ fetched_orders = response.json()
+ return fetched_orders.get("success")
+```
+
+
+
diff --git a/src/content/docs/cross-cats/Becoming a Solver/evm-orders.mdx b/src/content/docs/cross-cats/Becoming a Solver/evm-orders.mdx
new file mode 100644
index 0000000..4bcd233
--- /dev/null
+++ b/src/content/docs/cross-cats/Becoming a Solver/evm-orders.mdx
@@ -0,0 +1,785 @@
+---
+title: "Initiating EVM Orders"
+description: "Cross Cats allows solvers to collect order flow to and from various VM chains and to and from Bitcoin. Compared to competing solution, capital hungry solvers can improve their capital turnaround by using the underwriting network to their advantage."
+sidebar:
+ order: 4
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+On VM chains, assets can be pulled from users via approvals – a feature not available on non-VM chains – instead the sequence of operation has been modified to allow the user to push assets to the solver. This section outlines how orders are handled when originating from a VM chain, while the section [From Bitcoin](#from-bitcoin) covers orders originating from Bitcoin.
+
+### Collecting Orders
+
+The order server stores orders in a dictionary format, allowing for straightforward parsing by integrators and provides a high level of transparency in the implementation.
+
+
+
+
+ ```typescript
+ // The type parameter (DutchAuctionData.type) should not be submitted on-chain but can be used to differentiate order types.
+ type DutchAuctionData = {
+ type: "DutchAuction"; // Not to be submitted
+ verificationContext: string;
+ verificationContract: string;
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ slopeStartingTime: number;
+ inputSlopes: string[];
+ outputSlopes: string[];
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ type LimitOrderData = {
+ type: "LimitOrder"; // Not to be submitted
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ // With the CrossChainOrder defined as such:
+ type CrossChainOrder = {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchAuctionData | LimitOrderData;
+ };
+
+ type OrderDto = {
+ order: CrossChainOrder;
+ quote: {
+ fromAsset: string;
+ toAsset: string;
+ toPrice: string;
+ fromPrice: string;
+ intermediary: "USD" | "EUR" | "BTC" | string; // explicit string types here are examples.
+ discount: string;
+ };
+ signature: string;
+ submitTime: number;
+ };
+
+ type PaginationMeta = {
+ total: number;
+ limit: number;
+ offset: number;
+ };
+
+ type GetOrdersResponse = {
+ data: OrderDto[];
+ pagination: PaginationMeta;
+ };
+
+ async function getOrders(): GetOrdersResponse {
+ const API_URI = "https://crosscats-api-staging.catalyst.exchange";
+ const API_KEY = "your_api_key_here";
+
+ const orderServerResponse = await fetch(
+ API_URI + "/orders",
+ {
+ headers: {
+ "x-api-key": API_KEY,
+ Accept: "application/json",
+ },
+ }
+ );
+ if (!orderServerResponse.ok) {
+ throw new Error(`HTTP error! status: ${orderServerResponse.status}`);
+ }
+
+ const fetchedOrders = await orderServerResponse.json();
+ return fetchedOrders;
+ }
+ ```
+
+
+
+
+ ```python
+ import requests
+
+ def get_orders():
+ response = requests.get(API_URL + "orders/")
+ fetched_orders = response.json()
+ return fetched_orders
+ ```
+
+
+
+
+#### Subscribe to orders
+
+Subscribing to orders allow you to listen directly to the order flow. This section outlines how to subscribe to new orders using a WebSocket connection, allowing for real-time updates without the need to continuously poll the order server. By leveraging a WebSocket, the Catalyst order server broadcasts new orders as they arrive, offering a significant reduction in latency but at the cost of increased complexity due to the need for a persistent connection and local filtering of incoming data.
+
+Instead of polling for new orders, you can establish a WebSocket connection to the Catalyst order server. The server provides a WebSocket endpoint that pushes new order data to subscribers in real time. However, unlike polling, the data received through WebSocket is not pre-filtered. This means every order event will be pushed to your application, and it’s up to your implementation to manage and filter these events locally.
+
+Below is a simplified implementation in pure JavaScript that demonstrates how to connect to the WebSocket server, handle incoming messages, respond to ping events, and automatically attempt to reconnect if the connection is lost.
+
+```typescript
+const WebSocket = require("ws");
+
+// Configuration variables
+const wsUri = process.env.ORDER_SERVER_WS_URI; // Set your WebSocket server URI
+const apiKey = process.env.ORDER_SERVER_API_KEY; // Set your API key
+const reconnectInterval = 5000; // Reconnect interval in milliseconds
+
+let ws;
+
+// Function to connect to the WebSocket server
+function connectToOrderServer() {
+ ws = new WebSocket(wsUri, {
+ headers: {
+ "x-api-key": apiKey,
+ },
+ });
+
+ ws.on("open", () => {
+ console.log("Connected to WebSocket server");
+ });
+
+ ws.on("message", (data) => {
+ try {
+ const parsedData = JSON.parse(data.toString());
+ console.log("Received message:", parsedData);
+
+ switch (parsedData.event) {
+ case "ping":
+ handleReceivePing();
+ break;
+ case "quote-request":
+ handleReceiveQuoteRequest(parsedData, ws);
+ break;
+ case "order":
+ handleReceiveOrder(parsedData, ws);
+ break;
+ default:
+ console.log("Unknown message type:", parsedData);
+ }
+ } catch (error) {
+ console.error("Error parsing JSON:", error);
+ }
+ });
+
+ ws.on("error", (error) => {
+ console.error("WebSocket error:", error);
+ });
+
+ ws.on("close", () => {
+ console.log("Disconnected from WebSocket");
+ reconnect();
+ });
+}
+
+// Function to handle ping messages, you will be automatically disconnected if you don't respond to ping messages
+function handleReceivePing() {
+ ws.send(JSON.stringify({ event: "pong" }));
+}
+
+// Function to handle quote requests
+function handleReceiveQuoteRequest(data, ws) {
+ console.log("Handling quote request:", data);
+ // Add your custom handling logic here
+}
+
+// Function to handle orders
+function handleReceiveOrder(data, ws) {
+ console.log("Handling order:", data);
+ // Add your custom handling logic here
+}
+
+// Function to attempt reconnection
+function reconnect() {
+ console.log("Attempting to reconnect...");
+ setTimeout(() => {
+ ws.close(); // Close any existing connection
+ connectToOrderServer(); // Attempt to reconnect
+ }, reconnectInterval);
+}
+
+// Start listening to the order server
+connectToOrderServer();
+```
+
+### Evaluate Orders
+
+After fetching an order, the solver must thoroughly evaluate it to determine its viability and potential execution. To facilitate this evaluation, several contextual pointers are available within the returned order data. Key aspects to consider include:
+
+1. **Quote Validation**: Use the `OrderDto.quote` field to access the price context, which provides the pricing details for the inputs and outputs of the order. If you trust the order server, you can primarily rely on this quote to validate the order’s pricing. However, it’s crucial to verify that the solver supports the specific origin chain (`OrderDto.order.originChainId`) and output chains (`OrderDto.order.orderData.outputs[...].chainId`) as well as their respective tokens (`input[].token` and `output[].token`). These parameters are guaranteed to be present across all order types.
+
+2. **Solver-Exclusive Orders**: Some orders may initially be restricted to specific solvers. This is indicated by the `OrderDto.order.orderData.verificationContract` field. If this field is defined and not equal to `address(0)`, the order is exclusive to the designated solver until the `slopeStartingTime` elapses, after which the order becomes available for anyone to fulfill.
+
+3. **Mutually Exclusive Orders**: Be aware of potential conflicts between orders. If you encounter two orders with the same `OrderDto.order.swapper` and `OrderDto.order.nonce`, these orders are mutually exclusive, meaning only one of them can be submitted on-chain. This mechanism prevents double submissions and ensures the integrity of the order processing.
+
+Evaluating orders carefully ensures that solvers can accurately determine the feasibility of executing an order, adhere to exclusivity rules, and avoid conflicts, thereby maintaining the integrity and efficiency of the order fulfillment process.
+
+### Initiate Orders
+
+Once an order has been fetched and validated, the next step is to submit it on-chain. Catalyst Orders are accompanied by a signature (`OrderDto.signature`) that serves a dual purpose:
+
+1. **Permit2 Signature**: This signature acts as a Permit2, authorizing the Catalyst contracts to withdraw the submitter's tokens directly. This streamlines the process by eliminating the need for separate approval transactions.
+
+2. **User Authorization**: The signature also confirms that the user has approved the order, ensuring consent and alignment with the terms of execution.
+
+Orders are processed on a first-come, first-served basis, emphasizing the importance of swift submission to secure the desired transaction. By leveraging the Permit2 signature mechanism, Catalyst simplifies the initiation process, reducing overhead and ensuring seamless order execution.
+
+
+
+
+```typescript
+// This tutorial uses ethersjs but you can easily replace it by similar libraries.
+import { ethers } from "ethers";
+
+const reactorAbi = "...";
+const signer = "ethers.signer...";
+
+async function initiateOrder() {
+ // Get an order
+ const orders = await getOrders();
+ const order = orders.orders[0];
+
+ // Define the reactor we will call. You can get the reactor address from the order
+ const reactorAddress = order.order.settlementContract;
+ const reactor = new ethers.Contract(reactorAddress, reactorAbi, signer);
+
+ // TODO: Set approvals for the reactorAddress for all inputs & collateral.
+
+ // The order arrives almost ready to use,
+ // we just need to remove the type from the orderdata.
+ const { type: _, ...cleanedOrderData } = order.order.orderData;
+ const cleanedOrder = { ...order.order, orderData: cleanedOrderData };
+ const signature = order.signature;
+ const fillerData = "0x"; // #custom-fillerdata--underwriting
+
+ // Call the reactor to initiate the order.
+ return reactor.initiate(cleanedOrder, signature, fillerData);
+}
+```
+
+
+
+
+```python
+from web3 import Web3
+
+rpc_url = ""
+web3 = Web3(Web3.HTTPProvider(eth_node_url))
+
+# Your ABI and signer details
+reactor_abi = "..."
+signer_private_key = "your_private_key_here"
+signer_address = web3.eth.account.from_key(signer_private_key).address
+
+def initiate_order():
+ # Get an order
+ orders = get_orders()
+ order = orders['orders'][0]
+
+ # Define the reactor we will call. You can get the reactor address from the order
+ reactor_address = order['order']['settlementContract']
+ reactor = web3.eth.contract(address=reactor_address, abi=reactor_abi)
+
+ # TODO: Set approvals for the reactorAddress for all inputs & collateral.
+ # This will depend on the specific ERC20 tokens you're using,
+ # you need to call approve() on the ERC20 token contracts
+
+ # Clean the order data by removing the type field
+ cleaned_order_data = order['order']['orderData'].copy()
+ cleaned_order_data.pop('type')
+ cleaned_order = {**order['order'], 'orderData': cleaned_order_data}
+ signature = order['signature']
+ filler_data = "0x" # #custom-fillerdata--underwriting
+
+ # Build the transaction
+ txn = reactor.functions.initiate(cleaned_order, signature, filler_data).build_transaction({
+ 'from': signer_address,
+ 'nonce': web3.eth.get_transaction_count(signer_address)
+ })
+ # Sign the transaction
+ signed_txn = web3.eth.account.sign_transaction(txn, private_key=signer_private_key)
+ # Send the transaction
+ tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
+ # Wait for the transaction receipt
+ receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
+ return receipt
+```
+
+
+
+
+#### Custom FillerData & Underwriting
+
+By default, if `fillerData` is not specified, the input assets (provided by the user) are sent directly to the caller. This behavior is generally suitable for most use cases, eliminating the need for additional customization.
+
+However, if there is a need to direct the input assets to another address or to enable underwriting, a customized `fillerData` must be utilized. Currently, only one custom version (`v1`) is supported. The `v1` structure includes:
+
+- **Version Byte (0x01)**: Identifies the custom data version.
+- **fillerAddress**: The address that will receive the input assets and collateral.
+- **orderPurchaseDeadline**: A timestamp that allows an alternative buyer to purchase the order before this time. "Buying" the order in this context means transferring all of the input assets and collateral to the `fillerAddress`.
+- **orderDiscount**: Provides buyers with a discount on the inputs, represented as a fraction of 2^16 - 1. For example, to offer a 1% discount, the value would be calculated as `0.01 * (2^16 - 1) = 655`. This feature is particularly useful for chains with slower block confirmations (e.g., Bitcoin), enabling the solver to be paid after 0-1 confirmations while assuring the user of higher finality (3-6 confirmations).
+
+
+
+
+```typescript
+const fillerDataVersion = "0x01";
+const fillerAddress = "0x....".replace("0x", "");
+// fillerAddress.length === 20*2;
+const orderPurchaseDeadline = Number(1723199919)
+ .toString(16)
+ .padStart("0", 4 * 2);
+//orderPurchaseDeadline.length === 4*2
+const orderDiscount = Math.floor(0.01 * (2 ** 16 - 1))
+ .toString(16)
+ .padStart("0", 2 * 2);
+// orderDiscount.length === 2*2
+
+const fillerData =
+ fillerDataVersion + fillerAddress + orderPurchaseDeadline + orderDiscount;
+```
+
+
+
+
+```python
+fillerDataVersion = "0x01";
+fillerAddress = '0x....'.replace("0x", "");
+# len(fillerAddress) === 20*2;
+orderPurchaseDeadline = hex(1723199919).replace("0x", "").zfill(4*2);
+# len(orderPurchaseDeadline) === 4*2
+const orderDiscount = hex(int(0.01*(2**16-1))).replace("0x", "").zfill(2*2);
+# len(orderDiscount) === 2*2
+
+fillerData = fillerDataVersion + fillerAddress + orderPurchaseDeadline + orderDiscount;
+```
+
+
+
+
+## Delivery
+
+The delivery of assets varies based on the destination type: VM chains or Bitcoin.
+
+### EVM deliveries
+
+For EVM (Ethereum Virtual Machine) chains, you must interact with the specified oracle on the destination chain. Use the following details from the order to make the necessary call:
+
+- **Oracle Address**: Found in `OrderDto.order.orderData.outputs[].remoteOracle`.
+- **Destination Chain**: Identified by `OrderDto.order.orderData.outputs[].chainId`.
+
+
+
+
+```typescript
+// It is assumed you are continuing from the above steps.
+import { ethers } from "ethers";
+
+const oracleAbi = "...";
+
+// The oracle allows filling multiple outputs from different orders
+// in a single transaction. They do have to go to the same chain.
+// For simplicity, this function assumes that all outputs goes to
+// the same chain but it may not be the case.
+async function fillSingleChainOrder(order: CrossChainOrder) {
+ let recordedChain;
+ let recordedOracle;
+ for (const output of order.orderData.outputs) {
+ if (recordedChain === undefined) recordedChain = output.chainId;
+ if (recodedOracle === undefined) recodedOracle = output.remoteOracle;
+ if (recordedChain !== output.chainId)
+ throw Error(
+ `Mixed ChainIds, seen ${recordedChain} and ${output.chainId}`
+ );
+ if (recodedOracle !== output.remoteOracle)
+ throw Error(
+ `Mixed Oracles, seen ${recodedOracle} and ${output.remoteOracle}`
+ );
+ }
+ const oracle = new ethers.Contract(recordedOracle, oracleAbi, signer);
+
+ // TODO: Set approvals for the oracleAddress for the value of the output.
+
+ // We need to provide fill times. These have to be set to proofTime.
+ // These are used to ensure you can't reuse fills.
+ const fillTimes = order.orderData.outputs.map(
+ (_) => order.orderData.proofDeadline
+ );
+
+ // Call the reactor to initiate the order.
+ return oracle.fill(outputs, fillTimes);
+}
+```
+
+
+
+
+```python
+# It is assumed you are continuing from the above steps.
+
+oracleAbi = "...";
+
+# The oracle allows filling multiple outputs from different orders in a single transaction.
+# They do have to go to the same chain.
+# For simplicity, this function assumes that all outputs goes to the same chain but it may not be the case.
+def fill_single_chain_order(order):
+ oracle_address = order['orderData']['remoteOracle']
+
+ recordedChain = "";
+ recordedOracle = "";
+ for (output in order['orderData']['outputs']):
+ if (recordedChain == ""):
+ recordedChain = output.chainId
+ if (recordedOracle == ""):
+ recordedOracle = output.remoteOracle
+ if (recordedChain != output.chainId):
+ raise Exception(f"Mixed ChainIds, seen {recordedChain} and {output.chainId}");
+ if (recordedOracle != output.remoteOracle):
+ raise Exception(f"Mixed Oracles, seen {recordedOracle} and {output.remoteOracle}");
+
+ oracle = web3.eth.contract(address=recordedOracle, abi=oracle_abi)
+
+ # TODO: Set approvals for the oracleAddress for the value of the output.
+
+ # We need to provide fill times. These have to be set to proofTime.
+ # These are used to ensure you can't reuse fills.
+ fillTimes = [order.orderData.proofDeadline for _ in order.orderData.outputs]
+
+ # Build the transaction
+ txn = oracle.functions.fill(order.orderData.outputs, fillTimes).build_transaction({
+ 'from': signer_address,
+ 'nonce': web3.eth.get_transaction_count(signer_address)
+ })
+ # Sign the transaction
+ signed_txn = web3.eth.account.sign_transaction(txn, private_key=signer_private_key)
+ # Send the transaction
+ tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
+ # Wait for the transaction receipt
+ receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
+ return receipt
+```
+
+
+
+
+### Bitcoin Deliveries
+
+To determine whether an order involves a Bitcoin transaction, check the `GetOrderData.order.orderData.outputs[].token` field. If the token indicates Bitcoin, ensure the following conditions are met:
+
+- The first 30 bytes of the token should be `0x000000000000000000000000BC0000000000000000000000000000000000`. The 13th byte is `0xBC`.
+- The 31st byte indicates the number of confirmations required before the order can be verified on-chain. For example:
+ - `0x00` and `0x01` represent 1 confirmation.
+ - `0x02` represents 2 confirmations.
+ - `0x03` represents 3 confirmations, and so on.
+- The 32nd byte contains an address version identifier, which should be decoded as `uint8`.
+
+If the transaction is directed to Bitcoin, the address (`GetOrderData.order.orderData.outputs[].recipient`) will contain a relevant destination hash or witness, not the address itself. This value must be used along with the address version identifier to decode the address.
+
+
+
+
+ Version |
+ Name |
+ Encoding Scheme |
+ Prefix |
+ Hash Length |
+
+
+
+
+ 0 |
+ Unknown |
+ Ignore |
+ |
+ |
+
+
+ 1 |
+ P2PKH |
+ Base58Check(00+PKH) |
+ 1* |
+ 20 |
+
+
+ 2 |
+ P2SH |
+ Base58Check(05+SH) |
+ 3* |
+ 20 |
+
+
+ 3 |
+ P2WPKH |
+ Bech32 |
+ bc1q** |
+ 20 |
+
+
+ 4 |
+ P2WSH |
+ Bech32 |
+ bc1q** |
+ 32 |
+
+
+ 5 |
+ P2TR |
+ Bech32m |
+ bc1p** |
+ 32 |
+
+
+
+\* Prefixes are determined by the encoding scheme.
+\
+\*\* Part of the prefix – 1q/1p – is determined by the encoding scheme.
+
+The following guidelines assume you are implementing this from the perspective of a solver. You need to convert the expected output script into a Bitcoin address that can be used with your wallet:
+
+- **P2PKH (Pay-to-PubKey-Hash)**:
+
+ - The recipient is the public key hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `00`, and encode with Base58Check.
+
+- **P2SH (Pay-to-Script-Hash)**:
+
+ - The recipient is the script hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `05`, and encode with Base58Check.
+
+- **P2WPKH (Pay-to-Witness-PubKey-Hash)**:
+
+ - The recipient is the witness. Encode the first 20 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2WSH (Pay-to-Witness-Script-Hash)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2TR (Pay-to-Taproot)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32m](https://en.bitcoin.it/wiki/BIP_0350#Bech32m). Prepend with `bc1p`.
+
+- **Nested Witness Addresses/Outputs**:
+ - These are **P2SH** addresses and should be treated like any other **P2SH** address.
+
+Once the address is generated, create a Bitcoin transaction with at least one output that **exactly** matches the described output from the initiated order. The transaction can have any number of inputs and outputs, as long as one output precisely matches the one specified by the order's output. This flexibility allows for batch filling, consolidation, and more. While having multiple matching outputs is permitted, it benefits only the user to ensure the order’s conditions are met.
+
+## From Bitcoin
+
+Bitcoin operates differently from VM chains in terms of script functionality. Bitcoin scripts only define spending conditions, meaning that, unlike VM chains, we cannot pull funds from a user after they have signed a message. Consequently, the order flow is reversed, and the solver is responsible for signing the VM order. While it's technically feasible to create unique conditional deposit addresses, most wallets do not support pulling from custom scripts.
+
+### Encoding a Bitcoin Address
+
+Your solver must be capable of generating a Bitcoin deposit address. Although it's possible to use the same address for every order, it is crucial to ensure that each order amount is unique if you choose this approach. It is generally recommended to use different addresses for each order. Bitcoin does not impose additional fees for collecting UTXOs from multiple addresses.
+
+Bitcoin addresses are encoded in two fields: `token` and `address`.
+
+- **`token`**: This field serves to differentiate VM tokens from Bitcoin orders and encode the address version. Refer to the [Bitcoin Deliveries](#bitcoin-deliveries) table for details on converting address types to versions.
+
+- **`address`**: This field encodes the public key hash, script hash, or witness hash. Use the encoding schemes listed in the [Bitcoin Deliveries](#bitcoin-deliveries) table for various address versions. For addresses involving hashes of 20 bytes (P2PKH, P2SH, and P2WPKH), ensure to pad the hashes with zeros on the right side (e.g., `0xabcdef...00000`).
+
+### Quote Open Order (WIP)
+
+For solvers handling BTC to VM conversions, quoting orders for comparison with other solvers is essential. To begin quoting:
+
+1. **Subscription**: Solvers must subscribe to quote requests from the order server.
+2. **Response**: When a quote request is received, the solver must respond with a quote that remains valid for at least 60 seconds. If a signed order is requested within 30 seconds of the quote, the solver should provide a signed order with a lifetime of 30 seconds.
+
+Failure to respond with a signed order that matches or improves upon the quote within the 30-second window may result in blacklisting of the solver.
+
+Ensure your quoting mechanism is robust and timely to maintain competitive standing and avoid potential blacklisting.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CatalystQuoteRequestData {
+ quoteRequestId: string;
+ fromChain: string;
+ toChain: string;
+ fromAsset: string;
+ toAsset: string;
+ expirationTime: string;
+ amount: string;
+}
+
+async function handleReceiveQuoteRequest(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const quote = await getSolverQuote(
+ parsedData.data.fromAsset,
+ parsedData.data.toAsset,
+ parsedData.data.amount
+ );
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-quote",
+ data: {
+ origin: "your_identifier", // required
+ quoteRequestId: parsedData.data.quoteRequestId, // required
+ ...quote,
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error simulating quote:", error);
+ }
+}
+```
+
+### Return Signed Orders (WIP)
+
+Once the Order Server has evaluated all quotes from solvers, it selects the most favorable quote. After the user's deposit is confirmed, the Order Server will request the selected solver to provide a signed order.
+
+### Responsibilities of the Solver:
+
+1. **Provide a Signed Order**: The solver must return a signed order that aligns with the quoted terms and remains valid within the specified expiry period.
+
+2. **Order Exclusivity**: It is crucial that the signed order is exclusive to the user and the Order Server’s executor. This exclusivity ensures that the order cannot be fulfilled by other solvers or reused.
+
+3. **Asset Delivery Assurance**: The Cross Cats Order Server guarantees asset delivery through a Bitcoin address controlled by the Order Server but owned by the user. This setup ensures that the assets are securely delivered as promised.
+
+Ensuring these conditions helps maintain the integrity and efficiency of the order fulfillment process. It is essential that solvers adhere strictly to these requirements to ensure smooth operations and avoid any potential issues.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CrossChainOrder {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchOrderData | LimitOrderData;
+}
+
+export interface QuoteContext {
+ toAsset: string;
+ toPrice: string;
+ discount: string;
+ fromAsset: string;
+ fromPrice: string;
+ intermediary: string;
+}
+
+export interface CatalystOrderData {
+ order: CrossChainOrder;
+ quote: QuoteContext;
+ signature: string;
+}
+
+async function signOrderRequest(signOrderRequest: any): any | undefined {
+ const evaluationResponse = orderEvaluations(signOrderRequest);
+ if (evaluationResponse === undefined) return undefined;
+ ...
+}
+
+async function handleReceiveOrder(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const signedOrder = await signOrderRequest(...);
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-order-signed",
+ data: {
+ origin: "your_identifier", // required
+ ...signedOrder, // TODO: determine shape of signed order
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error signing order:", error);
+ }
+}
+```
+
+Once the signedOrder is sent & it correctly matches the expected quote, the vast vast majority of orders will be filled.
+
+TODO: The Order Server may respond back with a transaction hash.
+
+## Prove Orders (WIP)
+
+If Solvers' orders get challenged, they need to prove delivery. In exchange for these proofs, Solvers collect the collateral provided by Challengers.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Challenger (Has to be moved)
+
+Challengers work to ensure that solvers stay honest. Solvers submit collateral when claiming transaction. If Solvers aren't honest, Challengers can claim the collateral if they prove that Solvers havn't done settled their claimed orders.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Broadcast Orders (Depreciated)
+
+The first step of any order is broadcasting it. For solvers this can be seen as the 0'th step, since it is required before solvers can collect the orders from the order server.
+
+:::tip[Proof of Work]
+Submitting orders can easily be used to DDoS the order server. As a protection, the order server requires submitting a piece of PoW to rate-limit submissions.
+:::
+
+
+
+
+```typescript
+async function submitOrder(): bool {
+ const order: CrossChainorder;
+ const signature: string;
+ const body = {
+ order,
+ signature,
+ };
+ const response = await fecth(API_URL + "orders/submit", {
+ method: "POST",
+ body,
+ });
+ const parsedResponse = (await response.json()) as { success: bool };
+
+ return parsedResponse.success;
+}
+```
+
+
+
+
+```python
+import requests
+import json
+
+API_URL = "https://crosscats-api-staging.catalyst.exchange/"
+
+def submit_order(): bool:
+ order = {}
+ signature = "0x..."
+ data = {
+ "order": order,
+ "signature": signature
+ }
+ response = requests.post(API_URL+ "orders/submit", json=data)
+ fetched_orders = response.json()
+ return fetched_orders.get("success")
+```
+
+
+
diff --git a/src/content/docs/cross-cats/Becoming a Solver/fulfilling-orders.mdx b/src/content/docs/cross-cats/Becoming a Solver/fulfilling-orders.mdx
new file mode 100644
index 0000000..a0ffb6a
--- /dev/null
+++ b/src/content/docs/cross-cats/Becoming a Solver/fulfilling-orders.mdx
@@ -0,0 +1,422 @@
+---
+title: "Fulfilling Orders"
+description: "Cross Cats allows solvers to collect order flow to and from various VM chains and to and from Bitcoin. Compared to competing solution, capital hungry solvers can improve their capital turnaround by using the underwriting network to their advantage."
+sidebar:
+ order: 6
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+The delivery of assets varies based on the destination type: VM chains or Bitcoin.
+
+### EVM deliveries
+
+For EVM (Ethereum Virtual Machine) chains, you must interact with the specified oracle on the destination chain. Use the following details from the order to make the necessary call:
+
+- **Oracle Address**: Found in `OrderDto.order.orderData.outputs[].remoteOracle`.
+- **Destination Chain**: Identified by `OrderDto.order.orderData.outputs[].chainId`.
+
+
+
+
+```typescript
+// It is assumed you are continuing from the above steps.
+import { ethers } from "ethers";
+
+const oracleAbi = "...";
+
+// The oracle allows filling multiple outputs from different orders
+// in a single transaction. They do have to go to the same chain.
+// For simplicity, this function assumes that all outputs goes to
+// the same chain but it may not be the case.
+async function fillSingleChainOrder(order: CrossChainOrder) {
+ let recordedChain;
+ let recordedOracle;
+ for (const output of order.orderData.outputs) {
+ if (recordedChain === undefined) recordedChain = output.chainId;
+ if (recodedOracle === undefined) recodedOracle = output.remoteOracle;
+ if (recordedChain !== output.chainId)
+ throw Error(
+ `Mixed ChainIds, seen ${recordedChain} and ${output.chainId}`
+ );
+ if (recodedOracle !== output.remoteOracle)
+ throw Error(
+ `Mixed Oracles, seen ${recodedOracle} and ${output.remoteOracle}`
+ );
+ }
+ const oracle = new ethers.Contract(recordedOracle, oracleAbi, signer);
+
+ // TODO: Set approvals for the oracleAddress for the value of the output.
+
+ // We need to provide fill times. These have to be set to proofTime.
+ // These are used to ensure you can't reuse fills.
+ const fillTimes = order.orderData.outputs.map(
+ (_) => order.orderData.proofDeadline
+ );
+
+ // Call the reactor to initiate the order.
+ return oracle.fill(outputs, fillTimes);
+}
+```
+
+
+
+
+```python
+# It is assumed you are continuing from the above steps.
+
+oracleAbi = "...";
+
+# The oracle allows filling multiple outputs from different orders in a single transaction.
+# They do have to go to the same chain.
+# For simplicity, this function assumes that all outputs goes to the same chain but it may not be the case.
+def fill_single_chain_order(order):
+ oracle_address = order['orderData']['remoteOracle']
+
+ recordedChain = "";
+ recordedOracle = "";
+ for (output in order['orderData']['outputs']):
+ if (recordedChain == ""):
+ recordedChain = output.chainId
+ if (recordedOracle == ""):
+ recordedOracle = output.remoteOracle
+ if (recordedChain != output.chainId):
+ raise Exception(f"Mixed ChainIds, seen {recordedChain} and {output.chainId}");
+ if (recordedOracle != output.remoteOracle):
+ raise Exception(f"Mixed Oracles, seen {recordedOracle} and {output.remoteOracle}");
+
+ oracle = web3.eth.contract(address=recordedOracle, abi=oracle_abi)
+
+ # TODO: Set approvals for the oracleAddress for the value of the output.
+
+ # We need to provide fill times. These have to be set to proofTime.
+ # These are used to ensure you can't reuse fills.
+ fillTimes = [order.orderData.proofDeadline for _ in order.orderData.outputs]
+
+ # Build the transaction
+ txn = oracle.functions.fill(order.orderData.outputs, fillTimes).build_transaction({
+ 'from': signer_address,
+ 'nonce': web3.eth.get_transaction_count(signer_address)
+ })
+ # Sign the transaction
+ signed_txn = web3.eth.account.sign_transaction(txn, private_key=signer_private_key)
+ # Send the transaction
+ tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
+ # Wait for the transaction receipt
+ receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
+ return receipt
+```
+
+
+
+
+### Bitcoin Deliveries
+
+To determine whether an order involves a Bitcoin transaction, check the `GetOrderData.order.orderData.outputs[].token` field. If the token indicates Bitcoin, ensure the following conditions are met:
+
+- The first 30 bytes of the token should be `0x000000000000000000000000BC0000000000000000000000000000000000`. The 13th byte is `0xBC`.
+- The 31st byte indicates the number of confirmations required before the order can be verified on-chain. For example:
+ - `0x00` and `0x01` represent 1 confirmation.
+ - `0x02` represents 2 confirmations.
+ - `0x03` represents 3 confirmations, and so on.
+- The 32nd byte contains an address version identifier, which should be decoded as `uint8`.
+
+If the transaction is directed to Bitcoin, the address (`GetOrderData.order.orderData.outputs[].recipient`) will contain a relevant destination hash or witness, not the address itself. This value must be used along with the address version identifier to decode the address.
+
+
+
+
+ Version |
+ Name |
+ Encoding Scheme |
+ Prefix |
+ Hash Length |
+
+
+
+
+ 0 |
+ Unknown |
+ Ignore |
+ |
+ |
+
+
+ 1 |
+ P2PKH |
+ Base58Check(00+PKH) |
+ 1* |
+ 20 |
+
+
+ 2 |
+ P2SH |
+ Base58Check(05+SH) |
+ 3* |
+ 20 |
+
+
+ 3 |
+ P2WPKH |
+ Bech32 |
+ bc1q** |
+ 20 |
+
+
+ 4 |
+ P2WSH |
+ Bech32 |
+ bc1q** |
+ 32 |
+
+
+ 5 |
+ P2TR |
+ Bech32m |
+ bc1p** |
+ 32 |
+
+
+
+\* Prefixes are determined by the encoding scheme.
+\
+\*\* Part of the prefix – 1q/1p – is determined by the encoding scheme.
+
+The following guidelines assume you are implementing this from the perspective of a solver. You need to convert the expected output script into a Bitcoin address that can be used with your wallet:
+
+- **P2PKH (Pay-to-PubKey-Hash)**:
+
+ - The recipient is the public key hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `00`, and encode with Base58Check.
+
+- **P2SH (Pay-to-Script-Hash)**:
+
+ - The recipient is the script hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `05`, and encode with Base58Check.
+
+- **P2WPKH (Pay-to-Witness-PubKey-Hash)**:
+
+ - The recipient is the witness. Encode the first 20 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2WSH (Pay-to-Witness-Script-Hash)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2TR (Pay-to-Taproot)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32m](https://en.bitcoin.it/wiki/BIP_0350#Bech32m). Prepend with `bc1p`.
+
+- **Nested Witness Addresses/Outputs**:
+ - These are **P2SH** addresses and should be treated like any other **P2SH** address.
+
+Once the address is generated, create a Bitcoin transaction with at least one output that **exactly** matches the described output from the initiated order. The transaction can have any number of inputs and outputs, as long as one output precisely matches the one specified by the order's output. This flexibility allows for batch filling, consolidation, and more. While having multiple matching outputs is permitted, it benefits only the user to ensure the order’s conditions are met.
+
+## From Bitcoin
+
+Bitcoin operates differently from VM chains in terms of script functionality. Bitcoin scripts only define spending conditions, meaning that, unlike VM chains, we cannot pull funds from a user after they have signed a message. Consequently, the order flow is reversed, and the solver is responsible for signing the VM order. While it's technically feasible to create unique conditional deposit addresses, most wallets do not support pulling from custom scripts.
+
+### Encoding a Bitcoin Address
+
+Your solver must be capable of generating a Bitcoin deposit address. Although it's possible to use the same address for every order, it is crucial to ensure that each order amount is unique if you choose this approach. It is generally recommended to use different addresses for each order. Bitcoin does not impose additional fees for collecting UTXOs from multiple addresses.
+
+Bitcoin addresses are encoded in two fields: `token` and `address`.
+
+- **`token`**: This field serves to differentiate VM tokens from Bitcoin orders and encode the address version. Refer to the [Bitcoin Deliveries](#bitcoin-deliveries) table for details on converting address types to versions.
+
+- **`address`**: This field encodes the public key hash, script hash, or witness hash. Use the encoding schemes listed in the [Bitcoin Deliveries](#bitcoin-deliveries) table for various address versions. For addresses involving hashes of 20 bytes (P2PKH, P2SH, and P2WPKH), ensure to pad the hashes with zeros on the right side (e.g., `0xabcdef...00000`).
+
+### Quote Open Order (WIP)
+
+For solvers handling BTC to VM conversions, quoting orders for comparison with other solvers is essential. To begin quoting:
+
+1. **Subscription**: Solvers must subscribe to quote requests from the order server.
+2. **Response**: When a quote request is received, the solver must respond with a quote that remains valid for at least 60 seconds. If a signed order is requested within 30 seconds of the quote, the solver should provide a signed order with a lifetime of 30 seconds.
+
+Failure to respond with a signed order that matches or improves upon the quote within the 30-second window may result in blacklisting of the solver.
+
+Ensure your quoting mechanism is robust and timely to maintain competitive standing and avoid potential blacklisting.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CatalystQuoteRequestData {
+ quoteRequestId: string;
+ fromChain: string;
+ toChain: string;
+ fromAsset: string;
+ toAsset: string;
+ expirationTime: string;
+ amount: string;
+}
+
+async function handleReceiveQuoteRequest(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const quote = await getSolverQuote(
+ parsedData.data.fromAsset,
+ parsedData.data.toAsset,
+ parsedData.data.amount
+ );
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-quote",
+ data: {
+ origin: "your_identifier", // required
+ quoteRequestId: parsedData.data.quoteRequestId, // required
+ ...quote,
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error simulating quote:", error);
+ }
+}
+```
+
+### Return Signed Orders (WIP)
+
+Once the Order Server has evaluated all quotes from solvers, it selects the most favorable quote. After the user's deposit is confirmed, the Order Server will request the selected solver to provide a signed order.
+
+### Responsibilities of the Solver:
+
+1. **Provide a Signed Order**: The solver must return a signed order that aligns with the quoted terms and remains valid within the specified expiry period.
+
+2. **Order Exclusivity**: It is crucial that the signed order is exclusive to the user and the Order Server’s executor. This exclusivity ensures that the order cannot be fulfilled by other solvers or reused.
+
+3. **Asset Delivery Assurance**: The Cross Cats Order Server guarantees asset delivery through a Bitcoin address controlled by the Order Server but owned by the user. This setup ensures that the assets are securely delivered as promised.
+
+Ensuring these conditions helps maintain the integrity and efficiency of the order fulfillment process. It is essential that solvers adhere strictly to these requirements to ensure smooth operations and avoid any potential issues.
+
+```typescript
+export interface CatalystEvent {
+ event: string;
+ data: T;
+}
+
+export interface CrossChainOrder {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchOrderData | LimitOrderData;
+}
+
+export interface QuoteContext {
+ toAsset: string;
+ toPrice: string;
+ discount: string;
+ fromAsset: string;
+ fromPrice: string;
+ intermediary: string;
+}
+
+export interface CatalystOrderData {
+ order: CrossChainOrder;
+ quote: QuoteContext;
+ signature: string;
+}
+
+async function signOrderRequest(signOrderRequest: any): any | undefined {
+ const evaluationResponse = orderEvaluations(signOrderRequest);
+ if (evaluationResponse === undefined) return undefined;
+ ...
+}
+
+async function handleReceiveOrder(
+ parsedData: CatalystEvent,
+ ws: WebSocket
+) {
+ try {
+ // an example of your custom function that will get the quote
+ const signedOrder = await signOrderRequest(...);
+
+ ws.send(
+ JSON.stringify({
+ event: "solver-order-signed",
+ data: {
+ origin: "your_identifier", // required
+ ...signedOrder, // TODO: determine shape of signed order
+ },
+ })
+ );
+ } catch (error) {
+ console.error("Error signing order:", error);
+ }
+}
+```
+
+Once the signedOrder is sent & it correctly matches the expected quote, the vast vast majority of orders will be filled.
+
+TODO: The Order Server may respond back with a transaction hash.
+
+## Prove Orders (WIP)
+
+If Solvers' orders get challenged, they need to prove delivery. In exchange for these proofs, Solvers collect the collateral provided by Challengers.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Challenger (Has to be moved)
+
+Challengers work to ensure that solvers stay honest. Solvers submit collateral when claiming transaction. If Solvers aren't honest, Challengers can claim the collateral if they prove that Solvers havn't done settled their claimed orders.
+
+Initially, Cata Labs will prove every single order for completeness.
+
+## Broadcast Orders (Depreciated)
+
+The first step of any order is broadcasting it. For solvers this can be seen as the 0'th step, since it is required before solvers can collect the orders from the order server.
+
+:::tip[Proof of Work]
+Submitting orders can easily be used to DDoS the order server. As a protection, the order server requires submitting a piece of PoW to rate-limit submissions.
+:::
+
+
+
+
+```typescript
+async function submitOrder(): bool {
+ const order: CrossChainorder;
+ const signature: string;
+ const body = {
+ order,
+ signature,
+ };
+ const response = await fecth(API_URL + "orders/submit", {
+ method: "POST",
+ body,
+ });
+ const parsedResponse = (await response.json()) as { success: bool };
+
+ return parsedResponse.success;
+}
+```
+
+
+
+
+```python
+import requests
+import json
+
+API_URL = "https://crosscats-api-staging.catalyst.exchange/"
+
+def submit_order(): bool:
+ order = {}
+ signature = "0x..."
+ data = {
+ "order": order,
+ "signature": signature
+ }
+ response = requests.post(API_URL+ "orders/submit", json=data)
+ fetched_orders = response.json()
+ return fetched_orders.get("success")
+```
+
+
+
diff --git a/src/content/docs/cross-cats/Becoming a Solver/introduction.mdx b/src/content/docs/cross-cats/Becoming a Solver/introduction.mdx
new file mode 100644
index 0000000..e9621f0
--- /dev/null
+++ b/src/content/docs/cross-cats/Becoming a Solver/introduction.mdx
@@ -0,0 +1,91 @@
+---
+title: "Introduction to solving"
+description: "Cross Cats allows solvers to collect order flow to and from various VM chains and to and from Bitcoin. Compared to competing solution, capital hungry solvers can improve their capital turnaround by using the underwriting network to their advantage."
+sidebar:
+ order: 3
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+> If you are not interested in the on-chain order structure, skip to [EVM Orders](../evm-orders). For API documentation, refer to the [API Swagger documentation](https://catalyst-order-server-0140d799e2f7.herokuapp.com/api).
+
+Cross Cats utilizes three main order structures:
+
+1. [**CrossChainOrder**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L6-L27) is a generic input order with an ERC-7683 compatible structure. The key component here is orderData, which contains the core functionality and varies significantly across ERC-7683 supporting implementations.
+2. [**ResolvedCrossChainOrder**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L29-L52) provides a quote description, detailing the value of a cross-chain order at a specific point in time. It is also ERC-7683 compliant\*, allowing solvers to efficiently compare the resolution of orders across various protocols.
+3. [**OrderKey**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/Structs.sol#L41-L65) is used to monitor a Catalyst order throughout its lifecycle. It includes Catalyst-specific context and provides an in-depth description of an order.
+
+Below is the generic ERC-7683 CrossChainOrder structure. The `CrossChainOrder.orderData` field is an ABI-encoded order struct.
+
+```solidity
+struct CrossChainOrder {
+ address settlementContract;
+ address swapper;
+ uint256 nonce;
+ uint32 originChainId;
+ uint32 initiateDeadline;
+ uint32 fillDeadline;
+ bytes orderData;
+}
+```
+
+The `orderData` field is uniquely encoded by Cross Cats. Currently, two orderdata structs are supported:
+
+```solidity
+/// @notice Simpler and slightly cheaper for order types with fixed inputs and outputs.
+struct CatalystLimitOrderData {
+ uint32 proofDeadline;
+ uint32 challengeDeadline;
+ address collateralToken;
+ uint256 fillerCollateralAmount;
+ uint256 challengerCollateralAmount;
+ address localOracle;
+ Input[] inputs;
+ OutputDescription[] outputs;
+}
+/// @notice Supports Dutch Auctions on both input and output and support for additional custom order verification.
+struct CatalystDutchOrderData {
+ bytes32 verificationContext;
+ address verificationContract;
+ uint32 proofDeadline;
+ uint32 challengeDeadline;
+ address collateralToken;
+ uint256 fillerCollateralAmount;
+ uint256 challengerCollateralAmount;
+ address localOracle;
+ uint32 slopeStartingTime;
+ /** @dev Input rate of change. */
+ int256[] inputSlopes;
+ /** @dev Output rate of change. */
+ int256[] outputSlopes;
+ Input[] inputs;
+ OutputDescription[] outputs;
+}
+
+// With the input and output structs defined as:
+struct Input {
+ address token;
+ uint256 amount;
+}
+
+struct OutputDescription {
+ /** @dev Contract on the destination that tells whether an order was filled.
+ * Format is bytes32() slice of the encoded bytearray from the messaging protocol.
+ * If local: bytes32(uint256(uint160(address(localOracle)))). */
+ bytes32 remoteOracle;
+ /** @dev The address of the token on the destination chain. */
+ bytes32 token;
+ /** @dev The amount of the token to be sent. */
+ uint256 amount;
+ /** @dev The address to receive the output tokens. */
+ bytes32 recipient;
+ /** @dev The destination chain for this output. */
+ uint32 chainId;
+ /** @dev Additional data that is relevant for the caller. */
+ bytes remoteCall;
+}
+```
+
+Users generate a `CrossChainOrder` with the appropriate order data and sign it as a Permit2 witness, thereby approving both the order description and its associated inputs with a single signature. The signed struct will be a new structure where `orderData` is an ABI-encoded order type.
+
+Cross Cats has directionality. That means the ways orders are initiated depends on the initiating chain (where the user is swapping out of). In the current iteration, there are 2 important origin types: EVM and Bitcoin. In the future, all virtual machine chains (including EVM) will generally be initiated similarly and all non-VM chains (including Bitcoin) will be initiated similarly but different from VM chains.
diff --git a/src/content/docs/cross-cats/solver.mdx b/src/content/docs/cross-cats/Becoming a Solver/solver copy.mdx
similarity index 82%
rename from src/content/docs/cross-cats/solver.mdx
rename to src/content/docs/cross-cats/Becoming a Solver/solver copy.mdx
index 2a2673c..2ed69a4 100644
--- a/src/content/docs/cross-cats/solver.mdx
+++ b/src/content/docs/cross-cats/Becoming a Solver/solver copy.mdx
@@ -6,19 +6,18 @@ sidebar:
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
-import GetQuotes from "../../../components/solver/GetQuotes.svelte";
## Introduction
-> If you aren't interested in the order structure in Solidity, skip to [From VM](#from-evm). For API documentation, refer to the [API Swagger documentation](https://catalyst-order-server-0140d799e2f7.herokuapp.com/api).
+> If you are not interested in the on-chain order structure, skip to [From VM](#from-evm). For API documentation, refer to the [API Swagger documentation](https://catalyst-order-server-0140d799e2f7.herokuapp.com/api).
Cross Cats utilizes three main order structures:
-1. [CrossChainOrder](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L6-L27) This is a generic input order with an ERC-7683 compatible structure. The key component here is orderData, which contains the core functionality and varies significantly across ERC-7683 supporting implementations.
-2. [ResolvedCrossChainOrder](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L29-L52) This structure provides a quote description, detailing the value of a cross-chain order at a specific point in time. It is also ERC-7683 compliant\*, allowing solvers to efficiently compare the resolution of orders across various protocols.
-3. [OrderKey](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/Structs.sol#L41-L65) These are used to monitor a Catalyst order throughout its lifecycle. It includes Catalyst-specific context and provides an in-depth description of the orders.
+1. [**CrossChainOrder**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L6-L27) is a generic input order with an ERC-7683 compatible structure. The key component here is orderData, which contains the core functionality and varies significantly across ERC-7683 supporting implementations.
+2. [**ResolvedCrossChainOrder**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/ISettlementContract.sol#L29-L52) provides a quote description, detailing the value of a cross-chain order at a specific point in time. It is also ERC-7683 compliant\*, allowing solvers to efficiently compare the resolution of orders across various protocols.
+3. [**OrderKey**](https://github.com/catalystdao/cross-cats/blob/7e07281eef10ffadc10f9f75eb42d1c2419224ca/src/interfaces/Structs.sol#L41-L65) is used to monitor a Catalyst order throughout its lifecycle. It includes Catalyst-specific context and provides an in-depth description of an order.
-> Below is the generic ERC-7683 CrossChainOrder structure. The `CrossChainOrder.orderData` field is an ABI-encoded order struct.
+Below is the generic ERC-7683 CrossChainOrder structure. The `CrossChainOrder.orderData` field is an ABI-encoded order struct.
```solidity
struct CrossChainOrder {
@@ -32,11 +31,11 @@ struct CrossChainOrder {
}
```
-The `orderData` field is uniquely encoded by Cross Cats. Currently, two order structs are supported:
+The `orderData` field is uniquely encoded by Cross Cats. Currently, two orderdata structs are supported:
```solidity
/// @notice Simpler and slightly cheaper for order types with fixed inputs and outputs.
-struct LimitOrderData {
+struct CatalystLimitOrderData {
uint32 proofDeadline;
uint32 challengeDeadline;
address collateralToken;
@@ -47,7 +46,7 @@ struct LimitOrderData {
OutputDescription[] outputs;
}
/// @notice Supports Dutch Auctions on both input and output and support for additional custom order verification.
-struct DutchOrderData {
+struct CatalystDutchOrderData {
bytes32 verificationContext;
address verificationContract;
uint32 proofDeadline;
@@ -57,9 +56,9 @@ struct DutchOrderData {
uint256 challengerCollateralAmount;
address localOracle;
uint32 slopeStartingTime;
- /// @dev Input rate of change.
+ /** @dev Input rate of change. */
int256[] inputSlopes;
- /// @dev Output rate of change.
+ /** @dev Output rate of change. */
int256[] outputSlopes;
Input[] inputs;
OutputDescription[] outputs;
@@ -72,150 +71,146 @@ struct Input {
}
struct OutputDescription {
- /// @dev Contract on the destination that tells whether an order was filled.
- /// Format is bytes32() slice of the encoded bytearray from the messaging protocol (or bytes32(0) if local)
+ /** @dev Contract on the destination that tells whether an order was filled.
+ * Format is bytes32() slice of the encoded bytearray from the messaging protocol.
+ * If local: bytes32(uint256(uint160(address(localOracle)))). */
bytes32 remoteOracle;
- /// @dev The address of the ERC20 token on the destination chain
- /// @dev address(0) used as a sentinel for the native token
+ /** @dev The address of the token on the destination chain. */
bytes32 token;
- /// @dev The amount of the token to be sent
+ /** @dev The amount of the token to be sent. */
uint256 amount;
- /// @dev The address to receive the output tokens
+ /** @dev The address to receive the output tokens. */
bytes32 recipient;
- /// @dev The destination chain for this output
+ /** @dev The destination chain for this output. */
uint32 chainId;
+ /** @dev Additional data that is relevant for the caller. */
bytes remoteCall;
}
```
-Users generate a `CrossChainOrder` with the appropriate order data and sign it using Permit2, thereby creating an approval and order description with a single signature. The signed struct will be a new structure where `orderData` is an ABI-encoded order type.
+Users generate a `CrossChainOrder` with the appropriate order data and sign it as a Permit2 witness, thereby approving both the order description and its associated inputs with a single signature. The signed struct will be a new structure where `orderData` is an ABI-encoded order type.
## From EVM
Cross Cats has directionality. That means the ways orders are initiated depends on the initiating chain (where the user is swapping out of). In the current iteration, there are 2 important origin types: EVM and Bitcoin. In the future, all virtual machine chains (including EVM) will generally be initiated similarly and all non-VM chains (including Bitcoin) will be initiated similarly but different from VM chains.
-On VM chains, assets can be pulled from users via approvals—a feature not available on non-VM chains
-As a result, the sequence of operations differs slightly.
-This section outlines how orders are handled when originating from a VM chain, while the section [From Bitcoin](#from-bitcoin) covers orders originating from Bitcoin.
+On VM chains, assets can be pulled from users via approvals – a feature not available on non-VM chains – instead the sequence of operation has been modified to allow the user to push assets to the solver. This section outlines how orders are handled when originating from a VM chain, while the section [From Bitcoin](#from-bitcoin) covers orders originating from Bitcoin.
-### Get Orders
+### Collecting Orders
-The order server stores orders in a dictionary format, allowing for straightforward parsing by integrators and providing a high level of transparency in the implementation.
+The order server stores orders in a dictionary format, allowing for straightforward parsing by integrators and provides a high level of transparency in the implementation.
-
-
-```typescript
-// The type parameter {}.type should not be submitted on-chain but can be used to differentiate order types.
-type DutchAuctionData = {
- type: "DutchAuction"; // Not to be submitted
- verificationContext: string;
- verificationContract: string;
- proofDeadline: number;
- challengeDeadline: number;
- collateralToken: string;
- fillerCollateralAmount: string;
- challengerCollateralAmount: string;
- localOracle: string;
- slopeStartingTime: number;
- inputSlopes: string[];
- outputSlopes: string[];
- inputs: {}[];
- outputs: {}[];
-};
-
-type LimitOrderData = {
- type: "LimitOrder"; // Not to be submitted
- proofDeadline: number;
- challengeDeadline: number;
- collateralToken: string;
- fillerCollateralAmount: string;
- challengerCollateralAmount: string;
- localOracle: string;
- inputs: {}[];
- outputs: {}[];
-};
-
-// With the CrossChainOrder defined as such:
-type CrossChainOrder = {
- settlementContract: string;
- swapper: string;
- nonce: string;
- originChainId: number;
- initiateDeadline: number;
- fillDeadline: number;
- orderData: DutchAuctionData | LimitOrderData;
-};
+
+
+ ```typescript
+ // The type parameter (DutchAuctionData.type) should not be submitted on-chain but can be used to differentiate order types.
+ type DutchAuctionData = {
+ type: "DutchAuction"; // Not to be submitted
+ verificationContext: string;
+ verificationContract: string;
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ slopeStartingTime: number;
+ inputSlopes: string[];
+ outputSlopes: string[];
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ type LimitOrderData = {
+ type: "LimitOrder"; // Not to be submitted
+ proofDeadline: number;
+ challengeDeadline: number;
+ collateralToken: string;
+ fillerCollateralAmount: string;
+ challengerCollateralAmount: string;
+ localOracle: string;
+ inputs: {}[];
+ outputs: {}[];
+ };
+
+ // With the CrossChainOrder defined as such:
+ type CrossChainOrder = {
+ settlementContract: string;
+ swapper: string;
+ nonce: string;
+ originChainId: number;
+ initiateDeadline: number;
+ fillDeadline: number;
+ orderData: DutchAuctionData | LimitOrderData;
+ };
+
+ type OrderDto = {
+ order: CrossChainOrder;
+ quote: {
+ fromAsset: string;
+ toAsset: string;
+ toPrice: string;
+ fromPrice: string;
+ intermediary: "USD" | "EUR" | "BTC" | string; // explicit string types here are examples.
+ discount: string;
+ };
+ signature: string;
+ submitTime: number;
+ };
+
+ type PaginationMeta = {
+ total: number;
+ limit: number;
+ offset: number;
+ };
+
+ type GetOrdersResponse = {
+ data: OrderDto[];
+ pagination: PaginationMeta;
+ };
+
+ async function getOrders(): GetOrdersResponse {
+ const API_URI = "https://crosscats-api-staging.catalyst.exchange";
+ const API_KEY = "your_api_key_here";
+
+ const orderServerResponse = await fetch(
+ API_URI + "/orders",
+ {
+ headers: {
+ "x-api-key": API_KEY,
+ Accept: "application/json",
+ },
+ }
+ );
+ if (!orderServerResponse.ok) {
+ throw new Error(`HTTP error! status: ${orderServerResponse.status}`);
+ }
-type OrderDto = {
- order: CrossChainOrder;
- quote: {
- fromAsset: string;
- toAsset: string;
- toPrice: string;
- fromPrice: string;
- intermediary: "USD" | "EUR" | "BTC" | string; // explicit string types here are examples.
- discount: string;
- };
- signature: string;
- submitTime: number;
-};
-
-type PaginationMeta = {
- total: number;
- limit: number;
- offset: number;
-};
-
-type GetOrdersResponse = {
- data: OrderDto[];
- pagination: PaginationMeta;
-};
-
-async function getOrders(): GetOrdersResponse {
- const API_URI = "https://crosscats-api-staging.catalyst.exchange";
- const API_KEY = "your_api_key_here";
-
- const orderServerResponse = await fetch(
- API_URI + "/orders",
- {
- headers: {
- "x-api-key": API_KEY,
- Accept: "application/json",
- },
+ const fetchedOrders = await orderServerResponse.json();
+ return fetchedOrders;
}
- );
- if (!orderServerResponse.ok) {
- throw new Error(`HTTP error! status: ${orderServerResponse.status}`);
- }
+ ```
- const fetchedOrders = await orderServerResponse.json();
- return fetchedOrders;
-}
-```
+
+
-
-
+ ```python
+ import requests
-```python
-import requests
-
-def get_orders():
- response = requests.get(API_URL + "orders/")
- fetched_orders = response.json()
- return fetched_orders
-```
+ def get_orders():
+ response = requests.get(API_URL + "orders/")
+ fetched_orders = response.json()
+ return fetched_orders
+ ```
-
+
-{/* */}
-
#### Subscribe to orders
-This section outlines how to subscribe to new orders using a WebSocket connection, allowing for real-time updates without the need to continuously poll the order server. By leveraging a WebSocket, the Catalyst order server broadcasts new orders as they arrive, offering a significant reduction in latency but at the cost of increased complexity due to the need for a persistent connection and local filtering of incoming data.
+Subscribing to orders allow you to listen directly to the order flow. This section outlines how to subscribe to new orders using a WebSocket connection, allowing for real-time updates without the need to continuously poll the order server. By leveraging a WebSocket, the Catalyst order server broadcasts new orders as they arrive, offering a significant reduction in latency but at the cost of increased complexity due to the need for a persistent connection and local filtering of incoming data.
-How It Works
Instead of polling for new orders, you can establish a WebSocket connection to the Catalyst order server. The server provides a WebSocket endpoint that pushes new order data to subscribers in real time. However, unlike polling, the data received through WebSocket is not pre-filtered. This means every order event will be pushed to your application, and it’s up to your implementation to manage and filter these events locally.
Below is a simplified implementation in pure JavaScript that demonstrates how to connect to the WebSocket server, handle incoming messages, respond to ping events, and automatically attempt to reconnect if the connection is lost.
diff --git a/src/content/docs/cross-cats/ERC7683.md b/src/content/docs/cross-cats/ERC7683.md
index b9d2fc9..ca93196 100644
--- a/src/content/docs/cross-cats/ERC7683.md
+++ b/src/content/docs/cross-cats/ERC7683.md
@@ -2,7 +2,7 @@
title: "ERC7863 Compatibility"
description: "Cross Cats is ERC7863 compatible with a few non-breaking changes."
sidebar:
- order: 2
+ order: 1000
---
Catalyst is [ERC-7683](https://eips.ethereum.org/EIPS/eip-7683)* compatible. *The implementation differs in 2 ways:
diff --git a/src/content/docs/cross-cats/bitcoin-primer.mdx b/src/content/docs/cross-cats/bitcoin-primer.mdx
new file mode 100644
index 0000000..106d05c
--- /dev/null
+++ b/src/content/docs/cross-cats/bitcoin-primer.mdx
@@ -0,0 +1,145 @@
+---
+title: "Bitcoin Primer"
+description: "What is Bitcoin? What is a Bitcoin Block? What is a Bitcoin transaction? And how do Cross Cats prove Bitcoin fulfillments? This page contains all information required to understand how Cross Cats interact with BTC."
+sidebar:
+ order: 2
+---
+
+Bitcoin is regarded as the origin blockchain or cryptocurrency. While the last core upgrade to the Bitcoin network was in 2010 several smaller upgrades have been made adjacent to the core protocol, the most significant of these are [Segwit](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) and [Taproot](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki).
+
+### Bitcoin Blocks
+
+A Bitcoin block can be thought of as a container attesting to a validity of several transactions. It starts with `0xD9B4BEF9` and then continues describing the new state of the network, including listing all newly included transaction. The Bitcoin block also describes a block header. The block header is a self-contained description of the newly added state. If you only cared about a subset of all transaction in a block, the block header is a more efficient description of the block itself.
+
+For the purpose of validating transactions outside the _noise_ of the core network, block headers are perfect. Satoshi Nakamoto designed block headers to be self describing, that is, if you have a list of block headers it is possible to verify if a new block header belongs to the list. A block header is 80 bytes and consist of:
+
+`Version(4B) | PrevBlock(32B) | MerkleRoot(32B) | Time(4B) | Bits(4B) | Nonce(4B)`
+> https://en.bitcoin.it/wiki/Block_hashing_algorithm
+
+By checking if the hash of the bitcoin hash is sufficiently _low_ compared to the specified `Bits`, the header can be authenticated to be correctly mined. By checking if `PrevBlock` is the same hash as the leading transaction in your list, it can be verified to extend your list. Lastly, `Bits` has to be checked if it follows the difficulty rules.
+
+You will have noticed that these checks do not assert any validity whether the included transactions within are valid. The performed checks can be viewed as the least amount of work required to authenticate a Bitcoin block. This technique is very fittingly called Simplified Payment Validation.
+
+### Bitcoin Transaction
+
+This section has not been written yet.
+
+#### Bitcoin Outputs:
+
+Transaction Outputs contains the spending conditions written in Bitcoin Script. Legacy transaction contains the entirety of the spending condition within the transaction itself while Segwit transaction places the spending condition in the witness and only stores the hash of it in the output. The Bitcoin blockchain itself has no concept of addresses, instead output scripts have been standardized into 7 defined transaction types with 5 still in general use today. P2PK and P2MS are generally not used.
+
+While non-standard scripts may be spendable by a user's privatekey they are unlikely to be recognised by their wallet wallet. Additionally, most custom scripts are implemented through P2SH to allow wallets to pay into it.
+
+To determine whether an order involves a Bitcoin transaction, check the `GetOrderData.order.orderData.outputs[].token` field. If the token indicates Bitcoin, ensure the following conditions are met:
+
+- The first 30 bytes of the token should be `0x000000000000000000000000BC0000000000000000000000000000000000`. The 13th byte is `0xBC`.
+- The 31st byte indicates the number of confirmations required before the order can be verified on-chain. For example:
+ - `0x00` and `0x01` represent 1 confirmation.
+ - `0x02` represents 2 confirmations.
+ - `0x03` represents 3 confirmations, and so on.
+- The 32nd byte contains an address version identifier, which should be decoded as `uint8`.
+
+If the transaction is directed to Bitcoin, the address (`GetOrderData.order.orderData.outputs[].recipient`) will contain a relevant destination hash or witness, not the address itself. This value must be used along with the address version identifier to decode the address.
+
+
+
+
+ Version |
+ Name |
+ Encoding Scheme |
+ Prefix |
+ Hash Length |
+
+
+
+
+ 0 |
+ Unknown |
+ Ignore |
+ |
+ |
+
+
+ 1 |
+ P2PKH |
+ Base58Check(00+PKH) |
+ 1* |
+ 20 |
+
+
+ 2 |
+ P2SH |
+ Base58Check(05+SH) |
+ 3* |
+ 20 |
+
+
+ 3 |
+ P2WPKH |
+ Bech32 |
+ bc1q** |
+ 20 |
+
+
+ 4 |
+ P2WSH |
+ Bech32 |
+ bc1q** |
+ 32 |
+
+
+ 5 |
+ P2TR |
+ Bech32m |
+ bc1p** |
+ 32 |
+
+
+
+\* Prefixes are determined by the encoding scheme.
+\
+\*\* Part of the prefix – 1q/1p – is determined by the encoding scheme.
+
+The following guidelines assume you are implementing this from the perspective of a solver. You need to convert the expected output script into a Bitcoin address that can be used with your wallet:
+
+- **P2PKH (Pay-to-PubKey-Hash)**:
+
+ - The recipient is the public key hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `00`, and encode with Base58Check.
+
+- **P2SH (Pay-to-Script-Hash)**:
+
+ - The recipient is the script hash. Encode the first 20 bytes with [Base58Check](https://rosettacode.org/wiki/Base58Check_encoding). Prepend with `05`, and encode with Base58Check.
+
+- **P2WPKH (Pay-to-Witness-PubKey-Hash)**:
+
+ - The recipient is the witness. Encode the first 20 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2WSH (Pay-to-Witness-Script-Hash)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32](https://github.com/bitcoinjs/bech32). Prepend with `bc1q`.
+
+- **P2TR (Pay-to-Taproot)**:
+
+ - The recipient is the witness hash. Encode the first 32 bytes with [Bech32m](https://en.bitcoin.it/wiki/BIP_0350#Bech32m). Prepend with `bc1p`.
+
+- **Nested Witness Addresses/Outputs**:
+ - These are **P2SH** addresses and should be treated like any other **P2SH** address.
+
+Once the address is generated, create a Bitcoin transaction with at least one output that **exactly** matches the described output from the initiated order. The transaction can have any number of inputs and outputs, as long as one output precisely matches the one specified by the order's output. This flexibility allows for batch filling, consolidation, and more. While having multiple matching outputs is permitted, it benefits only the user to ensure the order’s conditions are met.
+
+#### Bitcoin Inputs
+
+
+## From Bitcoin
+
+Bitcoin operates differently from VM chains in terms of script functionality. Bitcoin scripts only define spending conditions, meaning that, unlike VM chains, we cannot pull funds from a user after they have signed a message. Consequently, the order flow is reversed, and the solver is responsible for signing the VM order. While it's technically feasible to create unique conditional deposit addresses, most wallets do not support pulling from custom scripts.
+
+### Encoding a Bitcoin Address
+
+Your solver must be capable of generating a Bitcoin deposit address. Although it's possible to use the same address for every order, it is crucial to ensure that each order amount is unique if you choose this approach. It is generally recommended to use different addresses for each order. Bitcoin does not impose additional fees for collecting UTXOs from multiple addresses.
+
+Bitcoin addresses are encoded in two fields: `token` and `address`.
+
+- **`token`**: This field serves to differentiate VM tokens from Bitcoin orders and encode the address version. Refer to the [Bitcoin Deliveries](#bitcoin-deliveries) table for details on converting address types to versions.
+
+- **`address`**: This field encodes the public key hash, script hash, or witness hash. Use the encoding schemes listed in the [Bitcoin Deliveries](#bitcoin-deliveries) table for various address versions. For addresses involving hashes of 20 bytes (P2PKH, P2SH, and P2WPKH), ensure to pad the hashes with zeros on the right side (e.g., `0xabcdef...00000`).
diff --git a/src/content/docs/cross-cats/cross-cats.md b/src/content/docs/cross-cats/cross-cats.md
deleted file mode 100644
index 341601c..0000000
--- a/src/content/docs/cross-cats/cross-cats.md
+++ /dev/null
@@ -1,303 +0,0 @@
----
-title: "Cross Cats: Solving Liquidity x Bitcoin"
-description: "Catalyst v2 will support Bitcoin swaps. This is facilitated through intent based swaps that are verified through an on-chain Bitcoin SPV client. This provides: Fast settlement, Competitive rates, and near full security."
-sidebar:
- order: 1
----
-
-Cross-Cats is an intent-based cross-chain swap protocol built with flexibility in mind. The core idea is to allow anyone to create a request for anything provable. At launch, EVM settlements and Bitcoin transactions will be part of the provable set. Bitcoin transactions are proven using a Bitcoin SPV (light client) and allows VM chain payments to be conditional on Bitcoin transactions.
-
-
-```d2 animateInterval=5000
-style.fill: transparent
-direction: right
-
-vars: {
- default-opacity: 0.1
-}
-
-title: System Overview {
- near: top-center
- shape: text
- style: {
- font-size: 40
- }
-}
-
-Order Server: {shape: cloud}
-
-Source Chain: {
- User: {
- shape: person
- }
- Solver: {
- style.fill-pattern: none
- }
- Reactor: {shape: page}
- Oracle: {
- style.opacity: ${default-opacity}
- shape: page
- }
- Challenger: {style.opacity: ${default-opacity}}
- SPV: {
- style.opacity: ${default-opacity}
- shape: page
- }
-
- Solver -> Reactor: 3. resolve() => Quote
- Solver -> Reactor: 4.1. initiate()
- User -> Reactor: 4.2. Tokens
-
- # Optimistic Payout
- Solver <-> Reactor: O.6.1. optimisticPayout() {style.opacity: ${default-opacity}}
-
- # Challenge Parts
- Challenger -> Reactor: C.6.1. dispute() {style.opacity: ${default-opacity}}
-
- # - Challenged Rejected (VM)
- Oracle -> Reactor: C.9.2. proveOrderFulfilment() {style.opacity: ${default-opacity}}
- Reactor -> Solver: C.9.3. payout Tokens {style.opacity: ${default-opacity}}
-
- # - Challenged Rejected (Bitcoin)
- Solver -> SPV: C.7.1. verify() {style.opacity: ${default-opacity}}
- SPV -> Reactor: C.7.2. proveOrderFulfilment() {style.opacity: ${default-opacity}}
- Reactor -> Solver: C.7.3. payout Tokens {style.opacity: ${default-opacity}}
-
- # - Challenged Accepted
- Challenger -> Reactor: C.7.1. completeDispute() {style.opacity: ${default-opacity}}
- Reactor -> Challenger: C.7.2. Reward {style.opacity: ${default-opacity}}
- Reactor -> User: C.7.3. Tokens {style.opacity: ${default-opacity}}
-}
-
-Source Chain.User -> Order Server: 1. Sign Message {style.animated: false}
-Order Server -> Source Chain.Solver: 2. Signed Orders
-
-Destination Chain: {
- Oracle: {
- shape: page
- }
- Solver
- SPV: {
- style.opacity: ${default-opacity}
- shape: page
- }
- User: {
- shape: person
- }
-
- # Ordinary
- Solver -> Oracle: 5.1. fill()
- Solver -> User: 5.2. Tokens
-
- # Challenged submit proof (VM)
- Solver -> Oracle: C.7.1. submit() {style.opacity: ${default-opacity}}
-
- # Challenged submit proof (Bitcoin)
- Solver -> SPV: C.7.1: verify() {style.opacity: ${default-opacity}}
- SPV -> Oracle: C.7.2. Send fill details {style.opacity: ${default-opacity}}
-}
-
-Bitcoin: {
- Solver: {
- style.opacity: ${default-opacity}
- }
- User: {
- shape: person
- style.opacity: ${default-opacity}
- }
-
- Solver -> User: 5.1 transfer {style.opacity: ${default-opacity}}
- style.opacity: ${default-opacity}
-}
-
-Bitcoin -> Destination Chain.SPV: Bitcoin Headers {
- style.opacity: ${default-opacity}
- style.stroke-dash: 4
-}
-Bitcoin -> Source Chain.SPV: Bitcoin Headers {
- style.opacity: ${default-opacity}
- style.stroke-dash: 4
-}
-
-Proof: "AMB\n" {
- shape: cloud
- style.opacity: ${default-opacity}
-}
-
-Destination Chain.Oracle -> Proof: C.8.1. Collect event {style.opacity: ${default-opacity}}
-Proof -> Source Chain.Oracle: C.9.1. receiveMessage() {style.opacity: ${default-opacity}}
-
-scenarios: {
- optimistic resolution: {
- title.label: Optimistic Resolution
- Source Chain: {
- (Solver <-> Reactor)[0].style.opacity: 1
-
- (Solver <-> Reactor)[0].style.stroke-width: 3
- }
- }
- challenged: {
- title.label: Challenged
- Source Chain.Challenger.style.opacity: 1
- Source Chain.(Challenger -> Reactor)[0].style.opacity: 1
- Source Chain.(Challenger -> Reactor)[0]: C.6.1. dispute()
-
- Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 3
-
- scenarios: {
- challenged uncontested: {
- title.label: Challenged Uncontested
- Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
- Source Chain.(Challenger -> Reactor)[1].style.opacity: 1
- Source Chain.(Challenger -> Reactor)[1]: C.7.1. completeDispute()
-
- Source Chain.(Challenger -> Reactor)[1].style.stroke-width: 3
- Source Chain.(Reactor -> Challenger)[0].style.opacity: 1
- Source Chain.(Reactor -> User)[0].style.opacity: 1
- Destination Chain.style.opacity: ${default-opacity}
- Destination Chain.*.style.opacity: ${default-opacity}
- Destination Chain.(* -> *)[*].style.opacity: ${default-opacity}
- }
- order fill proven (VM): {
- title.label: Order Fill Proven (VM)
- Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
-
- Destination Chain.(Solver -> Oracle)[1].style.opacity: 1
- Destination Chain.(Solver -> Oracle)[1].style.stroke-width: 3
- Destination Chain.Oracle.style.opacity: 1
- Proof.style.opacity: 1
- (Destination Chain.Oracle -> Proof)[0].style.opacity: 1
- (Proof -> Source Chain.oracle)[0].style.opacity: 1
- Source Chain.Oracle.style.opacity: 1
- Source Chain.(Oracle -> Reactor)[0].style.opacity: 1
- Source Chain.(Reactor -> Solver)[0].style.opacity: 1
- }
- order fill proven, (BTC): {
- Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
- Destination Chain.style.opacity: ${default-opacity}
- Destination Chain.*.style.opacity: ${default-opacity}
- Destination Chain.(* -> *)[*].style.opacity: ${default-opacity}
- Bitcoin.style.opacity: 1
- Bitcoin.*.style.opacity: 1
- Bitcoin.(* -> *)[*].style.opacity: 1
-
- scenarios: {
- local SPV client: {
- title.label: Local SPV Client
- (Bitcoin -> Source Chain.SPV)[0].style.opacity: 1
- Source Chain.SPV.style.opacity: 1
-
- Source Chain.(Solver -> SPV)[0].style.opacity: 1
- Source Chain.(Solver -> SPV)[0].style.stroke-width: 3
- Source Chain.(SPV -> Reactor)[0].style.opacity: 1
- Source Chain.(Reactor -> Solver)[1].style.opacity: 1
- }
- remote SPV client: {
- title.label: Remote SPV Client
- (Bitcoin -> Destination Chain.SPV)[0].style.opacity: 1
- Destination Chain.SPV.style.opacity: 1
-
- Destination Chain.style.opacity: 1
- Destination Chain.Solver.style.opacity: 1
- Destination Chain.(Solver -> SPV)[0].style.opacity: 1
- Destination Chain.SPV.style.opacity: 1
-
- Destination Chain.Oracle.style.opacity: 1
- Destination Chain.(SPV -> Oracle)[0].style.opacity: 1
- (Destination Chain.Oracle -> Proof)[0].style.opacity: 1
-
- Proof.style.opacity: 1
- Source Chain.Oracle.style.opacity: 1
- (Proof -> Source Chain.Oracle)[0].style.opacity: 1
- Source Chain.(Oracle -> Reactor)[0].style.opacity: 1
- Source Chain.(Reactor -> Solver)[0].style.opacity: 1
- }
- }
- }
- }
- }
-}
-```
-The above diagram animates between all system states. Reloading the page or opening the SVG in a separate tab may be required for the animation to show.
-
-## System Overview
-
-Cross-Cats is designed for cross-chain intents. Never the less, the majorty of the logic exists on the source chain. **Source Chain** here refers to the chain where the order was **Initiated** by a solver. **Remote Chain** refers to the chain (or chains) where proofs are sent from. This includes asset deliveries.
-
-### Initiation (order claim)
-
-An order is initiated by the user signing an order description. An example of an order description is _My 1 Ether (Ethereum) for 3000 USD (Base)_. The signed order is a permit2 witness allowing the solver to submit the order to the Reactor and collect the **input** (1 Ether) from the user. Importantly, during this step some collateral is collected from the solver. This ensures the solver has some skin in the game and settles the order. The collateral is paid back when the input is released to the solver.
-
-To improve the user and solver experience, an order server sits between the user and solver and aids with order validation, propagation, and quoting.
-
-### Output Payment (to user)
-
-The payment pathway depends on the order intent. For a **VM to VM** swap, the solver calls the oracle contract on the destination chain which sends & records the token payment to the user. For **VM to Bitcoin** swap, the solver makes the payment described in the order. In other words, make a Bitcoin transaction that has **a** TXO that matches the order description. If needed, the payment can then be validated using an SPV client.
-
-### Input Payment (to solver)
-
-Cross Cats has 3 payment release schemes to optimise the solver experience.
-
-1. Optimistic resolution. This assumes that the resolver delivered the payment to the user. After a dispute window (configured by the user), the payment will be released. If the order is disputed the operation falls back to option 2.
-2. Explicit validation. At anytime, orders can be proven. This requires that someone send the proof from the remote chains to the source chain. This is more costly than optimistic resolution but may be significantly faster than optimistic resolution. Additionally, through batch verification the cost can be reduced at a slight increase in verification speed.
-3. Underwriting. The last release scheme isn't a payment proof scheme as much as it is a responsibility delegration scheme. If configured, an order can be bought by someone else at any point priorer to the release of the input (proof / fraud). This allows the initial solver to immediately get their capital back and hand off the payment validation to a third party.
-
-
-By using these 3 in conjunction with each other, solvers only have to lock liquidity for a small period of time while not sacrificing any system security. At the same time, speed, security, and cost can be rebalanced based on the specific needs of a user or chain conditions.
-
-## Bitcoin & Pseudo Solving
-
-VM to Bitcoin swaps are relatively straight forward:
-1. User signs a message stating the input assets.
-2. Solver claims the order, inputs assets are automatically collected.
-3. Solver delivers assets
-4. Solver is paid.
-
-However this flow breaks on step 2 when the user wants to go from Bitcoin to VM. (sell Bitcoin). There is no way to pull assets from a user. To solve this issue, the user becomes a **pseudo solver** & relies on release scheme 3. Pseudo solving works by asking solvers for short-lived Bitcoin short quotes. These orders are after validation & selection signed by the solver. The user then quickly claims the order.
-
-Say the user wants to swap 1 Bitcoin for 50000 USDC. Using the order server, they need to collect & claim a signed order of the opposite: 50000 USDC for 1 Bitcoin. Once this order is filled, they get the input (50000 USDC) which matches their desire.
-
-Important to notice, this adds a delay between when the price risk begins for the solver (issuance of signed order) to when it resolves (0-1 block confirmations of Bitcoin TXO). These values are best migrated by the following configuration:
-1. Short initiation time. Using a source chain with a low block time, the initiation time can be kept to an absolute minimum.
-2. Short proof time. The user may only have 1 or 2 Blocks to get their transaction confirmed.
-3. Designing a compatibility layer between the UI and the Order Server that only requests binding order when it is known that the user can get their Bitcoins filled.
-
-In a future version, VM to Bitcoin swaps will upgrade to a price oracle book scheme that further minimizes time between start of price risk to end of price risk.
-
-# Key Integration Metrics
-
-Cross-Cats has been designed to optimise integration metrics like cost of capital, speed, human, developer, cost, price risk, and capital risk. Below are the most notiable metrics. If important metrics are missing feel free to reach out and the documentation will be updated.
-
-## Locked Capital & Underwriting
-
-There is no need to pre-lock capital into Cross-Cats. Capital is only locked during the actual order flow. In the beginning orders will be batch verified which will take between 2 and 4 hours (TBD). During this period there is **PRICE CERTANCY** but assets are temporarily locked.
-
-The user will not experience any delays.
-
-Additionally, underwriting will be available for some routes in which case assets will be available 1-5 minutes after asset delivery. This includes Bitcoin deliveries.
-
-### Price (un)Certainty
-
-For VM to VM swaps and VM to Bitcoin swaps, the price uncertainty window is the time it takes from the moment your system commits to the order (or when the claim transaction is initiated) to when the order claim arrives on-chain and is successfully mined. On fast chains this is at most 2-3 seconds but may be up-to 12 seconds. Cleverly timing the commitment can reduce the uncertainty period below the block time.
-
-For Bitcoin to VM swaps the price uncertainty window is from when the order is signed to when the user initiates the Bitcoin transaction and it gets your desired number of confirmations. This may vary from 30 seconds to 10 minutes.
-
-Cross-Cats has developed a user owned pseudo escrow, that allows the Order Server to give a soft commitment to solvers that a Bitcoin TXO will be generated. Given that a solver trusts this promise, the price uncertainty window may be as low as 30 seconds or shorter.
-
-## Timings
-
-In the beginning Catalyst will rely on more conservative optimistic timings. As a result, most transactions will be explicitly verified rather than optimistically verified. Additionally, small to medium sized swaps will be underwritten for supported routes. Funds will be locked up for upto 4-6 hours, however, inputs may be paid to the relayer shortly (30 seconds to 1 minute) after asset delivery.
-
-Otherwise, all numbers are configurable but here are some conservative estimates of important values:
-- Time from order sign to claim (speed of solver): 1-5 minutes (limit orders), 5-15 minutes (dutch auction)
-- Time from claim to delivery (delivery speed): 1-3 minutes (VM-VM), 30-60 minutes (Bitcoin-VM).
-- Time from claim to challenge (fraud observation time): 2-24 hours.
-- Time from challenge to proof: 2-24 hours.
-
-Thus the following experience applies:
-- Optimistic resolution after 2-24 hours.
-- Fraud Accepted after 4-48 hours.
-- VM deliveries can be proven in ~30 minutes
-- Bitcoin delivers can be proven in ~40-50 minutes depending on block timings.
-
-The upper end estimations are the initial order configuration estimates and over time as we get more confidence in these values they will be reduced towards the lower end.
diff --git a/src/content/docs/cross-cats/cross-cats.mdx b/src/content/docs/cross-cats/cross-cats.mdx
new file mode 100644
index 0000000..84ca158
--- /dev/null
+++ b/src/content/docs/cross-cats/cross-cats.mdx
@@ -0,0 +1,285 @@
+---
+title: "Cross Cats: Solving Liquidity x Bitcoin"
+description: "Catalyst v2 will support Bitcoin swaps. This is facilitated through intent based swaps that are verified through an on-chain Bitcoin SPV client. This provides: Fast settlement, Competitive rates, and near full security."
+sidebar:
+ order: 1
+---
+
+Cross-Cats is an intent-based cross-chain swap protocol built with flexibility in mind. The core idea is to allow anyone to create a request for anything provable. At launch, EVM settlements and Bitcoin transactions will be part of the provable set. Bitcoin transactions are proven using a Bitcoin SPV client and allows VM chain payments to be conditional on Bitcoin transactions.
+
+```d2 animateInterval=5000
+ style.fill: transparent
+ direction: right
+
+ vars: {
+ default-opacity: 0.1
+ }
+
+ title: System Overview {
+ near: top-center
+ shape: text
+ style: {
+ font-size: 40
+ }
+ }
+
+ Order Server: {shape: cloud}
+
+ Source Chain: {
+ User: {
+ shape: person
+ }
+ Solver: {
+ style.fill-pattern: none
+ }
+ Reactor: {shape: page}
+ Oracle: {
+ style.opacity: ${default-opacity}
+ shape: page
+ }
+ Challenger: {style.opacity: ${default-opacity}}
+ SPV: {
+ style.opacity: ${default-opacity}
+ shape: page
+ }
+
+ Solver -> Reactor: 3. resolve() => Quote
+ Solver -> Reactor: 4.1. initiate()
+ User -> Reactor: 4.2. Tokens
+
+ # Optimistic Payout
+ Solver <-> Reactor: O.6.1. optimisticPayout() {style.opacity: ${default-opacity}}
+
+ # Challenge Parts
+ Challenger -> Reactor: C.6.1. dispute() {style.opacity: ${default-opacity}}
+
+ # - Challenged Rejected (VM)
+ Oracle -> Reactor: C.9.2. proveOrderFulfilment() {style.opacity: ${default-opacity}}
+ Reactor -> Solver: C.9.3. payout Tokens {style.opacity: ${default-opacity}}
+
+ # - Challenged Rejected (Bitcoin)
+ Solver -> SPV: C.7.1. verify() {style.opacity: ${default-opacity}}
+ SPV -> Reactor: C.7.2. proveOrderFulfilment() {style.opacity: ${default-opacity}}
+ Reactor -> Solver: C.7.3. payout Tokens {style.opacity: ${default-opacity}}
+
+ # - Challenged Accepted
+ Challenger -> Reactor: C.7.1. completeDispute() {style.opacity: ${default-opacity}}
+ Reactor -> Challenger: C.7.2. Reward {style.opacity: ${default-opacity}}
+ Reactor -> User: C.7.3. Tokens {style.opacity: ${default-opacity}}
+ }
+
+ Source Chain.User -> Order Server: 1. Sign Message {style.animated: false}
+ Order Server -> Source Chain.Solver: 2. Signed Orders
+
+ Destination Chain: {
+ Oracle: {
+ shape: page
+ }
+ Solver
+ SPV: {
+ style.opacity: ${default-opacity}
+ shape: page
+ }
+ User: {
+ shape: person
+ }
+
+ # Ordinary
+ Solver -> Oracle: 5.1. fill()
+ Solver -> User: 5.2. Tokens
+
+ # Challenged submit proof (VM)
+ Solver -> Oracle: C.7.1. submit() {style.opacity: ${default-opacity}}
+
+ # Challenged submit proof (Bitcoin)
+ Solver -> SPV: C.7.1: verify() {style.opacity: ${default-opacity}}
+ SPV -> Oracle: C.7.2. Send fill details {style.opacity: ${default-opacity}}
+ }
+
+ Bitcoin: {
+ Solver: {
+ style.opacity: ${default-opacity}
+ }
+ User: {
+ shape: person
+ style.opacity: ${default-opacity}
+ }
+
+ Solver -> User: 5.1 transfer {style.opacity: ${default-opacity}}
+ style.opacity: ${default-opacity}
+ }
+
+ Bitcoin -> Destination Chain.SPV: Bitcoin Headers {
+ style.opacity: ${default-opacity}
+ style.stroke-dash: 4
+ }
+ Bitcoin -> Source Chain.SPV: Bitcoin Headers {
+ style.opacity: ${default-opacity}
+ style.stroke-dash: 4
+ }
+
+ Proof: "AMB\n" {
+ shape: cloud
+ style.opacity: ${default-opacity}
+ }
+
+ Destination Chain.Oracle -> Proof: C.8.1. Collect event {style.opacity: ${default-opacity}}
+ Proof -> Source Chain.Oracle: C.9.1. receiveMessage() {style.opacity: ${default-opacity}}
+
+ scenarios: {
+ optimistic resolution: {
+ title.label: Optimistic Resolution
+ Source Chain: {
+ (Solver <-> Reactor)[0].style.opacity: 1
+
+ (Solver <-> Reactor)[0].style.stroke-width: 3
+ }
+ }
+ challenged: {
+ title.label: Challenged
+ Source Chain.Challenger.style.opacity: 1
+ Source Chain.(Challenger -> Reactor)[0].style.opacity: 1
+ Source Chain.(Challenger -> Reactor)[0]: C.6.1. dispute()
+
+ Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 3
+
+ scenarios: {
+ challenged uncontested: {
+ title.label: Challenged Uncontested
+ Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
+ Source Chain.(Challenger -> Reactor)[1].style.opacity: 1
+ Source Chain.(Challenger -> Reactor)[1]: C.7.1. completeDispute()
+
+ Source Chain.(Challenger -> Reactor)[1].style.stroke-width: 3
+ Source Chain.(Reactor -> Challenger)[0].style.opacity: 1
+ Source Chain.(Reactor -> User)[0].style.opacity: 1
+ Destination Chain.style.opacity: ${default-opacity}
+ Destination Chain.*.style.opacity: ${default-opacity}
+ Destination Chain.(* -> *)[*].style.opacity: ${default-opacity}
+ }
+ order fill proven (VM): {
+ title.label: Order Fill Proven (VM)
+ Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
+
+ Destination Chain.(Solver -> Oracle)[1].style.opacity: 1
+ Destination Chain.(Solver -> Oracle)[1].style.stroke-width: 3
+ Destination Chain.Oracle.style.opacity: 1
+ Proof.style.opacity: 1
+ (Destination Chain.Oracle -> Proof)[0].style.opacity: 1
+ (Proof -> Source Chain.oracle)[0].style.opacity: 1
+ Source Chain.Oracle.style.opacity: 1
+ Source Chain.(Oracle -> Reactor)[0].style.opacity: 1
+ Source Chain.(Reactor -> Solver)[0].style.opacity: 1
+ }
+ order fill proven, (BTC): {
+ Source Chain.(Challenger -> Reactor)[0].style.stroke-width: 2
+ Destination Chain.style.opacity: ${default-opacity}
+ Destination Chain.*.style.opacity: ${default-opacity}
+ Destination Chain.(* -> *)[*].style.opacity: ${default-opacity}
+ Bitcoin.style.opacity: 1
+ Bitcoin.*.style.opacity: 1
+ Bitcoin.(* -> *)[*].style.opacity: 1
+
+ scenarios: {
+ local SPV client: {
+ title.label: Local SPV Client
+ (Bitcoin -> Source Chain.SPV)[0].style.opacity: 1
+ Source Chain.SPV.style.opacity: 1
+
+ Source Chain.(Solver -> SPV)[0].style.opacity: 1
+ Source Chain.(Solver -> SPV)[0].style.stroke-width: 3
+ Source Chain.(SPV -> Reactor)[0].style.opacity: 1
+ Source Chain.(Reactor -> Solver)[1].style.opacity: 1
+ }
+ remote SPV client: {
+ title.label: Remote SPV Client
+ (Bitcoin -> Destination Chain.SPV)[0].style.opacity: 1
+ Destination Chain.SPV.style.opacity: 1
+
+ Destination Chain.style.opacity: 1
+ Destination Chain.Solver.style.opacity: 1
+ Destination Chain.(Solver -> SPV)[0].style.opacity: 1
+ Destination Chain.SPV.style.opacity: 1
+
+ Destination Chain.Oracle.style.opacity: 1
+ Destination Chain.(SPV -> Oracle)[0].style.opacity: 1
+ (Destination Chain.Oracle -> Proof)[0].style.opacity: 1
+
+ Proof.style.opacity: 1
+ Source Chain.Oracle.style.opacity: 1
+ (Proof -> Source Chain.Oracle)[0].style.opacity: 1
+ Source Chain.(Oracle -> Reactor)[0].style.opacity: 1
+ Source Chain.(Reactor -> Solver)[0].style.opacity: 1
+ }
+ }
+ }
+ }
+ }
+ }
+```
+
+The above diagram animates between all system states. Reloading the page or opening the SVG in a separate tab may be required for the animation to show.
+
+## System Overview
+
+Cross-Cats is designed for cross-chain intents. Nevertheless, the majority of logic exists on the source chain. **Source Chain** here refers to the chain where the order was **Initiated** by a solver. **Remote Chain** refers to the chain (or chains) where proofs are sent from. This includes asset deliveries.
+
+### Initiation (order claim)
+
+An order is initiated by the user signing an order description. An example of an order description is _My 1 Ether (Ethereum) for 3000 USD (Base)_. The signed order is a permit2 witness allowing the solver to submit the order to the Reactor and collect the **input** (1 Ether) from the user. Importantly, during this step, some collateral is collected from the solver. This ensures the solver has some skin in the game and settles the order. The collateral is paid back when the input is released to the solver.
+
+To improve the user and solver experience, an order server sits between the user and solver and aids with order validation, propagation, and quoting.
+
+### Output Payment (to user)
+
+The payment pathway depends on the order intent. For a **VM to VM** swap, the solver calls the oracle contract on the destination chain, which sends & records the token payment to the user. For **VM to Bitcoin** swap, the solver makes the payment described in the order. In other words, make a Bitcoin transaction that has **a** UTXO that matches the order description. Using an SPV client, the transaction containing the UTXO can be proven to exist.
+
+### Input Payment (to solver)
+
+Cross Cats has 3 payment release schemes to optimise the solver experience.
+
+1. Optimistic resolution. This assumes that the resolver delivered the payment to the user. After a dispute window (configured by the user), the payment will be released. If the order is disputed, the operation falls back to option 2.
+2. Explicit validation. At any time, orders can be proven. This requires that someone send the proof from the remote chains to the source chain. This is more costly than optimistic resolution, but may be significantly faster than optimistic resolution. Additionally, through batch verification, the cost can be reduced at a slight increase in verification speed.
+3. Underwriting. The last release scheme isn't a payment proof scheme as much as it is a responsibility delegation scheme. If configured, an order can be bought by someone else at any point prior to the release of the input. This allows the initial solver to immediately get their capital back and hand off the payment validation to a third party.
+
+By using these 3 schemes in conjunction with each other, solvers only have to lock liquidity for a small period of time while not sacrificing any system security. At the same time, speed, security, and cost can be rebalanced based on the specific needs of a user or chain conditions.
+
+## Bitcoin & Pseudo Solving
+
+VM to Bitcoin swaps are relatively straight forward:
+1. User signs a message stating the input assets.
+2. Solver claims the order, inputs assets are automatically collected.
+3. Solver delivers assets.
+4. Solver is paid.
+
+However, this flow breaks on step 2 when the user wants to go from Bitcoin to VM. (sell Bitcoin). There is no way to pull assets from a user. To solve this issue, the user becomes a **pseudo solver** & relies on release scheme 3. Pseudo solving works by asking solvers for short-lived Bitcoin short quotes. These orders are after validation & selection signed by the solver. The user then quickly claims the order.
+
+Say the user wants to swap 1 Bitcoin for 50000 USDC. Using the order server, they need to collect & claim a signed order of the opposite: 50000 USDC for 1 Bitcoin. Once this order is filled, they get the input (50000 USDC) which matches their desired swap.
+
+Important to notice, this adds a delay between when the price risk begins for the solver (issuance of signed order) to when it resolves (0-1 block confirmations of Bitcoin TXO). These values are best migrated by the following configuration:
+1. Short initiation time. Using a source chain with a low block time, the initiation time can be kept to an absolute minimum.
+2. Short proof time. The user may only have 1 or 2 Blocks to get their transaction confirmed.
+3. Designing a compatibility layer between the UI and the Order Server that only requests binding order when it is known that the user can get their Bitcoins filled.
+
+In a future version, VM to Bitcoin swaps will upgrade to an oracle book scheme that further minimizes time between start of price risk to end of price risk.
+
+## Key differentiators
+
+Cross-Cats has been designed to optimise solver integration metrics:
+- Cost of capital – Capital is only held for the duration of a swap and can be underwritten to further decrease the lock duration.
+- Speed – Minimizing settlement overhead, users receive their assets immediately, while solvers can opt in to underwriting for quick input payout.
+- Price Risk – Price Risk is minimized by letting solvers commit to swaps & providing strong order guarantees.
+
+### Locked Capital & Underwriting
+
+Cross-Cats has no concept of pre-locking liquidity. Capital is only locked during the actual order flow. Additionally, Users will immediately receive their assets as deliveries are directly from the solver to the user.
+
+Additionally, for routes supported by underwriters input assets will be available 1-5 minutes after asset delivery. This includes Bitcoin routes for users.
+
+### Price (un)Certainty
+
+For VM to VM swaps and VM to Bitcoin swaps, the price uncertainty window is the time it takes from the moment your system commits to the order – which may be the initiate transaction – to when the order claim arrives on-chain and is successfully mined. On fast chains this is at most 2-3 seconds but may be up-to 12 seconds. Cleverly timing the commitment can reduce the uncertainty period below the block time.
+
+For Bitcoin to VM swaps the price uncertainty window is from when the order is signed to when the user initiates the Bitcoin transaction and it gets your desired number of confirmations. This may vary from 30 seconds to 60 seconds.
+
+Cross-Cats has developed a user owned pseudo escrow, that allows the Order Server to give a soft commitment to solvers that a Bitcoin UTXO will be generated. Given that a solver trusts this promise, the price uncertainty window is as low as 30 seconds or shorter.