Files
2022-11-25 15:14:43 +02:00

201 lines
8.2 KiB
Solidity

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "../../common/implementation/MultiRole.sol";
import "../interfaces/RegistryInterface.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
/**
* @title Registry for financial contracts and approved financial contract creators.
* @dev Maintains a whitelist of financial contract creators that are allowed
* to register new financial contracts and stores party members of a financial contract.
*/
contract Registry is RegistryInterface, MultiRole {
using SafeMath for uint256;
/****************************************
* INTERNAL VARIABLES AND STORAGE *
****************************************/
enum Roles {
Owner, // The owner manages the set of ContractCreators.
ContractCreator // Can register financial contracts.
}
// This enum is required because a `WasValid` state is required
// to ensure that financial contracts cannot be re-registered.
enum Validity { Invalid, Valid }
// Local information about a contract.
struct FinancialContract {
Validity valid;
uint128 index;
}
struct Party {
address[] contracts; // Each financial contract address is stored in this array.
// The address of each financial contract is mapped to its index for constant time look up and deletion.
mapping(address => uint256) contractIndex;
}
// Array of all contracts that are approved to use the UMA Oracle.
address[] public registeredContracts;
// Map of financial contract contracts to the associated FinancialContract struct.
mapping(address => FinancialContract) public contractMap;
// Map each party member to their their associated Party struct.
mapping(address => Party) private partyMap;
/****************************************
* EVENTS *
****************************************/
event NewContractRegistered(address indexed contractAddress, address indexed creator, address[] parties);
event PartyAdded(address indexed contractAddress, address indexed party);
event PartyRemoved(address indexed contractAddress, address indexed party);
/**
* @notice Construct the Registry contract.
*/
constructor() {
_createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender);
// Start with no contract creators registered.
_createSharedRole(uint256(Roles.ContractCreator), uint256(Roles.Owner), new address[](0));
}
/****************************************
* REGISTRATION FUNCTIONS *
****************************************/
/**
* @notice Registers a new financial contract.
* @dev Only authorized contract creators can call this method.
* @param parties array of addresses who become parties in the contract.
* @param contractAddress address of the contract against which the parties are registered.
*/
function registerContract(address[] calldata parties, address contractAddress)
external
override
onlyRoleHolder(uint256(Roles.ContractCreator))
{
FinancialContract storage financialContract = contractMap[contractAddress];
require(contractMap[contractAddress].valid == Validity.Invalid, "Can only register once");
// Store contract address as a registered contract.
registeredContracts.push(contractAddress);
// No length check necessary because we should never hit (2^127 - 1) contracts.
financialContract.index = uint128(registeredContracts.length.sub(1));
// For all parties in the array add them to the contract's parties.
financialContract.valid = Validity.Valid;
for (uint256 i = 0; i < parties.length; i = i.add(1)) {
_addPartyToContract(parties[i], contractAddress);
}
emit NewContractRegistered(contractAddress, msg.sender, parties);
}
/**
* @notice Adds a party member to the calling contract.
* @dev msg.sender will be used to determine the contract that this party is added to.
* @param party new party for the calling contract.
*/
function addPartyToContract(address party) external override {
address contractAddress = msg.sender;
require(contractMap[contractAddress].valid == Validity.Valid, "Can only add to valid contract");
_addPartyToContract(party, contractAddress);
}
/**
* @notice Removes a party member from the calling contract.
* @dev msg.sender will be used to determine the contract that this party is removed from.
* @param partyAddress address to be removed from the calling contract.
*/
function removePartyFromContract(address partyAddress) external override {
address contractAddress = msg.sender;
Party storage party = partyMap[partyAddress];
uint256 numberOfContracts = party.contracts.length;
require(numberOfContracts != 0, "Party has no contracts");
require(contractMap[contractAddress].valid == Validity.Valid, "Remove only from valid contract");
require(isPartyMemberOfContract(partyAddress, contractAddress), "Can only remove existing party");
// Index of the current location of the contract to remove.
uint256 deleteIndex = party.contractIndex[contractAddress];
// Store the last contract's address to update the lookup map.
address lastContractAddress = party.contracts[numberOfContracts - 1];
// Swap the contract to be removed with the last contract.
party.contracts[deleteIndex] = lastContractAddress;
// Update the lookup index with the new location.
party.contractIndex[lastContractAddress] = deleteIndex;
// Pop the last contract from the array and update the lookup map.
party.contracts.pop();
delete party.contractIndex[contractAddress];
emit PartyRemoved(contractAddress, partyAddress);
}
/****************************************
* REGISTRY STATE GETTERS *
****************************************/
/**
* @notice Returns whether the contract has been registered with the registry.
* @dev If it is registered, it is an authorized participant in the UMA system.
* @param contractAddress address of the financial contract.
* @return bool indicates whether the contract is registered.
*/
function isContractRegistered(address contractAddress) external view override returns (bool) {
return contractMap[contractAddress].valid == Validity.Valid;
}
/**
* @notice Returns a list of all contracts that are associated with a particular party.
* @param party address of the party.
* @return an array of the contracts the party is registered to.
*/
function getRegisteredContracts(address party) external view override returns (address[] memory) {
return partyMap[party].contracts;
}
/**
* @notice Returns all registered contracts.
* @return all registered contract addresses within the system.
*/
function getAllRegisteredContracts() external view override returns (address[] memory) {
return registeredContracts;
}
/**
* @notice checks if an address is a party of a contract.
* @param party party to check.
* @param contractAddress address to check against the party.
* @return bool indicating if the address is a party of the contract.
*/
function isPartyMemberOfContract(address party, address contractAddress) public view override returns (bool) {
uint256 index = partyMap[party].contractIndex[contractAddress];
return partyMap[party].contracts.length > index && partyMap[party].contracts[index] == contractAddress;
}
/****************************************
* INTERNAL FUNCTIONS *
****************************************/
function _addPartyToContract(address party, address contractAddress) internal {
require(!isPartyMemberOfContract(party, contractAddress), "Can only register a party once");
uint256 contractIndex = partyMap[party].contracts.length;
partyMap[party].contracts.push(contractAddress);
partyMap[party].contractIndex[contractAddress] = contractIndex;
emit PartyAdded(contractAddress, party);
}
}