Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions contracts/mocks/external/ZeroExMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,42 @@ contract ZeroExMock {
_transferTokens();
}

function sellEthForTokenToUniswapV3(
bytes memory /* encodedPath */,
uint256 /* minBuyAmount */,
address /* recipient */
)
external
payable
returns (uint256)
{
_transferTokens();
}

function sellTokenForEthToUniswapV3(
bytes memory /* encodedPath */,
uint256 /* sellAmount */,
uint256 /* minBuyAmount */,
address payable /* recipient */
)
external
returns (uint256)
{
_transferTokens();
}

function sellTokenForTokenToUniswapV3(
bytes memory /* encodedPath */,
uint256 /* sellAmount */,
uint256 /* minBuyAmount */,
address /* recipient */
)
external
returns (uint256)
{
_transferTokens();
}

function _transferTokens()
private
{
Expand Down
54 changes: 54 additions & 0 deletions contracts/protocol/integration/exchange/ZeroExApiAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ contract ZeroExApiAdapter {
// ETH pseudo-token address used by 0x API.
address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

// Byte size of Uniswap V3 encoded path addresses and pool fees
uint256 private constant UNISWAP_V3_PATH_ADDRESS_SIZE = 20;
uint256 private constant UNISWAP_V3_PATH_FEE_SIZE = 3;
// Minimum byte size of a single hop Uniswap V3 encoded path (token address + fee + token adress)
uint256 private constant UNISWAP_V3_SINGLE_HOP_PATH_SIZE = UNISWAP_V3_PATH_ADDRESS_SIZE + UNISWAP_V3_PATH_FEE_SIZE + UNISWAP_V3_PATH_ADDRESS_SIZE;
// Byte size of one hop in the Uniswap V3 encoded path (token address + fee)
uint256 private constant UNISWAP_V3_SINGLE_HOP_OFFSET_SIZE = UNISWAP_V3_PATH_ADDRESS_SIZE + UNISWAP_V3_PATH_FEE_SIZE;

// Address of the deployed ZeroEx contract.
address public immutable zeroExAddress;

Expand Down Expand Up @@ -152,6 +160,29 @@ contract ZeroExApiAdapter {
inputToken = fillData.tokens[0];
outputToken = fillData.tokens[fillData.tokens.length - 1];
inputTokenAmount = fillData.sellAmount;
} else if (selector == 0x6af479b2) {
// sellTokenForTokenToUniswapV3()
bytes memory encodedPath;
(encodedPath, inputTokenAmount, minOutputTokenAmount, recipient) =
abi.decode(_data[4:], (bytes, uint256, uint256, address));
supportsRecipient = true;
(inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath);
} else if (selector == 0x803ba26d) {
// sellTokenForEthToUniswapV3()
bytes memory encodedPath;
(encodedPath, inputTokenAmount, minOutputTokenAmount, recipient) =
abi.decode(_data[4:], (bytes, uint256, uint256, address));
supportsRecipient = true;
(inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath);
} else if (selector == 0x3598d8ab) {
// sellEthForTokenToUniswapV3()
inputTokenAmount = _sourceQuantity;
bytes memory encodedPath;
(encodedPath, minOutputTokenAmount, recipient) =
abi.decode(_data[4:], (bytes, uint256, address));
supportsRecipient = true;
(, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath);
inputToken = ETH_ADDRESS;
} else {
revert("Unsupported 0xAPI function selector");
}
Expand All @@ -170,4 +201,27 @@ contract ZeroExApiAdapter {
_data
);
}

// Decode input and output tokens from an arbitrary length encoded Uniswap V3 path
function _decodeTokensFromUniswapV3EncodedPath(bytes memory encodedPath)
private
pure
returns (
address inputToken,
address outputToken
)
{
require(encodedPath.length >= UNISWAP_V3_SINGLE_HOP_PATH_SIZE, "UniswapV3 token path too short");

// UniswapV3 paths are packed encoded as (address(token0), uint24(fee), address(token1), [...])
// We want the first and last token.
uint256 numHops = (encodedPath.length - UNISWAP_V3_PATH_ADDRESS_SIZE)/UNISWAP_V3_SINGLE_HOP_OFFSET_SIZE;
uint256 lastTokenOffset = numHops * UNISWAP_V3_SINGLE_HOP_OFFSET_SIZE;
assembly {
let p := add(encodedPath, 32)
inputToken := shr(96, mload(p))
p := add(p, lastTokenOffset)
outputToken := shr(96, mload(p))
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"license": "MIT",
"homepage": "https://github.com/SetProtocol",
"devDependencies": {
"@0x/utils": "^6.4.3",
"@ethersproject/bignumber": "^5.0.12",
"@ethersproject/providers": "^5.0.17",
"@nomiclabs/hardhat-ethers": "^2.0.1",
Expand Down
Loading