Skip to content

Commit 1ae5459

Browse files
authored
Sip10 delegate approvals audit remediations (#477)
1 parent 9caca24 commit 1ae5459

File tree

3 files changed

+113
-18
lines changed

3 files changed

+113
-18
lines changed

contracts/DelegateApprovals.sol

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ contract DelegateApprovals is Owned {
1212
bytes32 public constant EXCHANGE_FOR_ADDRESS = "ExchangeForAddress";
1313
bytes32 public constant APPROVE_ALL = "ApproveAll";
1414

15+
bytes32[5] private _delegatableFunctions = [
16+
APPROVE_ALL,
17+
BURN_FOR_ADDRESS,
18+
ISSUE_FOR_ADDRESS,
19+
CLAIM_FOR_ADDRESS,
20+
EXCHANGE_FOR_ADDRESS
21+
];
22+
1523
/* ========== STATE VARIABLES ========== */
1624
EternalStorage public eternalStorage;
1725

@@ -70,8 +78,11 @@ contract DelegateApprovals is Owned {
7078
_setApproval(APPROVE_ALL, msg.sender, delegate);
7179
}
7280

81+
// Removes all delegate approvals
7382
function removeAllDelegatePowers(address delegate) external {
74-
_withdrawApproval(APPROVE_ALL, msg.sender, delegate);
83+
for (uint i = 0; i < _delegatableFunctions.length; i++) {
84+
_withdrawApproval(_delegatableFunctions[i], msg.sender, delegate);
85+
}
7586
}
7687

7788
// Burn on behalf
@@ -117,11 +128,15 @@ contract DelegateApprovals is Owned {
117128
}
118129

119130
function _withdrawApproval(bytes32 action, address authoriser, address delegate) internal {
120-
eternalStorage.deleteBooleanValue(_getKey(action, authoriser, delegate));
121-
emit WithdrawApproval(authoriser, delegate, action);
131+
// Check approval is set otherwise skip deleting approval
132+
if (eternalStorage.getBooleanValue(_getKey(action, authoriser, delegate))) {
133+
eternalStorage.deleteBooleanValue(_getKey(action, authoriser, delegate));
134+
emit WithdrawApproval(authoriser, delegate, action);
135+
}
122136
}
123137

124138
function setEternalStorage(EternalStorage _eternalStorage) external onlyOwner {
139+
require(_eternalStorage != address(0), "Can't set eternalStorage to address(0)");
125140
eternalStorage = _eternalStorage;
126141
emit EternalStorageUpdated(eternalStorage);
127142
}

contracts/interfaces/IDelegateApprovals.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ pragma solidity 0.4.25;
22

33

44
interface IDelegateApprovals {
5-
function canBurnFor(address owner, address delegate) external view returns (bool);
5+
function canBurnFor(address authoriser, address delegate) external view returns (bool);
66

7-
function canIssueFor(address owner, address delegate) external view returns (bool);
7+
function canIssueFor(address authoriser, address delegate) external view returns (bool);
88

9-
function canClaimFor(address owner, address delegate) external view returns (bool);
9+
function canClaimFor(address authoriser, address delegate) external view returns (bool);
1010

11-
function canExchangeFor(address owner, address delegate) external view returns (bool);
11+
function canExchangeFor(address authoriser, address delegate) external view returns (bool);
1212
}

test/contracts/DelegateApprovals.js

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
ensureOnlyExpectedMutativeFunctions,
55
} = require('../utils/setupUtils');
66
const { toBytes32 } = require('../../.');
7+
const { ZERO_ADDRESS } = require('../utils/testUtils');
78

89
require('.'); // import common test scaffolding
910

@@ -46,6 +47,14 @@ contract('DelegateApprovals', async accounts => {
4647
newEternalStorage: account1,
4748
});
4849
});
50+
it('reverts if set to ZERO_ADDRESS', async () => {
51+
await assert.revert(
52+
delegateApprovals.setEternalStorage(ZERO_ADDRESS, {
53+
from: owner,
54+
}),
55+
"Can't set eternalStorage to address(0)"
56+
);
57+
});
4958
});
5059

5160
it('ensure only known functions are mutative', () => {
@@ -89,27 +98,26 @@ contract('DelegateApprovals', async accounts => {
8998
assert.isTrue(result);
9099

91100
// remove approval
92-
await delegateApprovals.removeAllDelegatePowers(delegate, { from: authoriser });
93-
const newResult = await delegateApprovals.canBurnFor(authoriser, delegate);
94-
assert.isNotTrue(newResult);
95-
});
96-
it('should add approval and emit an Approval event', async () => {
97-
const transaction = await delegateApprovals.approveAllDelegatePowers(delegate, {
101+
const transaction = await delegateApprovals.removeAllDelegatePowers(delegate, {
98102
from: authoriser,
99103
});
100104

101-
assert.eventEqual(transaction, 'Approval', {
105+
// only WithdrawApproval event emitted for ApproveAll
106+
assert.eventEqual(transaction, 'WithdrawApproval', {
102107
authoriser: account1,
103108
delegate: account2,
104109
action: toBytes32('ApproveAll'),
105110
});
111+
112+
const newResult = await delegateApprovals.canBurnFor(authoriser, delegate);
113+
assert.isNotTrue(newResult);
106114
});
107-
it('should withdraw approval and emit an WithdrawApproval event', async () => {
108-
const transaction = await delegateApprovals.removeAllDelegatePowers(delegate, {
115+
it('should add approval and emit an Approval event', async () => {
116+
const transaction = await delegateApprovals.approveAllDelegatePowers(delegate, {
109117
from: authoriser,
110118
});
111119

112-
assert.eventEqual(transaction, 'WithdrawApproval', {
120+
assert.eventEqual(transaction, 'Approval', {
113121
authoriser: account1,
114122
delegate: account2,
115123
action: toBytes32('ApproveAll'),
@@ -141,9 +149,81 @@ contract('DelegateApprovals', async accounts => {
141149
assert.isTrue(result);
142150

143151
// remove approval
144-
await delegateApprovals.removeExchangeOnBehalf(delegate, { from: authoriser });
152+
const transaction = await delegateApprovals.removeExchangeOnBehalf(delegate, {
153+
from: authoriser,
154+
});
155+
156+
assert.eventEqual(transaction, 'WithdrawApproval', {
157+
authoriser: account1,
158+
delegate: account2,
159+
action: toBytes32('ExchangeForAddress'),
160+
});
161+
145162
const newResult = await delegateApprovals.canExchangeFor(authoriser, delegate);
146163
assert.isNotTrue(newResult);
147164
});
148165
});
166+
167+
describe('when invoking removeAllDelegatePowers', async () => {
168+
const authoriser = account1;
169+
const delegate = account2;
170+
171+
beforeEach(async () => {
172+
await delegateApprovals.approveExchangeOnBehalf(delegate, { from: authoriser });
173+
await delegateApprovals.approveIssueOnBehalf(delegate, { from: authoriser });
174+
await delegateApprovals.approveBurnOnBehalf(delegate, { from: authoriser });
175+
await delegateApprovals.approveClaimOnBehalf(delegate, { from: authoriser });
176+
});
177+
178+
it('should remove all delegate powers that have been set', async () => {
179+
// check approvals is all true
180+
assert.isTrue(await delegateApprovals.canExchangeFor(authoriser, delegate));
181+
assert.isTrue(await delegateApprovals.canIssueFor(authoriser, delegate));
182+
assert.isTrue(await delegateApprovals.canBurnFor(authoriser, delegate));
183+
assert.isTrue(await delegateApprovals.canClaimFor(authoriser, delegate));
184+
185+
// invoke removeAllDelegatePowers
186+
await await delegateApprovals.removeAllDelegatePowers(delegate, { from: authoriser });
187+
188+
// each delegations revoked
189+
assert.isNotTrue(await delegateApprovals.canExchangeFor(authoriser, delegate));
190+
assert.isNotTrue(await delegateApprovals.canIssueFor(authoriser, delegate));
191+
assert.isNotTrue(await delegateApprovals.canBurnFor(authoriser, delegate));
192+
assert.isNotTrue(await delegateApprovals.canClaimFor(authoriser, delegate));
193+
});
194+
195+
it('should withdraw approval and emit an WithdrawApproval event for each withdrawn delegation', async () => {
196+
const transaction = await delegateApprovals.removeAllDelegatePowers(delegate, {
197+
from: authoriser,
198+
});
199+
200+
assert.eventsEqual(
201+
transaction,
202+
'WithdrawApproval',
203+
{
204+
authoriser: account1,
205+
delegate: account2,
206+
action: toBytes32('BurnForAddress'),
207+
},
208+
'WithdrawApproval',
209+
{
210+
authoriser: account1,
211+
delegate: account2,
212+
action: toBytes32('IssueForAddress'),
213+
},
214+
'WithdrawApproval',
215+
{
216+
authoriser: account1,
217+
delegate: account2,
218+
action: toBytes32('ClaimForAddress'),
219+
},
220+
'WithdrawApproval',
221+
{
222+
authoriser: account1,
223+
delegate: account2,
224+
action: toBytes32('ExchangeForAddress'),
225+
}
226+
);
227+
});
228+
});
149229
});

0 commit comments

Comments
 (0)