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

Feat/staking progarms tech debt #709

Draft
wants to merge 2 commits into
base: feat/multi-agent-middleware
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion operate/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,13 @@ def create_user_account(self, password: str) -> UserAccount:

def service_manager(self) -> services.manage.ServiceManager:
"""Load service manager."""
return services.manage.ServiceManager(
manager = services.manage.ServiceManager(
path=self._services,
keys_manager=self.keys_manager,
wallet_manager=self.wallet_manager,
logger=self.logger,
)
return manager

@property
def user_account(self) -> t.Optional[UserAccount]:
Expand Down
8 changes: 7 additions & 1 deletion operate/quickstart/run_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,24 +430,30 @@ def ensure_enough_funds(operate: "OperateApp", service: Service) -> None:

def run_service(operate: "OperateApp", config_path: str) -> None:
"""Run service."""

with open(config_path, "r") as config_file:
template = json.load(config_file)

print_title(f"{template['name']} quickstart")
config = configure_local_config(template)
manager = operate.service_manager()
service = get_service(manager, template)

# Set config in manager
manager.config_file = template

ask_password_if_needed(operate, config)

# reload manger and config after setting operate.password
manager = operate.service_manager()
manager.config_file = template

config = load_local_config()
ensure_enough_funds(operate, service)

print_box("PLEASE, DO NOT INTERRUPT THIS PROCESS.")
print_section(f"Deploying on-chain service on {config.principal_chain}...")
print("Cancelling the on-chain service update prematurely could lead to an inconsistent state of the Safe or the on-chain service state, which may require manual intervention to resolve.\n")

manager.deploy_service_onchain_from_safe(service_config_id=service.service_config_id)

print_section("Funding the service")
Expand Down
104 changes: 69 additions & 35 deletions operate/services/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ def __init__(
path: Path,
keys_manager: KeysManager,
wallet_manager: MasterWalletManager,
config_file: t.Optional[dict] = None,
logger: t.Optional[logging.Logger] = None,
) -> None:
"""
Initialze service manager

:param path: Path to service storage.
:param keys_manager: Keys manager.
:param wallet_manager: Wallet manager instance.
Expand All @@ -107,6 +107,7 @@ def __init__(
self.path = path
self.keys_manager = keys_manager
self.wallet_manager = wallet_manager
self.config_file = config_file
self.logger = logger or setup_logger(name="operate.manager")

def setup(self) -> None:
Expand Down Expand Up @@ -161,7 +162,16 @@ def get_eth_safe_tx_builder(self, ledger_config: LedgerConfig) -> EthSafeTxBuild
wallet=self.wallet_manager.load(ledger_config.chain.ledger_type),
contracts=CONTRACTS[ledger_config.chain],
)


def get_staking_contract(self, staking_program_id: str) -> str:
"""Get staking contract address from config."""
self.logger.info(f"Staking Choice Selected {staking_program_id}")
if not self.config_file or "staking_programs" not in self.config_file:
raise ValueError("Config file missing staking programs")
if staking_program_id not in self.config_file["staking_programs"]:
raise ValueError(f"Staking program {staking_program_id} not found")
return self.config_file["staking_programs"][staking_program_id]

def load_or_create(
self,
hash: str,
Expand Down Expand Up @@ -328,12 +338,10 @@ def _deploy_service_onchain( # pylint: disable=too-many-statements,too-many-loc
chain_data.multisig = info["multisig"]
service.store()
self.logger.info(f"Service state: {chain_data.on_chain_state.name}")

if user_params.use_staking:
staking_params = ocm.get_staking_params(
staking_contract=STAKING[ledger_config.chain][
user_params.staking_program_id
],
staking_contract=self.get_staking_contract(user_params.staking_program_id),
)
else: # TODO fix this - using pearl beta params
staking_params = dict( # nosec
Expand Down Expand Up @@ -400,7 +408,7 @@ def _deploy_service_onchain( # pylint: disable=too-many-statements,too-many-loc
)
)
current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, ocm # type: ignore # FIXME
chain_data, ledger_config, ocm , self.config_file # type: ignore # FIXME
)

self.logger.info(f"{current_staking_program=}")
Expand Down Expand Up @@ -522,6 +530,15 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
# TODO fix this
os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc

# Checkin if staking slots available
if user_params.use_staking and not sftxb.is_staking_slots_available(
staking_contract= self.get_staking_contract(user_params.staking_program_id)
):
raise ValueError("No staking slots available for this staking choice")




current_agent_id = None
if chain_data.token > -1:
self.logger.info("Syncing service state")
Expand All @@ -535,9 +552,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to

if user_params.use_staking:
staking_params = sftxb.get_staking_params(
staking_contract=STAKING[ledger_config.chain][
user_params.staking_program_id
],
staking_contract=self.get_staking_contract(user_params.staking_program_id)
)
else:
staking_params = dict( # nosec
Expand Down Expand Up @@ -721,7 +736,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
)
)
current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, sftxb
chain_data, ledger_config, sftxb, self.config_file
)

self.logger.info(f"{chain_data.token=}")
Expand Down Expand Up @@ -788,10 +803,8 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
self._get_on_chain_state(service=service, chain=chain)
== OnChainState.NON_EXISTENT
):
if user_params.use_staking and not sftxb.staking_slots_available(
staking_contract=STAKING[ledger_config.chain][
user_params.staking_program_id
]
if user_params.use_staking and not sftxb.is_staking_slots_available(
staking_contract= self.get_staking_contract(user_params.staking_program_id)
):
raise ValueError("No staking slots available")

Expand Down Expand Up @@ -1090,15 +1103,15 @@ def terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals

# Determine if the service is staked in a known staking program
current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, sftxb
chain_data, ledger_config, sftxb, self.config_file
)
is_staked = current_staking_program is not None

can_unstake = False
if current_staking_program is not None:
can_unstake = sftxb.can_unstake(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][current_staking_program],
staking_contract= self.get_staking_contract(current_staking_program),
)

# Cannot unstake, terminate flow.
Expand Down Expand Up @@ -1203,19 +1216,36 @@ def terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals

@staticmethod
def _get_current_staking_program(
chain_data: OnChainData, ledger_config: LedgerConfig, sftxb: EthSafeTxBuilder
) -> t.Optional[str]:
chain_data: OnChainData,
ledger_config: LedgerConfig,
sftxb: EthSafeTxBuilder,
config_file: dict,
logger: logging.Logger = None,
) -> t.Optional[str]:
"""Get current staking program."""
logger = logger or logging.getLogger(__name__)

if chain_data.token == NON_EXISTENT_TOKEN:
logger.info("Token is NON_EXISTENT_TOKEN, returning None")
return None

if not config_file or "staking_programs" not in config_file:
logger.error("Config file missing or no staking_programs found")
raise ValueError("Config file missing staking programs")

staking_programs = {k: v for k, v in config_file["staking_programs"].items() if k != "no_staking"}
current_staking_program = None
for staking_program in STAKING[ledger_config.chain]:

for staking_program in staking_programs:
state = sftxb.staking_status(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][staking_program],
staking_contract=config_file["staking_programs"][staking_program]
)
if state in (StakingState.STAKED, StakingState.EVICTED):
current_staking_program = staking_program
logger.info(f"Found active staking program: {staking_program}")
break

return current_staking_program

def unbond_service_on_chain(
Expand Down Expand Up @@ -1268,19 +1298,19 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too
chain_data = chain_config.chain_data
user_params = chain_data.user_params
target_staking_program = user_params.staking_program_id
target_staking_contract = STAKING[ledger_config.chain][target_staking_program]
target_staking_contract = self.get_staking_contract(target_staking_program)
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)

# TODO fixme
os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc

# Determine if the service is staked in a known staking program
current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, sftxb
chain_data, ledger_config, sftxb, self.config_file
)
is_staked = current_staking_program is not None
current_staking_contract = (
STAKING[ledger_config.chain][current_staking_program]
self.get_staking_contract(current_staking_program)
if current_staking_program is not None
else None
)
Expand Down Expand Up @@ -1355,18 +1385,18 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too
staking_rewards_available = sftxb.staking_rewards_available(
target_staking_contract
)
staking_slots_available = sftxb.staking_slots_available(target_staking_contract)
is_staking_slots_available = sftxb.is_staking_slots_available(target_staking_contract)
on_chain_state = self._get_on_chain_state(service=service, chain=chain)
current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, sftxb
chain_data, ledger_config, sftxb, self.config_file
)

self.logger.info(
f"use_staking={chain_config.chain_data.user_params.use_staking}"
)
self.logger.info(f"{staking_state=}")
self.logger.info(f"{staking_rewards_available=}")
self.logger.info(f"{staking_slots_available=}")
self.logger.info(f"{is_staking_slots_available=}")
self.logger.info(f"{on_chain_state=}")
self.logger.info(f"{current_staking_program=}")
self.logger.info(f"{target_staking_program=}")
Expand All @@ -1375,7 +1405,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too
chain_config.chain_data.user_params.use_staking
and staking_state == StakingState.UNSTAKED
and staking_rewards_available
and staking_slots_available
and is_staking_slots_available
and on_chain_state == OnChainState.DEPLOYED
):
self.logger.info(f"Approving staking: {chain_config.chain_data.token}")
Expand All @@ -1398,7 +1428,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too
service.store()

current_staking_program = self._get_current_staking_program(
chain_data, ledger_config, sftxb
chain_data, ledger_config, sftxb, self.config_file
)
self.logger.info(f"{target_staking_program=}")
self.logger.info(f"{current_staking_program=}")
Expand All @@ -1420,7 +1450,9 @@ def unstake_service_on_chain(

state = ocm.staking_status(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain], # type: ignore # TODO fix mypy
# staking_contract=STAKING[ledger_config.chain], # type: ignore # TODO fix
staking_contract=self.get_staking_contract(chain_data.user_params.staking_program_id),

)
self.logger.info(f"Staking status for service {chain_data.token}: {state}")
if state not in {StakingState.STAKED, StakingState.EVICTED}:
Expand All @@ -1432,7 +1464,8 @@ def unstake_service_on_chain(
self.logger.info(f"Unstaking service: {chain_data.token}")
ocm.unstake(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain], # type: ignore # TODO fix mypy
staking_contract=self.get_staking_contract(chain_data.user_params.staking_program_id),

)
chain_data.staked = False
service.store()
Expand Down Expand Up @@ -1465,7 +1498,7 @@ def unstake_service_on_chain_from_safe(
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
state = sftxb.staking_status(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][staking_program_id],
staking_contract=self.get_staking_contract(staking_program_id)
)
self.logger.info(f"Staking status for service {chain_data.token}: {state}")
if state not in {StakingState.STAKED, StakingState.EVICTED}:
Expand All @@ -1478,7 +1511,7 @@ def unstake_service_on_chain_from_safe(
sftxb.new_tx().add(
sftxb.get_unstaking_data(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][staking_program_id],
staking_contract=self.get_staking_contract(staking_program_id),
force=force,
)
).settle()
Expand All @@ -1505,7 +1538,7 @@ def claim_on_chain_from_safe(
f"OLAS Balance on service Safe {chain_data.multisig}: "
f"{get_asset_balance(ledger_api, OLAS[Chain(chain)], chain_data.multisig)}"
)
if staking_program_id not in STAKING[ledger_config.chain]:
if staking_program_id not in self.config_file["staking_programs"]:
raise RuntimeError(
"No staking contract found for the current staking_program_id: "
f"{staking_program_id}. Not claiming the rewards."
Expand All @@ -1515,7 +1548,8 @@ def claim_on_chain_from_safe(
receipt = sftxb.new_tx().add(
sftxb.get_claiming_data(
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][staking_program_id],
staking_contract=self.get_staking_contract(staking_program_id),

)
).settle()
return receipt['transactionHash']
Expand Down
Loading
Loading