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 Adding On-chain Transaction Data)
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
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']}