refactor contrats with aragonos

This commit is contained in:
2019-03-22 18:15:16 +01:00
parent d687ff604e
commit 6c569239de
97 changed files with 85411 additions and 1155 deletions

View File

@@ -1,126 +0,0 @@
pragma solidity ^0.4.19;
import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol";
import './upgradeable/Upgradeable.sol';
// ToDo: only load interfaces
import './Token.sol';
import './Contributors.sol';
contract Contribution is Upgradeable, ERC721Token {
struct ContributionData {
address contributor;
uint amount;
bool claimed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
string tokenMetadataURL;
uint claimAfterBlock;
bool exists;
}
string internal name_;
string internal symbol_;
mapping(uint256 => address) contributionOwner;
mapping(address => uint256[]) ownedContributions;
mapping(uint256 => ContributionData) public contributions;
uint256 public contributionsCount;
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
modifier coreOnly() {
require(contributorsContract().addressIsCore(msg.sender));
_;
}
modifier contributorOnly() {
require(contributorsContract().addressExists(msg.sender));
_;
}
function contributorsContract() view public returns (Contributors) {
return Contributors(registry.getProxyFor('Contributors'));
}
function tokenContract() view public returns (Token) {
return Token(registry.getProxyFor('Token'));
}
function name() external view returns (string) {
return name_;
}
function symbol() external view returns (string) {
return symbol_;
}
function ownerOf(uint256 contributionId) public view returns (address) {
require(exists(contributionId));
return contributions[contributionId].contributor;
}
function balanceOf(address contributor) public view returns (uint) {
return ownedContributions[contributor].length;
}
function tokenOfOwnerByIndex(address contributor, uint index) public view returns (uint) {
return ownedContributions[contributor][index];
}
function tokenMetadata(uint contributionId) public view returns (string) {
return contributions[contributionId].tokenMetadataURL;
}
function getContribution(uint contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists) {
id = contributionId;
ContributionData storage c = contributions[id];
return (
id,
c.contributor,
c.amount,
c.claimed,
c.hashDigest,
c.hashFunction,
c.hashSize,
c.claimAfterBlock,
c.exists
);
}
function add(uint256 amount, address contributor, uint256 blocksToWait) public coreOnly {
uint contributionId = contributionsCount + 1;
ContributionData storage c = contributions[contributionId];
c.exists = true;
c.amount = amount;
c.claimed = false;
c.contributor = contributor;
c.claimAfterBlock = block.number + blocksToWait;
contributionsCount++;
contributionOwner[contributionId] = contributor;
ownedContributions[contributor].push(contributionId);
ContributionAdded(contributionId, contributor, amount);
}
function claim(uint256 contributionId) public {
ContributionData storage c = contributions[contributionId];
require(c.exists);
require(!c.claimed);
require(block.number > c.claimAfterBlock);
c.claimed = true;
tokenContract().mintFor(c.contributor, c.amount, contributionId);
ContributionClaimed(contributionId, c.contributor, c.amount);
}
function exists(uint256 contributionId) view public returns (bool) {
return contributions[contributionId].exists;
}
}

View File

@@ -1,128 +0,0 @@
pragma solidity ^0.4.18;
// import basic ERC20 details to be able to call balanceOf
import 'zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol';
import './upgradeable/Upgradeable.sol';
contract Contributors is Upgradeable {
struct Contributor {
address account;
bytes32 ipfsHash;
uint8 hashFunction;
uint8 hashSize;
bool isCore;
bool exists;
}
mapping (address => uint) public contributorIds;
mapping (uint => Contributor) public contributors;
uint256 public contributorsCount;
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
event ContributorAdded(uint id, address account);
modifier onlyCoreOrOperator() {
require(msg.sender == registry.getProxyFor('Operator') || addressIsCore(msg.sender));
_;
}
function initialize(address sender) public payable {
require(msg.sender == address(registry));
uint _id = 1;
Contributor storage c = contributors[_id];
c.exists = true;
c.isCore = true;
c.account = sender;
contributorIds[sender] = _id;
contributorsCount += 1;
}
function coreContributorsCount() view public returns (uint) {
uint count = 0;
for (uint256 i = 1; i <= contributorsCount; i++) {
if (contributors[i].isCore) {
count += 1;
}
}
return count;
}
function updateContributorAccount(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
contributorIds[oldAccount] = 0;
contributorIds[newAccount] = id;
contributors[id].account = newAccount;
ContributorAccountUpdated(id, oldAccount, newAccount);
}
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public onlyCoreOrOperator {
Contributor storage c = contributors[id];
bytes32 oldIpfsHash = c.ipfsHash;
c.ipfsHash = ipfsHash;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
}
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public onlyCoreOrOperator {
require(!addressExists(account));
uint _id = contributorsCount + 1;
assert(!contributors[_id].exists); // this can not be acually
Contributor storage c = contributors[_id];
c.exists = true;
c.isCore = isCore;
c.ipfsHash = ipfsHash;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
contributorIds[account] = _id;
contributorsCount += 1;
ContributorAdded(_id, account);
}
function isCore(uint id) view public returns (bool) {
return contributors[id].isCore;
}
function exists(uint id) view public returns (bool) {
return contributors[id].exists;
}
function addressIsCore(address account) view public returns (bool) {
return getContributorByAddress(account).isCore;
}
function addressExists(address account) view public returns (bool) {
return getContributorByAddress(account).exists;
}
function getContributorIdByAddress(address account) view public returns (uint) {
return contributorIds[account];
}
function getContributorAddressById(uint id) view public returns (address) {
return contributors[id].account;
}
function getContributorByAddress(address account) internal view returns (Contributor) {
uint id = contributorIds[account];
return contributors[id];
}
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint balance, bool exists ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
ipfsHash = c.ipfsHash;
hashFunction = c.hashFunction;
hashSize = c.hashSize;
isCore = c.isCore;
exists = c.exists;
ERC20Basic token = ERC20Basic(registry.getProxyFor('Token'));
balance = token.balanceOf(account);
}
}

85
contracts/KreditsKit.sol Normal file
View File

@@ -0,0 +1,85 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/acl/ACLSyntaxSugar.sol";
import "@aragon/kits-base/contracts/KitBase.sol";
import "./misc/APMNamehashOpen.sol";
import "../apps/contribution/contracts/Contribution.sol";
import "../apps/contributor/contracts/Contributor.sol";
import "../apps/token/contracts/Token.sol";
import "../apps/proposal/contracts/Proposal.sol";
contract KreditsKit is KitBase, APMNamehashOpen, ACLSyntaxSugar {
bytes32 public contributorAppId = apmNamehash("contributor"); // 0xe9140f1e39c8a1d04167c3b710688a3eecea2976f34735c8eb98956f4764635b
bytes32 public contributionAppId = apmNamehash("contribution"); // 0x7fcf91283b719b30c2fa954ff0da021e1b91aed09d7aa13df5e8078a4a1007eb
bytes32 public tokenAppId = apmNamehash("token"); // 0xe04a882e7a6adf5603207d545ea49aec17e6b936c4d9eae3d74dbe482264991a
bytes32 public proposalAppId = apmNamehash("proposal"); // 0xaf5fe5c3b0d9581ee88974bbc8699e6fa71efd1b321e44b2227103c9ef21dbdb
event DeployInstance(address dao);
event InstalledApp(address dao, address appProxy, bytes32 appId);
constructor (DAOFactory _fac, ENS _ens) public KitBase(_fac, _ens) {}
function newInstance() public returns (Kernel dao, ERCProxy proxy) {
address root = msg.sender;
dao = fac.newDAO(this);
ACL acl = ACL(dao.acl());
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
Contributor contributor = Contributor(_installApp(dao, contributorAppId));
contributor.initialize(root);
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), root);
Token token = Token(_installApp(dao, tokenAppId));
token.initialize();
Contribution contribution = Contribution(_installApp(dao, contributionAppId));
contribution.initialize();
Proposal proposal = Proposal(_installApp(dao, proposalAppId));
proposal.initialize();
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
uint256[] memory params = new uint256[](1);
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
acl.grantPermissionP(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
acl.grantPermissionP(root, proposal, proposal.ADD_PROPOSAL_ROLE(), params);
acl.setPermissionManager(root, proposal, proposal.VOTE_PROPOSAL_ROLE());
acl.setPermissionManager(root, proposal, proposal.ADD_PROPOSAL_ROLE());
acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE());
acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this);
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
cleanupDAOPermissions(dao, acl, root);
emit DeployInstance(dao);
//return dao;
}
function _installApp(Kernel _dao, bytes32 _appId) internal returns (AragonApp) {
address baseAppAddress = latestVersionAppBase(_appId);
require(baseAppAddress != address(0), "App should be deployed");
AragonApp appProxy = AragonApp(_dao.newAppInstance(_appId, baseAppAddress, new bytes(0), true));
emit InstalledApp(_dao, appProxy, _appId);
return appProxy;
}
}

View File

@@ -1,23 +0,0 @@
pragma solidity ^0.4.17;
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) _;
}
function Migrations() public {
owner = msg.sender;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}

View File

@@ -1,133 +0,0 @@
pragma solidity ^0.4.18;
// ToDo: only load interfaces
import './Token.sol';
import './Contributors.sol';
import './Contribution.sol';
contract Operator is Upgradeable {
struct Proposal {
address creatorAccount;
uint contributorId;
uint votesCount;
uint votesNeeded;
uint256 amount;
bool executed;
bytes32 ipfsHash;
uint8 hashFunction;
uint8 hashSize;
uint256[] voterIds;
mapping (uint256 => bool) votes;
bool exists;
}
mapping(uint256 => Proposal) public proposals;
uint256 public proposalsCount;
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
modifier coreOnly() {
require(contributorsContract().addressIsCore(msg.sender));
_;
}
modifier contributorOnly() {
require(contributorsContract().addressExists(msg.sender));
_;
}
modifier noEther() {
require(msg.value == 0);
_;
}
function contributorsContract() view public returns (Contributors) {
return Contributors(registry.getProxyFor('Contributors'));
}
function tokenContract() view public returns (Token) {
return Token(registry.getProxyFor('Token'));
}
function contributionContract() view public returns (Contribution) {
return Contribution(registry.getProxyFor('Contribution'));
}
function contributorsCount() view public returns (uint) {
return contributorsContract().contributorsCount();
}
function coreContributorsCount() view public returns (uint) {
return contributorsContract().coreContributorsCount();
}
function addProposal(uint contributorId, uint256 amount, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public {
require(contributorsContract().exists(contributorId));
uint256 proposalId = proposalsCount + 1;
uint256 _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
var p = proposals[proposalId];
p.creatorAccount = msg.sender;
p.contributorId = contributorId;
p.amount = amount;
p.ipfsHash = ipfsHash;
p.hashFunction = hashFunction;
p.hashSize = hashSize;
p.votesCount = 0;
p.votesNeeded = _votesNeeded;
p.exists = true;
proposalsCount++;
ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
}
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] voterIds, bool exists) {
id = proposalId;
Proposal storage p = proposals[id];
return (
id,
p.creatorAccount,
p.contributorId,
p.votesCount,
p.votesNeeded,
p.amount,
p.executed,
p.ipfsHash,
p.hashFunction,
p.hashSize,
p.voterIds,
p.exists
);
}
function vote(uint256 proposalId) public coreOnly {
var p = proposals[proposalId];
require(!p.executed);
uint256 voterId = contributorsContract().getContributorIdByAddress(msg.sender);
require(p.votes[voterId] != true);
p.voterIds.push(voterId);
p.votes[voterId] = true;
p.votesCount++;
if (p.votesCount >= p.votesNeeded) {
executeProposal(proposalId);
}
ProposalVoted(proposalId, voterId, p.votesCount);
}
function batchVote(uint256[] _proposalIds) public coreOnly {
for (uint256 i = 0; i < _proposalIds.length; i++) {
vote(_proposalIds[i]);
}
}
function executeProposal(uint proposalId) private {
var p = proposals[proposalId];
require(!p.executed);
require(p.votesCount >= p.votesNeeded);
address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId);
contributionContract().add(p.amount, recipientAddress, 0);
p.executed = true;
ProposalExecuted(proposalId, p.contributorId, p.amount);
}
}

View File

@@ -1,27 +0,0 @@
pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
import './upgradeable/Upgradeable.sol';
contract Token is Upgradeable, BasicToken {
string public name;
string public symbol;
uint8 public decimals;
event LogMint(address indexed recipient, uint256 amount, uint256 proposalId);
function initialize(address sender) public payable {
require(msg.sender == address(registry));
name = 'Kredits';
symbol = 'K';
decimals = 18;
}
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Contribution') public {
totalSupply_ = totalSupply_.add(amount);
balances[contributorAccount] = balances[contributorAccount].add(amount);
LogMint(contributorAccount, amount, proposalId);
}
}

View File

@@ -0,0 +1,13 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apm/APMNamehash.sol";
contract APMNamehashOpen is APMNamehash {
bytes32 public constant OPEN_TITLE = keccak256("open");
bytes32 public constant OPEN_APM_NODE = keccak256(abi.encodePacked(APM_NODE, OPEN_TITLE));
function apmNamehashOpen(string name) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(OPEN_APM_NODE, keccak256(name)));
}
}

View File

@@ -0,0 +1,16 @@
pragma solidity 0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
// This is a "Dummy" app which's only purpose to exist is because
// Aragon's CLI still doesn't support running a Kit inside a project
// which isn't considered to be a "valid" Aragon project.
// It requires us to have an arrap.json file pointing to the contract
// and a manifest.json file which describes the front-end structure.
contract DummyApp is AragonApp {
function initialize() public onlyInit {
initialized();
}
}

View File

@@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.4;
contract Migrations {
address public owner;
@@ -8,7 +8,7 @@ contract Migrations {
if (msg.sender == owner) _;
}
function Migrations() public {
function constructor() public {
owner = msg.sender;
}

View File

@@ -1,53 +0,0 @@
pragma solidity ^0.4.18;
/**
* @title IRegistry
* @dev This contract represents the interface of a registry contract
*/
interface IRegistry {
/**
* @dev This event will be emitted every time a new proxy is created
* @param name of the contract, as specified in the registry
* @param proxy representing the address of the proxy created
*/
event ProxyCreated(string name, address proxy);
/**
* @dev This event will be emitted every time a new implementation is registered
* @param name of the contract, as specified in the registry
* @param version representing the version name of the registered implementation
* @param implementation representing the address of the registered implementation
*/
event VersionAdded(string name, uint version, address implementation);
/**
* @dev This event will be emitted every time a proxy is upgraded to a new version
* @param name of the contract, as specified in the registry
* @param version representing the version name of the registered implementation
*/
event ProxyImplementationUpgraded(string name, uint version);
/**
* @dev Registers a new version with its implementation address
* @param name of the contract, as specified in the registry
* @param implementation representing the address of the new implementation to be registered
*/
function addVersion(string name, address implementation) public;
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract, as specified in the registry
* @param version to query the implementation of
* @return address of the implementation registered for the given version
*/
function getVersion(string name, uint version) public view returns (address);
/**
* @dev Tells the latest address of the implementation
* @param name of the contract, as specified in the registry
* @return address of the implementation registered for the latest version
*/
function getLatestVersion(string name) public view returns (address);
function getProxyFor(string name) public view returns (address);
}

View File

@@ -1,36 +0,0 @@
pragma solidity ^0.4.18;
/**
* @title Proxy
* @dev Gives the possibility to delegate any call to a foreign implementation.
*/
contract Proxy {
/**
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
function implementation() public view returns (address);
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
function () payable public {
address _impl = implementation();
require(_impl != address(0));
bytes memory data = msg.data;
assembly {
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}

View File

@@ -1,86 +0,0 @@
pragma solidity ^0.4.18;
import './IRegistry.sol';
import './Upgradeable.sol';
import './UpgradeabilityProxy.sol';
/**
* @title Registry
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions.
*/
contract Registry is IRegistry {
// mapping of contract names to versions to implementation
// "Token" => "1.0.0" => "0x123"
mapping(bytes32 => mapping(uint => address)) public versions;
// current version for a certain contract
mapping(bytes32 => uint) public currentVersions;
// mapping of the contract names to the proxy addresses
mapping(bytes32 => address) public proxies;
/**
* @dev Registers a new version with its implementation address
* @param name of the contract
* @param implementation representing the address of the new implementation to be registered
*/
function addVersion(string name, address implementation) public {
bytes32 key = keccak256(name);
currentVersions[key] = currentVersions[key] + 1;
uint version = currentVersions[key];
require(versions[key][version] == 0x0);
versions[key][version] = implementation;
VersionAdded(name, version, implementation);
}
/**
* @dev Tells the address of the implementation for a given version
* @param name of the contract
* @param version to query the implementation of
* @return address of the implementation registered for the given version
*/
function getVersion(string name, uint version) public view returns (address) {
bytes32 key = keccak256(name);
return versions[key][version];
}
function getLatestVersion(string name) public view returns (address) {
bytes32 key = keccak256(name);
uint current = currentVersions[key];
return getVersion(name, current);
}
function getProxyFor(string name) public view returns (address) {
bytes32 key = keccak256(name);
return proxies[key];
}
function upgrade(string name, uint version) public {
bytes32 key = keccak256(name);
UpgradeabilityProxy(proxies[key]).upgradeTo(version);
ProxyImplementationUpgraded(name, version);
}
function upgradeToLatest(string name) public {
bytes32 key = keccak256(name);
uint current = currentVersions[key];
upgrade(name, current);
}
/**
* @dev Creates an upgradeable proxy
* @param name of the contract
* @param version representing the first version to be set for the proxy
* @return address of the new proxy created
*/
function createProxy(string name, uint version) public payable returns (UpgradeabilityProxy) {
bytes32 key = keccak256(name);
require(proxies[key] == 0x0);
UpgradeabilityProxy proxy = new UpgradeabilityProxy(name, version);
proxies[key] = address(proxy);
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
ProxyCreated(name, proxy);
return proxy;
}
}

View File

@@ -1,28 +0,0 @@
pragma solidity ^0.4.18;
import './Proxy.sol';
import './IRegistry.sol';
import './UpgradeabilityStorage.sol';
/**
* @title UpgradeabilityProxy
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
*/
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
function UpgradeabilityProxy(string _name, uint _version) public {
_proxiedContractName = _name;
registry = IRegistry(msg.sender);
upgradeTo(_version);
}
/**
* @dev Upgrades the implementation to the requested version
* @param _version representing the version name of the new implementation to be set
*/
function upgradeTo(uint _version) public {
require(msg.sender == address(registry));
_implementation = registry.getVersion(_proxiedContractName, _version);
}
}

View File

@@ -1,37 +0,0 @@
pragma solidity ^0.4.18;
import './IRegistry.sol';
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Versions registry
IRegistry internal registry;
// Address of the current implementation
address internal _implementation;
// contract name
string public _proxiedContractName;
modifier requireRegistry() {
require(address(registry) != 0x0);
_;
}
modifier onlyRegistryContractFor(string name) {
require(address(registry) != 0x0);
require(msg.sender == registry.getProxyFor(name));
_;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}

View File

@@ -1,19 +0,0 @@
pragma solidity ^0.4.18;
import './UpgradeabilityStorage.sol';
/**
* @title Upgradeable
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable.
* This means, required state variables for owned upgradeability purpose and simple initialization validation.
*/
contract Upgradeable is UpgradeabilityStorage {
/**
* @dev Validates the caller is the versions registry.
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER
* @param sender representing the address deploying the initial behavior of the contract
*/
function initialize(address sender) public payable {
require(msg.sender == address(registry));
}
}