// SPDX-License-Identifier: MITpragmasolidity >=0.8.0;/// @notice Modern, minimalist, and gas efficient ERC-721 implementation./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed id);
eventApproval(addressindexed owner, addressindexed spender, uint256indexed id);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
functiontokenURI(uint256 id) publicviewvirtualreturns (stringmemory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) internal _ownerOf;
mapping(address=>uint256) internal _balanceOf;
functionownerOf(uint256 id) publicviewvirtualreturns (address owner) {
require((owner = _ownerOf[id]) !=address(0), "NOT_MINTED");
}
functionbalanceOf(address owner) publicviewvirtualreturns (uint256) {
require(owner !=address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) public getApproved;
mapping(address=>mapping(address=>bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name, stringmemory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 id) publicvirtual{
address owner = _ownerOf[id];
require(msg.sender== owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
functionsetApprovalForAll(address operator, bool approved) publicvirtual{
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functiontransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
require(from== _ownerOf[id], "WRONG_FROM");
require(to !=address(0), "INVALID_RECIPIENT");
require(
msg.sender==from|| isApprovedForAll[from][msg.sender] ||msg.sender== getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
transferFrom(from, to, id);
if (to.code.length!=0)
require(
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
bytescalldata data
) publicvirtual{
transferFrom(from, to, id);
if (to.code.length!=0)
require(
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x80ac58cd||// ERC165 Interface ID for ERC721
interfaceId ==0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 id) internalvirtual{
require(to !=address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] ==address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function_burn(uint256 id) internalvirtual{
address owner = _ownerOf[id];
require(owner !=address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/function_safeMint(address to, uint256 id) internalvirtual{
_mint(to, id);
if (to.code.length!=0)
require(
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_safeMint(address to,
uint256 id,
bytesmemory data
) internalvirtual{
_mint(to, id);
if (to.code.length!=0)
require(
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721TokenReceiver{
functiononERC721Received(address,
address,
uint256,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
Contract Source Code
File 2 of 6: GenericRegistry.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.15;import"../lib/solmate/src/tokens/ERC721.sol";
import"./interfaces/IErrorsRegistries.sol";
/// @title Generic Registry - Smart contract for generic registry template/// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz>abstractcontractGenericRegistryisIErrorsRegistries, ERC721{
eventOwnerUpdated(addressindexed owner);
eventManagerUpdated(addressindexed manager);
eventBaseURIChanged(string baseURI);
// Owner addressaddresspublic owner;
// Unit manageraddresspublic manager;
// Base URIstringpublic baseURI;
// Unit counteruint256public totalSupply;
// Reentrancy lockuint256internal _locked =1;
// To better understand the CID anatomy, please refer to: https://proto.school/anatomy-of-a-cid/05// CID = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length><multihash-hash>)// CID prefix = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length>)// to complement the multibase_encoding(<multihash-hash>)// multibase_encoding = base16 = "f"// cid-version = version 1 = "0x01"// multicodec = dag-pb = "0x70"// multihash-algorithm = sha2-256 = "0x12"// multihash-length = 256 bits = "0x20"stringpublicconstant CID_PREFIX ="f01701220";
/// @dev Changes the owner address./// @param newOwner Address of a new owner.functionchangeOwner(address newOwner) externalvirtual{
// Check for the ownershipif (msg.sender!= owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for the zero addressif (newOwner ==address(0)) {
revert ZeroAddress();
}
owner = newOwner;
emit OwnerUpdated(newOwner);
}
/// @dev Changes the unit manager./// @param newManager Address of a new unit manager.functionchangeManager(address newManager) externalvirtual{
if (msg.sender!= owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for the zero addressif (newManager ==address(0)) {
revert ZeroAddress();
}
manager = newManager;
emit ManagerUpdated(newManager);
}
/// @dev Checks for the unit existence./// @notice Unit counter starts from 1./// @param unitId Unit Id./// @return true if the unit exists, false otherwise.functionexists(uint256 unitId) externalviewvirtualreturns (bool) {
return unitId >0&& unitId < (totalSupply +1);
}
/// @dev Sets unit base URI./// @param bURI Base URI string.functionsetBaseURI(stringmemory bURI) externalvirtual{
// Check for the ownershipif (msg.sender!= owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for the zero valueif (bytes(bURI).length==0) {
revert ZeroValue();
}
baseURI = bURI;
emit BaseURIChanged(bURI);
}
/// @dev Gets the valid unit Id from the provided index./// @notice Unit counter starts from 1./// @param id Unit counter./// @return unitId Unit Id.functiontokenByIndex(uint256 id) externalviewvirtualreturns (uint256 unitId) {
unitId = id +1;
if (unitId > totalSupply) {
revert Overflow(unitId, totalSupply);
}
}
// Open sourced from: https://stackoverflow.com/questions/67893318/solidity-how-to-represent-bytes32-as-string/// @dev Converts bytes16 input data to hex16./// @notice This method converts bytes into the same bytes-character hex16 representation./// @param data bytes16 input data./// @return result hex16 conversion from the input bytes16 data.function_toHex16(bytes16 data) internalpurereturns (bytes32 result) {
result =bytes32 (data) &0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000|
(bytes32 (data) &0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >>64;
result = result &0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000|
(result &0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >>32;
result = result &0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000|
(result &0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >>16;
result = result &0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000|
(result &0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >>8;
result = (result &0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >>4|
(result &0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >>8;
result =bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030+uint256 (result) +
(uint256 (result) +0x0606060606060606060606060606060606060606060606060606060606060606>>4&0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) *39);
}
/// @dev Gets the hash of the unit./// @param unitId Unit Id./// @return Unit hash.function_getUnitHash(uint256 unitId) internalviewvirtualreturns (bytes32);
/// @dev Returns unit token URI./// @notice Expected multicodec: dag-pb; hashing function: sha2-256, with base16 encoding and leading CID_PREFIX removed./// @param unitId Unit Id./// @return Unit token URI string.functiontokenURI(uint256 unitId) publicviewvirtualoverridereturns (stringmemory) {
bytes32 unitHash = _getUnitHash(unitId);
// Parse 2 parts of bytes32 into left and right hex16 representation, and concatenate into string// adding the base URI and a cid prefix for the full base16 multibase prefix IPFS hash representationreturnstring(abi.encodePacked(baseURI, CID_PREFIX, _toHex16(bytes16(unitHash)),
_toHex16(bytes16(unitHash <<128))));
}
}
Contract Source Code
File 3 of 6: IErrorsRegistries.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.15;/// @dev Errors.interfaceIErrorsRegistries{
/// @dev Only `manager` has a privilege, but the `sender` was provided./// @param sender Sender address./// @param manager Required sender address as a manager.errorManagerOnly(address sender, address manager);
/// @dev Only `owner` has a privilege, but the `sender` was provided./// @param sender Sender address./// @param owner Required sender address as an owner.errorOwnerOnly(address sender, address owner);
/// @dev Hash already exists in the records.errorHashExists();
/// @dev Provided zero address.errorZeroAddress();
/// @dev Agent Id is not correctly provided for the current routine./// @param agentId Component Id.errorWrongAgentId(uint256 agentId);
/// @dev Wrong length of two arrays./// @param numValues1 Number of values in a first array./// @param numValues2 Numberf of values in a second array.errorWrongArrayLength(uint256 numValues1, uint256 numValues2);
/// @dev Canonical agent Id is not found./// @param agentId Canonical agent Id.errorAgentNotFound(uint256 agentId);
/// @dev Component Id is not found./// @param componentId Component Id.errorComponentNotFound(uint256 componentId);
/// @dev Multisig threshold is out of bounds./// @param currentThreshold Current threshold value./// @param minThreshold Minimum possible threshold value./// @param maxThreshold Maximum possible threshold value.errorWrongThreshold(uint256 currentThreshold, uint256 minThreshold, uint256 maxThreshold);
/// @dev Agent instance is already registered with a specified `operator`./// @param operator Operator that registered an instance.errorAgentInstanceRegistered(address operator);
/// @dev Wrong operator is specified when interacting with a specified `serviceId`./// @param serviceId Service Id.errorWrongOperator(uint256 serviceId);
/// @dev Operator has no registered instances in the service./// @param operator Operator address./// @param serviceId Service Id.errorOperatorHasNoInstances(address operator, uint256 serviceId);
/// @dev Canonical `agentId` is not found as a part of `serviceId`./// @param agentId Canonical agent Id./// @param serviceId Service Id.errorAgentNotInService(uint256 agentId, uint256 serviceId);
/// @dev The contract is paused.errorPaused();
/// @dev Zero value when it has to be different from zero.errorZeroValue();
/// @dev Value overflow./// @param provided Overflow value./// @param max Maximum possible value.errorOverflow(uint256 provided, uint256 max);
/// @dev Service must be inactive./// @param serviceId Service Id.errorServiceMustBeInactive(uint256 serviceId);
/// @dev All the agent instance slots for a specific `serviceId` are filled./// @param serviceId Service Id.errorAgentInstancesSlotsFilled(uint256 serviceId);
/// @dev Wrong state of a service./// @param state Service state./// @param serviceId Service Id.errorWrongServiceState(uint256 state, uint256 serviceId);
/// @dev Only own service multisig is allowed./// @param provided Provided address./// @param expected Expected multisig address./// @param serviceId Service Id.errorOnlyOwnServiceMultisig(address provided, address expected, uint256 serviceId);
/// @dev Multisig is not whitelisted./// @param multisig Address of a multisig implementation.errorUnauthorizedMultisig(address multisig);
/// @dev Incorrect deposit provided for the registration activation./// @param sent Sent amount./// @param expected Expected amount./// @param serviceId Service Id.errorIncorrectRegistrationDepositValue(uint256 sent, uint256 expected, uint256 serviceId);
/// @dev Insufficient value provided for the agent instance bonding./// @param sent Sent amount./// @param expected Expected amount./// @param serviceId Service Id.errorIncorrectAgentBondingValue(uint256 sent, uint256 expected, uint256 serviceId);
/// @dev Failure of a transfer./// @param token Address of a token./// @param from Address `from`./// @param to Address `to`./// @param value Value.errorTransferFailed(address token, addressfrom, address to, uint256 value);
/// @dev Caught reentrancy violation.errorReentrancyGuard();
}
Contract Source Code
File 4 of 6: IMultisig.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.15;/// @dev Generic multisig.interfaceIMultisig{
/// @dev Creates a multisig./// @param owners Set of multisig owners./// @param threshold Number of required confirmations for a multisig transaction./// @param data Packed data related to the creation of a chosen multisig./// @return multisig Address of a created multisig.functioncreate(address[] memory owners,
uint256 threshold,
bytesmemory data
) externalreturns (address multisig);
}
Contract Source Code
File 5 of 6: IRegistry.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.15;/// @dev Required interface for the component / agent manipulation.interfaceIRegistry{
enumUnitType {
Component,
Agent
}
/// @dev Creates component / agent./// @param unitOwner Owner of the component / agent./// @param unitHash IPFS hash of the component / agent./// @param dependencies Set of component dependencies in a sorted ascending order./// @return The id of a minted component / agent.functioncreate(address unitOwner,
bytes32 unitHash,
uint32[] memory dependencies
) externalreturns (uint256);
/// @dev Updates the component / agent hash./// @param owner Owner of the component / agent./// @param unitId Unit Id./// @param unitHash Updated IPFS hash of the component / agent./// @return success True, if function executed successfully.functionupdateHash(address owner, uint256 unitId, bytes32 unitHash) externalreturns (bool success);
/// @dev Gets subcomponents of a provided unit Id from a local public map./// @param unitId Unit Id./// @return subComponentIds Set of subcomponents./// @return numSubComponents Number of subcomponents.functiongetLocalSubComponents(uint256 unitId) externalviewreturns (uint32[] memory subComponentIds, uint256 numSubComponents);
/// @dev Calculates the set of subcomponent Ids./// @param unitIds Set of unit Ids./// @return subComponentIds Subcomponent Ids.functioncalculateSubComponents(uint32[] memory unitIds) externalviewreturns (uint32[] memory subComponentIds);
/// @dev Gets updated component / agent hashes./// @param unitId Unit Id./// @return numHashes Number of hashes./// @return unitHashes The list of component / agent hashes.functiongetUpdatedHashes(uint256 unitId) externalviewreturns (uint256 numHashes, bytes32[] memory unitHashes);
/// @dev Gets the total supply of components / agents./// @return Total supply.functiontotalSupply() externalviewreturns (uint256);
}
Contract Source Code
File 6 of 6: ServiceRegistry.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.15;import"./GenericRegistry.sol";
import"./interfaces/IMultisig.sol";
import"./interfaces/IRegistry.sol";
// This struct is 128 bits in totalstructAgentParams {
// Number of agent instances. This number is limited by the number of agent instancesuint32 slots;
// Bond per agent instance. This is enough for 1b+ ETH or 1e27uint96 bond;
}
// This struct is 192 bits in totalstructAgentInstance {
// Address of an agent instanceaddress instance;
// Canonical agent Id. This number is limited by the max number of agent Ids (see UnitRegistry contract)uint32 agentId;
}
/// @title Service Registry - Smart contract for registering services/// @author Aleksandr Kuperman - <aleksandr.kuperman@valory.xyz>contractServiceRegistryisGenericRegistry{
eventDrainerUpdated(addressindexed drainer);
eventDeposit(addressindexed sender, uint256 amount);
eventRefund(addressindexed receiver, uint256 amount);
eventCreateService(uint256indexed serviceId);
eventUpdateService(uint256indexed serviceId, bytes32 configHash);
eventRegisterInstance(addressindexed operator, uint256indexed serviceId, addressindexed agentInstance, uint256 agentId);
eventCreateMultisigWithAgents(uint256indexed serviceId, addressindexed multisig);
eventActivateRegistration(uint256indexed serviceId);
eventTerminateService(uint256indexed serviceId);
eventOperatorSlashed(uint256 amount, addressindexed operator, uint256indexed serviceId);
eventOperatorUnbond(addressindexed operator, uint256indexed serviceId);
eventDeployService(uint256indexed serviceId);
eventDrain(addressindexed drainer, uint256 amount);
enumServiceState {
NonExistent,
PreRegistration,
ActiveRegistration,
FinishedRegistration,
Deployed,
TerminatedBonded
}
// Service parametersstructService {
// Registration activation deposit// This is enough for 1b+ ETH or 1e27uint96 securityDeposit;
// Multisig address for agent instancesaddress multisig;
// IPFS hashes pointing to the config metadatabytes32 configHash;
// Agent instance signers threshold: must no less than ceil((n * 2 + 1) / 3) of all the agent instances combined// This number will be enough to have ((2^32 - 1) * 3 - 1) / 2, which is bigger than 6.44buint32 threshold;
// Total number of agent instances. We assume that the number of instances is bounded by 2^32 - 1uint32 maxNumAgentInstances;
// Actual number of agent instances. This number is less or equal to maxNumAgentInstancesuint32 numAgentInstances;
// Service state
ServiceState state;
// Canonical agent Ids for the service. Individual agent Id is bounded by the max number of agent Iduint32[] agentIds;
}
// Agent Registry addressaddresspublicimmutable agentRegistry;
// The amount of funds slashed. This is enough for 1b+ ETH or 1e27uint96public slashedFunds;
// Drainer address: set by the government and is allowed to drain ETH funds accumulated in this contractaddresspublic drainer;
// Service registry version numberstringpublicconstant VERSION ="1.0.0";
// Map of service Id => set of IPFS hashes pointing to the config metadatamapping (uint256=>bytes32[]) public mapConfigHashes;
// Map of operator address and serviceId => set of registered agent instance addressesmapping(uint256=> AgentInstance[]) public mapOperatorAndServiceIdAgentInstances;
// Service Id and canonical agent Id => number of agent instances and correspondent instance registration bondmapping(uint256=> AgentParams) public mapServiceAndAgentIdAgentParams;
// Actual agent instance addresses. Service Id and canonical agent Id => Set of agent instance addresses.mapping(uint256=>address[]) public mapServiceAndAgentIdAgentInstances;
// Map of operator address and serviceId => agent instance bonding / escrow balancemapping(uint256=>uint96) public mapOperatorAndServiceIdOperatorBalances;
// Map of agent instance address => service id it is registered with and operator address that supplied the instancemapping (address=>address) public mapAgentInstanceOperators;
// Map of service Id => set of unique component Ids// Updated during the service deployment via deploy() functionmapping (uint256=>uint32[]) public mapServiceIdSetComponentIds;
// Map of service Id => set of unique agent Idsmapping (uint256=>uint32[]) public mapServiceIdSetAgentIds;
// Map of policy for multisig implementationsmapping (address=>bool) public mapMultisigs;
// Map of service counter => servicemapping (uint256=> Service) public mapServices;
/// @dev Service registry constructor./// @param _name Service contract name./// @param _symbol Agent contract symbol./// @param _baseURI Agent registry token base URI./// @param _agentRegistry Agent registry address.constructor(stringmemory _name, stringmemory _symbol, stringmemory _baseURI, address _agentRegistry)
ERC721(_name, _symbol)
{
baseURI = _baseURI;
agentRegistry = _agentRegistry;
owner =msg.sender;
}
/// @dev Changes the drainer./// @param newDrainer Address of a drainer.functionchangeDrainer(address newDrainer) external{
if (msg.sender!= owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for the zero addressif (newDrainer ==address(0)) {
revert ZeroAddress();
}
drainer = newDrainer;
emit DrainerUpdated(newDrainer);
}
/// @dev Going through basic initial service checks./// @param configHash IPFS hash pointing to the config metadata./// @param agentIds Canonical agent Ids./// @param agentParams Number of agent instances and required required bond to register an instance in the service.function_initialChecks(bytes32 configHash,
uint32[] memory agentIds,
AgentParams[] memory agentParams
) privateview{
// Check for the non-zero hash valueif (configHash ==0) {
revert ZeroValue();
}
// Checking for non-empty arrays and correct number of values in themif (agentIds.length==0|| agentIds.length!= agentParams.length) {
revert WrongArrayLength(agentIds.length, agentParams.length);
}
// Check for duplicate canonical agent Idsuint256 agentTotalSupply = IRegistry(agentRegistry).totalSupply();
uint256 lastId;
for (uint256 i =0; i < agentIds.length; i++) {
if (agentIds[i] < (lastId +1) || agentIds[i] > agentTotalSupply) {
revert WrongAgentId(agentIds[i]);
}
lastId = agentIds[i];
}
}
/// @dev Sets the service data./// @param service A service instance to fill the data for./// @param agentIds Canonical agent Ids./// @param agentParams Number of agent instances and required required bond to register an instance in the service./// @param size Size of a canonical agent ids set./// @param serviceId ServiceId.function_setServiceData(
Service memory service,
uint32[] memory agentIds,
AgentParams[] memory agentParams,
uint256 size,
uint256 serviceId
) private{
// Security deposituint96 securityDeposit;
// Add canonical agent Ids for the service and the slots map
service.agentIds =newuint32[](size);
for (uint256 i =0; i < size; i++) {
service.agentIds[i] = agentIds[i];
// Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves// As with other units, we assume that the system is not expected to support more than than 2^32-1 services// Need to carefully check pairings, since it's hard to find if something is incorrectly misplaced bitwise// serviceId occupies first 32 bitsuint256 serviceAgent = serviceId;
// agentId takes the second 32 bits
serviceAgent |=uint256(agentIds[i]) <<32;
mapServiceAndAgentIdAgentParams[serviceAgent] = agentParams[i];
service.maxNumAgentInstances += agentParams[i].slots;
// Security deposit is the maximum of the canonical agent registration bondif (agentParams[i].bond > securityDeposit) {
securityDeposit = agentParams[i].bond;
}
}
service.securityDeposit = securityDeposit;
// Check for the correct threshold: no less than ceil((n * 2 + 1) / 3) of all the agent instances combineduint256 checkThreshold =uint256(service.maxNumAgentInstances *2+1);
if (checkThreshold %3==0) {
checkThreshold = checkThreshold /3;
} else {
checkThreshold = checkThreshold /3+1;
}
if (service.threshold < checkThreshold || service.threshold > service.maxNumAgentInstances) {
revert WrongThreshold(service.threshold, checkThreshold, service.maxNumAgentInstances);
}
}
/// @dev Creates a new service./// @param serviceOwner Individual that creates and controls a service./// @param configHash IPFS hash pointing to the config metadata./// @param agentIds Canonical agent Ids in a sorted ascending order./// @param agentParams Number of agent instances and required required bond to register an instance in the service./// @param threshold Signers threshold for a multisig composed by agent instances./// @return serviceId Created service Id.functioncreate(address serviceOwner,
bytes32 configHash,
uint32[] memory agentIds,
AgentParams[] memory agentParams,
uint32 threshold
) externalreturns (uint256 serviceId)
{
// Reentrancy guardif (_locked >1) {
revert ReentrancyGuard();
}
_locked =2;
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check for the non-empty service owner addressif (serviceOwner ==address(0)) {
revert ZeroAddress();
}
// Execute initial checks
_initialChecks(configHash, agentIds, agentParams);
// Check that there are no zero number of slots for a specific canonical agent id and no zero registration bondfor (uint256 i =0; i < agentIds.length; i++) {
if (agentParams[i].slots ==0|| agentParams[i].bond ==0) {
revert ZeroValue();
}
}
// Create a new service Id
serviceId = totalSupply;
serviceId++;
// Set high-level data components of the service instance
Service memory service;
// Updating high-level data components of the service
service.threshold = threshold;
// Assigning the initial hash
service.configHash = configHash;
// Set the initial service state
service.state = ServiceState.PreRegistration;
// Set service data
_setServiceData(service, agentIds, agentParams, agentIds.length, serviceId);
mapServices[serviceId] = service;
totalSupply = serviceId;
// Mint the service instance to the service owner and record the service structure
_safeMint(serviceOwner, serviceId);
emit CreateService(serviceId);
_locked =1;
}
/// @dev Updates a service in a CRUD way./// @param serviceOwner Individual that creates and controls a service./// @param configHash IPFS hash pointing to the config metadata./// @param agentIds Canonical agent Ids in a sorted ascending order./// @param agentParams Number of agent instances and required required bond to register an instance in the service./// @param threshold Signers threshold for a multisig composed by agent instances./// @param serviceId Service Id to be updated./// @return success True, if function executed successfully.functionupdate(address serviceOwner,
bytes32 configHash,
uint32[] memory agentIds,
AgentParams[] memory agentParams,
uint32 threshold,
uint256 serviceId
) externalreturns (bool success)
{
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check for the service ownershipaddress actualOwner = ownerOf(serviceId);
if (actualOwner != serviceOwner) {
revert OwnerOnly(serviceOwner, actualOwner);
}
Service memory service = mapServices[serviceId];
if (service.state != ServiceState.PreRegistration) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Execute initial checks
_initialChecks(configHash, agentIds, agentParams);
// Updating high-level data components of the service
service.threshold = threshold;
service.maxNumAgentInstances =0;
// Collect non-zero canonical agent ids and slots / costs, remove any canonical agent Ids from the params mapuint32[] memory newAgentIds =newuint32[](agentIds.length);
AgentParams[] memory newAgentParams =new AgentParams[](agentIds.length);
uint256 size;
for (uint256 i =0; i < agentIds.length; i++) {
if (agentParams[i].slots ==0) {
// Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(agentIds[i]) <<32;
delete mapServiceAndAgentIdAgentParams[serviceAgent];
} else {
newAgentIds[size] = agentIds[i];
newAgentParams[size] = agentParams[i];
size++;
}
}
// Check if the previous hash is the same / hash was not updatedbytes32 lastConfigHash = service.configHash;
if (lastConfigHash != configHash) {
mapConfigHashes[serviceId].push(lastConfigHash);
service.configHash = configHash;
}
// Set service data and record the modified service struct
_setServiceData(service, newAgentIds, newAgentParams, size, serviceId);
mapServices[serviceId] = service;
emit UpdateService(serviceId, configHash);
success =true;
}
/// @dev Activates the service./// @param serviceOwner Individual that creates and controls a service./// @param serviceId Correspondent service Id./// @return success True, if function executed successfully.functionactivateRegistration(address serviceOwner, uint256 serviceId) externalpayablereturns (bool success)
{
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check for the service ownershipaddress actualOwner = ownerOf(serviceId);
if (actualOwner != serviceOwner) {
revert OwnerOnly(serviceOwner, actualOwner);
}
Service storage service = mapServices[serviceId];
// Service must be inactiveif (service.state != ServiceState.PreRegistration) {
revert ServiceMustBeInactive(serviceId);
}
if (msg.value!= service.securityDeposit) {
revert IncorrectRegistrationDepositValue(msg.value, service.securityDeposit, serviceId);
}
// Activate the agent instance registration
service.state = ServiceState.ActiveRegistration;
emit ActivateRegistration(serviceId);
success =true;
}
/// @dev Registers agent instances./// @param operator Address of the operator./// @param serviceId Service Id to be updated./// @param agentInstances Agent instance addresses./// @param agentIds Canonical Ids of the agent correspondent to the agent instance./// @return success True, if function executed successfully.functionregisterAgents(address operator,
uint256 serviceId,
address[] memory agentInstances,
uint32[] memory agentIds
) externalpayablereturns (bool success)
{
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check if the length of canonical agent instance addresses array and ids array have the same lengthif (agentInstances.length!= agentIds.length) {
revert WrongArrayLength(agentInstances.length, agentIds.length);
}
Service storage service = mapServices[serviceId];
// The service has to be active to register agentsif (service.state != ServiceState.ActiveRegistration) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Check for the sufficient amount of bond fee is provideduint256 numAgents = agentInstances.length;
uint256 totalBond =0;
for (uint256 i =0; i < numAgents; ++i) {
// Check if canonical agent Id exists in the service// Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(agentIds[i]) <<32;
AgentParams memory agentParams = mapServiceAndAgentIdAgentParams[serviceAgent];
if (agentParams.slots ==0) {
revert AgentNotInService(agentIds[i], serviceId);
}
totalBond += agentParams.bond;
}
if (msg.value!= totalBond) {
revert IncorrectAgentBondingValue(msg.value, totalBond, serviceId);
}
// Operator address must not be used as an agent instance anywhere elseif (mapAgentInstanceOperators[operator] !=address(0)) {
revert WrongOperator(serviceId);
}
// Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves// operator occupies first 160 bitsuint256 operatorService =uint256(uint160(operator));
// serviceId occupies next 32 bits assuming it is not greater than 2^32 - 1 in value
operatorService |= serviceId <<160;
for (uint256 i =0; i < numAgents; ++i) {
address agentInstance = agentInstances[i];
uint32 agentId = agentIds[i];
// Operator address must be different from agent instance oneif (operator == agentInstance) {
revert WrongOperator(serviceId);
}
// Check if the agent instance is already engaged with another serviceif (mapAgentInstanceOperators[agentInstance] !=address(0)) {
revert AgentInstanceRegistered(mapAgentInstanceOperators[agentInstance]);
}
// Check if there is an empty slot for the agent instance in this specific service// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(agentIds[i]) <<32;
if (mapServiceAndAgentIdAgentInstances[serviceAgent].length== mapServiceAndAgentIdAgentParams[serviceAgent].slots) {
revert AgentInstancesSlotsFilled(serviceId);
}
// Add agent instance and operator and set the instance engagement
mapServiceAndAgentIdAgentInstances[serviceAgent].push(agentInstance);
mapOperatorAndServiceIdAgentInstances[operatorService].push(AgentInstance(agentInstance, agentId));
service.numAgentInstances++;
mapAgentInstanceOperators[agentInstance] = operator;
emit RegisterInstance(operator, serviceId, agentInstance, agentId);
}
// If the service agent instance capacity is reached, the service becomes finished-registrationif (service.numAgentInstances == service.maxNumAgentInstances) {
service.state = ServiceState.FinishedRegistration;
}
// Update operator's bonding balance
mapOperatorAndServiceIdOperatorBalances[operatorService] +=uint96(msg.value);
emit Deposit(operator, msg.value);
success =true;
}
/// @dev Creates multisig instance controlled by the set of service agent instances and deploys the service./// @param serviceOwner Individual that creates and controls a service./// @param serviceId Correspondent service Id./// @param multisigImplementation Multisig implementation address./// @param data Data payload for the multisig creation./// @return multisig Address of the created multisig.functiondeploy(address serviceOwner,
uint256 serviceId,
address multisigImplementation,
bytesmemory data
) externalreturns (address multisig)
{
// Reentrancy guardif (_locked >1) {
revert ReentrancyGuard();
}
_locked =2;
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check for the service ownershipaddress actualOwner = ownerOf(serviceId);
if (actualOwner != serviceOwner) {
revert OwnerOnly(serviceOwner, actualOwner);
}
// Check for the whitelisted multisig implementationif (!mapMultisigs[multisigImplementation]) {
revert UnauthorizedMultisig(multisigImplementation);
}
Service storage service = mapServices[serviceId];
if (service.state != ServiceState.FinishedRegistration) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Get all agent instances for the multisigaddress[] memory agentInstances = _getAgentInstances(service, serviceId);
// Create a multisig with agent instances
multisig = IMultisig(multisigImplementation).create(agentInstances, service.threshold, data);
// Update maps of service Id to subcomponent and agent Ids
mapServiceIdSetAgentIds[serviceId] = service.agentIds;
mapServiceIdSetComponentIds[serviceId] = IRegistry(agentRegistry).calculateSubComponents(service.agentIds);
service.multisig = multisig;
service.state = ServiceState.Deployed;
emit CreateMultisigWithAgents(serviceId, multisig);
emit DeployService(serviceId);
_locked =1;
}
/// @dev Slashes a specified agent instance./// @param agentInstances Agent instances to slash./// @param amounts Correspondent amounts to slash./// @param serviceId Service Id./// @return success True, if function executed successfully.functionslash(address[] memory agentInstances, uint96[] memory amounts, uint256 serviceId) externalreturns (bool success)
{
// Check if the service is deployed// Since we do not kill (burn) services, we want this check to happen in a right service state.// If the service is deployed, it definitely exists and is running. We do not want this function to be abused// when the service was deployed, then terminated, then in a sleep mode or before next deployment somebody// could use this function and try to slash operators.
Service memory service = mapServices[serviceId];
if (service.state != ServiceState.Deployed) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Check for the array sizeif (agentInstances.length!= amounts.length) {
revert WrongArrayLength(agentInstances.length, amounts.length);
}
// Only the multisig of a correspondent address can slash its agent instancesif (msg.sender!= service.multisig) {
revert OnlyOwnServiceMultisig(msg.sender, service.multisig, serviceId);
}
// Loop over each agent instanceuint256 numInstancesToSlash = agentInstances.length;
for (uint256 i =0; i < numInstancesToSlash; ++i) {
// Get the service Id from the agentInstance mapaddress operator = mapAgentInstanceOperators[agentInstances[i]];
// Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves// operator occupies first 160 bitsuint256 operatorService =uint256(uint160(operator));
// serviceId occupies next 32 bits
operatorService |= serviceId <<160;
// Slash the balance of the operator, make sure it does not go below zerouint96 balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
if ((amounts[i] +1) > balance) {
// We cannot add to the slashed amount more than the balance of the operator
slashedFunds += balance;
balance =0;
} else {
slashedFunds += amounts[i];
balance -= amounts[i];
}
mapOperatorAndServiceIdOperatorBalances[operatorService] = balance;
emit OperatorSlashed(amounts[i], operator, serviceId);
}
success =true;
}
/// @dev Terminates the service./// @param serviceOwner Owner of the service./// @param serviceId Service Id to be updated./// @return success True, if function executed successfully./// @return refund Refund to return to the service owner.functionterminate(address serviceOwner, uint256 serviceId) externalreturns (bool success, uint256 refund)
{
// Reentrancy guardif (_locked >1) {
revert ReentrancyGuard();
}
_locked =2;
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Check for the service ownershipaddress actualOwner = ownerOf(serviceId);
if (actualOwner != serviceOwner) {
revert OwnerOnly(serviceOwner, actualOwner);
}
Service storage service = mapServices[serviceId];
// Check if the service is already terminatedif (service.state == ServiceState.PreRegistration || service.state == ServiceState.TerminatedBonded) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Define the state of the service depending on the number of bonded agent instancesif (service.numAgentInstances >0) {
service.state = ServiceState.TerminatedBonded;
} else {
service.state = ServiceState.PreRegistration;
}
// Delete the sensitive datadelete mapServiceIdSetComponentIds[serviceId];
delete mapServiceIdSetAgentIds[serviceId];
for (uint256 i =0; i < service.agentIds.length; ++i) {
// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(service.agentIds[i]) <<32;
delete mapServiceAndAgentIdAgentInstances[serviceAgent];
}
// Return registration deposit back to the service owner
refund = service.securityDeposit;
// By design, the refund is always a non-zero value, so no check is needed here fo that
(bool result, ) = serviceOwner.call{value: refund}("");
if (!result) {
revert TransferFailed(address(0), address(this), serviceOwner, refund);
}
emit Refund(serviceOwner, refund);
emit TerminateService(serviceId);
success =true;
_locked =1;
}
/// @dev Unbonds agent instances of the operator from the service./// @param operator Operator of agent instances./// @param serviceId Service Id./// @return success True, if function executed successfully./// @return refund The amount of refund returned to the operator.functionunbond(address operator, uint256 serviceId) externalreturns (bool success, uint256 refund) {
// Reentrancy guardif (_locked >1) {
revert ReentrancyGuard();
}
_locked =2;
// Check for the manager privilege for a service managementif (manager !=msg.sender) {
revert ManagerOnly(msg.sender, manager);
}
// Checks if the operator address is not zeroif (operator ==address(0)) {
revert ZeroAddress();
}
Service storage service = mapServices[serviceId];
// Service can only be in the terminated-bonded state or expired-registration in order to proceedif (service.state != ServiceState.TerminatedBonded) {
revert WrongServiceState(uint256(service.state), serviceId);
}
// Check for the operator and unbond all its agent instances// Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves// operator occupies first 160 bitsuint256 operatorService =uint256(uint160(operator));
// serviceId occupies next 32 bits
operatorService |= serviceId <<160;
AgentInstance[] memory agentInstances = mapOperatorAndServiceIdAgentInstances[operatorService];
uint256 numAgentsUnbond = agentInstances.length;
if (numAgentsUnbond ==0) {
revert OperatorHasNoInstances(operator, serviceId);
}
// Subtract number of unbonded agent instances
service.numAgentInstances -=uint32(numAgentsUnbond);
// When number of instances is equal to zero, all the operators have unbonded and the service is moved into// the PreRegistration state, from where it can be updated / start registration / get deployed againif (service.numAgentInstances ==0) {
service.state = ServiceState.PreRegistration;
}
// else condition is redundant here, since the service is either in the TerminatedBonded state, or moved// into the PreRegistration state and unbonding is not possible before the new TerminatedBonded state is reached// Calculate registration refund and free all agent instancesfor (uint256 i =0; i < numAgentsUnbond; i++) {
// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(agentInstances[i].agentId) <<32;
refund += mapServiceAndAgentIdAgentParams[serviceAgent].bond;
// Clean-up the sensitive data such that it is not reused laterdelete mapAgentInstanceOperators[agentInstances[i].instance];
}
// Clean all the operator agent instances records for this servicedelete mapOperatorAndServiceIdAgentInstances[operatorService];
// Calculate the refunduint96 balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
// This situation is possible if the operator was slashed for the agent instance misbehaviorif (refund > balance) {
refund = balance;
}
// Refund the operatorif (refund >0) {
// Operator's balance is essentially zero after the refund
mapOperatorAndServiceIdOperatorBalances[operatorService] =0;
// Send the refund
(bool result, ) = operator.call{value: refund}("");
if (!result) {
revert TransferFailed(address(0), address(this), operator, refund);
}
emit Refund(operator, refund);
}
emit OperatorUnbond(operator, serviceId);
success =true;
_locked =1;
}
/// @dev Gets the service instance./// @param serviceId Service Id./// @return service Corresponding Service struct.functiongetService(uint256 serviceId) externalviewreturns (Service memory service) {
service = mapServices[serviceId];
}
/// @dev Gets service agent parameters: number of agent instances (slots) and a bond amount./// @param serviceId Service Id./// @return numAgentIds Number of canonical agent Ids in the service./// @return agentParams Set of agent parameters for each canonical agent Id.functiongetAgentParams(uint256 serviceId) externalviewreturns (uint256 numAgentIds, AgentParams[] memory agentParams)
{
Service memory service = mapServices[serviceId];
numAgentIds = service.agentIds.length;
agentParams =new AgentParams[](numAgentIds);
for (uint256 i =0; i < numAgentIds; ++i) {
uint256 serviceAgent = serviceId;
serviceAgent |=uint256(service.agentIds[i]) <<32;
agentParams[i] = mapServiceAndAgentIdAgentParams[serviceAgent];
}
}
/// @dev Lists all the instances of a given canonical agent Id if the service./// @param serviceId Service Id./// @param agentId Canonical agent Id./// @return numAgentInstances Number of agent instances./// @return agentInstances Set of agent instances for a specified canonical agent Id.functiongetInstancesForAgentId(uint256 serviceId, uint256 agentId) externalviewreturns (uint256 numAgentInstances, address[] memory agentInstances)
{
uint256 serviceAgent = serviceId;
serviceAgent |= agentId <<32;
numAgentInstances = mapServiceAndAgentIdAgentInstances[serviceAgent].length;
agentInstances =newaddress[](numAgentInstances);
for (uint256 i =0; i < numAgentInstances; i++) {
agentInstances[i] = mapServiceAndAgentIdAgentInstances[serviceAgent][i];
}
}
/// @dev Gets all agent instances./// @param service Service instance./// @param serviceId ServiceId./// @return agentInstances Pre-allocated list of agent instance addresses.function_getAgentInstances(Service memory service, uint256 serviceId) privateviewreturns (address[] memory agentInstances)
{
agentInstances =newaddress[](service.numAgentInstances);
uint256 count;
for (uint256 i =0; i < service.agentIds.length; i++) {
// serviceId occupies first 32 bits, agentId gets the next 32 bitsuint256 serviceAgent = serviceId;
serviceAgent |=uint256(service.agentIds[i]) <<32;
for (uint256 j =0; j < mapServiceAndAgentIdAgentInstances[serviceAgent].length; j++) {
agentInstances[count] = mapServiceAndAgentIdAgentInstances[serviceAgent][j];
count++;
}
}
}
/// @dev Gets service agent instances./// @param serviceId ServiceId./// @return numAgentInstances Number of agent instances./// @return agentInstances Pre-allocated list of agent instance addresses.functiongetAgentInstances(uint256 serviceId) externalviewreturns (uint256 numAgentInstances, address[] memory agentInstances)
{
Service memory service = mapServices[serviceId];
agentInstances = _getAgentInstances(service, serviceId);
numAgentInstances = agentInstances.length;
}
/// @dev Gets previous service config hashes./// @param serviceId Service Id./// @return numHashes Number of hashes./// @return configHashes The list of previous component hashes (excluding the current one).functiongetPreviousHashes(uint256 serviceId) externalviewreturns (uint256 numHashes, bytes32[] memory configHashes)
{
configHashes = mapConfigHashes[serviceId];
numHashes = configHashes.length;
}
/// @dev Gets the full set of linearized components / canonical agent Ids for a specified service./// @notice The service must be / have been deployed in order to get the actual data./// @param serviceId Service Id./// @return numUnitIds Number of component / agent Ids./// @return unitIds Set of component / agent Ids.functiongetUnitIdsOfService(IRegistry.UnitType unitType, uint256 serviceId) externalviewreturns (uint256 numUnitIds, uint32[] memory unitIds)
{
if (unitType == IRegistry.UnitType.Component) {
unitIds = mapServiceIdSetComponentIds[serviceId];
} else {
unitIds = mapServiceIdSetAgentIds[serviceId];
}
numUnitIds = unitIds.length;
}
/// @dev Gets the operator's balance in a specific service./// @param operator Operator address./// @param serviceId Service Id./// @return balance The balance of the operator.functiongetOperatorBalance(address operator, uint256 serviceId) externalviewreturns (uint256 balance)
{
uint256 operatorService =uint256(uint160(operator));
operatorService |= serviceId <<160;
balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
}
/// @dev Controls multisig implementation address permission./// @param multisig Address of a multisig implementation./// @param permission Grant or revoke permission./// @return success True, if function executed successfully.functionchangeMultisigPermission(address multisig, bool permission) externalreturns (bool success) {
// Check for the contract ownershipif (msg.sender!= owner) {
revert OwnerOnly(msg.sender, owner);
}
if (multisig ==address(0)) {
revert ZeroAddress();
}
mapMultisigs[multisig] = permission;
success =true;
}
/// @dev Drains slashed funds./// @return amount Drained amount.functiondrain() externalreturns (uint256 amount) {
// Reentrancy guardif (_locked >1) {
revert ReentrancyGuard();
}
_locked =2;
// Check for the drainer addressif (msg.sender!= drainer) {
revert ManagerOnly(msg.sender, drainer);
}
// Drain the slashed funds
amount = slashedFunds;
if (amount >0) {
slashedFunds =0;
// Send the refund
(bool result, ) =msg.sender.call{value: amount}("");
if (!result) {
revert TransferFailed(address(0), address(this), msg.sender, amount);
}
emit Drain(msg.sender, amount);
}
_locked =1;
}
/// @dev Gets the hash of the service./// @param serviceId Service Id./// @return Service hash.function_getUnitHash(uint256 serviceId) internalviewoverridereturns (bytes32) {
return mapServices[serviceId].configHash;
}
}