Description
-
A public interface should faithfully describe the concrete contract’s external ABI so integrators can rely on function names, parameter lists, and return types. Calls made through the interface must successfully hit existing selectors on the implementation.
-
IStratax no longer matches Stratax:
-
The interface exposes calculateParams(TradeDetails) while the implementation only provides calculateOpenParams(TradeDetails) (different name/selector).
-
The interface declares unwindPosition(address,address,uint256,bytes,uint256) (5 params), but the implementation requires 6 params including collateralToWithdraw.
-
The interface declares getters like BASIS_POINTS() that do not exist; the implementation defines constants such as FLASHLOAN_FEE_PREC instead.
As a result, interface-based calls compile but revert at runtime because the function selectors are missing on Stratax.
function calculateParams(TradeDetails memory details)
external view returns (uint256 flashLoanAmount, uint256 borrowAmount);
function unwindPosition(
address _collateralToken,
address _debtToken,
uint256 _debtAmount,
bytes calldata _oneInchSwapData,
uint256 _minReturnAmount
) external;
function BASIS_POINTS() external view returns (uint256);
function calculateOpenParams(TradeDetails memory details)
public view returns (uint256 flashLoanAmount, uint256 borrowAmount);
function unwindPosition(
address _collateralToken,
uint256 _collateralToWithdraw,
address _debtToken,
uint256 _debtAmount,
bytes calldata _oneInchSwapData,
uint256 _minReturnAmount
) external onlyOwner;
uint256 public constant FLASHLOAN_FEE_PREC = 10000;
Risk
Likelihood: High
-
Integrators commonly code against interfaces; whenever they call calculateParams or the 5‑arg unwindPosition through IStratax, the call will happen during integration or testing and revert due to missing selectors.
-
The Stratax test bundle includes both interface and implementation files, increasing the chance someone imports the interface in new tests/scripts and hits this drift.
Impact: High
-
Immediate integration breakage: On-chain calls through IStratax revert at runtime, halting workflows (e.g., position management UIs, automation bots).
-
Ecosystem confusion / maintenance burden: Conflicting ABIs complicate audits, SDKs, and partner integrations; downstream users may ship incorrect calldata or assume unsupported behaviors.
Proof of Concept
function test_InterfaceDrift_missingConstantSelector_fails() public view {
bytes4 sel = bytes4(keccak256("BASIS_POINTS()"));
(bool ok, ) = address(stratax).staticcall(abi.encodeWithSelector(sel));
assertTrue(!ok, "BASIS_POINTS() unexpectedly succeeded");
}
function test_InterfaceDrift_unwindSignatureMismatch_fails() public {
bytes4 sel = bytes4(
keccak256("unwindPosition(address,address,uint256,bytes,uint256)")
);
address collateral = USDC;
address debtToken = WETH;
uint256 debtAmount = 0;
bytes memory swapData = hex"";
uint256 minReturn = 0;
(bool ok, ) = address(stratax).call(
abi.encodeWithSelector(sel, collateral, debtToken, debtAmount, swapData, minReturn)
);
assertTrue(!ok, "5-arg unwindPosition unexpectedly succeeded");
}
function test_InterfaceDrift_calculatePararams_fails() public view {
bytes4 sel = bytes4(keccak256("calculateParams((address,address,uint256,uint256,uint256,uint256,uint8,uint8))"));
(bool ok, ) = address(stratax).staticcall(abi.encodeWithSelector(sel, Stratax.TradeDetails({
collateralToken: USDC,
borrowToken: WETH,
desiredLeverage: 30_000,
collateralAmount: 1_000 * 1e6,
collateralTokenPrice: 0,
borrowTokenPrice: 0,
collateralTokenDec: 6,
borrowTokenDec: 18
})));
assertTrue(!ok, "calculateParams unexpectedly succeeded");
}
function test_InterfaceDrift_reference_calculateOpenParams_works() public view {
(uint256 flashLoanAmount, uint256 borrowAmount) = stratax.calculateOpenParams(
Stratax.TradeDetails({
collateralToken: USDC,
borrowToken: WETH,
desiredLeverage: 30_000,
collateralAmount: 1_000 * 1e6,
collateralTokenPrice: 0,
borrowTokenPrice: 0,
collateralTokenDec: 6,
borrowTokenDec: 18
})
);
assertTrue(flashLoanAmount > 0, "flashLoanAmount should be > 0");
assertTrue(borrowAmount > 0, "borrowAmount should be > 0");
}
Output:
[⠒] Compiling...
No files changed, compilation skipped
Ran 8 tests for test/fork/Stratax.t.sol:StrataxForkTest
[PASS] test_Example_SwapWithRealData() (gas: 1212169)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[SKIP] test_FFI_Get1inchSwapData() (gas: 0)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[PASS] test_InterfaceDrift_calculatePararams_fails() (gas: 18868)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[PASS] test_InterfaceDrift_missingConstantSelector_fails() (gas: 17566)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[PASS] test_InterfaceDrift_reference_calculateOpenParams_works() (gas: 100902)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[PASS] test_InterfaceDrift_unwindSignatureMismatch_fails() (gas: 18611)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
[PASS] test_OpenAndUnwindPosition() (gas: 2269015)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
Unwind: calculating params
Unwind: get 1inch data
Unwind: calling stratax to unwind position
[PASS] test_USDCTokenExists() (gas: 16408)
Logs:
Available swap data files: 3
Randomly selected block: 24329390
Current fork block number is: 24329390
Suite result: ok. 7 passed; 0 failed; 1 skipped; finished in 831.29ms (89.61ms CPU time)
Ran 1 test suite in 838.50ms (831.29ms CPU time): 7 tests passed, 0 failed, 1 skipped (8 total tests)
Recommended Mitigation
- interface IStratax {
- struct TradeDetails {
- uint256 ltv;
- uint256 desiredLeverage;
- uint256 collateralAmount;
- uint256 collateralTokenPrice;
- uint256 borrowTokenPrice;
- uint256 collateralTokenDec;
- uint256 borrowTokenDec;
- }
-
- function calculateParams(TradeDetails memory details)
- external view returns (uint256 flashLoanAmount, uint256 borrowAmount);
-
- function unwindPosition(
- address _collateralToken,
- address _debtToken,
- uint256 _debtAmount,
- bytes calldata _oneInchSwapData,
- uint256 _minReturnAmount
- ) external;
-
- function BASIS_POINTS() external view returns (uint256);
- }
+ interface IStratax {
+ struct TradeDetails {
+ address collateralToken;
+ address borrowToken;
+ uint256 desiredLeverage;
+ uint256 collateralAmount;
+ uint256 collateralTokenPrice;
+ uint256 borrowTokenPrice;
+ uint256 collateralTokenDec;
+ uint256 borrowTokenDec;
+ }
+
+ function calculateOpenParams(TradeDetails memory details)
+ external view returns (uint256 flashLoanAmount, uint256 borrowAmount);
+
+ function unwindPosition(
+ address _collateralToken,
+ uint256 _collateralToWithdraw,
+ address _debtToken,
+ uint256 _debtAmount,
+ bytes calldata _oneInchSwapData,
+ uint256 _minReturnAmount
+ ) external;
+ // remove nonexistent getters like BASIS_POINTS()
+ }