Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate transaction commands added for farms #46

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions contracts/dex_proxy_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ def __init__(self, farmContract: FarmContract,


class DexProxyExitFarmEvent:
def __init__(self, farmContract: FarmContract, token: str, nonce: int, amount):
def __init__(self, farmContract: FarmContract, token: str, nonce: int, amount, original_caller: str = ""):
self.farmContract = farmContract
self.token = token
self.nonce = nonce
self.amount = amount
self.original_caller = original_caller


class DexProxyClaimRewardsEvent:
Expand Down Expand Up @@ -181,8 +182,7 @@ def exit_farm_proxy(self, user: Account, proxy: ProxyNetworkProvider, event: Dex

sc_args = [
tokens,
Address(event.farmContract.address),
event.amount
Address(event.farmContract.address)
]
return multi_esdt_endpoint_call(function_purpose, proxy, gas_limit, user, Address(self.address),
"exitFarmProxy", sc_args)
Expand Down
9 changes: 2 additions & 7 deletions contracts/farm_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,19 @@ def has_proxy(self) -> bool:
return True
return False

def enterFarm(self, network_provider: NetworkProviders, user: Account, event: EnterFarmEvent, lock: int = 0, initial: bool = False) -> str:
def enterFarm(self, network_provider: NetworkProviders, user: Account, event: EnterFarmEvent) -> str:
# TODO: remove initial parameter by using the event data
function_purpose = "enter farm"
logger.info(function_purpose)
logger.debug(f"Account: {user.address}")

enterFarmFn = "enterFarm"
if lock == 1:
enterFarmFn = "enterFarmAndLockRewards"
elif lock == 0:
enterFarmFn = "enterFarm"

logger.info(f"Calling {enterFarmFn} endpoint...")

gas_limit = 50000000

tokens = [ESDTToken(event.farming_tk, event.farming_tk_nonce, event.farming_tk_amount)]
if not initial:
if event.farm_tk_amount > 0:
tokens.append(ESDTToken(event.farm_tk, event.farm_tk_nonce, event.farm_tk_amount))

sc_args = [tokens]
Expand Down
11 changes: 4 additions & 7 deletions events/event_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,7 @@ def generate_random_swap_fixed_output(context: Context):
generate_swap_fixed_output(context, userAccount, pairContract)


def generateEnterFarmEvent(context: Context, userAccount: Account, farmContract: FarmContract, lockRewards: int = 0):
"""lockRewards: -1 - random; 0 - unlocked rewards; 1 - locked rewards;"""
def generateEnterFarmEvent(context: Context, userAccount: Account, farmContract: FarmContract):
logger.info(f"Attempt generateEnterFarmEvent for {userAccount.address.bech32()} on {farmContract.address}")
tx_hash = ""
try:
Expand All @@ -248,8 +247,6 @@ def generateEnterFarmEvent(context: Context, userAccount: Account, farmContract:
logger.warning(f"SKIPPED: No {farming_token} found for {userAccount.address.bech32()}!")
return

initial = True if farmTkNonce == 0 else False

# set correct token balance in case it has been changed since the init of observers
set_token_balance_event = SetTokenBalanceEvent(farming_token, farmingTkAmount, farmingTkNonce)
context.observable.set_event(None, userAccount, set_token_balance_event, '')
Expand All @@ -265,7 +262,7 @@ def generateEnterFarmEvent(context: Context, userAccount: Account, farmContract:
event_log.set_generic_event_data(event, userAccount.address.bech32(), farmContract)
event_log.set_pre_event_data(context.network_provider.proxy)

tx_hash = farmContract.enterFarm(context.network_provider, userAccount, event, lockRewards, initial)
tx_hash = farmContract.enterFarm(context.network_provider, userAccount, event)
context.observable.set_event(farmContract, userAccount, event, tx_hash)

# post-event logging
Expand Down Expand Up @@ -742,7 +739,7 @@ def generateRemoveLiquidityProxyEvent(context: Context):
context.dexProxyContract.removeLiquidityProxy(context, userAccount, event)


def generateEnterFarmProxyEvent(context: Context, user_account: Account, farm_contract: FarmContract, lock_rewards: int = 0):
def generateEnterFarmProxyEvent(context: Context, user_account: Account, farm_contract: FarmContract):

try:
farm_token = farm_contract.proxyContract.farm_token
Expand All @@ -764,7 +761,7 @@ def generateEnterFarmProxyEvent(context: Context, user_account: Account, farm_co
farm_contract, farming_token, farming_tk_nonce, farming_tk_amount, farm_token, farm_tk_nonce, farm_tk_amount
)

context.dexProxyContract.enterFarmProxy(context, user_account, event, lock_rewards, initial_enter_farm)
context.dexProxyContract.enterFarmProxy(context, user_account, event, initial_enter_farm)

except Exception as ex:
logger.error("Exception encountered:", ex)
Expand Down
3 changes: 1 addition & 2 deletions tools/notebooks/boosted-farm.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,7 @@
"event = EnterFarmEvent(farm_contract.farmingToken, 0, farming_tk_balance // 10,\n",
" farm_contract.farmToken, farm_tk_nonce, farm_tk_balance)\n",
"\n",
"initial_enter: bool = False if farm_tk_nonce else True\n",
"tx_hash = farm_contract.enterFarm(context.network_provider, user_account, event, initial=initial_enter)"
"tx_hash = farm_contract.enterFarm(context.network_provider, user_account, event)"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion tools/notebooks/boosted-staking.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@
"metadata": {},
"outputs": [],
"source": [
"event = EnterFarmEvent(staking_contract.farming_token, 0, 1000000000000000000, \"\", 0, 0)\n",
"event = EnterFarmEvent(staking_contract.farming_token, 0, 1000000000000000000, \"\", 0, 0, False, False)\n",
"txhash = staking_contract.stake_farm(context.network_provider, depositer, event, True)"
]
},
Expand Down
5 changes: 4 additions & 1 deletion tools/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ def fetch_all_contracts_states(prefix: str):
farm_v13_addresses = farm_runner.get_all_farm_v13_addresses()
fetch_contracts_states(prefix, network_providers, farm_v13_addresses, farm_runner.FARMSV13_LABEL)


# get farm v2 states
farm_v2_addresses = farm_runner.get_all_farm_v2_addresses()
fetch_contracts_states(prefix, network_providers, farm_v2_addresses, farm_runner.FARMSV2_LABEL)

if __name__ == '__main__':
main(sys.argv[1:])
177 changes: 169 additions & 8 deletions tools/runners/farm_runner.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
from argparse import ArgumentParser
from concurrent.futures import ThreadPoolExecutor
import json
import os
from time import sleep
from typing import Any

from multiversx_sdk import Address
from events.event_generators import get_lp_from_metastake_token_attributes
from multiversx_sdk import Address, ContractCallBuilder, \
DefaultTransactionBuildersConfiguration
from config import GRAPHQL
from context import Context
from contracts.contract_identities import FarmContractVersion
from contracts.farm_contract import FarmContract
from contracts.simple_lock_contract import SimpleLockContract
from events.farm_events import EnterFarmEvent, ExitFarmEvent
from tools.common import API, OUTPUT_FOLDER, OUTPUT_PAUSE_STATES, \
PROXY, fetch_and_save_contracts, fetch_new_and_compare_contract_states, \
get_owner, get_saved_contract_addresses, get_user_continue, run_graphql_query, fetch_contracts_states
from tools.runners.common_runner import add_upgrade_all_command, add_upgrade_command
from utils.contract_data_fetchers import FarmContractDataFetcher
get_owner, get_saved_contract_addresses, get_user_continue, rule_of_three, run_graphql_query, fetch_contracts_states
from tools.runners import metastaking_runner
from tools.runners.common_runner import add_generate_transaction_command, add_upgrade_all_command, add_upgrade_command, fund_shadowfork_accounts, get_acounts_with_token, get_default_signature, read_accounts_from_json, sync_account_nonce
from tools.runners.metastaking_runner import generate_unstake_farm_tokens_transaction
from utils.contract_data_fetchers import FarmContractDataFetcher, SimpleLockContractDataFetcher
from utils.contract_retrievers import retrieve_farm_by_address
from utils.utils_tx import NetworkProviders
from utils.utils_chain import Account, WrapperAddress, base64_to_hex, get_all_token_nonces_details_for_account, hex_to_string
from utils.utils_generic import split_to_chunks
from utils.utils_tx import ESDTToken, NetworkProviders
import config


FARMSV13_LABEL = "farmsv13"
FARMSV12_LABEL = "farmsv12"
FARMSV2_LABEL = "farmsv2"
Expand Down Expand Up @@ -46,8 +56,13 @@ def setup_parser(subparsers: ArgumentParser) -> ArgumentParser:
command_parser = contract_group.add_parser('resume-all', help='resume all contracts command')
command_parser.set_defaults(func=resume_farm_contracts)

return group_parser
fgandila marked this conversation as resolved.
Show resolved Hide resolved

transactions_parser = subgroup_parser.add_parser('generate-transactions', help='farms transactions commands')

transactions_group = transactions_parser.add_subparsers()
add_generate_transaction_command(transactions_group, generate_unstake_farm_tokens_transaction, 'unstakeFarmTokens', 'exit farm tokens command')
add_generate_transaction_command(transactions_group, generate_stake_farm_tokens_transaction, 'stakeFarmTokens', 'enter farm tokens command')
add_generate_transaction_command(transactions_group, generate_exit_farm_locked_transaction, 'exitFarmLocked', 'exit simple lock command')

def fetch_and_save_farms_from_chain():
"""Fetch and save farms from chain"""
Expand Down Expand Up @@ -340,7 +355,7 @@ def upgrade_farmv2_contract(args: Any):
return

tx_hash = contract.contract_upgrade(dex_owner, network_providers.proxy,
config.FARM_V2_BYTECODE_PATH,
config.FARM_V3_BYTECODE_PATH,
[], True)

if not network_providers.check_complex_tx_status(tx_hash, f"upgrade farm v2 contract: {farm_address}"):
Expand Down Expand Up @@ -467,7 +482,153 @@ def remove_penalty_farms():
return

count += 1
def generate_unstake_farm_tokens_transaction(args: Any):
context = Context()
farm_contracts = context.get_contracts(config.FARMS_V2)

"""Generate unstake farm tokens transaction"""
farm_address = args.address
exported_accounts_path = args.accounts_export

if not exported_accounts_path:
print("Missing required arguments!")
return

if not farm_address and not args.all:
print("Missing required arguments!")
return

default_account = Account(None, config.DEFAULT_OWNER)
network_providers = NetworkProviders(API, PROXY)
chain_id = network_providers.proxy.get_network_config().chain_id
config_tx = DefaultTransactionBuildersConfiguration(chain_id=chain_id)
signature = get_default_signature()
proxy = network_providers.proxy
default_account.sync_nonce(proxy)

exported_accounts = read_accounts_from_json(exported_accounts_path)
fund_shadowfork_accounts(exported_accounts)
sleep(30)

farm_contract = FarmContract.load_contract_by_address(farm_address, FarmContractVersion.V2Boosted)
accounts_with_token = get_acounts_with_token(exported_accounts, farm_contract.farmToken)

transactions = []
accounts_index = 1
with ThreadPoolExecutor(max_workers=500) as executor:
exported_accounts = list(executor.map(sync_account_nonce, exported_accounts))

for account_with_token in accounts_with_token:
account = Account(account_with_token.address, config.DEFAULT_OWNER)
account.address = WrapperAddress.from_bech32(account_with_token.address)
account.sync_nonce(proxy)
tokens = [token for token in account_with_token.account_tokens_supply if token.token_name == farm_contract.farmToken ]
for token in tokens:
event = ExitFarmEvent(token.token_name, int(token.supply), int(token.token_nonce_hex, 16), '')
payment_tokens = [ESDTToken(token.token_name, int(token.token_nonce_hex, 16), int(token.supply)).to_token_payment()]

if not account.address.is_smart_contract():
builder = ContractCallBuilder(
config=config_tx,
contract=Address.new_from_bech32(farm_address),
function_name="exitFarm",
caller=account.address,
call_arguments=[1, 1, event.amount],
value=0,
gas_limit=75000000,
nonce=account.nonce,
esdt_transfers=payment_tokens
)
tx = builder.build()
tx.signature = signature

transactions.append(tx)
account.nonce += 1

index = exported_accounts.index(account_with_token)
exported_accounts[index].nonce = account.nonce
accounts_index += 1

transactions_chunks = split_to_chunks(transactions, 100)
for chunk in transactions_chunks:
network_providers.proxy.send_transactions(chunk)

def generate_stake_farm_tokens_transaction(args: Any):
"""Generate unstake farm tokens transaction"""

farm_address = args.address
exported_accounts_path = args.accounts_export

network_providers = NetworkProviders(API, PROXY)
proxy = network_providers.proxy

if not farm_address or not exported_accounts_path:
print("Missing required arguments!")
return

print(f"Generate stake farm tokens transaction for metastaking contract {farm_address}")

network_providers = NetworkProviders(API, PROXY)
farm_contract = FarmContract.load_contract_by_address(farm_address, FarmContractVersion.V2Boosted)

exported_accounts = read_accounts_from_json(exported_accounts_path)
accounts_with_token = get_acounts_with_token(exported_accounts, farm_contract.farmToken)

for account_with_token in accounts_with_token:
account = Account(account_with_token.address, config.DEFAULT_OWNER)
account.address = WrapperAddress.from_bech32(account_with_token.address)
account.sync_nonce(network_providers.proxy)
tokens = [token for token in account_with_token.account_tokens_supply if token.token_name == farm_contract.farmToken]
for token in tokens:
event = EnterFarmEvent(token.token_name, int(token.supply), int(token.token_nonce_hex, 16), '')
farm_contract.enterFarm(
proxy,
account,
event
)

def generate_exit_farm_locked_transaction(args: Any):
"""Generate exit farm locked tokens transaction"""
farm_address = args.address
exported_accounts_path = args.accounts_export

if not farm_address or not exported_accounts_path:
print("Missing required arguments!")
return

print(f"Generate unstake farm tokens transaction for contract {farm_address}")
network_providers = NetworkProviders(API, PROXY)
proxy = network_providers.proxy

locked_token_hex = SimpleLockContractDataFetcher(Address(farm_address),
proxy.url).get_data("getLockedTokenId")
locked_lp_token_hex = SimpleLockContractDataFetcher(Address(farm_address),
proxy.url).get_data("getLpProxyTokenId")
locked_farm_token_hex = SimpleLockContractDataFetcher(Address(farm_address),
proxy.url).get_data("getFarmProxyTokenId")
LOCKED_TOKEN = hex_to_string(locked_token_hex)
LOCKED_LP_TOKEN = hex_to_string(locked_lp_token_hex)
LOCKED_FARM_TOKEN = hex_to_string(locked_farm_token_hex)


farm_contract = SimpleLockContract(LOCKED_TOKEN, LOCKED_LP_TOKEN, LOCKED_FARM_TOKEN, farm_address)

exported_accounts = read_accounts_from_json(exported_accounts_path)
accounts_with_token = get_acounts_with_token(exported_accounts, farm_contract.farm_proxy_token)

for account_with_token in accounts_with_token:
account = Account(account_with_token.address, config.DEFAULT_OWNER)
account.address = WrapperAddress.from_bech32(account_with_token.address)
account.sync_nonce(network_providers.proxy)
tokens = [token for token in account_with_token.account_tokens_supply if token.token_name == farm_contract.farm_proxy_token ]

for token in tokens:
if(account.address.to_bech32() != ""):
farm_contract.exit_farm_locked_token(
account,
proxy,
[[ESDTToken(farm_contract.farm_proxy_token, int(token.token_nonce_hex, 16), int(token.supply))]]
)

def get_farm_addresses_from_chain(version: str) -> list:
"""
Expand Down
37 changes: 34 additions & 3 deletions tools/runners/proxy_runner.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from argparse import ArgumentParser
from context import Context
from contracts.contract_identities import ProxyContractVersion
from contracts.dex_proxy_contract import DexProxyContract
from contracts.contract_identities import FarmContractVersion, ProxyContractVersion
from contracts.dex_proxy_contract import DexProxyContract, DexProxyExitFarmEvent
from contracts.farm_contract import FarmContract
from tools.common import API, PROXY, fetch_contracts_states, fetch_new_and_compare_contract_states, get_owner, get_user_continue
from tools.runners.common_runner import add_upgrade_command
from tools.runners.common_runner import add_generate_transaction_command, add_upgrade_command, get_acounts_with_token, read_accounts_from_json
from utils.utils_chain import Account, WrapperAddress, get_token_details_for_address
from utils.utils_tx import NetworkProviders
import config

Expand All @@ -18,6 +20,10 @@ def setup_parser(subparsers: ArgumentParser) -> ArgumentParser:
contract_group = contract_parser.add_subparsers()
add_upgrade_command(contract_group, upgrade_proxy_dex_contracts)

transaction_parser = subgroup_parser.add_parser('transaction', help='proxy dex transaction commands')
fgandila marked this conversation as resolved.
Show resolved Hide resolved
transactions_group = transaction_parser.add_subparsers()
add_generate_transaction_command(transactions_group, exit_proxy, 'exitFarmProxy', 'exit farm proxy command')

return group_parser


Expand Down Expand Up @@ -51,3 +57,28 @@ def upgrade_proxy_dex_contracts(compare_states: bool = False):

if compare_states:
fetch_new_and_compare_contract_states("proxy_dex", proxy_dex_contract.address, context.network_provider)

def exit_proxy(args: any):
farm_address = args.address
exported_accounts_path = args.accounts_export

context = Context()
farm_contract = DexProxyContract.load_contract_by_address(farm_address)
network_providers = NetworkProviders(API, PROXY)

farm_contract: FarmContract
farm_contract = context.get_contracts(config.FARMS_V2)[0]
proxy_contract: DexProxyContract
proxy_contract = context.get_contracts(config.PROXIES_V2)[0]

exported_accounts = read_accounts_from_json(exported_accounts_path)
accounts_with_token = get_acounts_with_token(exported_accounts, proxy_contract.proxy_farm_token)

for account_with_token in accounts_with_token:
account = Account(account_with_token.address, config.DEFAULT_OWNER)
account.address = WrapperAddress.from_bech32(account_with_token.address)
account.sync_nonce(network_providers.proxy)
tokens = [token for token in account_with_token.account_tokens_supply if token.token_name == proxy_contract.proxy_farm_token ]
for token in tokens:
event = DexProxyExitFarmEvent(farm_contract, proxy_contract.proxy_farm_token, int(token.token_nonce_hex,16), int(token.supply) )
proxy_contract.exit_farm_proxy(account, context.network_provider.proxy, event)