address nits
This commit is contained in:
parent
062e29c848
commit
50681fcdbd
@ -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
|
||||||
|
@ -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 {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user