|
| 1 | +--- |
| 2 | +title: Wormhole Settlement Solver |
| 3 | +description: Set up, configure, and run a Wormhole Settlement Solver on Solana's Matching Engine to fulfill cross-chain transfers efficiently and securely. |
| 4 | +--- |
| 5 | + |
| 6 | +# Run a Wormhole Settlement Solver |
| 7 | + |
| 8 | +## Introduction |
| 9 | + |
| 10 | +This page provides instructions on how to set up, configure, and run a Solver for Wormhole Settlement using the [example solver](https://github.com/wormholelabs-xyz/example-liquidity-layer/tree/update-solver-example/solver){target=\_blank}. |
| 11 | + |
| 12 | +A Solver is an off-chain agent responsible for: |
| 13 | + |
| 14 | +- Listening to cross-chain transfer requests sent over Wormhole |
| 15 | +- Bidding in auctions (on Solana) to fulfill each request |
| 16 | +- Facilitating the actual cross-chain transfer by locking/burning assets on Solana and minting/unlocking them on the destination chain |
| 17 | +- Rebalancing once the origin chain transaction finalizes and is redeemed back on Solana |
| 18 | + |
| 19 | +For information on how the protocol functions and its core features, please visit the [Wormhole Settlement](/docs/learn/transfers/settlement/overview/){target=\_blank} page. |
| 20 | + |
| 21 | +## Background |
| 22 | + |
| 23 | +The Solana Matching Engine's permissionless English auction is a central component of Wormhole Settlement protocol architecture. The Matching Engine contract allows any third-party solver to interact with the matching engine to place bids or improve existing ones. The contract includes four key instructions: |
| 24 | + |
| 25 | +1. `initialize_auction` - creates a new auction account on-chain and sets basic parameters like the auction's token mint, the amount required, and the bidding period details |
| 26 | +2. `bid` - allows a solver to place or update a bid on the active auction |
| 27 | +3. `finalize_auction` - following the conclusion of the auction, this instruction completes the fast transfer by sending funds to the recipient on the target chain. This instruction may call the Circle CCTP contract or release an NTT contract in the future, depending on the shuttle asset in question. Failure to execute this message within a predefined grace period may result in a penalty for the winning bidder. |
| 28 | +4. `cancel_auction` - cancels an open auction when the auction is no longer valid or was created by mistake. The program returns all locked funds to their respective owners |
| 29 | + |
| 30 | +These instructions work together to carry out the auction as follows: |
| 31 | + |
| 32 | +- The solver transfers the bid amount to the program escrow account, which ensures they have liquidity |
| 33 | +- With each successful call of `bid`, the program updates the auction to the new highest bidder, and the prior bid is atomically sent back to the originating solver |
| 34 | +- The originating solver can repurpose the returned funds and use them to improve their bid |
| 35 | +- Following the auction, the winning solver has to call an instruction on the matching engine to execute the intent |
| 36 | + |
| 37 | +When placing a bid, whether initial or improved, the solver must deposit the required funds plus a security deposit into the matching engine contract. In this permissionless auction, the requirement of this principal amount plus the security deposit ensures a solver's credible commitment to fulfill the transfer. Malicious actors could place hollow bids without this safeguard, undermining the auction's credibility and hindering true price discovery. |
| 38 | + |
| 39 | +If the winning solver fails to call the `finalize_auction` instruction, other competing solvers may permissionlessly 'slash' the solver by executing the instruction on their behalf and collecting a portion of the original security deposit as a reward. The remaining portion is routed to the user as compensation for the unanticipated delay. This mechanism properly incentivizes timely execution through solver redundancy and competition. |
| 40 | + |
| 41 | +## Testnet Example Solver |
| 42 | + |
| 43 | +You can clone the Wormhole [`example-liquidity-layer`](https://github.com/wormholelabs-xyz/example-liquidity-layer){target=\_blank} repository to use the included [`solver`](https://github.com/wormholelabs-xyz/example-liquidity-layer/tree/main/solver){target=\_blank} directory as an example solver to fulfill fast orders by interacting with the Matching Engine on Solana. |
| 44 | + |
| 45 | +!!!warning |
| 46 | + This example is not optimized for performance, has only been tested on Solana devnet, and is not intended for production use. Any assumptions made in this example may not translate to mainnet. |
| 47 | + |
| 48 | +### Prerequisites |
| 49 | + |
| 50 | +In order to build and install dependencies locally in this repo, you will need: |
| 51 | + |
| 52 | +- node v20.18.1 |
| 53 | +- npmv - get started by installing `nvm` using this [installation guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating){target=\_blank} |
| 54 | + |
| 55 | +Navigate into the `solver` directory, then run the command below to set up your environment and install the node dependencies and Matching Engine package: |
| 56 | + |
| 57 | +```sh |
| 58 | +make dependencies |
| 59 | +``` |
| 60 | + |
| 61 | +### Set up Config |
| 62 | + |
| 63 | +The following is an example of a `config.json` file for Solana devnet. The keys here are required for both the publisher and example solver processes. |
| 64 | + |
| 65 | +```json title="config.json" |
| 66 | +--8<-- 'code/build/transfers/settlement/solver-config.json' |
| 67 | +``` |
| 68 | + |
| 69 | +The rollback risks and offer edges configured in the sample config are arbitrary placeholders. You should use historical data and your risk tolerance, to determine appropriate values for your project. |
| 70 | + |
| 71 | +### Listen to Activity |
| 72 | + |
| 73 | +The example solver listens to attested Wormhole messages (VAAs) published on the Wormhole Guardian gossip network. To listen to this gossip network and run the VAA publisher, run the command below. Docker compose is used to listen to the Pyth Beacon and start the [`publishActivity`](https://github.com/wormholelabs-xyz/example-liquidity-layer/blob/update-solver-example/solver/app/publishActivity.ts){target=\_blank} process. |
| 74 | + |
| 75 | +```sh |
| 76 | +NETWORK=testnet CONFIG=path/to/config.json make run-publisher |
| 77 | +``` |
| 78 | + |
| 79 | +You should see output resembling: |
| 80 | + |
| 81 | +<div id="termynal" data-termynal> |
| 82 | + <span data-ty> Start logging with info level.</span> |
| 83 | + <span data-ty> 2025-01-21 16:38:28.145 [publisher] info: Environment: Testnet</span> |
| 84 | + <span data-ty> 2025-01-21 16:38:36.631 [publisher] info: Fast VAA. chain=OptimismSepolia, sequence=33635, vaaTime=1737499116</span> |
| 85 | + <span data-ty> 2025-01-21 16:38:51.044 [publisher] info: Fast VAA. chain=OptimismSepolia, sequence=33637, vaaTime=1737499130</span> |
| 86 | + <span data-ty> 2025-01-21 16:40:24.890 [publisher] info: Fast VAA. chain=OptimismSepolia, sequence=33639, vaaTime=1737499224</span> |
| 87 | +</div> |
| 88 | + |
| 89 | +To set up the Pyth Beacon (which is run using make `run-publisher`), you may need to increase the UDP buffer size for the OS: |
| 90 | + |
| 91 | +=== "Linux" |
| 92 | + |
| 93 | + ```sh |
| 94 | + sudo sysctl -w net.core.rmem_max=2097152 |
| 95 | + sudo sysctl -w net.core.rmem_default=2097152 |
| 96 | + ``` |
| 97 | + |
| 98 | +=== "MacOS" |
| 99 | + |
| 100 | + ```sh |
| 101 | + sudo sysctl -w net.inet.udp.recvspace=2097152 |
| 102 | + ``` |
| 103 | + |
| 104 | +### Running the Example Solver |
| 105 | + |
| 106 | +Using the same config for your publisher, run the example solver with the command below. |
| 107 | + |
| 108 | +```sh |
| 109 | +CONFIG=path/to/config.json make run-solver |
| 110 | +``` |
| 111 | + |
| 112 | +It is recommended you write log output to a file so errors can be tracked. The example config above specifies an example log filename. |
| 113 | + |
| 114 | +This process reads the following environment variables: |
| 115 | + |
| 116 | +```sh |
| 117 | +SOLANA_PRIVATE_KEY_1= |
| 118 | +SOLANA_PRIVATE_KEY_2= |
| 119 | +SOLANA_PRIVATE_KEY_3= |
| 120 | +SOLANA_PRIVATE_KEY_4= |
| 121 | +SOLANA_PRIVATE_KEY_5= |
| 122 | +``` |
| 123 | + |
| 124 | +At least one of these environment variables must be defined as a keypair encoded in base64 format. These payers must have SOL to send transactions on Solana devnet. If they need funds, they can request them from the [Solana devnet faucet](https://faucet.solana.com/){target=\_blank}. |
| 125 | + |
| 126 | +The example solver assumes that these payers own USDC Associated Token Accounts(ATAs), which will be used to fulfill fast transfers. These ATAs must be funded with Solana Devnet USDC. If your ATAs need funds, request some at the [Circle testnet faucet](https://faucet.circle.com/){target=\_blank}. |
| 127 | + |
| 128 | +Wallets and their corresponding ATA will be disabled if there are insufficient funds to pay for transactions or fulfill fast transfers. These constraints can be modified using the `updatePayerMinimumLamports` and `updateTokenMinimumBalance` methods. |
| 129 | + |
| 130 | +An address lookup table is required to execute some transactions. Use the command below to create one. |
| 131 | + |
| 132 | +```sh |
| 133 | +CONFIG=path/to/config.json make create-lut |
| 134 | +``` |
| 135 | + |
| 136 | +`SOLANA_PRIVATE_KEY_1` must be defined for this script to work. |
| 137 | + |
| 138 | +The example solver has the following toggles depending on which orders you want to fulfill: |
| 139 | + |
| 140 | +- `enableCctpOrderPipeline()` |
| 141 | +- `enableLocalOrderPipeline()` |
| 142 | +- `enablePlaceInitialOffer()` |
| 143 | +- `enableImproveOffer()` |
| 144 | + |
| 145 | +See the comments in [runExampleSolver](https://github.com/wormholelabs-xyz/example-liquidity-layer/blob/update-solver-example/solver/app/runExampleSolver.ts){target=\_blank} for more information. |
| 146 | + |
| 147 | +This example solver does NOT do the following: |
| 148 | + |
| 149 | +- Discriminate between the CCTP source networks. You must add logic to determine whether you want to constrain fulfilling orders from specific networks. This solver will try to fulfill all orders as long as `enableCctpOrderPipeline()` is called |
| 150 | +- Discriminate among fulfillment sizes. No logic determines how small or large fast order transfer sizes should be. This solver will try to fulfill anything as long as your balance can handle it |
| 151 | +- Add auctions to auction history. We recommend that after settling a complete auction (one that you have won), you write the auction pubkey to a database and have a separate process to add auction history entries to reclaim rent from these auction accounts. The auction history time delay is two hours after the VAA timestamp. This example does not prescribe any specific database, so add whichever you want |
| 152 | + |
| 153 | + |
0 commit comments