In order to reproduce user behaviour in existing pools, data is pulled from several sources and combined into a temporal sequence of actions, in a json file (see )
ActionDecoder reads the actions from the json every simulation step and converts the actions into pool "opcodes", each represented by a class in pool_method_entities.py . These are passed to the pool state update function s_update_pool.py
First action (pool_creation)is skipped, since the results are already in the initial state variables
There are two main types of actions:
a) Pool actionsb) Price updates
Pool actions call pool related policies, like this swap:
tx_hash: Transaction hash that resulted in the action. May not be unique, for example a transaction generated by an aggregator like 1inch can result in several swaps against the same pool
block_number: Ethereum block number where the transaction happened
swap_fee: Pool swap fee setting at this stage.
contract_call: Result of parsing LOG_CALL anonymous event, that has the exact method name and parameters of the smart contract method
action: Outcome of the action in this pool, combining LOG_CALL, LOG_JOIN and LOG_EXIT events getting data from BigQuery ETL project
2. External price updates trigger state updates to the USD price of tokens
The decoder has a selector to execute the relevant pool method or price update. In this version, only some pool events are used according to action type.
In every timestep we update the date time and action type.
@staticmethod
def p_action_decoder(params, step, history, current_state):
if ActionDecoder.action_df is None:
raise Exception('call ActionDecoder.load_actions(path_to_action.json) first')
'''
In this simplified model of Balancer, we have not modeled user behavior. Instead, we map events to actions.
'''
decoding_type = get_param(params, 'decoding_type')
ActionDecoder.decoding_type = ActionDecodingType(decoding_type)
idx = current_state['timestep'] + 1
if ActionDecoder.decoding_type == ActionDecodingType.simplified:
return ActionDecoder.p_simplified_action_decoder(idx, params, step, history, current_state)
elif ActionDecoder.decoding_type == ActionDecodingType.contract_call:
return ActionDecoder.p_contract_call_action_decoder(idx, params, step, history, current_state)
elif ActionDecoder.decoding_type == ActionDecodingType.replay_output:
return ActionDecoder.p_plot_output_action_decoder(idx, params, step, history, current_state)
else:
raise Exception(f'unknown decoding type {decoding_type}')
...
@staticmethod
def p_simplified_action_decoder(idx, params, step, history, current_state):
action = ActionDecoder.action_df['action'][idx]
timestamp = ActionDecoder.action_df['timestamp'][idx]
tx_hash = ActionDecoder.action_df['tx_hash'][idx]
if action['type'] == 'swap':
pool_method_params = PoolMethodParamsDecoder.swap_exact_amount_in_simplified(action)
elif action['type'] == 'join':
pool_method_params = PoolMethodParamsDecoder.join_pool_simplified(action)
elif action['type'] == 'join_swap':
pool_method_params = PoolMethodParamsDecoder.join_swap_extern_amount_in_simplified(action)
elif action['type'] == 'exit_swap':
pool_method_params = PoolMethodParamsDecoder.exit_swap_pool_amount_in_simplified(action)
elif action['type'] == 'exit':
pool_method_params = PoolMethodParamsDecoder.exit_pool_simplified(action)
elif action['type'] == 'external_price_update':
return {'external_price_update': action['tokens'], 'change_datetime_update': timestamp, 'action_type': action['type'],
'pool_update': None}
else:
raise Exception("Action type {} unimplemented".format(action['type']))
return {'pool_update': pool_method_params, 'change_datetime_update': timestamp, 'action_type': action['type']}