Skip to content

Commit c7cb516

Browse files
committed
Turn SignalBuy into a standalone contract
1 parent 471fbd8 commit c7cb516

File tree

4 files changed

+148
-238
lines changed

4 files changed

+148
-238
lines changed

contracts/SignalBuyContract.sol

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ import "./interfaces/ISignalBuyContract.sol";
1212
import "./interfaces/IPermanentStorage.sol";
1313
import "./interfaces/ISpender.sol";
1414
import "./interfaces/IWeth.sol";
15-
import "./utils/StrategyBase.sol";
1615
import "./utils/BaseLibEIP712.sol";
1716
import "./utils/LibConstant.sol";
1817
import "./utils/LibSignalBuyContractOrderStorage.sol";
18+
import "./utils/Ownable.sol";
1919
import "./utils/SignalBuyContractLibEIP712.sol";
2020
import "./utils/SignatureValidator.sol";
2121

2222
/// @title SignalBuy Contract
2323
/// @notice Order can be filled as long as the provided dealerToken/userToken ratio is better than or equal to user's specfied dealerToken/userToken ratio.
2424
/// @author imToken Labs
25-
contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, SignatureValidator, ReentrancyGuard {
25+
contract SignalBuyContract is ISignalBuyContract, BaseLibEIP712, SignatureValidator, ReentrancyGuard, Ownable {
2626
using SafeMath for uint256;
2727
using SafeERC20 for IERC20;
2828

29+
IWETH public immutable weth;
2930
uint256 public immutable factorActivateDelay;
3031

3132
// Below are the variables which consume storage slots.
@@ -37,23 +38,70 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
3738
uint16 public tokenlonFeeFactor = 0;
3839
uint16 public pendingTokenlonFeeFactor;
3940

41+
mapping(bytes32 => uint256) public filledAmount;
42+
43+
/// @notice Emitted when allowing another account to spend assets
44+
/// @param spender The address that is allowed to transfer tokens
45+
event AllowTransfer(address indexed spender, address token);
46+
47+
/// @notice Emitted when disallowing an account to spend assets
48+
/// @param spender The address that is removed from allow list
49+
event DisallowTransfer(address indexed spender, address token);
50+
51+
/// @notice Emitted when ETH converted to WETH
52+
/// @param amount The amount of converted ETH
53+
event DepositETH(uint256 amount);
54+
4055
constructor(
4156
address _owner,
42-
address _userProxy,
4357
address _weth,
44-
address _permStorage,
45-
address _spender,
4658
address _coordinator,
4759
uint256 _factorActivateDelay,
4860
address _feeCollector
49-
) StrategyBase(_owner, _userProxy, _weth, _permStorage, _spender) {
61+
) Ownable(_owner) {
62+
weth = IWETH(_weth);
5063
coordinator = _coordinator;
5164
factorActivateDelay = _factorActivateDelay;
5265
feeCollector = _feeCollector;
5366
}
5467

5568
receive() external payable {}
5669

70+
/// @notice Set allowance of tokens to an address
71+
/// @notice Only owner can call
72+
/// @param _tokenList The list of tokens
73+
/// @param _spender The address that will be allowed
74+
function setAllowance(address[] calldata _tokenList, address _spender) external onlyOwner {
75+
for (uint256 i = 0; i < _tokenList.length; ++i) {
76+
IERC20(_tokenList[i]).safeApprove(_spender, LibConstant.MAX_UINT);
77+
78+
emit AllowTransfer(_spender, _tokenList[i]);
79+
}
80+
}
81+
82+
/// @notice Clear allowance of tokens to an address
83+
/// @notice Only owner can call
84+
/// @param _tokenList The list of tokens
85+
/// @param _spender The address that will be cleared
86+
function closeAllowance(address[] calldata _tokenList, address _spender) external onlyOwner {
87+
for (uint256 i = 0; i < _tokenList.length; ++i) {
88+
IERC20(_tokenList[i]).safeApprove(_spender, 0);
89+
90+
emit DisallowTransfer(_spender, _tokenList[i]);
91+
}
92+
}
93+
94+
/// @notice Convert ETH in this contract to WETH
95+
/// @notice Only owner can call
96+
function depositETH() external onlyOwner {
97+
uint256 balance = address(this).balance;
98+
if (balance > 0) {
99+
weth.deposit{ value: balance }();
100+
101+
emit DepositETH(balance);
102+
}
103+
}
104+
57105
/// @notice Only owner can call
58106
/// @param _newCoordinator The new address of coordinator
59107
function upgradeCoordinator(address _newCoordinator) external onlyOwner {
@@ -99,7 +147,7 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
99147
bytes calldata _orderUserSig,
100148
TraderParams calldata _params,
101149
CoordinatorParams calldata _crdParams
102-
) external override onlyUserProxy nonReentrant returns (uint256, uint256) {
150+
) external override nonReentrant returns (uint256, uint256) {
103151
bytes32 orderHash = getEIP712Hash(SignalBuyContractLibEIP712._getOrderStructHash(_order));
104152

105153
_validateOrder(_order, orderHash, _orderUserSig);
@@ -161,11 +209,11 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
161209
require(_fill.recipient != address(0), "SignalBuyContract: recipient can not be zero address");
162210

163211
bytes32 fillHash = getEIP712Hash(SignalBuyContractLibEIP712._getFillStructHash(_fill));
212+
require(!LibSignalBuyContractOrderStorage.getStorage().fillSeen[fillHash], "SignalBuyContract: Fill seen before");
164213
require(isValidSignature(_fill.dealer, fillHash, bytes(""), _fillTakerSig), "SignalBuyContract: Fill is not signed by dealer");
165214

166215
// Set fill seen to avoid replay attack.
167-
// PermanentStorage would throw error if fill is already seen.
168-
permStorage.setLimitOrderTransactionSeen(fillHash);
216+
LibSignalBuyContractOrderStorage.getStorage().fillSeen[fillHash] = true;
169217
}
170218

171219
function _validateFillPermission(
@@ -187,11 +235,11 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
187235
})
188236
)
189237
);
238+
require(!LibSignalBuyContractOrderStorage.getStorage().fillSeen[allowFillHash], "SignalBuyContract: AllowFill seen before");
190239
require(isValidSignature(coordinator, allowFillHash, bytes(""), _crdParams.sig), "SignalBuyContract: AllowFill is not signed by coordinator");
191240

192241
// Set allow fill seen to avoid replay attack
193-
// PermanentStorage would throw error if allow fill is already seen.
194-
permStorage.setLimitOrderAllowFillSeen(allowFillHash);
242+
LibSignalBuyContractOrderStorage.getStorage().allowFillSeen[allowFillHash] = true;
195243

196244
return allowFillHash;
197245
}
@@ -214,7 +262,6 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
214262

215263
function _settleForTrader(TraderSettlement memory _settlement) internal {
216264
// memory cache
217-
ISpender _spender = spender;
218265
address _feeCollector = feeCollector;
219266

220267
// Calculate user fee (user receives dealer token so fee is charged in dealer token)
@@ -226,14 +273,14 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
226273
require(dealerTokenForUser >= _settlement.minDealerTokenAmount, "SignalBuyContract: dealer token amount not enough");
227274

228275
// trader -> user
229-
_spender.spendFromUserTo(_settlement.trader, address(_settlement.dealerToken), _settlement.user, dealerTokenForUser);
276+
_settlement.dealerToken.safeTransferFrom(_settlement.trader, _settlement.user, dealerTokenForUser);
230277

231278
// user -> recipient
232-
_spender.spendFromUserTo(_settlement.user, address(_settlement.userToken), _settlement.recipient, _settlement.userTokenAmount);
279+
_settlement.userToken.safeTransferFrom(_settlement.user, _settlement.recipient, _settlement.userTokenAmount);
233280

234281
// Collect user fee (charged in dealer token)
235282
if (tokenlonFee > 0) {
236-
_spender.spendFromUserTo(_settlement.trader, address(_settlement.dealerToken), _feeCollector, tokenlonFee);
283+
_settlement.dealerToken.safeTransferFrom(_settlement.trader, _feeCollector, tokenlonFee);
237284
}
238285

239286
// bypass stack too deep error
@@ -256,12 +303,7 @@ contract SignalBuyContract is ISignalBuyContract, StrategyBase, BaseLibEIP712, S
256303
}
257304

258305
/// @inheritdoc ISignalBuyContract
259-
function cancelSignalBuy(SignalBuyContractLibEIP712.Order calldata _order, bytes calldata _cancelOrderUserSig)
260-
external
261-
override
262-
onlyUserProxy
263-
nonReentrant
264-
{
306+
function cancelSignalBuy(SignalBuyContractLibEIP712.Order calldata _order, bytes calldata _cancelOrderUserSig) external override nonReentrant {
265307
require(_order.expiry > uint64(block.timestamp), "SignalBuyContract: Order is expired");
266308
bytes32 orderHash = getEIP712Hash(SignalBuyContractLibEIP712._getOrderStructHash(_order));
267309
bool isCancelled = LibSignalBuyContractOrderStorage.getStorage().orderHashToCancelled[orderHash];

contracts/interfaces/ISignalBuyContract.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ pragma abicoder v2;
44

55
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
66

7-
import "./IStrategyBase.sol";
87
import "../utils/SignalBuyContractLibEIP712.sol";
98

109
/// @title ISignalBuyContract Interface
1110
/// @author imToken Labs
12-
interface ISignalBuyContract is IStrategyBase {
11+
interface ISignalBuyContract {
1312
/// @notice Emitted when coordinator address is updated
1413
/// @param newCoordinator The address of the new coordinator
1514
event UpgradeCoordinator(address newCoordinator);

contracts/utils/LibSignalBuyContractOrderStorage.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ library LibSignalBuyContractOrderStorage {
55
bytes32 private constant STORAGE_SLOT = 0x1360fb69f36f46eb45cf50ca3a6184b38e4ef3bde9e5aff734dccec027d7b9f7;
66
/// @dev Storage bucket for this feature.
77
struct Storage {
8+
// Has the fill been executed.
9+
mapping(bytes32 => bool) fillSeen;
10+
// Has the allowFill been executed.
11+
mapping(bytes32 => bool) allowFillSeen;
812
// How much maker token has been filled in order.
913
mapping(bytes32 => uint256) orderHashToUserTokenFilledAmount;
1014
// Whether order is cancelled or not.

0 commit comments

Comments
 (0)