Skip to content

Commit

Permalink
Implement automatic on-chain arbitrage (#833)
Browse files Browse the repository at this point in the history
* WIP

* Do dependency injection of `max_iterations`

* Complete benchmarks

* Remove `println!`

* Clean up benchmarks

* Finish benchmarks

* Reduce size of minimum weight

* `cargo fmt`

* Fix clippy issues

* Change `Hasher` of arbitrage cache

* More clippy fixes

* Remove outdated TODO

* Change hasher of cache

* Remove `MIN_ARBITRAGE_WEIGHT`

* Correctly mimic buy/sell complete set ops

* Fix formatting

* Add `market_account` to `market-commons`

* Fix benchmark

* Fix benchmarks some more

* Add missing changelog

* Remove `market_account` from `prediction-markets`

* Update changelog

* Add a better solution for enforcing pool limit

* Revert hasher to `Twox64Concat`

* Update zrml/swaps/src/benchmarks.rs

Co-authored-by: Chralt <chralt.developer@gmail.com>

* Fix benchmarks

* Add comment explaining a test

* Use variable for the base asset

* Replace `is_err` with explicit `assert_eq`

* Replace `is_err` with explicit `assert_eq`

* Remove TODO

* Remove `iteration_count` parameter from benchmarks

* `cargo fmt`

* Fix `WeightInfo` invocation

* Fix clippy issues

* Update zrml/swaps/src/arbitrage.rs

Co-authored-by: Chralt <chralt.developer@gmail.com>

* correct review suggestion comment

* Default to min on low volume domains

* Fix doc string formatting

* Fix clippy errors

* Apply suggestions from code review

Co-authored-by: Harald Heckmann <mail@haraldheckmann.de>

* Add module docstrings

* Add missing docstrings

* Update `zrml_market_commons` mock

* Fix formatting

* Ensure that min balance is observed by arbitrage

Co-authored-by: Chralt <chralt.developer@gmail.com>
Co-authored-by: Chralt98 <chralt98@gmail.com>
Co-authored-by: Harald Heckmann <mail@haraldheckmann.de>
  • Loading branch information
4 people committed Nov 18, 2022
1 parent 4d4f2c1 commit 6fcdae8
Show file tree
Hide file tree
Showing 21 changed files with 1,780 additions and 160 deletions.
9 changes: 9 additions & 0 deletions docs/changelog_for_devs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# v0.3.7

- Added on-chain arbitrage. See
[ZIP-1](https://hackmd.io/@1ypDLjlbQ_e2Gp_1EW7kkg/BksyTQc-o) for details. When
a pool is arbitraged, we emit one of the following events:
`ArbitrageMintSell(pool_id, amount)`, `ArbitrageBuyBurn(pool_id, amount)` or
`ArbitrageSkipped(pool_id)`. The latter can be safely ignored by the indexer.
The `amount` parameter signifies the amount of funds moved into or out of the
prize pool (mint-sell/buy-burn resp.) and the amount of full sets
minted/burned. Note that in addition to these events, the low-level
`tokens.Deposited` and `tokens.Transfer` events are also emitted.
- Added new pallet: GlobalDisputes. Dispatchable calls are:
- `add_vote_outcome` - Add voting outcome to a global dispute in exchange for
a constant fee. Errors if the voting outcome already exists or if the global
Expand Down
2 changes: 1 addition & 1 deletion runtime/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ macro_rules! impl_config_traits {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down Expand Up @@ -1026,7 +1027,6 @@ macro_rules! impl_config_traits {
type LiquidityMining = NoopLiquidityMining;
// type LiquidityMining = LiquidityMining;
type MarketCommons = MarketCommons;
type MarketId = MarketId;
type MinAssets = MinAssets;
type MaxAssets = MaxAssets;
type MaxInRatio = MaxInRatio;
Expand Down
5 changes: 4 additions & 1 deletion zrml/authorized/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
};
use zeitgeist_primitives::{
constants::mock::{AuthorizedPalletId, BlockHashCount, MaxReserves, MinimumPeriod, BASE},
constants::mock::{
AuthorizedPalletId, BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId, BASE,
},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
UncheckedExtrinsicTest,
Expand Down Expand Up @@ -106,6 +108,7 @@ impl pallet_balances::Config for Runtime {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
5 changes: 3 additions & 2 deletions zrml/court/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ use sp_runtime::{
};
use zeitgeist_primitives::{
constants::mock::{
BlockHashCount, CourtCaseDuration, CourtPalletId, MaxReserves, MinimumPeriod, StakeWeight,
BASE,
BlockHashCount, CourtCaseDuration, CourtPalletId, MaxReserves, MinimumPeriod, PmPalletId,
StakeWeight, BASE,
},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
Expand Down Expand Up @@ -114,6 +114,7 @@ impl pallet_randomness_collective_flip::Config for Runtime {}
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
3 changes: 2 additions & 1 deletion zrml/global-disputes/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use sp_runtime::{
use zeitgeist_primitives::{
constants::mock::{
BlockHashCount, GlobalDisputeLockId, GlobalDisputesPalletId, MaxReserves,
MinOutcomeVoteAmount, MinimumPeriod, RemoveKeysLimit, VotingOutcomeFee, BASE,
MinOutcomeVoteAmount, MinimumPeriod, PmPalletId, RemoveKeysLimit, VotingOutcomeFee, BASE,
},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
Expand Down Expand Up @@ -124,6 +124,7 @@ impl pallet_timestamp::Config for Runtime {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
3 changes: 2 additions & 1 deletion zrml/liquidity-mining/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use sp_runtime::{
use zeitgeist_primitives::{
constants::mock::{
BlockHashCount, ExistentialDeposit, LiquidityMiningPalletId, MaxLocks, MaxReserves,
MinimumPeriod, BASE,
MinimumPeriod, PmPalletId, BASE,
},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
Expand Down Expand Up @@ -105,6 +105,7 @@ impl pallet_balances::Config for Runtime {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
23 changes: 19 additions & 4 deletions zrml/market-commons/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ mod pallet {
ensure,
pallet_prelude::{StorageMap, StorageValue, ValueQuery},
storage::PrefixIterator,
traits::{Hooks, NamedReservableCurrency, StorageVersion, Time},
Blake2_128Concat, Parameter,
traits::{Get, Hooks, NamedReservableCurrency, StorageVersion, Time},
Blake2_128Concat, PalletId, Parameter,
};
use parity_scale_codec::MaxEncodedLen;
use sp_runtime::{
traits::{AtLeast32Bit, CheckedAdd, MaybeSerializeDeserialize, Member, Saturating},
ArithmeticError, DispatchError,
traits::{
AccountIdConversion, AtLeast32Bit, CheckedAdd, MaybeSerializeDeserialize, Member,
Saturating,
},
ArithmeticError, DispatchError, SaturatedConversion,
};
use zeitgeist_primitives::types::{Market, PoolId};

Expand Down Expand Up @@ -71,6 +74,11 @@ mod pallet {
+ Member
+ Parameter;

// TODO(#837): Remove when on-chain arbitrage is removed!
/// The prefix used to calculate the prize pool accounts.
#[pallet::constant]
type PredictionMarketsPalletId: Get<PalletId>;

/// Time tracker
type Timestamp: Time<Moment = u64>;
}
Expand Down Expand Up @@ -182,6 +190,13 @@ mod pallet {
Ok(())
}

// TODO(#837): Remove when on-chain arbitrage is removed!
#[inline]
fn market_account(market_id: Self::MarketId) -> Self::AccountId {
T::PredictionMarketsPalletId::get()
.into_sub_account_truncating(market_id.saturated_into::<u128>())
}

// MarketPool

fn insert_market_pool(market_id: Self::MarketId, pool_id: PoolId) -> DispatchResult {
Expand Down
3 changes: 3 additions & 0 deletions zrml/market-commons/src/market_commons_pallet_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ pub trait MarketCommonsPalletApi {
/// Removes a market from the storage.
fn remove_market(market_id: &Self::MarketId) -> DispatchResult;

/// Return the account id of a market's prize pool.
fn market_account(market_id: Self::MarketId) -> Self::AccountId;

// MarketPool

/// Connects a pool identified by `pool_id` to a market identified by `market_id`.
Expand Down
3 changes: 2 additions & 1 deletion zrml/market-commons/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
};
use zeitgeist_primitives::{
constants::mock::{BlockHashCount, MaxReserves, MinimumPeriod},
constants::mock::{BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
UncheckedExtrinsicTest,
Expand All @@ -48,6 +48,7 @@ construct_runtime!(
impl crate::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
19 changes: 7 additions & 12 deletions zrml/prediction-markets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ mod pallet {
use orml_traits::{MultiCurrency, NamedMultiReservableCurrency};
use sp_arithmetic::per_things::{Perbill, Percent};
use sp_runtime::{
traits::{AccountIdConversion, CheckedDiv, Saturating, Zero},
traits::{CheckedDiv, Saturating, Zero},
DispatchError, DispatchResult, SaturatedConversion,
};
use zeitgeist_primitives::{
Expand Down Expand Up @@ -125,7 +125,7 @@ mod pallet {
let market = T::MarketCommons::market(&market_id)?;
ensure!(market.scoring_rule == ScoringRule::CPMM, Error::<T>::InvalidScoringRule);
let market_status = market.status;
let market_account = Self::market_account(market_id);
let market_account = T::MarketCommons::market_account(market_id);

// Slash outstanding bonds; see
// https://github.com/zeitgeistpm/runtime-audit-1/issues/34#issuecomment-1120187097 for
Expand Down Expand Up @@ -637,7 +637,7 @@ mod pallet {
}

let market_id = T::MarketCommons::push_market(market.clone())?;
let market_account = Self::market_account(market_id);
let market_account = T::MarketCommons::market_account(market_id);
let mut extra_weight = 0;

if market.status == MarketStatus::CollectingSubsidy {
Expand Down Expand Up @@ -912,7 +912,7 @@ mod pallet {
let sender = ensure_signed(origin)?;

let market = T::MarketCommons::market(&market_id)?;
let market_account = Self::market_account(market_id);
let market_account = T::MarketCommons::market_account(market_id);

ensure!(market.status == MarketStatus::Resolved, Error::<T>::MarketIsNotResolved);

Expand Down Expand Up @@ -1189,7 +1189,7 @@ mod pallet {
ensure!(market.scoring_rule == ScoringRule::CPMM, Error::<T>::InvalidScoringRule);
Self::ensure_market_is_active(&market)?;

let market_account = Self::market_account(market_id);
let market_account = T::MarketCommons::market_account(market_id);
ensure!(
T::AssetManager::free_balance(Asset::Ztg, &market_account) >= amount,
"Market account does not have sufficient reserves.",
Expand Down Expand Up @@ -1870,11 +1870,6 @@ mod pallet {
}
}

#[inline]
pub(crate) fn market_account(market_id: MarketIdOf<T>) -> T::AccountId {
T::PalletId::get().into_sub_account_truncating(market_id.saturated_into::<u128>())
}

fn insert_auto_close(market_id: &MarketIdOf<T>) -> Result<u32, DispatchError> {
let market = T::MarketCommons::market(market_id)?;

Expand Down Expand Up @@ -2008,7 +2003,7 @@ mod pallet {
ensure!(market.scoring_rule == ScoringRule::CPMM, Error::<T>::InvalidScoringRule);
Self::ensure_market_is_active(&market)?;

let market_account = Self::market_account(market_id);
let market_account = T::MarketCommons::market_account(market_id);
T::AssetManager::transfer(Asset::Ztg, &who, &market_account, amount)?;

let assets = Self::outcome_assets(market_id, &market);
Expand Down Expand Up @@ -2729,7 +2724,7 @@ mod pallet {
} else {
return Ok(T::DbWeight::get().reads(1));
};
let market_account = Self::market_account(*market_id);
let market_account = T::MarketCommons::market_account(*market_id);
let weight = T::Swaps::clean_up_pool(
&market.market_type,
pool_id,
Expand Down
2 changes: 1 addition & 1 deletion zrml/prediction-markets/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ impl zrml_liquidity_mining::Config for Runtime {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down Expand Up @@ -331,7 +332,6 @@ impl zrml_swaps::Config for Runtime {
type FixedTypeS = <Runtime as zrml_rikiddo::Config>::FixedTypeS;
type LiquidityMining = LiquidityMining;
type MarketCommons = MarketCommons;
type MarketId = MarketId;
type MaxAssets = MaxAssets;
type MaxInRatio = MaxInRatio;
type MaxOutRatio = MaxOutRatio;
Expand Down
8 changes: 4 additions & 4 deletions zrml/prediction-markets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ fn admin_destroy_market_correctly_cleans_up_accounts() {
let market_id = 0;
let pool_id = 0;
let pool_account = Swaps::pool_account_id(&pool_id);
let market_account = PredictionMarkets::market_account(market_id);
let market_account = MarketCommons::market_account(market_id);
let alice_ztg_before = AssetManager::free_balance(Asset::Ztg, &ALICE);
assert_ok!(PredictionMarkets::admin_destroy_market(Origin::signed(SUDO), 0));
assert_eq!(AssetManager::free_balance(Asset::CategoricalOutcome(0, 0), &pool_account), 0);
Expand Down Expand Up @@ -824,7 +824,7 @@ fn create_categorical_market_deposits_the_correct_event() {
simple_create_categorical_market(MarketCreation::Permissionless, 1..2, ScoringRule::CPMM);
let market_id = 0;
let market = MarketCommons::market(&market_id).unwrap();
let market_account = PredictionMarkets::market_account(market_id);
let market_account = MarketCommons::market_account(market_id);
System::assert_last_event(Event::MarketCreated(0, market_account, market).into());
});
}
Expand All @@ -836,7 +836,7 @@ fn create_scalar_market_deposits_the_correct_event() {
simple_create_scalar_market(MarketCreation::Permissionless, 1..2, ScoringRule::CPMM);
let market_id = 0;
let market = MarketCommons::market(&market_id).unwrap();
let market_account = PredictionMarkets::market_account(market_id);
let market_account = MarketCommons::market_account(market_id);
System::assert_last_event(Event::MarketCreated(0, market_account, market).into());
});
}
Expand Down Expand Up @@ -1530,7 +1530,7 @@ fn it_allows_to_buy_a_complete_set() {
let bal = Balances::free_balance(&BOB);
assert_eq!(bal, 1_000 * BASE - CENT);

let market_account = PredictionMarkets::market_account(0);
let market_account = MarketCommons::market_account(0);
let market_bal = Balances::free_balance(market_account);
assert_eq!(market_bal, CENT);
System::assert_last_event(Event::BoughtCompleteSet(0, CENT, BOB).into());
Expand Down
5 changes: 4 additions & 1 deletion zrml/simple-disputes/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
};
use zeitgeist_primitives::{
constants::mock::{BlockHashCount, MaxReserves, MinimumPeriod, SimpleDisputesPalletId},
constants::mock::{
BlockHashCount, MaxReserves, MinimumPeriod, PmPalletId, SimpleDisputesPalletId,
},
types::{
AccountIdTest, Balance, BlockNumber, BlockTest, Hash, Index, MarketId, Moment,
UncheckedExtrinsicTest,
Expand Down Expand Up @@ -94,6 +96,7 @@ impl pallet_balances::Config for Runtime {
impl zrml_market_commons::Config for Runtime {
type Currency = Balances;
type MarketId = MarketId;
type PredictionMarketsPalletId = PmPalletId;
type Timestamp = Timestamp;
}

Expand Down
Loading

0 comments on commit 6fcdae8

Please sign in to comment.