Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/protorev PR Breakdown #3594

Closed
8 tasks done
davidterpay opened this issue Dec 1, 2022 · 1 comment
Closed
8 tasks done

x/protorev PR Breakdown #3594

davidterpay opened this issue Dec 1, 2022 · 1 comment
Assignees

Comments

@davidterpay
Copy link
Contributor

davidterpay commented Dec 1, 2022

Background

The most up to date spec of x/protorev can be found here.

As development of the x/protorev module is approaching a V1, Skip is ready to start breaking up the implementation into PRs that are modular and straightforward to review. The proposed outline for PRs is as follows:

Suggested Approach

1. Proto-code + App Configuration + Scaffolding ✅

Update: This PR has been merged–––> #3587

This PR includes all of the proto and basic app configuration code. Much of the boilerplate code for the module is also in this PR. Although this includes all of the proto code that will be used end to end, some of it will be implemented in subsequent PRs.

High Level Overview of Changes

  • Init proto code for the module
  • Hook up x/protorev to the app
  • Genesis code + stateless validation
  • Keeper interfaces (dependencies with gamm, epoch, etc.)
  • Implementing the Msg types + stateless validation
  • Governance proposals handlers + stateless validation
  • TokenPairArbRoutes stateless validation (read more about this in the spec linked below)

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests for stateless validation for the genesis state
  • Added unit tests for stateless validation for Msgs
  • Added unit tests for stateless validation of governance proposals

2. Basic Keeper + Epoch Hook ✅

Update: This PR has been merged –––> #3646

This PR includes all of the store keys + setters/getters needed to build cyclic arbitrage routes as well as the epoch hook which helps construct the stores.

High Level Overview of Changes

  • Keys for keeper
  • Setters and getters for TokenPairArbRoutes
  • Setters and getters for Osmo Pools
  • Setters and getters for Atom Pools
  • Epoch hook runs after every week to update the highest liquidity pool pairings

Keeper Updates

State Object Description Key Values Store
TokenPairArbRoutes TokenPairRoutes tracks cyclic arb routes that can be used to create a MultiHopSwap given two denoms []byte{1} + []byte{inputDenom} +[]byte{outputDenom} []byte{TokenPairArbRoutes} KV
OsmoPools List of highest liquidity pools for every token pair with Osmo []byte{2} + []byte{tokenDenom} []byte{poolID} KV
AtomPools List of highest liquidity pools for every token pair with Atom []byte{3} + []byte{tokenDenom} []byte{poolID} KV

TokenPairArbRoutes
Each hot route configured through the admin account must be stored in the keeper. Each Route will be indexed by an input and output denomination. The value will correspond to a TokenPairArbRoutes object which contains a list of lists which correspond to routes that may be taken.

OsmoPools
This tracks the highest liquidity pools for every single asset that Osmo is paired with on-chain. This is updated through the epoch hook and on genesis.

AtomPools
This tracks the highest liquidity pools for every single asset that Atom is paired with on-chain. This is updated through the epoch hook and on genesis.

Epoch Hook
The Epoch hook allows the module to update the highest liquidity pools and distribute developer profits after every week while incrementally tracking profits through the day epoch identifier.

Acceptance Criteria

This PR will also include our testing environment that is configured through keeper_test.go. This test suite initializes user accounts for trading, creates over 30 pools, sets up token pair routes, and allows us to test all of the setters and getters for stateful information.

This change added tests and can be verified as follows:

  • Added unit tests for OsmoPools
  • Added unit tests for AtomPools
  • Added unit tests for TokenPairArbRoutes
  • Added unit tests for the Epoch Hook

3. Route Building ✅

Update: This PR has been merged –––> #3683

This PR implements the logic of building routes given a swap. There are two primary methods for route generation: Highest Liquidity Pools and Hot Routes.

High Level Overview of Changes

  • Build routes that start and end with osmo (Highest Liquidity Pools)
  • Build routes that start and end with atom (Highest Liquidity Pools)
  • Build routes that start and end with osmo or atom from the set of hot routes (Hot Routes)

Highest Liquidity Pools: Updated via the daily epoch, the module iterates through all the the pools and stores the highest liquidity pool for every asset that pairs with either osmo or atom in a pool (for example, the osmo/juno key will have a single pool id stored, that pool id having the most liquidity out of all the osmo/juno pools). This store is then used to create a route(s) at runtime after analyzing a swap. Store is updated through the epoch hook.

There are two types of trades that must be considered:

  1. The assets in the liquidity pool are neither Atom nor Osmo
    • In this case, x/protorev will sandwich the pool with either Osmo or Atom on the other end. For example, say that a swap occurs on the Akash ↔ Juno pool. There are four possible routes that can be taken (in order of trades made starting on the left and ending on the right).
      • (Osmo → Akash), (Akash → Juno), (Juno → Osmo)
      • (Osmo → Juno), (Juno → Akash), (Akash → Osmo)
      • (Atom → Akash), (Akash → Juno), Juno → Atom)
      • (Atom → Juno), (Juno → Akash), (Akash → Atom)
    • Capturing cyclic arbitrage opportunities happen in the opposite direction of the trade. Using the example above, we can cut down the routes to only two routes if we know that the user traded Akash → Juno. The routes in that case would be
      • (Osmo → Juno), (Juno → Akash), (Akash → Osmo)
      • (Atom → Juno), (Juno → Akash), (Akash → Atom)
  2. The assets in the liquidity pool include either Atom or Osmo or both
  • If the Osmo ↔ Atom pool is swapped against, then the highest liquidity pool route building method does not produce any routes. Otherwise, if only one of OSMO or ATOM was swapped against in a single pool, then x/protorev will look for the opposite asset pools from what was traded in the pool. For example, say a swap has been executed on the Osmo ↔ TokenXYZ pool, tendering OSMO and receiving TokenXYZ, the route generated would be:
    • (Atom → TokenXYZ), (TokenXYZ → Osmo), (Osmo → Atom)

In both cases, the route that is built will always sandwich the swap that was made. However, we allow for more flexibility in route generation as the highest liquidity method may not be optimal via Hot Routes.

Hot Routes: Populated through the admin account (which will be implemented in a later PR), the module’s keeper holds a KV store that associates token pairs (for example, osmo/juno) to the routes that result in a high percentage of arbitrage profit on Osmosis (as determined by external analysis, max 2 routes stored per token pair key).

  • The purpose of storing Hot Routes is a recognition that the Highest Liquidity Pool method may not present the best arbitrage routes. As such, hot routes can be configured through governance to store additional routes that may be more effective at capturing arbitrage opportunities. Each hot route will store a placeholder for where the current swapped pool will fit into the trade (since the key is token pairs and not pool ids for the hot routes, we can use the same set of routes for multiple pools).

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests for atom routes
  • Added unit tests for osmo routes
  • Added unit tests for routes built with tokenPairArbRoutes

4. Trade Execution + Statistics ✅

Update: This PR has been merged –––> #3709

This PR implements all of the logic surrounding finding the most profitable arbitrage opportunities given a set of routes, executing the most profitable arbitrage route, and storing profits in the module account. Additionally, the module tracks various statistics about how many trades have been executed, what pools had the most amount of arbitrage opportunities and more.

High Level Overview of Changes

  • Calculating whether a cyclic arbitrage route is profitable
  • Determining the most profitable cyclic arbitrage route from a set of routes given a swap
  • Executing the most profitable cyclic arbitrage opportunity (if one exists)
  • Updating trading statistics in the module

Keeper Updates

State Object Description Key Values Store
NumberOfTrades Tracks the number of trades protorev has executed []byte{4} []byte{numberOfTrades} KV
ProfitsByDenom Tracks the profits protorev has made []byte{5} + []byte{tokenDenom} []byte{sdk.Coin} KV
TradesByRoute Tracks the number of trades the module has executed on a given route []byte{6} + []byte{route} []byte{numberOfTrades} KV
ProfitsByRoute Tracks the profits the module has accumulated after trading on a given route []byte{7} + []byte{route} []byte{sdk.Coin} KV

NumberOfTrades
This will store the total number of arbitrage trades that x/protorev has executed since genesis. When finding a pool that has a cyclic arbitrage opportunity, the trade will be executed and this incremented.

ProfitsByDenom
x/protorev can execute cyclic arbitrage trades with either Osmosis or Atom. As such, this will store the profits that the module has earned from either denomination.

TradesByRoute & ProfitsByRoute
These metrics allows users and researchers to query the number of cyclic arbitrage trades that have been executed by x/protorev on an cyclic arbitrage route as well as all of the profits captured on that same route.

Trade Execution Logic

Now that we have a list of cyclic routes for each pool swapped by the user’s tx, we then determine if any of the routes are profitable. We determine this using a binary search algorithm that finds the amount of the asset to swap in that results in the most of that same asset out. We then calculate profits by taking the difference between the amount of asset out and amount of asset in. By iterating through the routes and storing the route, optimal input amount, and profit of the route with the highest profit > 0, we are left with the route and amount to execute the MultiHopSwap against.

The module mints the optimal input amount of the coin to swap in from the bankkeeper to the x/protorev module account, executes the MultiHopSwap by interacting with the x/gamm module, burns the optimal input amount of the coin minted to execute the MultiHopSwap, and sends subsequent profits to the module account.

  • Note: x/protorev will exclusively execute MultiHopSwaps that originate and end in either OSMO or ATOM.

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests for setters and getters for module statistics
  • Added unit tests for FindMaxProfitRoute
  • Added unit tests for CalculateRouteProfit
  • Added unit tests for ExecuteTrade
  • Added unit tests for ConvertProfits

5. Post Handler + Governance Proposals + Developer Profit Sharing ✅

Update: This PR has been merged –––> #3806

This PR implements the post handler, the proposal handlers that are responsible for setting the admin account, admin functionality, stateful changes depending on proposals, and developer profit sharing.

High Level Overview of Changes

  • Add and do stateful testing of proposals
  • Create admin functionality to set hot routes and the developer account
  • Profit splitting after every successful trade execution for the developer account
  • Implement the post handler

The postHandler extracts pools that were swapped in a transaction and determines if there is a cyclic arbitrage opportunity. If so, the handler will find an optimal route and execute it - rebalancing the pool and returning arbitrage profits to the module account. Additionally, the gas meter gets reset at the end of the post handler in order to refund users for computation done in the handler.

  1. Extract all pools that were traded on in the transaction (ExtractSwappedPools) as well as the direction of the trade.
  2. Create cyclic arbitrage routes for each of the swaps above (BuildRoutes)
  3. For each feasible route, determine if there is a cyclic arbitrage opportunity (IterateRoutes)
    1. Determine the optimal amount to swap in and its respective profits via binary search over range of potential input amounts (FindMaxProfitForRoute)
    2. Compare profits of each route, keep the best route and input amount with the highest profit
  4. If the best route and input amount has a profit > 0, execute the trade (ExecuteTrade) and rebalance the pools on-behalf of the chain through the gammkeeper (MultiHopSwapExactAmountIn)
  5. Keep the profits in the module’s account for subsequent distribution.

ExtractSwappedPools

Checks if there were any swaps made on pools in a transaction, returning the pool ids and input/output denoms for each pool that was traded on.

BuildRoutes

BuildRoutes takes a token pair (input and output denom) and returns a list of routes for that token pair that potentially contain a cyclic arbitrage opportunity, populated via the Hot Route and Highest Liquidity Pools method as described above.

IterateRoutes

IterateRoutes iterates through a list of routes, determining the route and input amount that results in the highest cyclic arbitrage profits, and if the highest profits > 0, executes the MultiHopSwapExactAmountIn to rebalance the pools and collect the profits.

FindMaxProfitForRoute

This will take in a route and determine the optimal amount to swap in to maximize profits, given the reserves of all of the pools that are swapped against in the route.

ExecuteTrade

Execute trade takes the route and optimal input amount as params, mints the optimal amount of input coin, executes the swaps via gammKeeper’s MultiHopSwapExactAmountIn, and then burns the amount of coins originally minted, storing the profits in it’s own module account.

This will also update various trading statistics in the module’s store. It will update the total number of trades the module has executed, total profits captured, profits made on this specific pool and share of profits the developer account can withdraw.

Keeper Updates

State Object Description Key Values Store
ProtoRevEnabled Tracks whether the protorev module is enabled []byte{7} []byte{bool} KV
AdminAccount Tracks the admin account for protorev []byte{8} []byte{sdk.AccAddress} KV
DeveloperAccount Tracks the developer account for protorev []byte{9} []byte{sdk.AccAddress} KV
DaysSinceGenesis Tracks the number of days since the module was initialized. Used to track profits that can be withdrawn by the developer account []byte{10} []byte{uint} KV
DeveloperFees Tracks the profits that the developer account can withdraw []byte{11} + []byte{tokenDenom} []byte{sdk.Coin} KV
MaxRoutesPerTx Tracks the maximum number of routes that can be traversed per tx []byte{12} []byte{uint64} KV
MaxRoutesPerBlock Tracks the maximum number of routes that can be traversed per block []byte{13} []byte{uint64} KV
RouteCountForBlock Tracks the number of routes that have been traversed in the current block []byte{14} []byte{uint64} KV
LatestBlockHeight Tracks the latest recorded block height []byte{15} []byte{uint64} KV
RouteWeights Tracks the weights of the different routes []byte{16} []byte{uint64} KV

ProtoRevEnabled
x/protorev can be enabled or disabled through governance. As a proposal is a stateful change, we store whether the module is currently enabled or disabled in the module.

AdminAccount
The admin account is set through governance and has permissions to set hot routes and the developer account. On genesis, the admin account is nil.

DeveloperAccount
The developer account is set through a NewMsgSetDeveloperAccount tx. This is the account that will be able to withdraw a portion of the profits from x/protorev as specified by the Osmosis ↔ Skip proposal. Only the admin account has permission to make this message.

DaysSinceGenesis
x/protorev will distribute 20% of profits to skip in the year 1, 10% of profits in year 2, and 5% thereafter. To track how much profit can be distributed to the developer account at any given moment, we store the amount of days since genesis.

DeveloperFees
DeveloperFees tracks the total amount of profit that can be withdrawn by the developer account. These fees are sent to the developer account, if set, every week through the epoch hook. If unset, the funds are held in the module account.

MaxRoutesPerTx
MaxRoutesPerTx tracks the maximum number of routes that can be traversed in a given transaction. This is configurable (but bounded) by the admin account. We limit the number of routes per transaction so that all x/protorev execution is not limited to the top of the block.

MaxRoutesPerBlock
MaxRoutesPerBlock tracks the maximum number of routes that can be traversed in a given block. This is configurable (but bounded) by the admin account. We limit the number of routes per block so that the execution time of the x/protorev posthandler is reasonably bounded to ensure that block time remains as is.

RouteCountForBlock
RouteCountForBlock tracks the number of routes that have been traversed in the current block. Used to ensure that the module is not slowing down block speed.

LatestBlockHeight
LatestBlockHeight tracks the latest recorded block height. Used to track the number of routes that have been traversed in the current block.

RouteWeights
RouteWeights contains the weights of all of the different route types. Routes are broken up into different types based on the pool that is sandwiched in between the arbitrage route. This distinction is made and necessary because the execution time ranges fairly between the different route types.

Proposal Handlers

x/protorev implements two different governance proposals.

SetProtoRevAdminAccountProposal
As the landscape of pools on Osmosis evolves, an admin account will be able to add and remove routes for x/protorev to check for cyclic arbitrage opportunities. Largely, the purpose of maintaining hot routes is to reduce the amount of computation that would otherwise be required to determine optimal paths at runtime. In addition, introducing a means of altering hot can allow external researchers to conduct meaningful analysis into the markets on-chain.

SetProtoRevEnabledProposal
This proposal type allows the chain to turn the module on or off. This is meant to be used as a fail safe in the case stakers and the chain decide to turn the module off. This might be used to halt the execution of trades in the case that the x/gamm module has significant upgrades that might produce unexpected behavior from the module.

Profit Sharing

Profits accumulated by the module will be partially distributed to the developers that built the module in accordance with the governance proposal that was passed: year 1 is 20% of profits, year 2 is 10%, and subsequent years is 5%.

In order to track how much profit the developers can withdraw at any given moment, the module tracks the number of days since genesis. This gets incremented in the epoch hook after every day. When a trade gets executed by the module, the module will determine how much of the profit from the trade the developers can receive by using daysSinceGenesis in a simple calculation.

If the developer account is not set (which it is not on genesis), all funds are held in the module account. Once the admin account is set through a successful governance proposal, the developer address can be set and will start to automatically receive a share of profits every week through the epoch hook. The distribution of funds from the module account is done through SendDeveloperFeesToDeveloperAccount. Once the funds are distributed, the amount of profit developers can withdraw gets reset to 0 and profits will start to be accumulated and distributed on a week to week basis.

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests for gas refunds
  • Added unit tests for mock swap tx
  • Added unit tests for extracting the correct pools from the tx
  • Added unit tests for proposals
  • Added unit tests for new setters and getters
  • Added unit tests for updating developer fees
  • Added unit tests for enabling or disabling the module (and affects in epoch and post handler)

6. Generalizable Route Building ✅

Update: This PR has been merged –––> #4000

This PR upgrades x/protorev to support arbitrary sets of base denominations that can be used to build cyclic arbitrage routes, introduces a new pool point system that bounds the execution time of x/protorev more accurately, and enables the execution of routes of arbitrary length.

High Level Overview of Changes

  • New pool point system where each pool point is essentially 1 ms of simulation & execution time
    • Configurable number of pool points per tx
    • Configurable number of pool points per block
    • Each pool in a route will have an associated weight which directly corresponds to the number of points it costs
  • Arbitrary sets of base denominations (configurable by the admin account)
    • Route building logic is abstracted away from osmo or atom
  • <10 LOC changed to support execution of routes of arbitrary length

Keeper Updates

State Object Description Key Values Store
DenomPairToPool Tracks the pool ids of the highest liquidity pools matched with a given denom []byte{2} []byte{baseDenom} + []byte{denomToMatch} KV
BaseDenoms Tracks all of the base denominations that will be used to construct arbitrage routes []byte{3} []byte{[]string{}} KV
MaxPoolPointsPerTx Tracks the maximum number of pool points that can be consumed per tx []byte{12} []byte{uint64} KV
MaxPoolPointsPerBlock Tracks the maximum number of pool points that can be consumed per block []byte{13} []byte{uint64} KV
PoolPointCountForBlock Tracks the number of pool points that have been consumed in this block []byte{14} []byte{uint64} KV
PoolWeights Tracks the weights (pool points) of the different pool types []byte{16} []byte{{}types.PoolWeights} KV

Where PoolWeights looks like the following

// PoolWeights contains the weights of all of the different pool types. This
// distinction is made and necessary because the execution time ranges
// significantly between the different pool types. Each weight roughly
// corresponds to the amount of time (in ms) it takes to execute a swap on that
// pool type.
type PoolWeights struct {
	// The weight of a stableswap pool
	StableWeight uint64 `protobuf:"varint,1,opt,name=stable_weight,json=stableWeight,proto3" json:"stable_weight,omitempty"`
	// The weight of a balancer pool
	BalancerWeight uint64 `protobuf:"varint,2,opt,name=balancer_weight,json=balancerWeight,proto3" json:"balancer_weight,omitempty"`
	// The weight of a concentrated pool
	ConcentratedWeight uint64 `protobuf:"varint,3,opt,name=concentrated_weight,json=concentratedWeight,proto3" json:"concentrated_weight,omitempty"`
}

DenomPairToPool
DenomPairToPool takes in a base denomination – denom that is used to build routes (ex. osmo, atom, usdc) – and a denom to match and returns the highest liquidity pool id between the pair of denominations. Each base denomination is going to have its own set of denomination it maps to. Each denomination that each base denom maps to will correspond to the highest liquidity pool for that pair. This will be updated in the same manner it was before.

BaseDenoms
BaseDenoms are the denominations that are used to build the highest liquidity routes. This will be configurable by the admin account, but will always maintain at least osmo as a base denom. These checks will be done in the last PR where the msg server is implemented. By default, osmo will be included.

MaxPoolPointsPerTx
This tracks the maximum number of pool points that can be consumed per transaction. A pool point roughly corresponds to 1 ms of simulation & execution time. i.e. this roughly tracks the maximum execution time of protorev per transaction.

MaxPoolPointsPerBlock
This tracks the maximum number of pool points that can be consumed per transaction. This will roughly track the maximum execution time of protorev per block.

PoolPointCountForBlock
This tracks the current pool point count for the current block. This cannot ever exceed MaxPoolPointsPerBlock.

PoolWeights
This tracks the pool points or weight of each pool type that can be traversed. This distinction is necessary because different pool types have different simulation and execution times.

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests that allow us to generate routes using different base denoms
  • Added unit tests to ensure the pool point system is correctly tracking the cost of different routes
  • Added unit tests for routes of varying lengths
  • Added unit tests for configuring the pool point system, base denoms, and hot routes

7. Query server + Message server + CLI

Update: This PR is currently being reviewed –––> #4214

This PR implements the query server, message server, and CLI.

High Level Overview of Changes

  • Implement the query server
  • Implement the message server
  • Implement CLI commands

Keeper Updates

State Object Description Key Values Store
BaseDenoms Tracks all of the base denominations that will be used to construct arbitrage routes []byte{3} []byte{[]BaseDenom{}} KV

Where BaseDenom looks like the following

// BaseDenom is a struct that contains the base denomination and the
// BaseDenom represents a single base denom that the module uses for its
// arbitrage trades. It contains the denom name alongside the step size of the
// binary search that is used to find the optimal swap amount
type BaseDenom struct {
	// The denom i.e. name of the base denom (ex. uosmo)
	Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"`
	// The step size of the binary search that is used to find the optimal swap
	// amount
	StepSize github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=step_size,json=stepSize,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"step_size"`
}

Queries

Verb Method Description
gRPC osmosis.v14.protorev.Query/Params Queries the parameters of the module
gRPC osmosis.v14.protorev.Query/GetProtoRevNumberOfTrades Queries the number of arbitrage trades the module has executed
gRPC osmosis.v14.protorev.Query/GetProtoRevProfitsByDenom Queries the profits of the module by denom
gRPC osmosis.v14.protorev.Query/GetProtoRevAllProfits Queries all of the profits from the module
gRPC osmosis.v14.protorev.Query/GetProtoRevStatisticsByRoute Queries the number of arbitrages and profits that have happened for a given route
gRPC osmosis.v14.protorev.Query/GetProtoRevAllRouteStatistics Queries all of pools that the module has arbitrage against and the number of trades and profits that have happened for each route
gRPC osmosis.v14.protorev.Query/GetProtoRevTokenPairArbRoutes Queries all of the hot routes that the module is currently arbitraging
gRPC osmosis.v14.protorev.Query/GetProtoRevAdminAccount Queries the admin account of the module
gRPC osmosis.v14.protorev.Query/GetProtoRevDeveloperAccount Queries the developer account of the module
gRPC osmosis.v14.protorev.Query/GetProtoRevPoolWeights Queries the pool weights of the module
gRPC osmosis.v14.protorev.Query/GetProtoRevMaxPoolPointsPerTx Queries the maximum pool points that can be consumed per transaction
gRPC osmosis.v14.protorev.Query/GetProtoRevMaxPoolPointsPerBlock Queries the maximum pool points that can be consumed per block
gRPC osmosis.v14.protorev.Query/GetProtoRevBaseDenoms Queries the base denoms of the module
gRPC osmosis.v14.protorev.Query/GetProtoRevEnabled Queries whether the module is enabled or not
GET /osmosis/v14/protorev/params Queries the parameters of the module
GET /osmosis/v14/protorev/number_of_trades Queries the number of arbitrage trades the module has executed
GET /osmosis/v14/protorev/profits_by_denom Queries the profits of the module by denom
GET /osmosis/v14/protorev/all_profits Queries all of the profits from the module
GET /osmosis/v14/protorev/statistics_by_route Queries the number of arbitrages and profits that have happened for a given route
GET /osmosis/v14/protorev/all_route_statistics Queries all of pools that the module has arbitrage against and the number of trades and profits that have happened for each route
GET /osmosis/v14/protorev/token_pair_arb_routes Queries all of the hot routes that the module is currently arbitraging
GET /osmosis/v14/protorev/admin_account Queries the admin account of the module
GET /osmosis/v14/protorev/developer_account Queries the developer account of the module
GET /osmosis/v14/protorev/pool_weights Queries the pool weights of the module
GET /osmosis/v14/protorev/max_pool_points_per_tx Queries the maximum pool points that can be consumed per transaction
GET /osmosis/v14/protorev/max_pool_points_per_block Queries the maximum pool points that can be consumed per block
GET /osmosis/v14/protorev/base_denoms Queries the base denoms of the module
GET /osmosis/v14/protorev/enabled Queries whether the module is enabled or not

Transactions

Verb Method Description
gRPC osmosis.v14.protorev.Msg/SetHotRoutes Sets the hot routes that will be explored when creating cyclic arbitrage routes. Can only be called by the admin account.
gRPC osmosis.v14.protorev.Msg/SetDeveloperAccount Sets the account that can withdraw a portion of the profit from the protorev module. This will be Skip's address once deployed. Can only be called by the admin account.
gRPC osmosis.v14.protorev.Msg/SetPoolWeights Sets the pool weights of the module. Can only be called by the admin account.
gRPC osmosis.v14.protorev.Msg/SetMaxPoolPointsPerTx Sets the maximum pool points that can be consumed per transaction. Can only be called by the admin account.
gRPC osmosis.v14.protorev.Msg/SetMaxPoolPointsPerBlock Sets the maximum pool points that can be consumed per block. Can only be called by the admin account.
gRPC osmosis.v14.protorev.Msg/SetBaseDenoms Sets the base denoms of the module. Can only be called by the admin account.
POST /osmosis/protorev/v1beta1/set_hot_routes Sets the hot routes that will be explored when creating cyclic arbitrage routes. Can only be called by the admin account.
POST /osmosis/protorev/v1beta1/set_developer_account Sets the account that can withdraw a portion of the profit from the protorev module. This will be Skip's address once deployed
POST /osmosis/protorev/v1beta1/set_pool_weights Sets the pool weights of the module. Can only be called by the admin account.
POST /osmosis/protorev/v1beta1/set_max_pool_points_per_tx Sets the maximum pool points that can be consumed per transaction. Can only be called by the admin account.
POST /osmosis/protorev/v1beta1/set_max_pool_points_per_block Sets the maximum pool points that can be consumed per block. Can only be called by the admin account.
POST /osmosis/protorev/v1beta1/set_base_denoms Sets the base denoms of the module. Can only be called by the admin account.

Messages

NewMsgSetDeveloperAccount
The admin account broadcasts a NewMsgSetDeveloperAccount to set the developer account.

Messsage stateless validation fails if:

  • The admin is not a valid bech32 address
  • The signature of the user does not match the admin account’s
  • The developer account is not a valid bech32 address

Message stateful validation fails if:

  • The admin is not set in state
  • The admin entered in the message does not match the admin on chain
  • The admin’s signatures are not the same

NewMsgSetHotRoutes
The admin account broadcasts a NewMsgSetHotRoutes to set the hot routes.

Message statless validation fails if:

  • The admin is not a valid bech32 address
  • The signature of the user does not match the admin account’s
  • The hot routes are not valid

Message stateful validation fails if:

  • The admin is not set in state
  • The admin entered in the message does not match the admin on chain
  • The admin’s signatures are not the same

NewMsgSetPoolWeights
The admin account broadcasts a NewMsgSetPoolWeights to set the pool weights. The pool weights roughly correspond to the execution time of a swap on that pool type (stable, balancer, concentrated).

Message stateless validation fails if:

  • The admin is not a valid bech32 address
  • Any of the pool weights are less than 1

Message stateful validation fails if:

  • The admin is not set in state
  • The admin’s signatures are not the same

NewMsgSetMaxPoolPointsPerTx
The admin account broadcasts a NewMsgSetMaxPoolPointsPerTx to set the maximum pool points that can be consumed per transaction.

Message stateless validation fails if:

  • The admin is not a valid bech32 address
  • MaxPoolPointsPerTx is outside of the valid range

Message stateful validation fails if:

  • The admin is not set in state
  • The admin’s signatures are not the same

NewMsgSetMaxPoolPointsPerBlock
The admin account broadcasts a NewMsgSetMaxPoolPointsPerBlock to set the maximum pool points that can be consumed per block.

Message stateless validation fails if:

  • The admin is not a valid bech32 address
  • MaxPoolPointsPerBlock is outside of the valid range

Message stateful validation fails if:

  • The admin is not set in state
  • The admin’s signatures are not the same

NewMsgSetBaseDenoms
The admin account broadcasts a NewMsgSetBaseDenoms to set the base denoms of the module. The base denoms are the denominations that the module will use to construct cyclic arbitrage routes. The order of the lists

Message stateless validation fails if:

  • The admin is not a valid bech32 address
  • Osmosis is not the first base denom in the list

Acceptance Criteria

This change added tests and can be verified as follows:

  • Added unit tests for Msg Server
  • Added unit tests for Query Server

8. Optimizations + E2E testing ✅

Update: This PR has been merged –––> #4181

This PR refactors some of the route calculation and trade execution logic to be more optimal.

High Level Overview of Changes

  • Routes and their pool points are only counted if a trade is guaranteed to be profitable
  • The step size of the binary search method is going to be related to the amount of decimal points of the underlying base denom
  • Check the smallest arb possible to see if any input amount would lead to a profitable arbitrage trade
  • Expand the range of the binary search in case there is a big arbitrage trade available
  • Added testing for routes of length > 3

Given a route, we now calculate if the minAmountIn is profitable as a pre-check. If minAmountIn is not profitable, then the route is not profitable and we don't run the full binary search algo. This will make Protorev take much less compute time in production compared to its previous design (which would run the full binary search even when a route has no-arb).

Given a route, we now calculate if the maxAmountIn and maxAmountIn + 1 profit is increasing as a pre-check and increase the range if so. If the profit from maxInputIn is still increasing, this means that we need to increase the range to capture the max profit.

Adds tests that verifies the limit/point system works properly, >3 pool routes work, and other denom besides osmo/atom work.

Acceptance Criteria

This change added tests and can be verified as follows:

  • All previous tests pass with new changes
  • Added a test to ensure range extension works properly
  • Added a test for swapExactAmountOut
  • Added benchmark testing
  • Added doomsday testing to ensure point system works properly and doesn't allow protorev to keep running
  • Adds tests for generalized denoms
  • Adds a test to ensure panic catching is handled properly. This currently FAILS. This is on purpose, this PR is not to be merged until Panic Handling In MultihopEstimateOutGivenExactAmountIn borks Protorev #4180 is resolved
@davidterpay
Copy link
Contributor Author

All PRs for ProtoRev have been merged! Will keep this open in case we make any other last second additions.

@github-project-automation github-project-automation bot moved this from Needs Triage 🔍 to Done ✅ in Osmosis Chain Development Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

No branches or pull requests

3 participants