-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from multiversx/mxpy-porting
Mxpy porting
- Loading branch information
Showing
101 changed files
with
15,231 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/wallets/* | ||
/venv* | ||
/.idea | ||
/.vscode | ||
/logs/* | ||
/tools/notebooks/env.py | ||
.venv | ||
__pycache__ | ||
/deploy/*/tokens.json | ||
/deploy/*/deployed_*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,59 @@ | ||
# mx-sdk-py-exchange | ||
Python utility toolkit for xExchange interactions | ||
Python utility toolkit for xExchange interactions. | ||
|
||
Features include: | ||
- Configurable DEX SC setup deployment | ||
- save/load setup | ||
- additive deployment to existing setup | ||
- Interaction with DEX SCs via exposed endpoints | ||
- Data reading from DEX SCs via exposed views | ||
- DEX SC operation trackers | ||
- Attributes decoding tool | ||
- Setup updater tool | ||
|
||
### Disclaimer | ||
This is currently a work in progress and should be treated as such. | ||
Modules are under migration process to mxpy, need a lot of cleanup & refactors and may not be functional/used anylonger. | ||
|
||
### Virtual environment | ||
|
||
Create a virtual environment and install the dependencies: | ||
|
||
``` | ||
python3 -m venv ./.venv | ||
source ./.venv/bin/activate | ||
pip install -r ./requirements.txt --upgrade | ||
``` | ||
|
||
### Operation | ||
Start by initializing the environment: | ||
``` | ||
export PYTHONPATH=. | ||
``` | ||
|
||
#### Config | ||
To configure the exchange setup and operation, config.py has to be edited accordingly. | ||
In the config.py file you should configure the following: | ||
- used network | ||
- PEM accounts for exchange operations | ||
- DEX deploy configuration | ||
- DEX contract binaries paths | ||
|
||
#### Deploy | ||
The dex_deploy script is used to deploy a configured exchange setup. | ||
An exchange setup consists of several elements usually gathered in a dedicated directory: | ||
- deploy_structure.json - defines the exchange setup structure containing definitions for each contract type | ||
- deployed_*.json - contains already deployed tokens/contracts for this specific exchange setup | ||
|
||
To deploy a specific exchange setup, configure the desired exchange setup directory in config.py then run: | ||
``` | ||
python3 deploy/dex_deploy.py --deploy-contracts=clean --deploy-tokens=clean | ||
``` | ||
The flags `--deploy-contracts` and `--deploy-tokens` can be set to `clean` or `config` to either deploy a clean setup | ||
(ignoring the already deployed contracts) or add on top of an existing one (considering the already deployed contracts). | ||
|
||
#### Scenarios | ||
To run a defined scenario, just run the desired scenario script located in /scenarios directory, such as: | ||
``` | ||
python3 scenarios/stress_create_positions.py | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
from pathlib import Path | ||
|
||
HOME = Path().home() | ||
DEFAULT_WORKSPACE = Path(__file__).parent | ||
|
||
# ------------ For normal operation, modify below ------------ # | ||
# Used net | ||
DEFAULT_PROXY = "https://devnet-gateway.multiversx.com" # Proxy to be used for ALL operations | ||
DEFAULT_API = "https://devnet-api.multiversx.com" # API to be used for ALL operations | ||
HISTORY_PROXY = "" # Proxy to be used for history operations; not used for the moment | ||
# TODO: try to override the default issue token price with testnet definition to tidy code up | ||
DEFAULT_ISSUE_TOKEN_PRICE = 50000000000000000 # 0.05 EGLD - change only if different setup on nets | ||
|
||
# Operation wallets | ||
DEFAULT_ACCOUNTS = DEFAULT_WORKSPACE.absolute() / "wallets" / "C10.pem" # Accounts to be used for user operations | ||
DEFAULT_OWNER = DEFAULT_WORKSPACE.absolute() / "wallets" / "C1.pem" # DEX owner address | ||
DEFAULT_ADMIN = DEFAULT_WORKSPACE.absolute() / "wallets" / "C1_1.pem" # DEX admin address | ||
|
||
# Used DEX deploy configuration | ||
DEFAULT_CONFIG_SAVE_PATH = DEFAULT_WORKSPACE.absolute() / "deploy" / "configs-devnet" # Deploy configuration folder | ||
DEPLOY_STRUCTURE_JSON = DEFAULT_CONFIG_SAVE_PATH / "deploy_structure.json" # Deploy structure - change only if needed | ||
|
||
# DEX contract bytecode paths | ||
LOCKED_ASSET_FACTORY_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "factory.wasm" | ||
SIMPLE_LOCK_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "locked-asset" / "simple-lock" / "output" / "simple-lock.wasm" | ||
ROUTER_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "router" / "output" / "router.wasm" | ||
PROXY_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "locked-asset" / "proxy_dex" / "output" / "proxy_dex.wasm" | ||
PROXY_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "proxy_dex.wasm" | ||
PAIR_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "pair" / "output" / "pair.wasm" | ||
FARM_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "farm" / "output" / "farm.wasm" | ||
FARM_LOCKED_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "farm_with_lock" / "output" / "farm_with_lock.wasm" | ||
FARM_COMMUNITY_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "farm_with_community_rewards" / "output" / "farm_with_community_rewards.wasm" | ||
PRICE_DISCOVERY_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "dex" / "price-discovery" / "output" / "price-discovery.wasm" | ||
STAKING_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "farm-staking" / "farm-staking" / "output" / "farm-staking.wasm" | ||
STAKING_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "farm-staking.wasm" | ||
STAKING_PROXY_BYTECODE_PATH = Path().home() / "dev" / "dex" / "sc-dex-rs" / "farm-staking" / "farm-staking-proxy" / "output" / "farm-staking-proxy.wasm" | ||
STAKING_PROXY_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "farm-staking-proxy.wasm" | ||
SIMPLE_LOCK_ENERGY_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "energy-factory.wasm" | ||
UNSTAKER_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "token-unstake.wasm" | ||
FEES_COLLECTOR_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "fees-collector.wasm" | ||
ROUTER_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "router.wasm" | ||
PAIR_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "pair.wasm" | ||
FARM_DEPLOYER_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "proxy-deployer.wasm" | ||
FARM_V2_BYTECODE_PATH = Path().home() / "projects" / "dex" / "dex-v2" / "dexv2-rs" / "farm-with-locked-rewards.wasm" | ||
|
||
|
||
# ------------ Generic configuration below; Modify only in case of framework changes ------------ # | ||
TOKENS_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u" | ||
ZERO_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu" | ||
|
||
DEFAULT_GAS_BASE_LIMIT_ISSUE = 60000000 | ||
DEFAULT_TOKEN_PREFIX = "TDEX" # limit yourself to max 6 chars to allow automatic ticker build | ||
DEFAULT_TOKEN_SUPPLY_EXP = 27 # supply to be minted in exponents of 10 | ||
DEFAULT_TOKEN_DECIMALS = 18 # decimals on minted tokens in exponents of 10 | ||
DEFAULT_MINT_VALUE = 1 # EGLD # TODO: don't go sub-unitary cause headaches occur. just don't be cheap for now... | ||
|
||
CROSS_SHARD_DELAY = 60 | ||
INTRA_SHARD_DELAY = 10 | ||
|
||
LOCKED_ASSETS = "locked_assets" | ||
PROXIES = "proxies" | ||
PROXIES_V2 = "proxies_v2" | ||
SIMPLE_LOCKS = "simple_locks" | ||
SIMPLE_LOCKS_ENERGY = "simple_locks_energy" | ||
UNSTAKERS = "unstakers" | ||
ROUTER = "router" | ||
ROUTER_V2 = "router_v2" | ||
PAIRS = "pairs" | ||
PAIRS_V2 = "pairs_v2" | ||
PROXY_DEPLOYERS = "proxy_deployers" | ||
FARMS_V2 = "farms_boosted" | ||
FARMS_COMMUNITY = "farms_community" | ||
FARMS_UNLOCKED = "farms_unlocked" | ||
FARMS_LOCKED = "farms_locked" | ||
PRICE_DISCOVERIES = "price_discoveries" | ||
STAKINGS = "stakings" | ||
STAKINGS_V2 = "stakings_v2" | ||
METASTAKINGS = "metastakings" | ||
METASTAKINGS_V2 = "metastakings_v2" | ||
FEES_COLLECTORS = "fees_collectors" | ||
|
||
|
||
def get_default_tokens_file(): | ||
return DEFAULT_CONFIG_SAVE_PATH / "tokens.json" | ||
|
||
|
||
def get_default_log_file(): | ||
return DEFAULT_WORKSPACE / "logs" / "trace.log" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import random | ||
from datetime import datetime | ||
|
||
import config | ||
from contracts.farm_contract import FarmContract | ||
from contracts.metastaking_contract import MetaStakingContract | ||
from contracts.pair_contract import PairContract | ||
from deploy.dex_structure import DeployStructure | ||
from utils.results_logger import ResultsLogger | ||
from utils.utils_tx import NetworkProviders | ||
from trackers.farm_economics_tracking import FarmEconomics, FarmAccountEconomics | ||
from trackers.pair_economics_tracking import PairEconomics | ||
from trackers.staking_economics_tracking import StakingEconomics | ||
from trackers.metastaking_economics_tracking import MetastakingEconomics | ||
from trackers.concrete_observer import Observable | ||
from utils.utils_chain import Account, BunchOfAccounts | ||
|
||
|
||
class Context: | ||
def __init__(self): | ||
|
||
self.deploy_structure = DeployStructure() | ||
self.deployer_account = Account(pem_file=config.DEFAULT_OWNER) | ||
self.accounts = BunchOfAccounts.load_accounts_from_files([config.DEFAULT_ACCOUNTS]) | ||
self.nonces_file = config.DEFAULT_WORKSPACE / "_nonces.json" | ||
self.debug_level = 1 | ||
|
||
self.network_provider = NetworkProviders(config.DEFAULT_API, config.DEFAULT_PROXY) | ||
|
||
# logger | ||
self.start_time = datetime.now() | ||
self.results_logger = ResultsLogger(f"{self.start_time.day}_{self.start_time.hour}_{self.start_time.minute}_event_results.json") | ||
|
||
self.add_liquidity_max_amount = 0.1 | ||
self.remove_liquidity_max_amount = 0.5 | ||
self.numEvents = 100 # sys.maxsize | ||
self.pair_slippage = 0.05 | ||
self.swap_min_tokens_to_spend = 0 | ||
self.swap_max_tokens_to_spend = 0.8 | ||
|
||
self.enter_farm_max_amount = 0.2 | ||
self.exit_farm_max_amount = 0.5 | ||
|
||
self.enter_metastake_max_amount = 0.1 | ||
self.exit_metastake_max_amount = 0.3 | ||
|
||
# BEGIN DEPLOY | ||
self.deployer_account.sync_nonce(self.network_provider.proxy) | ||
|
||
# TOKENS HANDLING | ||
self.deploy_structure.deploy_tokens(self.deployer_account, self.network_provider, False) | ||
|
||
# configure contracts and deploy them | ||
# DEPLOY CONTRACTS | ||
self.deploy_structure.deploy_structure(self.deployer_account, self.network_provider, False) | ||
|
||
# CONTRACTS START | ||
self.deploy_structure.start_deployed_contracts(self.deployer_account, self.network_provider, False) | ||
|
||
# deploy closing | ||
self.deploy_structure.print_deployed_contracts() | ||
|
||
self.observable = Observable() | ||
# self.init_observers() # call should be parameterized so that observers can be disabled programmatically | ||
|
||
def init_observers(self): | ||
|
||
farm_unlocked_contracts = self.deploy_structure.contracts[config.FARMS_UNLOCKED].deployed_contracts | ||
for contract in farm_unlocked_contracts: | ||
contract_dict = contract.get_config_dict() | ||
observer = FarmEconomics(contract_dict['address'], contract_dict['version'], self.network_provider) | ||
self.observable.subscribe(observer) | ||
|
||
farm_locked_contracts = self.deploy_structure.contracts[config.FARMS_LOCKED].deployed_contracts | ||
for contract in farm_locked_contracts: | ||
contract_dict = contract.get_config_dict() | ||
observer = FarmEconomics(contract_dict['address'], contract_dict['version'], self.network_provider) | ||
self.observable.subscribe(observer) | ||
|
||
for acc in self.accounts.get_all(): | ||
account_observer = FarmAccountEconomics(acc.address, self.network_provider) | ||
self.observable.subscribe(account_observer) | ||
|
||
pair_contracts = self.deploy_structure.contracts[config.PAIRS].deployed_contracts | ||
for contract in pair_contracts: | ||
contract_dict = contract.get_config_dict() | ||
observer = PairEconomics(contract_dict['address'], contract.firstToken, contract.secondToken, self.network_provider) | ||
self.observable.subscribe(observer) | ||
|
||
staking_contracts = self.deploy_structure.contracts[config.STAKINGS].deployed_contracts | ||
for contract in staking_contracts: | ||
contract_dict = contract.get_config_dict() | ||
observer = StakingEconomics(contract_dict['address'], self.network_provider) | ||
self.observable.subscribe(observer) | ||
|
||
metastaking_contracts = self.deploy_structure.contracts[config.METASTAKINGS].deployed_contracts | ||
for contract in metastaking_contracts: | ||
contract_dict = contract.get_config_dict() | ||
farm_contract = self.get_farm_contract_by_address(contract_dict['farm_address']) | ||
pair_contract = self.get_pair_contract_by_address(contract_dict['lp_address']) | ||
observer = MetastakingEconomics(contract_dict['address'], contract_dict['stake_address'], | ||
farm_contract, pair_contract, self.network_provider) | ||
self.observable.subscribe(observer) | ||
|
||
def get_slippaged_below_value(self, value: int): | ||
return value - int(value * self.pair_slippage) | ||
|
||
def get_slippaged_above_value(self, value: int): | ||
return value + int(value * self.pair_slippage) | ||
|
||
def set_swap_spend_limits(self, swap_min_spend, swap_max_spend): | ||
self.swap_min_tokens_to_spend = swap_min_spend | ||
self.swap_max_tokens_to_spend = swap_max_spend | ||
|
||
def get_router_v2_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.ROUTER_V2, index) | ||
|
||
def get_simple_lock_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.SIMPLE_LOCKS, index) | ||
|
||
def get_pair_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.PAIRS, index) | ||
|
||
def get_pair_v2_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.PAIRS_V2, index) | ||
|
||
def get_fee_collector_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.FEES_COLLECTORS, index) | ||
|
||
def get_unlocked_farm_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.FARMS_UNLOCKED, index) | ||
|
||
def get_locked_farm_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.FARMS_LOCKED, index) | ||
|
||
def get_staking_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.STAKINGS, index) | ||
|
||
def get_metastaking_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.METASTAKINGS, index) | ||
|
||
def get_price_discovery_contract(self, index: int): | ||
return self.deploy_structure.get_deployed_contract_by_index(config.PRICE_DISCOVERIES, index) | ||
|
||
def get_contracts(self, contract_label: str): | ||
return self.deploy_structure.get_deployed_contracts(contract_label) | ||
|
||
def get_farm_contract_by_address(self, address: str) -> FarmContract: | ||
contract = self.deploy_structure.get_deployed_contract_by_address(config.FARMS_LOCKED, address) | ||
if contract is None: | ||
contract = self.deploy_structure.get_deployed_contract_by_address(config.FARMS_UNLOCKED, address) | ||
|
||
return contract | ||
|
||
def get_random_farm_contract(self): | ||
return random.choice([random.choice(self.deploy_structure.get_deployed_contracts(config.FARMS_LOCKED)), | ||
random.choice(self.deploy_structure.get_deployed_contracts(config.FARMS_UNLOCKED))]) | ||
|
||
def get_pair_contract_by_address(self, address: str) -> PairContract: | ||
return self.deploy_structure.get_deployed_contract_by_address(config.PAIRS, address) | ||
|
||
def get_random_pair_contract(self): | ||
return random.choice(self.deploy_structure.get_deployed_contracts(config.PAIRS)) | ||
|
||
def get_random_user_account(self): | ||
account_list = self.accounts.get_all() | ||
return random.choice(account_list) | ||
|
||
def get_random_price_discovery_contract(self): | ||
return random.choice(self.deploy_structure.get_deployed_contracts(config.PRICE_DISCOVERIES)) | ||
|
||
def get_random_metastaking_contract(self) -> MetaStakingContract: | ||
return random.choice(self.deploy_structure.get_deployed_contracts(config.METASTAKINGS)) | ||
|
||
def get_contract_index(self, contract_label: str, contract): | ||
return self.deploy_structure.get_deployed_contracts(contract_label).index(contract) |
Oops, something went wrong.