diff --git a/api.md b/api.md index 4ed49d22..7bcaec3e 100644 --- a/api.md +++ b/api.md @@ -203,9 +203,9 @@ Creates a master wallet for given chain type. If a wallet already exists for a g
Request -```js +```json { - "chain_type": ChainType, + "chain": Chain, } ``` @@ -240,7 +240,7 @@ Creates a Gnosis safe for given chain type. ```js { - "chain_type": ChainType, + "chain": Chain, } ``` @@ -295,7 +295,7 @@ Returns the list of existing service configurations. "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", @@ -324,7 +324,7 @@ Create a service configuration using a template. "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "image": "https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75", - "home_chain_id": "100", + "home_chain": "gnosis", "name": "valory/trader_omen_gnosis", "service_version": "v0.18.4" } @@ -342,7 +342,7 @@ Create a service configuration using a template. "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", @@ -369,7 +369,7 @@ Update all the service configurations whose Service Public ID match the Service "env_variables": {...}, "hash": "bafybeibpseosblmaw6sk6zsnic2kfxfsijrnfluuhkwboyqhx7ma7zw2me", "image": "https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75", - "home_chain_id": "100", + "home_chain": "gnosis", "name": "valory/trader_omen_gnosis", "service_version": "v0.19.0" } @@ -390,7 +390,7 @@ The response contains an array of the services which have been updated (an empty "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "1731490000": "bafybeibpseosblmaw6sk6zsnic2kfxfsijrnfluuhkwboyqhx7ma7zw2me"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", @@ -451,7 +451,7 @@ Returns the service configuration `service_config_id`. "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", @@ -498,7 +498,7 @@ The response contains the updated service configuration following the on-chain o "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", @@ -525,7 +525,7 @@ Update service configuration `service_config_id` with the provided template. "env_variables": {...}, "hash": "bafybeibpseosblmaw6sk6zsnic2kfxfsijrnfluuhkwboyqhx7ma7zw2me", "image": "https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75", - "home_chain_id": "100", + "home_chain": "gnosis", "name": "valory/trader_omen_gnosis", "service_version": "v0.19.0" } @@ -545,7 +545,7 @@ Update service configuration `service_config_id` with the provided template. "env_variables": {...}, "hash": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "hash_history": {"1731487112": "bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u", "1731490000": "bafybeibpseosblmaw6sk6zsnic2kfxfsijrnfluuhkwboyqhx7ma7zw2me"}, - "home_chain_id": "100", + "home_chain": "gnosis", "keys": [...], "name": "valory/trader_omen_gnosis", "service_config_id": "sc-85a7a12a-8c6b-46b8-919a-b8a3b8e3ad39", diff --git a/frontend/client/enums.ts b/frontend/client/enums.ts index 4c01dd39..e6e6316c 100644 --- a/frontend/client/enums.ts +++ b/frontend/client/enums.ts @@ -6,13 +6,13 @@ export enum MiddlewareAction { } export enum MiddlewareChain { - ETHEREUM = 0, - GOERLI = 1, - GNOSIS = 2, - SOLANA = 3, - OPTIMISM = 4, - BASE = 5, - MODE = 6, + ETHEREUM = "ethereum", + GOERLI = "goerli", + GNOSIS = "gnosis", + SOLANA = "solana", + OPTIMISM = "optimism", + BASE = "base", + MODE = "mode", } export enum MiddlewareLedger { diff --git a/frontend/client/types.ts b/frontend/client/types.ts index 770eb4e8..c9db25a6 100644 --- a/frontend/client/types.ts +++ b/frontend/client/types.ts @@ -18,7 +18,6 @@ export type ServiceKeys = { export type LedgerConfig = { rpc: string; - type: MiddlewareLedger; chain: MiddlewareChain; }; @@ -50,12 +49,12 @@ export type MiddlewareServiceResponse = { hash_history: { [block: string]: string; }; - home_chain_id: number; + home_chain: MiddlewareChain; keys: ServiceKeys[]; service_path?: string; version: string; chain_configs: { - [chainId: number]: { + [chain in MiddlewareChain]: { ledger_config: LedgerConfig; chain_data: ChainData; }; @@ -75,7 +74,7 @@ export type ServiceTemplate = { description: string; image: string; service_version: string; - home_chain_id: string; + home_chain: string; configurations: { [key: string]: ConfigurationTemplate }; env_variables: { [key: string]: EnvVariableAttributes }; deploy?: boolean; diff --git a/frontend/components/SetupPage/Create/SetupCreateSafe.tsx b/frontend/components/SetupPage/Create/SetupCreateSafe.tsx index 01988a14..caaefe08 100644 --- a/frontend/components/SetupPage/Create/SetupCreateSafe.tsx +++ b/frontend/components/SetupPage/Create/SetupCreateSafe.tsx @@ -16,10 +16,14 @@ import { delayInSeconds } from '@/utils/delay'; const { Text } = Typography; -const capitalizedMiddlewareChainNames = { - [+MiddlewareChain.ETHEREUM]: 'Ethereum', - [+MiddlewareChain.BASE]: 'Base', - [+MiddlewareChain.OPTIMISM]: 'Optimism', +const capitalizedMiddlewareChainNames: { [key in MiddlewareChain]: string } = { + [MiddlewareChain.ETHEREUM]: 'Ethereum', + [MiddlewareChain.BASE]: 'Base', + [MiddlewareChain.OPTIMISM]: 'Optimism', + [MiddlewareChain.GOERLI]: 'Goerli', + [MiddlewareChain.GNOSIS]: 'Gnosis', + [MiddlewareChain.SOLANA]: 'Solana', + [MiddlewareChain.MODE]: 'Mode', }; export const SetupCreateSafe = () => { @@ -129,7 +133,7 @@ export const SetupCreateSafe = () => { const safeCreationsRequired = Object.entries(chainsToCreateSafesFor).reduce( (acc, [chain, safeAddressAlreadyExists]) => { - const middlewareChain = +chain as MiddlewareChain; + const middlewareChain = chain as MiddlewareChain; if (safeAddressAlreadyExists) { switch (middlewareChain) { case MiddlewareChain.OPTIMISM: diff --git a/frontend/components/SetupPage/Create/SetupEoaFunding.tsx b/frontend/components/SetupPage/Create/SetupEoaFunding.tsx index 9b566ca3..721552d3 100644 --- a/frontend/components/SetupPage/Create/SetupEoaFunding.tsx +++ b/frontend/components/SetupPage/Create/SetupEoaFunding.tsx @@ -173,11 +173,11 @@ export const SetupEoaFunding = () => { const { goto } = useSetup(); const { masterEoaAddress } = useWallet(); const [currentChain, setCurrentChain] = useState( - +Object.keys(eoaFundingMap)[0] as MiddlewareChain, + Object.keys(eoaFundingMap)[0] as MiddlewareChain, ); const currentFundingMapObject = - eoaFundingMap[+currentChain as keyof typeof eoaFundingMap]; + eoaFundingMap[currentChain as keyof typeof eoaFundingMap]; const getIsCurrentChainFunded = useCallback( async ( @@ -218,7 +218,7 @@ export const SetupEoaFunding = () => { if (nextChainExists) { // goto next chain setCurrentChain( - +Object.keys(eoaFundingMap)[indexOfCurrentChain + 1] as MiddlewareChain, + Object.keys(eoaFundingMap)[indexOfCurrentChain + 1] as MiddlewareChain, ); return; } diff --git a/frontend/components/YourWalletPage/YourAgent.tsx b/frontend/components/YourWalletPage/YourAgent.tsx index 8155d6bd..cbec1163 100644 --- a/frontend/components/YourWalletPage/YourAgent.tsx +++ b/frontend/components/YourWalletPage/YourAgent.tsx @@ -109,7 +109,7 @@ const AgentTitle = () => { const ServiceAndNftDetails = () => { const { serviceId } = useServices(); const serviceAddress = - SERVICE_REGISTRY_L2_CONTRACT_ADDRESS[`${MiddlewareChain.OPTIMISM}`]; + SERVICE_REGISTRY_L2_CONTRACT_ADDRESS[MiddlewareChain.OPTIMISM]; return ( diff --git a/frontend/config/chains.ts b/frontend/config/chains.ts index 3e318814..a38fc47f 100644 --- a/frontend/config/chains.ts +++ b/frontend/config/chains.ts @@ -12,7 +12,7 @@ type ChainConfig = { name: string; currency: string; chainId: number; - middlewareChain: number; + middlewareChain: MiddlewareChainId; rpc: HttpUrl; }; diff --git a/frontend/constants/serviceTemplates.ts b/frontend/constants/serviceTemplates.ts index 1204df06..0b500f71 100644 --- a/frontend/constants/serviceTemplates.ts +++ b/frontend/constants/serviceTemplates.ts @@ -1,4 +1,5 @@ import { ServiceTemplate, EnvProvisionType } from '@/client'; +import { CHAIN_CONFIG } from '@/config/chains'; import { ChainId } from '@/enums/Chain'; import { StakingProgramId } from '@/enums/StakingProgram'; @@ -10,9 +11,9 @@ export const SERVICE_TEMPLATES: ServiceTemplate[] = [ image: 'https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75', service_version: 'v0.18.4', - home_chain_id: ChainId.Gnosis.toString(), + home_chain: CHAIN_CONFIG[ChainId.Gnosis].middlewareChain, configurations: { - [ChainId.Optimism]: { + [CHAIN_CONFIG[ChainId.Optimism].middlewareChain]: { staking_program_id: StakingProgramId.OptimusAlpha, // default, may be overwritten nft: 'bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq', rpc: 'http://localhost:8545', @@ -105,9 +106,9 @@ export const SERVICE_TEMPLATES: ServiceTemplate[] = [ // image: // 'https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75', // service_version: 'v0.2.9', - // home_chain_id: `${CHAINS.OPTIMISM.chainId}`, + // home_chain: `${CHAINS.OPTIMISM}`, // configurations: { - // [CHAINS.OPTIMISM.chainId]: { + // [CHAINS.OPTIMISM.middlewareChain]: { // staking_program_id: StakingProgramId.OptimusAlpha, // default, may be overwritten // nft: 'bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq', // // rpc: 'http://localhost:8545', @@ -122,7 +123,7 @@ export const SERVICE_TEMPLATES: ServiceTemplate[] = [ // safe: 1000, // }, // }, - // [CHAINS.ETHEREUM.chainId]: { + // [CHAINS.ETHEREUM.middlewareChain]: { // staking_program_id: StakingProgramId.OptimusAlpha, // default, may be overwritten // nft: 'bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq', // // rpc: 'http://localhost:8545', @@ -137,7 +138,7 @@ export const SERVICE_TEMPLATES: ServiceTemplate[] = [ // safe: 1000, // }, // }, - // [CHAINS.BASE.chainId]: { + // [CHAINS.BASE.middlewareChain]: { // staking_program_id: StakingProgramId.OptimusAlpha, // default, may be overwritten // nft: 'bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq', // // rpc: 'http://localhost:8545', diff --git a/frontend/context/BalanceProvider.tsx b/frontend/context/BalanceProvider.tsx index c3fd0da4..2c573aad 100644 --- a/frontend/context/BalanceProvider.tsx +++ b/frontend/context/BalanceProvider.tsx @@ -63,7 +63,7 @@ export const BalanceContext = createContext<{ optimismBalance?: number; }>({ isLoaded: false, - setIsLoaded: () => {}, + setIsLoaded: () => { }, isBalanceLoaded: false, olasBondBalance: undefined, olasDepositBalance: undefined, @@ -76,8 +76,8 @@ export const BalanceContext = createContext<{ walletBalances: {}, agentSafeBalance: undefined, agentEoaBalance: undefined, - updateBalances: async () => {}, - setIsPaused: () => {}, + updateBalances: async () => { }, + setIsPaused: () => { }, totalOlasStakedBalance: undefined, baseBalance: undefined, ethereumBalance: undefined, @@ -171,7 +171,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { // TODO: refactor to use ChainId enum, service from useService(), const serviceId = - services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data + services?.[0]?.chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data .token; if (!isNumber(serviceId)) { @@ -232,7 +232,7 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { const agentEoaAddress = useMemo( () => - services?.[0]?.chain_configs?.[CHAIN_CONFIG.OPTIMISM.chainId]?.chain_data + services?.[0]?.chain_configs?.[CHAIN_CONFIG[ChainId.Optimism].middlewareChain]?.chain_data ?.instances?.[0], [services], ); @@ -249,11 +249,11 @@ export const BalanceProvider = ({ children }: PropsWithChildren) => { const agentSafeBalance = useMemo( () => - services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data + services?.[0]?.chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data ?.multisig && walletBalances[ - services[0].chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data - .multisig! + services[0].chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data + .multisig! ], [services, walletBalances], ); diff --git a/frontend/context/StakingContractInfoProvider.tsx b/frontend/context/StakingContractInfoProvider.tsx index e023be78..17dd4ce5 100644 --- a/frontend/context/StakingContractInfoProvider.tsx +++ b/frontend/context/StakingContractInfoProvider.tsx @@ -22,6 +22,7 @@ import { DEFAULT_STAKING_PROGRAM_ID, StakingProgramContext, } from './StakingProgramProvider'; +import { ChainId } from '@/enums/Chain'; type StakingContractInfoContextProps = { activeStakingContractInfo?: Partial; @@ -43,8 +44,8 @@ export const StakingContractInfoContext = isStakingContractInfoRecordLoaded: false, isActiveStakingContractInfoLoaded: false, stakingContractInfoRecord: undefined, - updateActiveStakingContractInfo: async () => {}, - setIsPaused: () => {}, + updateActiveStakingContractInfo: async () => { }, + setIsPaused: () => { }, }); export const StakingContractInfoProvider = ({ @@ -71,7 +72,7 @@ export const StakingContractInfoProvider = ({ const serviceId = useMemo( () => - services?.[0]?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data?.token, + services?.[0]?.chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data?.token, [services], ); diff --git a/frontend/context/StakingProgramProvider.tsx b/frontend/context/StakingProgramProvider.tsx index dcb78765..3371c2f1 100644 --- a/frontend/context/StakingProgramProvider.tsx +++ b/frontend/context/StakingProgramProvider.tsx @@ -11,6 +11,7 @@ import { CHAIN_CONFIG } from '@/config/chains'; import { StakingProgramId } from '@/enums/StakingProgram'; import { useServices } from '@/hooks/useServices'; import { AutonolasService } from '@/service/Autonolas'; +import { ChainId } from '@/enums/Chain'; export const INITIAL_DEFAULT_STAKING_PROGRAM_ID = StakingProgramId.Beta; @@ -22,8 +23,8 @@ export const StakingProgramContext = createContext<{ }>({ activeStakingProgramId: undefined, defaultStakingProgramId: INITIAL_DEFAULT_STAKING_PROGRAM_ID, - updateActiveStakingProgramId: async () => {}, - setDefaultStakingProgramId: () => {}, + updateActiveStakingProgramId: async () => { }, + setDefaultStakingProgramId: () => { }, }); /** Determines the current active staking program, if any */ @@ -40,10 +41,10 @@ export const StakingProgramProvider = ({ children }: PropsWithChildren) => { const updateActiveStakingProgramId = useCallback(async () => { // if no service nft, not staked const serviceId = - service?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data?.token; + service?.chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data?.token; if ( - !service?.chain_configs[CHAIN_CONFIG.OPTIMISM.chainId].chain_data?.token + !service?.chain_configs[CHAIN_CONFIG[ChainId.Optimism].middlewareChain].chain_data?.token ) { setActiveStakingProgramId(null); return; diff --git a/frontend/hooks/useAddress.ts b/frontend/hooks/useAddress.ts index d729957d..e7895909 100644 --- a/frontend/hooks/useAddress.ts +++ b/frontend/hooks/useAddress.ts @@ -1,17 +1,18 @@ import { CHAIN_CONFIG } from '@/config/chains'; import { useServices } from './useServices'; +import { ChainId } from '@/enums/Chain'; export const useAddress = () => { const { service } = useServices(); /** agent safe multisig address */ const multisigAddress = - service?.chain_configs?.[CHAIN_CONFIG.OPTIMISM.chainId]?.chain_data?.multisig; + service?.chain_configs?.[CHAIN_CONFIG[ChainId.Optimism].middlewareChain]?.chain_data?.multisig; /** agent instance EOA address */ const instanceAddress = - service?.chain_configs?.[CHAIN_CONFIG.OPTIMISM.chainId]?.chain_data + service?.chain_configs?.[CHAIN_CONFIG[ChainId.Optimism].middlewareChain]?.chain_data ?.instances?.[0]; return { instanceAddress, multisigAddress }; diff --git a/frontend/hooks/useNeedsFunds.ts b/frontend/hooks/useNeedsFunds.ts index 6898ea77..4219897a 100644 --- a/frontend/hooks/useNeedsFunds.ts +++ b/frontend/hooks/useNeedsFunds.ts @@ -6,6 +6,7 @@ import { CHAIN_CONFIG } from '@/config/chains'; import { useBalance } from './useBalance'; import { useServiceTemplates } from './useServiceTemplates'; import { useStore } from './useStore'; +import { ChainId } from '@/enums/Chain'; export const useNeedsFunds = () => { const { getServiceTemplates } = useServiceTemplates(); @@ -26,7 +27,7 @@ export const useNeedsFunds = () => { const serviceFundRequirements = useMemo(() => { const gasEstimate = - serviceTemplate.configurations[CHAIN_CONFIG.OPTIMISM.chainId] + serviceTemplate.configurations[CHAIN_CONFIG[ChainId.Optimism].middlewareChain] .monthly_gas_estimate; const monthlyGasEstimate = Number(formatUnits(`${gasEstimate}`, 18)); const minimumStakedAmountRequired = diff --git a/frontend/service/Services.ts b/frontend/service/Services.ts index a80189c7..686aa31b 100644 --- a/frontend/service/Services.ts +++ b/frontend/service/Services.ts @@ -67,8 +67,8 @@ const createService = async ({ ...serviceTemplate, deploy, configurations: { - [chainId]: { - ...serviceTemplate.configurations[ChainId.Optimism], + [CHAIN_CONFIG[chainId].middlewareChain]: { + ...serviceTemplate.configurations[CHAIN_CONFIG[chainId].middlewareChain], staking_program_id: stakingProgramId, rpc: CHAIN_CONFIG[chainId].rpc, use_mech_marketplace: useMechMarketplace, @@ -109,8 +109,8 @@ const updateService = async ({ ...serviceTemplate, deploy, configurations: { - [chainId]: { - ...serviceTemplate.configurations[ChainId.Optimism], + [CHAIN_CONFIG[chainId].middlewareChain]: { + ...serviceTemplate.configurations[CHAIN_CONFIG[chainId].middlewareChain], staking_program_id: stakingProgramId, rpc: CHAIN_CONFIG[chainId].rpc, use_mech_marketplace: useMechMarketplace, diff --git a/frontend/service/Wallet.ts b/frontend/service/Wallet.ts index b9224ec9..42eb0efd 100644 --- a/frontend/service/Wallet.ts +++ b/frontend/service/Wallet.ts @@ -17,7 +17,7 @@ const createEoa = async (chain: MiddlewareChain) => headers: { ...CONTENT_TYPE_JSON_UTF8, }, - body: JSON.stringify({ chain_type: chain }), + body: JSON.stringify({ chain: chain }), }).then((res) => { if (res.ok) return res.json(); throw new Error('Failed to create EOA'); @@ -29,7 +29,7 @@ const createSafe = async (chain: MiddlewareChain, owner?: string) => headers: { ...CONTENT_TYPE_JSON_UTF8, }, - body: JSON.stringify({ chain_type: chain, owner: owner }), + body: JSON.stringify({ chain: chain, owner: owner }), }).then((res) => { if (res.ok) return res.json(); throw new Error('Failed to create safe'); @@ -41,7 +41,7 @@ const addBackupOwner = async (chain: MiddlewareChain, owner: string) => headers: { ...CONTENT_TYPE_JSON_UTF8, }, - body: JSON.stringify({ chain_type: chain, owner: owner }), + body: JSON.stringify({ chain: chain, owner: owner }), }).then((res) => { if (res.ok) return res.json(); throw new Error('Failed to add backup owner'); diff --git a/operate/cli.py b/operate/cli.py index cf5ac763..73ef0535 100644 --- a/operate/cli.py +++ b/operate/cli.py @@ -42,8 +42,7 @@ from operate import services from operate.account.user import UserAccount from operate.constants import KEY, KEYS, OPERATE, SERVICES -from operate.ledger import get_ledger_type_from_chain_type -from operate.operate_types import ChainType, DeploymentStatus +from operate.operate_types import Chain, DeploymentStatus, LedgerType from operate.services.health_checker import HealthChecker from operate.wallet.master import MasterWalletManager @@ -408,9 +407,7 @@ async def _get_wallets(request: Request) -> t.List[t.Dict]: @with_retries async def _get_wallet_by_chain(request: Request) -> t.List[t.Dict]: """Create wallet safe""" - ledger_type = get_ledger_type_from_chain_type( - chain=ChainType.from_string(request.path_params["chain"]) - ) + ledger_type = Chain.from_string(request.path_params["chain"]).ledger_type manager = operate.wallet_manager if not manager.exists(ledger_type=ledger_type): return JSONResponse( @@ -438,8 +435,7 @@ async def _create_wallet(request: Request) -> t.List[t.Dict]: ) data = await request.json() - chain_type = ChainType(data["chain_type"]) - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + ledger_type = LedgerType(data["ledger_type"]) manager = operate.wallet_manager if manager.exists(ledger_type=ledger_type): return JSONResponse( @@ -467,8 +463,8 @@ async def _get_safes(request: Request) -> t.List[t.Dict]: @with_retries async def _get_safe(request: Request) -> t.List[t.Dict]: """Create wallet safe""" - chain_type = ChainType.from_string(request.path_params["chain"]) - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + chain = Chain.from_string(request.path_params["chain"]) + ledger_type = chain.ledger_type manager = operate.wallet_manager if not manager.exists(ledger_type=ledger_type): return JSONResponse( @@ -476,12 +472,12 @@ async def _get_safe(request: Request) -> t.List[t.Dict]: status_code=404, ) safes = manager.load(ledger_type=ledger_type).safes - if safes is None or safes.get(chain_type) is None: + if safes is None or safes.get(chain) is None: return JSONResponse(content={"error": "No safes found"}) return JSONResponse( content={ - "safe": safes[chain_type], + "safe": safes[chain], }, ) @@ -502,34 +498,34 @@ async def _create_safe(request: Request) -> t.List[t.Dict]: ) data = await request.json() - chain_type = ChainType(data["chain_type"]) - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + chain = Chain(data["chain"]) + ledger_type = chain.ledger_type manager = operate.wallet_manager if not manager.exists(ledger_type=ledger_type): return JSONResponse(content={"error": "Wallet does not exist"}) wallet = manager.load(ledger_type=ledger_type) - if wallet.safes is not None and wallet.safes.get(chain_type) is not None: + if wallet.safes is not None and wallet.safes.get(chain) is not None: return JSONResponse( content={ - "safe": wallet.safes.get(chain_type), - "message": f"Safe already exists {chain_type=}.", + "safe": wallet.safes.get(chain), + "message": f"Safe already exists {chain=}.", } ) - safes = t.cast(t.Dict[ChainType, str], wallet.safes) + safes = t.cast(t.Dict[Chain, str], wallet.safes) wallet.create_safe( # pylint: disable=no-member - chain_type=chain_type, + chain=chain, owner=data.get("owner"), ) wallet.transfer( - to=t.cast(str, safes.get(chain_type)), + to=t.cast(str, safes.get(chain)), amount=int(1e18), - chain_type=chain_type, + chain=chain, from_safe=False, ) return JSONResponse( - content={"safe": safes.get(chain_type), "message": "Safe created!"} + content={"safe": safes.get(chain), "message": "Safe created!"} ) @app.post("/api/wallet/safes") @@ -549,37 +545,35 @@ async def _create_safes(request: Request) -> t.List[t.Dict]: ) data = await request.json() - chain_types = [ChainType(chain_type) for chain_type in data["chain_types"]] + chains = [Chain(chain_str) for chain_str in data["chains"]] # check that all chains are supported - for chain_type in chain_types: - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + for chain in chains: + ledger_type = chain.ledger_type manager = operate.wallet_manager if not manager.exists(ledger_type=ledger_type): return JSONResponse( - content={ - "error": f"Wallet does not exist for chain_type {chain_type}" - } + content={"error": f"Wallet does not exist for chain {chain}"} ) # mint the safes - for chain_type in chain_types: - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + for chain in chains: + ledger_type = chain.ledger_type manager = operate.wallet_manager wallet = manager.load(ledger_type=ledger_type) - if wallet.safes is not None and wallet.safes.get(chain_type) is not None: - logger.info(f"Safe already exists for chain_type {chain_type}") + if wallet.safes is not None and wallet.safes.get(chain) is not None: + logger.info(f"Safe already exists for chain {chain}") continue - safes = t.cast(t.Dict[ChainType, str], wallet.safes) + safes = t.cast(t.Dict[Chain, str], wallet.safes) wallet.create_safe( # pylint: disable=no-member - chain_type=chain_type, + chain=chain, owner=data.get("owner"), ) wallet.transfer( - to=t.cast(str, safes.get(chain_type)), + to=t.cast(str, safes.get(chain)), amount=int(1e18), - chain_type=chain_type, + chain=chain, from_safe=False, ) @@ -603,15 +597,15 @@ async def _update_safe(request: Request) -> t.List[t.Dict]: ) data = await request.json() - chain_type = ChainType(data["chain_type"]) - ledger_type = get_ledger_type_from_chain_type(chain=chain_type) + chain = Chain(data["chain"]) + ledger_type = chain.ledger_type manager = operate.wallet_manager if not manager.exists(ledger_type=ledger_type): return JSONResponse(content={"error": "Wallet does not exist"}) wallet = manager.load(ledger_type=ledger_type) wallet.add_or_swap_owner( - chain_type=chain_type, + chain=chain, owner=data.get("owner"), ) return JSONResponse(content=wallet.json) diff --git a/operate/ledger/__init__.py b/operate/ledger/__init__.py index 7238257f..76cbf012 100644 --- a/operate/ledger/__init__.py +++ b/operate/ledger/__init__.py @@ -25,7 +25,7 @@ from operate.ledger.base import LedgerHelper from operate.ledger.ethereum import Ethereum from operate.ledger.solana import Solana -from operate.operate_types import ChainType, LedgerType +from operate.operate_types import Chain, LedgerType ETHEREUM_PUBLIC_RPC = os.environ.get( @@ -55,33 +55,33 @@ MODE_RPC = os.environ.get("MODE_DEV_RPC", "https://rpc.mode.network") PUBLIC_RPCS = { - ChainType.ETHEREUM: ETHEREUM_PUBLIC_RPC, - ChainType.GNOSIS: GNOSIS_PUBLIC_RPC, - ChainType.GOERLI: GOERLI_PUBLIC_RPC, - ChainType.SOLANA: SOLANA_PUBLIC_RPC, - ChainType.BASE: BASE_PUBLIC_RPC, - ChainType.OPTIMISM: OPTIMISM_PUBLIC_RPC, - ChainType.MODE: MODE_PUBLIC_RPC, + Chain.ETHEREUM: ETHEREUM_PUBLIC_RPC, + Chain.GNOSIS: GNOSIS_PUBLIC_RPC, + Chain.GOERLI: GOERLI_PUBLIC_RPC, + Chain.SOLANA: SOLANA_PUBLIC_RPC, + Chain.BASE: BASE_PUBLIC_RPC, + Chain.OPTIMISTIC: OPTIMISM_PUBLIC_RPC, + Chain.MODE: MODE_PUBLIC_RPC, } DEFAULT_RPCS = { - ChainType.ETHEREUM: ETHEREUM_RPC, - ChainType.GNOSIS: GNOSIS_RPC, - ChainType.GOERLI: GOERLI_RPC, - ChainType.SOLANA: SOLANA_RPC, - ChainType.BASE: BASE_RPC, - ChainType.OPTIMISM: OPTIMISM_RPC, - ChainType.MODE: MODE_RPC, + Chain.ETHEREUM: ETHEREUM_RPC, + Chain.GNOSIS: GNOSIS_RPC, + Chain.GOERLI: GOERLI_RPC, + Chain.SOLANA: SOLANA_RPC, + Chain.BASE: BASE_RPC, + Chain.OPTIMISTIC: OPTIMISM_RPC, + Chain.MODE: MODE_RPC, } -CHAIN_HELPERS: t.Dict[ChainType, t.Type[LedgerHelper]] = { - ChainType.ETHEREUM: Ethereum, - ChainType.GNOSIS: Ethereum, - ChainType.GOERLI: Ethereum, - ChainType.SOLANA: Solana, - ChainType.BASE: Ethereum, - ChainType.OPTIMISM: Ethereum, - ChainType.MODE: Ethereum, +CHAIN_HELPERS: t.Dict[Chain, t.Type[LedgerHelper]] = { + Chain.ETHEREUM: Ethereum, + Chain.GNOSIS: Ethereum, + Chain.GOERLI: Ethereum, + Chain.SOLANA: Solana, + Chain.BASE: Ethereum, + Chain.OPTIMISTIC: Ethereum, + Chain.MODE: Ethereum, } LEDGER_HELPERS: t.Dict[LedgerType, t.Type[LedgerHelper]] = { @@ -90,36 +90,22 @@ } CURRENCY_DENOMS = { - ChainType.ETHEREUM: "Wei", - ChainType.GNOSIS: "xDai", - ChainType.GOERLI: "GWei", - ChainType.SOLANA: "Lamp", - ChainType.BASE: "Wei", - ChainType.OPTIMISM: "Wei", - ChainType.MODE: "Wei", + Chain.ETHEREUM: "Wei", + Chain.GNOSIS: "xDai", + Chain.GOERLI: "GWei", + Chain.SOLANA: "Lamp", + Chain.BASE: "Wei", + Chain.OPTIMISTIC: "Wei", + Chain.MODE: "Wei", } -def get_default_rpc(chain: ChainType) -> str: +def get_default_rpc(chain: Chain) -> str: """Get default RPC chain type.""" return DEFAULT_RPCS.get(chain, ETHEREUM_RPC) -def get_ledger_type_from_chain_type(chain: ChainType) -> LedgerType: - """Get LedgerType from ChainType.""" - if chain in ( - ChainType.ETHEREUM, - ChainType.GOERLI, - ChainType.GNOSIS, - ChainType.BASE, - ChainType.OPTIMISM, - ChainType.MODE, - ): - return LedgerType.ETHEREUM - return LedgerType.SOLANA - - -def get_ledger_helper_by_chain(rpc: str, chain: ChainType) -> LedgerHelper: +def get_ledger_helper_by_chain(rpc: str, chain: Chain) -> LedgerHelper: """Get ledger helper by chain type.""" return CHAIN_HELPERS.get(chain, Ethereum)(rpc=rpc) @@ -129,6 +115,6 @@ def get_ledger_helper_by_ledger(rpc: str, ledger: LedgerHelper) -> LedgerHelper: return LEDGER_HELPERS.get(ledger, Ethereum)(rpc=rpc) # type: ignore -def get_currency_denom(chain: ChainType) -> str: +def get_currency_denom(chain: Chain) -> str: """Get currency denom by chain type.""" return CURRENCY_DENOMS.get(chain, "Wei") diff --git a/operate/ledger/profiles.py b/operate/ledger/profiles.py index c1810de8..9d15b866 100644 --- a/operate/ledger/profiles.py +++ b/operate/ledger/profiles.py @@ -21,11 +21,11 @@ import typing as t -from operate.operate_types import ChainType, ContractAddresses +from operate.operate_types import Chain, ContractAddresses -CONTRACTS: t.Dict[ChainType, ContractAddresses] = { - ChainType.GNOSIS: ContractAddresses( +CONTRACTS: t.Dict[Chain, ContractAddresses] = { + Chain.GNOSIS: ContractAddresses( { "service_manager": "0x04b0007b2aFb398015B76e5f22993a1fddF83644", "service_registry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD", @@ -35,7 +35,7 @@ "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", } ), - ChainType.OPTIMISM: ContractAddresses( + Chain.OPTIMISTIC: ContractAddresses( { "service_manager": "0xFbBEc0C8b13B38a9aC0499694A69a10204c5E2aB", "service_registry": "0x3d77596beb0f130a4415df3D2D8232B3d3D31e44", @@ -45,7 +45,7 @@ "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", } ), - ChainType.ETHEREUM: ContractAddresses( + Chain.ETHEREUM: ContractAddresses( { "service_manager": "0x2EA682121f815FBcF86EA3F3CaFdd5d67F2dB143", "service_registry": "0x48b6af7B12C71f09e2fC8aF4855De4Ff54e775cA", @@ -55,7 +55,7 @@ "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", } ), - ChainType.BASE: ContractAddresses( + Chain.BASE: ContractAddresses( { "service_manager": "0x63e66d7ad413C01A7b49C7FF4e3Bb765C4E4bd1b", "service_registry": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE", @@ -65,7 +65,7 @@ "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", } ), - ChainType.MODE: ContractAddresses( + Chain.MODE: ContractAddresses( { "service_manager": "0x63e66d7ad413C01A7b49C7FF4e3Bb765C4E4bd1b", "service_registry": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE", @@ -77,8 +77,8 @@ ), } -STAKING: t.Dict[ChainType, t.Dict[str, str]] = { - ChainType.GNOSIS: { +STAKING: t.Dict[Chain, t.Dict[str, str]] = { + Chain.GNOSIS: { "pearl_alpha": "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A", "pearl_beta": "0xeF44Fb0842DDeF59D37f85D61A1eF492bbA6135d", "pearl_beta_2": "0x1c2F82413666d2a3fD8bC337b0268e62dDF67434", @@ -87,20 +87,20 @@ "pearl_beta_5": "0x4Abe376Fda28c2F43b84884E5f822eA775DeA9F4", "pearl_beta_mech_marketplace": "0xDaF34eC46298b53a3d24CBCb431E84eBd23927dA", }, - ChainType.OPTIMISM: { + Chain.OPTIMISTIC: { "optimus_alpha": "0x88996bbdE7f982D93214881756840cE2c77C4992", }, - ChainType.ETHEREUM: {}, - ChainType.BASE: {}, - ChainType.MODE: { + Chain.ETHEREUM: {}, + Chain.BASE: {}, + Chain.MODE: { "optimus_alpha": "0x5fc25f50E96857373C64dC0eDb1AbCBEd4587e91", }, } -OLAS: t.Dict[ChainType, str] = { - ChainType.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", - ChainType.OPTIMISM: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527", - ChainType.BASE: "0x54330d28ca3357F294334BDC454a032e7f353416", - ChainType.ETHEREUM: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0", - ChainType.MODE: "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9", +OLAS: t.Dict[Chain, str] = { + Chain.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", + Chain.OPTIMISTIC: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527", + Chain.BASE: "0x54330d28ca3357F294334BDC454a032e7f353416", + Chain.ETHEREUM: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0", + Chain.MODE: "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9", } diff --git a/operate/operate_types.py b/operate/operate_types.py index 36482a0e..26002611 100644 --- a/operate/operate_types.py +++ b/operate/operate_types.py @@ -20,9 +20,12 @@ """Types module.""" import enum +import os import typing as t from dataclasses import dataclass +from autonomy.chain.config import ChainType +from autonomy.chain.constants import CHAIN_NAME_TO_CHAIN_ID from typing_extensions import TypedDict from operate.resource import LocalResource @@ -35,45 +38,19 @@ "stop": 3, } - -_CHAIN_NAME_TO_ENUM = { - "ethereum": 0, - "goerli": 1, - "gnosis": 2, - "solana": 3, - "optimism": 4, - "base": 5, - "mode": 6, -} +CHAIN_NAME_TO_CHAIN_ID["mode"] = 34443 # TODO: update open-autonomy and remove this +CHAIN_NAME_TO_CHAIN_ID["solana"] = 900 _CHAIN_ID_TO_CHAIN_NAME = { - 1: "ethereum", - 5: "goerli", - 100: "gnosis", - 1399811149: "solana", - 10: "optimism", - 8453: "base", - 34443: "mode", -} - -_CHAIN_NAME_TO_ID = {val: key for key, val in _CHAIN_ID_TO_CHAIN_NAME.items()} - -_LEDGER_TYPE_TO_ENUM = { - "ethereum": 0, - "solana": 1, + chain_id: chain_name for chain_name, chain_id in CHAIN_NAME_TO_CHAIN_ID.items() } -class LedgerType(enum.IntEnum): +class LedgerType(str, enum.Enum): """Ledger type enum.""" - ETHEREUM = 0 - SOLANA = 1 - - @classmethod - def from_string(cls, chain: str) -> "LedgerType": - """Load from string.""" - return cls(_LEDGER_TYPE_TO_ENUM[chain.lower()]) + ETHEREUM = "ethereum" + SOLANA = "solana" @property def config_file(self) -> str: @@ -85,32 +62,68 @@ def key_file(self) -> str: """Key filename.""" return f"{self.name.lower()}.txt" + @classmethod + def from_id(cls, chain_id: int) -> "LedgerType": + """Load from chain ID.""" + return Chain(_CHAIN_ID_TO_CHAIN_NAME[chain_id]).ledger_type + + +# Dynamically create the Chain enum from the ChainType +# TODO: Migrate this to open-autonomy and remove this modified version of Chain here and use the one from open-autonomy +# This version of open-autonomy must support the LedgerType to support SOLANA in the future +# If solana support is not fuly implemented, decide to keep this half-baked feature +Chain = enum.Enum( + "Chain", + [(member.name, member.value) for member in ChainType] + + [ + ("MODE", "mode"), # TODO: update open-autonomy version and remove this + ("SOLANA", "solana"), + ], +) -class ChainType(enum.IntEnum): - """Chain type enum.""" - ETHEREUM = 0 - GOERLI = 1 - GNOSIS = 2 - SOLANA = 3 - OPTIMISM = 4 - BASE = 5 - MODE = 6 +class ChainMixin: + """Mixin for some new functions in the ChainType class.""" @property - def id(self) -> int: - """Returns chain id.""" - return _CHAIN_NAME_TO_ID[self.name.lower()] + def id(self) -> t.Optional[int]: + """Chain ID""" + if self == Chain.CUSTOM: + chain_id = os.environ.get("CUSTOM_CHAIN_ID") + if chain_id is None: + return None + return int(chain_id) + return CHAIN_NAME_TO_CHAIN_ID[self.value] + + @property + def ledger_type(self) -> LedgerType: + """Ledger type.""" + if self in ( + Chain.ETHEREUM, + Chain.GOERLI, + Chain.GNOSIS, + Chain.BASE, + Chain.OPTIMISTIC, + Chain.MODE, + ): + return LedgerType.ETHEREUM + return LedgerType.SOLANA @classmethod - def from_string(cls, chain: str) -> "ChainType": + def from_string(cls, chain: str) -> "Chain": """Load from string.""" - return cls(_CHAIN_NAME_TO_ENUM[chain.lower()]) + return Chain(chain.lower()) @classmethod - def from_id(cls, cid: int) -> "ChainType": + def from_id(cls, chain_id: int) -> "Chain": """Load from chain ID.""" - return cls(_CHAIN_NAME_TO_ENUM[_CHAIN_ID_TO_CHAIN_NAME[cid]]) + return Chain(_CHAIN_ID_TO_CHAIN_NAME[chain_id]) + + +# Add the ChainMixin methods to the Chain enum +for name in dir(ChainMixin): + if not name.startswith("__"): + setattr(Chain, name, getattr(ChainMixin, name)) class Action(enum.IntEnum): @@ -168,8 +181,7 @@ class LedgerConfig(LocalResource): """Ledger config.""" rpc: str - type: LedgerType - chain: ChainType + chain: Chain LedgerConfigs = t.Dict[str, LedgerConfig] @@ -232,7 +244,7 @@ class ServiceTemplate(TypedDict): image: str description: str service_version: str - home_chain_id: str + home_chain: str configurations: ConfigurationTemplates env_variables: EnvVariables diff --git a/operate/resource.py b/operate/resource.py index a82db9ad..b0ddc690 100644 --- a/operate/resource.py +++ b/operate/resource.py @@ -32,11 +32,11 @@ def serialize(obj: t.Any) -> t.Any: """Serialize object.""" if is_dataclass(obj): - return asdict(obj) + return serialize(asdict(obj)) if isinstance(obj, Path): return str(obj) if isinstance(obj, dict): - return {key: serialize(obj=value) for key, value in obj.items()} + return {serialize(key): serialize(obj=value) for key, value in obj.items()} if isinstance(obj, list): return [serialize(obj=value) for value in obj] if isinstance(obj, enum.Enum): diff --git a/operate/services/manage.py b/operate/services/manage.py index 1a92a051..387aabf9 100644 --- a/operate/services/manage.py +++ b/operate/services/manage.py @@ -38,7 +38,7 @@ from operate.keys import Key, KeysManager from operate.ledger import PUBLIC_RPCS from operate.ledger.profiles import CONTRACTS, OLAS, STAKING -from operate.operate_types import ChainType, LedgerConfig, ServiceTemplate +from operate.operate_types import Chain, LedgerConfig, ServiceTemplate from operate.services.protocol import EthSafeTxBuilder, OnChainManager, StakingState from operate.services.service import ( ChainConfig, @@ -109,10 +109,10 @@ def _get_all_services(self) -> t.List[Service]: except ValueError as e: raise e except Exception as e: # pylint: disable=broad-except - self.logger.warning( - f"Failed to load service: {path.name}. Exception: {e}" + self.logger.error( + f"Failed to load service: {path.name}. Exception {e}: {traceback.format_exc()}" ) - # rename the invalid path + # Rename the invalid path timestamp = int(time.time()) invalid_path = path.parent / f"invalid_{timestamp}_{path.name}" os.rename(path, invalid_path) @@ -135,7 +135,7 @@ def get_on_chain_manager(self, ledger_config: LedgerConfig) -> OnChainManager: """Get OnChainManager instance.""" return OnChainManager( rpc=ledger_config.rpc, - wallet=self.wallet_manager.load(ledger_config.type), + wallet=self.wallet_manager.load(ledger_config.chain.ledger_type), contracts=CONTRACTS[ledger_config.chain], ) @@ -143,7 +143,7 @@ def get_eth_safe_tx_builder(self, ledger_config: LedgerConfig) -> EthSafeTxBuild """Get EthSafeTxBuilder instance.""" return EthSafeTxBuilder( rpc=ledger_config.rpc, - wallet=self.wallet_manager.load(ledger_config.type), + wallet=self.wallet_manager.load(ledger_config.chain.ledger_type), contracts=CONTRACTS[ledger_config.chain], ) @@ -232,8 +232,8 @@ def create( return service - def _get_on_chain_state(self, service: Service, chain_id: str) -> OnChainState: - chain_config = service.chain_configs[chain_id] + def _get_on_chain_state(self, service: Service, chain: str) -> OnChainState: + chain_config = service.chain_configs[chain] chain_data = chain_config.chain_data ledger_config = chain_config.ledger_config if chain_data.token == NON_EXISTENT_TOKEN: @@ -273,23 +273,23 @@ def deploy_service_onchain( # pylint: disable=too-many-statements,too-many-loca # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version. service = self.load(service_config_id=service_config_id) - for chain_id in service.chain_configs.keys(): + for chain in service.chain_configs.keys(): self._deploy_service_onchain( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, ) def _deploy_service_onchain( # pylint: disable=too-many-statements,too-many-locals self, service_config_id: str, - chain_id: str, + chain: str, ) -> None: """Deploy as service on-chain""" # 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=}") + self.logger.info(f"_deploy_service_onchain_from_safe {chain=}") service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data user_params = chain_config.chain_data.user_params @@ -367,7 +367,7 @@ def _deploy_service_onchain( # pylint: disable=too-many-statements,too-many-loc "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) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.NON_EXISTENT ) is_update = ( @@ -474,31 +474,30 @@ def deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,too """Deploy as service on-chain""" service = self.load(service_config_id=service_config_id) - for chain_id in service.chain_configs.keys(): + for chain in service.chain_configs.keys(): self._deploy_service_onchain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, ) def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,too-many-locals self, service_config_id: str, - chain_id: str, + chain: str, ) -> None: """Deploy service on-chain""" - self.logger.info(f"_deploy_service_onchain_from_safe {chain_id=}") + self.logger.info(f"_deploy_service_onchain_from_safe {chain=}") service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] 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] - wallet = self.wallet_manager.load(ledger_config.type) + wallet = self.wallet_manager.load(ledger_config.chain.ledger_type) sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config) - chain_type = ChainType.from_id(int(chain_id)) - safe = wallet.safes[chain_type] # type: ignore + safe = wallet.safes[Chain(chain)] # TODO fix this os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc @@ -532,20 +531,22 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to ) # TODO A customized, arbitrary computation mechanism should be devised. - if chain_id == service.home_chain_id: + if chain == service.home_chain: env_var_to_value = { - "ETHEREUM_LEDGER_RPC": PUBLIC_RPCS[ChainType.ETHEREUM], - "GNOSIS_LEDGER_RPC": PUBLIC_RPCS[ChainType.GNOSIS], - "BASE_LEDGER_RPC": PUBLIC_RPCS[ChainType.BASE], - "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[ChainType.OPTIMISM], + "ETHEREUM_LEDGER_RPC": PUBLIC_RPCS[Chain.ETHEREUM], + "GNOSIS_LEDGER_RPC": PUBLIC_RPCS[Chain.GNOSIS], + "BASE_LEDGER_RPC": PUBLIC_RPCS[Chain.BASE], + "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISTIC], "STAKING_CONTRACT_ADDRESS": staking_params.get("staking_contract"), - "MECH_ACTIVITY_CHECKER_CONTRACT": staking_params.get("activity_checker"), + "MECH_ACTIVITY_CHECKER_CONTRACT": staking_params.get( + "activity_checker" + ), "MECH_CONTRACT_ADDRESS": staking_params.get("agent_mech"), "MECH_REQUEST_PRICE": "10000000000000000", "USE_MECH_MARKETPLACE": str( "mech_marketplace" in service.chain_configs[ - service.home_chain_id + service.home_chain ].chain_data.user_params.staking_program_id ), "REQUESTER_STAKING_INSTANCE_ADDRESS": staking_params.get( @@ -603,7 +604,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to ) is_first_mint = ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.NON_EXISTENT ) is_update = ( @@ -636,11 +637,11 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to if is_update: self._terminate_service_on_chain_from_safe( - service_config_id=service_config_id, chain_id=chain_id + service_config_id=service_config_id, chain=chain ) # Update service if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.PRE_REGISTRATION ): self.logger.info("Updating service") @@ -682,7 +683,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to # Mint service if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.NON_EXISTENT ): if user_params.use_staking and not sftxb.staking_slots_available( @@ -731,7 +732,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to service.store() if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.PRE_REGISTRATION ): # TODO Verify that this is incorrect: cost_of_bond = staking_params["min_staking_deposit"] @@ -784,7 +785,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to service.store() if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.ACTIVE_REGISTRATION ): cost_of_bond = user_params.cost_of_bond @@ -840,7 +841,7 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to service.store() if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.FINISHED_REGISTRATION ): self.logger.info("Deploying service") @@ -873,11 +874,11 @@ def _deploy_service_onchain_from_safe( # pylint: disable=too-many-statements,to service.store() if user_params.use_staking: self.stake_service_on_chain_from_safe( - service_config_id=service_config_id, chain_id=chain_id + service_config_id=service_config_id, chain=chain ) def terminate_service_on_chain( - self, service_config_id: str, chain_id: t.Optional[str] = None + self, service_config_id: str, chain: t.Optional[str] = None ) -> None: """Terminate service on-chain""" # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version. @@ -885,10 +886,7 @@ def terminate_service_on_chain( self.logger.info("terminate_service_on_chain") service = self.load(service_config_id=service_config_id) - if chain_id is None: - chain_id = service.home_chain_id - - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain or service.home_chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data ocm = self.get_on_chain_manager(ledger_config=ledger_config) @@ -912,20 +910,19 @@ def terminate_service_on_chain( service.store() def _terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals - self, service_config_id: str, chain_id: str + self, service_config_id: str, chain: str ) -> None: """Terminate service on-chain""" self.logger.info("terminate_service_on_chain_from_safe") service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data keys = service.keys instances = [key.address for key in keys] - wallet = self.wallet_manager.load(ledger_config.type) - chain_type = ChainType.from_id(int(chain_id)) - safe = wallet.safes[chain_type] # type: ignore + wallet = self.wallet_manager.load(ledger_config.chain.ledger_type) + safe = wallet.safes[Chain(chain)] # type: ignore # TODO fixme os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc @@ -956,11 +953,11 @@ def _terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals if is_staked and can_unstake: self.unstake_service_on_chain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, staking_program_id=current_staking_program, ) - if self._get_on_chain_state(service=service, chain_id=chain_id) in ( + if self._get_on_chain_state(service=service, chain=chain) in ( OnChainState.ACTIVE_REGISTRATION, OnChainState.FINISHED_REGISTRATION, OnChainState.DEPLOYED, @@ -973,7 +970,7 @@ def _terminate_service_on_chain_from_safe( # pylint: disable=too-many-locals ).settle() if ( - self._get_on_chain_state(service=service, chain_id=chain_id) + self._get_on_chain_state(service=service, chain=chain) == OnChainState.TERMINATED_BONDED ): self.logger.info("Unbonding service") @@ -1031,17 +1028,14 @@ def _get_current_staking_program( return current_staking_program def unbond_service_on_chain( - self, service_config_id: str, chain_id: t.Optional[str] = None + self, service_config_id: str, chain: t.Optional[str] = None ) -> None: """Unbond service on-chain""" # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version. service = self.load(service_config_id=service_config_id) - if chain_id is None: - chain_id = service.home_chain_id - - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain or service.home_chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data ocm = self.get_on_chain_manager(ledger_config=ledger_config) @@ -1073,12 +1067,12 @@ def stake_service_on_chain(self, hash: str) -> None: raise NotImplementedError def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too-many-locals - self, service_config_id: str, chain_id: str + self, service_config_id: str, chain: str ) -> None: """Stake service on-chain""" service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data user_params = chain_data.user_params @@ -1111,7 +1105,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too ) self.unstake_service_on_chain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, staking_program_id=current_staking_program, ) @@ -1128,7 +1122,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too ) self.unstake_service_on_chain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, staking_program_id=current_staking_program, ) @@ -1143,7 +1137,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too ) self.unstake_service_on_chain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, staking_program_id=current_staking_program, ) @@ -1157,7 +1151,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too ) self.unstake_service_on_chain_from_safe( service_config_id=service_config_id, - chain_id=chain_id, + chain=chain, staking_program_id=current_staking_program, ) @@ -1171,7 +1165,7 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too target_staking_contract ) staking_slots_available = sftxb.staking_slots_available(target_staking_contract) - on_chain_state = self._get_on_chain_state(service=service, chain_id=chain_id) + 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 ) @@ -1219,17 +1213,13 @@ def stake_service_on_chain_from_safe( # pylint: disable=too-many-statements,too self.logger.info(f"{current_staking_program=}") def unstake_service_on_chain( - self, service_config_id: str, chain_id: t.Optional[str] = None + self, service_config_id: str, chain: t.Optional[str] = None ) -> None: """Unbond service on-chain""" # TODO This method has not been thoroughly reviewed. Deprecated usage in favour of Safe version. service = self.load(service_config_id=service_config_id) - - if chain_id is None: - chain_id = service.home_chain_id - - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain or service.home_chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data ocm = self.get_on_chain_manager(ledger_config=ledger_config) @@ -1259,14 +1249,14 @@ def unstake_service_on_chain( def unstake_service_on_chain_from_safe( self, service_config_id: str, - chain_id: str, + chain: str, staking_program_id: t.Optional[str] = None, ) -> None: """Unbond service on-chain""" self.logger.info("unstake_service_on_chain_from_safe") service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data @@ -1315,8 +1305,8 @@ def fund_service( # pylint: disable=too-many-arguments,too-many-locals """Fund service if required.""" service = self.load(service_config_id=service_config_id) - for chain_id in service.chain_configs.keys(): - self.logger.info(f"Funding chain_id {chain_id}") + for chain in service.chain_configs.keys(): + self.logger.info(f"Funding {chain=}") self.fund_service_single_chain( service_config_id=service_config_id, rpc=rpc, @@ -1325,7 +1315,7 @@ def fund_service( # pylint: disable=too-many-arguments,too-many-locals agent_fund_threshold=agent_fund_threshold, safe_fund_treshold=safe_fund_treshold, from_safe=from_safe, - chain_id=chain_id, + chain=chain, ) def fund_service_single_chain( # pylint: disable=too-many-arguments,too-many-locals @@ -1337,17 +1327,17 @@ def fund_service_single_chain( # pylint: disable=too-many-arguments,too-many-lo agent_fund_threshold: t.Optional[float] = None, safe_fund_treshold: t.Optional[float] = None, from_safe: bool = True, - chain_id: str = "100", + chain: str = "gnosis", ) -> None: """Fund service if required.""" service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data - wallet = self.wallet_manager.load(ledger_config.type) + wallet = self.wallet_manager.load(ledger_config.chain.ledger_type) ledger_api = wallet.ledger_api( - chain_type=ledger_config.chain, rpc=rpc or ledger_config.rpc + chain=ledger_config.chain, rpc=rpc or ledger_config.rpc ) agent_fund_threshold = ( agent_fund_threshold @@ -1371,7 +1361,7 @@ def fund_service_single_chain( # pylint: disable=too-many-arguments,too-many-lo wallet.transfer( to=key.address, amount=int(to_transfer), - chain_type=ledger_config.chain, + chain=ledger_config.chain, from_safe=from_safe, rpc=rpc or ledger_config.rpc, ) @@ -1391,7 +1381,7 @@ def fund_service_single_chain( # pylint: disable=too-many-arguments,too-many-lo wallet.transfer( to=t.cast(str, chain_data.multisig), amount=int(to_transfer), - chain_type=ledger_config.chain, + chain=ledger_config.chain, rpc=rpc or ledger_config.rpc, ) @@ -1405,16 +1395,16 @@ def fund_service_erc20( # pylint: disable=too-many-arguments,too-many-locals agent_fund_threshold: t.Optional[float] = None, safe_fund_treshold: t.Optional[float] = None, from_safe: bool = True, - chain_id: str = "100", + chain: str = "gnosis", ) -> None: """Fund service if required.""" service = self.load(service_config_id=service_config_id) - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data - wallet = self.wallet_manager.load(ledger_config.type) + wallet = self.wallet_manager.load(ledger_config.chain.ledger_type) ledger_api = wallet.ledger_api( - chain_type=ledger_config.chain, rpc=rpc or ledger_config.rpc + chain=ledger_config.chain, rpc=rpc or ledger_config.rpc ) agent_fund_threshold = ( agent_fund_threshold or chain_data.user_params.fund_requirements.agent @@ -1434,7 +1424,7 @@ def fund_service_erc20( # pylint: disable=too-many-arguments,too-many-locals token=token, to=key.address, amount=int(to_transfer), - chain_type=ledger_config.chain, + chain=ledger_config.chain, from_safe=from_safe, rpc=rpc or ledger_config.rpc, ) @@ -1459,7 +1449,7 @@ def fund_service_erc20( # pylint: disable=too-many-arguments,too-many-locals token=token, to=t.cast(str, chain_data.multisig), amount=int(to_transfer), - chain_type=ledger_config.chain, + chain=ledger_config.chain, rpc=rpc or ledger_config.rpc, ) @@ -1472,8 +1462,7 @@ async def funding_job( """Start a background funding job.""" loop = loop or asyncio.get_event_loop() service = self.load(service_config_id=service_config_id) - chain_id = service.home_chain_id - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[service.home_chain] ledger_config = chain_config.ledger_config with ThreadPoolExecutor() as executor: while True: @@ -1502,7 +1491,7 @@ def deploy_service_locally( self, service_config_id: str, force: bool = True, - chain_id: t.Optional[str] = None, + chain: t.Optional[str] = None, use_docker: bool = False, ) -> Deployment: """ @@ -1510,7 +1499,7 @@ def deploy_service_locally( :param hash: Service hash :param force: Remove previous deployment and start a new one. - :param chain_id: Chain ID to set runtime parameters on the deployment (home_chain_id if not provided). + :param chain: Chain to set runtime parameters on the deployment (home_chain if not provided). :param use_docker: Use a Docker Compose deployment (True) or Host deployment (False). :return: Deployment instance """ @@ -1518,7 +1507,9 @@ def deploy_service_locally( service = self.load(service_config_id=service_config_id) deployment = service.deployment - deployment.build(use_docker=use_docker, force=force, chain_id=chain_id) + deployment.build( + use_docker=use_docker, force=force, chain=chain or service.home_chain + ) deployment.start(use_docker=use_docker) return deployment @@ -1594,14 +1585,26 @@ def migrate_service_configs(self) -> None: paths = list(self.path.iterdir()) for path in paths: - if path.name.startswith(DELETE_PREFIX): - shutil.rmtree(path) - self.logger.info(f"Deleted folder: {path.name}") - - if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith( - "bafybei" - ): - self.logger.info(f"migrate_service_configs {str(path)}") - migrated = Service.migrate_format(path) - if migrated: - self.logger.info(f"Folder {str(path)} has been migrated.") + try: + if path.name.startswith(DELETE_PREFIX): + shutil.rmtree(path) + self.logger.info(f"Deleted folder: {path.name}") + + if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith( + "bafybei" + ): + self.logger.info(f"migrate_service_configs {str(path)}") + migrated = Service.migrate_format(path) + if migrated: + self.logger.info(f"Folder {str(path)} has been migrated.") + except Exception as e: # pylint: disable=broad-except + self.logger.error( + f"Failed to migrate service: {path.name}. Exception {e}: {traceback.format_exc()}" + ) + # 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}" + ) diff --git a/operate/services/protocol.py b/operate/services/protocol.py index a2d79350..5444d4ba 100644 --- a/operate/services/protocol.py +++ b/operate/services/protocol.py @@ -67,7 +67,7 @@ from operate.data import DATA_DIR from operate.data.contracts.staking_token.contract import StakingTokenContract from operate.ledger.profiles import STAKING -from operate.operate_types import ChainType as OperateChainType +from operate.operate_types import Chain as OperateChain from operate.operate_types import ContractAddresses from operate.utils.gnosis import ( MultiSendOperation, @@ -520,12 +520,12 @@ def _patch(self) -> None: def safe(self) -> str: """Get safe address.""" chain_id = self.ledger_api.api.eth.chain_id - chain_type = OperateChainType.from_id(chain_id) + chain = OperateChain.from_id(chain_id) if self.wallet.safes is None: raise ValueError("Safes not initialized") - if chain_type not in self.wallet.safes: - raise ValueError(f"Safe for chain type {chain_type} not found") - return self.wallet.safes[chain_type] + if chain not in self.wallet.safes: + raise ValueError(f"Safe for chain type {chain} not found") + return self.wallet.safes[chain] @property def crypto(self) -> Crypto: @@ -807,7 +807,7 @@ def get_staking_params(self, staking_contract: str) -> t.Dict: # TODO Read from activity checker contract. Read remaining variables for marketplace. if ( staking_contract - == STAKING[operate.operate_types.ChainType.GNOSIS][ + == STAKING[operate.operate_types.Chain.GNOSIS][ "pearl_beta_mech_marketplace" ] ): diff --git a/operate/services/service.py b/operate/services/service.py index fee2a021..1e953eb8 100644 --- a/operate/services/service.py +++ b/operate/services/service.py @@ -70,16 +70,15 @@ from operate.keys import Keys from operate.operate_http.exceptions import NotAllowed from operate.operate_types import ( + Chain, ChainConfig, ChainConfigs, - ChainType, DeployedNodes, DeploymentConfig, DeploymentStatus, EnvVariables, LedgerConfig, LedgerConfigs, - LedgerType, OnChainData, OnChainState, OnChainUserParams, @@ -262,11 +261,10 @@ def ledger_configs(self) -> LedgerConfigs: ): for _, config in override["config"]["ledger_apis"].items(): # TODO chain name is inferred from the chain_id. The actual id provided on service.yaml is ignored. - chain = ChainType.from_id(cid=config["chain_id"]) - ledger_configs[str(config["chain_id"])] = LedgerConfig( + chain = Chain.from_id(chain_id=config["chain_id"]) # type: ignore + ledger_configs[chain.value] = LedgerConfig( rpc=config["address"], chain=chain, - type=LedgerType.ETHEREUM, ) return ledger_configs @@ -395,7 +393,7 @@ def load(cls, path: Path) -> "Deployment": def _build_docker( self, force: bool = True, - chain_id: t.Optional[str] = None, + chain: t.Optional[str] = None, ) -> None: """Build docker deployment.""" service = Service.load(path=self.path) @@ -438,10 +436,10 @@ def _build_docker( builder.deplopyment_type = DockerComposeGenerator.deployment_type builder.try_update_abci_connection_params() - if not chain_id: - chain_id = service.home_chain_id + if not chain: + chain = service.home_chain - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data @@ -453,7 +451,7 @@ def _build_docker( ) # TODO: Support for multiledger builder.try_update_ledger_params( - chain=LedgerType(ledger_config.type).name.lower(), + chain=chain, address=ledger_config.rpc, ) @@ -502,7 +500,7 @@ def _build_docker( self.status = DeploymentStatus.BUILT self.store() - def _build_host(self, force: bool = True, chain_id: t.Optional[str] = None) -> None: + def _build_host(self, force: bool = True, chain: t.Optional[str] = None) -> None: """Build host depployment.""" build = self.path / DEPLOYMENT if build.exists() and not force: @@ -526,10 +524,10 @@ def _build_host(self, force: bool = True, chain_id: t.Optional[str] = None) -> N "Host deployment currently only supports single agent deployments" ) - if not chain_id: - chain_id = service.home_chain_id + if not chain: + chain = service.home_chain - chain_config = service.chain_configs[chain_id] + chain_config = service.chain_configs[chain] ledger_config = chain_config.ledger_config chain_data = chain_config.chain_data @@ -565,7 +563,7 @@ def _build_host(self, force: bool = True, chain_id: t.Optional[str] = None) -> N ) # TODO: Support for multiledger builder.try_update_ledger_params( - chain=LedgerType(ledger_config.type).name.lower(), + chain=ledger_config.chain.ledger_type.name.lower(), address=ledger_config.rpc, ) @@ -592,20 +590,20 @@ def build( self, use_docker: bool = False, force: bool = True, - chain_id: t.Optional[str] = None, + chain: t.Optional[str] = None, ) -> None: """ Build a deployment :param use_docker: Use a Docker Compose deployment (True) or Host deployment (False). :param force: Remove existing deployment and build a new one - :param chain_id: Chain ID to set runtime parameters on the deployment (home_chain_id if not provided). + :param chain: Chain to set runtime parameters on the deployment (home_chain if not provided). :return: Deployment object """ - # TODO: Maybe remove usage of chain_id and use home_chain_id always? + # TODO: Maybe remove usage of chain and use home_chain always? if use_docker: - return self._build_docker(force=force, chain_id=chain_id) - return self._build_host(force=force, chain_id=chain_id) + return self._build_docker(force=force, chain=chain) + return self._build_host(force=force, chain=chain) def start(self, use_docker: bool = False) -> None: """Start the service""" @@ -662,7 +660,7 @@ class Service(LocalResource): hash: str hash_history: t.Dict[int, str] keys: Keys - home_chain_id: str + home_chain: str chain_configs: ChainConfigs description: str env_variables: EnvVariables @@ -689,6 +687,14 @@ def migrate_format(cls, path: Path) -> bool: ): return False + if path.name.startswith("bafybei"): + backup_name = f"backup_{int(time.time())}_{path.name}" + backup_path = path.parent / backup_name + shutil.copytree(path, backup_path) + deployment_path = backup_path / "deployment" + if deployment_path.is_dir(): + shutil.rmtree(deployment_path) + with open(path / Service._file, "r", encoding="utf-8") as file: data = json.load(file) @@ -707,9 +713,9 @@ def migrate_format(cls, path: Path) -> bool: "version": 2, "hash": data.get("hash"), "keys": data.get("keys"), - "home_chain_id": "100", # Assuming a default value for home_chain_id + "home_chain_id": "100", # This is the default value for version 2 - do not change, will be corrected below "chain_configs": { - "100": { + "100": { # This is the default value for version 2 - do not change, will be corrected below "ledger_config": { "rpc": data.get("ledger_config", {}).get("rpc"), "type": data.get("ledger_config", {}).get("type"), @@ -742,6 +748,9 @@ def migrate_format(cls, path: Path) -> bool: "fund_requirements": data.get("chain_data", {}) .get("user_params", {}) .get("fund_requirements", {}), + "agent_id": data.get("chain_data", {}) + .get("user_params", {}) + .get("agent_id", "14"), }, }, } @@ -766,21 +775,51 @@ def migrate_format(cls, path: Path) -> bool: service_config_id = Service.get_new_service_config_id(path) new_path = path.parent / service_config_id data["service_config_id"] = service_config_id + path = path.rename(new_path) - service_path = Path(data["service_path"]) - if service_path.exists() and service_path.is_dir(): - shutil.rmtree(service_path) + old_to_new_ledgers = ["ethereum", "solana"] + for key_data in data["keys"]: + key_data["ledger"] = old_to_new_ledgers[key_data["ledger"]] + + old_to_new_chains = [ + "ethereum", + "goerli", + "gnosis", + "solana", + "optimistic", + "base", + "mode", + ] + new_chain_configs = {} + for chain_id, chain_data in data["chain_configs"].items(): + chain_data["ledger_config"]["chain"] = old_to_new_chains[ + chain_data["ledger_config"]["chain"] + ] + del chain_data["ledger_config"]["type"] + new_chain_configs[Chain.from_id(int(chain_id)).value] = chain_data # type: ignore - path = path.rename(new_path) - service_path = Path( - IPFSTool().download( - hash_id=data["hash"], - target_dir=path, - ) - ) - data["service_path"] = str(service_path) + data["chain_configs"] = new_chain_configs + data["home_chain"] = data.setdefault("home_chain", Chain.from_id(int(data.get("home_chain_id", "100"))).value) # type: ignore + del data["home_chain_id"] - data["version"] = 4 + if "env_variables" not in data: + data["env_variables"] = {} + + data["version"] = SERVICE_CONFIG_VERSION + + # Redownload service path + service_path = path / Path(data["service_path"]).name + if service_path.exists() and service_path.is_dir(): + print("EXISTS") + shutil.rmtree(service_path) + + service_path = Path( + IPFSTool().download( + hash_id=data["hash"], + target_dir=path, + ) + ) + data["service_path"] = str(service_path) with open(path / Service._file, "w", encoding="utf-8") as file: json.dump(data, file, indent=2) @@ -857,7 +896,7 @@ def new( # pylint: disable=too-many-locals description=service_template["description"], hash=service_template["hash"], keys=keys, - home_chain_id=service_template["home_chain_id"], + home_chain=service_template["home_chain"], hash_history={current_timestamp: service_template["hash"]}, chain_configs=chain_configs, path=service_path.parent, @@ -954,7 +993,7 @@ def update( self.hash_history[current_timestamp] = service_template["hash"] self.description = service_template["description"] - self.home_chain_id = service_template["home_chain_id"] + self.home_chain = service_template["home_chain"] ledger_configs = ServiceHelper(path=self.service_path).ledger_configs() for chain, config in service_template["configurations"].items(): diff --git a/operate/wallet/master.py b/operate/wallet/master.py index e3ec758e..30fd975b 100644 --- a/operate/wallet/master.py +++ b/operate/wallet/master.py @@ -40,7 +40,7 @@ ON_CHAIN_INTERACT_TIMEOUT, ) from operate.ledger import get_default_rpc -from operate.operate_types import ChainType, LedgerType +from operate.operate_types import Chain, LedgerType from operate.resource import LocalResource from operate.utils.gnosis import add_owner from operate.utils.gnosis import create_safe as create_gnosis_safe @@ -53,8 +53,8 @@ class MasterWallet(LocalResource): """Master wallet.""" path: Path - safes: t.Optional[t.Dict[ChainType, str]] = {} - safe_chains: t.List[ChainType] = [] + safes: t.Optional[t.Dict[Chain, str]] = {} + safe_chains: t.List[Chain] = [] ledger_type: LedgerType _key: str @@ -88,21 +88,21 @@ def key_path(self) -> Path: def ledger_api( self, - chain_type: ChainType, + chain: Chain, rpc: t.Optional[str] = None, ) -> LedgerApi: """Get ledger api object.""" return make_ledger_api( self.ledger_type.name.lower(), - address=(rpc or get_default_rpc(chain=chain_type)), - chain_id=chain_type.id, + address=(rpc or get_default_rpc(chain=chain)), + chain_id=chain.id, ) def transfer( self, to: str, amount: int, - chain_type: ChainType, + chain: Chain, from_safe: bool = True, rpc: t.Optional[str] = None, ) -> None: @@ -115,7 +115,7 @@ def transfer_erc20( token: str, to: str, amount: int, - chain_type: ChainType, + chain: Chain, from_safe: bool = True, rpc: t.Optional[str] = None, ) -> None: @@ -129,7 +129,7 @@ def new(password: str, path: Path) -> t.Tuple["MasterWallet", t.List[str]]: def create_safe( self, - chain_type: ChainType, + chain: Chain, owner: t.Optional[str] = None, rpc: t.Optional[str] = None, ) -> None: @@ -138,7 +138,7 @@ def create_safe( def add_backup_owner( self, - chain_type: ChainType, + chain: Chain, owner: str, rpc: t.Optional[str] = None, ) -> None: @@ -147,7 +147,7 @@ def add_backup_owner( def swap_backup_owner( self, - chain_type: ChainType, + chain: Chain, old_owner: str, new_owner: str, rpc: t.Optional[str] = None, @@ -157,7 +157,7 @@ def swap_backup_owner( def add_or_swap_owner( self, - chain_type: ChainType, + chain: Chain, owner: str, rpc: t.Optional[str] = None, ) -> None: @@ -177,8 +177,8 @@ class EthereumMasterWallet(MasterWallet): path: Path address: str - safes: t.Optional[t.Dict[ChainType, str]] = field(default_factory=dict) # type: ignore - safe_chains: t.List[ChainType] = field(default_factory=list) # type: ignore + safes: t.Optional[t.Dict[Chain, str]] = field(default_factory=dict) # type: ignore + safe_chains: t.List[Chain] = field(default_factory=list) # type: ignore ledger_type: LedgerType = LedgerType.ETHEREUM safe_nonce: t.Optional[int] = None # For cross-chain reusability @@ -187,12 +187,10 @@ class EthereumMasterWallet(MasterWallet): _crypto_cls = EthereumCrypto def _transfer_from_eoa( - self, to: str, amount: int, chain_type: ChainType, rpc: t.Optional[str] = None + self, to: str, amount: int, chain: Chain, rpc: t.Optional[str] = None ) -> None: """Transfer funds from EOA wallet.""" - ledger_api = t.cast( - EthereumApi, self.ledger_api(chain_type=chain_type, rpc=rpc) - ) + ledger_api = t.cast(EthereumApi, self.ledger_api(chain=chain, rpc=rpc)) tx_helper = TxSettler( ledger_api=ledger_api, crypto=self.crypto, @@ -214,7 +212,7 @@ def _build_tx( # pylint: disable=unused-argument amount=amount, tx_fee=50000, tx_nonce="0x", - chain_id=chain_type.id, + chain_id=chain.id, raise_on_try=True, max_fee_per_gas=int(max_fee_per_gas) if max_fee_per_gas else None, max_priority_fee_per_gas=int(max_priority_fee_per_gas) @@ -230,14 +228,14 @@ def _build_tx( # pylint: disable=unused-argument tx_helper.transact(lambda x: x, "", kwargs={}) def _transfer_from_safe( - self, to: str, amount: int, chain_type: ChainType, rpc: t.Optional[str] = None + self, to: str, amount: int, chain: Chain, rpc: t.Optional[str] = None ) -> None: """Transfer funds from safe wallet.""" if self.safes is not None: transfer_from_safe( - ledger_api=self.ledger_api(chain_type=chain_type, rpc=rpc), + ledger_api=self.ledger_api(chain=chain, rpc=rpc), crypto=self.crypto, - safe=t.cast(str, self.safes[chain_type]), + safe=t.cast(str, self.safes[chain]), to=to, amount=amount, ) @@ -249,15 +247,15 @@ def _transfer_erc20_from_safe( token: str, to: str, amount: int, - chain_type: ChainType, + chain: Chain, rpc: t.Optional[str] = None, ) -> None: """Transfer funds from safe wallet.""" transfer_erc20_from_safe( - ledger_api=self.ledger_api(chain_type=chain_type, rpc=rpc), + ledger_api=self.ledger_api(chain=chain, rpc=rpc), crypto=self.crypto, token=token, - safe=t.cast(str, self.safes[chain_type]), # type: ignore + safe=t.cast(str, self.safes[chain]), # type: ignore to=to, amount=amount, ) @@ -266,7 +264,7 @@ def transfer( self, to: str, amount: int, - chain_type: ChainType, + chain: Chain, from_safe: bool = True, rpc: t.Optional[str] = None, ) -> None: @@ -275,13 +273,13 @@ def transfer( return self._transfer_from_safe( to=to, amount=amount, - chain_type=chain_type, + chain=chain, rpc=rpc, ) return self._transfer_from_eoa( to=to, amount=amount, - chain_type=chain_type, + chain=chain, rpc=rpc, ) @@ -291,7 +289,7 @@ def transfer_erc20( token: str, to: str, amount: int, - chain_type: ChainType, + chain: Chain, from_safe: bool = True, rpc: t.Optional[str] = None, ) -> None: @@ -302,7 +300,7 @@ def transfer_erc20( token=token, to=to, amount=amount, - chain_type=chain_type, + chain=chain, rpc=rpc, ) @@ -334,36 +332,36 @@ def new( def create_safe( self, - chain_type: ChainType, + chain: Chain, owner: t.Optional[str] = None, rpc: t.Optional[str] = None, ) -> None: """Create safe.""" - if chain_type in self.safe_chains: + if chain in self.safe_chains: return safe, self.safe_nonce = create_gnosis_safe( - ledger_api=self.ledger_api(chain_type=chain_type, rpc=rpc), + ledger_api=self.ledger_api(chain=chain, rpc=rpc), crypto=self.crypto, owner=owner, salt_nonce=self.safe_nonce, ) - self.safe_chains.append(chain_type) + self.safe_chains.append(chain) if self.safes is None: self.safes = {} - self.safes[chain_type] = safe + self.safes[chain] = safe self.store() def add_backup_owner( self, - chain_type: ChainType, + chain: Chain, owner: str, rpc: t.Optional[str] = None, ) -> None: """Add a backup owner.""" - ledger_api = self.ledger_api(chain_type=chain_type, rpc=rpc) - if chain_type not in self.safes: # type: ignore - raise ValueError(f"Safes not created for chain_type {chain_type}!") - safe = t.cast(str, self.safes[chain_type]) # type: ignore + ledger_api = self.ledger_api(chain=chain, rpc=rpc) + if chain not in self.safes: # type: ignore + raise ValueError(f"Safes not created for chain {chain}!") + safe = t.cast(str, self.safes[chain]) # type: ignore if len(get_owners(ledger_api=ledger_api, safe=safe)) == 2: raise ValueError("Backup owner already exist!") add_owner( @@ -375,16 +373,16 @@ def add_backup_owner( def swap_backup_owner( self, - chain_type: ChainType, + chain: Chain, old_owner: str, new_owner: str, rpc: t.Optional[str] = None, ) -> None: """Swap backup owner.""" - ledger_api = self.ledger_api(chain_type=chain_type, rpc=rpc) - if chain_type not in self.safes: # type: ignore - raise ValueError(f"Safes not created for chain_type {chain_type}!") - safe = t.cast(str, self.safes[chain_type]) # type: ignore + ledger_api = self.ledger_api(chain=chain, rpc=rpc) + if chain not in self.safes: # type: ignore + raise ValueError(f"Safes not created for chain {chain}!") + safe = t.cast(str, self.safes[chain]) # type: ignore if len(get_owners(ledger_api=ledger_api, safe=safe)) == 1: raise ValueError("Backup owner does not exist, cannot swap!") swap_owner( @@ -397,18 +395,18 @@ def swap_backup_owner( def add_or_swap_owner( self, - chain_type: ChainType, + chain: Chain, owner: str, rpc: t.Optional[str] = None, ) -> None: """Add or swap backup owner.""" - ledger_api = self.ledger_api(chain_type=chain_type, rpc=rpc) - if self.safes is None or chain_type not in self.safes: - raise ValueError(f"Safes not created for chain_type {chain_type}!") - safe = t.cast(str, self.safes[chain_type]) + ledger_api = self.ledger_api(chain=chain, rpc=rpc) + if self.safes is None or chain not in self.safes: + raise ValueError(f"Safes not created for chain {chain}!") + safe = t.cast(str, self.safes[chain]) owners = get_owners(ledger_api=ledger_api, safe=safe) if len(owners) == 1: - return self.add_backup_owner(chain_type=chain_type, owner=owner, rpc=rpc) + return self.add_backup_owner(chain=chain, owner=owner, rpc=rpc) owners.remove(self.address) (old_owner,) = owners @@ -416,7 +414,7 @@ def add_or_swap_owner( return None return self.swap_backup_owner( - chain_type=chain_type, + chain=chain, old_owner=old_owner, new_owner=owner, rpc=rpc, @@ -426,16 +424,16 @@ def add_or_swap_owner( def load(cls, path: Path) -> "EthereumMasterWallet": """Load master wallet.""" # TODO: This is a complex way to read the 'safes' dictionary. - # The reason for that is that wallet.safes[chain_type] would fail + # The reason for that is that wallet.safes[chain] would fail # (for example in service manager) when passed a ChainType key. - raw_ethereum_wallet = super().load(path) # type: ignore + raw_ethereum_wallet = t.cast(EthereumMasterWallet, super().load(path)) # type: ignore safes = {} - for id_, safe_address in raw_ethereum_wallet.safes.items(): # type: ignore - safes[ChainType(int(id_))] = safe_address + for chain, safe_address in raw_ethereum_wallet.safes.items(): + safes[Chain(chain)] = safe_address - raw_ethereum_wallet.safes = safes # type: ignore - return t.cast(EthereumMasterWallet, raw_ethereum_wallet) + raw_ethereum_wallet.safes = safes + return raw_ethereum_wallet @classmethod def migrate_format(cls, path: Path) -> bool: @@ -453,6 +451,38 @@ def migrate_format(cls, path: Path) -> bool: data["safes"] = safes migrated = True + old_to_new_chains = [ + "ethereum", + "goerli", + "gnosis", + "solana", + "optimistic", + "base", + "mode", + ] + safe_chains = [] + for chain in data["safe_chains"]: + if isinstance(chain, int): + safe_chains.append(old_to_new_chains[chain]) + migrated = True + else: + safe_chains.append(chain) + data["safe_chains"] = safe_chains + + if isinstance(data["ledger_type"], int): + old_to_new_ledgers = [ledger_type.value for ledger_type in LedgerType] + data["ledger_type"] = old_to_new_ledgers[data["ledger_type"]] + migrated = True + + safes = {} + for chain, address in data["safes"].items(): + if chain.isnumeric(): + safes[old_to_new_chains[int(chain)]] = address + migrated = True + else: + safes[chain] = address + data["safes"] = safes + with open(wallet_path, "w", encoding="utf-8") as file: json.dump(data, file, indent=2) @@ -551,7 +581,11 @@ def migrate_wallet_configs(self) -> None: for ledger_type in LedgerType: if not self.exists(ledger_type=ledger_type): continue - wallet_class = LEDGER_TYPE_TO_WALLET_CLASS[ledger_type] + + wallet_class = LEDGER_TYPE_TO_WALLET_CLASS.get(ledger_type) + if wallet_class is None: + continue + migrated = wallet_class.migrate_format(path=self.path) if migrated: self.logger.info(f"Wallet {wallet_class} has been migrated.") diff --git a/scripts/setup_wallet.py b/scripts/setup_wallet.py index 5d5a47f7..99279ae1 100644 --- a/scripts/setup_wallet.py +++ b/scripts/setup_wallet.py @@ -21,7 +21,7 @@ import requests -from operate.operate_types import ChainType +from operate.operate_types import Chain from scripts.fund import fund @@ -48,7 +48,7 @@ wallet = requests.post( "http://localhost:8000/api/wallet", json={ - "chain_type": ChainType.GNOSIS, + "chain": Chain.GNOSIS, }, ).json() print("Setting up wallet") @@ -61,7 +61,7 @@ requests.post( "http://localhost:8000/api/wallet/safe", json={ - "chain_type": ChainType.GNOSIS, + "chain": Chain.GNOSIS, "owner": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", # Backup owner }, ).json() diff --git a/templates/optimus.yaml b/templates/optimus.yaml index 331c61eb..4076c9bb 100644 --- a/templates/optimus.yaml +++ b/templates/optimus.yaml @@ -3,9 +3,9 @@ hash: bafybeibiiuhqronhgkxjo7x5xve24lkbqom5rqcjxg7vrl6jwavfyypmhu description: Optimus image: https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75 service_version: v0.2.9 -home_chain_id: 10 +home_chain: "optimistic" configurations: - 1: + ethereum: staking_program_id: optimus_alpha nft: bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq agent_id: 14 @@ -17,7 +17,7 @@ configurations: fund_requirements: agent: 1000 safe: 1000 - 10: + optimistic: staking_program_id: optimus_alpha nft: bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq agent_id: 14 @@ -29,7 +29,7 @@ configurations: fund_requirements: agent: 1000 safe: 1000 - 8453: + base: staking_program_id: optimus_alpha nft: bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq agent_id: 14 diff --git a/templates/trader.yaml b/templates/trader.yaml index 7c9e4b3b..cf639bf4 100644 --- a/templates/trader.yaml +++ b/templates/trader.yaml @@ -3,9 +3,9 @@ description: "A single-agent service (sovereign agent) placing bets on Omen" hash: bafybeidicxsruh3r4a2xarawzan6ocwyvpn3ofv42po5kxf7x6ck7kn22u image: https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75 service_version: v0.18.4 -home_chain_id: 100 +home_chain: "gnosis" configurations: - 100: + gnosis: staking_program_id: pearl_beta nft: bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq rpc: http://localhost:8545 # User provided