Skip to content

Commit ec2c63f

Browse files
committed
feat(contracts): add getIssuanceAllocator to IIssuanceTarget interface
Every issuance target should expose its allocator. Add getIssuanceAllocator() returning IIssuanceAllocationDistribution to IIssuanceTarget. Implement in RecurringAgreementManager (reads from storage), DirectAllocation (stores and returns), and RewardsManager (existing impl, moved from IRewardsManager to IIssuanceTarget). Also change IIssuanceTarget.setIssuanceAllocator parameter from address to IIssuanceAllocationDistribution for compile-time type safety.
1 parent fcb563d commit ec2c63f

20 files changed

Lines changed: 188 additions & 60 deletions

File tree

packages/contracts-test/tests/unit/rewards/rewards-interface.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ describe('RewardsManager interfaces', () => {
5454
})
5555

5656
it('IIssuanceTarget should have stable interface ID', () => {
57-
expect(IIssuanceTarget__factory.interfaceId).to.equal('0xaee4dc43')
57+
expect(IIssuanceTarget__factory.interfaceId).to.equal('0x19f6601a')
5858
})
5959

6060
it('IRewardsManager should have stable interface ID', () => {
61-
expect(IRewardsManager__factory.interfaceId).to.equal('0x337b092e')
61+
expect(IRewardsManager__factory.interfaceId).to.equal('0x8469b577')
6262
})
6363
})
6464

packages/contracts/contracts/rewards/RewardsManager.sol

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,24 +173,25 @@ contract RewardsManager is
173173
* Note that the IssuanceAllocator can be set to the zero address to disable use of an allocator, and
174174
* use the local `issuancePerBlock` variable instead to control issuance.
175175
*/
176-
function setIssuanceAllocator(address newIssuanceAllocator) external override onlyGovernor {
177-
if (address(issuanceAllocator) != newIssuanceAllocator) {
176+
function setIssuanceAllocator(IIssuanceAllocationDistribution newIssuanceAllocator) external override onlyGovernor {
177+
if (issuanceAllocator != newIssuanceAllocator) {
178178
// Update rewards calculation before changing the issuance allocator
179179
updateAccRewardsPerSignal();
180180

181181
// Check that the contract supports the IIssuanceAllocationDistribution interface
182182
// Allow zero address to disable the allocator
183-
if (newIssuanceAllocator != address(0)) {
183+
if (address(newIssuanceAllocator) != address(0)) {
184184
// solhint-disable-next-line gas-small-strings
185185
require(
186-
IERC165(newIssuanceAllocator).supportsInterface(type(IIssuanceAllocationDistribution).interfaceId),
186+
IERC165(address(newIssuanceAllocator)).supportsInterface(
187+
type(IIssuanceAllocationDistribution).interfaceId
188+
),
187189
"Contract does not support IIssuanceAllocationDistribution interface"
188190
);
189191
}
190192

191-
address oldIssuanceAllocator = address(issuanceAllocator);
192-
issuanceAllocator = IIssuanceAllocationDistribution(newIssuanceAllocator);
193-
emit IssuanceAllocatorSet(oldIssuanceAllocator, newIssuanceAllocator);
193+
emit IssuanceAllocatorSet(issuanceAllocator, newIssuanceAllocator);
194+
issuanceAllocator = newIssuanceAllocator;
194195
}
195196
}
196197

@@ -325,7 +326,7 @@ contract RewardsManager is
325326
}
326327

327328
/**
328-
* @inheritdoc IRewardsManager
329+
* @inheritdoc IIssuanceTarget
329330
*/
330331
function getIssuanceAllocator() external view override returns (IIssuanceAllocationDistribution) {
331332
return issuanceAllocator;

packages/deployment/lib/abis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function loadAbi(artifactPath: string): Abi {
2121
// Verified by tests: packages/issuance/testing/tests/allocate/InterfaceIdStability.test.ts
2222
// and packages/contracts-test/tests/unit/rewards/rewards-interface.test.ts
2323
export const IERC165_INTERFACE_ID = '0x01ffc9a7' as const
24-
export const IISSUANCE_TARGET_INTERFACE_ID = '0xaee4dc43' as const
24+
export const IISSUANCE_TARGET_INTERFACE_ID = '0x19f6601a' as const
2525
export const IREWARDS_MANAGER_INTERFACE_ID = '0xa0a2f219' as const
2626

2727
export const REWARDS_MANAGER_ABI = loadAbi(

packages/interfaces/contracts/contracts/rewards/IRewardsManager.sol

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
pragma solidity ^0.7.6 || ^0.8.0;
44

5-
import { IIssuanceAllocationDistribution } from "../../issuance/allocate/IIssuanceAllocationDistribution.sol";
65
import { IRewardsIssuer } from "./IRewardsIssuer.sol";
76

87
/**
@@ -179,13 +178,6 @@ interface IRewardsManager {
179178
*/
180179
function subgraphService() external view returns (IRewardsIssuer);
181180

182-
/**
183-
* @notice Get the issuance allocator address
184-
* @dev When set, this allocator controls issuance distribution instead of issuancePerBlock
185-
* @return The issuance allocator contract (zero address if not set)
186-
*/
187-
function getIssuanceAllocator() external view returns (IIssuanceAllocationDistribution);
188-
189181
/**
190182
* @notice Get the reclaim address for a specific reason
191183
* @param reason The reclaim reason identifier

packages/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
pragma solidity ^0.7.6 || ^0.8.0;
44

5+
import { IIssuanceAllocationDistribution } from "./IIssuanceAllocationDistribution.sol";
6+
57
/**
68
* @title IIssuanceTarget
79
* @author Edge & Node
@@ -13,7 +15,10 @@ interface IIssuanceTarget {
1315
* @param oldIssuanceAllocator Old issuance allocator address
1416
* @param newIssuanceAllocator New issuance allocator address
1517
*/
16-
event IssuanceAllocatorSet(address indexed oldIssuanceAllocator, address indexed newIssuanceAllocator);
18+
event IssuanceAllocatorSet(
19+
IIssuanceAllocationDistribution indexed oldIssuanceAllocator,
20+
IIssuanceAllocationDistribution indexed newIssuanceAllocator
21+
);
1722

1823
/// @notice Emitted before the issuance allocation changes
1924
event BeforeIssuanceAllocationChange();
@@ -27,11 +32,17 @@ interface IIssuanceTarget {
2732
*/
2833
function beforeIssuanceAllocationChange() external;
2934

35+
/**
36+
* @notice Returns the current issuance allocator
37+
* @return The issuance allocator contract (zero address if not set)
38+
*/
39+
function getIssuanceAllocator() external view returns (IIssuanceAllocationDistribution);
40+
3041
/**
3142
* @notice Sets the issuance allocator for this target
3243
* @dev This function facilitates upgrades by providing a standard way for targets
3344
* to change their allocator. Implementations can define their own access control.
3445
* @param newIssuanceAllocator Address of the issuance allocator
3546
*/
36-
function setIssuanceAllocator(address newIssuanceAllocator) external;
47+
function setIssuanceAllocator(IIssuanceAllocationDistribution newIssuanceAllocator) external;
3748
}

packages/issuance/contracts/agreement/RecurringAgreementManager.sol

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ contract RecurringAgreementManager is
277277
/// @inheritdoc IIssuanceTarget
278278
function beforeIssuanceAllocationChange() external virtual override {}
279279

280+
/// @inheritdoc IIssuanceTarget
281+
function getIssuanceAllocator() external view virtual override returns (IIssuanceAllocationDistribution) {
282+
return _getStorage().issuanceAllocator;
283+
}
284+
280285
/// @inheritdoc IIssuanceTarget
281286
/// @dev The allocator is expected to call distributeIssuance() (bringing distribution up to
282287
/// the current block) before any configuration change. As a result, the same-block dedup in
@@ -285,21 +290,23 @@ contract RecurringAgreementManager is
285290
/// in a standalone transaction to avoid interleaving with collection in the same block.
286291
/// Even if interleaved, the only effect is a one-block lag before the new allocator's
287292
/// distribution is picked up — corrected automatically on the next block.
288-
function setIssuanceAllocator(address newIssuanceAllocator) external virtual override onlyRole(GOVERNOR_ROLE) {
293+
function setIssuanceAllocator(
294+
IIssuanceAllocationDistribution newIssuanceAllocator
295+
) external virtual override onlyRole(GOVERNOR_ROLE) {
289296
RecurringAgreementManagerStorage storage $ = _getStorage();
290-
if (address($.issuanceAllocator) == newIssuanceAllocator) return;
297+
if (address($.issuanceAllocator) == address(newIssuanceAllocator)) return;
291298

292-
if (newIssuanceAllocator != address(0))
299+
if (address(newIssuanceAllocator) != address(0))
293300
require(
294301
ERC165Checker.supportsInterface(
295-
newIssuanceAllocator,
302+
address(newIssuanceAllocator),
296303
type(IIssuanceAllocationDistribution).interfaceId
297304
),
298-
InvalidIssuanceAllocator(newIssuanceAllocator)
305+
InvalidIssuanceAllocator(address(newIssuanceAllocator))
299306
);
300307

301-
emit IssuanceAllocatorSet(address($.issuanceAllocator), newIssuanceAllocator);
302-
$.issuanceAllocator = IIssuanceAllocationDistribution(newIssuanceAllocator);
308+
emit IssuanceAllocatorSet($.issuanceAllocator, newIssuanceAllocator);
309+
$.issuanceAllocator = newIssuanceAllocator;
303310
}
304311

305312
// -- IAgreementOwner --

packages/issuance/contracts/allocate/DirectAllocation.sol

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
pragma solidity ^0.8.27;
44

5+
import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";
56
import { IIssuanceTarget } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol";
67
import { ISendTokens } from "@graphprotocol/interfaces/contracts/issuance/allocate/ISendTokens.sol";
78
import { BaseUpgradeable } from "../common/BaseUpgradeable.sol";
@@ -24,6 +25,36 @@ import { ERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/int
2425
* @custom:security-contact Please email security+contracts@thegraph.com if you find any bugs. We might have an active bug bounty program.
2526
*/
2627
contract DirectAllocation is BaseUpgradeable, IIssuanceTarget, ISendTokens {
28+
// -- Namespaced Storage --
29+
30+
/// @notice ERC-7201 storage location for DirectAllocation
31+
bytes32 private constant DIRECT_ALLOCATION_STORAGE_LOCATION =
32+
// solhint-disable-next-line gas-small-strings
33+
keccak256(abi.encode(uint256(keccak256("graphprotocol.storage.DirectAllocation")) - 1)) &
34+
~bytes32(uint256(0xff));
35+
36+
/// @notice Main storage structure for DirectAllocation using ERC-7201 namespaced storage
37+
/// @param issuanceAllocator The issuance allocator that distributes tokens to this contract
38+
/// @custom:storage-location erc7201:graphprotocol.storage.DirectAllocation
39+
struct DirectAllocationData {
40+
IIssuanceAllocationDistribution issuanceAllocator;
41+
}
42+
43+
/**
44+
* @notice Returns the storage struct for DirectAllocation
45+
* @return $ contract storage
46+
*/
47+
function _getDirectAllocationStorage() private pure returns (DirectAllocationData storage $) {
48+
// solhint-disable-previous-line use-natspec
49+
// Solhint does not support $ return variable in natspec
50+
51+
bytes32 slot = DIRECT_ALLOCATION_STORAGE_LOCATION;
52+
// solhint-disable-next-line no-inline-assembly
53+
assembly {
54+
$.slot := slot
55+
}
56+
}
57+
2758
// -- Custom Errors --
2859

2960
/// @notice Thrown when token transfer fails
@@ -89,9 +120,19 @@ contract DirectAllocation is BaseUpgradeable, IIssuanceTarget, ISendTokens {
89120
*/
90121
function beforeIssuanceAllocationChange() external virtual override {}
91122

92-
/**
93-
* @dev No-op for DirectAllocation; issuanceAllocator is not stored.
94-
* @inheritdoc IIssuanceTarget
95-
*/
96-
function setIssuanceAllocator(address issuanceAllocator) external virtual override onlyRole(GOVERNOR_ROLE) {}
123+
/// @inheritdoc IIssuanceTarget
124+
function getIssuanceAllocator() external view virtual override returns (IIssuanceAllocationDistribution) {
125+
return _getDirectAllocationStorage().issuanceAllocator;
126+
}
127+
128+
/// @inheritdoc IIssuanceTarget
129+
function setIssuanceAllocator(
130+
IIssuanceAllocationDistribution newIssuanceAllocator
131+
) external virtual override onlyRole(GOVERNOR_ROLE) {
132+
DirectAllocationData storage $ = _getDirectAllocationStorage();
133+
if (address(newIssuanceAllocator) == address($.issuanceAllocator)) return;
134+
135+
emit IssuanceAllocatorSet($.issuanceAllocator, newIssuanceAllocator);
136+
$.issuanceAllocator = newIssuanceAllocator;
137+
}
97138
}

packages/issuance/contracts/test/allocate/MockNotificationTracker.sol

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4+
import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";
45
import { IIssuanceTarget } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol";
56
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
67

@@ -30,7 +31,12 @@ contract MockNotificationTracker is IIssuanceTarget, ERC165 {
3031
}
3132

3233
/// @inheritdoc IIssuanceTarget
33-
function setIssuanceAllocator(address _issuanceAllocator) external pure override {}
34+
function getIssuanceAllocator() external pure override returns (IIssuanceAllocationDistribution) {
35+
return IIssuanceAllocationDistribution(address(0));
36+
}
37+
38+
/// @inheritdoc IIssuanceTarget
39+
function setIssuanceAllocator(IIssuanceAllocationDistribution _issuanceAllocator) external pure override {}
3440

3541
/// @inheritdoc ERC165
3642
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {

packages/issuance/contracts/test/allocate/MockReentrantTarget.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,13 @@ contract MockReentrantTarget is IIssuanceTarget, ERC165 {
8585
}
8686

8787
/// @inheritdoc IIssuanceTarget
88-
function setIssuanceAllocator(address _issuanceAllocator) external override {
89-
issuanceAllocator = _issuanceAllocator;
88+
function getIssuanceAllocator() external view override returns (IIssuanceAllocationDistribution) {
89+
return IIssuanceAllocationDistribution(issuanceAllocator);
90+
}
91+
92+
/// @inheritdoc IIssuanceTarget
93+
function setIssuanceAllocator(IIssuanceAllocationDistribution _issuanceAllocator) external override {
94+
issuanceAllocator = address(_issuanceAllocator);
9095
}
9196

9297
/// @inheritdoc ERC165

packages/issuance/contracts/test/allocate/MockRevertingTarget.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4+
import { IIssuanceAllocationDistribution } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceAllocationDistribution.sol";
45
import { IIssuanceTarget } from "@graphprotocol/interfaces/contracts/issuance/allocate/IIssuanceTarget.sol";
56
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
67

@@ -23,7 +24,14 @@ contract MockRevertingTarget is IIssuanceTarget, ERC165 {
2324
/**
2425
* @inheritdoc IIssuanceTarget
2526
*/
26-
function setIssuanceAllocator(address _issuanceAllocator) external pure override {
27+
function getIssuanceAllocator() external pure override returns (IIssuanceAllocationDistribution) {
28+
return IIssuanceAllocationDistribution(address(0));
29+
}
30+
31+
/**
32+
* @inheritdoc IIssuanceTarget
33+
*/
34+
function setIssuanceAllocator(IIssuanceAllocationDistribution _issuanceAllocator) external pure override {
2735
// No-op
2836
}
2937

0 commit comments

Comments
 (0)