diff --git a/.gitignore b/.gitignore index 95d7c12f5..fd2899023 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,6 @@ frontend/build .env*.local # python -data backend/__pycache__/ backend/scripts/__pycache__/ .operate/ @@ -29,3 +28,7 @@ __pycache__/ backend/tmp/ backend/temp/ +tmp/ +temp/ + +!backend/operate/data diff --git a/backend/operate/data/__init__.py b/backend/operate/data/__init__.py new file mode 100644 index 000000000..14575eed2 --- /dev/null +++ b/backend/operate/data/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""Data module.""" + +from pathlib import Path + +DATA_DIR = Path(__file__).parent diff --git a/backend/operate/data/contracts/__init__.py b/backend/operate/data/contracts/__init__.py new file mode 100644 index 000000000..4407dec6d --- /dev/null +++ b/backend/operate/data/contracts/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""Contract packages.""" diff --git a/backend/operate/data/contracts/service_staking_token/__init__.py b/backend/operate/data/contracts/service_staking_token/__init__.py new file mode 100644 index 000000000..cf1e8467e --- /dev/null +++ b/backend/operate/data/contracts/service_staking_token/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the support resources for the agent registry contract.""" diff --git a/backend/operate/data/contracts/service_staking_token/build/ServiceStakingToken.json b/backend/operate/data/contracts/service_staking_token/build/ServiceStakingToken.json new file mode 100644 index 000000000..27185c342 --- /dev/null +++ b/backend/operate/data/contracts/service_staking_token/build/ServiceStakingToken.json @@ -0,0 +1,1134 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ServiceStakingToken", + "sourceName": "contracts/staking/ServiceStakingToken.sol", + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "maxNumServices", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardsPerSecond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minStakingDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "livenessPeriod", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "livenessRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numAgentInstances", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "agentIds", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "configHash", + "type": "bytes32" + } + ], + "internalType": "struct ServiceStakingBase.StakingParams", + "name": "_stakingParams", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_serviceRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "_serviceRegistryTokenUtility", + "type": "address" + }, + { + "internalType": "address", + "name": "_stakingToken", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_proxyHash", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "AgentInstanceRegistered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "AgentInstancesSlotsFilled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + } + ], + "name": "AgentNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "AgentNotInService", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "componentId", + "type": "uint256" + } + ], + "name": "ComponentNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "HashExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "sent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "IncorrectAgentBondingValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "sent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "IncorrectRegistrationDepositValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + } + ], + "name": "LowerThan", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "ManagerOnly", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxNumServices", + "type": "uint256" + } + ], + "name": "MaxNumServicesReached", + "type": "error" + }, + { + "inputs": [], + "name": "NoRewardsAvailable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "provided", + "type": "address" + }, + { + "internalType": "address", + "name": "expected", + "type": "address" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "OnlyOwnServiceMultisig", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "OperatorHasNoInstances", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "name": "Overflow", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnerOnly", + "type": "error" + }, + { + "inputs": [], + "name": "Paused", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuard", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "ServiceMustBeInactive", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "ServiceNotStaked", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TokenTransferFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "multisig", + "type": "address" + } + ], + "name": "UnauthorizedMultisig", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + } + ], + "name": "ValueLowerThan", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "agentId", + "type": "uint256" + } + ], + "name": "WrongAgentId", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "numValues1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numValues2", + "type": "uint256" + } + ], + "name": "WrongArrayLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "WrongOperator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "WrongServiceConfiguration", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "state", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "WrongServiceState", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expected", + "type": "address" + }, + { + "internalType": "address", + "name": "provided", + "type": "address" + } + ], + "name": "WrongStakingToken", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "currentThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxThreshold", + "type": "uint256" + } + ], + "name": "WrongThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroValue", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "availableRewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "numServices", + "type": "uint256" + } + ], + "name": "Checkpoint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "availableRewards", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "multisig", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "nonces", + "type": "uint256[]" + } + ], + "name": "ServiceStaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "multisig", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "nonces", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tsStart", + "type": "uint256" + } + ], + "name": "ServiceUnstaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "agentIds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "availableRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "balance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "calculateServiceStakingReward", + "outputs": [ + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkpoint", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[][]", + "name": "", + "type": "uint256[][]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "configHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getNextRewardCheckpointTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "tsNext", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getServiceIds", + "outputs": [ + { + "internalType": "uint256[]", + "name": "serviceIds", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "isServiceStaked", + "outputs": [ + { + "internalType": "bool", + "name": "isStaked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "livenessPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "livenessRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "mapServiceInfo", + "outputs": [ + { + "internalType": "address", + "name": "multisig", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tsStart", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxNumServices", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minStakingDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "numAgentInstances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "proxyHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardsPerSecond", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "serviceRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "serviceRegistryTokenUtility", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "setServiceIds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakingToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "threshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tsCheckpoint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serviceId", + "type": "uint256" + } + ], + "name": "unstake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6102006040523480156200001257600080fd5b506040516200294c3803806200294c8339810160408190526200003591620003bc565b8484828260000151600014806200004e57506020830151155b806200005c57506060830151155b806200006a57506080830151155b8062000078575060a0830151155b156200009757604051637c946ed760e01b815260040160405180910390fd5b600283604001511015620000d357604080840151905163491a2bb160e01b81526004810191909152600260248201526044015b60405180910390fd5b6001600160a01b038216620000fb5760405163d92e233d60e01b815260040160405180910390fd5b82516080908152602084015160a0908152604085015160c052606085015160e09081529185015161010090815290850151610120526001600160a01b038416610180529084015161014052830151610160526000805b8460c00151518110156200023357818560c001518281518110620001795762000179620004c6565b602002602001015111620001c5578460c001518181518110620001a057620001a0620004c6565b6020026020010151604051632ab10b0b60e21b8152600401620000ca91815260200190565b8460c001518181518110620001de57620001de620004c6565b6020908102919091010151600380546001810182556000919091527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0181905591506200022b81620004dc565b905062000151565b50816200025357604051637c946ed760e01b815260040160405180910390fd5b506101a0525050426002556001600160a01b03821615806200027c57506001600160a01b038316155b156200029b5760405163d92e233d60e01b815260040160405180910390fd5b506001600160a01b039081166101e052166101c05250620005049050565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715620002f557620002f5620002b9565b60405290565b600082601f8301126200030d57600080fd5b815160206001600160401b03808311156200032c576200032c620002b9565b8260051b604051601f19603f83011681018181108482111715620003545762000354620002b9565b6040529384528581018301938381019250878511156200037357600080fd5b83870191505b84821015620003945781518352918301919083019062000379565b979650505050505050565b80516001600160a01b0381168114620003b757600080fd5b919050565b600080600080600060a08688031215620003d557600080fd5b85516001600160401b0380821115620003ed57600080fd5b90870190610120828a0312156200040357600080fd5b6200040d620002cf565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c0830151828111156200045657600080fd5b620004648b828601620002fb565b60c08301525060e08381015190820152610100928301519281019290925250945062000493602087016200039f565b9350620004a3604087016200039f565b9250620004b3606087016200039f565b9150608086015190509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600060018201620004fd57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161231c620006306000396000818161036401528181611030015281816114a50152818161192e015261197201526000818161025801526118b401526000818161039e0152610d3b015260008181610486015281816107cc01528181610af70152610f400152600081816104e201528181610bbf0152610bee0152600081816102b501528181610c350152610c690152600081816103160152610b7d01526000818161033d0152611cb10152600081816102dc015281816114580152611529015260008181610509015281816119a90152611a0001526000818161053001526117da01526000818161022301528181610a7e0152610ab2015261231c6000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c8063a694fc3a116100f9578063e1f1176d11610097578063eb338c9611610071578063eb338c9614610552578063f189e85a14610565578063f4dce7141461057a578063ffa1ad741461058257600080fd5b8063e1f1176d146104dd578063e77cdcc914610504578063eacdaabc1461052b57600080fd5b8063b6b55f25116100d3578063b6b55f2514610454578063c2c4c5c114610467578063cbcf252a14610481578063cd25fe38146104a857600080fd5b8063a694fc3a146103c9578063a74466ad146103dc578063b69ef8a81461044b57600080fd5b806356e760581161016657806372f702f31161014057806372f702f31461035f57806378e0613614610386578063809cee2f14610399578063879d9090146103c057600080fd5b806356e76058146102fe5780635829c5ec14610311578063592cf3fb1461033857600080fd5b80632e17de78116101a25780632e17de78146102925780633e732997146102a757806342cde4e8146102b057806352c824f5146102d757600080fd5b8063150b7a02146101c957806316a751721461021e5780632871405114610253575b600080fd5b6101e86101d7366004611d73565b630a85bd0160e11b95945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b6102457f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610215565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610215565b6102a56102a0366004611e12565b6105b3565b005b61024560025481565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61024561030c366004611e12565b610896565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b610245610394366004611e12565b6108b7565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61024560015481565b6102a56103d7366004611e12565b610a56565b6104206103ea366004611e12565b600460208190526000918252604090912080546001820154600383015492909301546001600160a01b0391821693909116919084565b604080516001600160a01b039586168152949093166020850152918301526060820152608001610215565b61024560005481565b6102a5610462366004611e12565b610ffd565b61046f61109f565b60405161021596959493929190611e66565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b6104cd6104b6366004611e12565b600090815260046020526040902060030154151590565b6040519015158152602001610215565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b610245610560366004611e12565b61139e565b61056d6113ae565b6040516102159190611f3e565b610245611454565b6105a6604051806040016040528060058152602001640302e312e360dc1b81525081565b6040516102159190611f51565b600081815260046020526040902060018101546001600160a01b0316331461060a57600181015460405163521eb56d60e11b81523360048201526001600160a01b0390911660248201526044015b60405180910390fd5b60008061061561109f565b95505050505091508061062d5761062a6113ae565b91505b60005b825181101561066a578483828151811061064c5761064c611f9f565b6020026020010151031561066a5761066381611fcb565b9050610630565b600484015460028501805460408051602080840282018101909252828152600093909290918301828280156106be57602002820191906000526020600020905b8154815260200190600101908083116106aa575b5050506003890154895460008c8152600460205260408120805473ffffffffffffffffffffffffffffffffffffffff199081168255600182018054909116905595965091946001600160a01b039091169350915061071f6002830182611cdd565b506000600382018190556004909101556005805461073f90600190611fe4565b8154811061074f5761074f611f9f565b90600052602060002001546005868154811061076d5761076d611f9f565b600091825260209091200155600580548061078a5761078a611ff7565b600082815260208120820160001990810191909155019055604051632142170760e11b8152306004820152336024820152604481018a90526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906342842e0e90606401600060405180830381600087803b15801561081057600080fd5b505af1158015610824573d6000803e3d6000fd5b50505050600084111561083b5761083b8185611489565b806001600160a01b0316336001600160a01b03168a7f246ee6115bfd84e00097b16569c2ff2f822026bb9595a82cd2c1e69d4b6ea50c8688876040516108839392919061200d565b60405180910390a4505050505050505050565b600381815481106108a657600080fd5b600091825260209091200154905081565b6000818152600460209081526040808320815160a08101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561093757602002820191906000526020600020905b815481526020019060010190808311610923575b505050505081526020016003820154815260200160048201548152505090508060800151915080606001516000036109855760405163e0606b6360e01b815260048101849052602401610601565b6000806000806000610995611512565b50509450945094509450945060005b84811015610a4a57888382815181106109bf576109bf611f9f565b602002602001015103610a3a5785841115610a155783868383815181106109e8576109e8611f9f565b60200260200101516109fa9190612032565b610a049190612049565b610a0e908961206b565b9750610a4a565b818181518110610a2757610a27611f9f565b602002602001015188610a0e919061206b565b610a4381611fcb565b90506109a4565b50505050505050919050565b600154600003610a795760405163afb0be3360e01b815260040160405180910390fd5b6005547f00000000000000000000000000000000000000000000000000000000000000008103610ade5760405163fd20861560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602401610601565b60405163ef0e239b60e01b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ef0e239b90602401600060405180830381865afa158015610b46573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b6e91908101906121a4565b9050806080015163ffffffff167f000000000000000000000000000000000000000000000000000000000000000014610bbd57604051637ad404bf60e11b815260048101849052602401610601565b7f000000000000000000000000000000000000000000000000000000000000000015801590610c10575080604001517f000000000000000000000000000000000000000000000000000000000000000014155b15610c3157604051637ad404bf60e11b815260048101849052602401610601565b60007f0000000000000000000000000000000000000000000000000000000000000000118015610c8b5750806060015163ffffffff167f000000000000000000000000000000000000000000000000000000000000000014155b15610cac57604051637ad404bf60e11b815260048101849052602401610601565b60048160c001516005811115610cc457610cc4612282565b14610d03578060c001516005811115610cdf57610cdf612282565b604051633c053f9d60e21b8152600481019190915260248101849052604401610601565b600081602001516001600160a01b0316803b806020016040519081016040528181526000908060200190933c805190602001209050807f000000000000000000000000000000000000000000000000000000000000000014610d8857602082015160405162a2307960e51b81526001600160a01b039091166004820152602401610601565b6003548015610e595760e083015151818114610dba57604051637ad404bf60e11b815260048101879052602401610601565b60005b81811015610e56578460e001518181518110610ddb57610ddb611f9f565b602002602001015163ffffffff1660038281548110610dfc57610dfc611f9f565b906000526020600020015414610e465760038181548110610e1f57610e1f611f9f565b9060005260206000200154604051632ab10b0b60e21b815260040161060191815260200190565b610e4f81611fcb565b9050610dbd565b50505b610e758584600001516bffffffffffffffffffffffff16611891565b600085815260046020908152604082209085015181546001600160a01b03821673ffffffffffffffffffffffffffffffffffffffff1991821617835560018301805490911633179055909190610eca90611a32565b8051909150610ee29060028401906020840190611cfe565b50426003830155600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001879055604051632142170760e11b8152336004820152306024820152604481018890527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e90606401600060405180830381600087803b158015610f8c57600080fd5b505af1158015610fa0573d6000803e3d6000fd5b5050505084602001516001600160a01b0316336001600160a01b0316887f5d43ac9b1b213902df90d405b0006308578486b6c62182c5df202ed572c844e484604051610fec9190611f3e565b60405180910390a450505050505050565b60008160005461100d919061206b565b905060008260015461101f919061206b565b6000839055600181905590506110577f0000000000000000000000000000000000000000000000000000000000000000333086611a43565b604080518481526020810184905290810182905233907f36af321ec8d3c75236829c5317affd40ddb308863a1236d2d277a4025cccee1e9060600160405180910390a2505050565b60608060006060806000806000806000806000806110bb611512565b965096509650965096509650965060008611156112bf5760008786111561122e5760008060015b8981101561118057888b8883815181106110fe576110fe611f9f565b60200260200101516111109190612032565b61111a9190612049565b9250611126838361206b565b915087818151811061113a5761113a611f9f565b602002602001015193508260046000868152602001908152602001600020600401600082825461116a919061206b565b90915550611179905081611fcb565b90506110e2565b50878a8760008151811061119657611196611f9f565b60200260200101516111a89190612032565b6111b29190612049565b91506111be828261206b565b9050866000815181106111d3576111d3611f9f565b60200260200101519250808a11156111fc576111ef818b611fe4565b6111f9908361206b565b91505b6000838152600460208190526040822001805484929061121d90849061206b565b9091555060009a506112b892505050565b60005b878110156112aa5785818151811061124b5761124b611f9f565b6020026020010151915084818151811061126757611267611f9f565b6020026020010151600460008481526020019081526020016000206004016000828254611294919061206b565b909155506112a3905081611fcb565b9050611231565b506112b58689611fe4565b97505b5060018790555b8051156113895760005b82518110156113465760008382815181106112e6576112e6611f9f565b6020026020010151905082828151811061130257611302611f9f565b6020026020010151600460008381526020019081526020016000206002019080519060200190611333929190611cfe565b50508061133f90611fcb565b90506112c9565b50426002556040805188815260208101889052600199507f21d81d5d656869e8ce3ba8d65526a2f0dbbcd3d36f5f9999eb7c84360e45eced910160405180910390a15b909c909b509399509097509550929350915050565b600581815481106108a657600080fd5b6005546060908067ffffffffffffffff8111156113cd576113cd61207e565b6040519080825280602002602001820160405280156113f6578160200160208202803683370190505b50915060005b8181101561144f576005818154811061141757611417611f9f565b906000526020600020015483828151811061143457611434611f9f565b602090810291909101015261144881611fcb565b90506113fc565b505090565b60007f0000000000000000000000000000000000000000000000000000000000000000600254611484919061206b565b905090565b8060008082825461149a9190611fe4565b909155506114cb90507f00000000000000000000000000000000000000000000000000000000000000008383611acd565b816001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243648260405161150691815260200190565b60405180910390a25050565b6002546001549060009081906060908190819081907f00000000000000000000000000000000000000000000000000000000000000006115528242611fe4565b101580156115605750600088115b15611887576005548067ffffffffffffffff8111156115815761158161207e565b6040519080825280602002602001820160405280156115aa578160200160208202803683370190505b5093508067ffffffffffffffff8111156115c6576115c661207e565b6040519080825280602002602001820160405280156115ef578160200160208202803683370190505b5095508067ffffffffffffffff81111561160b5761160b61207e565b604051908082528060200260200182016040528015611634578160200160208202803683370190505b5094508067ffffffffffffffff8111156116505761165061207e565b60405190808252806020026020018201604052801561168357816020015b606081526020019060019003908161166e5790505b50925060005b8181101561188457600581815481106116a4576116a4611f9f565b90600052602060002001548582815181106116c1576116c1611f9f565b6020026020010181815250506000600460008784815181106116e5576116e5611f9f565b60209081029190910181015182528101919091526040016000208054909150611716906001600160a01b0316611a32565b85838151811061172857611728611f9f565b60209081029190910101526003810154849081811115611746578091505b6117508242611fe4565b905060006117ca88868151811061176957611769611f9f565b6020026020010151856002018054806020026020016040519081016040528092919081815260200182805480156117bf57602002820191906000526020600020905b8154815260200190600101908083116117ab575b505050505084611b4a565b9050801561186f5760006117fe837f0000000000000000000000000000000000000000000000000000000000000000612032565b905061180a818e61206b565b9c50808b8f8151811061181f5761181f611f9f565b60200260200101818152505089868151811061183d5761183d611f9f565b60200260200101518c8f8151811061185757611857611f9f565b602090810291909101015261186b8e611fcb565b9d50505b505050508061187d90611fcb565b9050611689565b50505b5090919293949596565b604051633cebfa4f60e01b81526004810183905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690633cebfa4f906024016040805180830381865afa1580156118fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191e9190612298565b91509150816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146119a757604051630b80380d60e31b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015283166024820152604401610601565b7f0000000000000000000000000000000000000000000000000000000000000000816bffffffffffffffffffffffff161015611a2c57604051632b30b24760e21b81526bffffffffffffffffffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006024820152604401610601565b50505050565b6060611a3d82611b5f565b92915050565b60006040516323b872dd60e01b6000528460045283602452826044526020600060646000808a5af13d15601f3d1160016000511416171691506000606052806040525080611ac65760405163abae3d6d60e01b81526001600160a01b03808716600483015280861660248301528416604482015260648101839052608401610601565b5050505050565b600060405163a9059cbb60e01b6000528360045282602452602060006044600080895af13d15601f3d1160016000511416171691506000606052806040525080611a2c5760405163abae3d6d60e01b81526001600160a01b0380861660048301523060248301528416604482015260648101839052608401610601565b6000611b57848484611c07565b949350505050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663affed0e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be391906122cd565b81600081518110611bf657611bf6611f9f565b602002602001018181525050919050565b60008082118015611c4b575082600081518110611c2657611c26611f9f565b602002602001015184600081518110611c4157611c41611f9f565b6020026020010151115b15611cd65760008284600081518110611c6657611c66611f9f565b602002602001015186600081518110611c8157611c81611f9f565b6020026020010151611c939190611fe4565b611ca590670de0b6b3a7640000612032565b611caf9190612049565b7f000000000000000000000000000000000000000000000000000000000000000011159150505b9392505050565b5080546000825590600052602060002090810190611cfb9190611d49565b50565b828054828255906000526020600020908101928215611d39579160200282015b82811115611d39578251825591602001919060010190611d1e565b50611d45929150611d49565b5090565b5b80821115611d455760008155600101611d4a565b6001600160a01b0381168114611cfb57600080fd5b600080600080600060808688031215611d8b57600080fd5b8535611d9681611d5e565b94506020860135611da681611d5e565b935060408601359250606086013567ffffffffffffffff80821115611dca57600080fd5b818801915088601f830112611dde57600080fd5b813581811115611ded57600080fd5b896020828501011115611dff57600080fd5b9699959850939650602001949392505050565b600060208284031215611e2457600080fd5b5035919050565b600081518084526020808501945080840160005b83811015611e5b57815187529582019590820190600101611e3f565b509495945050505050565b60c081526000611e7960c0830189611e2b565b6020838203818501528189518084528284019150828160051b850101838c016000805b84811015611eef57878403601f19018652825180518086529088019088860190845b81811015611eda5783518352928a0192918a0191600101611ebe565b50509688019694505091860191600101611e9c565b5050508a60408801528681036060880152611f0a818b611e2b565b9450505050508281036080840152611f228186611e2b565b915050611f3360a083018415159052565b979650505050505050565b602081526000611cd66020830184611e2b565b600060208083528351808285015260005b81811015611f7e57858101830151858201604001528201611f62565b506000604082860101526040601f19601f8301168501019250505092915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201611fdd57611fdd611fb5565b5060010190565b81810381811115611a3d57611a3d611fb5565b634e487b7160e01b600052603160045260246000fd5b6060815260006120206060830186611e2b565b60208301949094525060400152919050565b8082028115828204841417611a3d57611a3d611fb5565b60008261206657634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115611a3d57611a3d611fb5565b634e487b7160e01b600052604160045260246000fd5b604051610100810167ffffffffffffffff811182821017156120b8576120b861207e565b60405290565b80516bffffffffffffffffffffffff811681146120da57600080fd5b919050565b80516120da81611d5e565b805163ffffffff811681146120da57600080fd5b8051600681106120da57600080fd5b600082601f83011261211e57600080fd5b8151602067ffffffffffffffff8083111561213b5761213b61207e565b8260051b604051601f19603f830116810181811084821117156121605761216061207e565b60405293845285810183019383810192508785111561217e57600080fd5b83870191505b84821015611f3357612195826120ea565b83529183019190830190612184565b6000602082840312156121b657600080fd5b815167ffffffffffffffff808211156121ce57600080fd5b9083019061010082860312156121e357600080fd5b6121eb612094565b6121f4836120be565b8152612202602084016120df565b60208201526040830151604082015261221d606084016120ea565b606082015261222e608084016120ea565b608082015261223f60a084016120ea565b60a082015261225060c084016120fe565b60c082015260e08301518281111561226757600080fd5b6122738782860161210d565b60e08301525095945050505050565b634e487b7160e01b600052602160045260246000fd5b600080604083850312156122ab57600080fd5b82516122b681611d5e565b91506122c4602084016120be565b90509250929050565b6000602082840312156122df57600080fd5b505191905056fea2646970667358221220892b3547cd047374259e7ce0b4c8c70e47fe8f83349b516cd97fa732cc75de9164736f6c63430008150033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101c45760003560e01c8063a694fc3a116100f9578063e1f1176d11610097578063eb338c9611610071578063eb338c9614610552578063f189e85a14610565578063f4dce7141461057a578063ffa1ad741461058257600080fd5b8063e1f1176d146104dd578063e77cdcc914610504578063eacdaabc1461052b57600080fd5b8063b6b55f25116100d3578063b6b55f2514610454578063c2c4c5c114610467578063cbcf252a14610481578063cd25fe38146104a857600080fd5b8063a694fc3a146103c9578063a74466ad146103dc578063b69ef8a81461044b57600080fd5b806356e760581161016657806372f702f31161014057806372f702f31461035f57806378e0613614610386578063809cee2f14610399578063879d9090146103c057600080fd5b806356e76058146102fe5780635829c5ec14610311578063592cf3fb1461033857600080fd5b80632e17de78116101a25780632e17de78146102925780633e732997146102a757806342cde4e8146102b057806352c824f5146102d757600080fd5b8063150b7a02146101c957806316a751721461021e5780632871405114610253575b600080fd5b6101e86101d7366004611d73565b630a85bd0160e11b95945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b6102457f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610215565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610215565b6102a56102a0366004611e12565b6105b3565b005b61024560025481565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61024561030c366004611e12565b610896565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b610245610394366004611e12565b6108b7565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b61024560015481565b6102a56103d7366004611e12565b610a56565b6104206103ea366004611e12565b600460208190526000918252604090912080546001820154600383015492909301546001600160a01b0391821693909116919084565b604080516001600160a01b039586168152949093166020850152918301526060820152608001610215565b61024560005481565b6102a5610462366004611e12565b610ffd565b61046f61109f565b60405161021596959493929190611e66565b61027a7f000000000000000000000000000000000000000000000000000000000000000081565b6104cd6104b6366004611e12565b600090815260046020526040902060030154151590565b6040519015158152602001610215565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b610245610560366004611e12565b61139e565b61056d6113ae565b6040516102159190611f3e565b610245611454565b6105a6604051806040016040528060058152602001640302e312e360dc1b81525081565b6040516102159190611f51565b600081815260046020526040902060018101546001600160a01b0316331461060a57600181015460405163521eb56d60e11b81523360048201526001600160a01b0390911660248201526044015b60405180910390fd5b60008061061561109f565b95505050505091508061062d5761062a6113ae565b91505b60005b825181101561066a578483828151811061064c5761064c611f9f565b6020026020010151031561066a5761066381611fcb565b9050610630565b600484015460028501805460408051602080840282018101909252828152600093909290918301828280156106be57602002820191906000526020600020905b8154815260200190600101908083116106aa575b5050506003890154895460008c8152600460205260408120805473ffffffffffffffffffffffffffffffffffffffff199081168255600182018054909116905595965091946001600160a01b039091169350915061071f6002830182611cdd565b506000600382018190556004909101556005805461073f90600190611fe4565b8154811061074f5761074f611f9f565b90600052602060002001546005868154811061076d5761076d611f9f565b600091825260209091200155600580548061078a5761078a611ff7565b600082815260208120820160001990810191909155019055604051632142170760e11b8152306004820152336024820152604481018a90526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906342842e0e90606401600060405180830381600087803b15801561081057600080fd5b505af1158015610824573d6000803e3d6000fd5b50505050600084111561083b5761083b8185611489565b806001600160a01b0316336001600160a01b03168a7f246ee6115bfd84e00097b16569c2ff2f822026bb9595a82cd2c1e69d4b6ea50c8688876040516108839392919061200d565b60405180910390a4505050505050505050565b600381815481106108a657600080fd5b600091825260209091200154905081565b6000818152600460209081526040808320815160a08101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561093757602002820191906000526020600020905b815481526020019060010190808311610923575b505050505081526020016003820154815260200160048201548152505090508060800151915080606001516000036109855760405163e0606b6360e01b815260048101849052602401610601565b6000806000806000610995611512565b50509450945094509450945060005b84811015610a4a57888382815181106109bf576109bf611f9f565b602002602001015103610a3a5785841115610a155783868383815181106109e8576109e8611f9f565b60200260200101516109fa9190612032565b610a049190612049565b610a0e908961206b565b9750610a4a565b818181518110610a2757610a27611f9f565b602002602001015188610a0e919061206b565b610a4381611fcb565b90506109a4565b50505050505050919050565b600154600003610a795760405163afb0be3360e01b815260040160405180910390fd5b6005547f00000000000000000000000000000000000000000000000000000000000000008103610ade5760405163fd20861560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602401610601565b60405163ef0e239b60e01b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ef0e239b90602401600060405180830381865afa158015610b46573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b6e91908101906121a4565b9050806080015163ffffffff167f000000000000000000000000000000000000000000000000000000000000000014610bbd57604051637ad404bf60e11b815260048101849052602401610601565b7f000000000000000000000000000000000000000000000000000000000000000015801590610c10575080604001517f000000000000000000000000000000000000000000000000000000000000000014155b15610c3157604051637ad404bf60e11b815260048101849052602401610601565b60007f0000000000000000000000000000000000000000000000000000000000000000118015610c8b5750806060015163ffffffff167f000000000000000000000000000000000000000000000000000000000000000014155b15610cac57604051637ad404bf60e11b815260048101849052602401610601565b60048160c001516005811115610cc457610cc4612282565b14610d03578060c001516005811115610cdf57610cdf612282565b604051633c053f9d60e21b8152600481019190915260248101849052604401610601565b600081602001516001600160a01b0316803b806020016040519081016040528181526000908060200190933c805190602001209050807f000000000000000000000000000000000000000000000000000000000000000014610d8857602082015160405162a2307960e51b81526001600160a01b039091166004820152602401610601565b6003548015610e595760e083015151818114610dba57604051637ad404bf60e11b815260048101879052602401610601565b60005b81811015610e56578460e001518181518110610ddb57610ddb611f9f565b602002602001015163ffffffff1660038281548110610dfc57610dfc611f9f565b906000526020600020015414610e465760038181548110610e1f57610e1f611f9f565b9060005260206000200154604051632ab10b0b60e21b815260040161060191815260200190565b610e4f81611fcb565b9050610dbd565b50505b610e758584600001516bffffffffffffffffffffffff16611891565b600085815260046020908152604082209085015181546001600160a01b03821673ffffffffffffffffffffffffffffffffffffffff1991821617835560018301805490911633179055909190610eca90611a32565b8051909150610ee29060028401906020840190611cfe565b50426003830155600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db001879055604051632142170760e11b8152336004820152306024820152604481018890527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e90606401600060405180830381600087803b158015610f8c57600080fd5b505af1158015610fa0573d6000803e3d6000fd5b5050505084602001516001600160a01b0316336001600160a01b0316887f5d43ac9b1b213902df90d405b0006308578486b6c62182c5df202ed572c844e484604051610fec9190611f3e565b60405180910390a450505050505050565b60008160005461100d919061206b565b905060008260015461101f919061206b565b6000839055600181905590506110577f0000000000000000000000000000000000000000000000000000000000000000333086611a43565b604080518481526020810184905290810182905233907f36af321ec8d3c75236829c5317affd40ddb308863a1236d2d277a4025cccee1e9060600160405180910390a2505050565b60608060006060806000806000806000806000806110bb611512565b965096509650965096509650965060008611156112bf5760008786111561122e5760008060015b8981101561118057888b8883815181106110fe576110fe611f9f565b60200260200101516111109190612032565b61111a9190612049565b9250611126838361206b565b915087818151811061113a5761113a611f9f565b602002602001015193508260046000868152602001908152602001600020600401600082825461116a919061206b565b90915550611179905081611fcb565b90506110e2565b50878a8760008151811061119657611196611f9f565b60200260200101516111a89190612032565b6111b29190612049565b91506111be828261206b565b9050866000815181106111d3576111d3611f9f565b60200260200101519250808a11156111fc576111ef818b611fe4565b6111f9908361206b565b91505b6000838152600460208190526040822001805484929061121d90849061206b565b9091555060009a506112b892505050565b60005b878110156112aa5785818151811061124b5761124b611f9f565b6020026020010151915084818151811061126757611267611f9f565b6020026020010151600460008481526020019081526020016000206004016000828254611294919061206b565b909155506112a3905081611fcb565b9050611231565b506112b58689611fe4565b97505b5060018790555b8051156113895760005b82518110156113465760008382815181106112e6576112e6611f9f565b6020026020010151905082828151811061130257611302611f9f565b6020026020010151600460008381526020019081526020016000206002019080519060200190611333929190611cfe565b50508061133f90611fcb565b90506112c9565b50426002556040805188815260208101889052600199507f21d81d5d656869e8ce3ba8d65526a2f0dbbcd3d36f5f9999eb7c84360e45eced910160405180910390a15b909c909b509399509097509550929350915050565b600581815481106108a657600080fd5b6005546060908067ffffffffffffffff8111156113cd576113cd61207e565b6040519080825280602002602001820160405280156113f6578160200160208202803683370190505b50915060005b8181101561144f576005818154811061141757611417611f9f565b906000526020600020015483828151811061143457611434611f9f565b602090810291909101015261144881611fcb565b90506113fc565b505090565b60007f0000000000000000000000000000000000000000000000000000000000000000600254611484919061206b565b905090565b8060008082825461149a9190611fe4565b909155506114cb90507f00000000000000000000000000000000000000000000000000000000000000008383611acd565b816001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243648260405161150691815260200190565b60405180910390a25050565b6002546001549060009081906060908190819081907f00000000000000000000000000000000000000000000000000000000000000006115528242611fe4565b101580156115605750600088115b15611887576005548067ffffffffffffffff8111156115815761158161207e565b6040519080825280602002602001820160405280156115aa578160200160208202803683370190505b5093508067ffffffffffffffff8111156115c6576115c661207e565b6040519080825280602002602001820160405280156115ef578160200160208202803683370190505b5095508067ffffffffffffffff81111561160b5761160b61207e565b604051908082528060200260200182016040528015611634578160200160208202803683370190505b5094508067ffffffffffffffff8111156116505761165061207e565b60405190808252806020026020018201604052801561168357816020015b606081526020019060019003908161166e5790505b50925060005b8181101561188457600581815481106116a4576116a4611f9f565b90600052602060002001548582815181106116c1576116c1611f9f565b6020026020010181815250506000600460008784815181106116e5576116e5611f9f565b60209081029190910181015182528101919091526040016000208054909150611716906001600160a01b0316611a32565b85838151811061172857611728611f9f565b60209081029190910101526003810154849081811115611746578091505b6117508242611fe4565b905060006117ca88868151811061176957611769611f9f565b6020026020010151856002018054806020026020016040519081016040528092919081815260200182805480156117bf57602002820191906000526020600020905b8154815260200190600101908083116117ab575b505050505084611b4a565b9050801561186f5760006117fe837f0000000000000000000000000000000000000000000000000000000000000000612032565b905061180a818e61206b565b9c50808b8f8151811061181f5761181f611f9f565b60200260200101818152505089868151811061183d5761183d611f9f565b60200260200101518c8f8151811061185757611857611f9f565b602090810291909101015261186b8e611fcb565b9d50505b505050508061187d90611fcb565b9050611689565b50505b5090919293949596565b604051633cebfa4f60e01b81526004810183905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690633cebfa4f906024016040805180830381865afa1580156118fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191e9190612298565b91509150816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146119a757604051630b80380d60e31b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015283166024820152604401610601565b7f0000000000000000000000000000000000000000000000000000000000000000816bffffffffffffffffffffffff161015611a2c57604051632b30b24760e21b81526bffffffffffffffffffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006024820152604401610601565b50505050565b6060611a3d82611b5f565b92915050565b60006040516323b872dd60e01b6000528460045283602452826044526020600060646000808a5af13d15601f3d1160016000511416171691506000606052806040525080611ac65760405163abae3d6d60e01b81526001600160a01b03808716600483015280861660248301528416604482015260648101839052608401610601565b5050505050565b600060405163a9059cbb60e01b6000528360045282602452602060006044600080895af13d15601f3d1160016000511416171691506000606052806040525080611a2c5760405163abae3d6d60e01b81526001600160a01b0380861660048301523060248301528416604482015260648101839052608401610601565b6000611b57848484611c07565b949350505050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663affed0e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be391906122cd565b81600081518110611bf657611bf6611f9f565b602002602001018181525050919050565b60008082118015611c4b575082600081518110611c2657611c26611f9f565b602002602001015184600081518110611c4157611c41611f9f565b6020026020010151115b15611cd65760008284600081518110611c6657611c66611f9f565b602002602001015186600081518110611c8157611c81611f9f565b6020026020010151611c939190611fe4565b611ca590670de0b6b3a7640000612032565b611caf9190612049565b7f000000000000000000000000000000000000000000000000000000000000000011159150505b9392505050565b5080546000825590600052602060002090810190611cfb9190611d49565b50565b828054828255906000526020600020908101928215611d39579160200282015b82811115611d39578251825591602001919060010190611d1e565b50611d45929150611d49565b5090565b5b80821115611d455760008155600101611d4a565b6001600160a01b0381168114611cfb57600080fd5b600080600080600060808688031215611d8b57600080fd5b8535611d9681611d5e565b94506020860135611da681611d5e565b935060408601359250606086013567ffffffffffffffff80821115611dca57600080fd5b818801915088601f830112611dde57600080fd5b813581811115611ded57600080fd5b896020828501011115611dff57600080fd5b9699959850939650602001949392505050565b600060208284031215611e2457600080fd5b5035919050565b600081518084526020808501945080840160005b83811015611e5b57815187529582019590820190600101611e3f565b509495945050505050565b60c081526000611e7960c0830189611e2b565b6020838203818501528189518084528284019150828160051b850101838c016000805b84811015611eef57878403601f19018652825180518086529088019088860190845b81811015611eda5783518352928a0192918a0191600101611ebe565b50509688019694505091860191600101611e9c565b5050508a60408801528681036060880152611f0a818b611e2b565b9450505050508281036080840152611f228186611e2b565b915050611f3360a083018415159052565b979650505050505050565b602081526000611cd66020830184611e2b565b600060208083528351808285015260005b81811015611f7e57858101830151858201604001528201611f62565b506000604082860101526040601f19601f8301168501019250505092915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201611fdd57611fdd611fb5565b5060010190565b81810381811115611a3d57611a3d611fb5565b634e487b7160e01b600052603160045260246000fd5b6060815260006120206060830186611e2b565b60208301949094525060400152919050565b8082028115828204841417611a3d57611a3d611fb5565b60008261206657634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115611a3d57611a3d611fb5565b634e487b7160e01b600052604160045260246000fd5b604051610100810167ffffffffffffffff811182821017156120b8576120b861207e565b60405290565b80516bffffffffffffffffffffffff811681146120da57600080fd5b919050565b80516120da81611d5e565b805163ffffffff811681146120da57600080fd5b8051600681106120da57600080fd5b600082601f83011261211e57600080fd5b8151602067ffffffffffffffff8083111561213b5761213b61207e565b8260051b604051601f19603f830116810181811084821117156121605761216061207e565b60405293845285810183019383810192508785111561217e57600080fd5b83870191505b84821015611f3357612195826120ea565b83529183019190830190612184565b6000602082840312156121b657600080fd5b815167ffffffffffffffff808211156121ce57600080fd5b9083019061010082860312156121e357600080fd5b6121eb612094565b6121f4836120be565b8152612202602084016120df565b60208201526040830151604082015261221d606084016120ea565b606082015261222e608084016120ea565b608082015261223f60a084016120ea565b60a082015261225060c084016120fe565b60c082015260e08301518281111561226757600080fd5b6122738782860161210d565b60e08301525095945050505050565b634e487b7160e01b600052602160045260246000fd5b600080604083850312156122ab57600080fd5b82516122b681611d5e565b91506122c4602084016120be565b90509250929050565b6000602082840312156122df57600080fd5b505191905056fea2646970667358221220892b3547cd047374259e7ce0b4c8c70e47fe8f83349b516cd97fa732cc75de9164736f6c63430008150033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/backend/operate/data/contracts/service_staking_token/contract.py b/backend/operate/data/contracts/service_staking_token/contract.py new file mode 100644 index 000000000..e0e91a0b2 --- /dev/null +++ b/backend/operate/data/contracts/service_staking_token/contract.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the class to connect to the `ServiceStakingTokenMechUsage` contract.""" + +from aea.common import JSONLike +from aea.configurations.base import PublicId +from aea.contracts.base import Contract +from aea.crypto.base import LedgerApi + + +class ServiceStakingTokenContract(Contract): + """The Service Staking contract.""" + + contract_id = PublicId.from_str("valory/service_staking_token:0.1.0") + + @classmethod + def is_service_staked( + cls, + ledger_api: LedgerApi, + contract_address: str, + service_id: int, + ) -> JSONLike: + """Check whether the service is staked.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + res = contract_instance.functions.isServiceStaked(service_id).call() + return dict(data=res) + + @classmethod + def build_stake_tx( + cls, + ledger_api: LedgerApi, + contract_address: str, + service_id: int, + ) -> JSONLike: + """Build stake tx.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + data = contract_instance.encodeABI("stake", args=[service_id]) + return dict(data=bytes.fromhex(data[2:])) + + @classmethod + def build_checkpoint_tx( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> JSONLike: + """Build checkpoint tx.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + data = contract_instance.encodeABI("checkpoint") + return dict(data=bytes.fromhex(data[2:])) + + @classmethod + def build_unstake_tx( + cls, + ledger_api: LedgerApi, + contract_address: str, + service_id: int, + ) -> JSONLike: + """Build unstake tx.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + data = contract_instance.encodeABI("unstake", args=[service_id]) + return dict(data=bytes.fromhex(data[2:])) + + @classmethod + def available_rewards( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> JSONLike: + """Get the available rewards.""" + contract_instance = cls.get_instance(ledger_api, contract_address) + res = contract_instance.functions.availableRewards().call() + return dict(data=res) + + @classmethod + def get_staking_rewards( + cls, + ledger_api: LedgerApi, + contract_address: str, + service_id: int, + ) -> JSONLike: + """Get the service's staking rewards.""" + contract = cls.get_instance(ledger_api, contract_address) + reward = contract.functions.calculateServiceStakingReward(service_id).call() + return dict(data=reward) + + @classmethod + def get_next_checkpoint_ts( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> JSONLike: + """Get the next checkpoint's timestamp.""" + contract = cls.get_instance(ledger_api, contract_address) + ts = contract.functions.getNextRewardCheckpointTimestamp().call() + return dict(data=ts) + + @classmethod + def get_liveness_period( + cls, + ledger_api: LedgerApi, + contract_address: str, + ) -> JSONLike: + """Retrieve the liveness period.""" + contract = cls.get_instance(ledger_api, contract_address) + liveness_period = contract.functions.livenessPeriod().call() + return dict(data=liveness_period) + + @classmethod + def get_service_info( + cls, + ledger_api: LedgerApi, + contract_address: str, + service_id: int, + ) -> JSONLike: + """Retrieve the service info for a service.""" + contract = cls.get_instance(ledger_api, contract_address) + info = contract.functions.mapServiceInfo(service_id).call() + return dict(data=info) diff --git a/backend/operate/data/contracts/service_staking_token/contract.yaml b/backend/operate/data/contracts/service_staking_token/contract.yaml new file mode 100644 index 000000000..7a9c886a3 --- /dev/null +++ b/backend/operate/data/contracts/service_staking_token/contract.yaml @@ -0,0 +1,23 @@ +name: service_staking_token +author: valory +version: 0.1.0 +type: contract +description: Service staking token contract +license: Apache-2.0 +aea_version: '>=1.0.0, <2.0.0' +fingerprint: + __init__.py: bafybeid3wfzglolebuo6jrrsopswzu4lk77bm76mvw3euizlsjtnt3wmgu + build/ServiceStakingToken.json: bafybeie2xbccvzmjuptqfqumctv6gtyyeoxe7fkx3t7fgbto7wbdugbqxm + contract.py: bafybeihhdg5mvd3jwc7q445ntdahaiyjpt2ne6ipkiaymycbrw5iwzuble +fingerprint_ignore_patterns: [] +contracts: [] +class_name: ServiceStakingTokenContract +contract_interface_paths: + ethereum: build/ServiceStakingToken.json +dependencies: + open-aea-ledger-ethereum: + version: ==1.42.0 + open-aea-test-autonomy: + version: ==0.13.6 + web3: + version: <7,>=6.0.0 diff --git a/backend/operate/ledger/ethereum.py b/backend/operate/ledger/ethereum.py index 50d67a1b2..8d9d4ad77 100644 --- a/backend/operate/ledger/ethereum.py +++ b/backend/operate/ledger/ethereum.py @@ -21,20 +21,19 @@ import typing as t -from aea_ledger_ethereum import EthereumCrypto +from aea_ledger_ethereum import EthereumApi, EthereumCrypto from operate.ledger.base import LedgerHelper from operate.types import LedgerType -from web3 import HTTPProvider, Web3 class Ethereum(LedgerHelper): """Ethereum ledger helper.""" - api: Web3 + api: EthereumApi def __init__(self, rpc: str) -> None: super().__init__(rpc) - self.api = Web3(provider=HTTPProvider(self.rpc)) + self.api = EthereumApi(address=self.rpc) def create_key(self) -> t.Dict: """Create key.""" diff --git a/backend/operate/ledger/profiles.py b/backend/operate/ledger/profiles.py index db2c1215d..3f95407c8 100644 --- a/backend/operate/ledger/profiles.py +++ b/backend/operate/ledger/profiles.py @@ -33,3 +33,11 @@ } ) } + +STAKING = { + ChainType.GNOSIS: "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326", +} + +OLAS = { + ChainType.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", +} diff --git a/backend/operate/services/manage.py b/backend/operate/services/manage.py index 91d159848..fa5fef3fa 100644 --- a/backend/operate/services/manage.py +++ b/backend/operate/services/manage.py @@ -26,6 +26,7 @@ from pathlib import Path from aea.helpers.base import IPFSHash +from autonomy.chain.base import registry_contracts from autonomy.deploy.constants import ( AGENT_KEYS_DIR, BENCHMARKS_DIR, @@ -35,11 +36,18 @@ VENVS_DIR, ) from operate.http import Resource +from operate.http.exceptions import BadRequest from operate.keys import Keys -from operate.ledger.profiles import CONTRACTS +from operate.ledger.profiles import CONTRACTS, OLAS, STAKING from operate.services.protocol import OnChainManager from operate.services.service import Service -from operate.types import ChainData, ServicesType, ServiceTemplate, ServiceType +from operate.types import ( + ChainData, + ConfigurationTemplate, + ServicesType, + ServiceTemplate, + ServiceType, +) from starlette.types import Receive, Scope, Send from typing_extensions import TypedDict @@ -140,9 +148,18 @@ def json(self) -> GetServices: data.append(Service.load(path=service).json) return data - def create(self, data: PostServices) -> PostServices: - """Create a service.""" - phash = data["hash"] + def _stake(self) -> None: + """Stake a service.""" + + def _create( + self, + phash: str, + configuration: ConfigurationTemplate, + instances: t.Optional[t.List[str]] = None, + update_token: t.Optional[int] = None, + reuse_multisig: bool = False, + ) -> Service: + """Create a new service.""" if (self.path / phash).exists(): # For testing only shutil.rmtree(self.path / phash) @@ -156,42 +173,79 @@ def create(self, data: PostServices) -> PostServices: ) ledger = service.helper.ledger_config() - deployment = service.helper.deployment_config() - instances = [ + instances = instances or [ self.keys.create() for _ in range(service.helper.config.number_of_agents) ] ocm = OnChainManager( - rpc=data["rpc"], + rpc=configuration["rpc"], key=self.key, contracts=CONTRACTS[ledger["chain"]], ) + if configuration["use_staking"] and not ocm.staking_slots_available( + staking_address=STAKING[ledger["chain"]], + ): + raise ValueError("No staking slots available") + # Update to user provided RPC - ledger["rpc"] = data["rpc"] + ledger["rpc"] = configuration["rpc"] logging.info(f"Minting service {phash}") service_id = t.cast( int, ocm.mint( package_path=service.service_path, - agent_id=deployment["chain"]["agent_id"], + agent_id=configuration["agent_id"], number_of_slots=service.helper.config.number_of_agents, - cost_of_bond=deployment["chain"]["cost_of_bond"], - threshold=deployment["chain"]["threshold"], - nft=IPFSHash(deployment["chain"]["nft"]), + cost_of_bond=( + configuration["olas_required_to_bond"] + if configuration["use_staking"] + else configuration["cost_of_bond"] + ), + threshold=configuration["threshold"], + nft=IPFSHash(configuration["nft"]), + update_token=update_token, + token=OLAS[ledger["chain"]] if configuration["use_staking"] else None, ).get("token"), ) logging.info(f"Activating service {phash}") - ocm.activate(service_id=service_id) + if configuration["use_staking"]: + required_olas = ( + configuration["olas_cost_of_bond"] + + configuration["olas_required_to_stake"] + ) + balance = ( + registry_contracts.erc20.get_instance( + ledger_api=ocm.ledger_api, + contract_address=OLAS[ledger["chain"]], + ) + .functions.balanceOf(ocm.crypto.address) + .call() + ) + if balance < required_olas: + raise BadRequest( + "You don't have enough olas to stake, " + f"required olas: {required_olas}; your balance {balance}" + ) + + ocm.activate( + service_id=service_id, + token=OLAS[ledger["chain"]] if configuration["use_staking"] else None, + ) ocm.register( service_id=service_id, instances=instances, - agents=[deployment["chain"]["agent_id"] for _ in instances], + agents=[configuration["agent_id"] for _ in instances], + token=OLAS[ledger["chain"]] if configuration["use_staking"] else None, ) logging.info(f"Deploying service {phash}") - ocm.deploy(service_id=service_id) + ocm.deploy( + service_id=service_id, + reuse_multisig=reuse_multisig, + token=OLAS[ledger["chain"]] if configuration["use_staking"] else None, + ) logging.info(f"Updating service {phash}") info = ocm.info(token_id=service_id) @@ -206,26 +260,38 @@ def create(self, data: PostServices) -> PostServices: ) service.store() + if configuration["use_staking"]: + ocm.stake( + service_id=service_id, + service_registry=CONTRACTS[ledger["chain"]]["service_registry"], + staking_contract=STAKING[ledger["chain"]], + ) + logging.info(f"Building deployment for service {phash}") deployment = service.deployment() deployment.create({}) deployment.store() + return service + + def create(self, data: PostServices) -> PostServices: + """Create a service.""" + service = self._create( + phash=data["hash"], + configuration=data["configuration"], + ) return service.json def update(self, data: PutServices) -> ServiceType: """Update service using a template.""" # NOTE: This method contains a lot of repetative code - - # Load old service - old = Service.load(path=self.path / data["old"]) - - rpc = data["new"]["rpc"] - phash = data["new"]["hash"] - + rpc = data["new"]["configuration"]["rpc"] + phash = data["new"]["configuration"]["hash"] if (self.path / phash).exists(): # For testing only shutil.rmtree(self.path / phash) + # Load old service + old = Service.load(path=self.path / data["old"]) instances = old.chain_data["instances"] ocm = OnChainManager( rpc=rpc, @@ -251,68 +317,14 @@ def update(self, data: PutServices) -> ServiceType: multisig=old.chain_data["multisig"], owner_key=owner_key, ) - - logging.info(f"Fetching service {phash}") - service = Service.new( - path=self.path, + service = self._create( phash=phash, - keys=[], - chain_data=ChainData(), - ledger={}, - ) - - ledger = service.helper.ledger_config() - deployment = service.helper.deployment_config() - - # Update to user provided RPC - ledger["rpc"] = data["new"]["rpc"] - - logging.info(f"Minting service {phash}") - service_id = t.cast( - int, - ocm.mint( - package_path=service.service_path, - agent_id=deployment["chain"]["agent_id"], - number_of_slots=service.helper.config.number_of_agents, - cost_of_bond=deployment["chain"]["cost_of_bond"], - threshold=deployment["chain"]["threshold"], - nft=IPFSHash(deployment["chain"]["nft"]), - update_token=old.chain_data["token"], - ).get("token"), - ) - - logging.info(f"Activating service {phash}") - ocm.activate(service_id=service_id) - ocm.register( - service_id=service_id, + rpc=rpc, instances=instances, - agents=[deployment["chain"]["agent_id"] for _ in instances], - ) - - logging.info(f"Deploying service {phash}") - ocm.deploy( - service_id=service_id, reuse_multisig=True, + update_token=old.chain_data["token"], ) - logging.info(f"Updating service {phash}") - info = ocm.info(token_id=service_id) - service.ledger = ledger - service.keys = [self.keys.get(key=key) for key in instances] - service.chain_data = ChainData( - { - "token": service_id, - "instances": info["instances"], - "multisig": info["multisig"], - } - ) - service.store() - - # Build docker-compose deployment - deployment = service.deployment() - deployment.create({}) - deployment.store() - # try: # shutil.rmtree(old.path) # except Exception as e: diff --git a/backend/operate/services/protocol.py b/backend/operate/services/protocol.py index 1452abcea..62a6325b3 100644 --- a/backend/operate/services/protocol.py +++ b/backend/operate/services/protocol.py @@ -31,18 +31,36 @@ from typing import Optional, Union from aea.configurations.data_types import PackageType +from aea.crypto.base import Crypto, LedgerApi from aea.helpers.base import IPFSHash, cd from aea_ledger_ethereum.ethereum import EthereumCrypto from autonomy.chain.base import registry_contracts from autonomy.chain.config import ChainConfigs, ChainType, ContractConfigs from autonomy.chain.service import get_agent_instances, get_service_info +from autonomy.chain.tx import TxSettler from autonomy.cli.helpers.chain import MintHelper as MintManager from autonomy.cli.helpers.chain import OnChainHelper from autonomy.cli.helpers.chain import ServiceHelper as ServiceManager from hexbytes import HexBytes +from operate.data import DATA_DIR +from operate.data.contracts.service_staking_token.contract import ( + ServiceStakingTokenContract, +) +from operate.ledger.profiles import CONTRACTS from ._subgraph import SubgraphClient +ZERO_ETH = 0 + + +class StakingState(Enum): + """Staking state enumeration for the staking.""" + + UNSTAKED = 0 + STAKED = 1 + EVICTED = 2 + + NULL_ADDRESS: str = "0x" + "0" * 40 MAX_UINT256 = 2**256 - 1 @@ -154,15 +172,142 @@ def skill_input_hex_to_payload(payload: str) -> dict: return tx_params -class OnChainManager: - """On chain service management.""" +class StakingManager(OnChainHelper): + """Helper class for staking a service.""" - def __init__( + def __init__(self, key: Path, chain_type: ChainType = ChainType.CUSTOM) -> None: + """Initialize object.""" + super().__init__(key=key, chain_type=chain_type) + self.staking_ctr = t.cast( + ServiceStakingTokenContract, + ServiceStakingTokenContract.from_dir( + directory=str(DATA_DIR / "contracts" / "service_staking_token") + ), + ) + + def status(self, service_id: int, staking_contract: str) -> StakingState: + """Is the service staked?""" + return StakingState( + self.staking_ctr.get_instance( + ledger_api=self.ledger_api, + contract_address=staking_contract, + ) + .getServiceStakingState(service_id) + .call() + ) + + def slots_available(self, staking_contract: str) -> bool: + """Check if there are available slots on the staking contract""" + instance = self.staking_ctr.get_instance( + ledger_api=self.ledger_api, + contract_address=staking_contract, + ) + return instance.maxNumServices().call() - len(instance.getServiceIds().call()) + + def stake( self, - rpc: str, - key: Path, - contracts: t.Dict, + service_id: int, + service_registry: str, + staking_contract: str, ) -> None: + """Stake the service""" + status = self.status(service_id) + if status == StakingState.STAKED: + raise ValueError("Service already stacked") + + if status == StakingState.EVICTED: + raise ValueError("Service is evicted") + + if not self.slots_available(): + raise ValueError("No sataking slots available.") + + tx_settler = TxSettler( + ledger_api=self.ledger_api, + crypto=self.crypto, + chain_type=self.chain_type, + timeout=self.timeout, + retries=self.retries, + sleep=self.sleep, + ) + + # we make use of the ERC20 contract to build the approval transaction + # since it has the same interface as ERC721 we might want to create + # a ERC721 contract package + + def _build_approval_tx(*args, **kargs) -> t.Dict: + return registry_contracts.erc20.get_approve_tx( + ledger_api=self.ledger_api, + contract_address=service_registry, + spender=staking_contract, + amount=service_id, + ) + + setattr(tx_settler, "build", _build_approval_tx) + tx_settler.transact( + method=lambda: {}, + contract="", + kwargs={}, + dry_run=False, + ) + + def _build_staking_tx(*args, **kargs) -> t.Dict: + return { + "data": self.staking_ctr.build_stake_tx( + ledger_api=self.ledger_api, + contract_address=staking_contract, + service_id=service_id, + ).pop("data"), + "to": staking_contract, + "value": ZERO_ETH, + } + + setattr(tx_settler, "build", _build_staking_tx) + tx_settler.transact( + method=lambda: {}, + contract="", + kwargs={}, + dry_run=False, + ) + + def unstake(self, service_id: int, staking_contract: str) -> None: + """Unstake the service""" + if self.status(service_id=service_id) != StakingState.STAKED: + raise ValueError("Service not staked.") + + tx_settler = TxSettler( + ledger_api=self.ledger_api, + crypto=self.crypto, + chain_type=self.chain_type, + timeout=self.timeout, + retries=self.retries, + sleep=self.sleep, + ) + + # TODO: check unstaking availability for EVICTED services + def _build_unstaking_tx() -> t.Dict: + self.staking_ctr.get_instance( + ledger_api=self.ledger_api, contract_address=staking_contract + ) + data = self.staking_ctr.encodeABI("unstake", args=[service_id]) + return { + "data": bytes.fromhex(data[2:]), + "to": CONTRACTS[ChainType.GNOSIS]["ServiceStakingToken"], + "value": ZERO_ETH, + } + + setattr(tx_settler, "build", _build_unstaking_tx) + tx_settler.transact( + method=lambda: {}, + contract="", + kwargs={}, + dry_run=False, + ) + + +class OnChainManager: + """On chain service management.""" + + def __init__(self, rpc: str, key: Path, contracts: t.Dict) -> None: """On chain manager.""" self.rpc = rpc self.key = key @@ -178,6 +323,26 @@ def _patch(self) -> None: for name, address in self.contracts.items(): ContractConfigs.get(name=name).contracts[self.chain_type] = address + @property + def crypto(self) -> Crypto: + """Load crypto object.""" + self._patch() + _, crypto = OnChainHelper.get_ledger_and_crypto_objects( + chain_type=self.chain_type, + key=self.key, + ) + return crypto + + @property + def ledger_api(self) -> LedgerApi: + """Load ledger api object.""" + self._patch() + ledger_api, _ = OnChainHelper.get_ledger_and_crypto_objects( + chain_type=self.chain_type, + key=self.key, + ) + return ledger_api + def info(self, token_id: int) -> t.Dict: """Get service info.""" self._patch() @@ -224,6 +389,7 @@ def mint( threshold: int, nft: Optional[Union[Path, IPFSHash]], update_token: t.Optional[int] = None, + token: t.Optional[str] = None, ): "Mint service." # TODO: Support for update @@ -259,6 +425,7 @@ def mint( number_of_slots=number_of_slots, cost_of_bond=cost_of_bond, threshold=threshold, + token=token, ) (metadata,) = Path(temp).glob("*.json") published = { @@ -333,6 +500,7 @@ def swap( ) -> None: """Swap safe owner.""" logging.info(f"Swapping safe for service {service_id} [{multisig}]...") + self._patch() manager = ServiceManager( service_id=service_id, chain_type=self.chain_type, @@ -416,13 +584,10 @@ def swap( if receipt["status"] != 1: raise RuntimeError("Error swapping owners") - def terminate( - self, - service_id: int, - token: t.Optional[str] = None, - ) -> None: + def terminate(self, service_id: int, token: t.Optional[str] = None) -> None: """Terminate service.""" logging.info(f"Terminating service {service_id}...") + self._patch() with contextlib.redirect_stdout(io.StringIO()): ServiceManager( service_id=service_id, @@ -432,13 +597,10 @@ def terminate( token=token, ).terminate_service() - def unbond( - self, - service_id: int, - token: t.Optional[str] = None, - ) -> None: + def unbond(self, service_id: int, token: t.Optional[str] = None) -> None: """Unbond service.""" logging.info(f"Unbonding service {service_id}...") + self._patch() with contextlib.redirect_stdout(io.StringIO()): ServiceManager( service_id=service_id, @@ -447,3 +609,38 @@ def unbond( ).check_is_service_token_secured( token=token, ).unbond_service() + + def staking_slots_available(self, staking_contract: str) -> None: + """Stake service.""" + StakingManager( + key=self.key, + chain_type=self.chain_type, + ).slots_available( + staking_contract=staking_contract, + ) + + def stake( + self, + service_id: int, + service_registry: str, + staking_contract: str, + ) -> None: + """Stake service.""" + StakingManager( + key=self.key, + chain_type=self.chain_type, + ).stake( + service_id=service_id, + service_registry=service_registry, + staking_contract=staking_contract, + ) + + def unstake(self, service_id: int, staking_contract: str) -> None: + """Unstake service.""" + StakingManager( + key=self.key, + chain_type=self.chain_type, + ).unstake( + service_id=service_id, + staking_contract=staking_contract, + ) diff --git a/backend/operate/services/service.py b/backend/operate/services/service.py index ab9c40590..39273aad5 100644 --- a/backend/operate/services/service.py +++ b/backend/operate/services/service.py @@ -239,10 +239,7 @@ def create(self, data: t.Dict) -> DeploymentType: _volumes = [] for volume, mount in ( - service.helper.deployment_config() - .get("local", {}) - .get("volumes", {}) - .items() + service.helper.deployment_config().get("volumes", {}).items() ): (build / volume).mkdir(exist_ok=True) _volumes.append(f"./{volume}:{mount}:Z") diff --git a/backend/operate/types.py b/backend/operate/types.py index 09918db86..1a8a82c97 100644 --- a/backend/operate/types.py +++ b/backend/operate/types.py @@ -157,18 +157,10 @@ class ChainDeployment(TypedDict): required_funds: float -class LocalDeployment(TypedDict): - """Local deployment template.""" - - ports: t.Dict - volumes: t.Dict[str, str] - - class DeploymentConfig(TypedDict): """Deployments template.""" - chain: ChainDeployment - local: LocalDeployment + volumes: t.Dict[str, str] class ServiceType(TypedDict): @@ -185,14 +177,35 @@ class ServiceType(TypedDict): ServicesType = t.List[ServiceType] +class FundRequirementsTemplate(TypedDict): + """Fund requirement template.""" + + agent: float + safe: float + + +class ConfigurationTemplate(TypedDict): + """Configuration template.""" + + nft: str + rpc: str + agent_id: int + threshold: int + use_staking: bool + cost_of_bond: int + olas_required_to_bond: int + olas_required_to_stake: int + fund_requirements: FundRequirementsTemplate + + class ServiceTemplate(TypedDict): """Service template.""" - rpc: str name: str hash: str image: str description: str + configuration: ConfigurationTemplate class Action(enum.IntEnum): diff --git a/backend/scripts/endpoint_e2e_test.py b/backend/scripts/test_e2e.py similarity index 83% rename from backend/scripts/endpoint_e2e_test.py rename to backend/scripts/test_e2e.py index 259e35e1c..acc74e60b 100644 --- a/backend/scripts/endpoint_e2e_test.py +++ b/backend/scripts/test_e2e.py @@ -19,25 +19,23 @@ # ------------------------------------------------------------------------------ """This module contains e2e tests.""" +from pathlib import Path + import requests +from aea.helpers.yaml_utils import yaml_load from aea_ledger_ethereum.ethereum import EthereumApi, EthereumCrypto -TRADER_TEMPLATE = { - "name": "Trader Agent", - "description": "Trader agent for omen prediction markets", - "hash": "bafybeigiwlvm6ey4dmlztg3z4xyvpol23n444vliivx2ybuki7xo4f3pae", - "image": "https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75", - "rpc": "http://localhost:8545", # User provided -} - BASE_URL = "http://localhost:8000/api" def test_endpoint_e2e(): + with Path("templates/trader.yaml").open("r", encoding="utf-8") as stream: + trader_template = yaml_load(stream=stream) + print("Creating service using template") response = requests.post( url=f"{BASE_URL}/services", - json=TRADER_TEMPLATE, + json=trader_template, ).json() print(response) @@ -72,8 +70,8 @@ def test_endpoint_e2e(): digest = ledger_api.send_signed_transaction(stx) ledger_api.get_transaction_receipt(tx_digest=digest) - old = TRADER_TEMPLATE["hash"] - TRADER_TEMPLATE["hash"] = ( + old = trader_template["hash"] + trader_template["hash"] = ( "bafybeicxdpkuk5z5zfbkso7v5pywf4v7chxvluyht7dtgalg6dnhl7ejoe" ) print( @@ -81,7 +79,7 @@ def test_endpoint_e2e(): url=f"{BASE_URL}/services", json={ "old": old, - "new": TRADER_TEMPLATE, + "new": trader_template, }, ).content.decode() ) diff --git a/backend/scripts/test_staking_e2e.py b/backend/scripts/test_staking_e2e.py new file mode 100644 index 000000000..1e3d8d779 --- /dev/null +++ b/backend/scripts/test_staking_e2e.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2021-2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ +"""This module contains e2e tests.""" + +from pathlib import Path + +import requests +from aea.helpers.yaml_utils import yaml_load +from aea_ledger_ethereum.ethereum import EthereumApi, EthereumCrypto + +BASE_URL = "http://localhost:8000/api" + + +def test_endpoint_e2e(): + with Path("templates/trader.yaml").open("r", encoding="utf-8") as stream: + trader_template = yaml_load(stream=stream) + trader_template["configuration"]["use_staking"] = True + + print("Creating service using template") + response = requests.post( + url=f"{BASE_URL}/services", + json=trader_template, + ).json() + print(response) + + input("> Press enter to start") + print( + requests.get( + url=f"{BASE_URL}/services/bafybeigiwlvm6ey4dmlztg3z4xyvpol23n444vliivx2ybuki7xo4f3pae/deploy/", + ).content.decode() + ) + + input("> Press enter to stop") + print( + requests.get( + url=f"{BASE_URL}/services/bafybeigiwlvm6ey4dmlztg3z4xyvpol23n444vliivx2ybuki7xo4f3pae/stop/", + ).content.decode() + ) + + input("> Press enter to update") + # Fund agent instance for swapping + ledger_api = EthereumApi(address="http://localhost:8545") + crypto = EthereumCrypto(".operate/key") + (owner,) = response["chain_data"]["instances"] + tx = ledger_api.get_transfer_transaction( + sender_address=crypto.address, + destination_address=owner, + amount=1000000000000000, + tx_fee=50000, + tx_nonce="0x", + chain_id=100, + ) + stx = crypto.sign_transaction(transaction=tx) + digest = ledger_api.send_signed_transaction(stx) + ledger_api.get_transaction_receipt(tx_digest=digest) + + old = trader_template["hash"] + trader_template["hash"] = ( + "bafybeicxdpkuk5z5zfbkso7v5pywf4v7chxvluyht7dtgalg6dnhl7ejoe" + ) + print( + requests.put( + url=f"{BASE_URL}/services", + json={ + "old": old, + "new": trader_template, + }, + ).content.decode() + ) + + +if __name__ == "__main__": + test_endpoint_e2e() diff --git a/templates/trader.yaml b/templates/trader.yaml index 0021ac01f..3c255671c 100644 --- a/templates/trader.yaml +++ b/templates/trader.yaml @@ -1,5 +1,17 @@ name: "Trader Agent" description: "Trader agent for omen prediction markets" -hash: bafybeifpttju2bwc4w5btg2ijdpoajvr3axjnxr3pivb7dt7okfrrse3iu +hash: bafybeieagxzdbmea3nttlve3yxjne5z7tt7mp26tfpgepm7p2ezovtdx4a image: https://operate.olas.network/_next/image?url=%2Fimages%2Fprediction-agent.png&w=3840&q=75 -rpc: http://localhost:8545 # User provided \ No newline at end of file +configuration: + nft: bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq + rpc: http://localhost:8545 # User provided + agent_id: 14 + threshold: 1 # TODO: Move to service component + use_staking: false # User provided + cost_of_bond: 10000000000000000 + olas_cost_of_bond: 10000000000000000000 + olas_required_to_bond: 10000000000000000000 + olas_required_to_stake: 10000000000000000000 + fund_requirements: + agent: 0.1 + safe: 0.5