Skip to content

Commit 96e291f

Browse files
Merge pull request #2915 from rainlanguage/fix-2910-arb4-bad-raindex
arb4 rejects an untrusted raindex with BadRaindex
2 parents 3e34fcf + 06a2f76 commit 96e291f

9 files changed

Lines changed: 62 additions & 141 deletions

‎.gas-snapshot‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ RaindexV6EnactTest:testRaindexEvalWriteStateDifferentOwnersNamespaced(address,ad
158158
RaindexV6EnactTest:testRaindexEvalWriteStateSequential() (gas: 556418)
159159
RaindexV6EnactTest:testRaindexEvalWriteStateSingle(address) (runs: 100, μ: 189480, ~: 189555)
160160
RaindexV6FlashBorrowerBadInitiatorTest:testOnFlashLoanBadInitiator(address) (runs: 5096, μ: 11005, ~: 11005)
161-
RaindexV6FlashBorrowerBadLenderApprovalTest:testBadLenderRevertsWithApproval() (gas: 4383922)
162161
RaindexV6FlashBorrowerFallbackTest:testFallbackAcceptsCalldata() (gas: 3149)
163162
RaindexV6FlashBorrowerFallbackTest:testFallbackAcceptsETHWithCalldata() (gas: 12730)
164163
RaindexV6FlashBorrowerFallbackTest:testFallbackAcceptsEmptyCalldata() (gas: 3113)

‎src/abstract/RaindexV6ArbCommon.sol‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ struct RaindexV6ArbConfig {
1212
/// Thrown when the task does not match the expected hash.
1313
error WrongTask();
1414

15+
/// Thrown when an arb is called with a `raindex` that is not the trusted
16+
/// deterministic deployment. The arb grants `raindex` an unlimited token
17+
/// approval and calls it, so an untrusted address could drain the contract's
18+
/// balance.
19+
/// @param badRaindex The untrusted `raindex` passed to the arb.
20+
error BadRaindex(address badRaindex);
21+
1522
/// @title RaindexV6ArbCommon
1623
/// @notice Common base for arb contracts that interact with `Raindex`.
1724
/// Provides a `_beforeArb` hook that is called at the start of every arb

‎src/abstract/RaindexV6ArbOrderTaker.sol‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,12 @@ import {IERC20, SafeERC20} from "@openzeppelin-contracts-5.7.0/token/ERC20/utils
88
import {IRaindexV6} from "raindex-interface-0.1.8/src/interface/IRaindexV6.sol";
99
import {IRaindexV6ArbOrderTaker, TaskV2} from "raindex-interface-0.1.8/src/interface/IRaindexV6ArbOrderTaker.sol";
1010
import {TakeOrdersConfigV5, Float} from "raindex-interface-0.1.8/src/interface/IRaindexV6.sol";
11-
import {RaindexV6ArbCommon} from "./RaindexV6ArbCommon.sol";
11+
import {RaindexV6ArbCommon, BadRaindex} from "./RaindexV6ArbCommon.sol";
1212
import {LibRaindexArb} from "../lib/LibRaindexArb.sol";
1313
import {LibRaindexDeploy} from "../lib/deploy/LibRaindexDeploy.sol";
1414
import {IRaindexV6OrderTaker} from "raindex-interface-0.1.8/src/interface/IRaindexV6OrderTaker.sol";
1515
import {LibTOFUTokenDecimals} from "rain-tofu-erc20-decimals-0.1.4/src/lib/LibTOFUTokenDecimals.sol";
1616

17-
/// Thrown when `arb5` is called with a `raindex` that is not the trusted
18-
/// deterministic deployment. `arb5` grants `raindex` an unlimited token approval
19-
/// and calls it, so an untrusted address could drain the contract's balance.
20-
error BadRaindex(address badRaindex);
21-
2217
/// Thrown when `onTakeOrders2` is called during an arb by anything other than
2318
/// the trusted raindex.
2419
error UnexpectedOnTakeOrdersCaller(address caller);

‎src/abstract/RaindexV6FlashBorrower.sol‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {ReentrancyGuard} from "@openzeppelin-contracts-5.7.0/utils/ReentrancyGua
99
import {ON_FLASH_LOAN_CALLBACK_SUCCESS} from "raindex-interface-0.1.8/src/interface/ierc3156/IERC3156FlashBorrower.sol";
1010
import {IRaindexV6, TakeOrdersConfigV5, TaskV2} from "raindex-interface-0.1.8/src/interface/IRaindexV6.sol";
1111
import {IERC3156FlashBorrower} from "raindex-interface-0.1.8/src/interface/ierc3156/IERC3156FlashBorrower.sol";
12-
import {RaindexV6ArbCommon} from "./RaindexV6ArbCommon.sol";
12+
import {RaindexV6ArbCommon, BadRaindex} from "./RaindexV6ArbCommon.sol";
1313
import {LibRaindexArb} from "../lib/LibRaindexArb.sol";
1414
import {LibRaindexDeploy} from "../lib/deploy/LibRaindexDeploy.sol";
1515
import {LibTOFUTokenDecimals} from "rain-tofu-erc20-decimals-0.1.4/src/lib/LibTOFUTokenDecimals.sol";
@@ -123,7 +123,8 @@ abstract contract RaindexV6FlashBorrower is IERC3156FlashBorrower, ReentrancyGua
123123
/// remaining assets are sent to the sender via `finalizeArb`.
124124
///
125125
/// @param raindex The `Raindex` contract to take orders from and flash
126-
/// loan against.
126+
/// loan against. MUST be the deterministic deployment, else reverts
127+
/// `BadRaindex`.
127128
/// @param takeOrders As per `IRaindexV6.takeOrders4`.
128129
/// @param exchangeData Arbitrary bytes that will be passed to `_exchange`
129130
/// after the flash loan is taken. The inheriting contract is responsible
@@ -142,6 +143,9 @@ abstract contract RaindexV6FlashBorrower is IERC3156FlashBorrower, ReentrancyGua
142143
if (takeOrders.orders.length == 0) {
143144
revert IRaindexV6.NoOrders();
144145
}
146+
if (address(raindex) != LibRaindexDeploy.RAINDEX_DEPLOYED_ADDRESS) {
147+
revert BadRaindex(address(raindex));
148+
}
145149
// The flash-loan amount below reads `minimumIO` as the order output the
146150
// taker receives, which only matches when `minimumIO` is the taker
147151
// input (`IOIsInput == true`).

‎src/generated/candidate/GenericPoolRaindexV6FlashBorrower.sol‎

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

‎test/abstract/RaindexV6ArbOrderTaker.badRaindex.t.sol‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pragma solidity =0.8.25;
55
import {GenericPoolRaindexV6ArbOrderTakerTest} from "test/util/abstract/GenericPoolRaindexV6ArbOrderTakerTest.sol";
66

77
import {GenericPoolRaindexV6ArbOrderTaker} from "../../src/concrete/arb/GenericPoolRaindexV6ArbOrderTaker.sol";
8-
import {BadRaindex} from "../../src/abstract/RaindexV6ArbOrderTaker.sol";
8+
import {BadRaindex} from "../../src/abstract/RaindexV6ArbCommon.sol";
99
import {LibRaindexDeploy} from "../../src/lib/deploy/LibRaindexDeploy.sol";
1010
import {
1111
IRaindexV6,

‎test/abstract/RaindexV6FlashBorrower.badLenderApproval.t.sol‎

Lines changed: 0 additions & 86 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: LicenseRef-DCL-1.0
2+
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
3+
pragma solidity =0.8.25;
4+
5+
import {Test} from "forge-std-1.17.0/src/Test.sol";
6+
import {IERC3156FlashLender} from "raindex-interface-0.1.8/src/interface/ierc3156/IERC3156FlashLender.sol";
7+
import {IRaindexV6} from "raindex-interface-0.1.8/src/interface/IRaindexV6.sol";
8+
import {BadRaindex} from "../../src/abstract/RaindexV6ArbCommon.sol";
9+
import {LibRaindexDeploy} from "../../src/lib/deploy/LibRaindexDeploy.sol";
10+
import {LibTestFlashBorrowerArb, FlashBorrowerSetup} from "test/util/lib/LibTestFlashBorrowerArb.sol";
11+
import {LibTestArb} from "test/util/lib/LibTestArb.sol";
12+
import {MockExchange} from "test/util/concrete/MockExchange.sol";
13+
14+
contract RaindexV6FlashBorrowerBadRaindexTest is Test {
15+
/// arb4 reverts BadRaindex for any raindex other than the deterministic
16+
/// deployment, even a funded working lender, before calling it.
17+
function testArb4RevertsUntrustedRaindex(address badRaindex) external {
18+
vm.assume(badRaindex != LibRaindexDeploy.RAINDEX_DEPLOYED_ADDRESS);
19+
vm.assume(badRaindex != address(0));
20+
assumeNotPrecompile(badRaindex);
21+
assumeNotForgeAddress(badRaindex);
22+
23+
MockExchange exchange = new MockExchange();
24+
FlashBorrowerSetup memory setup = LibTestFlashBorrowerArb.setup(vm, address(exchange), 100e18);
25+
vm.assume(badRaindex.code.length == 0);
26+
vm.assume(badRaindex != address(setup.arb));
27+
vm.etch(badRaindex, LibRaindexDeploy.RAINDEX_DEPLOYED_ADDRESS.code);
28+
setup.outputToken.mint(badRaindex, 1000e18);
29+
30+
vm.expectCall(badRaindex, abi.encodeWithSelector(IERC3156FlashLender.flashLoan.selector), 0);
31+
vm.expectRevert(abi.encodeWithSelector(BadRaindex.selector, badRaindex));
32+
setup.arb.arb4(IRaindexV6(badRaindex), setup.takeOrdersConfig, setup.exchangeData, LibTestArb.noopTask());
33+
}
34+
35+
/// arb4 reverts BadRaindex for the zero address.
36+
function testArb4RevertsZeroRaindex() external {
37+
MockExchange exchange = new MockExchange();
38+
FlashBorrowerSetup memory setup = LibTestFlashBorrowerArb.setup(vm, address(exchange), 100e18);
39+
40+
vm.expectRevert(abi.encodeWithSelector(BadRaindex.selector, address(0)));
41+
setup.arb.arb4(IRaindexV6(address(0)), setup.takeOrdersConfig, setup.exchangeData, LibTestArb.noopTask());
42+
}
43+
}

‎test/util/concrete/MaliciousRaindex.sol‎

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)