diff --git a/.gitleaksignore b/.gitleaksignore
index 33c8c27cf..95964f4b2 100644
--- a/.gitleaksignore
+++ b/.gitleaksignore
@@ -29,4 +29,7 @@ d8149e9b5b7bd6a7ed7bc1039900702f1d4f287b:operate/services/manage.py:generic-api-
99c0f139b037da2587708212fcf6d0e20786d0ba:operate/services/manage.py:generic-api-key:455
91ec07457f69e9a29f63693ac8ef887e4b5f49f0:operate/services/manage.py:generic-api-key:454
410bea2bd02ff54da69387fe8f3b58793e09f7b0:operate/services/manage.py:generic-api-key:421
-410bea2bd02ff54da69387fe8f3b58793e09f7b0:operate/services/manage.py:generic-api-key:422
\ No newline at end of file
+410bea2bd02ff54da69387fe8f3b58793e09f7b0:operate/services/manage.py:generic-api-key:422
+467e8e64f51fb3659e5af17ba53ab587ec24fc30:operate/services/manage.py:generic-api-key:290
+64afe2ea92daafa2515c054f0e09931622d99f31:operate/services/manage.py:generic-api-key:290
+64afe2ea92daafa2515c054f0e09931622d99f31:operate/services/manage.py:generic-api-key:289
\ No newline at end of file
diff --git a/electron/install.js b/electron/install.js
index a43856a9b..861bf44ed 100644
--- a/electron/install.js
+++ b/electron/install.js
@@ -14,7 +14,7 @@ const { paths } = require('./constants');
* - use "" (nothing as a suffix) for latest release candidate, for example "0.1.0rc26"
* - use "alpha" for alpha release, for example "0.1.0rc26-alpha"
*/
-const OlasMiddlewareVersion = '0.1.0rc117';
+const OlasMiddlewareVersion = '0.1.0rc120';
const path = require('path');
const { app } = require('electron');
diff --git a/frontend/components/MainPage/sections/AddFundsSection.tsx b/frontend/components/MainPage/sections/AddFundsSection.tsx
index 97bf85641..ad3e17053 100644
--- a/frontend/components/MainPage/sections/AddFundsSection.tsx
+++ b/frontend/components/MainPage/sections/AddFundsSection.tsx
@@ -18,7 +18,6 @@ import styled from 'styled-components';
import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { COW_SWAP_GNOSIS_XDAI_OLAS_URL } from '@/constants/urls';
import { useWallet } from '@/hooks/useWallet';
-import { Address } from '@/types/Address';
import { copyToClipboard } from '@/utils/copyToClipboard';
import { truncateAddress } from '@/utils/truncate';
@@ -35,23 +34,6 @@ const CustomizedCardSection = styled(CardSection)<{ border?: boolean }>`
export const AddFundsSection = () => {
const [isAddFundsVisible, setIsAddFundsVisible] = useState(false);
- const { masterSafeAddress } = useWallet();
-
- const fundingAddress: Address | undefined = masterSafeAddress;
-
- const truncatedFundingAddress: string | undefined = useMemo(
- () => fundingAddress && truncateAddress(fundingAddress),
- [fundingAddress],
- );
-
- const handleCopyAddress = useCallback(
- () =>
- fundingAddress &&
- copyToClipboard(fundingAddress).then(() =>
- message.success('Copied successfully!'),
- ),
- [fundingAddress],
- );
return (
<>
@@ -75,17 +57,36 @@ export const AddFundsSection = () => {
- {isAddFundsVisible && (
- <>
-
-
-
- >
- )}
+ {isAddFundsVisible && }
+ >
+ );
+};
+
+export const OpenAddFundsSection = () => {
+ const { masterSafeAddress } = useWallet();
+
+ const truncatedFundingAddress: string | undefined = useMemo(
+ () => masterSafeAddress && truncateAddress(masterSafeAddress),
+ [masterSafeAddress],
+ );
+
+ const handleCopyAddress = useCallback(
+ () =>
+ masterSafeAddress &&
+ copyToClipboard(masterSafeAddress).then(() =>
+ message.success('Copied successfully!'),
+ ),
+ [masterSafeAddress],
+ );
+ return (
+ <>
+
+
+
>
);
};
diff --git a/frontend/components/ManageStakingPage/StakingContractSection/alerts.tsx b/frontend/components/ManageStakingPage/StakingContractSection/alerts.tsx
index f2c1cf6cd..7df340884 100644
--- a/frontend/components/ManageStakingPage/StakingContractSection/alerts.tsx
+++ b/frontend/components/ManageStakingPage/StakingContractSection/alerts.tsx
@@ -6,28 +6,35 @@ import { UNICODE_SYMBOLS } from '@/constants/symbols';
const { Text } = Typography;
export const AlertInsufficientMigrationFunds = ({
- totalOlasBalance,
+ masterSafeOlasBalance,
+ stakedOlasBalance,
+ totalOlasRequiredForStaking,
}: {
- totalOlasBalance: number;
-}) => (
-
-
- Insufficient amount of funds to switch
-
+ masterSafeOlasBalance: number;
+ stakedOlasBalance: number;
+ totalOlasRequiredForStaking: number;
+}) => {
+ const requiredOlasDeposit =
+ totalOlasRequiredForStaking - (stakedOlasBalance + masterSafeOlasBalance);
- Add funds to your account to meet the program requirements.
-
- Your current OLAS balance:{' '}
- {totalOlasBalance} OLAS
-
-
- }
- />
-);
+ return (
+
+
+ An additional {requiredOlasDeposit} OLAS is required to switch
+
+
+ Add {requiredOlasDeposit} OLAS to your account to
+ meet the contract requirements and switch.
+
+
+ }
+ />
+ );
+};
export const AlertNoSlots = () => (
{
const { goto } = usePageState();
- const { setServiceStatus, serviceStatus, setIsServicePollingPaused } =
- useServices();
+ const {
+ setServiceStatus,
+ serviceStatus,
+ setIsServicePollingPaused,
+ updateServiceStatus,
+ } = useServices();
const { serviceTemplate } = useServiceTemplates();
const { setMigrationModalOpen } = useModals();
const { activeStakingProgram, defaultStakingProgram, updateStakingProgram } =
useStakingProgram();
- const { stakingContractInfoRecord } = useStakingContractInfo();
const { token } = useToken();
- const { totalOlasBalance, isBalanceLoaded } = useBalance();
- const { isServiceStakedForMinimumDuration } = useStakingContractInfo();
+ const { safeBalance, totalOlasStakedBalance, isBalanceLoaded } = useBalance();
+ const { isServiceStakedForMinimumDuration, stakingContractInfoRecord } =
+ useStakingContractInfo();
+ const [isFundingSectionOpen, setIsFundingSectionOpen] = useState(false);
+
+ const stakingContractAddress =
+ SERVICE_STAKING_TOKEN_MECH_USAGE_CONTRACT_ADDRESSES[Chain.GNOSIS][
+ stakingProgram
+ ];
const stakingContractInfoForStakingProgram =
stakingContractInfoRecord?.[stakingProgram];
@@ -87,10 +96,15 @@ export const StakingContractSection = ({
);
const hasEnoughOlasToMigrate = useMemo(() => {
- if (totalOlasBalance === undefined) return false;
- if (!minimumOlasRequiredToMigrate) return false;
- return totalOlasBalance >= minimumOlasRequiredToMigrate;
- }, [minimumOlasRequiredToMigrate, totalOlasBalance]);
+ if (safeBalance?.OLAS === undefined || totalOlasStakedBalance === undefined)
+ return false;
+
+ const balanceForMigration = safeBalance.OLAS + totalOlasStakedBalance;
+
+ if (minimumOlasRequiredToMigrate === undefined) return false;
+
+ return balanceForMigration >= minimumOlasRequiredToMigrate;
+ }, [minimumOlasRequiredToMigrate, safeBalance?.OLAS, totalOlasStakedBalance]);
const hasEnoughSlots =
stakingContractInfoForStakingProgram?.maxNumServices &&
@@ -167,6 +181,7 @@ export const StakingContractSection = ({
isBalanceLoaded,
isSelected,
isServiceStakedForMinimumDuration,
+ minimumOlasRequiredToMigrate,
serviceStatus,
]);
@@ -179,9 +194,17 @@ export const StakingContractSection = ({
return ;
}
- if (!hasEnoughOlasToMigrate) {
+ if (
+ !hasEnoughOlasToMigrate &&
+ safeBalance?.OLAS !== undefined &&
+ totalOlasStakedBalance !== undefined
+ ) {
return (
-
+
);
}
@@ -191,10 +214,12 @@ export const StakingContractSection = ({
}, [
isSelected,
isBalanceLoaded,
- totalOlasBalance,
hasEnoughSlots,
hasEnoughOlasToMigrate,
isAppVersionCompatible,
+ safeBalance?.OLAS,
+ totalOlasStakedBalance,
+ minimumOlasRequiredToMigrate,
]);
const contractTagStatus = useMemo(() => {
@@ -213,40 +238,41 @@ export const StakingContractSection = ({
}, [activeStakingProgram, defaultStakingProgram, stakingProgram]);
return (
-
- {/* Title */}
-
- {`${activeStakingProgramMeta.name} contract`}
- {/* TODO: pass `status` attribute */}
-
- {!isSelected && (
- // here instead of isSelected we should check that the contract is not the old staking contract
- // but the one from staking factory (if we want to open govern)
-
- Contract details {UNICODE_SYMBOLS.EXTERNAL_LINK}
-
- )}
-
-
- {/* TODO: fix */}
-
- {/* Contract details
+ <>
+
+ {/* Title */}
+
+ {`${activeStakingProgramMeta.name} contract`}
+
+ {!isSelected && (
+ // here instead of isSelected we should check that the contract is not the old staking contract
+ // but the one from staking factory (if we want to open govern)
+
+ Contract details {UNICODE_SYMBOLS.EXTERNAL_LINK}
+
+ )}
+
+
+ {/* TODO: redisplay once bugs resolved */}
+
+ {/* Contract details
{stakingContractInfo?.availableRewards && (
)} */}
- {cantMigrateAlert}
- {/* Switch to program button */}
- {!isSelected && (
-
+ {cantMigrateAlert}
+ {/* Switch to program button */}
+ {stakingProgram !== StakingProgram.Alpha && (
+
+
+
+ )}
+ {stakingProgram === StakingProgram.Beta && (
-
+ )}
+
+ {stakingProgram === StakingProgram.Beta && isFundingSectionOpen && (
+
)}
-
+ >
);
};
diff --git a/frontend/components/ManageStakingPage/index.tsx b/frontend/components/ManageStakingPage/index.tsx
index 310d19eca..82938256f 100644
--- a/frontend/components/ManageStakingPage/index.tsx
+++ b/frontend/components/ManageStakingPage/index.tsx
@@ -1,8 +1,6 @@
import { CloseOutlined } from '@ant-design/icons';
import { Button, Card } from 'antd';
-import { Chain } from '@/client';
-import { SERVICE_STAKING_TOKEN_MECH_USAGE_CONTRACT_ADDRESSES } from '@/constants/contractAddresses';
import { Pages } from '@/enums/PageState';
import { StakingProgram } from '@/enums/StakingProgram';
import { usePageState } from '@/hooks/usePageState';
@@ -26,15 +24,8 @@ export const ManageStakingPage = () => {
}
>
- {Object.entries(
- SERVICE_STAKING_TOKEN_MECH_USAGE_CONTRACT_ADDRESSES[Chain.GNOSIS],
- ).map(([stakingProgram, contractAddress]) => (
-
- ))}
+
+
);
};
diff --git a/operate/cli.py b/operate/cli.py
index 7d5285d13..99cabe0f1 100644
--- a/operate/cli.py
+++ b/operate/cli.py
@@ -530,8 +530,8 @@ async def _create_services(request: Request) -> JSONResponse:
if template.get("deploy", False):
def _fn() -> None:
+ # deploy_service_onchain_from_safe includes stake_service_on_chain_from_safe
manager.deploy_service_onchain_from_safe(hash=service.hash)
- # manager.stake_service_on_chain_from_safe(hash=service.hash) # Done inside deploy_service_onchain
manager.fund_service(hash=service.hash)
manager.deploy_service_locally(hash=service.hash)
@@ -556,8 +556,9 @@ async def _update_services(request: Request) -> JSONResponse:
)
if template.get("deploy", False):
manager = operate.service_manager()
+
+ # deploy_service_onchain_from_safe includes stake_service_on_chain_from_safe
manager.deploy_service_onchain_from_safe(hash=service.hash)
- # manager.stake_service_on_chain_from_safe(hash=service.hash) # Done in deploy_service_onchain_from_safe
manager.fund_service(hash=service.hash)
manager.deploy_service_locally(hash=service.hash)
schedule_funding_job(service=service.hash)
@@ -675,7 +676,7 @@ async def _start_service_locally(request: Request) -> JSONResponse:
def _fn() -> None:
manager.deploy_service_onchain(hash=service)
- # manager.stake_service_on_chain(hash=service)
+ manager.stake_service_on_chain(hash=service)
manager.fund_service(hash=service)
manager.deploy_service_locally(hash=service, force=True)
diff --git a/operate/services/manage.py b/operate/services/manage.py
index 6acf0b462..076701949 100644
--- a/operate/services/manage.py
+++ b/operate/services/manage.py
@@ -23,6 +23,7 @@
import logging
import os
import shutil
+import time
import traceback
import typing as t
from collections import Counter
@@ -115,21 +116,26 @@ def json(self) -> t.List[t.Dict]:
self.logger.warning(
f"Failed to load service: {path.name}. Exception: {e}"
)
- # delete the invalid path
- shutil.rmtree(path)
- self.logger.info(f"Deleted invalid service: {path.name}")
+ # rename the invalid path
+ timestamp = int(time.time())
+ invalid_path = path.parent / f"invalid_{timestamp}_{path.name}"
+ os.rename(path, invalid_path)
+ self.logger.info(
+ f"Renamed invalid service: {path.name} to {invalid_path.name}"
+ )
+
return data
def exists(self, service: str) -> bool:
"""Check if service exists."""
return (self.path / service).exists()
- def get_on_chain_manager(self, service: Service) -> OnChainManager:
+ def get_on_chain_manager(self, ledger_config: LedgerConfig) -> OnChainManager:
"""Get OnChainManager instance."""
return OnChainManager(
- rpc=service.ledger_config.rpc,
- wallet=self.wallet_manager.load(service.ledger_config.type),
- contracts=CONTRACTS[service.ledger_config.chain],
+ rpc=ledger_config.rpc,
+ wallet=self.wallet_manager.load(ledger_config.type),
+ contracts=CONTRACTS[ledger_config.chain],
)
def get_eth_safe_tx_builder(self, ledger_config: LedgerConfig) -> EthSafeTxBuilder:
@@ -159,7 +165,9 @@ def load_or_create(
service = Service.load(path=path)
if service_template is not None:
- service.update_user_params_from_template(service_template=service_template)
+ service.update_user_params_from_template(
+ service_template=service_template
+ )
return service
@@ -184,22 +192,21 @@ def load_or_create(
return service
- def _get_on_chain_state(self, chain_config: ChainConfig) -> OnChainState:
+ def _get_on_chain_state(self, service: Service, chain_id: str) -> OnChainState:
+ chain_config = service.chain_configs[chain_id]
chain_data = chain_config.chain_data
ledger_config = chain_config.ledger_config
if chain_data.token == NON_EXISTENT_TOKEN:
service_state = OnChainState.NON_EXISTENT
chain_data.on_chain_state = service_state
- # TODO save service state
- # service.store()
+ service.store()
return service_state
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
info = sftxb.info(token_id=chain_data.token)
service_state = OnChainState(info["service_state"])
chain_data.on_chain_state = service_state
- # TODO save service state
- # service.store()
+ service.store()
return service_state
def _get_on_chain_hash(self, chain_config: ChainConfig) -> t.Optional[str]:
@@ -218,10 +225,28 @@ def _get_on_chain_hash(self, chain_config: ChainConfig) -> t.Optional[str]:
f"Something went wrong while trying to get the code uri from IPFS: {res}"
)
- def deploy_service_onchain( # pylint: disable=too-many-statements
+ def deploy_service_onchain( # pylint: disable=too-many-statements,too-many-locals
self,
hash: str,
- update: bool = False,
+ ) -> None:
+ """
+ Deploy as service on-chain
+
+ :param hash: Service hash
+ """
+ # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version.
+
+ service = self.load_or_create(hash=hash)
+ for chain_id in service.chain_configs.keys():
+ self._deploy_service_onchain(
+ hash=hash,
+ chain_id=chain_id,
+ )
+
+ def _deploy_service_onchain( # pylint: disable=too-many-statements,too-many-locals
+ self,
+ hash: str,
+ chain_id: str,
) -> None:
"""
Deploy as service on-chain
@@ -229,49 +254,73 @@ def deploy_service_onchain( # pylint: disable=too-many-statements
:param hash: Service hash
:param update: Update the existing deployment
"""
- self.logger.info("Loading service")
+ # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version.
+
+ self.logger.info(f"_deploy_service_onchain_from_safe {chain_id=}")
service = self.load_or_create(hash=hash)
- user_params = service.chain_data.user_params
+ chain_config = service.chain_configs[chain_id]
+ ledger_config = chain_config.ledger_config
+ chain_data = chain_config.chain_data
+ user_params = chain_config.chain_data.user_params
keys = service.keys
instances = [key.address for key in keys]
- ocm = self.get_on_chain_manager(service=service)
- if user_params.use_staking and not ocm.staking_slots_available(
- staking_contract=STAKING[service.ledger_config.chain]
- ):
- raise ValueError("No staking slots available")
+ ocm = self.get_on_chain_manager(ledger_config=ledger_config)
- if user_params.use_staking and not ocm.staking_rewards_available(
- staking_contract=STAKING[service.ledger_config.chain]
- ):
- raise ValueError("No staking rewards available")
+ # TODO fix this
+ os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc
+ os.environ[
+ "OPEN_AUTONOMY_SUBGRAPH_URL"
+ ] = "https://subgraph.autonolas.tech/subgraphs/name/autonolas-staging"
- if service.chain_data.token > -1:
+ current_agent_id = None
+ if chain_data.token > -1:
self.logger.info("Syncing service state")
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
- service.chain_data.instances = info["instances"]
- service.chain_data.multisig = info["multisig"]
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
+ chain_data.instances = info["instances"]
+ chain_data.multisig = info["multisig"]
service.store()
- self.logger.info(f"Service state: {service.chain_data.on_chain_state.name}")
+ 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
+ ],
+ )
+ else: # TODO fix this - using pearl beta params
+ staking_params = dict( # nosec
+ agent_ids=[25],
+ service_registry="0x9338b5153AE39BB89f50468E608eD9d764B755fD", # nosec
+ staking_token="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", # nosec
+ service_registry_token_utility="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", # nosec
+ min_staking_deposit=20000000000000000000,
+ activity_checker="0x155547857680A6D51bebC5603397488988DEb1c8", # nosec
+ )
if user_params.use_staking:
self.logger.info("Checking staking compatibility")
- if service.chain_data.on_chain_state in (
+
+ # TODO: Missing check when the service is currently staked in a program, but needs to be staked
+ # in a different target program. The In this case, balance = currently staked balance + safe balance
+
+ if chain_data.on_chain_state in (
OnChainState.NON_EXISTENT,
OnChainState.PRE_REGISTRATION,
):
required_olas = (
- user_params.olas_cost_of_bond + user_params.olas_required_to_stake
+ staking_params["min_staking_deposit"]
+ + staking_params["min_staking_deposit"] # bond = staking
)
- elif service.chain_data.on_chain_state == OnChainState.ACTIVE_REGISTRATION:
- required_olas = user_params.olas_required_to_stake
+ elif chain_data.on_chain_state == OnChainState.ACTIVE_REGISTRATION:
+ required_olas = staking_params["min_staking_deposit"]
else:
required_olas = 0
balance = (
registry_contracts.erc20.get_instance(
ledger_api=ocm.ledger_api,
- contract_address=OLAS[service.ledger_config.chain],
+ contract_address=OLAS[ledger_config.chain],
)
.functions.balanceOf(ocm.crypto.address)
.call()
@@ -282,91 +331,108 @@ def deploy_service_onchain( # pylint: disable=too-many-statements
f"required olas: {required_olas}; your balance {balance}"
)
- if service.chain_data.on_chain_state == OnChainState.NON_EXISTENT:
+ on_chain_hash = self._get_on_chain_hash(chain_config=chain_config)
+ current_agent_bond = staking_params[
+ "min_staking_deposit"
+ ] # TODO fixme, read from service registry token utility contract
+ is_first_mint = (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.NON_EXISTENT
+ )
+ is_update = (
+ (not is_first_mint)
+ and (on_chain_hash is not None)
+ and (
+ on_chain_hash != service.hash
+ or current_agent_id != staking_params["agent_ids"][0]
+ or current_agent_bond != staking_params["min_staking_deposit"]
+ )
+ )
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, ocm # type: ignore # FIXME
+ )
+
+ self.logger.info(f"{current_staking_program=}")
+ self.logger.info(f"{user_params.staking_program_id=}")
+ self.logger.info(f"{on_chain_hash=}")
+ self.logger.info(f"{service.hash=}")
+ self.logger.info(f"{current_agent_id=}")
+ self.logger.info(f"{staking_params['agent_ids'][0]=}")
+ self.logger.info(f"{is_first_mint=}")
+ self.logger.info(f"{is_update=}")
+
+ if chain_data.on_chain_state == OnChainState.NON_EXISTENT:
self.logger.info("Minting service")
- service.chain_data.token = t.cast(
+ chain_data.token = t.cast(
int,
ocm.mint(
package_path=service.service_path,
- agent_id=user_params.agent_id,
+ agent_id=staking_params["agent_ids"][0],
number_of_slots=service.helper.config.number_of_agents,
cost_of_bond=(
- user_params.olas_cost_of_bond
+ staking_params["min_staking_deposit"]
if user_params.use_staking
else user_params.cost_of_bond
),
threshold=user_params.threshold,
nft=IPFSHash(user_params.nft),
- update_token=service.chain_data.token if update else None,
+ update_token=chain_data.token if is_update else None,
token=(
- OLAS[service.ledger_config.chain]
- if user_params.use_staking
- else None
+ OLAS[ledger_config.chain] if user_params.use_staking else None
),
).get("token"),
)
- service.chain_data.on_chain_state = OnChainState.PRE_REGISTRATION
+ chain_data.on_chain_state = OnChainState.PRE_REGISTRATION
service.store()
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
- if service.chain_data.on_chain_state == OnChainState.PRE_REGISTRATION:
+ if chain_data.on_chain_state == OnChainState.PRE_REGISTRATION:
self.logger.info("Activating service")
ocm.activate(
- service_id=service.chain_data.token,
- token=(
- OLAS[service.ledger_config.chain]
- if user_params.use_staking
- else None
- ),
+ service_id=chain_data.token,
+ token=(OLAS[ledger_config.chain] if user_params.use_staking else None),
)
- service.chain_data.on_chain_state = OnChainState.ACTIVE_REGISTRATION
+ chain_data.on_chain_state = OnChainState.ACTIVE_REGISTRATION
service.store()
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
- if service.chain_data.on_chain_state == OnChainState.ACTIVE_REGISTRATION:
+ if chain_data.on_chain_state == OnChainState.ACTIVE_REGISTRATION:
self.logger.info("Registering agent instances")
+ agent_id = staking_params["agent_ids"][0]
ocm.register(
- service_id=service.chain_data.token,
+ service_id=chain_data.token,
instances=instances,
- agents=[user_params.agent_id for _ in instances],
- token=(
- OLAS[service.ledger_config.chain]
- if user_params.use_staking
- else None
- ),
+ agents=[agent_id for _ in instances],
+ token=(OLAS[ledger_config.chain] if user_params.use_staking else None),
)
- service.chain_data.on_chain_state = OnChainState.FINISHED_REGISTRATION
+ chain_data.on_chain_state = OnChainState.FINISHED_REGISTRATION
service.store()
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
- if service.chain_data.on_chain_state == OnChainState.FINISHED_REGISTRATION:
+ if chain_data.on_chain_state == OnChainState.FINISHED_REGISTRATION:
self.logger.info("Deploying service")
ocm.deploy(
- service_id=service.chain_data.token,
- reuse_multisig=update,
- token=(
- OLAS[service.ledger_config.chain]
- if user_params.use_staking
- else None
- ),
+ service_id=chain_data.token,
+ reuse_multisig=is_update,
+ token=(OLAS[ledger_config.chain] if user_params.use_staking else None),
)
- service.chain_data.on_chain_state = OnChainState.DEPLOYED
+ chain_data.on_chain_state = OnChainState.DEPLOYED
service.store()
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data = OnChainData(
- token=service.chain_data.token,
+ info = ocm.info(token_id=chain_data.token)
+ chain_data = OnChainData(
+ token=chain_data.token,
instances=info["instances"],
multisig=info["multisig"],
staked=False,
- on_chain_state=service.chain_data.on_chain_state,
- user_params=service.chain_data.user_params,
+ on_chain_state=chain_data.on_chain_state,
+ user_params=chain_data.user_params,
)
service.store()
@@ -410,7 +476,9 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
# TODO fix this
os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc
- os.environ["OPEN_AUTONOMY_SUBGRAPH_URL"] = "https://subgraph.autonolas.tech/subgraphs/name/autonolas-staging"
+ os.environ[
+ "OPEN_AUTONOMY_SUBGRAPH_URL"
+ ] = "https://subgraph.autonolas.tech/subgraphs/name/autonolas-staging"
current_agent_id = None
if chain_data.token > -1:
@@ -425,16 +493,18 @@ 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=STAKING[ledger_config.chain][
+ user_params.staking_program_id
+ ],
)
else: # TODO fix this - using pearl beta params
- staking_params = dict(
+ staking_params = dict( # nosec
agent_ids=[25],
service_registry="0x9338b5153AE39BB89f50468E608eD9d764B755fD", # nosec
staking_token="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", # nosec
service_registry_token_utility="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", # nosec
min_staking_deposit=20000000000000000000,
- activity_checker="0x155547857680A6D51bebC5603397488988DEb1c8" # nosec
+ activity_checker="0x155547857680A6D51bebC5603397488988DEb1c8", # nosec
)
if user_params.use_staking:
@@ -448,7 +518,8 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
OnChainState.PRE_REGISTRATION,
):
required_olas = (
- staking_params["min_staking_deposit"] + staking_params["min_staking_deposit"] # bond = staking
+ staking_params["min_staking_deposit"]
+ + staking_params["min_staking_deposit"] # bond = staking
)
elif chain_data.on_chain_state == OnChainState.ACTIVE_REGISTRATION:
required_olas = staking_params["min_staking_deposit"]
@@ -470,13 +541,25 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
)
on_chain_hash = self._get_on_chain_hash(chain_config=chain_config)
- is_first_mint = self._get_on_chain_state(chain_config=chain_config) == OnChainState.NON_EXISTENT
+ current_agent_bond = staking_params[
+ "min_staking_deposit"
+ ] # TODO fixme, read from service registry token utility contract
+ is_first_mint = (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.NON_EXISTENT
+ )
is_update = (
(not is_first_mint)
and (on_chain_hash is not None)
- and (on_chain_hash != service.hash or current_agent_id != staking_params["agent_ids"][0])
+ and (
+ on_chain_hash != service.hash
+ or current_agent_id != staking_params["agent_ids"][0]
+ or current_agent_bond != staking_params["min_staking_deposit"]
+ )
+ )
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, sftxb
)
- current_staking_program = self._get_current_staking_program(chain_data, ledger_config, sftxb)
self.logger.info(f"{current_staking_program=}")
self.logger.info(f"{user_params.staking_program_id=}")
@@ -488,13 +571,13 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
self.logger.info(f"{is_update=}")
if is_update:
- self._terminate_service_on_chain_from_safe(
- hash=hash,
- chain_id=chain_id
- )
+ self._terminate_service_on_chain_from_safe(hash=hash, chain_id=chain_id)
# Update service
- if self._get_on_chain_state(chain_config=chain_config) == OnChainState.PRE_REGISTRATION:
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.PRE_REGISTRATION
+ ):
self.logger.info("Updating service")
receipt = (
sftxb.new_tx()
@@ -533,10 +616,14 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
service.store()
# Mint service
- if self._get_on_chain_state(chain_config=chain_config) == OnChainState.NON_EXISTENT:
-
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == 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]
+ staking_contract=STAKING[ledger_config.chain][
+ user_params.staking_program_id
+ ]
):
raise ValueError("No staking slots available")
@@ -578,7 +665,10 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
chain_data.on_chain_state = OnChainState.PRE_REGISTRATION
service.store()
- if self._get_on_chain_state(chain_config=chain_config) == OnChainState.PRE_REGISTRATION:
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.PRE_REGISTRATION
+ ):
cost_of_bond = staking_params["min_staking_deposit"]
if user_params.use_staking:
token_utility = staking_params["service_registry_token_utility"]
@@ -628,7 +718,10 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
chain_data.on_chain_state = OnChainState.ACTIVE_REGISTRATION
service.store()
- if self._get_on_chain_state(chain_config=chain_config) == OnChainState.ACTIVE_REGISTRATION:
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.ACTIVE_REGISTRATION
+ ):
cost_of_bond = user_params.cost_of_bond
if user_params.use_staking:
token_utility = staking_params["service_registry_token_utility"]
@@ -682,7 +775,10 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
chain_data.on_chain_state = OnChainState.FINISHED_REGISTRATION
service.store()
- if self._get_on_chain_state(chain_config=chain_config) == OnChainState.FINISHED_REGISTRATION:
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.FINISHED_REGISTRATION
+ ):
self.logger.info("Deploying service")
reuse_multisig = True
@@ -695,7 +791,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
messages = sftxb.get_deploy_data_from_safe(
service_id=chain_data.token,
reuse_multisig=reuse_multisig,
- master_safe=sftxb.wallet.safe,
+ master_safe=sftxb.wallet.safe, # type: ignore # TODO fix mypy
)
tx = sftxb.new_tx()
for message in messages:
@@ -713,34 +809,48 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to
service.store()
self.stake_service_on_chain_from_safe(hash=hash, chain_id=chain_id)
- def terminate_service_on_chain(self, hash: str) -> None:
+ def terminate_service_on_chain(
+ self, hash: str, chain_id: t.Optional[str] = None
+ ) -> None:
"""
Terminate service on-chain
:param hash: Service hash
"""
+ # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version.
+
+ self.logger.info("terminate_service_on_chain")
service = self.load_or_create(hash=hash)
- ocm = self.get_on_chain_manager(service=service)
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
- if service.chain_data.on_chain_state != OnChainState.DEPLOYED:
+ if chain_id is None:
+ chain_id = service.home_chain_id
+
+ chain_config = service.chain_configs[chain_id]
+ ledger_config = chain_config.ledger_config
+ chain_data = chain_config.chain_data
+ ocm = self.get_on_chain_manager(ledger_config=ledger_config)
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
+
+ if chain_data.on_chain_state != OnChainState.DEPLOYED:
self.logger.info("Cannot terminate service")
return
self.logger.info("Terminating service")
ocm.terminate(
- service_id=service.chain_data.token,
+ service_id=chain_data.token,
token=(
- OLAS[service.ledger_config.chain]
- if service.chain_data.user_params.use_staking
+ OLAS[ledger_config.chain]
+ if chain_data.user_params.use_staking
else None
),
)
- service.chain_data.on_chain_state = OnChainState.TERMINATED_BONDED
+ chain_data.on_chain_state = OnChainState.TERMINATED_BONDED
service.store()
- def _terminate_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
+ def _terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals
+ self, hash: str, chain_id: str
+ ) -> None:
"""
Terminate service on-chain
@@ -763,7 +873,9 @@ def _terminate_service_on_chain_from_safe(self, hash: str, chain_id: str) -> Non
chain_data.on_chain_state = OnChainState(info["service_state"])
# Determine if the service is staked in a known staking program
- current_staking_program = self._get_current_staking_program(chain_data, ledger_config, sftxb)
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, sftxb
+ )
is_staked = current_staking_program is not None
can_unstake = False
@@ -780,9 +892,11 @@ def _terminate_service_on_chain_from_safe(self, hash: str, chain_id: str) -> Non
# Unstake the service if applies
if is_staked and can_unstake:
- self.unstake_service_on_chain_from_safe(hash=hash, chain_id=chain_id, staking_program_id=current_staking_program)
+ self.unstake_service_on_chain_from_safe(
+ hash=hash, chain_id=chain_id, staking_program_id=current_staking_program
+ )
- if self._get_on_chain_state(chain_config) in (
+ if self._get_on_chain_state(service=service, chain_id=chain_id) in (
OnChainState.ACTIVE_REGISTRATION,
OnChainState.FINISHED_REGISTRATION,
OnChainState.DEPLOYED,
@@ -794,7 +908,10 @@ def _terminate_service_on_chain_from_safe(self, hash: str, chain_id: str) -> Non
)
).settle()
- if self._get_on_chain_state(chain_config) == OnChainState.TERMINATED_BONDED:
+ if (
+ self._get_on_chain_state(service=service, chain_id=chain_id)
+ == OnChainState.TERMINATED_BONDED
+ ):
self.logger.info("Unbonding service")
sftxb.new_tx().add(
sftxb.get_unbond_data(
@@ -808,17 +925,34 @@ def _terminate_service_on_chain_from_safe(self, hash: str, chain_id: str) -> Non
counter_instances = Counter(s.lower() for s in instances)
if counter_current_safe_owners == counter_instances:
+ self.logger.info("Service funded for safe swap")
+ self.fund_service(
+ hash=hash,
+ rpc=ledger_config.rpc,
+ agent_topup=chain_data.user_params.fund_requirements.agent,
+ agent_fund_threshold=chain_data.user_params.fund_requirements.agent,
+ safe_topup=0,
+ safe_fund_treshold=0,
+ )
+
self.logger.info("Swapping Safe owners")
sftxb.swap( # noqa: E800
service_id=chain_data.token, # noqa: E800
multisig=chain_data.multisig, # TODO this can be read from the registry
owner_key=str(
- self.keys_manager.get(key=current_safe_owners[0]).private_key # TODO allow multiple owners
+ self.keys_manager.get(
+ key=current_safe_owners[0]
+ ).private_key # TODO allow multiple owners
), # noqa: E800
- new_owner_address=wallet.safe if wallet.safe else wallet.crypto.address # TODO it should always be safe address
+ new_owner_address=wallet.safe
+ if wallet.safe
+ else wallet.crypto.address, # TODO it should always be safe address
) # noqa: E800
- def _get_current_staking_program(self, chain_data, ledger_config, sftxb) -> t.Optional[str]:
+ @staticmethod
+ def _get_current_staking_program(
+ chain_data: OnChainData, ledger_config: LedgerConfig, sftxb: EthSafeTxBuilder
+ ) -> t.Optional[str]:
if chain_data.token == NON_EXISTENT_TOKEN:
return None
@@ -832,34 +966,45 @@ def _get_current_staking_program(self, chain_data, ledger_config, sftxb) -> t.Op
current_staking_program = staking_program
return current_staking_program
- def unbond_service_on_chain(self, hash: str) -> None:
+ def unbond_service_on_chain(
+ self, hash: str, chain_id: t.Optional[str] = None
+ ) -> None:
"""
Unbond service on-chain
:param hash: Service hash
"""
+ # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version.
+
service = self.load_or_create(hash=hash)
- ocm = self.get_on_chain_manager(service=service)
- info = ocm.info(token_id=service.chain_data.token)
- service.chain_data.on_chain_state = OnChainState(info["service_state"])
- if service.chain_data.on_chain_state != OnChainState.TERMINATED_BONDED:
+ if chain_id is None:
+ chain_id = service.home_chain_id
+
+ chain_config = service.chain_configs[chain_id]
+ ledger_config = chain_config.ledger_config
+ chain_data = chain_config.chain_data
+ ocm = self.get_on_chain_manager(ledger_config=ledger_config)
+ info = ocm.info(token_id=chain_data.token)
+ chain_data.on_chain_state = OnChainState(info["service_state"])
+
+ if chain_data.on_chain_state != OnChainState.TERMINATED_BONDED:
self.logger.info("Cannot unbond service")
return
self.logger.info("Unbonding service")
ocm.unbond(
- service_id=service.chain_data.token,
+ service_id=chain_data.token,
token=(
- OLAS[service.ledger_config.chain]
- if service.chain_data.user_params.use_staking
+ OLAS[ledger_config.chain]
+ if chain_data.user_params.use_staking
else None
),
)
- service.chain_data.on_chain_state = OnChainState.UNBONDED
+ chain_data.on_chain_state = OnChainState.UNBONDED
service.store()
- def stake_service_on_chain(self, hash: str, chain_id: int, staking_program_id: str) -> None:
+ def stake_service_on_chain(self, hash: str) -> None:
"""
Stake service on-chain
@@ -867,7 +1012,9 @@ def stake_service_on_chain(self, hash: str, chain_id: int, staking_program_id: s
"""
raise NotImplementedError
- def stake_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
+ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too-many-locals
+ self, hash: str, chain_id: str
+ ) -> None:
"""
Stake service on-chain
@@ -888,40 +1035,76 @@ def stake_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
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)
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, sftxb
+ )
is_staked = current_staking_program is not None
- current_staking_contract = STAKING[ledger_config.chain][current_staking_program] if is_staked else None
+ current_staking_contract = (
+ STAKING[ledger_config.chain][current_staking_program]
+ if current_staking_program is not None
+ else None
+ )
# perform the unstaking flow if necessary
if is_staked:
- can_unstake = sftxb.can_unstake(chain_config.chain_data.token, current_staking_contract)
+ can_unstake = sftxb.can_unstake(
+ chain_config.chain_data.token, current_staking_contract # type: ignore # TODO fix mypy
+ )
if not chain_config.chain_data.user_params.use_staking and can_unstake:
- self.logger.info(f"Use staking is set to false, but service {chain_config.chain_data.token} is staked and can be unstaked. Unstaking...")
- self.unstake_service_on_chain_from_safe(hash=hash, chain_id=chain_id, staking_program_id=current_staking_program)
+ self.logger.info(
+ f"Use staking is set to false, but service {chain_config.chain_data.token} is staked and can be unstaked. Unstaking..."
+ )
+ self.unstake_service_on_chain_from_safe(
+ hash=hash,
+ chain_id=chain_id,
+ staking_program_id=current_staking_program,
+ )
info = sftxb.info(token_id=chain_config.chain_data.token)
chain_config.chain_data.on_chain_state = OnChainState(info["service_state"])
staking_state = sftxb.staking_status(
service_id=chain_data.token,
- staking_contract=current_staking_contract,
+ staking_contract=current_staking_contract, # type: ignore # TODO fix mypy
)
if staking_state == StakingState.EVICTED and can_unstake:
- self.logger.info(f"Service {chain_config.chain_data.token} has been evicted and can be unstaked. Unstaking...")
- self.unstake_service_on_chain_from_safe(hash=hash, chain_id=chain_id, staking_program_id=current_staking_program)
+ self.logger.info(
+ f"Service {chain_config.chain_data.token} has been evicted and can be unstaked. Unstaking..."
+ )
+ self.unstake_service_on_chain_from_safe(
+ hash=hash,
+ chain_id=chain_id,
+ staking_program_id=current_staking_program,
+ )
- if staking_state == StakingState.STAKED and can_unstake and not sftxb.staking_rewards_available(current_staking_contract):
+ if (
+ staking_state == StakingState.STAKED
+ and can_unstake
+ and not sftxb.staking_rewards_available(current_staking_contract) # type: ignore # TODO fix mypy
+ ):
self.logger.info(
f"There are no rewards available, service {chain_config.chain_data.token} "
f"is already staked and can be unstaked. Unstaking..."
)
- self.unstake_service_on_chain_from_safe(hash=hash, chain_id=chain_id, staking_program_id=current_staking_program)
+ self.unstake_service_on_chain_from_safe(
+ hash=hash,
+ chain_id=chain_id,
+ staking_program_id=current_staking_program,
+ )
- if staking_state == StakingState.STAKED and current_staking_program != target_staking_contract and can_unstake:
+ if (
+ staking_state == StakingState.STAKED
+ and current_staking_program != target_staking_contract
+ and can_unstake
+ ):
self.logger.info(
f"{chain_config.chain_data.token} is staked in a different staking program. Unstaking..."
)
- self.unstake_service_on_chain_from_safe(hash=hash, chain_id=chain_id, staking_program_id=current_staking_program)
+ self.unstake_service_on_chain_from_safe(
+ hash=hash,
+ chain_id=chain_id,
+ staking_program_id=current_staking_program,
+ )
staking_state = sftxb.staking_status(
service_id=chain_config.chain_data.token,
@@ -929,12 +1112,18 @@ def stake_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
)
self.logger.info("Checking conditions to stake.")
- staking_rewards_available = sftxb.staking_rewards_available(target_staking_contract)
+ staking_rewards_available = sftxb.staking_rewards_available(
+ target_staking_contract
+ )
staking_slots_available = sftxb.staking_slots_available(target_staking_contract)
- on_chain_state = self._get_on_chain_state(chain_config=chain_config)
- current_staking_program = self._get_current_staking_program(chain_data, ledger_config, sftxb)
-
- self.logger.info(f"use_staking={chain_config.chain_data.user_params.use_staking}")
+ on_chain_state = self._get_on_chain_state(service=service, chain_id=chain_id)
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, sftxb
+ )
+
+ 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=}")
@@ -943,19 +1132,17 @@ def stake_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
self.logger.info(f"{target_staking_program=}")
if (
- chain_config.chain_data.user_params.use_staking
- and staking_state == StakingState.UNSTAKED
- and staking_rewards_available
- and staking_slots_available
- and on_chain_state == OnChainState.DEPLOYED
+ chain_config.chain_data.user_params.use_staking
+ and staking_state == StakingState.UNSTAKED
+ and staking_rewards_available
+ and staking_slots_available
+ and on_chain_state == OnChainState.DEPLOYED
):
self.logger.info(f"Approving staking: {chain_config.chain_data.token}")
sftxb.new_tx().add(
sftxb.get_staking_approval_data(
service_id=chain_config.chain_data.token,
- service_registry=CONTRACTS[ledger_config.chain][
- "service_registry"
- ],
+ service_registry=CONTRACTS[ledger_config.chain]["service_registry"],
staking_contract=target_staking_contract,
)
).settle()
@@ -970,44 +1157,57 @@ def stake_service_on_chain_from_safe(self, hash: str, chain_id: str) -> None:
chain_config.chain_data.staked = True
service.store()
- current_staking_program = self._get_current_staking_program(chain_data, ledger_config, sftxb)
+ current_staking_program = self._get_current_staking_program(
+ chain_data, ledger_config, sftxb
+ )
self.logger.info(f"{target_staking_program=}")
self.logger.info(f"{current_staking_program=}")
- def unstake_service_on_chain(self, hash: str) -> None:
+ def unstake_service_on_chain(
+ self, hash: str, chain_id: t.Optional[str] = None
+ ) -> None:
"""
Unbond service on-chain
:param hash: Service hash
"""
+ # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version.
+
service = self.load_or_create(hash=hash)
- if not service.chain_data.user_params.use_staking:
+
+ if chain_id is None:
+ chain_id = service.home_chain_id
+
+ chain_config = service.chain_configs[chain_id]
+ ledger_config = chain_config.ledger_config
+ chain_data = chain_config.chain_data
+ ocm = self.get_on_chain_manager(ledger_config=ledger_config)
+ if not chain_data.user_params.use_staking:
self.logger.info("Cannot unstake service, `use_staking` is set to false")
return
- ocm = self.get_on_chain_manager(service=service)
state = ocm.staking_status(
- service_id=service.chain_data.token,
- staking_contract=STAKING[service.ledger_config.chain],
- )
- self.logger.info(
- f"Staking status for service {service.chain_data.token}: {state}"
+ service_id=chain_data.token,
+ staking_contract=STAKING[ledger_config.chain], # type: ignore # TODO fix mypy
)
+ self.logger.info(f"Staking status for service {chain_data.token}: {state}")
if state not in {StakingState.STAKED, StakingState.EVICTED}:
self.logger.info("Cannot unstake service, it's not staked")
- service.chain_data.staked = False
+ chain_data.staked = False
service.store()
return
- self.logger.info(f"Unstaking service: {service.chain_data.token}")
+ self.logger.info(f"Unstaking service: {chain_data.token}")
ocm.unstake(
- service_id=service.chain_data.token,
- staking_contract=STAKING[service.ledger_config.chain],
+ service_id=chain_data.token,
+ staking_contract=STAKING[ledger_config.chain], # type: ignore # TODO fix mypy
)
- service.chain_data.staked = False
+ chain_data.staked = False
service.store()
- def unstake_service_on_chain_from_safe(self, hash: str, chain_id: str, staking_program_id: str) -> None:
+ def unstake_service_on_chain_from_safe(
+ self, hash: str, chain_id: str, staking_program_id: t.Optional[str] = None
+ ) -> None:
"""
Unbond service on-chain
@@ -1020,6 +1220,12 @@ def unstake_service_on_chain_from_safe(self, hash: str, chain_id: str, staking_p
ledger_config = chain_config.ledger_config
chain_data = chain_config.chain_data
+ if staking_program_id is None:
+ self.logger.info(
+ "Cannot unstake service, `staking_program_id` is set to None"
+ )
+ return
+
if not chain_data.user_params.use_staking:
self.logger.info("Cannot unstake service, `use_staking` is set to false")
return
@@ -1029,9 +1235,7 @@ def unstake_service_on_chain_from_safe(self, hash: str, chain_id: str, staking_p
service_id=chain_data.token,
staking_contract=STAKING[ledger_config.chain][staking_program_id],
)
- self.logger.info(
- f"Staking status for service {chain_data.token}: {state}"
- )
+ self.logger.info(f"Staking status for service {chain_data.token}: {state}")
if state not in {StakingState.STAKED, StakingState.EVICTED}:
self.logger.info("Cannot unstake service, it's not staked")
chain_data.staked = False
@@ -1048,7 +1252,7 @@ def unstake_service_on_chain_from_safe(self, hash: str, chain_id: str, staking_p
chain_data.staked = False
service.store()
- def fund_service( # pylint: disable=too-many-arguments
+ def fund_service( # pylint: disable=too-many-arguments,too-many-locals
self,
hash: str,
rpc: t.Optional[str] = None,
@@ -1065,10 +1269,11 @@ def fund_service( # pylint: disable=too-many-arguments
ledger_config = chain_config.ledger_config
chain_data = chain_config.chain_data
wallet = self.wallet_manager.load(ledger_config.type)
- ledger_api = wallet.ledger_api(chain_type=ledger_config.chain, rpc=rpc if rpc else ledger_config.rpc)
+ ledger_api = wallet.ledger_api(
+ chain_type=ledger_config.chain, rpc=rpc if rpc else ledger_config.rpc
+ )
agent_fund_threshold = (
- agent_fund_threshold
- or chain_data.user_params.fund_requirements.agent
+ agent_fund_threshold or chain_data.user_params.fund_requirements.agent
)
for key in service.keys:
@@ -1078,8 +1283,7 @@ def fund_service( # pylint: disable=too-many-arguments
if agent_balance < agent_fund_threshold:
self.logger.info("Funding agents")
to_transfer = (
- agent_topup
- or chain_data.user_params.fund_requirements.agent
+ agent_topup or chain_data.user_params.fund_requirements.agent
)
self.logger.info(f"Transferring {to_transfer} units to {key.address}")
wallet.transfer(
@@ -1097,9 +1301,7 @@ def fund_service( # pylint: disable=too-many-arguments
self.logger.info(f"Required balance: {safe_fund_treshold}")
if safe_balance < safe_fund_treshold:
self.logger.info("Funding safe")
- to_transfer = (
- safe_topup or chain_data.user_params.fund_requirements.safe
- )
+ to_transfer = safe_topup or chain_data.user_params.fund_requirements.safe
self.logger.info(
f"Transferring {to_transfer} units to {chain_data.multisig}"
)
@@ -1177,25 +1379,28 @@ def update_service(
"""Update a service."""
self.logger.info("-----Entering update local service-----")
- old_service = self.load_or_create(
- hash=old_hash
- )
+ old_service = self.load_or_create(hash=old_hash)
new_service = self.load_or_create(
- hash=new_hash,
- service_template=service_template
+ hash=new_hash, service_template=service_template
)
new_service.keys = old_service.keys
- # new_Service.home_chain_id = old_service.home_chain_id
- # TODO - Ensure this works as expected - New service must copy all chain_data from old service,
- # but if service_template is not None, it must copy the user_params
- # passed in the service_template and copy the remaining attributes from old_service.
+ # TODO Ensure this is as intended.
+ new_service.home_chain_id = old_service.home_chain_id
+ # new_service must copy all chain_data from old_service.
+ # Additionally, if service_template is not None, it must overwrite
+ # the user_params on all chain_data by the values passed through the
+ # service_template.
new_service.chain_configs = {}
for chain_id, config in old_service.chain_configs.items():
- new_service.chain_configs[chain_id] = config
+ new_service.chain_configs[chain_id] = config
if service_template:
- new_service.chain_configs[chain_id].chain_data.user_params = OnChainUserParams.from_json(service_template["configurations"][chain_id])
+ new_service.chain_configs[
+ chain_id
+ ].chain_data.user_params = OnChainUserParams.from_json(
+ service_template["configurations"][chain_id] # type: ignore # TODO fix mypy
+ )
new_service.store()
diff --git a/operate/services/protocol.py b/operate/services/protocol.py
index 120355c94..5b122bb7e 100644
--- a/operate/services/protocol.py
+++ b/operate/services/protocol.py
@@ -242,8 +242,9 @@ def service_info(self, staking_contract: str, service_id: int) -> dict:
staking_contract,
service_id,
).get("data")
-
+
def agent_ids(self, staking_contract: str) -> t.List[int]:
+ """Get the agent IDs for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -251,6 +252,7 @@ def agent_ids(self, staking_contract: str) -> t.List[int]:
return instance.functions.getAgentIds().call()
def service_registry(self, staking_contract: str) -> str:
+ """Get the service registry address for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -258,6 +260,7 @@ def service_registry(self, staking_contract: str) -> str:
return instance.functions.serviceRegistry().call()
def staking_token(self, staking_contract: str) -> str:
+ """Get the staking token address for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -265,6 +268,7 @@ def staking_token(self, staking_contract: str) -> str:
return instance.functions.stakingToken().call()
def service_registry_token_utility(self, staking_contract: str) -> str:
+ """Get the service registry token utility address for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -272,6 +276,7 @@ def service_registry_token_utility(self, staking_contract: str) -> str:
return instance.functions.serviceRegistryTokenUtility().call()
def min_staking_deposit(self, staking_contract: str) -> str:
+ """Get the minimum staking deposit for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -279,6 +284,7 @@ def min_staking_deposit(self, staking_contract: str) -> str:
return instance.functions.minStakingDeposit().call()
def activity_checker(self, staking_contract: str) -> str:
+ """Get the activity checker address for the specified staking contract"""
instance = self.staking_ctr.get_instance(
ledger_api=self.ledger_api,
contract_address=staking_contract,
@@ -529,14 +535,6 @@ def ledger_api(self) -> LedgerApi:
)
return ledger_api
- def owner_of(self, token_id: int) -> str:
- """Get owner of a service."""
- self._patch()
- ledger_api, _ = OnChainHelper.get_ledger_and_crypto_objects(
- chain_type=self.chain_type
- )
-
-
def info(self, token_id: int) -> t.Dict:
"""Get service info."""
self._patch()
@@ -574,7 +572,6 @@ def info(self, token_id: int) -> t.Dict:
instances=instances,
)
-
def get_service_safe_owners(self, service_id: int) -> t.List[str]:
"""Get list of owners."""
ledger_api, _ = OnChainHelper.get_ledger_and_crypto_objects(
@@ -599,13 +596,8 @@ def get_service_safe_owners(self, service_id: int) -> t.List[str]:
contract_address=multisig_address,
).get("owners", [])
-
def swap( # pylint: disable=too-many-arguments,too-many-locals
- self,
- service_id: int,
- multisig: str,
- owner_key: str,
- new_owner_address: str
+ self, service_id: int, multisig: str, owner_key: str, new_owner_address: str
) -> None:
"""Swap safe owner."""
logging.info(f"Swapping safe for service {service_id} [{multisig}]...")
@@ -634,9 +626,7 @@ def swap( # pylint: disable=too-many-arguments,too-many-locals
ledger_api=manager.ledger_api,
contract_address=multisig,
old_owner=manager.ledger_api.api.to_checksum_address(owner_to_swap),
- new_owner=manager.ledger_api.api.to_checksum_address(
- new_owner_address
- ),
+ new_owner=manager.ledger_api.api.to_checksum_address(new_owner_address),
).get("data")
multisend_txs.append(
{
@@ -720,6 +710,55 @@ def staking_rewards_available(self, staking_contract: str) -> bool:
)
return available_rewards > 0
+ def staking_status(self, service_id: int, staking_contract: str) -> StakingState:
+ """Stake the service"""
+ self._patch()
+ return StakingManager(
+ key=self.wallet.key_path,
+ password=self.wallet.password,
+ chain_type=self.chain_type,
+ ).status(
+ service_id=service_id,
+ staking_contract=staking_contract,
+ )
+
+ def get_staking_params(self, staking_contract: str) -> t.Dict:
+ """Get agent IDs for the staking contract"""
+ self._patch()
+ staking_manager = StakingManager(
+ key=self.wallet.key_path,
+ password=self.wallet.password,
+ chain_type=self.chain_type,
+ )
+ agent_ids = staking_manager.agent_ids(
+ staking_contract=staking_contract,
+ )
+ service_registry = staking_manager.service_registry(
+ staking_contract=staking_contract,
+ )
+ staking_token = staking_manager.staking_token(
+ staking_contract=staking_contract,
+ )
+ service_registry_token_utility = staking_manager.service_registry_token_utility(
+ staking_contract=staking_contract,
+ )
+ min_staking_deposit = staking_manager.min_staking_deposit(
+ staking_contract=staking_contract,
+ )
+ activity_checker = staking_manager.activity_checker(
+ staking_contract=staking_contract,
+ )
+
+ return dict(
+ agent_ids=agent_ids,
+ service_registry=service_registry,
+ staking_token=staking_token,
+ service_registry_token_utility=service_registry_token_utility,
+ min_staking_deposit=min_staking_deposit,
+ activity_checker=activity_checker,
+ )
+
+
class OnChainManager(_ChainUtil):
"""On chain service management."""
@@ -974,7 +1013,7 @@ def get_mint_tx_data( # pylint: disable=too-many-arguments
)
.load_metadata()
.verify_nft(nft=nft)
- #.verify_service_dependencies(agent_id=agent_id) # TODO add this check once subgraph production indexes agent 25
+ .verify_service_dependencies(agent_id=agent_id)
.publish_metadata()
)
instance = registry_contracts.service_manager.get_instance(
@@ -1003,9 +1042,9 @@ def get_mint_tx_data( # pylint: disable=too-many-arguments
[agent_id],
[[number_of_slots, cost_of_bond]],
threshold,
- update_token
+ update_token,
],
- )
+ )
return {
"to": self.contracts["service_manager"],
@@ -1139,7 +1178,11 @@ def get_deploy_data_from_safe(
)
approve_hash_message = None
if reuse_multisig:
- _deployment_payload, approve_hash_message, error = get_reuse_multisig_from_safe_payload(
+ (
+ _deployment_payload,
+ approve_hash_message,
+ error,
+ ) = get_reuse_multisig_from_safe_payload(
ledger_api=self.ledger_api,
chain_type=self.chain_type,
service_id=service_id,
@@ -1289,54 +1332,6 @@ def staking_slots_available(self, staking_contract: str) -> bool:
staking_contract=staking_contract,
)
- def staking_status(self, service_id: int, staking_contract: str) -> StakingState:
- """Stake the service"""
- self._patch()
- return StakingManager(
- key=self.wallet.key_path,
- password=self.wallet.password,
- chain_type=self.chain_type,
- ).status(
- service_id=service_id,
- staking_contract=staking_contract,
- )
-
- def get_staking_params(self, staking_contract: str) -> t.Dict:
- """Get agent IDs for the staking contract"""
- self._patch()
- staking_manager = StakingManager(
- key=self.wallet.key_path,
- password=self.wallet.password,
- chain_type=self.chain_type,
- )
- agent_ids = staking_manager.agent_ids(
- staking_contract=staking_contract,
- )
- service_registry = staking_manager.service_registry(
- staking_contract=staking_contract,
- )
- staking_token = staking_manager.staking_token(
- staking_contract=staking_contract,
- )
- service_registry_token_utility = staking_manager.service_registry_token_utility(
- staking_contract=staking_contract,
- )
- min_staking_deposit = staking_manager.min_staking_deposit(
- staking_contract=staking_contract,
- )
- activity_checker = staking_manager.activity_checker(
- staking_contract=staking_contract,
- )
-
- return dict(
- agent_ids=agent_ids,
- service_registry=service_registry,
- staking_token=staking_token,
- service_registry_token_utility=service_registry_token_utility,
- min_staking_deposit=min_staking_deposit,
- activity_checker=activity_checker
- )
-
def can_unstake(self, service_id: int, staking_contract: str) -> bool:
"""Can unstake the service?"""
try:
@@ -1358,26 +1353,25 @@ def get_swap_data(self, service_id: int, multisig: str, owner_key: str) -> t.Dic
raise NotImplementedError()
-
def get_packed_signature_for_approved_hash(owners: t.Tuple[str]) -> bytes:
- """Get the packed signatures."""
- sorted_owners = sorted(owners, key=str.lower)
- signatures = b''
- for owner in sorted_owners:
- # Convert address to bytes and ensure it is 32 bytes long (left-padded with zeros)
- r_bytes = to_bytes(hexstr=owner[2:].rjust(64, '0'))
+ """Get the packed signatures."""
+ sorted_owners = sorted(owners, key=str.lower)
+ signatures = b""
+ for owner in sorted_owners:
+ # Convert address to bytes and ensure it is 32 bytes long (left-padded with zeros)
+ r_bytes = to_bytes(hexstr=owner[2:].rjust(64, "0"))
- # `s` as 32 zero bytes
- s_bytes = b'\x00' * 32
+ # `s` as 32 zero bytes
+ s_bytes = b"\x00" * 32
- # `v` as a single byte
- v_bytes = to_bytes(1)
+ # `v` as a single byte
+ v_bytes = to_bytes(1)
- # Concatenate r, s, and v to form the packed signature
- packed_signature = r_bytes + s_bytes + v_bytes
- signatures += packed_signature
+ # Concatenate r, s, and v to form the packed signature
+ packed_signature = r_bytes + s_bytes + v_bytes
+ signatures += packed_signature
- return signatures
+ return signatures
def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
@@ -1477,7 +1471,7 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
contract_address=multisend_address,
txs=txs,
)
- signature_bytes = get_packed_signature_for_approved_hash(owners=(master_safe, ))
+ signature_bytes = get_packed_signature_for_approved_hash(owners=(master_safe,))
safe_tx_hash = registry_contracts.gnosis_safe.get_raw_safe_transaction_hash(
ledger_api=ledger_api,
@@ -1517,4 +1511,3 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
)
payload = multisig_address + safe_exec_data[2:]
return payload, approve_hash_message, None
-
diff --git a/operate/services/service.py b/operate/services/service.py
index ea6c563ca..7e115f5ff 100644
--- a/operate/services/service.py
+++ b/operate/services/service.py
@@ -82,12 +82,13 @@
)
+# pylint: disable=no-member,redefined-builtin,too-many-instance-attributes
+
SAFE_CONTRACT_ADDRESS = "safe_contract_address"
ALL_PARTICIPANTS = "all_participants"
CONSENSUS_THRESHOLD = "consensus_threshold"
DELETE_PREFIX = "delete_"
-
-# pylint: disable=no-member,redefined-builtin,too-many-instance-attributes
+SERVICE_CONFIG_VERSION = 2
DUMMY_MULTISIG = "0xm"
NON_EXISTENT_TOKEN = -1
@@ -238,7 +239,7 @@ def __init__(self, path: Path) -> None:
self.path = path
self.config = load_service_config(service_path=path)
- def ledger_configs(self) -> "LedgerConfigs":
+ def ledger_configs(self) -> LedgerConfigs:
"""Get ledger configs."""
ledger_configs = {}
for override in self.config.overrides:
@@ -416,7 +417,10 @@ def _build_docker(
builder.deplopyment_type = DockerComposeGenerator.deployment_type
builder.try_update_abci_connection_params()
- home_chain_data = service.chain_configs[service.home_chain_id]
+ home_chain_data = service.chain_configs[service.home_chain_id].chain_data
+ home_chain_ledger_config = service.chain_configs[
+ service.home_chain_id
+ ].ledger_config
builder.try_update_runtime_params(
multisig_address=home_chain_data.multisig,
agent_instances=home_chain_data.instances,
@@ -425,8 +429,8 @@ def _build_docker(
)
# TODO: Support for multiledger
builder.try_update_ledger_params(
- chain=LedgerType(service.ledger_config.type).name.lower(),
- address=service.ledger_config.rpc,
+ chain=LedgerType(home_chain_ledger_config.type).name.lower(),
+ address=home_chain_ledger_config.rpc,
)
# build deployment
@@ -656,15 +660,19 @@ class Service(LocalResource):
@classmethod
def migrate_format(cls, path: Path) -> None:
"""Migrate the JSON file format if needed."""
- file_path = path / Service._file if Service._file is not None and path.name != Service._file else path
-
- with open(file_path, 'r', encoding='utf-8') as file:
+ file_path = (
+ path / Service._file
+ if Service._file is not None and path.name != Service._file
+ else path
+ )
+
+ with open(file_path, "r", encoding="utf-8") as file:
data = json.load(file)
-
- if 'version' in data:
+
+ if "version" in data:
# Data is already in the new format
return
-
+
# Migrate from old format to new format
new_data = {
"version": 2,
@@ -676,30 +684,42 @@ def migrate_format(cls, path: Path) -> None:
"ledger_config": {
"rpc": data.get("ledger_config", {}).get("rpc"),
"type": data.get("ledger_config", {}).get("type"),
- "chain": data.get("ledger_config", {}).get("chain")
+ "chain": data.get("ledger_config", {}).get("chain"),
},
"chain_data": {
"instances": data.get("chain_data", {}).get("instances", []),
"token": data.get("chain_data", {}).get("token"),
"multisig": data.get("chain_data", {}).get("multisig"),
"staked": data.get("chain_data", {}).get("staked", False),
- "on_chain_state": data.get("chain_data", {}).get("on_chain_state", 3),
+ "on_chain_state": data.get("chain_data", {}).get(
+ "on_chain_state", 3
+ ),
"user_params": {
"staking_program_id": "pearl_alpha",
- "nft": data.get("chain_data", {}).get("user_params", {}).get("nft"),
- "threshold": data.get("chain_data", {}).get("user_params", {}).get("threshold"),
- "use_staking": data.get("chain_data", {}).get("user_params", {}).get("use_staking"),
- "cost_of_bond": data.get("chain_data", {}).get("user_params", {}).get("cost_of_bond"),
- "fund_requirements": data.get("chain_data", {}).get("user_params", {}).get("fund_requirements", {})
- }
- }
+ "nft": data.get("chain_data", {})
+ .get("user_params", {})
+ .get("nft"),
+ "threshold": data.get("chain_data", {})
+ .get("user_params", {})
+ .get("threshold"),
+ "use_staking": data.get("chain_data", {})
+ .get("user_params", {})
+ .get("use_staking"),
+ "cost_of_bond": data.get("chain_data", {})
+ .get("user_params", {})
+ .get("cost_of_bond"),
+ "fund_requirements": data.get("chain_data", {})
+ .get("user_params", {})
+ .get("fund_requirements", {}),
+ },
+ },
}
},
"service_path": data.get("service_path", ""),
- "name": data.get("name", "")
+ "name": data.get("name", ""),
}
-
- with open(file_path, 'w', encoding='utf-8') as file:
+
+ with open(file_path, "w", encoding="utf-8") as file:
json.dump(new_data, file, indent=2)
@classmethod
@@ -725,7 +745,7 @@ def deployment(self) -> Deployment:
return t.cast(Deployment, self._deployment)
@staticmethod
- def new(
+ def new( # pylint: disable=too-many-locals
hash: str,
keys: Keys,
service_template: ServiceTemplate,
@@ -756,7 +776,7 @@ def new(
multisig=DUMMY_MULTISIG,
staked=False,
on_chain_state=OnChainState.NON_EXISTENT,
- user_params=OnChainUserParams.from_json(config),
+ user_params=OnChainUserParams.from_json(config), # type: ignore
)
chain_configs[chain] = ChainConfig(
@@ -765,7 +785,7 @@ def new(
)
service = Service(
- version=2, # TODO implement in appropriate place
+ version=SERVICE_CONFIG_VERSION,
name=service_yaml["author"] + "/" + service_yaml["name"],
hash=service_template["hash"],
keys=keys,
@@ -777,11 +797,16 @@ def new(
service.store()
return service
- def update_user_params_from_template(self, service_template: ServiceTemplate):
+ def update_user_params_from_template(
+ self, service_template: ServiceTemplate
+ ) -> None:
"""Update user params from template."""
for chain, config in service_template["configurations"].items():
- for chain, config in service_template["configurations"].items():
- self.chain_configs[chain].chain_data.user_params = OnChainUserParams.from_json(config)
+ self.chain_configs[
+ chain
+ ].chain_data.user_params = OnChainUserParams.from_json(
+ config # type: ignore
+ )
self.store()
diff --git a/package.json b/package.json
index b50e8fe5f..f940d09a7 100644
--- a/package.json
+++ b/package.json
@@ -58,5 +58,5 @@
"download-binaries": "sh download_binaries.sh",
"build:pearl": "sh build_pearl.sh"
},
- "version": "0.1.0-rc117"
-}
+ "version": "0.1.0-rc120"
+}
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 3c4bce6cd..a02e08312 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "olas-operate-middleware"
-version = "0.1.0-rc117"
+version = "0.1.0-rc120"
description = ""
authors = ["David Vilela ", "Viraj Patel "]
readme = "README.md"
diff --git a/tox.ini b/tox.ini
index b7de2caf0..88426bd93 100644
--- a/tox.ini
+++ b/tox.ini
@@ -215,3 +215,6 @@ ignore_missing_imports = True
[mypy-psutil.*]
ignore_missing_imports = True
+
+[mypy-eth_utils.*]
+ignore_missing_imports = True