Skip to content

Commit 587a4a7

Browse files
authored
[entropy] Add Fee Manager Role (#1623)
* initial cut at entropy fee manager * cleanup * tests * update ABIs * doc * bump version * add ABIs
1 parent 3513eed commit 587a4a7

File tree

9 files changed

+309
-2
lines changed

9 files changed

+309
-2
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/ethereum/contracts/contracts/entropy/Entropy.sol

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,38 @@ abstract contract Entropy is IEntropy, EntropyState {
162162
// Interaction with an external contract or token transfer
163163
(bool sent, ) = msg.sender.call{value: amount}("");
164164
require(sent, "withdrawal to msg.sender failed");
165+
166+
emit Withdrawal(msg.sender, msg.sender, amount);
167+
}
168+
169+
function withdrawAsFeeManager(
170+
address provider,
171+
uint128 amount
172+
) external override {
173+
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
174+
provider
175+
];
176+
177+
if (providerInfo.sequenceNumber == 0) {
178+
revert EntropyErrors.NoSuchProvider();
179+
}
180+
181+
if (providerInfo.feeManager != msg.sender) {
182+
revert EntropyErrors.Unauthorized();
183+
}
184+
185+
// Use checks-effects-interactions pattern to prevent reentrancy attacks.
186+
require(
187+
providerInfo.accruedFeesInWei >= amount,
188+
"Insufficient balance"
189+
);
190+
providerInfo.accruedFeesInWei -= amount;
191+
192+
// Interaction with an external contract or token transfer
193+
(bool sent, ) = msg.sender.call{value: amount}("");
194+
require(sent, "withdrawal to msg.sender failed");
195+
196+
emit Withdrawal(provider, msg.sender, amount);
165197
}
166198

167199
// requestHelper allocates and returns a new request for the given provider.
@@ -475,6 +507,28 @@ abstract contract Entropy is IEntropy, EntropyState {
475507
emit ProviderFeeUpdated(msg.sender, oldFeeInWei, newFeeInWei);
476508
}
477509

510+
function setProviderFeeAsFeeManager(
511+
address provider,
512+
uint128 newFeeInWei
513+
) external override {
514+
EntropyStructs.ProviderInfo storage providerInfo = _state.providers[
515+
provider
516+
];
517+
518+
if (providerInfo.sequenceNumber == 0) {
519+
revert EntropyErrors.NoSuchProvider();
520+
}
521+
522+
if (providerInfo.feeManager != msg.sender) {
523+
revert EntropyErrors.Unauthorized();
524+
}
525+
526+
uint128 oldFeeInWei = providerInfo.feeInWei;
527+
providerInfo.feeInWei = newFeeInWei;
528+
529+
emit ProviderFeeUpdated(provider, oldFeeInWei, newFeeInWei);
530+
}
531+
478532
// Set provider uri. It will revert if provider is not registered.
479533
function setProviderUri(bytes calldata newUri) external override {
480534
EntropyStructs.ProviderInfo storage provider = _state.providers[
@@ -488,6 +542,19 @@ abstract contract Entropy is IEntropy, EntropyState {
488542
emit ProviderUriUpdated(msg.sender, oldUri, newUri);
489543
}
490544

545+
function setFeeManager(address manager) external override {
546+
EntropyStructs.ProviderInfo storage provider = _state.providers[
547+
msg.sender
548+
];
549+
if (provider.sequenceNumber == 0) {
550+
revert EntropyErrors.NoSuchProvider();
551+
}
552+
553+
address oldFeeManager = provider.feeManager;
554+
provider.feeManager = manager;
555+
emit ProviderFeeManagerUpdated(msg.sender, oldFeeManager, manager);
556+
}
557+
491558
function constructUserCommitment(
492559
bytes32 userRandomness
493560
) public pure override returns (bytes32 userCommitment) {

target_chains/ethereum/contracts/forge-test/Entropy.t.sol

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,52 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents {
926926
provider1Proofs[assignedSequenceNumber]
927927
);
928928
}
929+
930+
function testFeeManager() public {
931+
address manager = address(12);
932+
933+
// Make sure there are some fees for provider1
934+
request(user1, provider1, 42, false);
935+
936+
// Manager isn't authorized, so can't withdraw or set fee.
937+
vm.prank(manager);
938+
vm.expectRevert();
939+
random.withdrawAsFeeManager(provider1, provider1FeeInWei);
940+
vm.prank(manager);
941+
vm.expectRevert();
942+
random.setProviderFeeAsFeeManager(provider1, 1000);
943+
944+
// You can't set a fee manager from an address that isn't a registered provider.
945+
vm.expectRevert();
946+
random.setFeeManager(address(manager));
947+
948+
// Authorizing the fee manager as the provider enables withdrawing and setting fees.
949+
vm.prank(provider1);
950+
random.setFeeManager(address(manager));
951+
952+
// Withdrawing decrements provider's accrued fees and sends balance to the fee manager.
953+
uint startingBalance = manager.balance;
954+
vm.prank(manager);
955+
random.withdrawAsFeeManager(provider1, provider1FeeInWei);
956+
assertEq(random.getProviderInfo(provider1).accruedFeesInWei, 0);
957+
assertEq(manager.balance, startingBalance + provider1FeeInWei);
958+
959+
// Setting provider fee updates the fee in the ProviderInfo.
960+
vm.prank(manager);
961+
random.setProviderFeeAsFeeManager(provider1, 10101);
962+
assertEq(random.getProviderInfo(provider1).feeInWei, 10101);
963+
964+
// Authorizing a different manager depermissions the previous one.
965+
address manager2 = address(13);
966+
vm.prank(provider1);
967+
random.setFeeManager(address(manager2));
968+
vm.prank(manager);
969+
vm.expectRevert();
970+
random.withdrawAsFeeManager(provider1, provider1FeeInWei);
971+
vm.prank(manager);
972+
vm.expectRevert();
973+
random.setProviderFeeAsFeeManager(provider1, 1000);
974+
}
929975
}
930976

931977
contract EntropyConsumer is IEntropyConsumer {

target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,16 @@ interface EntropyEvents {
3232
event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);
3333

3434
event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri);
35+
36+
event ProviderFeeManagerUpdated(
37+
address provider,
38+
address oldFeeManager,
39+
address newFeeManager
40+
);
41+
42+
event Withdrawal(
43+
address provider,
44+
address recipient,
45+
uint128 withdrawnAmount
46+
);
3547
}

target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ contract EntropyStructs {
3232
// are revealed on-chain.
3333
bytes32 currentCommitment;
3434
uint64 currentCommitmentSequenceNumber;
35+
// An address that is authorized to set / withdraw fees on behalf of this provider.
36+
address feeManager;
3537
}
3638

3739
struct Request {

target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ interface IEntropy is EntropyEvents {
2222
// balance of fees in the contract).
2323
function withdraw(uint128 amount) external;
2424

25+
// Withdraw a portion of the accumulated fees for provider. The msg.sender must be the fee manager for this provider.
26+
// Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
27+
// balance of fees in the contract).
28+
function withdrawAsFeeManager(address provider, uint128 amount) external;
29+
2530
// As a user, request a random number from `provider`. Prior to calling this method, the user should
2631
// generate a random number x and keep it secret. The user should then compute hash(x) and pass that
2732
// as the userCommitment argument. (You may call the constructUserCommitment method to compute the hash.)
@@ -102,8 +107,19 @@ interface IEntropy is EntropyEvents {
102107

103108
function setProviderFee(uint128 newFeeInWei) external;
104109

110+
function setProviderFeeAsFeeManager(
111+
address provider,
112+
uint128 newFeeInWei
113+
) external;
114+
105115
function setProviderUri(bytes calldata newUri) external;
106116

117+
// Set manager as the fee manager for the provider msg.sender.
118+
// After calling this function, manager will be able to set the provider's fees and withdraw them.
119+
// Only one address can be the fee manager for a provider at a time -- calling this function again with a new value
120+
// will override the previous value. Call this function with the all-zero address to disable the fee manager role.
121+
function setFeeManager(address manager) external;
122+
107123
function constructUserCommitment(
108124
bytes32 userRandomness
109125
) external pure returns (bytes32 userCommitment);

target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
11
[
2+
{
3+
"anonymous": false,
4+
"inputs": [
5+
{
6+
"indexed": false,
7+
"internalType": "address",
8+
"name": "provider",
9+
"type": "address"
10+
},
11+
{
12+
"indexed": false,
13+
"internalType": "address",
14+
"name": "oldFeeManager",
15+
"type": "address"
16+
},
17+
{
18+
"indexed": false,
19+
"internalType": "address",
20+
"name": "newFeeManager",
21+
"type": "address"
22+
}
23+
],
24+
"name": "ProviderFeeManagerUpdated",
25+
"type": "event"
26+
},
227
{
328
"anonymous": false,
429
"inputs": [
@@ -103,6 +128,11 @@
103128
"internalType": "uint64",
104129
"name": "currentCommitmentSequenceNumber",
105130
"type": "uint64"
131+
},
132+
{
133+
"internalType": "address",
134+
"name": "feeManager",
135+
"type": "address"
106136
}
107137
],
108138
"indexed": false,
@@ -399,5 +429,30 @@
399429
],
400430
"name": "RevealedWithCallback",
401431
"type": "event"
432+
},
433+
{
434+
"anonymous": false,
435+
"inputs": [
436+
{
437+
"indexed": false,
438+
"internalType": "address",
439+
"name": "provider",
440+
"type": "address"
441+
},
442+
{
443+
"indexed": false,
444+
"internalType": "address",
445+
"name": "recipient",
446+
"type": "address"
447+
},
448+
{
449+
"indexed": false,
450+
"internalType": "uint128",
451+
"name": "withdrawnAmount",
452+
"type": "uint128"
453+
}
454+
],
455+
"name": "Withdrawal",
456+
"type": "event"
402457
}
403458
]

0 commit comments

Comments
 (0)