This repository has been archived by the owner on Jan 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 200
/
Package.sol
129 lines (113 loc) · 5.77 KB
/
Package.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
pragma solidity ^0.4.24;
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
/**
* @title Package
* @dev A package is composed by a set of versions, identified via semantic versioning,
* where each version has a contract address that refers to a reusable implementation,
* plus an optional content URI with metadata. Note that the semver identifier is restricted
* to major, minor, and patch, as prerelease tags are not supported.
*/
contract Package is Ownable {
/**
* @dev Emitted when a version is added to the package.
* @param semanticVersion Name of the added version.
* @param contractAddress Contract associated with the version.
* @param contentURI Optional content URI with metadata of the version.
*/
event VersionAdded(uint64[3] semanticVersion, address contractAddress, bytes contentURI);
struct Version {
uint64[3] semanticVersion;
address contractAddress;
bytes contentURI;
}
mapping (bytes32 => Version) internal versions;
mapping (uint64 => bytes32) internal majorToLatestVersion;
uint64 internal latestMajor;
/**
* @dev Returns a version given its semver identifier.
* @param semanticVersion Semver identifier of the version.
* @return Contract address and content URI for the version, or zero if not exists.
*/
function getVersion(uint64[3] semanticVersion) public view returns (address contractAddress, bytes contentURI) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return (version.contractAddress, version.contentURI);
}
/**
* @dev Returns a contract for a version given its semver identifier.
* This method is equivalent to `getVersion`, but returns only the contract address.
* @param semanticVersion Semver identifier of the version.
* @return Contract address for the version, or zero if not exists.
*/
function getContract(uint64[3] semanticVersion) public view returns (address contractAddress) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return version.contractAddress;
}
/**
* @dev Adds a new version to the package. Only the Owner can add new versions.
* Reverts if the specified semver identifier already exists.
* Emits a `VersionAdded` event if successful.
* @param semanticVersion Semver identifier of the version.
* @param contractAddress Contract address for the version, must be non-zero.
* @param contentURI Optional content URI for the version.
*/
function addVersion(uint64[3] semanticVersion, address contractAddress, bytes contentURI) public onlyOwner {
require(contractAddress != address(0), "Contract address is required");
require(!hasVersion(semanticVersion), "Given version is already registered in package");
require(!semanticVersionIsZero(semanticVersion), "Version must be non zero");
// Register version
bytes32 versionId = semanticVersionHash(semanticVersion);
versions[versionId] = Version(semanticVersion, contractAddress, contentURI);
// Update latest major
uint64 major = semanticVersion[0];
if (major > latestMajor) {
latestMajor = semanticVersion[0];
}
// Update latest version for this major
uint64 minor = semanticVersion[1];
uint64 patch = semanticVersion[2];
uint64[3] latestVersionForMajor = versions[majorToLatestVersion[major]].semanticVersion;
if (semanticVersionIsZero(latestVersionForMajor) // No latest was set for this major
|| (minor > latestVersionForMajor[1]) // Or current minor is greater
|| (minor == latestVersionForMajor[1] && patch > latestVersionForMajor[2]) // Or current patch is greater
) {
majorToLatestVersion[major] = versionId;
}
emit VersionAdded(semanticVersion, contractAddress, contentURI);
}
/**
* @dev Checks whether a version is present in the package.
* @param semanticVersion Semver identifier of the version.
* @return true if the version is registered in this package, false otherwise.
*/
function hasVersion(uint64[3] semanticVersion) public view returns (bool) {
Version storage version = versions[semanticVersionHash(semanticVersion)];
return address(version.contractAddress) != address(0);
}
/**
* @dev Returns the version with the highest semver identifier registered in the package.
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will always return `2.0.0`, regardless
* of the order in which they were registered. Returns zero if no versions are registered.
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
*/
function getLatest() public view returns (uint64[3] semanticVersion, address contractAddress, bytes contentURI) {
return getLatestByMajor(latestMajor);
}
/**
* @dev Returns the version with the highest semver identifier for the given major.
* For instance, if `1.2.0`, `1.3.0`, and `2.0.0` are present, will return `1.3.0` for major `1`,
* regardless of the order in which they were registered. Returns zero if no versions are registered
* for the specified major.
* @param major Major identifier to query
* @return Semver identifier, contract address, and content URI for the version, or zero if not exists.
*/
function getLatestByMajor(uint64 major) public view returns (uint64[3] semanticVersion, address contractAddress, bytes contentURI) {
Version storage version = versions[majorToLatestVersion[major]];
return (version.semanticVersion, version.contractAddress, version.contentURI);
}
function semanticVersionHash(uint64[3] version) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(version[0], version[1], version[2]));
}
function semanticVersionIsZero(uint64[3] version) internal pure returns (bool) {
return version[0] == 0 && version[1] == 0 && version[2] == 0;
}
}