forked from Cyfrin/chainlink-gmx-automation
-
Notifications
You must be signed in to change notification settings - Fork 1
/
WithdrawalAutomation.sol
134 lines (116 loc) · 5.96 KB
/
WithdrawalAutomation.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {LibGMXEventLogDecoder} from "./libraries/LibGMXEventLogDecoder.sol";
import {GMXAutomationBase} from "./GMXAutomationBase.sol";
// gmx-synthetics
import {EventUtils} from "gmx-synthetics/event/EventUtils.sol";
import {DataStore} from "gmx-synthetics/data/DataStore.sol";
import {Reader} from "gmx-synthetics/reader/Reader.sol";
import {Market} from "gmx-synthetics/market/Market.sol";
import {OracleUtils} from "gmx-synthetics/oracle/OracleUtils.sol";
import {WithdrawalHandler} from "gmx-synthetics/exchange/WithdrawalHandler.sol";
// chainlink
import {StreamsLookupCompatibleInterface} from
"chainlink/dev/automation/2_1/interfaces/StreamsLookupCompatibleInterface.sol";
import {ILogAutomation, Log} from "chainlink/dev/automation/2_1/interfaces/ILogAutomation.sol";
/// @title Withdrawal Automation
/// @author Alex Roan - Cyfrin (@alexroan)
contract WithdrawalAutomation is ILogAutomation, StreamsLookupCompatibleInterface, GMXAutomationBase {
using LibGMXEventLogDecoder for Log;
using LibGMXEventLogDecoder for EventUtils.EventLogData;
// ERRORS
error WithdrawalAutomation_IncorrectEventName(string eventName, string expectedEventName);
// CONSTANTS
string public constant EXPECTED_LOG_EVENTNAME = "WithdrawalCreated";
string public constant STRING_DATASTREAMS_FEEDLABEL = "feedIdHex";
string public constant STRING_DATASTREAMS_QUERYLABEL = "blockNumber";
// IMMUTABLES
WithdrawalHandler public immutable i_withdrawalHandler;
/// @param dataStore the DataStore contract address - immutable
/// @param reader the Reader contract address - immutable
/// @param withdrawalHandler the WithdrawalHandler contract address - immutable
constructor(DataStore dataStore, Reader reader, WithdrawalHandler withdrawalHandler)
GMXAutomationBase(dataStore, reader)
{
i_withdrawalHandler = withdrawalHandler;
}
///////////////////////////
// AUTOMATION FUNCTIONS
///////////////////////////
/// @notice Retrieve relevant information from the log and perform a feed lookup
/// @dev Reverts with custom errors if the event name is not equal to the expected event name (WithdrawalCreated).
/// @dev In the success case, reverts with StreamsLookup error containing relevant information for the feed lookup
/// @dev This function is only ever simulated off-chain, so gas is not a concern.
function checkLog(Log calldata log, bytes memory) external returns (bool, bytes memory) {
// Decode Event Log 2
(
, //msgSender,
string memory eventName,
EventUtils.EventLogData memory eventData
) = log.decodeEventLog();
// Ensure that the event name is equal to the expected event name
if (keccak256(abi.encode(eventName)) != keccak256(abi.encode(EXPECTED_LOG_EVENTNAME))) {
revert WithdrawalAutomation_IncorrectEventName(eventName, EXPECTED_LOG_EVENTNAME);
}
// Decode the EventData struct to retrieve relevant data
(bytes32 key, address market,,, address[] memory longTokenSwapPath, address[] memory shortTokenSwapPath) =
eventData.decodeEventData();
// For each address in:
// - market
// - longTokenSwapPath[]
// - shortTokenSwapPath[]
// retrieve the Props struct from the DataStore. Use Props.marketToken to retrieve the feedId
// and add to a list of feedIds.
// Push the market feedId to the set
Market.Props memory marketProps = i_reader.getMarket(i_dataStore, market);
_addPropsToMapping(marketProps);
// Push the longTokenSwapPath feedIds to the set
for (uint256 i = 0; i < longTokenSwapPath.length; i++) {
Market.Props memory longTokenSwapPathProps = i_reader.getMarket(i_dataStore, longTokenSwapPath[i]);
_addPropsToMapping(longTokenSwapPathProps);
}
// Push the shortTokenSwapPath feedIds to the set
for (uint256 i = 0; i < shortTokenSwapPath.length; i++) {
Market.Props memory shortTokenSwapPathProps = i_reader.getMarket(i_dataStore, shortTokenSwapPath[i]);
_addPropsToMapping(shortTokenSwapPathProps);
}
// Clear the feedIdSet
(string[] memory feedIds, address[] memory addresses) = _flushMapping();
// Construct the feed lookup error
revert StreamsLookup(
STRING_DATASTREAMS_FEEDLABEL,
feedIds,
STRING_DATASTREAMS_QUERYLABEL,
log.blockNumber,
abi.encode(key, addresses)
);
}
/// @notice Check the callback
/// @dev Encode the values and extra data into performData and return true
function checkCallback(bytes[] calldata values, bytes calldata extraData)
external
pure
returns (bool, bytes memory)
{
return (true, abi.encode(values, extraData));
}
/// @notice Perform the upkeep
/// @param performData the data returned from checkCallback. Encoded:
/// - bytes[] values. Each value contains a signed report by the DON, and must be decoded:
/// - bytes32[3] memory reportContext,
/// - bytes memory reportData,
/// - bytes32[] memory rs,
/// - bytes32[] memory ss,
/// - bytes32 rawVs
/// - bytes extraData <- This is where the key and addresses array are stored
/// @dev Decode the performData and call executeWithdrawal
function performUpkeep(bytes calldata performData) external onlyForwarder {
if (tx.origin == address(0)) return;
(bytes[] memory values, bytes memory extraData) = abi.decode(performData, (bytes[], bytes));
(bytes32 key, address[] memory addresses) = abi.decode(extraData, (bytes32, address[]));
OracleUtils.SetPricesParams memory oracleParams;
oracleParams.realtimeFeedTokens = addresses;
oracleParams.realtimeFeedData = values;
i_withdrawalHandler.executeWithdrawal(key, oracleParams);
}
}