Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ $ forge build
### Test

```shell
$ forge test
$ forge test --evm-version cancun
```

### Format
Expand Down
81 changes: 49 additions & 32 deletions contracts/GearboxDCA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ pragma solidity ^0.8.13;

import {IContractsRegister} from "@gearbox-protocol/core-v2/contracts/interfaces/IContractsRegister.sol";
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";
import {MultiCallOps} from "@gearbox-protocol/core-v2/contracts/libraries/MultiCall.sol";
import {ICreditFacadeV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditFacadeV3.sol";
import {ICreditFacadeV3Multicall} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditFacadeV3Multicall.sol";
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";

import {IPriceOracleV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPriceOracleV3.sol";
import {BalanceDelta} from "@gearbox-protocol/core-v3/contracts/libraries/BalancesLogic.sol";
import {IRouterV3, RouterResult} from "@gearbox-protocol/liquidator-v2-contracts/contracts/interfaces/IRouterV3.sol";

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand All @@ -29,6 +32,7 @@ import "contracts/interfaces/IGearboxDCAException.sol";
contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent {
using LibFormatter for uint256;
using SafeCast for uint256;
using MultiCallOps for MultiCall[];

uint32 public constant MAX_PARTITION = 1_000_000;

Expand All @@ -40,18 +44,26 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent

IContractsRegister private _contractsRegister;

IRouterV3 private _router;

mapping(bytes32 => OrderStatus) internal _orderStatuses;

address[] private _connectors;

constructor(
string memory name,
string memory version,
address priceOracle,
address contractsRegister
address contractsRegister,
address router,
address[] memory connectors
)
EIP712(name, version)
{
_priceOracle = IPriceOracleV3(priceOracle);
_contractsRegister = IContractsRegister(contractsRegister);
_router = IRouterV3(router);
_connectors = connectors;
}

//
Expand Down Expand Up @@ -90,13 +102,9 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent
/// @notice Execute the order
/// @param order The order to execute
/// @param signature The signature of the order
/// @param adapter The address of the adapter to use
/// @param adapterCallData The call data of the adapter
function executeOrder(
Order calldata order,
bytes calldata signature,
address adapter,
bytes calldata adapterCallData
bytes calldata signature
)
external
override
Expand All @@ -106,7 +114,7 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent
{
_verifySigner(order, signature);

_execute(order, adapter, adapterCallData);
_execute(order);
}

/// @notice Cancel the order
Expand Down Expand Up @@ -135,7 +143,7 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent

/// @notice Get the order status
/// @param orderHash The order hash to get the status
function getOrderStatus(bytes32 orderHash) external view returns (OrderStatus memory) {
function getOrderStatus(bytes32 orderHash) external view override returns (OrderStatus memory) {
return _orderStatuses[orderHash];
}

Expand Down Expand Up @@ -242,15 +250,7 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent
return deltas;
}

function _genCalls(
Order calldata order,
address adapter,
bytes calldata adapterCallData
)
internal
view
returns (MultiCall[] memory)
{
function _genCollateralCalls(Order calldata order) internal view returns (MultiCall[] memory) {
address creditFacadeAddress = ICreditManagerV3(order.creditManager).creditFacade();
address collateral = order.collateral;
address tokenIn = order.tokenIn;
Expand All @@ -263,9 +263,9 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent
MultiCall[] memory calls;

if (isNonTokenInOrOutCollateral) {
calls = new MultiCall[](7);
calls = new MultiCall[](5);
} else {
calls = new MultiCall[](6);
calls = new MultiCall[](4);
}

calls[0] = MultiCall({
Expand All @@ -283,23 +283,21 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent
callData: abi.encodeCall(ICreditFacadeV3Multicall.increaseDebt, (amountIn))
});

calls[3] = MultiCall({target: adapter, callData: adapterCallData});

if (isNonTokenInOrOutCollateral) {
uint256 tokenOutMinAmount = _calcTokenOutMinAmount(order);
int96 quotaForCollateral = _calcQuotaForQuotedToken(collateral, tokenIn, collateralAmount);
int96 quotaForTokenOut = _calcQuotaForQuotedToken(tokenOut, tokenIn, tokenOutMinAmount);

/// can be enhanced to this formula (muliplied by LT):
/// https://github.com/Gearbox-protocol/sdk/blob/d7dda524d049a3c68e31e44a8eed3fecc288b52d/src/core/creditAccount.ts#L564
calls[4] = MultiCall({
calls[3] = MultiCall({
target: creditFacadeAddress,
callData: abi.encodeCall(
ICreditFacadeV3Multicall.updateQuota, (tokenOut, quotaForTokenOut, uint96(quotaForTokenOut))
)
});

calls[5] = MultiCall({
calls[4] = MultiCall({
target: creditFacadeAddress,
callData: abi.encodeCall(
ICreditFacadeV3Multicall.updateQuota, (collateral, quotaForCollateral, uint96(quotaForCollateral))
Expand All @@ -313,22 +311,40 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent

/// this can be enhanced to this formula (muliplied by LT):
/// https://github.com/Gearbox-protocol/sdk/blob/d7dda524d049a3c68e31e44a8eed3fecc288b52d/src/core/creditAccount.ts#L564
calls[4] = MultiCall({
calls[3] = MultiCall({
target: creditFacadeAddress,
callData: abi.encodeCall(ICreditFacadeV3Multicall.updateQuota, (tokenOut, quota, uint96(quota)))
});
}

/// isNonTokenInOrOutCollateral has an additional call data
calls[isNonTokenInOrOutCollateral ? 6 : 5] = MultiCall({
target: creditFacadeAddress,
callData: abi.encodeCall(ICreditFacadeV3Multicall.compareBalances, ())
});

return calls;
}

function _execute(Order calldata order, address adapter, bytes calldata adapterCallData) internal {
function _genCalls(Order calldata order) internal returns (MultiCall[] memory) {
MultiCall[] memory collateralCalls = _genCollateralCalls(order);
MultiCall[] memory calls = _genRouterCalls(order);

// bypass router slippage check becuase it is not working well
MultiCall[] memory newCalls = new MultiCall[](calls.length - 1);
for (uint256 i = 1; i < calls.length; i++) {
newCalls[i - 1] = calls[i];
}

return collateralCalls.concat(newCalls);
}

function _genRouterCalls(Order calldata order) internal returns (MultiCall[] memory) {
ICreditManagerV3 creditManager = ICreditManagerV3(order.creditManager);
address creditFacadeAddress = creditManager.creditFacade();
RouterResult memory result;

result =
_router.findOneTokenPath(order.tokenIn, order.amountIn, order.tokenOut, order.creditAccount, _connectors, 0);

return result.calls;
}

function _execute(Order calldata order) internal {
ICreditManagerV3 creditManager = ICreditManagerV3(order.creditManager);
address creditFacadeAddress = creditManager.creditFacade();

Expand All @@ -339,7 +355,8 @@ contract GearboxDCA is EIP712, IGearboxDCA, IGearboxDCAStruct, IGearboxDCAEvent

SafeERC20.forceApprove(IERC20Metadata(collateral), address(creditManager), collateralAmount);

MultiCall[] memory calls = _genCalls(order, adapter, adapterCallData);
MultiCall[] memory calls = _genCalls(order);

ICreditFacadeV3(creditFacadeAddress).botMulticall(order.creditAccount, calls);

bytes32 orderHash = _getOrderHash(order);
Expand Down
10 changes: 3 additions & 7 deletions contracts/interfaces/IGearboxDCA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ pragma solidity ^0.8.17;
import {IGearboxDCAStruct} from "./IGearboxDCAStruct.sol";

interface IGearboxDCA {
function executeOrder(
IGearboxDCAStruct.Order calldata order,
bytes calldata signature,
address adapter,
bytes calldata adapterCallData
)
external;
function executeOrder(IGearboxDCAStruct.Order calldata order, bytes calldata signature) external;

function cancelOrder(IGearboxDCAStruct.Order calldata order) external;

function getOrderHash(IGearboxDCAStruct.Order calldata order) external view returns (bytes32);

function getOrderStatus(bytes32 orderHash) external view returns (IGearboxDCAStruct.OrderStatus memory);
}
11 changes: 9 additions & 2 deletions contracts/tests/TestGearboxDCA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@ pragma solidity ^0.8.13;
import "../GearboxDCA.sol";

contract TestGearboxDCA is GearboxDCA {
constructor(string memory name, string memory version, address priceOracle, address contractsRegister)
GearboxDCA(name, version, priceOracle, contractsRegister)
constructor(
string memory name,
string memory version,
address priceOracle,
address contractsRegister,
address router,
address[] memory connectors
)
GearboxDCA(name, version, priceOracle, contractsRegister, router, connectors)
{}

function verifySigner(Order calldata order, bytes calldata signature) external view {
Expand Down
8 changes: 7 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@gearbox-protocol/core-v2": "^1.19.0-base.10",
"@gearbox-protocol/core-v3": "^1.49.7",
"@gearbox-protocol/integrations-v3": "^1.41.0",
"@gearbox-protocol/liquidator-v2-contracts": "^2.0.0",
"@gearbox-protocol/sdk-gov": "^2.9.0",
"@openzeppelin/contracts": "^5.0.2"
}
Expand Down
10 changes: 8 additions & 2 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@

pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {GearboxDCA} from "../contracts/GearboxDCA.sol";
import {Script, console} from "forge-std/Script.sol";

contract Deploy is Script {
GearboxDCA public gearboxDCA;

// mainnet oracle
address internal constant PRICE_ORACLE = 0x599f585D1042A14aAb194AC8031b2048dEFdFB85;
address internal constant CONTRACTS_REGISTER = 0xA50d4E7D8946a7c90652339CDBd262c375d54D99;
address internal constant ROUTER = 0xA6FCd1fE716aD3801C71F2DE4E7A15f3a6994835;
address[] internal CONNECTORS = [
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // USDC
0xdAC17F958D2ee523a2206206994597C13D831ec7 // USDT
];

function run() external {
vm.startBroadcast();

gearboxDCA = new GearboxDCA("GearboxDCA", "1.0.0", PRICE_ORACLE, CONTRACTS_REGISTER);
gearboxDCA = new GearboxDCA("GearboxDCA", "1.0.0", PRICE_ORACLE, CONTRACTS_REGISTER, ROUTER, CONNECTORS);

vm.stopBroadcast();
}
Expand Down
Loading