Skip to content

Commit fef2f3c

Browse files
feat: add publishMetadataURI (#1492)
**Motivation:** *Explain here the context, and why you're making that change. What is the problem you're trying to solve.* **Modifications:** *Describe the modifications you've done.* **Result:** *After your change, what will change.*
1 parent 6d6e54e commit fef2f3c

File tree

4 files changed

+93
-6
lines changed

4 files changed

+93
-6
lines changed

src/contracts/core/ReleaseManager.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ contract ReleaseManager is Initializable, ReleaseManagerStorage, PermissionContr
3434
) external checkCanCall(operatorSet.avs) returns (uint256 releaseId) {
3535
Release[] storage releases = _operatorSetReleases[operatorSet.key()];
3636

37+
require(bytes(_operatorSetMetadataURI[operatorSet.key()]).length != 0, MustPublishMetadataURI());
3738
require(release.upgradeByTime >= block.timestamp, InvalidUpgradeByTime());
3839

3940
// New release id is the length of the array before this call.
@@ -49,6 +50,16 @@ contract ReleaseManager is Initializable, ReleaseManagerStorage, PermissionContr
4950
emit ReleasePublished(operatorSet, releaseId, release);
5051
}
5152

53+
/// @inheritdoc IReleaseManager
54+
function publishMetadataURI(
55+
OperatorSet calldata operatorSet,
56+
string calldata metadataURI
57+
) external checkCanCall(operatorSet.avs) {
58+
require(bytes(metadataURI).length != 0, InvalidMetadataURI());
59+
_operatorSetMetadataURI[operatorSet.key()] = metadataURI;
60+
emit MetadataURIPublished(operatorSet, metadataURI);
61+
}
62+
5263
/**
5364
*
5465
* VIEW FUNCTIONS
@@ -89,4 +100,11 @@ contract ReleaseManager is Initializable, ReleaseManagerStorage, PermissionContr
89100
function isValidRelease(OperatorSet memory operatorSet, uint256 releaseId) external view returns (bool) {
90101
return releaseId == getTotalReleases(operatorSet) - 1;
91102
}
103+
104+
/// @inheritdoc IReleaseManager
105+
function getMetadataURI(
106+
OperatorSet memory operatorSet
107+
) external view returns (string memory) {
108+
return _operatorSetMetadataURI[operatorSet.key()];
109+
}
92110
}

src/contracts/core/ReleaseManagerStorage.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ abstract contract ReleaseManagerStorage is IReleaseManager {
1010
/// @notice Returns an array of releases for a given operator set.
1111
mapping(bytes32 operatorSetKey => Release[]) internal _operatorSetReleases;
1212

13+
/// @notice Returns the metadata URI for a given operator set.
14+
mapping(bytes32 operatorSetKey => string metadataURI) internal _operatorSetMetadataURI;
15+
1316
/**
1417
* @dev This empty reserved space is put in place to allow future versions to add new
1518
* variables without shifting down storage in the inheritance chain.

src/contracts/interfaces/IReleaseManager.sol

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@ pragma solidity ^0.8.27;
44
import "../libraries/OperatorSetLib.sol";
55

66
interface IReleaseManagerErrors {
7+
/// @notice Thrown when a metadata URI must be published before publishing a release.
8+
error MustPublishMetadataURI();
9+
710
/// @notice Thrown when the upgrade by time is in the past.
811
error InvalidUpgradeByTime();
12+
13+
/// @notice Thrown when the metadata URI is empty.
14+
error InvalidMetadataURI();
915
}
1016

1117
interface IReleaseManagerTypes {
1218
/// @notice Represents a software artifact with its digest and registry URL.
1319
/// @param digest The hash digest of the artifact.
14-
/// @param registryUrl The URL where the artifact can be found.
20+
/// @param registry Where the artifact can be found.
1521
struct Artifact {
1622
bytes32 digest;
17-
string registryUrl;
23+
string registry;
1824
}
1925

2026
/// @notice Represents a release containing multiple artifacts and an upgrade deadline.
@@ -32,6 +38,11 @@ interface IReleaseManagerEvents is IReleaseManagerTypes {
3238
/// @param releaseId The id of the release that was published.
3339
/// @param release The release that was published.
3440
event ReleasePublished(OperatorSet indexed operatorSet, uint256 indexed releaseId, Release release);
41+
42+
/// @notice Emitted when a metadata URI is published.
43+
/// @param operatorSet The operator set this metadata URI is for.
44+
/// @param metadataURI The metadata URI that was published.
45+
event MetadataURIPublished(OperatorSet indexed operatorSet, string metadataURI);
3546
}
3647

3748
interface IReleaseManager is IReleaseManagerErrors, IReleaseManagerEvents {
@@ -50,6 +61,11 @@ interface IReleaseManager is IReleaseManagerErrors, IReleaseManagerEvents {
5061
Release calldata release
5162
) external returns (uint256 releaseId);
5263

64+
/// @notice Publishes a metadata URI for an operator set.
65+
/// @param operatorSet The operator set this metadata URI is for.
66+
/// @param metadataURI The metadata URI that was published.
67+
function publishMetadataURI(OperatorSet calldata operatorSet, string calldata metadataURI) external;
68+
5369
/**
5470
*
5571
* VIEW FUNCTIONS
@@ -89,4 +105,11 @@ interface IReleaseManager is IReleaseManagerErrors, IReleaseManagerEvents {
89105
/// @param releaseId The id of the release to check.
90106
/// @return True if the release is the latest release, false otherwise.
91107
function isValidRelease(OperatorSet memory operatorSet, uint256 releaseId) external view returns (bool);
108+
109+
/// @notice Returns the metadata URI for an operator set.
110+
/// @param operatorSet The operator set to query.
111+
/// @return The metadata URI for the operator set.
112+
function getMetadataURI(
113+
OperatorSet memory operatorSet
114+
) external view returns (string memory);
92115
}

src/test/unit/ReleaseManagerUnit.t.sol

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@ contract ReleaseManagerUnitTests is EigenLayerUnitTestSetup, IReleaseManagerErro
4747
// Setup default test data
4848
defaultOperatorSet = OperatorSet(defaultAVS, 0);
4949

50-
defaultArtifacts.push(Artifact({digest: keccak256("artifact1"), registryUrl: "https://example.com/artifact1"}));
51-
defaultArtifacts.push(Artifact({digest: keccak256("artifact2"), registryUrl: "https://example.com/artifact2"}));
50+
defaultArtifacts.push(Artifact({digest: keccak256("artifact1"), registry: "https://example.com/artifact1"}));
51+
defaultArtifacts.push(Artifact({digest: keccak256("artifact2"), registry: "https://example.com/artifact2"}));
5252

5353
defaultRelease.upgradeByTime = uint32(block.timestamp + 1 days);
5454

5555
cheats.prank(defaultAVS);
5656
permissionController.setAppointee(defaultAVS, address(this), address(releaseManager), IReleaseManager.publishRelease.selector);
57+
58+
cheats.prank(defaultAVS);
59+
releaseManager.publishMetadataURI(defaultOperatorSet, "https://example.com/metadata");
5760
}
5861

5962
/// -----------------------------------------------------------------------
@@ -69,7 +72,7 @@ contract ReleaseManagerUnitTests is EigenLayerUnitTestSetup, IReleaseManagerErro
6972
for (uint i = 0; i < numArtifacts; i++) {
7073
artifacts[i] = Artifact({
7174
digest: keccak256(abi.encodePacked("artifact", i)),
72-
registryUrl: string(abi.encodePacked("https://example.com/artifact", i))
75+
registry: string(abi.encodePacked("https://example.com/artifact", i))
7376
});
7477
}
7578
return _createRelease(artifacts, upgradeByTime);
@@ -92,7 +95,7 @@ contract ReleaseManagerUnitTests is EigenLayerUnitTestSetup, IReleaseManagerErro
9295

9396
for (uint i = 0; i < actualRelease.artifacts.length; i++) {
9497
assertEq(actualRelease.artifacts[i].digest, expectedRelease.artifacts[i].digest, "artifact digest mismatch");
95-
assertEq(actualRelease.artifacts[i].registryUrl, expectedRelease.artifacts[i].registryUrl, "artifact registryUrl mismatch");
98+
assertEq(actualRelease.artifacts[i].registry, expectedRelease.artifacts[i].registry, "artifact registry mismatch");
9699
}
97100

98101
console.log("Success!".green().bold());
@@ -108,6 +111,14 @@ contract ReleaseManagerUnitTests_Initialization is ReleaseManagerUnitTests {
108111
}
109112

110113
contract ReleaseManagerUnitTests_publishRelease is ReleaseManagerUnitTests {
114+
function test_revert_MustPublishMetadataURI() public {
115+
OperatorSet memory operatorSet = OperatorSet(defaultAVS, 1);
116+
117+
cheats.prank(defaultAVS);
118+
vm.expectRevert(IReleaseManagerErrors.MustPublishMetadataURI.selector);
119+
releaseManager.publishRelease(operatorSet, defaultRelease);
120+
}
121+
111122
function test_revert_InvalidUpgradeByTime() public {
112123
// Create release with past timestamp
113124
Release memory pastRelease = _createRelease(defaultArtifacts, uint32(block.timestamp - 1));
@@ -204,6 +215,11 @@ contract ReleaseManagerUnitTests_publishRelease is ReleaseManagerUnitTests {
204215
OperatorSet memory operatorSet1 = OperatorSet(defaultAVS, operatorSetId1);
205216
OperatorSet memory operatorSet2 = OperatorSet(defaultAVS, operatorSetId2);
206217

218+
cheats.prank(operatorSet1.avs);
219+
releaseManager.publishMetadataURI(operatorSet1, "https://example.com/metadata");
220+
cheats.prank(operatorSet2.avs);
221+
releaseManager.publishMetadataURI(operatorSet2, "https://example.com/metadata");
222+
207223
// Publish to first operator set
208224
uint releaseId1 = _publishRelease(operatorSet1, defaultRelease);
209225
assertEq(releaseId1, 0, "first release in set1 should be 0");
@@ -247,6 +263,9 @@ contract ReleaseManagerUnitTests_getTotalReleases is ReleaseManagerUnitTests {
247263
function test_getTotalReleases_differentOperatorSets() public {
248264
OperatorSet memory operatorSet2 = OperatorSet(defaultAVS, 1);
249265

266+
cheats.prank(operatorSet2.avs);
267+
releaseManager.publishMetadataURI(operatorSet2, "https://example.com/metadata");
268+
250269
// Publish to different sets
251270
_publishRelease(defaultOperatorSet, defaultRelease);
252271
_publishRelease(defaultOperatorSet, defaultRelease);
@@ -395,6 +414,11 @@ contract ReleaseManagerUnitTests_EdgeCases is ReleaseManagerUnitTests {
395414
OperatorSet memory set1 = OperatorSet(defaultAVS, 1);
396415
OperatorSet memory set2 = OperatorSet(address(0x5678), 0);
397416

417+
cheats.prank(set1.avs);
418+
releaseManager.publishMetadataURI(set1, "https://example.com/metadata");
419+
cheats.prank(set2.avs);
420+
releaseManager.publishMetadataURI(set2, "https://example.com/metadata");
421+
398422
// Grant permission for second AVS
399423
cheats.prank(set2.avs);
400424
permissionController.setAppointee(set2.avs, address(this), address(releaseManager), IReleaseManager.publishRelease.selector);
@@ -474,3 +498,22 @@ contract ReleaseManagerUnitTests_isValidRelease is ReleaseManagerUnitTests {
474498
assertEq(isLatest, true, "second release should be the latest");
475499
}
476500
}
501+
502+
contract ReleaseManagerUnitTests_publishMetadataURI is ReleaseManagerUnitTests {
503+
function test_revert_InvalidMetadataURI() public {
504+
cheats.prank(defaultAVS);
505+
vm.expectRevert(IReleaseManagerErrors.InvalidMetadataURI.selector);
506+
releaseManager.publishMetadataURI(defaultOperatorSet, "");
507+
}
508+
509+
function test_publishMetadataURI_Correctness() public {
510+
string memory registry = "https://example.com/metadata";
511+
cheats.expectEmit(true, true, true, true, address(releaseManager));
512+
emit MetadataURIPublished(defaultOperatorSet, registry);
513+
514+
cheats.prank(defaultAVS);
515+
releaseManager.publishMetadataURI(defaultOperatorSet, registry);
516+
517+
assertEq(releaseManager.getMetadataURI(defaultOperatorSet), registry, "metadata URI not set correctly");
518+
}
519+
}

0 commit comments

Comments
 (0)