@@ -15,18 +15,37 @@ import "../buyback/library/RevertReasonParser.sol";
1515 * @dev result of swap will be sent to receiver address to distribute to users
1616 */
1717contract ListaAutoBuyback is Initializable , AccessControlUpgradeable {
18-
1918 using SafeERC20 for IERC20 ;
2019
20+ struct SwapDescription {
21+ address srcToken;
22+ address dstToken;
23+ address payable srcReceiver;
24+ address payable dstReceiver;
25+ uint256 amount;
26+ uint256 minReturnAmount;
27+ uint256 flags;
28+ }
29+
2130 event BoughtBack (address indexed tokenIn , uint256 amountIn , uint256 amountOut );
31+ event BoughtBack (
32+ address indexed pair ,
33+ address indexed tokenIn ,
34+ address indexed tokenOut ,
35+ uint256 amountIn ,
36+ uint256 amountOut
37+ );
2238
2339 event ReceiverChanged (address indexed receiver );
24-
2540 event RouterChanged (address indexed router , bool added );
41+ event TokenWhitelistChanged (address indexed token , bool added );
42+ event AdminTransfer (address token , uint256 amount );
43+
2644
2745 bytes32 public constant BOT = keccak256 ("BOT " );
2846
2947 bytes4 public constant SWAP_FUNCTION_SELECTOR = bytes4 (keccak256 ("swap(address,(address,address,address,address,uint256,uint256,uint256),bytes) " ));
48+ address public constant SWAP_NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ;
3049
3150 // The offset of the dstReceiver in the call data
3251 // 4 bytes for the function selector + 32 bytes for executor + 32 bytes for srcToken +
@@ -38,15 +57,19 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
3857
3958 address public defaultReceiver;
4059
41- mapping (address => bool ) public oneInchRouterWhitelist ;
60+ mapping (address => bool ) public routerWhitelist ;
4261
4362 mapping (uint256 => uint256 ) public dailyBought;
4463
64+ mapping (address => bool ) public tokenWhitelist;
65+
4566 /// @custom:oz-upgrades-unsafe-allow constructor
4667 constructor () {
4768 _disableInitializers ();
4869 }
4970
71+ receive () external payable {}
72+
5073 function initialize (
5174 address _admin ,
5275 address _bot ,
@@ -62,7 +85,7 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
6285 _setupRole (BOT, _bot);
6386
6487 defaultReceiver = _initReceiver;
65- oneInchRouterWhitelist [_initRouter] = true ;
88+ routerWhitelist [_initRouter] = true ;
6689 }
6790
6891 /**
@@ -77,8 +100,16 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
77100 onlyRole (BOT)
78101 {
79102 require (_amountIn > 0 , "amountIn is zero " );
80- require (oneInchRouterWhitelist [_1inchRouter], "router not whitelisted " );
103+ require (routerWhitelist [_1inchRouter], "router not whitelisted " );
81104 require (_getFunctionSelector (_data) == SWAP_FUNCTION_SELECTOR, "invalid function selector of _data " );
105+ require (tokenWhitelist[_tokenIn], "token in not whitelisted " );
106+ (, SwapDescription memory swapDesc , ) = abi.decode (_data[4 :], (address , SwapDescription, bytes ));
107+
108+ require (_tokenIn == swapDesc.srcToken, "Invalid swap input token " );
109+ require (tokenWhitelist[swapDesc.dstToken], "token out not whitelisted " );
110+ require (address (swapDesc.dstReceiver) == defaultReceiver, "Invalid receiver " );
111+ require (swapDesc.amount > 0 , "Invalid swap input amount " );
112+
82113 require (_extractDstReceiver (_data) == defaultReceiver, "invalid dst receiver of _data " );
83114 require (IERC20 (_tokenIn).balanceOf (address (this )) >= _amountIn, "insufficient balance " );
84115 // Approves the 1inch router contract to spend the specified amount of _tokenIn
@@ -92,14 +123,64 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
92123 }
93124
94125 (uint256 amountOut ,) = abi.decode (result, (uint256 , uint256 ));
126+ require (amountOut >= swapDesc.minReturnAmount, "insufficient output amount " );
95127 uint256 today = block .timestamp / DAY * DAY;
96128 dailyBought[today] = dailyBought[today] + amountOut;
97129
98130 emit BoughtBack (_tokenIn, _amountIn, amountOut);
99131 }
100132
133+ /// @dev buy back tokens using router
134+ /// @param _router The address of the router.
135+ /// @param _tokenIn The address of the input token.
136+ /// @param _tokenOut The address of the output token.
137+ /// @param _amountIn The amount to sell.
138+ /// @param _amountOutMin The minimum amount to receive.
139+ /// @param _swapData The swap data.
140+ function buyback (
141+ address _router ,
142+ address _tokenIn ,
143+ address _tokenOut ,
144+ uint256 _amountIn ,
145+ uint256 _amountOutMin ,
146+ bytes calldata _swapData
147+ ) external onlyRole (BOT) {
148+ require (tokenWhitelist[_tokenIn], "token not whitelisted " );
149+ require (tokenWhitelist[_tokenOut], "token not whitelisted " );
150+ require (routerWhitelist[_router], "router not whitelisted " );
151+
152+ uint256 beforeTokenIn = _getTokenBalance (_tokenIn, address (this ));
153+ uint256 beforeTokenOut = _getTokenBalance (_tokenOut, address (this ));
154+
155+ bool isNativeTokenIn = (_tokenIn == SWAP_NATIVE_TOKEN_ADDRESS);
156+ if (! isNativeTokenIn) {
157+ IERC20 (_tokenIn).safeApprove (_router, _amountIn);
158+ }
159+ (bool success , ) = _router.call {value: isNativeTokenIn ? _amountIn : 0 }(_swapData);
160+ require (success, "swap failed " );
161+ if (! isNativeTokenIn) {
162+ IERC20 (_tokenIn).safeApprove (_router, 0 );
163+ }
164+
165+ uint256 actualAmountIn = beforeTokenIn - _getTokenBalance (_tokenIn, address (this ));
166+ uint256 actualAmountOut = _getTokenBalance (_tokenOut, address (this )) - beforeTokenOut;
167+
168+ require (actualAmountIn <= _amountIn, "exceed amount in " );
169+ require (actualAmountOut >= _amountOutMin, "not enough profit " );
170+
171+ IERC20 (_tokenOut).safeTransfer (defaultReceiver, actualAmountOut);
172+
173+ emit BoughtBack (_router, _tokenIn, _tokenOut, actualAmountIn, actualAmountOut);
174+ }
175+
101176 function adminTransfer (address _token , uint256 _amount ) external onlyRole (DEFAULT_ADMIN_ROLE) {
102- IERC20 (_token).safeTransfer (msg .sender , _amount);
177+ if (_token == SWAP_NATIVE_TOKEN_ADDRESS) {
178+ (bool success , ) = payable (msg .sender ).call { value: _amount }("" );
179+ require (success, "Withdraw failed " );
180+ } else {
181+ IERC20 (_token).safeTransfer (msg .sender , _amount);
182+ }
183+ emit AdminTransfer (_token, _amount);
103184 }
104185
105186 function changeDefaultReceiver (address _receiver ) external onlyRole (DEFAULT_ADMIN_ROLE) {
@@ -110,20 +191,27 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
110191 emit ReceiverChanged (defaultReceiver);
111192 }
112193
113- function add1InchRouterWhitelist (address _router ) external onlyRole (DEFAULT_ADMIN_ROLE) {
114- require (! oneInchRouterWhitelist[_router], "router already whitelisted " );
115-
116- oneInchRouterWhitelist[_router] = true ;
117- emit RouterChanged (_router, true );
194+ /// @dev sets the router whitelist.
195+ /// @param _router The address of the router.
196+ /// @param status The status of the router.
197+ function setRouterWhitelist (address _router , bool status ) external onlyRole (DEFAULT_ADMIN_ROLE) {
198+ require (_router != address (0 ), "Invalid router address " );
199+ require (routerWhitelist[_router] != status, "whitelist same status " );
200+ routerWhitelist[_router] = status;
201+ emit RouterChanged (_router, status);
118202 }
119203
120- function remove1InchRouterWhitelist (address _router ) external onlyRole (DEFAULT_ADMIN_ROLE) {
121- require (oneInchRouterWhitelist[_router], "router not whitelisted " );
122-
123- delete oneInchRouterWhitelist[_router];
124- emit RouterChanged (_router, false );
204+ /// @dev sets the token whitelist.
205+ /// @param token The address of the token.
206+ /// @param status The status of the token.
207+ function setTokenWhitelist (address token , bool status ) external onlyRole (DEFAULT_ADMIN_ROLE) {
208+ require (token != address (0 ), "Invalid token " );
209+ require (tokenWhitelist[token] != status, "whitelist same status " );
210+ tokenWhitelist[token] = status;
211+ emit TokenWhitelistChanged (token, status);
125212 }
126213
214+
127215 function _getFunctionSelector (bytes calldata _data ) private pure returns (bytes4 ) {
128216 return bytes4 (_data[0 :4 ]);
129217 }
@@ -135,4 +223,12 @@ contract ListaAutoBuyback is Initializable, AccessControlUpgradeable {
135223 dstReceiver := calldataload (add (_data.offset, SWAP_DST_RECEIVER_OFFSET))
136224 }
137225 }
226+
227+ function _getTokenBalance (address _token , address account ) internal view returns (uint256 ) {
228+ if (_token == SWAP_NATIVE_TOKEN_ADDRESS) {
229+ return account.balance;
230+ } else {
231+ return IERC20 (_token).balanceOf (account);
232+ }
233+ }
138234}
0 commit comments