diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 00000000..90956456 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,28 @@ +name: 'build-docs' +on: + push: + branches: ['main'] + pull_request: + branches: ['main'] + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - name: Trigger mintlify workflow + if: github.event_name == 'push' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.DOCS_CHILDREN_ACTIONS_TRIGGER_TOKEN }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'mintlify-onboarding', + repo: 'skip', + workflow_id: 'mintlify-action.yml', + ref: 'main', + }); diff --git a/docs/for-searchers.mdx b/docs/for-searchers.mdx new file mode 100644 index 00000000..973a09ef --- /dev/null +++ b/docs/for-searchers.mdx @@ -0,0 +1,219 @@ +--- +title: "For Searchers" +--- + +Searcher Simluation + +Bundles must only pass basic `CheckTx` validation (e.g. nonce, account balance, etc.) in order to be accepted by the auction. This means that bundles that are submitted to the auction may not be entirely valid on-chain since `runMsgs` is not executed. Searchers are encouraged to simulate their bundles before submitting them to the auction. + + +### ➑️ How do searchers submit bundles to chains that use the Block SDK?[​](#️-how-do-searchers-submit-bundles-to-chains-that-use-the-block-sdk "Direct link to heading") + +Definitions + +πŸ’‘ `AuctionTx` (auction bid transaction) = `sdk.Tx` that includes a single `MsgAuctionBid` + +Searchers submit bundles by broadcasting a `AuctionTx` in the same way they broadcast any other transaction. A few important things to note: + +* When a `MsgAuctionBid` message is included in a transaction, no other `sdk.Msg` can be present. +* Interfacing with the auction _may be different across_ `Block SDK` chains. Bidding may involve interacting with a dedicated `AuctionHouse` smart contract instead of including this special message type. In the future, we will link a chain directory here to check on which interface you need, when there are different implementations. + +#### Default Auction Bid Message[​](#default-auction-bid-message "Direct link to heading") + +```json + + +type MsgAuctionBid struct { + + + Bidder string `protobuf:"bytes,1,opt,name=bidder,proto3" json:"bidder,omitempty"` + + + Bid types.Coin `protobuf:"bytes,3,opt,name=bid,proto3" json:"bid"` + + + Transactions [][]byte `protobuf:"bytes,4,rep,name=transactions,proto3" json:"transactions,omitempty"` +} + +``` + + +There are three things searchers must specify to participate in an auction: + +1. The **`Transactions`** they want executed at the top of block. + * Transactions will be executed in the order they are specified in the message. + * Each transactions included must be the raw bytes of the transaction. +2. The **`Bidder`** who is bidding for top of block execution. + * This must be the same account that signs the transaction. +3. The **`Bid`** they want to send alongside the bundle. + * This should be in the denom configured by the auction parameters (see below) +4. The **`Timeout`** height i.e. until what height the bid is valid for. + * This must be added to the transaction when it is being constructed i.e. `txBuilder.SetTimeoutHeight(timeoutHeight)` + +#### Nonce Checking[​](#nonce-checking "Direct link to heading") + +In general, all bundles must respect nonce ordering of accounts. If a bundle is submitted with an invalid nonce, it will be rejected. The execution of the bundle will always look like the following: + +* Auction Transaction (which extracts the bid) +* All transactions in the bundle (in order) + +For example, assume the following: + +1. Searcher has account `A` with nonce `n` +2. Searcher wants to submit a bundle with 3 transactions from account `A` + +The searcher must first sign the `AuctionTx` with nonce `n + 1`. Then, the searcher must sign the first transaction in the bundle with nonce `n + 2`, the second transaction with nonce `n + 3`, and the third transaction with nonce `n + 4`. + +#### Skipper Bot[​](#skipper-bot "Direct link to heading") + +User’s can bootstrap their searching bots by utilizing Skip’s own open source [Skipper Bot](https://github.com/skip-mev/skipper). + +#### Creating an `AuctionTx`[​](#creating-an-auctiontx "Direct link to heading") + +```json + +func createBidTx( + privateKey *secp256k1.PrivKey, + bidder string, + bid sdk.Coin, + bundle [][]byte, + height uint64, + sequenceOffset uint64, +) (sdk.Tx, error) { + + msgs := []sdk.Msg{ + &buildertypes.MsgAuctionBid{ + Bidder: bidder, + Bid: bid, + Transactions: bundle, + }, + } + + + sequenceNum, accountNum := getAccountInfo(privateKey) + + txConfig := authtx.NewTxConfig( + codec.NewProtoCodec(codectypes.NewInterfaceRegistry()), + authTx.DefaultSignModes, + ) + txBuilder := txConfig.NewTxBuilder() + + + txBuilder.SetMsgs(msgs...) + ... + txBuilder.SetGasLimit(5000000) + + + + + txBuilder.SetTimeoutHeight(height) + + + + + offsetNonce := sequenceNum + sequenceOffset + signerData := auth.SignerData{ + ChainID: CHAIN_ID, + AccountNumber: accountNumber, + Sequence: offsetNonce, + } + + sigV2, err = clientTx.SignWithPrivKey( + txConfig.SignModeHandler().DefaultMode(), + signerData, + txBuilder, + privateKey, + txConfig, + offsetNonce, + ) + if err != nil { + return nil, err + } + + if err = txBuilder.SetSignatures(sigV2); err != nil { + return nil, error + } + + return txBuilder.GetTx(), nil +} + +``` + + +### βš™οΈ Auction fees[​](#️-auction-fees "Direct link to heading") + +Auction Configuration + +All auction parameters are accessible though the `/block-sdk/x/auction/v1/params` HTTP path on a standard node or gRPC service defined by `x/auction`. + + +In order to participate in an auction, searchers must pay a fee. This fee is paid in the native token of the chain. The fee is determined by the auction parameters, which are set by the chain. The auction parameters are: + +1. **`MaxBundleSize`**: specifies the maximum number of transactions that can be included in a bundle (bundle = an ordered list of transactions). Bundles must be ≀ this number. +2. **`ReserveFee`**: specifies the bid floor to participate in the auction. Bids that are lower than the reserve fee are ignored. +3. **`MinBidIncrement`**: specifies how much greater each subsequent bid must be (as seen by an individual node) in order to be considered. If the bid is lower than the `highest current bid + min bid increment`, the bid is ignored. +4. **`FrontRunningProtection`**: determines whether front-running and sandwich protection is enabled. + + +Front-running and sandwich protection + +**If this is set to true, your bundle must follow these guidelines:** + +* You must put your signed transactions **after** transactions you didn’t sign +* You can only have **at most two** unique signers in the bundle + + +Bundle Examples: + +1. **Valid**: \[tx1, tx2, tx3\] where tx1 is signed by the signer 1 and tx2 and tx3 are signed by the bidder. +2. **Valid**: \[tx1, tx2, tx3, tx4\] where tx1 - tx4 are signed by the bidder. +3. **Invalid**: \[tx1, tx2, tx3\] where tx1 and tx3 are signed by the bidder and tx2 is signed by some other signer. (possible sandwich attack) +4. **Invalid**: \[tx1, tx2, tx3\] where tx1 is signed by the bidder, and tx2, tx3 are signed by some other signer. (possible front-running attack) + +#### Querying auction parameters[​](#querying-auction-parameters "Direct link to heading") + +```json +func getAuctionParams() (*auctiontypes.Params, error) { + # Replace this URL with the gRPC url of a node + url := "localhost:9090" + + # Establish a gRPC connection to query auction parameters + grpcConn, err := grpc.Dial( + url, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, err + } + + client := auctiontypes.NewQueryClient(grpcConn) + + req := &auctiontypes.QueryParamsRequest{} + resp, err := client.Params(context.Background(), req) + + return resp.Params +} + +``` + + +### 🚨 Chains currently using the MEV-Lane[​](#-chains-currently-using-the-mev-lane "Direct link to heading") + +#### Mainnets[​](#mainnets "Direct link to heading") + + +|Chain Name |Chain-ID |Block-SDK Version| +|-----------|-------------|-----------------| +|Juno |juno-1 |v1.0.2 | +|Persistence|persistence-1|v1.0.2 | +|Initia |NA |v1.0.2 | +|Prism |NA |v1.0.2 | +|Terra |phoenix-1 |v1.0.2 | + + +#### Testnets[​](#testnets "Direct link to heading") + + +|Chain Name|Chain-ID|Block-SDK Version| +|----------|--------|-----------------| +|Juno |uni-6 |v1.0.2 | diff --git a/docs/how-it-works.mdx b/docs/how-it-works.mdx new file mode 100644 index 00000000..6241e196 --- /dev/null +++ b/docs/how-it-works.mdx @@ -0,0 +1,57 @@ +--- +title: "How it Works" +--- +### Summary[​](#summary "Direct link to heading") + +With the Block SDK, blocks are broken up into smaller partial blocks called `lanes`. + +* Each `lane` has its own custom block building logic and stores distinct types of transactions. +* Each lane can only consume a portion of the block as defined on the `lane`'s configuration (`MaxBlockSpace`). +* When a block proposal is requested, a block will **fill** with transactions from each `lane`, iteratively, in the order in which the `lanes` are defined in the application. +* When a block proposal is processed, each `lane` will **verify** its portion of the block, iteratively, in the order in which the `lanes` are defined in the application. +* **Transactions in blocks MUST respect the ordering of lanes.** + +### πŸ” Background: Transaction Lifecycle[​](#-background-transaction-lifecycle "Direct link to heading") + +Knowledge of the general transaction lifecycle is important to understand how `lanes` work. + +* A transaction begins when it is signed and broadcasted to a node on a chain. +* It will be then be verified by the application on the node. +* If it is valid, it will be inserted into the node's `mempool`, which is a storage area for transactions before inclusion in a block. +* If the node happens to be a `validator`, and is proposing a block, the application will call `PrepareProposal` to create a new block proposal. +* The proposer will look at what transactions they have in their mempool, iteratively select transactions until the block is full, and share the proposal with other validators. +* When a different validator receives a proposal, the validator will verify its contents via `ProcessProposal` before signing it. +* If the proposal is valid, the validator will sign the proposal and broadcast their vote to the network. +* If the block is invalid, the validator will reject the proposal. +* Once a proposal is accepted by the network, it is committed as a block and the transactions that were included are removed from every validator's mempool. + +### πŸ›£οΈ Lane Lifecycle[​](#️-lane-lifecycle "Direct link to heading") + +`Lanes` introduce new steps in the transaction lifecycle outlined above. + +A `LanedMempool` is composed of several distinct `lanes` that store their own transactions. The `LanedMempool` will insert the transaction into all `lanes` that accept it + +* After the base application accepts a transaction, the transaction will be checked to see if it can go into any `lanes`, as defined by the lane's `MatchHandler`. +* `Lane`'s can be configured to only accept transactions that match a certain criteria. For example, a `lane` could be configured to only accept transactions that are staking related (such as a free-transaction lane). +* When a new block is proposed, the `PrepareProposalHandler` of the application will iteratively call `PrepareLane` on each `lane` (in the order in which they are defined in the application). The `PrepareLane` method is similar to `PrepareProposal`. +* Calling `PrepareLane` on a `lane` will trigger the lane to reap transactions from its mempool and add them to the proposal (if they respect the verification rules of the `lane`). +* When proposals are verified in `ProcessProposal` by other validators, the `ProcessProposalHandler` defined in `abci/abci.go` will call `ProcessLane` on each `lane` in the same order as they were called in the `PrepareProposalHandler`. +* Each subsequent call to `ProcessLane` will filter out transactions that belong to previous lanes. **A given lane's ProcessLane will only verify transactions that belong to that lane.** + + +Scenario + +Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`. + +`LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second. + +`LaneA` contains transactions Tx1 and Tx2 and `LaneB` contains transactions Tx3 and Tx4. + + +When a new block needs to be proposed, the `PrepareProposalHandler` will call `PrepareLane` on `LaneA` first and `LaneB` second. + +When `PrepareLane` is called on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the proposal. The same applies for `LaneB`. Say `LaneA` reaps transactions Tx1 and Tx2 and `LaneB` reaps transactions Tx3 and Tx4. This gives us a proposal composed of the following: + +* `Tx1`, `Tx2`, `Tx3`, `Tx4` + +When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` with the proposal composed of Tx1, Tx2, Tx3, and Tx4. `LaneA` will then verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the remaining transactions - Tx3 and Tx4. `LaneB` will then verify Tx3 and Tx4 and return no remaining transactions. \ No newline at end of file diff --git a/docs/integrate.mdx b/docs/integrate.mdx new file mode 100644 index 00000000..af716819 --- /dev/null +++ b/docs/integrate.mdx @@ -0,0 +1,216 @@ +--- +title: "Integrate the Block SDK" +--- + +**Block SDK** + +The Block SDK is **open-source software** licensed under MIT. It is free to use, and has existing plug-and-play Lanes that work immediately! + +Visit the GitHub repo [here](https://github.com/skip-mev/block-sdk). + +We strive to be responsive to questions and issues within 1-2 weeks - please open a GitHub issue or **[join our Discord](https://skip.money/discord)**. Note, we are not currently providing hands-on support for new integrations. + +## βš™οΈ Architecture \[15 mins\][​](#️-architecture-15-mins "Direct link to heading") + + +This is a high-level overview of the architecture, please reference [this page](https://docs.skip.money/blocksdk/how-it-works) or the [`Block-SDK` repo](https://github.com/skip-mev/block-sdk) for detailed and up to date info. For those eager to code, feel free to skip this and start down the page at **Set Up**! + + +### How Were Blocks Constructed pre-Block-SDK?[​](#how-were-blocks-constructed-pre-block-sdk "Direct link to heading") + +There are 3 relevant stages of consensus (these are all ABCI++ methods) + +* **PrepareProposal** + * In this step, the consensus-engine (CometBFT, etc.) gives the application all of the transactions it has seen thus far. + * The app looks over these, performs some app-specific logic, and then gives them back to the consensus-engine. The consensus-engine then creates and broadcasts a proposal containing the transactions sent back from the app. +* **ProcessProposal** + * In this step, all validators check that the transactions in the proposal are valid, and that the proposal (as a whole) satisfies validity conditions determined by the application + * If the proposal fails, validators will not vote on the block, and the network will be forced to another round of consensus + * if the proposal passes, valdiators vote on the block, and the block will become canonical (barring unforeseen events) + +### **Application Mempools**[​](#application-mempools "Direct link to heading") + +In `v0.47.0` of the cosmos-sdk, **app-side mempools** were added to the SDK. With app-side mempools, validators no longer need to rely on the consensus-engine to keep track of and order all available transactions. Now applications can define their own mempool implementations, that + +1. Store all pending (not finalized in a block) transactions +2. Order the set of pending transactions + +#### **How does block-building change?**[​](#how-does-block-building-change "Direct link to heading") + +Now in **PrepareProposal** instead of getting transactions from the consensus-engine, validators can pull transactions from their application-state aware mempools, and prioritize those transactions instead of the consensus-engine's transactions. + +**Why is this better?** + +* Mempools that are not app-state aware will not have the ability to make state-aware ordering rules. Like + +1. All staker transactions are placed at the top of the block +2. All IBC `LightClientUpdate` messages are placed at the top of the block +3. Anything you can think of!! + +* The consensus engine's mempool is generally in-efficient. + * The consensus-engine's mempool does not know when to remove transactions from its own mempool + * The consensus-engine spends most of its time re-broadcasting transactions between peers, hogging network bandwidth + +## Block-SDK!! [​](#block-sdk "Direct link to heading") + +The `Block-SDK` defines its own custom implementation of an **app-side mempool**, a `LaneMempool`. The `LaneMempool` is composed of `Lanes`, and handles transaction ingress, ordering, and cleaning. + +**transaction ingress** + +* The `LanedMempool` constructor defines an ordering of lanes. When a transaction is received by the app, it iterates through all lanes in order and inserts the transaction into the first `Lane` that it belongs in. **ordering** +* Each `Lane` of the `LanedMempool` maintains its own ordering of transactions. When the `LanedMempool` routes a transaction to its corresponding `Lane` the `Lane` then inserts the transaction at its designated position with respect to all other transactions in the lane + +### PrepareProposal[​](#prepareproposal "Direct link to heading") + +When the application is instructed to `PrepareProposal` it iterates through its `Lane`s in order, and calls each `Lane`'s `PrepareLane` method. The `Lane.PrepareLane` method collects transactions from a `Lane` and appends those transactions to the set of transactions from previous `Lane`'s `PrepareLane` calls. In other words, each block-proposal is now a collection of the transactions from the `LanedMempool`'s constituent lanes. + +### ProcessProposal[​](#processproposal "Direct link to heading") + +When the application receives a proposal, and calls `ProcessProposal`, the app delegates the validation to the `LaneMempool.ProcessLanes` method. Remember, the proposal is composed of transactions from the sub-lanes of the `LaneMempool`, as such, the `LaneMempool` can route each `Lane`'s contribution to the Proposal to that `Lane` for validation. The proposal passes iff all `Lane`'s contributions are valid. + + +⚠️ + +A block constructed from a `LaneMempool`'s `PrepareLanes` method must always pass that `LaneMempool`'s `ProcessLanes` method, otherwise, the chain will fail to produce blocks!! These functions are consensus critical, so practice caution when implementing them!! + + +## πŸ“– Set Up \[20 mins\][​](#-set-up-20-mins "Direct link to heading") + +To get set up, we're going to implement the `Default Lane`, which is the **most general and least restrictive** that accepts all transactions. This will cause **no changes** to your chain functionality, but will prepare you to add `lanes` with more functionality afterwards! + +The default lane mirrors how CometBFT creates proposals today. + +* It does a basic check to ensure that the transaction is valid. +* Orders the transactions based on tx fee amount (highest to lowest). +* The `PrepareLane` handler will reap transactions from the lane up to the `MaxBlockSpace` limit +* The `ProcessLane` handler will ensure that the transactions are ordered based on their fee amount and pass the same checks done in `PrepareLane`. + +## πŸ—οΈ Default Lane Setup + +## πŸ“¦ Dependencies[​](#-dependencies "Direct link to heading") + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### Release Compatibility Matrix[​](#release-compatibility-matrix "Direct link to heading") + + +|Block SDK Version|Cosmos SDK| +|-----------------|----------| +|v1.x.x |v0.47.x | +|v2.x.x |v0.50.x | + + +## πŸ“₯ Installation[​](#-installation "Direct link to heading") + +To install the Block SDK, run the following command: + +```bash +$ go install github.com/skip-mev/block-sdk + +``` + + +## πŸ“š Usage[​](#-usage "Direct link to heading") + +1. First determine the set of lanes that you want to use in your application. The available lanes can be found in our [Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). This guide only sets up the `default lane` + +```bash +import ( + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/block/base" + defaultlane "github.com/skip-mev/block-sdk/lanes/base" +) + + // 1. Create the lanes. + // + // NOTE: The lanes are ordered by priority. The first lane is the highest priority + // lane and the last lane is the lowest priority lane. Top of block lane allows + // transactions to bid for inclusion at the top of the next block. + // + // For more information on how to utilize the LaneConfig please + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. + // + // Default lane accepts all transactions. + +func NewApp() { + ... + defaultConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := defaultlane.NewDefaultLane(defaultConfig) + // TODO(you): Add more Lanes!!! + +``` + + +2. In your base application, you will need to create a `LanedMempool` composed of the `lanes` you want to use. + +```json + // 2. Set up the relative priority of lanes + lanes := []block.Lane{ + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + +``` + + +3. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. **It is recommended that the last lane is the default lane.** + +```json + // 3. Set up the ante handler. + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + + // Set the lane ante handlers on the lanes. + // + // NOTE: This step is very important. Without the antehandlers, lanes will not + // be able to verify transactions. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + +``` + + +4. You will also need to create a `PrepareProposalHandler` and a `ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. + +```json + // 4. Set the abci handlers on base app + // Create the LanedMempool's ProposalHandler + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + mempool, + ) + + // set the Prepare / ProcessProposal Handlers on the app to be the `LanedMempool`'s + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + +``` + + +### πŸ’… Next step: implement other `lanes`[​](#-next-step-implement-other-lanes "Direct link to heading") + +Visit our [Lane App Store](https://docs.skip.money/blocksdk/lanes/existing-lanes/mev) and select the `lanes` you want, or [Build Your Own](https://docs.skip.money/blocksdk/lanes/build-your-own-lane). \ No newline at end of file diff --git a/docs/lane-app-store/build-your-own-lane.mdx b/docs/lane-app-store/build-your-own-lane.mdx new file mode 100644 index 00000000..0e2340a5 --- /dev/null +++ b/docs/lane-app-store/build-your-own-lane.mdx @@ -0,0 +1,405 @@ +--- +title: "Build Your Own Lane" +--- + +**Background** + +Before reading over this section, it is highly recommended that developers read over the [**How It Works**](/blocksdk/how-it-works) page. + +If you have not already, this assumes you have completed the [General Setup]/block-sdk/integrate) guide first! + +This is an 🚨 **advanced** 🚨 setup, please [**reach out to us**](https://skip.money/contact) if you need help! + +## πŸ’‘ Overview[](#-overview "Direct link to heading") + +The **Base Lane** is a generic implementation of a lane. It comes out-of-the-box with default implementations for all the required interfaces. It is meant to be used as a starting point for building your own lane. + +With it, developers can build their own lane(s) in less than 10 minutes! + +## πŸ“¦ Dependencies[](#-dependencies "Direct link to heading") + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +## πŸ“₯ Installation[](#-installation "Direct link to heading") + +To install the Block SDK, run the following command: + +``` +$ go install github.com/skip-mev/block-sdk + +``` + +## πŸ€” How to use it \[30 min\][](#-how-to-use-it-30-min "Direct link to heading") + +There are **five** required components to building a custom lane using the base lane: + +1. `Mempool` \- The lane's mempool is responsible for storing transactions that have been verified and are waiting to be included in proposals. +2. `MatchHandler` \- This is responsible for determining whether a transaction should belong to this lane. +3. \[**OPTIONAL**\] `PrepareLaneHandler` \- Allows developers to define their own handler to customize the how transactions are verified and ordered before they are included into a proposal. +4. \[**OPTIONAL**\] `CheckOrderHandler` \- Allows developers to define their own handler that will run any custom checks on whether transactions included in block proposals are in the correct order (respecting the ordering rules of the lane and the ordering rules of the other lanes). +5. \[**OPTIONAL**\] `ProcessLaneHandler` \- Allows developers to define their own handler for processing transactions that are included in block proposals. +6. `Configuration` \- Configure high-level options for your lane. + +### 1\. πŸ—„οΈ Mempool[](#1-️-mempool "Direct link to heading") + +This is the data structure that is responsible for storing transactions as they are being verified and are waiting to be included in proposals.`block/base/mempool.go` provides an out-of-the-box implementation that should be used as a starting point for building out the mempool and should cover most use cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: + +* Implements a `GetTxPriority` method that returns the priority (as defined by the type `[C]`) of a given transaction. +* Implements a `Compare` method that returns the relative priority of two transactions. If the first transaction has a higher priority, the method should return -1, if the second transaction has a higher priority the method should return 1, otherwise the method should return 0. +* Implements a `MinValue` method that returns the minimum priority value that a transaction can have. + +The default implementation can be found in `block/base/mempool.go`. + +> Scenario What if we wanted to prioritize transactions by the amount they have staked on a chain? + +We could do the following: + +```cs +// CustomTxPriority returns a TxPriority that prioritizes transactions by the +// amount they have staked on chain. This means that transactions with a higher +// amount staked will be prioritized over transactions with a lower amount staked. +func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] { + return TxPriority[string]{ + GetTxPriority: func(ctx context.Context, tx sdk.Tx) string { + // Get the signer of the transaction. + signer := p.getTransactionSigner(tx) + + // Get the total amount staked by the signer on chain. + // This is abstracted away in the example, but you can + // implement this using the staking keeper. + totalStake, err := p.getTotalStake(ctx, signer) + if err != nil { + return "" + } + + return totalStake.String() + }, + Compare: func(a, b string) int { + aCoins, _ := sdk.ParseCoinsNormalized(a) + bCoins, _ := sdk.ParseCoinsNormalized(b) + + switch { + case aCoins == nil && bCoins == nil: + return 0 + + case aCoins == nil: + return -1 + + case bCoins == nil: + return 1 + + default: + switch { + case aCoins.IsAllGT(bCoins): + return 1 + + case aCoins.IsAllLT(bCoins): + return -1 + + default: + return 0 + } + } + }, + MinValue: "", + } +} + +``` + +#### Using a Custom TxPriority[](#using-a-custom-txpriority "Direct link to heading") + +To utilize this new priority configuration in a lane, all you have to then do is pass in the `TxPriority[C]` to the `NewMempool` function. + +```cs +// Create the lane config +laneCfg := NewLaneConfig( + ... + MaxTxs: 100, + ... +) + +// Pseudocode for creating the custom tx priority +priorityCfg := NewPriorityConfig( + stakingKeeper, + accountKeeper, + ... +) + +// define your mempool that orders transactions by on-chain stake +mempool := base.NewMempool[string]( + priorityCfg.CustomTxPriority(), // pass in the custom tx priority + laneCfg.TxEncoder, + laneCfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := base.NewBaseLane( + laneCfg, + LaneName, + mempool, + base.DefaultMatchHandler(), +) + +``` + +### 2\. 🀝 MatchHandler[](#2--matchhandler "Direct link to heading") + +`MatchHandler` is utilized to determine if a transaction should be included in the lane. **This function can be a stateless or stateful check on the transaction!** The default implementation can be found in `block/base/handlers.go`. + +The match handler can be as custom as desired. Following the example above, if we wanted to make a lane that only accepts transactions if they have a large amount staked, we could do the following: + +```cs +// CustomMatchHandler returns a custom implementation of the MatchHandler. It +// matches transactions that have a large amount staked. These transactions +// will then be charged no fees at execution time. +// +// NOTE: This is a stateful check on the transaction. The details of how to +// implement this are abstracted away in the example, but you can implement +// this using the staking keeper. +func (h *Handler) CustomMatchHandler() base.MatchHandler { + return func(ctx sdk.Context, tx sdk.Tx) bool { + if !h.IsStakingTx(tx) { + return false + } + + signer, err := getTxSigner(tx) + if err != nil { + return false + } + + stakedAmount, err := h.GetStakedAmount(signer) + if err != nil { + return false + } + + // The transaction can only be considered for inclusion if the amount + // staked is greater than some predetermined threshold. + return stakeAmount.GT(h.Threshold) + } +} + +``` + +#### Using a Custom MatchHandler[](#using-a-custom-matchhandler "Direct link to heading") + +If we wanted to create the lane using the custom match handler along with the custom mempool, we could do the following: + +```cs +// Pseudocode for creating the custom match handler +handler := NewHandler( + stakingKeeper, + accountKeeper, + ... +) + +// define your mempool that orders transactions by on chain stake +mempool := base.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := base.NewBaseLane( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) + +``` + +### \[OPTIONAL\] Steps 3-5[](#optional-steps-3-5 "Direct link to heading") + +The remaining steps walk through the process of creating custom block building/verification logic. The default implementation found in`block/base/handlers.go` should fit most use cases. Please reference that file for more details on the default implementation and whether it fits your use case. + +Implementing custom block building/verification logic is a bit more involved than the previous steps and is a all or nothing approach. This means that if you implement any of the handlers, you must implement all of them in most cases. If you do not implement all of them, the lane may have unintended behavior. + +### 3\. πŸ› οΈ PrepareLaneHandler[](#3-️-preparelanehandler "Direct link to heading") + +The `PrepareLaneHandler` is an optional field you can set on the base lane. This handler is responsible for the transaction selection logic when a new proposal is requested. + +The handler should return the following for a given lane: + +1. The transactions to be included in the block proposal. +2. The transactions to be removed from the lane's mempool. +3. An error if the lane is unable to prepare a block proposal. + +```cs +// PrepareLaneHandler is responsible for preparing transactions to be included +// in the block from a given lane. Given a lane, this function should return +// the transactions to include in the block, the transactions that must be +// removed from the lane, and an error if one occurred. +PrepareLaneHandler func(ctx sdk.Context,proposal BlockProposal,maxTxBytes int64) + (txsToInclude [][]byte, txsToRemove []sdk.Tx, err error) + +``` + +The default implementation is simple. It will continue to select transactions from its mempool under the following criteria: + +1. The transactions is not already included in the block proposal. +2. The transaction is valid and passes the AnteHandler check. +3. The transaction is not too large to be included in the block. + +If a more involved selection process is required, you can implement your own`PrepareLaneHandler` and and set it after creating the base lane. + +```cs +// Pseudocode for creating the custom prepare lane handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom PrepareLaneHandler on the lane +customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) + +``` + +### 4\. βœ… CheckOrderHandler[](#4--checkorderhandler "Direct link to heading") + +The `CheckOrderHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the ordering of the transactions in the block proposal that belong to the lane. + +```cs +// CheckOrderHandler is responsible for checking the order of transactions that +// belong to a given lane. This handler should be used to verify that the +// ordering of transactions passed into the function respect the ordering logic +// of the lane (if any transactions from the lane are included). This function +// should also ensure that transactions that belong to this lane are contiguous +// and do not have any transactions from other lanes in between them. +CheckOrderHandler func(ctx sdk.Context, txs []sdk.Tx) error + +``` + +The default implementation is simple and utilizes the same `TxPriority` struct that the mempool uses to determine if transactions are in order. The criteria for determining if transactions are in order is as follows: + +1. The transactions are in order according to the `TxPriority` struct. i.e. any two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a higher priority than `tx2` should be ordered before `tx2`. +2. The transactions are contiguous. i.e. there are no transactions from other lanes in between the transactions that belong to this lane. i.e. if `tx1` and`tx2` belong to the lane, there should be no transactions from other lanes in between `tx1` and `tx2`. + +If a more involved ordering process is required, you can implement your own`CheckOrderHandler` and and set it after creating the base lane. + +```cs +// Pseudocode for creating the custom check order handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom CheckOrderHandler on the lane +customLane.SetCheckOrderHandler(customlane.CheckOrderHandler()) + +``` + +### 5\. πŸ†— ProcessLaneHandler[](#5--processlanehandler "Direct link to heading") + +The `ProcessLaneHandler` is an optional field you can set on the base lane. This handler is responsible for verifying the transactions in the block proposal that belong to the lane. This handler is executed after the `CheckOrderHandler`so the transactions passed into this function SHOULD already be in order respecting the ordering rules of the lane and respecting the ordering rules of mempool relative to the lanes it has. This means that if the first transaction does not belong to the lane, the remaining transactions should not belong to the lane either. + +```cs +// ProcessLaneHandler is responsible for processing transactions that are +// included in a block and belong to a given lane. ProcessLaneHandler is +// executed after CheckOrderHandler so the transactions passed into this +// function SHOULD already be in order respecting the ordering rules of the +// lane and respecting the ordering rules of mempool relative to the lanes it has. +ProcessLaneHandler func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) + +``` + +Given the invarients above, the default implementation is simple. It will continue to verify transactions in the block proposal under the following criteria: + +1. If a transaction matches to this lane, verify it and continue. If it is not valid, return an error. +2. If a transaction does not match to this lane, return the remaining transactions to the next lane to process. + +Similar to the setup of handlers above, if a more involved verification process is required, you can implement your own `ProcessLaneHandler` and and set it after creating the base lane. + +```cs +// Pseudocode for creating the custom check order handler +// This assumes that the CustomLane inherits from the base +// lane. +customLane := NewCustomLane( + cfg, + mempool, + handler.CustomMatchHandler(), +) + +// Set the custom ProcessLaneHandler on the lane +customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler()) + +``` + +### 6\. πŸ“ Lane Configuration[](#6--lane-configuration "Direct link to heading") + +Once you have created your custom lane, you can configure it in the application by doing the following: + +1. Create a custom `LaneConfig` struct that defines the configuration of the lane. +2. Instantiate the lane with the custom `LaneConfig` struct alongside any other dependencies (mempool, match handler, etc.). +3. Instantiate a new `LanedMempool` with the custom lane. +4. Set the `LanedMempool` on the `BaseApp` instance. +5. Set up the proposal handlers of the Block SDK to use your lane. +6. That's it! You're done! + +The lane config (`LaneConfig`) is a simple configuration object that defines the desired amount of block space the lane should utilize when building a proposal, an antehandler that is used to verify transactions as they are added/verified to/in a proposal, and more. By default, we recommend that user's pass in all of the base apps configurations (txDecoder, logger, etc.). A sample`LaneConfig` might look like the following: + +```cs +config := base.LaneConfig{ + Logger: app.Logger(), + TxDecoder: app.TxDecoder(), + TxEncoder: app.TxEncoder(), + AnteHandler: app.AnteHandler(), + MaxTxs: 0, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []block.Lane{}, +} + +``` + +The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and `MaxBlockSpace`. + +#### **AnteHandler**[](#antehandler "Direct link to heading") + +With the default implementation, the `AnteHandler` is responsible for verifying transactions as they are being considered for a new proposal or are being processed in a proposed block. We recommend user's utilize the same antehandler chain that is used in the base app. If developers want a certain `AnteDecorator`to be ignored if it qualifies for a given lane, they can do so by using the`NewIgnoreDecorator` defined in `block/utils/ante.go`. + +For example, a free lane might want to ignore the `DeductFeeDecorator` so that its transactions are not charged any fees. Where ever the `AnteHandler` is defined, we could add the following to ignore the `DeductFeeDecorator`: + +```cs +anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ..., + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ..., +} + +``` + +Anytime a transaction that qualifies for the free lane is being processed, the`DeductFeeDecorator` will be ignored and no fees will be deducted! + +#### **MaxTxs**[](#maxtxs "Direct link to heading") + +This sets the maximum number of transactions allowed in the mempool with the semantics: + +* if `MaxTxs` \== 0, there is no cap on the number of transactions in the mempool +* if `MaxTxs` \> 0, the mempool will cap the number of transactions it stores, and will prioritize transactions by their priority and sender-nonce (sequence number) when evicting transactions. +* if `MaxTxs` < 0, `Insert` is a no-op. + +#### **MaxBlockSpace**[](#maxblockspace "Direct link to heading") + +MaxBlockSpace is the maximum amount of block space that the lane will attempt to fill when building a proposal. This parameter may be useful lanes that should be limited (such as a free or onboarding lane) in space usage. Setting this to 0 will allow the lane to fill the block with as many transactions as possible. + +If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a`MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes. + +#### **\[OPTIONAL\] IgnoreList**[](#optional-ignorelist "Direct link to heading") + +`IgnoreList` defines the list of lanes to ignore when processing transactions. For example, say there are two lanes: default and free. The free lane is processed after the default lane. In this case, the free lane should be added to the ignore list of the default lane. Otherwise, the transactions that belong to the free lane will be processed by the default lane (which accepts all transactions by default). \ No newline at end of file diff --git a/docs/lane-app-store/choose-your-lanes/custom-fee-market-lane.mdx b/docs/lane-app-store/choose-your-lanes/custom-fee-market-lane.mdx new file mode 100644 index 00000000..6da5fbed --- /dev/null +++ b/docs/lane-app-store/choose-your-lanes/custom-fee-market-lane.mdx @@ -0,0 +1,5 @@ +--- +title: "Custom Fee" +--- + +### πŸ‘€ Coming Soon![](#-coming-soon "Direct link to heading") \ No newline at end of file diff --git a/docs/lane-app-store/choose-your-lanes/free-lane.mdx b/docs/lane-app-store/choose-your-lanes/free-lane.mdx new file mode 100644 index 00000000..af9809b0 --- /dev/null +++ b/docs/lane-app-store/choose-your-lanes/free-lane.mdx @@ -0,0 +1,156 @@ +--- +title: "Free Lane" +--- + + +**TLDR** + +The `Free Lane` allows certain transactions to be included in a block without paying fees. This lane can be used to encourage certain behaviors on the chain, such as staking, governance, or others. + +If you have not already, this assumes you have completed the [General Setup](/blocksdk/integrate-the-sdk) guide first! + +Please [**reach out to us**](https://skip.money/contact) if you need help! + + + +### πŸ“– Overview[](#-overview "Direct link to heading") + +The free lane closely follows the block building logic of the default lane, with exception for the following: + +* Transactions can only be included in the free lane if they are considered free (as defined by the lane's `MatchHandler`). The default implementation matches transactions to the free lane iff the transaction is staking related (e.g. stake, re-delegate, etc.). +* By default, the ordering of transactions in the free lane is based on the transaction's fee amount (highest to lowest). However, this can be overridden to support ordering mechanisms that are not based on fee amount (e.g. ordering based on the user's on-chain stake amount). + +The free lane implements the same `ABCI++` interface as the other lanes, and does the same verification logic as the [default lane](/blocksdk/integrate-the-sdk). The free lane's `PrepareLane` handler will reap transactions from the lane up to the `MaxBlockSpace` limit, and the `ProcessLane` handler will ensure that the transactions are ordered based on their fee amount (by default) and pass the same checks done in `PrepareLane`. + +### πŸ“– Set Up \[10 mins\][](#-set-up-10-mins "Direct link to heading") + +**At a high level, to integrate the MEV Lane, chains must:** + +1. Be using Cosmos SDK version or higher `v0.47.0`. +2. Import and configure the `Free Lane` (alongside any other desired lanes) into their base app. +3. Import and configure the Block SDK mempool into their base app. +4. Import and configure the Block SDK `Prepare` / `Process` proposal handlers into their base app. + +## πŸ“¦ Dependencies[](#-dependencies "Direct link to heading") + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +### Release Compatibility Matrix[](#release-compatibility-matrix "Direct link to heading") + +| Block SDK Version | Cosmos SDK | +| ----------------- | ---------- | +| v1.x.x | v0.47.x | +| v2.x.x | v0.50.x | + +## πŸ“₯ Installation[](#-installation "Direct link to heading") + +To install the Block SDK, run the following command: + +```bash +$ go install github.com/skip-mev/block-sdk + +``` + +## πŸ“š Usage[](#-usage "Direct link to heading") + +1. First determine the set of lanes that you want to use in your application. The available lanes can be found in our[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). In your base application, you will need to create a `LanedMempool` composed of the lanes you want to use. _The free lane should not exist on its own. At minimum, it is recommended that the free lane is paired with the default lane._ +2. Next, order the lanes by priority. The first lane is the highest priority lane and the last lane is the lowest priority lane. +3. Set up your `FeeDeductorDecorator` to ignore the free lane where ever you initialize your `AnteHandler`. This will ensure that the free lane is not subject to deducting transaction fees. +4. You will also need to create a `PrepareProposalHandler` and a`ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. + +NOTE: This example walks through setting up the Free and Default lanes. + +```cs +import ( + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/block/base" + defaultlane "github.com/skip-mev/block-sdk/lanes/base" + freelane "github.com/skip-mev/block-sdk/lanes/free" +) + +... + +func NewApp() { + ... + // 1. Create the lanes. + // + // NOTE: The lanes are ordered by priority. The first lane is the highest priority + // lane and the last lane is the lowest priority lane. Top of block lane allows + // transactions to bid for inclusion at the top of the next block. + // + // For more information on how to utilize the LaneConfig please + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. + // + // Set up the configuration of the free lane and instantiate it. + freeConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + freeLane := freelane.NewFreeLane(freeConfig, base.DefaultTxPriority(), freelane.DefaultMatchHandler()) + + // Default lane accepts all transactions. + defaultConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, + } + defaultLane := defaultlane.NewDefaultLane(defaultConfig) + + // 2. Set up the relative priority of lanes + lanes := []block.Lane{ + freeLane, + defaultLane, + } + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) + app.App.SetMempool(mempool) + + ... + + // 3. Set up the ante handler. + // + // This will allow any transaction that matches the to the free lane to + // be processed without paying any fees. + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + + // Set the lane ante handlers on the lanes. + // + // NOTE: This step is very important. Without the antehandlers, lanes will not + // be able to verify transactions. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + + // 4. Set the abci handlers on base app + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.TxConfig().TxDecoder(), + mempool, + ) + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + + ... +} + +``` \ No newline at end of file diff --git a/docs/lane-app-store/choose-your-lanes/mev-lane.mdx b/docs/lane-app-store/choose-your-lanes/mev-lane.mdx new file mode 100644 index 00000000..ca59cdd5 --- /dev/null +++ b/docs/lane-app-store/choose-your-lanes/mev-lane.mdx @@ -0,0 +1,253 @@ +--- +title: "MEV Lane" +--- + + +**TLDR** + +The `MEV Lane` allows top-of-block MEV auctions in-protocol, with revenue being redistributed to chains. + +If you have not already, we recommend following the [General Setup]/block-sdk/integrate) guide first! + +Please [**reach out to us**](https://skip.money/contact) if you need help! + + + +### πŸ’° Overview[](#-overview "Direct link to heading") + +Blockspace is valuable, and MEV bots find arbitrage opportunities to capture value. The `MEV Lane` provides a fair auction for these by leveraging the `x/auction` module. The `MEVLane` ensures that proposals are constructed in accordance with the `x/auction`'s view of all bids, and the `x/auction` is responsible for ordering bids + keeping track of bids + selecting auction winners. + +The Block SDK uses the app-side `LanedMempool`, `PrepareLane` / `ProcessLane`, and `CheckTx` to create an MEV marketplace inside the protocol. It introduces a new message type, called a `MsgAuctionBid`, that allows the submitter to execute multiple transactions at the **top of the block atomically** atomically. + + +**atomicity** here refers to **transaction inclusion** and not **transaction execution**. The `x/auction` module does not make any guarantees for atomic execution, some transactions in a bundle may fail!! + + + +This means that β€˜searchers’ can find opportunities in the mempool, backrun them, and submit them at the top of the block. This covers most MEV recapture via arbitrage, liquidations, backrunning, oracle-updates, etc. It can be configured to **not allow** for sandwich attacks or harmful MEV. + +### πŸ“– Set Up \[10 mins\][](#-set-up-10-mins "Direct link to heading") + +**At a high level, to integrate the MEV Lane, chains must:** + +1. Be using Cosmos SDK version or higher `v0.47.0`. +2. Import and configure the `MEV Lane` (alongside any other desired lanes) into their base app. +3. Import and configure the Block SDK mempool into their base app. +4. Import and configure the Block SDK `Prepare` / `Process` proposal handlers into their base app. +5. Import and instantiate the `x/auction` module into their base app. + +## πŸ“¦ Dependencies[](#-dependencies "Direct link to heading") + +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. + +The `MEVLane` requires that apps use the `LanedMempool` as their app-side mempool, see [here]/block-sdk/integrate) + +### Release Compatibility Matrix[](#release-compatibility-matrix "Direct link to heading") + +| Block SDK Version | Cosmos SDK | +| ----------------- | ---------- | +| v1.x.x | v0.47.x | +| v2.x.x | v0.50.x | + +## πŸ“₯ Installation[](#-installation "Direct link to heading") + +To install the Block SDK, run the following command: + +```bash +$ go install github.com/skip-mev/block-sdk + +``` + +## πŸ“š Usage[](#-usage "Direct link to heading") + +1. This guide assumes you have already set up the [Block SDK (and the default lane)](https://docs.skip.money/chains/overview) +2. You will need to instantiate the `x/auction` module into your application. This module is responsible for processing auction transactions and distributing revenue to the auction house. The `x/auction` module is also responsible for ensuring the validity of auction transactions, and maintaining the set of bids / revenue distribution. The `MEVLane` is responsible for constructing blocks with transactions from the winning bid. +3. Next, add the MEV lane into the `lane` object on your `app.go`. The first lane is the highest priority lane and the last lane is the lowest priority lane. Since the MEV lane is meant to auction off the top of the block, **it should be the highest priority lane**. The default lane should follow. +4. You will also need to create a `PrepareProposalHandler` and a`ProcessProposalHandler` that will be responsible for preparing and processing proposals respectively. Configure the order of the lanes in the`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the lanes in the `LanedMempool`. + +NOTE: This example walks through setting up the MEV and Default lanes. + +1. Import the necessary dependencies into your application. This includes the Block SDK proposal handlers + mempool, keeper, auction types, and auction module. This tutorial will go into more detail into each of the dependencies. +```cs +import ( + ... + "github.com/skip-mev/block-sdk/abci" + "github.com/skip-mev/block-sdk/lanes/mev" + "github.com/skip-mev/block-sdk/lanes/base" + auctionmodule "github.com/skip-mev/block-sdk/x/auction" + auctionkeeper "github.com/skip-mev/block-sdk/x/auction/keeper" + auctiontypes "github.com/skip-mev/block-sdk/x/auction/types" + auctionante "github.com/skip-mev/block-sdk/x/auction/ante" + ... +) +``` +2. Add the `x/auction` module to the the `AppModuleBasic` manager. This manager is in charge of setting up basic, non-dependent module elements such as codec registration and genesis verification. This will register the special`MsgAuctionBid` message. When users want to bid for top of block execution, they will submit a transaction - which we call an auction transaction - that includes a single `MsgAuctionBid`. We prevent any other messages from being included in auction transaction to prevent malicious behavior - such as front running or sandwiching. +``` +var ( + ModuleBasics = module.NewBasicManager( + ... + auctionmodule.AppModuleBasic{}, + ) + ... +) +``` +3. The auction `Keeper` is MEV lane's gateway to processing special `MsgAuctionBid`messages that allow users to participate in the top of block auction, distribute revenue to the auction house, and ensure the validity of auction transactions. +a. First add the keeper to the app's struct definition. We also want to add MEV lane's custom checkTx handler to the app's struct definition. This will allow us to override the default checkTx handler to process bid transactions before they are inserted into the `LanedMempool`. NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious users. +```cs +type App struct { +... +// auctionKeeper is the keeper that handles processing auction transactions +AuctionKeeper auctionkeeper.Keeper +// Custom checkTx handler +checkTxHandler mev.CheckTx +} +``` +b. Add the auction module to the list of module account permissions. This will instantiate the auction module account on genesis. +```cs +maccPerms = map[string][]string{ +auction.ModuleName: nil, +... +} +``` +c. Instantiate the Block SDK's `LanedMempool` with the application's desired lanes. +```cs +// 1. Create the lanes. +// +// NOTE: The lanes are ordered by priority. The first lane is the +// highest priority +// lane and the last lane is the lowest priority lane. Top of block +// lane allows transactions to bid for inclusion at the top of the next block. +// +// For more information on how to utilize the LaneConfig please +// visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. +// +// MEV lane hosts an auction at the top of the block. +mevConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +mevLane := mev.NewMEVLane( + mevConfig, + mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), +) +// default lane accepts all other transactions. +defaultConfig := base.LaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, +} +defaultLane := base.NewStandardLane(defaultConfig) +// 2. Set up the relative priority of lanes +lanes := []block.Lane{ + mevLane, + defaultLane, +} +mempool := block.NewLanedMempool(app.Logger(), true, lanes...) +app.App.SetMempool(mempool) +``` +d. Add the `x/auction` module's `AuctionDecorator` to the ante-handler chain. The `AuctionDecorator` is an AnteHandler decorator that enforces various chain configurable MEV rules. +``` +anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + ... + auctionante.NewauctionDecorator( + options.auctionKeeper, + options.TxEncoder, + options.TOBLane, + options.Mempool, + ), +} +anteHandler := sdk.ChainAnteDecorators(anteDecorators...) +app.SetAnteHandler(anteHandler) +// Set the antehandlers on the lanes. +// +// NOTE: This step is required as otherwise the lanes will not be able to +// process auction transactions. +for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) +} +app.App.SetAnteHandler(anteHandler) +``` +e. Instantiate the auction keeper, store keys, and module manager. Note, be sure to do this after all the required keeper dependencies have been instantiated. +``` +keys := storetypes.NewKVStoreKeys( + auctiontypes.StoreKey, + ... +) +... +app.auctionKeeper := auctionkeeper.NewKeeper( + appCodec, + keys[auctiontypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.DistrKeeper, + app.StakingKeeper, + authtypes.NewModuleAddress(govv1.ModuleName).String(), +) +``` + + app.ModuleManager = module.NewManager( auction.NewAppModule(appCodec, app.auctionKeeper), ... ) + +``` + +f. Configure the proposal/checkTx handlers on base app. + +```go +// Create the proposal handler that will be used to build and validate blocks. +proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.txConfig.TxDecoder(), + mempool, +) +app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) +app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + +// Set the custom CheckTx handler on BaseApp. +checkTxHandler := mev.NewCheckTxHandler( + app.App, + app.txConfig.TxDecoder(), + mevLane, + anteHandler, +) +app.SetCheckTx(checkTxHandler.CheckTx()) + +// CheckTx will check the transaction with the provided checkTxHandler. +// We override the default handler so that we can verify transactions +// before they are inserted into the mempool. With the CheckTx, we can +// verify the bid transaction and all of the bundled transactions +// before inserting the bid transaction into the mempool. +func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) + (*cometabci.ResponseCheckTx, error) { + return app.checkTxHandler(req) +} + +// SetCheckTx sets the checkTxHandler for the app. +func (app *TestApp) SetCheckTx(handler mev.CheckTx) { + app.checkTxHandler = handler +} + +``` + + g. Finally, update the app's `InitGenesis` order. + +```go +genesisModuleOrder := []string{ + auctiontypes.ModuleName, + ..., +} + +``` + +## Params[](#params "Direct link to heading") + +* **MaxBundleSize** \- What is the maximal number of transactions a bundle can have? +* **EscrowAccountAddress** \- Where does the chain collect bid revenue? Notice, this is an `sdk.AccAdress` and is configured on genesis, or via `ParamChange` proposals. +* **ReserveFee** \- The minimum possible bid. Notice, no bids less than the `ReserveFee` will be accepted. +* **MinBidIncrement** \- This is the minimum difference from the max bid that `x/auction` module will accept. I.e if the current max bid is `12ujuno`, and `MinBidIncrement = 1`, then all new bids must be greater than `13ujuno` to be considered. +* **FrontRunningProtection** \- This determines whether front-running bundles will be accepted by the `x/auction` module +* **ProposerFee** \- This is a fractional value, i.e `0 <= ProposerFee <= 1`, this determines how much of the winning bid from the previous block goes to the proposer of that block, the rest will be sent to the `EscrowAccountAddress` \ No newline at end of file diff --git a/docs/overview.mdx b/docs/overview.mdx new file mode 100644 index 00000000..ce16a2de --- /dev/null +++ b/docs/overview.mdx @@ -0,0 +1,72 @@ +--- +title: "Block SDK Overview" +sidebarTitle: Overview +--- + +## πŸ€” What is the Block SDK?[](#what-is-the-block-sdk "Direct link to heading") + +🌐 **the Block SDK is a toolkit for building customized blocks** + +The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. + +Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs. + +🚦 **Blocks are like highways** + +Let's say you're the designer of a 4 lane highway. You'd want a paid lane, for fast drivers who'd like to be separated from other lanes. You'd like a lane for large vehicles, you can configure this lane to be wider, require more space between vehicles, etc. The other two lanes are for the rest of traffic. The beauty here, is that as the owner of the highway, you get to decide what vehicles (transactions) you'll allow, and how they can behave (ordering)!! + +### If you've been here before + +#### [Integrate Block-SDK](/block-sdk/integrate) + +#### [Building your own Lane](/block-sdk/lane-app-store/build-your-own-lane) + +#### [Searcher docs for MEV Lane](/block-sdk/for-searchers) + +## ❌ Problems: Blocks are not Customizable + +Most Cosmos chains today utilize traditional block construction - which is too limited. + +* Traditional block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol. +* Traditional block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets. +* Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be. + +## βœ… Solution: The Block SDK[](#-solution-the-block-sdk "Direct link to heading") + +You can think of the Block SDK as a **transaction `highway` system**, where each`lane` on the highway serves a specific purpose and has its own set of rules and traffic flow. + +In the Block SDK, each `lane` has its own set of rules and transaction flow management systems. + +* A `lane` is what we might traditionally consider to be a standard mempool where transaction **_validation_**, **_ordering_** and **_prioritization_** for contained transactions are shared. +* `lanes` implement a **standard interface** that allows each individual `lane` to propose and validate a portion of a block. +* `lanes` are ordered with each other, configurable by developers. All `lanes`together define the desired block structure of a chain. + +## ✨ Block SDK Use Cases[](#-block-sdk-use-cases "Direct link to heading") + +A block with separate `lanes` can be used for: + +1. **MEV mitigation**: a top of block lane could be designed to create an in-protocol top-of-block [auction](/blocksdk/lanes/existing-lanes/mev) to recapture MEV in a transparent and governable way. +2. **Free/reduced fee txs**: transactions with certain properties (e.g. from trusted accounts or performing encouraged actions) could leverage a free lane to facilitate _good_ behavior. +3. **Dedicated oracle space** Oracles could be included before other kinds of transactions to ensure that price updates occur first, and are not able to be sandwiched or manipulated. +4. **Orderflow auctions**: an OFA lane could be constructed such that order flow providers can have their submitted transactions bundled with specific backrunners, to guarantee MEV rewards are attributed back to users +5. **Enhanced and customizable privacy**: privacy-enhancing features could be introduced, such as threshold encrypted lanes, to protect user data and maintain privacy for specific use cases. +6. **Fee market improvements**: one or many fee markets - such as EIP-1559 - could be easily adopted for different lanes (potentially custom for certain dApps). Each smart contract/exchange could have its own fee market or auction for transaction ordering. +7. **Congestion management**: segmentation of transactions to lanes can help mitigate network congestion by capping usage of certain applications and tailoring fee markets. + +## πŸŽ† Chains Currently Using the Block-SDK[](#-chains-currently-using-the-block-sdk "Direct link to heading") + +### Mainnets[](#mainnets "Direct link to heading") + +| Chain Name | Chain-ID | Block-SDK Version | +| ----------- | ------------- | ----------------- | +| Juno | juno-1 | v1.0.2 | +| Persistence | persistence-1 | v1.0.2 | +| Initia | NA | v1.0.2 | +| Prism | NA | v1.0.2 | +| Terra | phoenix-1 | v1.0.2 | + +### Testnets[](#testnets "Direct link to heading") + +| Chain Name | Chain-ID | Block-SDK Version | +| ---------- | -------- | ----------------- | +| Juno | uni-6 | v1.0.2 | \ No newline at end of file diff --git a/favicon.png b/favicon.png new file mode 100644 index 00000000..0975457d Binary files /dev/null and b/favicon.png differ diff --git a/images/1.png b/images/1.png new file mode 100644 index 00000000..6213a44f Binary files /dev/null and b/images/1.png differ diff --git a/images/2.png b/images/2.png new file mode 100644 index 00000000..4d7b56d1 Binary files /dev/null and b/images/2.png differ diff --git a/images/3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png b/images/3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png new file mode 100644 index 00000000..4d2f2207 Binary files /dev/null and b/images/3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png differ diff --git a/images/443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png b/images/443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png new file mode 100644 index 00000000..e0956ab6 Binary files /dev/null and b/images/443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png differ diff --git a/images/51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png b/images/51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png new file mode 100644 index 00000000..15fd2000 Binary files /dev/null and b/images/51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png differ diff --git a/images/5b993ce-swap-warning-image.png b/images/5b993ce-swap-warning-image.png new file mode 100644 index 00000000..cc21717a Binary files /dev/null and b/images/5b993ce-swap-warning-image.png differ diff --git a/images/66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png b/images/66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png new file mode 100644 index 00000000..e475e4b3 Binary files /dev/null and b/images/66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png differ diff --git a/images/a61a075-swap-warning-page-image.png b/images/a61a075-swap-warning-page-image.png new file mode 100644 index 00000000..e67d0b96 Binary files /dev/null and b/images/a61a075-swap-warning-page-image.png differ diff --git a/images/prepare-ab3b2b6898825f3eb78a2c901a9436e8.svg b/images/prepare-ab3b2b6898825f3eb78a2c901a9436e8.svg new file mode 100644 index 00000000..2a899518 --- /dev/null +++ b/images/prepare-ab3b2b6898825f3eb78a2c901a9436e8.svg @@ -0,0 +1,21 @@ + + + + + + + + VEs* well formed* contains 2/3 stakeVerifyProposalInjected VEPrice DataPrepare Proposal \ No newline at end of file diff --git a/images/sidecar-18cc25dad8b4ca6d0ac7afc01b7511e0.svg b/images/sidecar-18cc25dad8b4ca6d0ac7afc01b7511e0.svg new file mode 100644 index 00000000..f4a944b8 --- /dev/null +++ b/images/sidecar-18cc25dad8b4ca6d0ac7afc01b7511e0.svg @@ -0,0 +1,21 @@ + + + + + + + + Chain ApplicationCrypto.comCoinbaseMexcKucoinBinance& MorePricesOracle SidecarPrice Providers \ No newline at end of file diff --git a/images/slinky-arch-cbd6c2e4e5b05737441aed9995c278e0.png b/images/slinky-arch-cbd6c2e4e5b05737441aed9995c278e0.png new file mode 100644 index 00000000..5133cff7 Binary files /dev/null and b/images/slinky-arch-cbd6c2e4e5b05737441aed9995c278e0.png differ diff --git a/images/slinky-banner.png b/images/slinky-banner.png new file mode 100644 index 00000000..2f052953 Binary files /dev/null and b/images/slinky-banner.png differ diff --git a/images/slinky-town-crier.png b/images/slinky-town-crier.png new file mode 100644 index 00000000..9620983f Binary files /dev/null and b/images/slinky-town-crier.png differ diff --git a/images/slinky_customers.png b/images/slinky_customers.png new file mode 100644 index 00000000..0fede17e Binary files /dev/null and b/images/slinky_customers.png differ diff --git a/images/slinky_math.png b/images/slinky_math.png new file mode 100644 index 00000000..d7f3fc1a Binary files /dev/null and b/images/slinky_math.png differ diff --git a/logo/dark.png b/logo/dark.png new file mode 100644 index 00000000..624a4e35 Binary files /dev/null and b/logo/dark.png differ diff --git a/logo/light.png b/logo/light.png new file mode 100644 index 00000000..6c5f85ee Binary files /dev/null and b/logo/light.png differ diff --git a/mint.json b/mint.json new file mode 100644 index 00000000..079939a7 --- /dev/null +++ b/mint.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Skip", + "logo": { + "light": "/logo/light.png", + "dark": "/logo/dark.png" + }, + "favicon": "/favicon.png", + "colors": { + "primary": "#0E0E0E", + "light": "#FAFAFA", + "dark": "#0E0E0E", + "background": { + "dark": "#151617" + } + }, + "topbarLinks": [ + { + "name": "Products", + "url": "https://skip.money/products" + }, + { + "name": "Contact", + "url": "https://skip.money/contact" + }, + { + "name": "Research", + "url": "https://ideas.skip.money" + }, + { + "name": "Jobs", + "url": "https://jobs.skip.money" + } + ], + "modeToggle": { + "default": "dark" + }, + "topAnchor": { + "name": "Block SDK", + "icon": "code" + }, + "anchors": [ + { + "name": "Home", + "icon": "house", + "url": "https://skip.mintlify.app" + } + ], + "navigation": [ + { + "group": "Block SDK", + "pages": [ + "docs/overview", + "docs/integrate", + "docs/how-it-works", + "docs/for-searchers", + { + "group": "Lane App Store", + "pages": [ + { + "group": "Choose Your Lanes", + "pages": [ + "docs/lane-app-store/choose-your-lanes/mev-lane", + "docs/lane-app-store/choose-your-lanes/free-lane", + "docs/lane-app-store/choose-your-lanes/custom-fee-market-lane" + ] + }, + "docs/lane-app-store/build-your-own-lane" + ] + } + ] + } + ], + "footerSocials": { + "twitter": "https://twitter.com/skipprotocol", + "discord": "https://discord.com/invite/hFeHVAE26P" + } +} \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 00000000..e4844946 --- /dev/null +++ b/style.css @@ -0,0 +1,9 @@ +@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap'); + +h1, h2, h3, #navbar { + font-family: "Inconsolata", monospace; +} + +.dark #navbar { + background-color: #121213; +}