pyaver.market
1from solana.rpc.async_api import AsyncClient 2from .constants import MAX_ITERATIONS_FOR_CONSUME_EVENTS 3from .event_queue import load_all_event_queues, prepare_user_accounts_list 4from .aver_client import AverClient 5from solana.publickey import PublicKey 6from solana.keypair import Keypair 7from .enums import Fill, MarketStatus 8from .constants import AVER_MARKET_AUTHORITY, AVER_PROGRAM_ID 9from .utils import load_multiple_bytes_data, sign_and_send_transaction_instructions, parse_market_store, parse_market_state 10from .data_classes import MarketState, MarketStoreState, OrderbookAccountsState 11from .orderbook import Orderbook 12from solana.transaction import AccountMeta 13from .slab import Slab 14from anchorpy import Context 15from spl.token.instructions import get_associated_token_address 16from spl.token.constants import TOKEN_PROGRAM_ID 17from solana.rpc.types import TxOpts 18 19class AverMarket(): 20 """ 21 AverMarket object 22 23 Contains information, references and and orderbooks on a particular market 24 """ 25 26 market_pubkey: PublicKey 27 """Market pubkey""" 28 market_state: MarketState 29 """MarketState object holding data about the market""" 30 market_store_state: MarketStoreState 31 """MarketStoreStateobject holding data about the market required during active trading. 32 33 This does not exist if the market has stopped trading, voided or resolved""" 34 orderbooks: list[Orderbook] 35 """ 36 Ordered list of Orderbooks for this market. 37 38 Note: Binary (two-outcome0) markets only have 1 orderbook. 39 """ 40 aver_client: AverClient 41 """AverClient object""" 42 43 def __init__( 44 self, 45 aver_client: AverClient, 46 market_pubkey: PublicKey, 47 market_state: MarketState, 48 market_store_state: MarketStoreState = None, 49 orderbooks: list[Orderbook] = None, 50 ): 51 """ 52 Initialise an AverMarket object. Do not use this function; use AverMarket.load() instead. 53 54 Args: 55 aver_client (AverClient): AverClient object 56 market_pubkey (PublicKey): Market public key 57 market_state (MarketState): MarketState object 58 market_store_state (MarketStoreState, optional): MarketStoreState object. Defaults to None. 59 orderbooks (list[Orderbook], optional): List of Orderbook objects. Defaults to None. 60 """ 61 self.market_pubkey = market_pubkey 62 self.market_state = market_state 63 self.market_store_state = market_store_state 64 self.orderbooks = orderbooks 65 self.aver_client = aver_client 66 67 if(market_state.number_of_outcomes == 2 and orderbooks is not None and len(orderbooks) == 1): 68 orderbooks.append(orderbooks[0].invert()) 69 70 @staticmethod 71 async def load(aver_client: AverClient, market_pubkey: PublicKey): 72 """ 73 Initialises an AverMarket object 74 75 To refresh data on an already loaded market use src.refresh.refresh_markets() 76 77 Args: 78 aver_client (AverClient): AverClient object 79 market_pubkey (PublicKey): Market public key 80 81 Returns: 82 AverMarket: AverMarket object 83 """ 84 market_state_and_store = await AverMarket.load_market_state_and_store(aver_client, market_pubkey) 85 market_state: MarketState = market_state_and_store['market_states'][0] 86 market_store_state = None 87 orderbooks = None 88 is_market_status_closed = AverMarket.is_market_status_closed(market_state.market_status) 89 market_store_state: MarketStoreState = market_state_and_store['market_stores'][0] 90 91 if(not is_market_status_closed): 92 orderbooks = await AverMarket.get_orderbooks_from_orderbook_accounts( 93 aver_client.provider.connection, 94 market_store_state.orderbook_accounts, 95 [market_state.decimals] * len(market_store_state.orderbook_accounts) 96 ) 97 98 return AverMarket(aver_client, market_pubkey, market_state, market_store_state, orderbooks) 99 100 @staticmethod 101 async def load_multiple(aver_client: AverClient, market_pubkeys: list[PublicKey]): 102 """ 103 Initialises multiple AverMarket objects 104 105 This method is quicker that using Market.load() multiple times 106 107 To refresh data on already loaded markets use src.refresh.refresh_multiple_markets() 108 109 Args: 110 aver_client (AverClient): AverClient object 111 market_pubkeys (list[PublicKey]): List of Market public keys 112 113 Returns: 114 list[AverMarket]: List of AverMarket objects 115 """ 116 market_states_and_stores = await AverMarket.load_multiple_market_states_and_stores(aver_client, market_pubkeys) 117 market_states: list[MarketState] = market_states_and_stores['market_states'] 118 119 are_market_statuses_closed = [] 120 for market_state in market_states: 121 are_market_statuses_closed.append(AverMarket.is_market_status_closed(market_state.market_status)) 122 123 market_stores: list[MarketStoreState] = market_states_and_stores['market_stores'] 124 125 orderbooks_market_list = await AverMarket.get_orderbooks_from_orderbook_accounts_multiple_markets( 126 aver_client.provider.connection, 127 market_states, 128 market_stores, 129 are_market_statuses_closed, 130 ) 131 132 markets: list[AverMarket] = [] 133 for index, market_pubkey in enumerate(market_pubkeys): 134 market = AverMarket( 135 aver_client, 136 market_pubkey, 137 market_states[index], 138 market_stores[index], 139 orderbooks_market_list[index] 140 ) 141 markets.append(market) 142 143 return markets 144 145 @staticmethod 146 async def load_market_state(aver_client: AverClient, market_pubkey: PublicKey) -> MarketState: 147 """ 148 Loads onchain data for a MarketState 149 150 Args: 151 aver_client (AverClient): AverClient object 152 market_pubkey (PublicKey): Market public key 153 154 Returns: 155 MarketState: MarketState object 156 """ 157 res = await aver_client.program.account['Market'].fetch(market_pubkey) 158 return res 159 160 @staticmethod 161 async def load_multiple_market_states(aver_client: AverClient, market_pubkeys: list[PublicKey]) -> list[MarketState]: 162 """ 163 Loads onchain data for multiple MarketStates 164 165 Args: 166 aver_client (AverClient): AverClient object 167 market_pubkeys (list[PublicKey]): List of market public keys 168 169 Returns: 170 list[MarketState]: List of MarketState objects 171 """ 172 res = await aver_client.program.account['Market'].fetch_multiple(market_pubkeys) 173 return res 174 175 @staticmethod 176 async def load_market_state_and_store(aver_client: AverClient, market_pubkey: PublicKey): 177 """ 178 Loads onchain data for multiple MarketStates and MarketStoreStates at once 179 180 Args: 181 aver_client (AverClient): AverClient object 182 market_pubkey (PublicKey]: Market public key 183 184 Returns: 185 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 186 """ 187 return await AverMarket.load_multiple_market_states_and_stores(aver_client, [market_pubkey]) 188 189 @staticmethod 190 async def load_multiple_market_states_and_stores(aver_client: AverClient, market_pubkeys: list[PublicKey]): 191 """ 192 Loads onchain data for multiple MarketStates and MarketStoreStates at once 193 194 Args: 195 aver_client (AverClient): AverClient object 196 market_pubkeys (list[PublicKey]): List of market public keys 197 198 Returns: 199 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 200 """ 201 market_store_pubkeys = [AverMarket.derive_market_store_pubkey_and_bump(m, AVER_PROGRAM_ID)[0] for m in market_pubkeys] 202 203 data = await load_multiple_bytes_data(aver_client.connection, market_pubkeys + market_store_pubkeys) 204 market_states_data = data[0:len(market_pubkeys)] 205 market_stores_data = data[len(market_pubkeys):] 206 207 market_states = [parse_market_state(d, aver_client) for d in market_states_data] 208 market_stores = [parse_market_store(d, aver_client) if d is not None else None for d in market_stores_data] 209 210 return {'market_states': market_states, 'market_stores': market_stores} 211 212 @staticmethod 213 def derive_market_store_pubkey_and_bump(market_pubkey: PublicKey, program_id: PublicKey = AVER_PROGRAM_ID): 214 """ 215 Derives PDA (Program Derived Account) for MarketStore public key. 216 MarketStore account addresses are derived deterministically using the market's pubkey. 217 218 Args: 219 market_pubkey (PublicKey): Market public key 220 program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID. 221 222 Returns: 223 PublicKey: MarketStore public key 224 """ 225 return PublicKey.find_program_address( 226 [bytes('market-store', 'utf-8'), bytes(market_pubkey)], 227 program_id 228 ) 229 230 @staticmethod 231 def get_markets_from_account_states( 232 aver_client: AverClient, 233 market_pubkeys: list[PublicKey], 234 market_states: list[MarketState], 235 market_stores: list[MarketStoreState], 236 slabs: list[Slab], 237 ): 238 """ 239 Returns multiple AverMarket objects from their respective MarketStates, stores and orderbook objects 240 241 Used in refresh.py 242 243 Args: 244 aver_client (AverClient): AverClient object 245 market_pubkeys (list[PublicKey]): List of market public keys 246 market_states (list[MarketState]): List of MarketState objects 247 market_stores (list[MarketStoreState]): List of MarketStoreState objects 248 slabs (list[Slab]): List of slab objects (used in orderbooks) 249 250 Returns: 251 lst[AverMarket]: List of AverMarket objects 252 """ 253 slab_position_counter = 0 254 all_orderbooks = [] 255 for j, market_store in enumerate(market_stores): 256 all_orderbooks_for_market = [] 257 if(market_store is None): 258 all_orderbooks.append(None) 259 continue 260 for i, orderbook in enumerate(market_store.orderbook_accounts): 261 orderbook = Orderbook( 262 orderbook.orderbook, 263 slabs[slab_position_counter + i * 2], 264 slabs[slab_position_counter + i * 2 + 1], 265 orderbook.bids, 266 orderbook.asks, 267 market_states[j].decimals 268 ) 269 all_orderbooks_for_market.append(orderbook) 270 slab_position_counter += len(market_store.orderbook_accounts) * 2 271 all_orderbooks.append(all_orderbooks_for_market) 272 273 markets: list[AverMarket] = [] 274 for i, m in enumerate(market_pubkeys): 275 markets.append(AverMarket( 276 aver_client, 277 m, 278 market_states[i], 279 market_stores[i], 280 all_orderbooks[i] 281 ) 282 ) 283 284 return markets 285 286 287 @staticmethod 288 async def load_market_store_state( 289 aver_client: AverClient, 290 is_market_status_closed: bool, 291 market_store_pubkey: PublicKey, 292 ) -> MarketStoreState: 293 """ 294 Loads onchain data for a MarketStore State 295 296 Args: 297 aver_client (AverClient): AverClient object 298 is_market_status_closed (bool): True if market status is closed, voided or resolved 299 market_store_pubkey (PublicKey): MarketStore public key 300 301 Returns: 302 MarketStoreState: MarketStoreStateobject 303 """ 304 #Closed markets do not have orderbooks 305 if(is_market_status_closed): 306 return None 307 res = await aver_client.program.account['MarketStore'].fetch(market_store_pubkey) 308 return res 309 310 311 @staticmethod 312 async def load_multiple_market_store_states( 313 aver_client: AverClient, 314 market_store_pubkeys: list[PublicKey], 315 ) -> list[MarketStoreState]: 316 """ 317 Loads onchain data for multiple MarketStore States 318 319 Args: 320 aver_client (AverClient): AverClient object 321 market_store_pubkeys (list[PublicKey]): List of MarketStore public keys 322 323 Returns: 324 list[MarketStoreState]: List of MarketStore public keys 325 """ 326 res = await aver_client.program.account['MarketStore'].fetch_multiple(market_store_pubkeys) 327 return res 328 329 @staticmethod 330 def is_market_status_closed(market_status: MarketStatus): 331 """ 332 Checks if a market no longer in a trading status, and therefore will have no Orderbook or MarketStore accounts. 333 Note: Once trading has ceased for a market, these accounts are closed. 334 335 Args: 336 market_status (MarketStatus): Market status (find in MarketState object) 337 338 Returns: 339 bool: Market status closed 340 """ 341 return market_status in [MarketStatus.CEASED_CRANKED_CLOSED, MarketStatus.RESOLVED, MarketStatus.VOIDED] 342 343 @staticmethod 344 async def get_orderbooks_from_orderbook_accounts( 345 conn: AsyncClient, 346 orderbook_accounts: list[OrderbookAccountsState], 347 decimals_list: list[int] 348 ) -> list[Orderbook]: 349 """ 350 Returns Orderbook objects from Orderbook Account objects, by fetching and parsing. 351 352 Args: 353 conn (AsyncClient): Solana AsyncClient object 354 orderbook_accounts (list[OrderbookAccountsState]): List of orderbook account objects 355 decimals_list (list[int]): List of decimal precision for each orderbook account state. Variable normally found in MarketState object 356 357 Raises: 358 Exception: Decimals list and orderbook accounts do not have the same length 359 360 Returns: 361 list[Orderbook]: List of orderbook objects 362 """ 363 if(len(decimals_list) != len(orderbook_accounts)): 364 raise Exception('decimals_list and orderbook_accounts should have the same length') 365 366 all_bids_and_asks_accounts = [] 367 for o in orderbook_accounts: 368 all_bids_and_asks_accounts.append(o.bids) 369 all_bids_and_asks_accounts.append(o.asks) 370 371 372 all_slabs = await Orderbook.load_multiple_slabs(conn, all_bids_and_asks_accounts) 373 orderbooks = [] 374 375 for i, o in enumerate(orderbook_accounts): 376 orderbook = Orderbook( 377 o.orderbook, 378 all_slabs[i*2], 379 all_slabs[i*2 + 1], 380 o.bids,o.asks, 381 decimals_list[i] 382 ) 383 orderbooks.append(orderbook) 384 return orderbooks 385 386 387 388 @staticmethod 389 async def get_orderbooks_from_orderbook_accounts_multiple_markets( 390 conn: AsyncClient, 391 market_states: list[MarketState], 392 market_stores: list[MarketStoreState], 393 are_market_statuses_closed: list[bool], 394 ): 395 """ 396 Returns Orderbook objects from MarketState and MarketStoreStateobjects, 397 by fetching and parsing for multiple markets. 398 399 Use when fetching orderbooks for multiple markets 400 401 Args: 402 conn (AsyncClient): Solana AsyncClient object 403 market_states (list[MarketState]): List of MarketState objects 404 market_stores (list[MarketStoreState]): List of MarketStoreStateobjects 405 are_market_statuses_closed (list[bool]): Lists if each market is closed or not 406 407 Returns: 408 list[list[Orderbook]]: List of orderbooks for each market 409 """ 410 #Create list of accounts and the decimals for each account 411 orderbook_accounts = [] 412 decimals_list = [] 413 for index, market_store in enumerate(market_stores): 414 if(market_store is None): 415 continue 416 orderbook_accounts.extend(market_store.orderbook_accounts) 417 for i in range(len(market_store.orderbook_accounts)): 418 decimals_list.append(market_states[index].decimals) 419 420 #Load all orderbooks 421 all_orderbooks: list[Orderbook] = await AverMarket.get_orderbooks_from_orderbook_accounts(conn, orderbook_accounts, decimals_list) 422 orderbooks_market_list = [] 423 424 #Create a list for each market we received. The list contains all orderbooks for that market 425 for index, market_state in enumerate(market_states): 426 number_of_outcomes = market_state.number_of_outcomes 427 orderbooks = [] 428 #Market is closed 429 if(are_market_statuses_closed[index]): 430 orderbooks_market_list.append(None) 431 continue 432 #Binary markets only have 1 orderbook 433 if(number_of_outcomes == 2): 434 orderbooks.append(all_orderbooks.pop(0)) 435 else: 436 orderbooks = [] 437 for i in range(number_of_outcomes): 438 orderbooks.append(all_orderbooks.pop(0)) 439 orderbooks_market_list.append(orderbooks) 440 441 return orderbooks_market_list 442 443 444 def make_sweep_fees_instruction(self): 445 """ 446 Creates instruction to sweeps fees and sends to relevant accounts 447 448 Returns TransactionInstruction object only. Does not send transaction. 449 450 Returns: 451 TransactionInstruction: TransactionInstruction object 452 """ 453 quote_token = self.aver_client.quote_token 454 third_party_vault_authority, bump = PublicKey.find_program_address( 455 [b"third-party-token-vault", bytes(quote_token)], AVER_PROGRAM_ID) 456 457 third_party_vault_token_account = get_associated_token_address( 458 third_party_vault_authority, quote_token) 459 460 aver_quote_token_account = get_associated_token_address( 461 AVER_MARKET_AUTHORITY, quote_token 462 ) 463 464 return self.aver_client.program.instruction["sweep_fees"]( 465 ctx=Context( 466 accounts={ 467 "market": self.market_pubkey, 468 "quote_vault": self.market_state.quote_vault, 469 "vault_authority": self.market_state.vault_authority, 470 "third_party_token_vault": third_party_vault_token_account, 471 "aver_quote_token_account": aver_quote_token_account, 472 "spl_token_program": TOKEN_PROGRAM_ID, 473 }, 474 signers=[], 475 ), 476 ) 477 478 async def sweep_fees(self, fee_payer: Keypair = None, send_options: TxOpts = None,): 479 """ 480 Sweeps fees and sends to relevant accounts 481 482 Sends instructions on chain 483 484 Args: 485 fee_payer (Keypair): Pays transaction fees. Defaults to AverClient wallet 486 send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None. 487 488 Returns: 489 RPCResponse: Response 490 """ 491 if(fee_payer == None): 492 fee_payer = self.aver_client.owner 493 494 ix = self.make_sweep_fees_instruction() 495 496 return await sign_and_send_transaction_instructions( 497 self.aver_client, 498 [], 499 fee_payer, 500 [ix], 501 send_options 502 ) 503 504 def list_all_pubkeys(self): 505 """ 506 Returns all pubkeys used in AverMarket object 507 508 Returns: 509 list[PublicKey]: List of public keys 510 """ 511 list_of_pubkeys = [self.market_pubkey, self.market_state.quote_vault, self.market_state.oracle_feed] 512 if not self.is_market_status_closed: 513 for ob in self.orderbooks: 514 list_of_pubkeys += [ob.pubkey, ob.slab_asks_pubkey, ob.slab_asks_pubkey] 515 return list_of_pubkeys 516 517 async def get_implied_market_status( 518 self 519 ) -> MarketStatus: 520 """ 521 Returns what we believe the market status ought to be (rather than what is stored in the market's state on-chain) 522 523 Note: As a fail safe, markets have specified times beyond which the market will react as if it is in another Status until it is formally cranked to that Status. 524 For example, if Solana were to have issues and it was not possible for the market's authority to crank it into In-Play status or to Cease Trading in time, 525 the market would use the System Time as a trigger to treat new requests as if it were in the later Status. 526 527 If Solana clock time is beyond TradingCeaseTime, market is TradingCeased 528 If Solana clock time is beyond InPlayStartTime but before TradingCeaseTime, market is ActiveInPlay 529 530 Returns: 531 MarketStatus: Implied market status 532 """ 533 solana_datetime = await self.aver_client.get_system_clock_datetime() 534 if solana_datetime.timestamp() > self.market_state.trading_cease_time: 535 return MarketStatus.TRADING_CEASED 536 if self.market_state.inplay_start_time is not None and solana_datetime.timestamp() > self.market_state.inplay_start_time : 537 return MarketStatus.ACTIVE_IN_PLAY 538 return self.market_state.market_status 539 540 async def crank_market( 541 self, 542 outcome_idxs: list[int] = None, 543 reward_target: PublicKey = None, 544 payer: Keypair = None, 545 ): 546 """ 547 Refresh market before cranking 548 If no outcome_idx are passed, all outcomes are cranked if they meet the criteria to be cranked. 549 """ 550 if outcome_idxs == None: 551 # For binary markets, there is only one orderbook 552 outcome_idxs = [idx for idx in range( 553 1 if self.market_state.number_of_outcomes == 2 else self.market_state.number_of_outcomes)] 554 if self.market_state.number_of_outcomes == 2 and (0 in outcome_idxs or 1 in outcome_idxs): 555 outcome_idxs = [0] 556 if reward_target == None: 557 reward_target = self.aver_client.owner.public_key 558 if payer == None: 559 payer = self.aver_client.owner 560 561 # refreshed_market = await refresh_market(self.aver_client, self) 562 563 event_queues = [o.event_queue for o in self.market_store_state.orderbook_accounts] 564 loaded_event_queues = await load_all_event_queues( 565 self.aver_client.provider.connection, 566 event_queues 567 ) 568 569 sig = '' 570 for idx in outcome_idxs: 571 if loaded_event_queues[idx]["header"].count == 0: 572 continue 573 574 print(f'Cranking market {str(self.market_pubkey)} for outcome {idx} - {loaded_event_queues[idx]["header"].count} events left to crank') 575 if loaded_event_queues[idx]['header'].count > 0: 576 user_accounts = [] 577 for j, event in enumerate(loaded_event_queues[idx]['nodes']): 578 if type(event) == Fill: 579 user_accounts += [event.maker_user_market] 580 else: # Out 581 user_accounts += [event.user_market] 582 if j == MAX_ITERATIONS_FOR_CONSUME_EVENTS: 583 break 584 user_accounts = prepare_user_accounts_list(user_accounts) 585 events_to_crank = min( 586 loaded_event_queues[idx]['header'].count, MAX_ITERATIONS_FOR_CONSUME_EVENTS) 587 588 sig = await self.consume_events( 589 outcome_idx=idx, 590 max_iterations=events_to_crank, 591 user_accounts=user_accounts, 592 reward_target=reward_target, 593 payer=payer, 594 ) 595 596 return sig 597 598 async def consume_events( 599 self, 600 outcome_idx: int, 601 user_accounts: list[PublicKey], 602 max_iterations: int = None, 603 reward_target: PublicKey = None, 604 payer: Keypair = None, 605 ): 606 """ 607 Consume events 608 609 Sends instructions on chain 610 611 Args: 612 outcome_idx (int): index of the outcome 613 user_accounts (list[PublicKey]): List of User Account public keys 614 max_iterations (int, optional): Depth of events to iterate through. Defaults to MAX_ITERATIONS_FOR_CONSUME_EVENTS. 615 reward_target (PublicKey, optional): Target for reward. Defaults to AverClient wallet. 616 payer (Keypair, optional): Fee payer. Defaults to AverClient wallet. 617 618 Returns: 619 Transaction Signature: TransactionSignature object 620 """ 621 if reward_target == None: 622 reward_target = self.aver_client.owner.public_key 623 if payer == None: 624 payer = self.aver_client.owner 625 if max_iterations > MAX_ITERATIONS_FOR_CONSUME_EVENTS or max_iterations == None: 626 max_iterations = MAX_ITERATIONS_FOR_CONSUME_EVENTS 627 628 user_accounts_unsorted = [AccountMeta( 629 pk, False, True) for pk in user_accounts] 630 631 remaining_accounts = sorted(user_accounts_unsorted, key=lambda account: bytes(account.pubkey)) 632 633 return await self.aver_client.program.rpc["consume_events"]( 634 max_iterations, 635 outcome_idx, 636 ctx=Context( 637 accounts={ 638 "market": self.market_pubkey, 639 "market_store": self.market_state.market_store, 640 "orderbook": self.market_store_state.orderbook_accounts[outcome_idx].orderbook, 641 "event_queue": self.market_store_state.orderbook_accounts[outcome_idx].event_queue, 642 "reward_target": reward_target, 643 }, 644 remaining_accounts=remaining_accounts, 645 ), 646 )
20class AverMarket(): 21 """ 22 AverMarket object 23 24 Contains information, references and and orderbooks on a particular market 25 """ 26 27 market_pubkey: PublicKey 28 """Market pubkey""" 29 market_state: MarketState 30 """MarketState object holding data about the market""" 31 market_store_state: MarketStoreState 32 """MarketStoreStateobject holding data about the market required during active trading. 33 34 This does not exist if the market has stopped trading, voided or resolved""" 35 orderbooks: list[Orderbook] 36 """ 37 Ordered list of Orderbooks for this market. 38 39 Note: Binary (two-outcome0) markets only have 1 orderbook. 40 """ 41 aver_client: AverClient 42 """AverClient object""" 43 44 def __init__( 45 self, 46 aver_client: AverClient, 47 market_pubkey: PublicKey, 48 market_state: MarketState, 49 market_store_state: MarketStoreState = None, 50 orderbooks: list[Orderbook] = None, 51 ): 52 """ 53 Initialise an AverMarket object. Do not use this function; use AverMarket.load() instead. 54 55 Args: 56 aver_client (AverClient): AverClient object 57 market_pubkey (PublicKey): Market public key 58 market_state (MarketState): MarketState object 59 market_store_state (MarketStoreState, optional): MarketStoreState object. Defaults to None. 60 orderbooks (list[Orderbook], optional): List of Orderbook objects. Defaults to None. 61 """ 62 self.market_pubkey = market_pubkey 63 self.market_state = market_state 64 self.market_store_state = market_store_state 65 self.orderbooks = orderbooks 66 self.aver_client = aver_client 67 68 if(market_state.number_of_outcomes == 2 and orderbooks is not None and len(orderbooks) == 1): 69 orderbooks.append(orderbooks[0].invert()) 70 71 @staticmethod 72 async def load(aver_client: AverClient, market_pubkey: PublicKey): 73 """ 74 Initialises an AverMarket object 75 76 To refresh data on an already loaded market use src.refresh.refresh_markets() 77 78 Args: 79 aver_client (AverClient): AverClient object 80 market_pubkey (PublicKey): Market public key 81 82 Returns: 83 AverMarket: AverMarket object 84 """ 85 market_state_and_store = await AverMarket.load_market_state_and_store(aver_client, market_pubkey) 86 market_state: MarketState = market_state_and_store['market_states'][0] 87 market_store_state = None 88 orderbooks = None 89 is_market_status_closed = AverMarket.is_market_status_closed(market_state.market_status) 90 market_store_state: MarketStoreState = market_state_and_store['market_stores'][0] 91 92 if(not is_market_status_closed): 93 orderbooks = await AverMarket.get_orderbooks_from_orderbook_accounts( 94 aver_client.provider.connection, 95 market_store_state.orderbook_accounts, 96 [market_state.decimals] * len(market_store_state.orderbook_accounts) 97 ) 98 99 return AverMarket(aver_client, market_pubkey, market_state, market_store_state, orderbooks) 100 101 @staticmethod 102 async def load_multiple(aver_client: AverClient, market_pubkeys: list[PublicKey]): 103 """ 104 Initialises multiple AverMarket objects 105 106 This method is quicker that using Market.load() multiple times 107 108 To refresh data on already loaded markets use src.refresh.refresh_multiple_markets() 109 110 Args: 111 aver_client (AverClient): AverClient object 112 market_pubkeys (list[PublicKey]): List of Market public keys 113 114 Returns: 115 list[AverMarket]: List of AverMarket objects 116 """ 117 market_states_and_stores = await AverMarket.load_multiple_market_states_and_stores(aver_client, market_pubkeys) 118 market_states: list[MarketState] = market_states_and_stores['market_states'] 119 120 are_market_statuses_closed = [] 121 for market_state in market_states: 122 are_market_statuses_closed.append(AverMarket.is_market_status_closed(market_state.market_status)) 123 124 market_stores: list[MarketStoreState] = market_states_and_stores['market_stores'] 125 126 orderbooks_market_list = await AverMarket.get_orderbooks_from_orderbook_accounts_multiple_markets( 127 aver_client.provider.connection, 128 market_states, 129 market_stores, 130 are_market_statuses_closed, 131 ) 132 133 markets: list[AverMarket] = [] 134 for index, market_pubkey in enumerate(market_pubkeys): 135 market = AverMarket( 136 aver_client, 137 market_pubkey, 138 market_states[index], 139 market_stores[index], 140 orderbooks_market_list[index] 141 ) 142 markets.append(market) 143 144 return markets 145 146 @staticmethod 147 async def load_market_state(aver_client: AverClient, market_pubkey: PublicKey) -> MarketState: 148 """ 149 Loads onchain data for a MarketState 150 151 Args: 152 aver_client (AverClient): AverClient object 153 market_pubkey (PublicKey): Market public key 154 155 Returns: 156 MarketState: MarketState object 157 """ 158 res = await aver_client.program.account['Market'].fetch(market_pubkey) 159 return res 160 161 @staticmethod 162 async def load_multiple_market_states(aver_client: AverClient, market_pubkeys: list[PublicKey]) -> list[MarketState]: 163 """ 164 Loads onchain data for multiple MarketStates 165 166 Args: 167 aver_client (AverClient): AverClient object 168 market_pubkeys (list[PublicKey]): List of market public keys 169 170 Returns: 171 list[MarketState]: List of MarketState objects 172 """ 173 res = await aver_client.program.account['Market'].fetch_multiple(market_pubkeys) 174 return res 175 176 @staticmethod 177 async def load_market_state_and_store(aver_client: AverClient, market_pubkey: PublicKey): 178 """ 179 Loads onchain data for multiple MarketStates and MarketStoreStates at once 180 181 Args: 182 aver_client (AverClient): AverClient object 183 market_pubkey (PublicKey]: Market public key 184 185 Returns: 186 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 187 """ 188 return await AverMarket.load_multiple_market_states_and_stores(aver_client, [market_pubkey]) 189 190 @staticmethod 191 async def load_multiple_market_states_and_stores(aver_client: AverClient, market_pubkeys: list[PublicKey]): 192 """ 193 Loads onchain data for multiple MarketStates and MarketStoreStates at once 194 195 Args: 196 aver_client (AverClient): AverClient object 197 market_pubkeys (list[PublicKey]): List of market public keys 198 199 Returns: 200 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 201 """ 202 market_store_pubkeys = [AverMarket.derive_market_store_pubkey_and_bump(m, AVER_PROGRAM_ID)[0] for m in market_pubkeys] 203 204 data = await load_multiple_bytes_data(aver_client.connection, market_pubkeys + market_store_pubkeys) 205 market_states_data = data[0:len(market_pubkeys)] 206 market_stores_data = data[len(market_pubkeys):] 207 208 market_states = [parse_market_state(d, aver_client) for d in market_states_data] 209 market_stores = [parse_market_store(d, aver_client) if d is not None else None for d in market_stores_data] 210 211 return {'market_states': market_states, 'market_stores': market_stores} 212 213 @staticmethod 214 def derive_market_store_pubkey_and_bump(market_pubkey: PublicKey, program_id: PublicKey = AVER_PROGRAM_ID): 215 """ 216 Derives PDA (Program Derived Account) for MarketStore public key. 217 MarketStore account addresses are derived deterministically using the market's pubkey. 218 219 Args: 220 market_pubkey (PublicKey): Market public key 221 program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID. 222 223 Returns: 224 PublicKey: MarketStore public key 225 """ 226 return PublicKey.find_program_address( 227 [bytes('market-store', 'utf-8'), bytes(market_pubkey)], 228 program_id 229 ) 230 231 @staticmethod 232 def get_markets_from_account_states( 233 aver_client: AverClient, 234 market_pubkeys: list[PublicKey], 235 market_states: list[MarketState], 236 market_stores: list[MarketStoreState], 237 slabs: list[Slab], 238 ): 239 """ 240 Returns multiple AverMarket objects from their respective MarketStates, stores and orderbook objects 241 242 Used in refresh.py 243 244 Args: 245 aver_client (AverClient): AverClient object 246 market_pubkeys (list[PublicKey]): List of market public keys 247 market_states (list[MarketState]): List of MarketState objects 248 market_stores (list[MarketStoreState]): List of MarketStoreState objects 249 slabs (list[Slab]): List of slab objects (used in orderbooks) 250 251 Returns: 252 lst[AverMarket]: List of AverMarket objects 253 """ 254 slab_position_counter = 0 255 all_orderbooks = [] 256 for j, market_store in enumerate(market_stores): 257 all_orderbooks_for_market = [] 258 if(market_store is None): 259 all_orderbooks.append(None) 260 continue 261 for i, orderbook in enumerate(market_store.orderbook_accounts): 262 orderbook = Orderbook( 263 orderbook.orderbook, 264 slabs[slab_position_counter + i * 2], 265 slabs[slab_position_counter + i * 2 + 1], 266 orderbook.bids, 267 orderbook.asks, 268 market_states[j].decimals 269 ) 270 all_orderbooks_for_market.append(orderbook) 271 slab_position_counter += len(market_store.orderbook_accounts) * 2 272 all_orderbooks.append(all_orderbooks_for_market) 273 274 markets: list[AverMarket] = [] 275 for i, m in enumerate(market_pubkeys): 276 markets.append(AverMarket( 277 aver_client, 278 m, 279 market_states[i], 280 market_stores[i], 281 all_orderbooks[i] 282 ) 283 ) 284 285 return markets 286 287 288 @staticmethod 289 async def load_market_store_state( 290 aver_client: AverClient, 291 is_market_status_closed: bool, 292 market_store_pubkey: PublicKey, 293 ) -> MarketStoreState: 294 """ 295 Loads onchain data for a MarketStore State 296 297 Args: 298 aver_client (AverClient): AverClient object 299 is_market_status_closed (bool): True if market status is closed, voided or resolved 300 market_store_pubkey (PublicKey): MarketStore public key 301 302 Returns: 303 MarketStoreState: MarketStoreStateobject 304 """ 305 #Closed markets do not have orderbooks 306 if(is_market_status_closed): 307 return None 308 res = await aver_client.program.account['MarketStore'].fetch(market_store_pubkey) 309 return res 310 311 312 @staticmethod 313 async def load_multiple_market_store_states( 314 aver_client: AverClient, 315 market_store_pubkeys: list[PublicKey], 316 ) -> list[MarketStoreState]: 317 """ 318 Loads onchain data for multiple MarketStore States 319 320 Args: 321 aver_client (AverClient): AverClient object 322 market_store_pubkeys (list[PublicKey]): List of MarketStore public keys 323 324 Returns: 325 list[MarketStoreState]: List of MarketStore public keys 326 """ 327 res = await aver_client.program.account['MarketStore'].fetch_multiple(market_store_pubkeys) 328 return res 329 330 @staticmethod 331 def is_market_status_closed(market_status: MarketStatus): 332 """ 333 Checks if a market no longer in a trading status, and therefore will have no Orderbook or MarketStore accounts. 334 Note: Once trading has ceased for a market, these accounts are closed. 335 336 Args: 337 market_status (MarketStatus): Market status (find in MarketState object) 338 339 Returns: 340 bool: Market status closed 341 """ 342 return market_status in [MarketStatus.CEASED_CRANKED_CLOSED, MarketStatus.RESOLVED, MarketStatus.VOIDED] 343 344 @staticmethod 345 async def get_orderbooks_from_orderbook_accounts( 346 conn: AsyncClient, 347 orderbook_accounts: list[OrderbookAccountsState], 348 decimals_list: list[int] 349 ) -> list[Orderbook]: 350 """ 351 Returns Orderbook objects from Orderbook Account objects, by fetching and parsing. 352 353 Args: 354 conn (AsyncClient): Solana AsyncClient object 355 orderbook_accounts (list[OrderbookAccountsState]): List of orderbook account objects 356 decimals_list (list[int]): List of decimal precision for each orderbook account state. Variable normally found in MarketState object 357 358 Raises: 359 Exception: Decimals list and orderbook accounts do not have the same length 360 361 Returns: 362 list[Orderbook]: List of orderbook objects 363 """ 364 if(len(decimals_list) != len(orderbook_accounts)): 365 raise Exception('decimals_list and orderbook_accounts should have the same length') 366 367 all_bids_and_asks_accounts = [] 368 for o in orderbook_accounts: 369 all_bids_and_asks_accounts.append(o.bids) 370 all_bids_and_asks_accounts.append(o.asks) 371 372 373 all_slabs = await Orderbook.load_multiple_slabs(conn, all_bids_and_asks_accounts) 374 orderbooks = [] 375 376 for i, o in enumerate(orderbook_accounts): 377 orderbook = Orderbook( 378 o.orderbook, 379 all_slabs[i*2], 380 all_slabs[i*2 + 1], 381 o.bids,o.asks, 382 decimals_list[i] 383 ) 384 orderbooks.append(orderbook) 385 return orderbooks 386 387 388 389 @staticmethod 390 async def get_orderbooks_from_orderbook_accounts_multiple_markets( 391 conn: AsyncClient, 392 market_states: list[MarketState], 393 market_stores: list[MarketStoreState], 394 are_market_statuses_closed: list[bool], 395 ): 396 """ 397 Returns Orderbook objects from MarketState and MarketStoreStateobjects, 398 by fetching and parsing for multiple markets. 399 400 Use when fetching orderbooks for multiple markets 401 402 Args: 403 conn (AsyncClient): Solana AsyncClient object 404 market_states (list[MarketState]): List of MarketState objects 405 market_stores (list[MarketStoreState]): List of MarketStoreStateobjects 406 are_market_statuses_closed (list[bool]): Lists if each market is closed or not 407 408 Returns: 409 list[list[Orderbook]]: List of orderbooks for each market 410 """ 411 #Create list of accounts and the decimals for each account 412 orderbook_accounts = [] 413 decimals_list = [] 414 for index, market_store in enumerate(market_stores): 415 if(market_store is None): 416 continue 417 orderbook_accounts.extend(market_store.orderbook_accounts) 418 for i in range(len(market_store.orderbook_accounts)): 419 decimals_list.append(market_states[index].decimals) 420 421 #Load all orderbooks 422 all_orderbooks: list[Orderbook] = await AverMarket.get_orderbooks_from_orderbook_accounts(conn, orderbook_accounts, decimals_list) 423 orderbooks_market_list = [] 424 425 #Create a list for each market we received. The list contains all orderbooks for that market 426 for index, market_state in enumerate(market_states): 427 number_of_outcomes = market_state.number_of_outcomes 428 orderbooks = [] 429 #Market is closed 430 if(are_market_statuses_closed[index]): 431 orderbooks_market_list.append(None) 432 continue 433 #Binary markets only have 1 orderbook 434 if(number_of_outcomes == 2): 435 orderbooks.append(all_orderbooks.pop(0)) 436 else: 437 orderbooks = [] 438 for i in range(number_of_outcomes): 439 orderbooks.append(all_orderbooks.pop(0)) 440 orderbooks_market_list.append(orderbooks) 441 442 return orderbooks_market_list 443 444 445 def make_sweep_fees_instruction(self): 446 """ 447 Creates instruction to sweeps fees and sends to relevant accounts 448 449 Returns TransactionInstruction object only. Does not send transaction. 450 451 Returns: 452 TransactionInstruction: TransactionInstruction object 453 """ 454 quote_token = self.aver_client.quote_token 455 third_party_vault_authority, bump = PublicKey.find_program_address( 456 [b"third-party-token-vault", bytes(quote_token)], AVER_PROGRAM_ID) 457 458 third_party_vault_token_account = get_associated_token_address( 459 third_party_vault_authority, quote_token) 460 461 aver_quote_token_account = get_associated_token_address( 462 AVER_MARKET_AUTHORITY, quote_token 463 ) 464 465 return self.aver_client.program.instruction["sweep_fees"]( 466 ctx=Context( 467 accounts={ 468 "market": self.market_pubkey, 469 "quote_vault": self.market_state.quote_vault, 470 "vault_authority": self.market_state.vault_authority, 471 "third_party_token_vault": third_party_vault_token_account, 472 "aver_quote_token_account": aver_quote_token_account, 473 "spl_token_program": TOKEN_PROGRAM_ID, 474 }, 475 signers=[], 476 ), 477 ) 478 479 async def sweep_fees(self, fee_payer: Keypair = None, send_options: TxOpts = None,): 480 """ 481 Sweeps fees and sends to relevant accounts 482 483 Sends instructions on chain 484 485 Args: 486 fee_payer (Keypair): Pays transaction fees. Defaults to AverClient wallet 487 send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None. 488 489 Returns: 490 RPCResponse: Response 491 """ 492 if(fee_payer == None): 493 fee_payer = self.aver_client.owner 494 495 ix = self.make_sweep_fees_instruction() 496 497 return await sign_and_send_transaction_instructions( 498 self.aver_client, 499 [], 500 fee_payer, 501 [ix], 502 send_options 503 ) 504 505 def list_all_pubkeys(self): 506 """ 507 Returns all pubkeys used in AverMarket object 508 509 Returns: 510 list[PublicKey]: List of public keys 511 """ 512 list_of_pubkeys = [self.market_pubkey, self.market_state.quote_vault, self.market_state.oracle_feed] 513 if not self.is_market_status_closed: 514 for ob in self.orderbooks: 515 list_of_pubkeys += [ob.pubkey, ob.slab_asks_pubkey, ob.slab_asks_pubkey] 516 return list_of_pubkeys 517 518 async def get_implied_market_status( 519 self 520 ) -> MarketStatus: 521 """ 522 Returns what we believe the market status ought to be (rather than what is stored in the market's state on-chain) 523 524 Note: As a fail safe, markets have specified times beyond which the market will react as if it is in another Status until it is formally cranked to that Status. 525 For example, if Solana were to have issues and it was not possible for the market's authority to crank it into In-Play status or to Cease Trading in time, 526 the market would use the System Time as a trigger to treat new requests as if it were in the later Status. 527 528 If Solana clock time is beyond TradingCeaseTime, market is TradingCeased 529 If Solana clock time is beyond InPlayStartTime but before TradingCeaseTime, market is ActiveInPlay 530 531 Returns: 532 MarketStatus: Implied market status 533 """ 534 solana_datetime = await self.aver_client.get_system_clock_datetime() 535 if solana_datetime.timestamp() > self.market_state.trading_cease_time: 536 return MarketStatus.TRADING_CEASED 537 if self.market_state.inplay_start_time is not None and solana_datetime.timestamp() > self.market_state.inplay_start_time : 538 return MarketStatus.ACTIVE_IN_PLAY 539 return self.market_state.market_status 540 541 async def crank_market( 542 self, 543 outcome_idxs: list[int] = None, 544 reward_target: PublicKey = None, 545 payer: Keypair = None, 546 ): 547 """ 548 Refresh market before cranking 549 If no outcome_idx are passed, all outcomes are cranked if they meet the criteria to be cranked. 550 """ 551 if outcome_idxs == None: 552 # For binary markets, there is only one orderbook 553 outcome_idxs = [idx for idx in range( 554 1 if self.market_state.number_of_outcomes == 2 else self.market_state.number_of_outcomes)] 555 if self.market_state.number_of_outcomes == 2 and (0 in outcome_idxs or 1 in outcome_idxs): 556 outcome_idxs = [0] 557 if reward_target == None: 558 reward_target = self.aver_client.owner.public_key 559 if payer == None: 560 payer = self.aver_client.owner 561 562 # refreshed_market = await refresh_market(self.aver_client, self) 563 564 event_queues = [o.event_queue for o in self.market_store_state.orderbook_accounts] 565 loaded_event_queues = await load_all_event_queues( 566 self.aver_client.provider.connection, 567 event_queues 568 ) 569 570 sig = '' 571 for idx in outcome_idxs: 572 if loaded_event_queues[idx]["header"].count == 0: 573 continue 574 575 print(f'Cranking market {str(self.market_pubkey)} for outcome {idx} - {loaded_event_queues[idx]["header"].count} events left to crank') 576 if loaded_event_queues[idx]['header'].count > 0: 577 user_accounts = [] 578 for j, event in enumerate(loaded_event_queues[idx]['nodes']): 579 if type(event) == Fill: 580 user_accounts += [event.maker_user_market] 581 else: # Out 582 user_accounts += [event.user_market] 583 if j == MAX_ITERATIONS_FOR_CONSUME_EVENTS: 584 break 585 user_accounts = prepare_user_accounts_list(user_accounts) 586 events_to_crank = min( 587 loaded_event_queues[idx]['header'].count, MAX_ITERATIONS_FOR_CONSUME_EVENTS) 588 589 sig = await self.consume_events( 590 outcome_idx=idx, 591 max_iterations=events_to_crank, 592 user_accounts=user_accounts, 593 reward_target=reward_target, 594 payer=payer, 595 ) 596 597 return sig 598 599 async def consume_events( 600 self, 601 outcome_idx: int, 602 user_accounts: list[PublicKey], 603 max_iterations: int = None, 604 reward_target: PublicKey = None, 605 payer: Keypair = None, 606 ): 607 """ 608 Consume events 609 610 Sends instructions on chain 611 612 Args: 613 outcome_idx (int): index of the outcome 614 user_accounts (list[PublicKey]): List of User Account public keys 615 max_iterations (int, optional): Depth of events to iterate through. Defaults to MAX_ITERATIONS_FOR_CONSUME_EVENTS. 616 reward_target (PublicKey, optional): Target for reward. Defaults to AverClient wallet. 617 payer (Keypair, optional): Fee payer. Defaults to AverClient wallet. 618 619 Returns: 620 Transaction Signature: TransactionSignature object 621 """ 622 if reward_target == None: 623 reward_target = self.aver_client.owner.public_key 624 if payer == None: 625 payer = self.aver_client.owner 626 if max_iterations > MAX_ITERATIONS_FOR_CONSUME_EVENTS or max_iterations == None: 627 max_iterations = MAX_ITERATIONS_FOR_CONSUME_EVENTS 628 629 user_accounts_unsorted = [AccountMeta( 630 pk, False, True) for pk in user_accounts] 631 632 remaining_accounts = sorted(user_accounts_unsorted, key=lambda account: bytes(account.pubkey)) 633 634 return await self.aver_client.program.rpc["consume_events"]( 635 max_iterations, 636 outcome_idx, 637 ctx=Context( 638 accounts={ 639 "market": self.market_pubkey, 640 "market_store": self.market_state.market_store, 641 "orderbook": self.market_store_state.orderbook_accounts[outcome_idx].orderbook, 642 "event_queue": self.market_store_state.orderbook_accounts[outcome_idx].event_queue, 643 "reward_target": reward_target, 644 }, 645 remaining_accounts=remaining_accounts, 646 ), 647 )
AverMarket object
Contains information, references and and orderbooks on a particular market
44 def __init__( 45 self, 46 aver_client: AverClient, 47 market_pubkey: PublicKey, 48 market_state: MarketState, 49 market_store_state: MarketStoreState = None, 50 orderbooks: list[Orderbook] = None, 51 ): 52 """ 53 Initialise an AverMarket object. Do not use this function; use AverMarket.load() instead. 54 55 Args: 56 aver_client (AverClient): AverClient object 57 market_pubkey (PublicKey): Market public key 58 market_state (MarketState): MarketState object 59 market_store_state (MarketStoreState, optional): MarketStoreState object. Defaults to None. 60 orderbooks (list[Orderbook], optional): List of Orderbook objects. Defaults to None. 61 """ 62 self.market_pubkey = market_pubkey 63 self.market_state = market_state 64 self.market_store_state = market_store_state 65 self.orderbooks = orderbooks 66 self.aver_client = aver_client 67 68 if(market_state.number_of_outcomes == 2 and orderbooks is not None and len(orderbooks) == 1): 69 orderbooks.append(orderbooks[0].invert())
Initialise an AverMarket object. Do not use this function; use AverMarket.load() instead.
Args
- aver_client (AverClient): AverClient object
- market_pubkey (PublicKey): Market public key
- market_state (MarketState): MarketState object
- market_store_state (MarketStoreState, optional): MarketStoreState object. Defaults to None.
- orderbooks (list[Orderbook], optional): List of Orderbook objects. Defaults to None.
MarketStoreStateobject holding data about the market required during active trading.
This does not exist if the market has stopped trading, voided or resolved
Ordered list of Orderbooks for this market.
Note: Binary (two-outcome0) markets only have 1 orderbook.
71 @staticmethod 72 async def load(aver_client: AverClient, market_pubkey: PublicKey): 73 """ 74 Initialises an AverMarket object 75 76 To refresh data on an already loaded market use src.refresh.refresh_markets() 77 78 Args: 79 aver_client (AverClient): AverClient object 80 market_pubkey (PublicKey): Market public key 81 82 Returns: 83 AverMarket: AverMarket object 84 """ 85 market_state_and_store = await AverMarket.load_market_state_and_store(aver_client, market_pubkey) 86 market_state: MarketState = market_state_and_store['market_states'][0] 87 market_store_state = None 88 orderbooks = None 89 is_market_status_closed = AverMarket.is_market_status_closed(market_state.market_status) 90 market_store_state: MarketStoreState = market_state_and_store['market_stores'][0] 91 92 if(not is_market_status_closed): 93 orderbooks = await AverMarket.get_orderbooks_from_orderbook_accounts( 94 aver_client.provider.connection, 95 market_store_state.orderbook_accounts, 96 [market_state.decimals] * len(market_store_state.orderbook_accounts) 97 ) 98 99 return AverMarket(aver_client, market_pubkey, market_state, market_store_state, orderbooks)
Initialises an AverMarket object
To refresh data on an already loaded market use src.refresh.refresh_markets()
Args
- aver_client (AverClient): AverClient object
- market_pubkey (PublicKey): Market public key
Returns
AverMarket: AverMarket object
101 @staticmethod 102 async def load_multiple(aver_client: AverClient, market_pubkeys: list[PublicKey]): 103 """ 104 Initialises multiple AverMarket objects 105 106 This method is quicker that using Market.load() multiple times 107 108 To refresh data on already loaded markets use src.refresh.refresh_multiple_markets() 109 110 Args: 111 aver_client (AverClient): AverClient object 112 market_pubkeys (list[PublicKey]): List of Market public keys 113 114 Returns: 115 list[AverMarket]: List of AverMarket objects 116 """ 117 market_states_and_stores = await AverMarket.load_multiple_market_states_and_stores(aver_client, market_pubkeys) 118 market_states: list[MarketState] = market_states_and_stores['market_states'] 119 120 are_market_statuses_closed = [] 121 for market_state in market_states: 122 are_market_statuses_closed.append(AverMarket.is_market_status_closed(market_state.market_status)) 123 124 market_stores: list[MarketStoreState] = market_states_and_stores['market_stores'] 125 126 orderbooks_market_list = await AverMarket.get_orderbooks_from_orderbook_accounts_multiple_markets( 127 aver_client.provider.connection, 128 market_states, 129 market_stores, 130 are_market_statuses_closed, 131 ) 132 133 markets: list[AverMarket] = [] 134 for index, market_pubkey in enumerate(market_pubkeys): 135 market = AverMarket( 136 aver_client, 137 market_pubkey, 138 market_states[index], 139 market_stores[index], 140 orderbooks_market_list[index] 141 ) 142 markets.append(market) 143 144 return markets
Initialises multiple AverMarket objects
This method is quicker that using Market.load() multiple times
To refresh data on already loaded markets use src.refresh.refresh_multiple_markets()
Args
- aver_client (AverClient): AverClient object
- market_pubkeys (list[PublicKey]): List of Market public keys
Returns
list[AverMarket]: List of AverMarket objects
146 @staticmethod 147 async def load_market_state(aver_client: AverClient, market_pubkey: PublicKey) -> MarketState: 148 """ 149 Loads onchain data for a MarketState 150 151 Args: 152 aver_client (AverClient): AverClient object 153 market_pubkey (PublicKey): Market public key 154 155 Returns: 156 MarketState: MarketState object 157 """ 158 res = await aver_client.program.account['Market'].fetch(market_pubkey) 159 return res
Loads onchain data for a MarketState
Args
- aver_client (AverClient): AverClient object
- market_pubkey (PublicKey): Market public key
Returns
MarketState: MarketState object
161 @staticmethod 162 async def load_multiple_market_states(aver_client: AverClient, market_pubkeys: list[PublicKey]) -> list[MarketState]: 163 """ 164 Loads onchain data for multiple MarketStates 165 166 Args: 167 aver_client (AverClient): AverClient object 168 market_pubkeys (list[PublicKey]): List of market public keys 169 170 Returns: 171 list[MarketState]: List of MarketState objects 172 """ 173 res = await aver_client.program.account['Market'].fetch_multiple(market_pubkeys) 174 return res
Loads onchain data for multiple MarketStates
Args
- aver_client (AverClient): AverClient object
- market_pubkeys (list[PublicKey]): List of market public keys
Returns
list[MarketState]: List of MarketState objects
176 @staticmethod 177 async def load_market_state_and_store(aver_client: AverClient, market_pubkey: PublicKey): 178 """ 179 Loads onchain data for multiple MarketStates and MarketStoreStates at once 180 181 Args: 182 aver_client (AverClient): AverClient object 183 market_pubkey (PublicKey]: Market public key 184 185 Returns: 186 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 187 """ 188 return await AverMarket.load_multiple_market_states_and_stores(aver_client, [market_pubkey])
Loads onchain data for multiple MarketStates and MarketStoreStates at once
Args
- aver_client (AverClient): AverClient object
- market_pubkey (PublicKey]: Market public key
Returns
dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores
190 @staticmethod 191 async def load_multiple_market_states_and_stores(aver_client: AverClient, market_pubkeys: list[PublicKey]): 192 """ 193 Loads onchain data for multiple MarketStates and MarketStoreStates at once 194 195 Args: 196 aver_client (AverClient): AverClient object 197 market_pubkeys (list[PublicKey]): List of market public keys 198 199 Returns: 200 dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores 201 """ 202 market_store_pubkeys = [AverMarket.derive_market_store_pubkey_and_bump(m, AVER_PROGRAM_ID)[0] for m in market_pubkeys] 203 204 data = await load_multiple_bytes_data(aver_client.connection, market_pubkeys + market_store_pubkeys) 205 market_states_data = data[0:len(market_pubkeys)] 206 market_stores_data = data[len(market_pubkeys):] 207 208 market_states = [parse_market_state(d, aver_client) for d in market_states_data] 209 market_stores = [parse_market_store(d, aver_client) if d is not None else None for d in market_stores_data] 210 211 return {'market_states': market_states, 'market_stores': market_stores}
Loads onchain data for multiple MarketStates and MarketStoreStates at once
Args
- aver_client (AverClient): AverClient object
- market_pubkeys (list[PublicKey]): List of market public keys
Returns
dict[str, list[MarketState] or list[MarketStoreState]]: Keys are market_states or market_stores
213 @staticmethod 214 def derive_market_store_pubkey_and_bump(market_pubkey: PublicKey, program_id: PublicKey = AVER_PROGRAM_ID): 215 """ 216 Derives PDA (Program Derived Account) for MarketStore public key. 217 MarketStore account addresses are derived deterministically using the market's pubkey. 218 219 Args: 220 market_pubkey (PublicKey): Market public key 221 program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID. 222 223 Returns: 224 PublicKey: MarketStore public key 225 """ 226 return PublicKey.find_program_address( 227 [bytes('market-store', 'utf-8'), bytes(market_pubkey)], 228 program_id 229 )
Derives PDA (Program Derived Account) for MarketStore public key. MarketStore account addresses are derived deterministically using the market's pubkey.
Args
- market_pubkey (PublicKey): Market public key
- program_id (PublicKey, optional): Program public key. Defaults to AVER_PROGRAM_ID.
Returns
PublicKey: MarketStore public key
231 @staticmethod 232 def get_markets_from_account_states( 233 aver_client: AverClient, 234 market_pubkeys: list[PublicKey], 235 market_states: list[MarketState], 236 market_stores: list[MarketStoreState], 237 slabs: list[Slab], 238 ): 239 """ 240 Returns multiple AverMarket objects from their respective MarketStates, stores and orderbook objects 241 242 Used in refresh.py 243 244 Args: 245 aver_client (AverClient): AverClient object 246 market_pubkeys (list[PublicKey]): List of market public keys 247 market_states (list[MarketState]): List of MarketState objects 248 market_stores (list[MarketStoreState]): List of MarketStoreState objects 249 slabs (list[Slab]): List of slab objects (used in orderbooks) 250 251 Returns: 252 lst[AverMarket]: List of AverMarket objects 253 """ 254 slab_position_counter = 0 255 all_orderbooks = [] 256 for j, market_store in enumerate(market_stores): 257 all_orderbooks_for_market = [] 258 if(market_store is None): 259 all_orderbooks.append(None) 260 continue 261 for i, orderbook in enumerate(market_store.orderbook_accounts): 262 orderbook = Orderbook( 263 orderbook.orderbook, 264 slabs[slab_position_counter + i * 2], 265 slabs[slab_position_counter + i * 2 + 1], 266 orderbook.bids, 267 orderbook.asks, 268 market_states[j].decimals 269 ) 270 all_orderbooks_for_market.append(orderbook) 271 slab_position_counter += len(market_store.orderbook_accounts) * 2 272 all_orderbooks.append(all_orderbooks_for_market) 273 274 markets: list[AverMarket] = [] 275 for i, m in enumerate(market_pubkeys): 276 markets.append(AverMarket( 277 aver_client, 278 m, 279 market_states[i], 280 market_stores[i], 281 all_orderbooks[i] 282 ) 283 ) 284 285 return markets
Returns multiple AverMarket objects from their respective MarketStates, stores and orderbook objects
Used in refresh.py
Args
- aver_client (AverClient): AverClient object
- market_pubkeys (list[PublicKey]): List of market public keys
- market_states (list[MarketState]): List of MarketState objects
- market_stores (list[MarketStoreState]): List of MarketStoreState objects
- slabs (list[Slab]): List of slab objects (used in orderbooks)
Returns
lst[AverMarket]: List of AverMarket objects
288 @staticmethod 289 async def load_market_store_state( 290 aver_client: AverClient, 291 is_market_status_closed: bool, 292 market_store_pubkey: PublicKey, 293 ) -> MarketStoreState: 294 """ 295 Loads onchain data for a MarketStore State 296 297 Args: 298 aver_client (AverClient): AverClient object 299 is_market_status_closed (bool): True if market status is closed, voided or resolved 300 market_store_pubkey (PublicKey): MarketStore public key 301 302 Returns: 303 MarketStoreState: MarketStoreStateobject 304 """ 305 #Closed markets do not have orderbooks 306 if(is_market_status_closed): 307 return None 308 res = await aver_client.program.account['MarketStore'].fetch(market_store_pubkey) 309 return res
Loads onchain data for a MarketStore State
Args
- aver_client (AverClient): AverClient object
- is_market_status_closed (bool): True if market status is closed, voided or resolved
- market_store_pubkey (PublicKey): MarketStore public key
Returns
MarketStoreState: MarketStoreStateobject
312 @staticmethod 313 async def load_multiple_market_store_states( 314 aver_client: AverClient, 315 market_store_pubkeys: list[PublicKey], 316 ) -> list[MarketStoreState]: 317 """ 318 Loads onchain data for multiple MarketStore States 319 320 Args: 321 aver_client (AverClient): AverClient object 322 market_store_pubkeys (list[PublicKey]): List of MarketStore public keys 323 324 Returns: 325 list[MarketStoreState]: List of MarketStore public keys 326 """ 327 res = await aver_client.program.account['MarketStore'].fetch_multiple(market_store_pubkeys) 328 return res
Loads onchain data for multiple MarketStore States
Args
- aver_client (AverClient): AverClient object
- market_store_pubkeys (list[PublicKey]): List of MarketStore public keys
Returns
list[MarketStoreState]: List of MarketStore public keys
330 @staticmethod 331 def is_market_status_closed(market_status: MarketStatus): 332 """ 333 Checks if a market no longer in a trading status, and therefore will have no Orderbook or MarketStore accounts. 334 Note: Once trading has ceased for a market, these accounts are closed. 335 336 Args: 337 market_status (MarketStatus): Market status (find in MarketState object) 338 339 Returns: 340 bool: Market status closed 341 """ 342 return market_status in [MarketStatus.CEASED_CRANKED_CLOSED, MarketStatus.RESOLVED, MarketStatus.VOIDED]
Checks if a market no longer in a trading status, and therefore will have no Orderbook or MarketStore accounts. Note: Once trading has ceased for a market, these accounts are closed.
Args
- market_status (MarketStatus): Market status (find in MarketState object)
Returns
bool: Market status closed
344 @staticmethod 345 async def get_orderbooks_from_orderbook_accounts( 346 conn: AsyncClient, 347 orderbook_accounts: list[OrderbookAccountsState], 348 decimals_list: list[int] 349 ) -> list[Orderbook]: 350 """ 351 Returns Orderbook objects from Orderbook Account objects, by fetching and parsing. 352 353 Args: 354 conn (AsyncClient): Solana AsyncClient object 355 orderbook_accounts (list[OrderbookAccountsState]): List of orderbook account objects 356 decimals_list (list[int]): List of decimal precision for each orderbook account state. Variable normally found in MarketState object 357 358 Raises: 359 Exception: Decimals list and orderbook accounts do not have the same length 360 361 Returns: 362 list[Orderbook]: List of orderbook objects 363 """ 364 if(len(decimals_list) != len(orderbook_accounts)): 365 raise Exception('decimals_list and orderbook_accounts should have the same length') 366 367 all_bids_and_asks_accounts = [] 368 for o in orderbook_accounts: 369 all_bids_and_asks_accounts.append(o.bids) 370 all_bids_and_asks_accounts.append(o.asks) 371 372 373 all_slabs = await Orderbook.load_multiple_slabs(conn, all_bids_and_asks_accounts) 374 orderbooks = [] 375 376 for i, o in enumerate(orderbook_accounts): 377 orderbook = Orderbook( 378 o.orderbook, 379 all_slabs[i*2], 380 all_slabs[i*2 + 1], 381 o.bids,o.asks, 382 decimals_list[i] 383 ) 384 orderbooks.append(orderbook) 385 return orderbooks
Returns Orderbook objects from Orderbook Account objects, by fetching and parsing.
Args
- conn (AsyncClient): Solana AsyncClient object
- orderbook_accounts (list[OrderbookAccountsState]): List of orderbook account objects
- decimals_list (list[int]): List of decimal precision for each orderbook account state. Variable normally found in MarketState object
Raises
- Exception: Decimals list and orderbook accounts do not have the same length
Returns
list[Orderbook]: List of orderbook objects
389 @staticmethod 390 async def get_orderbooks_from_orderbook_accounts_multiple_markets( 391 conn: AsyncClient, 392 market_states: list[MarketState], 393 market_stores: list[MarketStoreState], 394 are_market_statuses_closed: list[bool], 395 ): 396 """ 397 Returns Orderbook objects from MarketState and MarketStoreStateobjects, 398 by fetching and parsing for multiple markets. 399 400 Use when fetching orderbooks for multiple markets 401 402 Args: 403 conn (AsyncClient): Solana AsyncClient object 404 market_states (list[MarketState]): List of MarketState objects 405 market_stores (list[MarketStoreState]): List of MarketStoreStateobjects 406 are_market_statuses_closed (list[bool]): Lists if each market is closed or not 407 408 Returns: 409 list[list[Orderbook]]: List of orderbooks for each market 410 """ 411 #Create list of accounts and the decimals for each account 412 orderbook_accounts = [] 413 decimals_list = [] 414 for index, market_store in enumerate(market_stores): 415 if(market_store is None): 416 continue 417 orderbook_accounts.extend(market_store.orderbook_accounts) 418 for i in range(len(market_store.orderbook_accounts)): 419 decimals_list.append(market_states[index].decimals) 420 421 #Load all orderbooks 422 all_orderbooks: list[Orderbook] = await AverMarket.get_orderbooks_from_orderbook_accounts(conn, orderbook_accounts, decimals_list) 423 orderbooks_market_list = [] 424 425 #Create a list for each market we received. The list contains all orderbooks for that market 426 for index, market_state in enumerate(market_states): 427 number_of_outcomes = market_state.number_of_outcomes 428 orderbooks = [] 429 #Market is closed 430 if(are_market_statuses_closed[index]): 431 orderbooks_market_list.append(None) 432 continue 433 #Binary markets only have 1 orderbook 434 if(number_of_outcomes == 2): 435 orderbooks.append(all_orderbooks.pop(0)) 436 else: 437 orderbooks = [] 438 for i in range(number_of_outcomes): 439 orderbooks.append(all_orderbooks.pop(0)) 440 orderbooks_market_list.append(orderbooks) 441 442 return orderbooks_market_list
Returns Orderbook objects from MarketState and MarketStoreStateobjects, by fetching and parsing for multiple markets.
Use when fetching orderbooks for multiple markets
Args
- conn (AsyncClient): Solana AsyncClient object
- market_states (list[MarketState]): List of MarketState objects
- market_stores (list[MarketStoreState]): List of MarketStoreStateobjects
- are_market_statuses_closed (list[bool]): Lists if each market is closed or not
Returns
list[list[Orderbook]]: List of orderbooks for each market
445 def make_sweep_fees_instruction(self): 446 """ 447 Creates instruction to sweeps fees and sends to relevant accounts 448 449 Returns TransactionInstruction object only. Does not send transaction. 450 451 Returns: 452 TransactionInstruction: TransactionInstruction object 453 """ 454 quote_token = self.aver_client.quote_token 455 third_party_vault_authority, bump = PublicKey.find_program_address( 456 [b"third-party-token-vault", bytes(quote_token)], AVER_PROGRAM_ID) 457 458 third_party_vault_token_account = get_associated_token_address( 459 third_party_vault_authority, quote_token) 460 461 aver_quote_token_account = get_associated_token_address( 462 AVER_MARKET_AUTHORITY, quote_token 463 ) 464 465 return self.aver_client.program.instruction["sweep_fees"]( 466 ctx=Context( 467 accounts={ 468 "market": self.market_pubkey, 469 "quote_vault": self.market_state.quote_vault, 470 "vault_authority": self.market_state.vault_authority, 471 "third_party_token_vault": third_party_vault_token_account, 472 "aver_quote_token_account": aver_quote_token_account, 473 "spl_token_program": TOKEN_PROGRAM_ID, 474 }, 475 signers=[], 476 ), 477 )
Creates instruction to sweeps fees and sends to relevant accounts
Returns TransactionInstruction object only. Does not send transaction.
Returns
TransactionInstruction: TransactionInstruction object
479 async def sweep_fees(self, fee_payer: Keypair = None, send_options: TxOpts = None,): 480 """ 481 Sweeps fees and sends to relevant accounts 482 483 Sends instructions on chain 484 485 Args: 486 fee_payer (Keypair): Pays transaction fees. Defaults to AverClient wallet 487 send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None. 488 489 Returns: 490 RPCResponse: Response 491 """ 492 if(fee_payer == None): 493 fee_payer = self.aver_client.owner 494 495 ix = self.make_sweep_fees_instruction() 496 497 return await sign_and_send_transaction_instructions( 498 self.aver_client, 499 [], 500 fee_payer, 501 [ix], 502 send_options 503 )
Sweeps fees and sends to relevant accounts
Sends instructions on chain
Args
- fee_payer (Keypair): Pays transaction fees. Defaults to AverClient wallet
- send_options (TxOpts, optional): Options to specify when broadcasting a transaction. Defaults to None.
Returns
RPCResponse: Response
505 def list_all_pubkeys(self): 506 """ 507 Returns all pubkeys used in AverMarket object 508 509 Returns: 510 list[PublicKey]: List of public keys 511 """ 512 list_of_pubkeys = [self.market_pubkey, self.market_state.quote_vault, self.market_state.oracle_feed] 513 if not self.is_market_status_closed: 514 for ob in self.orderbooks: 515 list_of_pubkeys += [ob.pubkey, ob.slab_asks_pubkey, ob.slab_asks_pubkey] 516 return list_of_pubkeys
Returns all pubkeys used in AverMarket object
Returns
list[PublicKey]: List of public keys
518 async def get_implied_market_status( 519 self 520 ) -> MarketStatus: 521 """ 522 Returns what we believe the market status ought to be (rather than what is stored in the market's state on-chain) 523 524 Note: As a fail safe, markets have specified times beyond which the market will react as if it is in another Status until it is formally cranked to that Status. 525 For example, if Solana were to have issues and it was not possible for the market's authority to crank it into In-Play status or to Cease Trading in time, 526 the market would use the System Time as a trigger to treat new requests as if it were in the later Status. 527 528 If Solana clock time is beyond TradingCeaseTime, market is TradingCeased 529 If Solana clock time is beyond InPlayStartTime but before TradingCeaseTime, market is ActiveInPlay 530 531 Returns: 532 MarketStatus: Implied market status 533 """ 534 solana_datetime = await self.aver_client.get_system_clock_datetime() 535 if solana_datetime.timestamp() > self.market_state.trading_cease_time: 536 return MarketStatus.TRADING_CEASED 537 if self.market_state.inplay_start_time is not None and solana_datetime.timestamp() > self.market_state.inplay_start_time : 538 return MarketStatus.ACTIVE_IN_PLAY 539 return self.market_state.market_status
Returns what we believe the market status ought to be (rather than what is stored in the market's state on-chain)
Note: As a fail safe, markets have specified times beyond which the market will react as if it is in another Status until it is formally cranked to that Status. For example, if Solana were to have issues and it was not possible for the market's authority to crank it into In-Play status or to Cease Trading in time, the market would use the System Time as a trigger to treat new requests as if it were in the later Status.
If Solana clock time is beyond TradingCeaseTime, market is TradingCeased If Solana clock time is beyond InPlayStartTime but before TradingCeaseTime, market is ActiveInPlay
Returns
MarketStatus: Implied market status
541 async def crank_market( 542 self, 543 outcome_idxs: list[int] = None, 544 reward_target: PublicKey = None, 545 payer: Keypair = None, 546 ): 547 """ 548 Refresh market before cranking 549 If no outcome_idx are passed, all outcomes are cranked if they meet the criteria to be cranked. 550 """ 551 if outcome_idxs == None: 552 # For binary markets, there is only one orderbook 553 outcome_idxs = [idx for idx in range( 554 1 if self.market_state.number_of_outcomes == 2 else self.market_state.number_of_outcomes)] 555 if self.market_state.number_of_outcomes == 2 and (0 in outcome_idxs or 1 in outcome_idxs): 556 outcome_idxs = [0] 557 if reward_target == None: 558 reward_target = self.aver_client.owner.public_key 559 if payer == None: 560 payer = self.aver_client.owner 561 562 # refreshed_market = await refresh_market(self.aver_client, self) 563 564 event_queues = [o.event_queue for o in self.market_store_state.orderbook_accounts] 565 loaded_event_queues = await load_all_event_queues( 566 self.aver_client.provider.connection, 567 event_queues 568 ) 569 570 sig = '' 571 for idx in outcome_idxs: 572 if loaded_event_queues[idx]["header"].count == 0: 573 continue 574 575 print(f'Cranking market {str(self.market_pubkey)} for outcome {idx} - {loaded_event_queues[idx]["header"].count} events left to crank') 576 if loaded_event_queues[idx]['header'].count > 0: 577 user_accounts = [] 578 for j, event in enumerate(loaded_event_queues[idx]['nodes']): 579 if type(event) == Fill: 580 user_accounts += [event.maker_user_market] 581 else: # Out 582 user_accounts += [event.user_market] 583 if j == MAX_ITERATIONS_FOR_CONSUME_EVENTS: 584 break 585 user_accounts = prepare_user_accounts_list(user_accounts) 586 events_to_crank = min( 587 loaded_event_queues[idx]['header'].count, MAX_ITERATIONS_FOR_CONSUME_EVENTS) 588 589 sig = await self.consume_events( 590 outcome_idx=idx, 591 max_iterations=events_to_crank, 592 user_accounts=user_accounts, 593 reward_target=reward_target, 594 payer=payer, 595 ) 596 597 return sig
Refresh market before cranking If no outcome_idx are passed, all outcomes are cranked if they meet the criteria to be cranked.
599 async def consume_events( 600 self, 601 outcome_idx: int, 602 user_accounts: list[PublicKey], 603 max_iterations: int = None, 604 reward_target: PublicKey = None, 605 payer: Keypair = None, 606 ): 607 """ 608 Consume events 609 610 Sends instructions on chain 611 612 Args: 613 outcome_idx (int): index of the outcome 614 user_accounts (list[PublicKey]): List of User Account public keys 615 max_iterations (int, optional): Depth of events to iterate through. Defaults to MAX_ITERATIONS_FOR_CONSUME_EVENTS. 616 reward_target (PublicKey, optional): Target for reward. Defaults to AverClient wallet. 617 payer (Keypair, optional): Fee payer. Defaults to AverClient wallet. 618 619 Returns: 620 Transaction Signature: TransactionSignature object 621 """ 622 if reward_target == None: 623 reward_target = self.aver_client.owner.public_key 624 if payer == None: 625 payer = self.aver_client.owner 626 if max_iterations > MAX_ITERATIONS_FOR_CONSUME_EVENTS or max_iterations == None: 627 max_iterations = MAX_ITERATIONS_FOR_CONSUME_EVENTS 628 629 user_accounts_unsorted = [AccountMeta( 630 pk, False, True) for pk in user_accounts] 631 632 remaining_accounts = sorted(user_accounts_unsorted, key=lambda account: bytes(account.pubkey)) 633 634 return await self.aver_client.program.rpc["consume_events"]( 635 max_iterations, 636 outcome_idx, 637 ctx=Context( 638 accounts={ 639 "market": self.market_pubkey, 640 "market_store": self.market_state.market_store, 641 "orderbook": self.market_store_state.orderbook_accounts[outcome_idx].orderbook, 642 "event_queue": self.market_store_state.orderbook_accounts[outcome_idx].event_queue, 643 "reward_target": reward_target, 644 }, 645 remaining_accounts=remaining_accounts, 646 ), 647 )
Consume events
Sends instructions on chain
Args
- outcome_idx (int): index of the outcome
- user_accounts (list[PublicKey]): List of User Account public keys
- max_iterations (int, optional): Depth of events to iterate through. Defaults to MAX_ITERATIONS_FOR_CONSUME_EVENTS.
- reward_target (PublicKey, optional): Target for reward. Defaults to AverClient wallet.
- payer (Keypair, optional): Fee payer. Defaults to AverClient wallet.
Returns
Transaction Signature: TransactionSignature object