address nits

This commit is contained in:
dextracker 2023-02-22 18:24:15 -05:00
parent 062e29c848
commit 50681fcdbd
3 changed files with 111 additions and 91 deletions

View File

@ -416,9 +416,9 @@ contract MultiplexFeature is
_multiHopSellLiquidityProvider(state, params, subcall.data); _multiHopSellLiquidityProvider(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.BatchSell) { } else if (subcall.id == MultiplexSubcall.BatchSell) {
_nestedBatchSell(state, params, subcall.data); _nestedBatchSell(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.OTC){ } else if (subcall.id == MultiplexSubcall.OTC) {
_multiHopSellOtcOrder(state, params, subcall.data); _multiHopSellOtcOrder(state, params, subcall.data);
}else { } else {
revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL"); revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL");
} }
// The recipient of the current hop will be the source // The recipient of the current hop will be the source

View File

@ -72,10 +72,8 @@ abstract contract MultiplexOtc is FixinEIP712 {
bytes memory wrappedCallData bytes memory wrappedCallData
) internal { ) internal {
// Decode the tokens[], Otc order, and signature. // Decode the tokens[], Otc order, and signature.
(address[] memory tokens, LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) = abi.decode( (address[] memory tokens, LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) = abi
wrappedCallData, .decode(wrappedCallData, (address[], LibNativeOrder.OtcOrder, LibSignature.Signature));
(address[], LibNativeOrder.OtcOrder, LibSignature.Signature)
);
// Validate tokens. // Validate tokens.
require( require(
tokens.length >= 2 && tokens.length >= 2 &&
@ -83,29 +81,19 @@ abstract contract MultiplexOtc is FixinEIP712 {
tokens[tokens.length - 1] == params.tokens[state.hopIndex + 1], tokens[tokens.length - 1] == params.tokens[state.hopIndex + 1],
"MultiplexOtcOrder::_multiHopSellOtcOrder/INVALID_TOKENS" "MultiplexOtcOrder::_multiHopSellOtcOrder/INVALID_TOKENS"
); );
// Pre-emptively check if the order is expired.
uint64 expiry = uint64(order.expiryAndNonce >> 192);
if (expiry <= uint64(block.timestamp)) {
bytes32 orderHash = _getEIP712Hash(LibNativeOrder.getOtcOrderStructHash(order));
emit ExpiredOtcOrder(orderHash, order.maker, expiry);
return;
}
uint256 sellAmount = state.outputTokenAmount; uint256 sellAmount = state.outputTokenAmount;
// Try filling the Otc order. Swallows reverts. // Try filling the Otc order. Bubble up reverts.
try (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) = IOtcOrdersFeature(address(this))
IOtcOrdersFeature(address(this))._fillOtcOrder( ._fillOtcOrder(
order, order,
signature, signature,
sellAmount.safeDowncastToUint128(), sellAmount.safeDowncastToUint128(),
msg.sender, msg.sender,
params.useSelfBalance, params.useSelfBalance,
params.recipient params.recipient
) );
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) { //store the bought amount for the next hop
//store the amount bought from the otc order state.outputTokenAmount = makerTokenFilledAmount;
state.outputTokenAmount = makerTokenFilledAmount;
} catch {}
} }
} }

View File

@ -21,6 +21,7 @@ import "../utils/TestUtils.sol";
import "src/IZeroEx.sol"; import "src/IZeroEx.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol"; import "@0x/contracts-erc20/src/IEtherToken.sol";
import "src/features/TransformERC20Feature.sol"; import "src/features/TransformERC20Feature.sol";
import "src/features/multiplex/MultiplexFeature.sol";
import "src/external/TransformerDeployer.sol"; import "src/external/TransformerDeployer.sol";
import "src/transformers/WethTransformer.sol"; import "src/transformers/WethTransformer.sol";
import "src/transformers/FillQuoteTransformer.sol"; import "src/transformers/FillQuoteTransformer.sol";
@ -50,106 +51,137 @@ contract MultiplexRfqtTest is Test, ForkUtils, TestUtils {
getContractAddresses(i), getContractAddresses(i),
getLiquiditySourceAddresses(i) getLiquiditySourceAddresses(i)
); );
swapWithOtcOrder(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
MultiplexFeature memory mf = new MultiplexFeature(
address(ZERO_EX),
ZERO_EX_DEPLOYED.weth,
ILiquidityProviderSandbox(addresses.exchangeProxyLiquidityProviderSandbox),
address(0), // uniswapFactory
address(0), // sushiswapFactory
bytes32(0), // uniswapPairInitCodeHash
bytes32(0) // sushiswapPairInitCodeHash
);
swapMultihopOtc(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
} }
} }
/* solhint-disable function-max-lines */ /* solhint-disable function-max-lines */
function swapWithOtcOrder(
function swapMultihopOtc(
TokenAddresses memory tokens, TokenAddresses memory tokens,
ContractAddresses memory addresses, ContractAddresses memory addresses,
LiquiditySources memory sources LiquiditySources memory sources
) public onlyForked { ) public onlyForked {
IZERO_EX = IZeroEx(addresses.exchangeProxy); IZERO_EX = IZeroEx(addresses.exchangeProxy);
address USDC = address(tokens.USDC);
// Create our list of transformations, let's do WethTransformer and FillQuoteTransformer
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2);
// Use our cheeky search helper to find the nonce rather than hardcode it address[] memory tradeTokens = new address[](3);
transformations[0].deploymentNonce = _findTransformerNonce( tradeTokens[0] = address(tokens.WrappedNativeToken);
address(addresses.transformers.wethTransformer), tradeTokens[1] = address(tokens.USDC);
address(addresses.exchangeProxyTransformerDeployer) tradeTokens[2] = address(tokens.DAI);
uint inputAmount = 1e18;
uint outputAmount = 5e17;
IMultiplexFeature.MultiHopSellSubcall[] memory subcalls = new IMultiplexFeature.MultiHopSellSubcall[](2);
IMultiplexFeature.MultiHopSellState memory state;
state.outputTokenAmount = 0;
state.from = address(this);
state.to = address(addresses.exchangeProxy);
state.hopIndex = uint256(0);
IMultiplexFeature.MultiHopSellSubcall memory subcall1;
subcall1.id = IMultiplexFeature.MultiplexSubcall.OTC;
//subcall.data = abi.encode(address[], LibNativeOrder.OtcOrder, LibSignature.Signature);
(LibNativeOrder.OtcOrder memory order1, LibSignature.Signature memory signature1) = createOtcOrder(
tokens.WrappedNativeToken,
tokens.DAI,
1e18,
5e17,
0
); );
emit log_named_uint(" WethTransformer nonce", transformations[0].deploymentNonce); subcall1.data = abi.encode(tradeTokens, order1, signature1);
createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer);
// Set the first transformation to transform ETH into WETH
transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18);
transformations[1].deploymentNonce = _findTransformerNonce( IMultiplexFeature.MultiHopSellSubcall memory subcall2;
address(fillQuoteTransformer), subcall2.id = IMultiplexFeature.MultiplexSubcall.OTC;
address(addresses.exchangeProxyTransformerDeployer) (LibNativeOrder.OtcOrder memory order2, LibSignature.Signature memory signature2) = createOtcOrder(
tokens.USDC,
tokens.DAI,
5e17,
5e17,
1
); );
log_named_uint(" FillQuoteTransformer nonce", transformations[1].deploymentNonce); subcall2.data = abi.encode(tradeTokens, order2, signature2);
// Set up the FillQuoteTransformer data
FillQuoteTransformer.TransformData memory fqtData;
fqtData.side = FillQuoteTransformer.Side.Sell;
fqtData.sellToken = IERC20Token(address(tokens.WrappedNativeToken));
fqtData.buyToken = tokens.USDC;
// the FQT has a sequence, e.g first RFQ then Limit then Bridge
// since solidity doesn't support arrays of different types, this is one simple solution
// We use a Bridge order type here as we will fill on UniswapV2
fqtData.fillSequence = new FillQuoteTransformer.OrderType[](1);
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Otc;
// The amount to fill
fqtData.fillAmount = 1e18;
// Now let's set up an OTC fill subcalls[0] = subcall1;
fqtData.otcOrders = new FillQuoteTransformer.OtcOrderInfo[](1); subcalls[1] = subcall2;
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
/// via the given sequence of tokens and calls.
/// The last token in `tokens` is the output token that
/// will ultimately be sent to `msg.sender`
/// @param tokens The sequence of tokens to use for the sell,
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
/// `calls[i]`.
/// @param calls The sequence of calls to use for the sell.
/// @param sellAmount The amount of `inputToken` to sell.
/// @param minBuyAmount The minimum amount of output tokens that
/// must be bought for this function to not revert.
/// @return boughtAmount The amount of output tokens bought.
IZERO_EX.multiplexMultiHopSellTokenForToken(
// input token[] [input, intermediate, output]
tradeTokens,
//array of subcalls [{},{}]
subcalls,
// input token amount
inputAmount,
// min output token amount
outputAmount
);
}
function createOtcOrder(
IERC20Token inputToken,
IERC20Token ouputToken,
uint128 takerAmount,
uint128 makerAmount,
uint bump
) public returns (LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) {
LibNativeOrder.OtcOrder memory order; LibNativeOrder.OtcOrder memory order;
FillQuoteTransformer.OtcOrderInfo memory orderInfo; FillQuoteTransformer.OtcOrderInfo memory orderInfo;
LibSignature.Signature memory signature;
order.makerToken = fqtData.buyToken; order.makerToken = inputToken;
order.takerToken = fqtData.sellToken; order.takerToken = ouputToken;
order.makerAmount = 1e18; order.takerAmount = takerAmount;
order.takerAmount = 1e18; order.makerAmount = makerAmount;
uint privateKey; uint privateKey;
(order.maker, privateKey) = getSigner(); (order.maker, privateKey) = getSigner();
deal(address(order.makerToken), order.maker, 1e20);
deal(address(order.makerToken), order.maker, 2e20);
vm.prank(order.maker); vm.prank(order.maker);
IERC20Token(tokens.USDC).approve(address(addresses.exchangeProxy), 1e20); IERC20Token(order.makerToken).approve(addresses.exchangeProxy, 1e19);
vm.prank(order.maker); vm.prank(order.maker);
order.taker = address(0); order.taker = address(0);
order.txOrigin = address(tx.origin); order.txOrigin = address(tx.origin);
order.expiryAndNonce = encodeExpiryAndNonce(order.maker); order.expiryAndNonce = encodeExpiryAndNonce(order.maker, bump);
orderInfo.order = order;
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, IZERO_EX.getOtcOrderHash(order)); (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, IZERO_EX.getOtcOrderHash(order));
// How much we want to fill on this order, which can be different to the total
// e.g 50/50 split this would be half
order.takerAmount = 1e18;
// Set this low as the price of ETH/USDC can change
order.makerAmount = 1e18;
orderInfo.signature.signatureType = LibSignature.SignatureType.EIP712; signature.signatureType = LibSignature.SignatureType.EIP712;
orderInfo.signature.v = v; signature.v = v;
orderInfo.signature.r = r; signature.r = r;
orderInfo.signature.s = s; signature.s = s;
orderInfo.maxTakerTokenFillAmount = 1e18; return (order, signature);
fqtData.otcOrders[0] = orderInfo;
transformations[1].data = abi.encode(fqtData);
log_string(" Successful fill, makerTokens bought");
IZERO_EX.transformERC20{value: 1e18}(
// input token
IERC20Token(LibERC20Transformer.ETH_TOKEN_ADDRESS),
// output token
tokens.USDC,
// input token amount
order.takerAmount,
// min output token amount
order.makerAmount,
// list of transform
transformations
);
assert(tokens.USDC.balanceOf(address(this)) > 0);
} }
/* solhint-enable function-max-lines */ /* solhint-enable function-max-lines */
function encodeExpiryAndNonce(address maker) public returns (uint256) { function encodeExpiryAndNonce(address maker, uint bump) public returns (uint256) {
uint256 expiry = (block.timestamp + 120) << 192; uint256 expiry = (block.timestamp + 120) << 192;
uint256 bucket = 0 << 128; uint256 bucket = (0 + bump) << 128;
uint256 nonce = vm.getNonce(maker); uint256 nonce = vm.getNonce(maker);
return expiry | bucket | nonce; return expiry | bucket | nonce;
} }