Balancer Simulations
  • Getting Started
  • Understanding Balancer AMMs
    • Balancer Basics
    • Use cases
  • Simulation Packages
    • Model Overview
    • Pool Functions
    • Pool Exploration
  • Code & Instructions
    • Dynamic weights changing
    • Arbitrage agent
    • Balancer: the Python edition
      • ActionDecoder
      • pool_state_updates.py
      • partial_state_update_blocks.py
      • Spot Price calculation
      • balancer_constants.py
      • balancer_math.py
      • balancer_pool.py
      • Verification & Limitations
    • Naming Convention
    • Adding On-chain Transaction Data and USD Prices
    • Modeling Time
Powered by GitBook
On this page

Was this helpful?

  1. Code & Instructions
  2. Balancer: the Python edition

ActionDecoder

PreviousBalancer: the Python editionNextpool_state_updates.py

Last updated 4 years ago

Was this helpful?

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 actions b) Price updates

  1. Pool actions call pool related policies, like this swap:

{
		"timestamp": "2021-01-13T18:46:38+00:00",
		"tx_hash": "0x8819714eaa6ce5310586e33b4c7e1dfbc947318330984d1d110a369c9de6da2c",
		"block_number": "11648399",
		"swap_fee": "0.0025",
		"denorms": [
			{
				"token_address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
				"token_symbol": "DAI",
				"denorm": "10.000000000"
			},
			{
				"token_address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
				"token_symbol": "WETH",
				"denorm": "40.000000000"
			}
		],
		"contract_call": [
			{
				"type": "swapExactAmountIn",
				"inputs": {
					"tokenIn_symbol": "DAI",
					"tokenIn": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
					"tokenAmountIn": "236.906023596408551105",
					"tokenOut_symbol": "WETH",
					"tokenOut": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
					"minAmountOut": "0",
					"maxPrice": "115792089237316195423570985008687907853269984665640564039457.584007913129639935"
				}
			}
		],
		"action": {
			"type": "swap",
			"token_in": {
				"amount": "236.906023596408551105",
				"symbol": "DAI"
			},
			"token_out": {
				"amount": "0.21445704314241238",
				"symbol": "WETH"
			}
		}
	},

The structure in general is:

  • timestamp: When the action happened

  • 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

{
		"timestamp": "2021-01-13T19:00:00+00:00",
		"fiat_currency": "USD",
		"action": {
			"type": "external_price_update",
			"tokens": {
				"DAI": 1.0010362320777462,
				"WETH": 1068.3949468116527
			}
		}
	},

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']}
            

denorms: Pool tokens' at this stage

Adding On-chain Transaction Data
denormalized weight