Skip to content

Commit b29e11e

Browse files
Quantstamp finding updates (#90)
* Simplify tests * SEQ-3 Add 721 owner to RequireUtils * Add bal/allow require functions * RequireUtils update terminology * S-3 Update README re delegatecall context * S-4 Remove unchecked * S-4 Unused deps * Fix comment typo on require utils --------- Co-authored-by: agusx1211 <aaguilar@polygon.technology>
1 parent fe405fa commit b29e11e

File tree

5 files changed

+481
-135
lines changed

5 files changed

+481
-135
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,17 @@ onlyFallback: false
9292

9393
All functions are able to be used from the `TrailsUtils` context via a call from the Intent Address via a `delegatecall`.
9494

95-
Delegatecall via the Intent Address is only able to access `hydrateExecute`. This is due to the Sequence Wallet wrapping of delegatecalls within `handleSequenceDelegateCall`.
95+
`TrailsUtils` supports the `handleSequenceDelegateCall` interface, allowing an Intent to interact within it's own context. This interface is required to support the Sequence Wallet wrapping of delegatecalls. This pattern exposes all functions on `TrailsUtils`.
9696

97-
Delegatecalls are only allowed in a nested delegatecall context. The `TrailsUtils` will not process a delegatecall when executing from the context of it's own address.
97+
Delegatecalls from `TrailsUtils` via the `HydrateProxy` execution logic, are only allowed in a nested delegatecall context. The `TrailsUtils` will not process a delegatecall when executing from the context of it's own address.
9898

9999
Any funds accumulated in the `TrailsUtils` context, via a `call` should be swept during batched execution. Remaining funds are to be considered lost.
100100

101101
### Intent Security
102102

103103
The Trails contracts are flexible in what they allow a configuration to represent. Misuse can cause an Intent to be exploitable.
104104

105-
`RequireUtils`, `Hydrate` and `MalleableSapient` are tools to help create functionally complete and secure Intent configurations. The actual creation of the configuration must be done with care and is out of scope of this repository.
105+
`RequireUtils`, `HydrateProxy` and `MalleableSapient` are tools to help create functionally complete and secure Intent configurations. The actual creation of the configuration must be done with care and is out of scope of this repository.
106106

107107
### Token Handling
108108

src/modules/HydrateProxy.sol

Lines changed: 53 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ pragma solidity ^0.8.27;
44
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
55
import {Payload} from "wallet-contracts-v3/modules/Payload.sol";
66
import {Sweepable} from "src/modules/Sweepable.sol";
7-
import {CalldataDecode} from "src/utils/CalldataDecode.sol";
87
import {ReplaceBytes} from "src/utils/ReplaceBytes.sol";
98
import {LibBytes} from "wallet-contracts-v3/utils/LibBytes.sol";
109
import {IDelegatedExtension} from "wallet-contracts-v3/modules/interfaces/IDelegatedExtension.sol";
@@ -19,7 +18,6 @@ import {LibOptim} from "wallet-contracts-v3/utils/LibOptim.sol";
1918
contract HydrateProxy is Sweepable, IDelegatedExtension {
2019
using LibBytes for bytes;
2120
using ReplaceBytes for bytes;
22-
using CalldataDecode for bytes;
2321

2422
/// @notice An unknown hydration type flag is encountered.
2523
error UnknownHydrateTypeCommand(uint256 flag);
@@ -101,79 +99,75 @@ contract HydrateProxy is Sweepable, IDelegatedExtension {
10199
}
102100

103101
function _hydrateExecute(bytes calldata packedPayload, bytes calldata hydratePayload) internal {
104-
unchecked {
105-
// Decode + hash the payload, then apply hydration and execute each call sequentially.
106-
Payload.Decoded memory decoded = Payload.fromPackedCalls(packedPayload);
107-
bytes32 opHash = Payload.hash(decoded);
108-
bytes32 hydratableOpHash = LibOptim.fkeccak256(opHash, keccak256(hydratePayload));
109-
110-
(uint256 rindex, uint256 tindex) = _firstHydrateCall(hydratePayload);
111-
bool errorFlag = false;
112-
113-
uint256 numCalls = decoded.calls.length;
114-
for (uint256 i = 0; i < numCalls; i++) {
115-
if (tindex == i) {
116-
(rindex, tindex) = _hydrate(decoded, hydratePayload, rindex, tindex);
117-
}
102+
// Decode + hash the payload, then apply hydration and execute each call sequentially.
103+
Payload.Decoded memory decoded = Payload.fromPackedCalls(packedPayload);
104+
bytes32 opHash = Payload.hash(decoded);
105+
bytes32 hydratableOpHash = LibOptim.fkeccak256(opHash, keccak256(hydratePayload));
106+
107+
(uint256 rindex, uint256 tindex) = _firstHydrateCall(hydratePayload);
108+
bool errorFlag = false;
109+
110+
uint256 numCalls = decoded.calls.length;
111+
for (uint256 i = 0; i < numCalls; i++) {
112+
if (tindex == i) {
113+
(rindex, tindex) = _hydrate(decoded, hydratePayload, rindex, tindex);
114+
}
118115

119-
Payload.Call memory call = decoded.calls[i];
116+
Payload.Call memory call = decoded.calls[i];
120117

121-
// Skip `onlyFallback` calls unless the immediately preceding call failed and was ignored.
122-
if (call.onlyFallback && !errorFlag) {
123-
emit Calls.CallSkipped(hydratableOpHash, i);
124-
continue;
125-
}
118+
// Skip `onlyFallback` calls unless the immediately preceding call failed and was ignored.
119+
if (call.onlyFallback && !errorFlag) {
120+
emit Calls.CallSkipped(hydratableOpHash, i);
121+
continue;
122+
}
126123

127-
// `onlyFallback` only inspects the immediately preceding call.
128-
errorFlag = false;
124+
// `onlyFallback` only inspects the immediately preceding call.
125+
errorFlag = false;
129126

130-
uint256 gasLimit = call.gasLimit;
131-
if (gasLimit != 0 && gasleft() < gasLimit) {
132-
revert Calls.NotEnoughGas(decoded, i, gasleft());
133-
}
127+
uint256 gasLimit = call.gasLimit;
128+
if (gasLimit != 0 && gasleft() < gasLimit) {
129+
revert Calls.NotEnoughGas(decoded, i, gasleft());
130+
}
134131

135-
if (call.delegateCall && address(this) == SELF) {
136-
// Delegatecall is not allowed from this contract context.
137-
revert DelegateCallNotAllowed(i);
138-
}
132+
if (call.delegateCall && address(this) == SELF) {
133+
// Delegatecall is not allowed from this contract context.
134+
revert DelegateCallNotAllowed(i);
135+
}
139136

140-
bool success;
141-
if (call.delegateCall) {
142-
(success) = LibOptim.delegatecall(call.to, gasLimit == 0 ? gasleft() : gasLimit, call.data);
143-
} else {
144-
(success) = LibOptim.call(call.to, call.value, gasLimit == 0 ? gasleft() : gasLimit, call.data);
137+
bool success;
138+
if (call.delegateCall) {
139+
(success) = LibOptim.delegatecall(call.to, gasLimit == 0 ? gasleft() : gasLimit, call.data);
140+
} else {
141+
(success) = LibOptim.call(call.to, call.value, gasLimit == 0 ? gasleft() : gasLimit, call.data);
142+
}
143+
144+
if (!success) {
145+
if (call.behaviorOnError == Payload.BEHAVIOR_IGNORE_ERROR) {
146+
errorFlag = true;
147+
emit Calls.CallFailed(hydratableOpHash, i, LibOptim.returnData());
148+
continue;
145149
}
146150

147-
if (!success) {
148-
if (call.behaviorOnError == Payload.BEHAVIOR_IGNORE_ERROR) {
149-
errorFlag = true;
150-
emit Calls.CallFailed(hydratableOpHash, i, LibOptim.returnData());
151-
continue;
152-
}
153-
154-
if (call.behaviorOnError == Payload.BEHAVIOR_REVERT_ON_ERROR) {
155-
revert Calls.Reverted(decoded, i, LibOptim.returnData());
156-
}
157-
158-
if (call.behaviorOnError == Payload.BEHAVIOR_ABORT_ON_ERROR) {
159-
emit Calls.CallAborted(hydratableOpHash, i, LibOptim.returnData());
160-
break;
161-
}
151+
if (call.behaviorOnError == Payload.BEHAVIOR_REVERT_ON_ERROR) {
152+
revert Calls.Reverted(decoded, i, LibOptim.returnData());
162153
}
163154

164-
emit Calls.CallSucceeded(hydratableOpHash, i);
155+
if (call.behaviorOnError == Payload.BEHAVIOR_ABORT_ON_ERROR) {
156+
emit Calls.CallAborted(hydratableOpHash, i, LibOptim.returnData());
157+
break;
158+
}
165159
}
160+
161+
emit Calls.CallSucceeded(hydratableOpHash, i);
166162
}
167163
}
168164

169165
function _firstHydrateCall(bytes calldata hydratePayload) internal pure returns (uint256 rindex, uint256 tindex) {
170-
unchecked {
171-
if (hydratePayload.length == 0) {
172-
return (0, type(uint256).max);
173-
}
174-
175-
return (1, uint256(uint8(hydratePayload[0])));
166+
if (hydratePayload.length == 0) {
167+
return (0, type(uint256).max);
176168
}
169+
170+
return (1, uint256(uint8(hydratePayload[0])));
177171
}
178172

179173
function _hydrate(Payload.Decoded memory decoded, bytes calldata hydratePayload, uint256 rindex, uint256 tindex)

0 commit comments

Comments
 (0)