feat: v4 final (#136)
* v4 FillQuoteTransformer (#104) * Update FQT to support v4 orders * `@0x/contracts-zero-ex`: Tweak FQT `@0x/contracts-zero-ex`: Drop `ERC20BridgeTransfer` event and add `PartialQuoteFill` event. * `@0x/contracts-utils`: Add `LibSafeMathV06.downcastToUint128()` * `@0x/protocol-utils`: Update transformer utils for V4 FQT * `@0x/contracts-zero-ex`: Fixing FQT tests... * `@0x/contracts-zero-ex`: rename FQT bridge event * `@0x/contracts-zero-ex`: Un-`only` tests * `@0x/migrations`: Update `BridgeAdapter` deployment * `@0x/contracts-integrations`: Delete `mtx_tests` * `@0x/protocol-utils`: Address review comments * `@0x/contracts-zero-ex`: Address review comments * `@0x/migrations`: Update migrations Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com> Co-authored-by: Lawrence Forman <me@merklejerk.com> * v4: Asset-swapper (main branch) (#113) * refactor quote_requestor * WIP v4/asset-swapper: Clean up SwapQuoter and remove @0x/orderbook * Start replacing SignedOrder everywhere * wip: new order type * wip * remove order-utils from most places * hack: Play around with VerboseX types (#119) * hack: Play around with VerboseX types * More hacks * Fix up the bridgeData encodings * Rework Orderbook return type * feat: Don't charge a protocol fee for RFQ orders WIP (#121) * fix simple build errors * simplify types a little * remove SwapQuoteCalculator: unnecessary abstraction * Fix all ./src build errors; make types consistent * export more types for use in 0x API; modify Orderbook interface * stop overriding APIOrder * feat: RFQ v4 + consolidated bridge encoders (#125) * feat: check if taker address is contract * Rework bridge data * Worst case adjustments * RFQT v4 * Future/v4 validate orders (#126) * RFQT v4 * v4 validate native orders * use default invalid signature * refactor rfqt validations in swap quoter * fix types * fix RFQT unlisted api key * remove priceAwareRFQFlag * adjust maker/taker amounts * update JSON schemas * filter zero fillable orders Co-authored-by: xianny <xianny@gmail.com> * fix type export Co-authored-by: xianny <xianny@gmail.com> * remove order-utils as much as possible * work on tests compile * Comment out quote reporter test * updated tests * restore order-utils accidental changes * some lints * Remove old fill_test * ts lint disable for now * update quote report * Re-enable quote report tests * make fill data required field * fix lint * type guards * force fillData as required * fix lint * fix naming * exports * adjust MultiBridge by slippage * cleanups (checkpoint 1) * cleanup types (checkpoint #2) * remove unused deps * `@0x/contract-addresses`: Deploy new FQT (#129) Co-authored-by: Lawrence Forman <me@merklejerk.com> * commit bump to republish * DRY up the rfqt mocker * fix: Balancer load top pools (#131) * fix: Balancer load top 250 pools * refetch top pools on an interval Co-authored-by: Jacob Evans <jacob@dekz.net> Co-authored-by: Kim Persson <kimpers@users.noreply.github.com> Co-authored-by: Lawrence Forman <lawrence@0xproject.com> Co-authored-by: Lawrence Forman <me@merklejerk.com> * Update post rebase * prettier * Remove test helpers exported in asset-swapper * Clean up from review comments * prettier * lint * recreate rfqt mocker * change merge and INVALID_SIGNATURE Co-authored-by: Lawrence Forman <lawrence@0xproject.com> Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com> Co-authored-by: Lawrence Forman <me@merklejerk.com> Co-authored-by: Xianny <8582774+xianny@users.noreply.github.com> Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
This commit is contained in:
@@ -1,239 +0,0 @@
|
||||
import { MarketBuySwapQuote, MarketSellSwapQuote, Orderbook, SwapQuoter } from '@0x/asset-swapper';
|
||||
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { FillResults, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { TestMainnetAggregatorFillsContract } from '../wrappers';
|
||||
|
||||
import { tokens } from './tokens';
|
||||
|
||||
blockchainTests.live('Aggregator Mainnet Tests', env => {
|
||||
// Mainnet address of the `TestMainnetAggregatorFills` contract.
|
||||
const TEST_CONTRACT_ADDRESS = '0x37Ca306F42748b7fe105F89FCBb2CD03D27c8146';
|
||||
const TAKER_ADDRESS = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'; // Vitalik
|
||||
const ORDERBOOK_POLLING_MS = 1000;
|
||||
const GAS_PRICE = new BigNumber(1);
|
||||
const TAKER_ASSET_ETH_VALUE = 500e18;
|
||||
const MIN_BALANCE = 500.1e18;
|
||||
const SYMBOLS = ['ETH', 'DAI', 'USDC', 'FOAM'];
|
||||
const TEST_PAIRS = _.flatten(SYMBOLS.map(m => SYMBOLS.filter(t => t !== m).map(t => [m, t])));
|
||||
const FILL_VALUES = [1, 10, 1e2, 1e3, 1e4, 2.5e4, 5e4];
|
||||
|
||||
let testContract: TestMainnetAggregatorFillsContract;
|
||||
let swapQuoter: SwapQuoter;
|
||||
let takerEthBalance: BigNumber;
|
||||
const orderbooks: { [name: string]: Orderbook } = {};
|
||||
|
||||
async function getTakerOrdersAsync(takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
if (takerAssetSymbol === 'ETH') {
|
||||
return [];
|
||||
}
|
||||
return getOrdersAsync(takerAssetSymbol, 'ETH');
|
||||
}
|
||||
|
||||
// Fetches ETH -> taker asset orders for the forwarder contract.
|
||||
async function getOrdersAsync(makerAssetSymbol: string, takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
const takerTokenAddress = tokens[takerAssetSymbol].address;
|
||||
const makerTokenAddress = tokens[makerAssetSymbol].address;
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
||||
const orders = _.flatten(
|
||||
await Promise.all(
|
||||
Object.keys(orderbooks).map(async name =>
|
||||
getOrdersFromOrderBookAsync(name, makerAssetData, takerAssetData),
|
||||
),
|
||||
),
|
||||
);
|
||||
const uniqueOrders: SignedOrder[] = [];
|
||||
for (const order of orders) {
|
||||
if (!order.makerFee.eq(0) || !order.takerFee.eq(0)) {
|
||||
continue;
|
||||
}
|
||||
if (uniqueOrders.findIndex(o => isSameOrder(order, o)) === -1) {
|
||||
uniqueOrders.push(order);
|
||||
}
|
||||
}
|
||||
return uniqueOrders;
|
||||
}
|
||||
|
||||
async function getOrdersFromOrderBookAsync(
|
||||
name: string,
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
): Promise<SignedOrder[]> {
|
||||
try {
|
||||
return (await orderbooks[name].getOrdersAsync(makerAssetData, takerAssetData)).map(r => r.order);
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to retrieve orders from orderbook "${name}".`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function isSameOrder(a: SignedOrder, b: SignedOrder): boolean {
|
||||
for (const [k, v] of Object.entries(a)) {
|
||||
if (k in (b as any)) {
|
||||
if (BigNumber.isBigNumber(v) && !v.eq((b as any)[k])) {
|
||||
return false;
|
||||
}
|
||||
if (v !== (b as any)[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function toTokenUnits(symbol: string, weis: Numberish): BigNumber {
|
||||
return new BigNumber(weis).div(new BigNumber(10).pow(tokens[symbol].decimals));
|
||||
}
|
||||
|
||||
function fromTokenUnits(symbol: string, units: Numberish): BigNumber {
|
||||
return new BigNumber(units)
|
||||
.times(new BigNumber(10).pow(tokens[symbol].decimals))
|
||||
.integerValue(BigNumber.ROUND_DOWN);
|
||||
}
|
||||
|
||||
interface MarketOperationResult {
|
||||
makerAssetBalanceBefore: BigNumber;
|
||||
takerAssetBalanceBefore: BigNumber;
|
||||
makerAssetBalanceAfter: BigNumber;
|
||||
takerAssetBalanceAfter: BigNumber;
|
||||
fillResults: FillResults;
|
||||
}
|
||||
|
||||
// Liquidity is low right now so it's possible we didn't have
|
||||
// enough taker assets to cover the orders, so occasionally we'll get incomplete
|
||||
// fills. This function will catch those cases.
|
||||
// TODO(dorothy-zbornak): Remove this special case when liquidity is up.
|
||||
function checkHadEnoughTakerAsset(
|
||||
quote: MarketBuySwapQuote | MarketSellSwapQuote,
|
||||
result: MarketOperationResult,
|
||||
): boolean {
|
||||
if (result.takerAssetBalanceBefore.gte(quote.worstCaseQuoteInfo.takerAssetAmount)) {
|
||||
return true;
|
||||
}
|
||||
const takerAssetPct = result.takerAssetBalanceBefore
|
||||
.div(quote.worstCaseQuoteInfo.takerAssetAmount)
|
||||
.times(100)
|
||||
.toNumber()
|
||||
.toFixed(1);
|
||||
logUtils.warn(`Could not acquire enough taker asset to complete the fill: ${takerAssetPct}%`);
|
||||
expect(result.fillResults.makerAssetFilledAmount).to.bignumber.lt(quote.worstCaseQuoteInfo.makerAssetAmount);
|
||||
return false;
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
testContract = new TestMainnetAggregatorFillsContract(TEST_CONTRACT_ADDRESS, env.provider, {
|
||||
...env.txDefaults,
|
||||
gasPrice: GAS_PRICE,
|
||||
gas: 10e6,
|
||||
});
|
||||
swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(env.provider, 'https://api.0x.org/sra');
|
||||
// Pool orderbooks because we're desperate for liquidity.
|
||||
orderbooks.swapQuoter = swapQuoter.orderbook;
|
||||
orderbooks.bamboo = Orderbook.getOrderbookForPollingProvider({
|
||||
httpEndpoint: 'https://sra.bamboorelay.com/0x/v3',
|
||||
pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
});
|
||||
// TODO(dorothy-zbornak): Uncomment when radar's SRA is up.
|
||||
// orderbooks.radar = Orderbook.getOrderbookForPollingProvider({
|
||||
// httpEndpoint: 'https://api-v3.radarrelay.com/v3',
|
||||
// pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
// });
|
||||
takerEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(TAKER_ADDRESS);
|
||||
});
|
||||
|
||||
it('taker has minimum ETH', async () => {
|
||||
expect(takerEthBalance).to.bignumber.gte(MIN_BALANCE);
|
||||
});
|
||||
|
||||
describe('market sells', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(takerSymbol, new BigNumber(fillValue).div(tokens[takerSymbol].price));
|
||||
it(`sell ${toTokenUnits(takerSymbol, fillAmount)} ${takerSymbol} for ${makerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketSellSwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market sell on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketSell(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.takerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.worstCaseQuoteInfo.makerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.takerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('market buys', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(makerSymbol, new BigNumber(fillValue).div(tokens[makerSymbol].price));
|
||||
it(`buy ${toTokenUnits(makerSymbol, fillAmount)} ${makerSymbol} with ${takerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketBuySwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market buy on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketBuy(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.makerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.worstCaseQuoteInfo.takerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.makerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@@ -1,77 +0,0 @@
|
||||
export const tokens: { [symbol: string]: { address: string; decimals: number; price: number } } = {
|
||||
ETH: {
|
||||
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
decimals: 18,
|
||||
price: 133,
|
||||
},
|
||||
SAI: {
|
||||
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
DAI: {
|
||||
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
USDC: {
|
||||
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||
decimals: 6,
|
||||
price: 1,
|
||||
},
|
||||
WBTC: {
|
||||
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
||||
decimals: 8,
|
||||
price: 6900,
|
||||
},
|
||||
MKR: {
|
||||
address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2',
|
||||
decimals: 18,
|
||||
price: 454,
|
||||
},
|
||||
BAT: {
|
||||
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
||||
decimals: 18,
|
||||
price: 0.17,
|
||||
},
|
||||
OMG: {
|
||||
address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07',
|
||||
decimals: 18,
|
||||
price: 0.65,
|
||||
},
|
||||
ZRX: {
|
||||
address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498',
|
||||
decimals: 18,
|
||||
price: 0.19,
|
||||
},
|
||||
ZIL: {
|
||||
address: '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27',
|
||||
decimals: 12,
|
||||
price: 0.004,
|
||||
},
|
||||
FOAM: {
|
||||
address: '0x4946Fcea7C692606e8908002e55A582af44AC121',
|
||||
decimals: 18,
|
||||
price: 0.004,
|
||||
},
|
||||
USDT: {
|
||||
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
decimals: 6,
|
||||
price: 0.019,
|
||||
},
|
||||
REP: {
|
||||
address: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
|
||||
decimals: 18,
|
||||
price: 8.9,
|
||||
},
|
||||
MANA: {
|
||||
address: '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942',
|
||||
decimals: 18,
|
||||
price: 0.025,
|
||||
},
|
||||
LINK: {
|
||||
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
|
||||
decimals: 18,
|
||||
price: 1.8,
|
||||
},
|
||||
};
|
@@ -1,360 +0,0 @@
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { IExchangeContract } from '@0x/contracts-exchange';
|
||||
import { blockchainTests, constants, expect, getRandomPortion, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import {
|
||||
artifacts as exchangeProxyArtifacts,
|
||||
IZeroExContract,
|
||||
LogMetadataTransformerContract,
|
||||
} from '@0x/contracts-zero-ex';
|
||||
import { migrateOnceAsync } from '@0x/migrations';
|
||||
import { assetDataUtils, signatureUtils, SignedExchangeProxyMetaTransaction } from '@0x/order-utils';
|
||||
import {
|
||||
encodeFillQuoteTransformerData,
|
||||
encodePayTakerTransformerData,
|
||||
ETH_TOKEN_ADDRESS,
|
||||
FillQuoteTransformerSide,
|
||||
findTransformerNonce,
|
||||
Signature,
|
||||
} from '@0x/protocol-utils';
|
||||
import { AssetProxyId, Order, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as ethjs from 'ethereumjs-util';
|
||||
|
||||
const { MAX_UINT256, NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;
|
||||
|
||||
function sigstruct(signature: string): Signature {
|
||||
return {
|
||||
v: parseInt(hexUtils.slice(signature, 0, 1), 16),
|
||||
signatureType: parseInt(hexUtils.slice(signature, 65, 66), 16),
|
||||
r: hexUtils.slice(signature, 1, 33),
|
||||
s: hexUtils.slice(signature, 33, 65),
|
||||
};
|
||||
}
|
||||
|
||||
blockchainTests.resets('exchange proxy - meta-transactions', env => {
|
||||
const quoteSignerKey = hexUtils.random();
|
||||
const quoteSigner = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(quoteSignerKey)));
|
||||
let owner: string;
|
||||
let relayer: string;
|
||||
let maker: string;
|
||||
let taker: string;
|
||||
let flashWalletAddress: string;
|
||||
let zeroEx: IZeroExContract;
|
||||
let exchange: IExchangeContract;
|
||||
let inputToken: DummyERC20TokenContract;
|
||||
let outputToken: DummyERC20TokenContract;
|
||||
let feeToken: DummyERC20TokenContract;
|
||||
let addresses: ContractAddresses;
|
||||
let protocolFee: BigNumber;
|
||||
let metadataTransformer: LogMetadataTransformerContract;
|
||||
const GAS_PRICE = new BigNumber('1e9');
|
||||
const MAKER_BALANCE = new BigNumber('100e18');
|
||||
const TAKER_BALANCE = new BigNumber('100e18');
|
||||
const TAKER_FEE_BALANCE = new BigNumber('100e18');
|
||||
|
||||
before(async () => {
|
||||
[, relayer, maker, taker] = await env.getAccountAddressesAsync();
|
||||
addresses = await migrateOnceAsync(env.provider);
|
||||
zeroEx = new IZeroExContract(addresses.exchangeProxy, env.provider, env.txDefaults, {
|
||||
LogMetadataTransformer: LogMetadataTransformerContract.ABI(),
|
||||
DummyERC20Token: DummyERC20TokenContract.ABI(),
|
||||
});
|
||||
exchange = new IExchangeContract(addresses.exchange, env.provider, env.txDefaults);
|
||||
[inputToken, outputToken, feeToken] = await Promise.all(
|
||||
[...new Array(3)].map(i =>
|
||||
DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
`DummyToken-${i}`,
|
||||
`TOK${i}`,
|
||||
new BigNumber(18),
|
||||
BigNumber.max(MAKER_BALANCE, TAKER_BALANCE),
|
||||
),
|
||||
),
|
||||
);
|
||||
// LogMetadataTransformer is not deployed in migrations.
|
||||
metadataTransformer = await LogMetadataTransformerContract.deployFrom0xArtifactAsync(
|
||||
exchangeProxyArtifacts.LogMetadataTransformer,
|
||||
env.provider,
|
||||
{
|
||||
...env.txDefaults,
|
||||
from: addresses.exchangeProxyTransformerDeployer,
|
||||
},
|
||||
{},
|
||||
);
|
||||
owner = await zeroEx.owner().callAsync();
|
||||
protocolFee = await exchange.protocolFeeMultiplier().callAsync();
|
||||
flashWalletAddress = await zeroEx.getTransformWallet().callAsync();
|
||||
const erc20Proxy = await exchange.getAssetProxy(AssetProxyId.ERC20).callAsync();
|
||||
const allowanceTarget = await zeroEx.getAllowanceTarget().callAsync();
|
||||
await outputToken.mint(MAKER_BALANCE).awaitTransactionSuccessAsync({ from: maker });
|
||||
await inputToken.mint(TAKER_BALANCE).awaitTransactionSuccessAsync({ from: taker });
|
||||
await feeToken.mint(TAKER_FEE_BALANCE).awaitTransactionSuccessAsync({ from: taker });
|
||||
await outputToken.approve(erc20Proxy, MAX_UINT256).awaitTransactionSuccessAsync({ from: maker });
|
||||
await inputToken.approve(allowanceTarget, MAX_UINT256).awaitTransactionSuccessAsync({ from: taker });
|
||||
await feeToken.approve(allowanceTarget, MAX_UINT256).awaitTransactionSuccessAsync({ from: taker });
|
||||
await zeroEx.setQuoteSigner(quoteSigner).awaitTransactionSuccessAsync({ from: owner });
|
||||
});
|
||||
|
||||
interface Transformation {
|
||||
deploymentNonce: number;
|
||||
data: string;
|
||||
}
|
||||
|
||||
interface SwapInfo {
|
||||
inputTokenAddress: string;
|
||||
outputTokenAddress: string;
|
||||
inputTokenAmount: BigNumber;
|
||||
minOutputTokenAmount: BigNumber;
|
||||
transformations: Transformation[];
|
||||
orders: SignedOrder[];
|
||||
}
|
||||
|
||||
async function generateSwapAsync(orderFields: Partial<Order> = {}, isRfqt: boolean = false): Promise<SwapInfo> {
|
||||
const order = await signatureUtils.ecSignTypedDataOrderAsync(
|
||||
env.provider,
|
||||
{
|
||||
chainId: 1337,
|
||||
exchangeAddress: exchange.address,
|
||||
expirationTimeSeconds: new BigNumber(Date.now()),
|
||||
salt: new BigNumber(hexUtils.random()),
|
||||
feeRecipientAddress: NULL_ADDRESS,
|
||||
senderAddress: NULL_ADDRESS,
|
||||
takerAddress: isRfqt ? flashWalletAddress : NULL_ADDRESS,
|
||||
makerAddress: maker,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(outputToken.address),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(inputToken.address),
|
||||
makerFeeAssetData: NULL_BYTES,
|
||||
takerFeeAssetData: NULL_BYTES,
|
||||
takerAssetAmount: getRandomPortion(TAKER_BALANCE),
|
||||
makerAssetAmount: getRandomPortion(MAKER_BALANCE),
|
||||
makerFee: ZERO_AMOUNT,
|
||||
takerFee: ZERO_AMOUNT,
|
||||
...orderFields,
|
||||
},
|
||||
maker,
|
||||
);
|
||||
const transformations = [
|
||||
{
|
||||
deploymentNonce: findTransformerNonce(
|
||||
addresses.transformers.fillQuoteTransformer,
|
||||
addresses.exchangeProxyTransformerDeployer,
|
||||
),
|
||||
data: encodeFillQuoteTransformerData({
|
||||
orders: [order],
|
||||
signatures: [order.signature],
|
||||
buyToken: outputToken.address,
|
||||
sellToken: inputToken.address,
|
||||
fillAmount: order.takerAssetAmount,
|
||||
maxOrderFillAmounts: [],
|
||||
refundReceiver: hexUtils.leftPad(2, 20), // Send refund to sender.
|
||||
rfqtTakerAddress: isRfqt ? taker : NULL_ADDRESS,
|
||||
side: FillQuoteTransformerSide.Sell,
|
||||
}),
|
||||
},
|
||||
{
|
||||
deploymentNonce: findTransformerNonce(
|
||||
addresses.transformers.payTakerTransformer,
|
||||
addresses.exchangeProxyTransformerDeployer,
|
||||
),
|
||||
data: encodePayTakerTransformerData({
|
||||
tokens: [inputToken.address, outputToken.address, ETH_TOKEN_ADDRESS],
|
||||
amounts: [MAX_UINT256, MAX_UINT256, MAX_UINT256],
|
||||
}),
|
||||
},
|
||||
{
|
||||
deploymentNonce: findTransformerNonce(
|
||||
metadataTransformer.address,
|
||||
addresses.exchangeProxyTransformerDeployer,
|
||||
),
|
||||
data: NULL_BYTES,
|
||||
},
|
||||
];
|
||||
return {
|
||||
transformations,
|
||||
orders: [order],
|
||||
inputTokenAddress: inputToken.address,
|
||||
outputTokenAddress: outputToken.address,
|
||||
inputTokenAmount: order.takerAssetAmount,
|
||||
minOutputTokenAmount: order.makerAssetAmount,
|
||||
};
|
||||
}
|
||||
|
||||
function getSwapData(swap: SwapInfo): string {
|
||||
return zeroEx
|
||||
.transformERC20(
|
||||
swap.inputTokenAddress,
|
||||
swap.outputTokenAddress,
|
||||
swap.inputTokenAmount,
|
||||
swap.minOutputTokenAmount,
|
||||
swap.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
}
|
||||
|
||||
async function createMetaTransactionAsync(
|
||||
data: string,
|
||||
value: BigNumber,
|
||||
fee?: BigNumber | number,
|
||||
): Promise<SignedExchangeProxyMetaTransaction> {
|
||||
return signatureUtils.ecSignTypedDataExchangeProxyMetaTransactionAsync(
|
||||
env.provider,
|
||||
{
|
||||
value,
|
||||
signer: taker,
|
||||
sender: relayer,
|
||||
minGasPrice: GAS_PRICE,
|
||||
maxGasPrice: GAS_PRICE,
|
||||
expirationTimeSeconds: new BigNumber(Math.floor(Date.now() / 1000) + 60),
|
||||
salt: new BigNumber(hexUtils.random()),
|
||||
callData: data,
|
||||
feeToken: feeToken.address,
|
||||
feeAmount: fee !== undefined ? new BigNumber(fee) : getRandomPortion(TAKER_FEE_BALANCE),
|
||||
domain: {
|
||||
chainId: 1,
|
||||
name: 'ZeroEx',
|
||||
version: '1.0.0',
|
||||
verifyingContract: zeroEx.address,
|
||||
},
|
||||
},
|
||||
taker,
|
||||
);
|
||||
}
|
||||
|
||||
it('can call `transformERC20()` with calldata and no relayer fee', async () => {
|
||||
const swap = await generateSwapAsync();
|
||||
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee, 0);
|
||||
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||
const receipt = await zeroEx
|
||||
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
|
||||
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
|
||||
const relayerEthRefund = relayerEthBalanceBefore
|
||||
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
|
||||
.minus(GAS_PRICE.times(receipt.gasUsed));
|
||||
// Ensure the relayer got back the unused protocol fees.
|
||||
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
|
||||
// Ensure the relayer got paid no mtx fees.
|
||||
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
|
||||
// Ensure the taker got output tokens.
|
||||
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
|
||||
// Ensure the maker got input tokens.
|
||||
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
|
||||
// Check events.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
taker,
|
||||
sender: zeroEx.address,
|
||||
data: NULL_BYTES,
|
||||
},
|
||||
],
|
||||
'TransformerMetadata',
|
||||
);
|
||||
});
|
||||
|
||||
it('can call `transformERC20()` with calldata and a relayer fee', async () => {
|
||||
const swap = await generateSwapAsync();
|
||||
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee);
|
||||
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||
const receipt = await zeroEx
|
||||
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
|
||||
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
|
||||
const relayerEthRefund = relayerEthBalanceBefore
|
||||
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
|
||||
.minus(GAS_PRICE.times(receipt.gasUsed));
|
||||
// Ensure the relayer got back the unused protocol fees.
|
||||
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
|
||||
// Ensure the relayer got paid mtx fees.
|
||||
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(mtx.feeAmount);
|
||||
// Ensure the taker got output tokens.
|
||||
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
|
||||
// Ensure the maker got input tokens.
|
||||
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
|
||||
// Check events.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
taker,
|
||||
sender: zeroEx.address,
|
||||
data: NULL_BYTES,
|
||||
},
|
||||
],
|
||||
'TransformerMetadata',
|
||||
);
|
||||
});
|
||||
|
||||
it('`transformERC20()` can fill RFQT order', async () => {
|
||||
const swap = await generateSwapAsync({}, true);
|
||||
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee, 0);
|
||||
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||
const receipt = await zeroEx
|
||||
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
|
||||
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
|
||||
const relayerEthRefund = relayerEthBalanceBefore
|
||||
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
|
||||
.minus(GAS_PRICE.times(receipt.gasUsed));
|
||||
// Ensure the relayer got back the unused protocol fees.
|
||||
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
|
||||
// Ensure the relayer got paid no mtx fees.
|
||||
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
|
||||
// Ensure the taker got output tokens.
|
||||
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
|
||||
// Ensure the maker got input tokens.
|
||||
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
|
||||
// Check events.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
taker,
|
||||
sender: zeroEx.address,
|
||||
data: NULL_BYTES,
|
||||
},
|
||||
],
|
||||
'TransformerMetadata',
|
||||
);
|
||||
});
|
||||
|
||||
it('`transformERC20()` can fill RFQT order if quote signer configured', async () => {
|
||||
const swap = await generateSwapAsync({}, true);
|
||||
const callData = getSwapData(swap);
|
||||
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||
const mtx = await createMetaTransactionAsync(callData, _protocolFee, 0);
|
||||
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||
await zeroEx.setQuoteSigner(NULL_ADDRESS).awaitTransactionSuccessAsync({ from: owner });
|
||||
const receipt = await zeroEx
|
||||
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
|
||||
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
|
||||
const relayerEthRefund = relayerEthBalanceBefore
|
||||
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
|
||||
.minus(GAS_PRICE.times(receipt.gasUsed));
|
||||
// Ensure the relayer got back the unused protocol fees.
|
||||
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
|
||||
// Ensure the relayer got paid no mtx fees.
|
||||
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
|
||||
// Ensure the taker got output tokens.
|
||||
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
|
||||
// Ensure the maker got input tokens.
|
||||
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
|
||||
// Check events.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
taker,
|
||||
sender: zeroEx.address,
|
||||
data: NULL_BYTES,
|
||||
},
|
||||
],
|
||||
'TransformerMetadata',
|
||||
);
|
||||
});
|
||||
});
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "0.18.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update FQT for v4 native orders",
|
||||
"pr": 104
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.18.1",
|
||||
"changes": [
|
||||
|
@@ -22,13 +22,12 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
import "../errors/LibTransformERC20RichErrors.sol";
|
||||
import "../vendor/v3/IExchange.sol";
|
||||
import "../vendor/v3/LibOrderHash.sol";
|
||||
import "../features/INativeOrdersFeature.sol";
|
||||
import "../features/libs/LibNativeOrder.sol";
|
||||
import "./bridges/IBridgeAdapter.sol";
|
||||
import "./Transformer.sol";
|
||||
import "./LibERC20Transformer.sol";
|
||||
@@ -41,8 +40,8 @@ contract FillQuoteTransformer is
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibERC20Transformer for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibSafeMathV06 for uint128;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
using LibBytesV06 for bytes;
|
||||
|
||||
/// @dev Whether we are performing a market sell or buy.
|
||||
enum Side {
|
||||
@@ -50,6 +49,26 @@ contract FillQuoteTransformer is
|
||||
Buy
|
||||
}
|
||||
|
||||
enum OrderType {
|
||||
Bridge,
|
||||
Limit,
|
||||
Rfq
|
||||
}
|
||||
|
||||
struct LimitOrderInfo {
|
||||
LibNativeOrder.LimitOrder order;
|
||||
LibSignature.Signature signature;
|
||||
// Maximum taker token amount of this limit order to fill.
|
||||
uint256 maxTakerTokenFillAmount;
|
||||
}
|
||||
|
||||
struct RfqOrderInfo {
|
||||
LibNativeOrder.RfqOrder order;
|
||||
LibSignature.Signature signature;
|
||||
// Maximum taker token amount of this limit order to fill.
|
||||
uint256 maxTakerTokenFillAmount;
|
||||
}
|
||||
|
||||
/// @dev Transform data to ABI-encode and pass into `transform()`.
|
||||
struct TransformData {
|
||||
// Whether we are performing a market sell or buy.
|
||||
@@ -60,31 +79,34 @@ contract FillQuoteTransformer is
|
||||
// The token being bought.
|
||||
// This should be an actual token, not the ETH pseudo-token.
|
||||
IERC20TokenV06 buyToken;
|
||||
// The orders to fill.
|
||||
IExchange.Order[] orders;
|
||||
// Signatures for each respective order in `orders`.
|
||||
bytes[] signatures;
|
||||
// Maximum fill amount for each order. This may be shorter than the
|
||||
// number of orders, where missing entries will be treated as `uint256(-1)`.
|
||||
// For sells, this will be the maximum sell amount (taker asset).
|
||||
// For buys, this will be the maximum buy amount (maker asset).
|
||||
uint256[] maxOrderFillAmounts;
|
||||
|
||||
// External liquidity bridge orders. Sorted by fill sequence.
|
||||
IBridgeAdapter.BridgeOrder[] bridgeOrders;
|
||||
// Native limit orders. Sorted by fill sequence.
|
||||
LimitOrderInfo[] limitOrders;
|
||||
// Native RFQ orders. Sorted by fill sequence.
|
||||
RfqOrderInfo[] rfqOrders;
|
||||
|
||||
// The sequence to fill the orders in. Each item will fill the next
|
||||
// order of that type in either `bridgeOrders`, `limitOrders`,
|
||||
// or `rfqOrders.`
|
||||
OrderType[] fillSequence;
|
||||
|
||||
// Amount of `sellToken` to sell or `buyToken` to buy.
|
||||
// For sells, this may be `uint256(-1)` to sell the entire balance of
|
||||
// `sellToken`.
|
||||
// For sells, setting the high-bit indicates that
|
||||
// `sellAmount & LOW_BITS` should be treated as a `1e18` fraction of
|
||||
// the current balance of `sellToken`, where
|
||||
// `1e18+ == 100%` and `0.5e18 == 50%`, etc.
|
||||
uint256 fillAmount;
|
||||
|
||||
// Who to transfer unused protocol fees to.
|
||||
// May be a valid address or one of:
|
||||
// `address(0)`: Stay in flash wallet.
|
||||
// `address(1)`: Send to the taker.
|
||||
// `address(2)`: Send to the sender (caller of `transformERC20()`).
|
||||
address payable refundReceiver;
|
||||
// Required taker address for RFQT orders.
|
||||
// Null means any taker can fill it.
|
||||
address rfqtTakerAddress;
|
||||
}
|
||||
|
||||
/// @dev Results of a call to `_fillOrder()`.
|
||||
struct FillOrderResults {
|
||||
// The amount of taker tokens sold, according to balance checks.
|
||||
uint256 takerTokenSoldAmount;
|
||||
@@ -101,7 +123,8 @@ contract FillQuoteTransformer is
|
||||
uint256 soldAmount;
|
||||
uint256 protocolFee;
|
||||
uint256 takerTokenBalanceRemaining;
|
||||
bool isRfqtAllowed;
|
||||
uint256[3] currentIndices;
|
||||
OrderType currentOrderType;
|
||||
}
|
||||
|
||||
/// @dev Emitted when a trade is skipped due to a lack of funds
|
||||
@@ -109,12 +132,12 @@ contract FillQuoteTransformer is
|
||||
/// @param orderHash The hash of the order that was skipped.
|
||||
event ProtocolFeeUnfunded(bytes32 orderHash);
|
||||
|
||||
/// @dev The Exchange ERC20Proxy ID.
|
||||
bytes4 private constant ERC20_ASSET_PROXY_ID = 0xf47261b0;
|
||||
/// @dev The Exchange ERC20BridgeProxy ID.
|
||||
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
|
||||
/// @dev Maximum uint256 value.
|
||||
uint256 private constant MAX_UINT256 = uint256(-1);
|
||||
/// @dev The highest bit of a uint256 value.
|
||||
uint256 private constant HIGH_BIT = 2 ** 255;
|
||||
/// @dev Mask of the lower 255 bits of a uint256 value.
|
||||
uint256 private constant LOWER_255_BITS = HIGH_BIT - 1;
|
||||
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||
/// protocol fees will be sent to the taker.
|
||||
address private constant REFUND_RECEIVER_TAKER = address(1);
|
||||
@@ -122,33 +145,32 @@ contract FillQuoteTransformer is
|
||||
/// protocol fees will be sent to the sender.
|
||||
address private constant REFUND_RECEIVER_SENDER = address(2);
|
||||
|
||||
/// @dev The Exchange contract.
|
||||
IExchange public immutable exchange;
|
||||
/// @dev The ERC20Proxy address.
|
||||
address public immutable erc20Proxy;
|
||||
/// @dev The BridgeAdapter address
|
||||
IBridgeAdapter public immutable bridgeAdapter;
|
||||
|
||||
/// @dev The exchange proxy contract.
|
||||
INativeOrdersFeature public immutable zeroEx;
|
||||
|
||||
/// @dev Create this contract.
|
||||
/// @param exchange_ The Exchange V3 instance.
|
||||
constructor(IExchange exchange_, IBridgeAdapter bridgeAdapter_)
|
||||
/// @param bridgeAdapter_ The bridge adapter contract.
|
||||
/// @param zeroEx_ The Exchange Proxy contract.
|
||||
constructor(IBridgeAdapter bridgeAdapter_, INativeOrdersFeature zeroEx_)
|
||||
public
|
||||
Transformer()
|
||||
{
|
||||
exchange = exchange_;
|
||||
erc20Proxy = exchange_.getAssetProxy(ERC20_ASSET_PROXY_ID);
|
||||
bridgeAdapter = bridgeAdapter_;
|
||||
zeroEx = zeroEx_;
|
||||
}
|
||||
|
||||
/// @dev Sell this contract's entire balance of of `sellToken` in exchange
|
||||
/// for `buyToken` by filling `orders`. Protocol fees should be attached
|
||||
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
/// @return magicBytes The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
returns (bytes4 magicBytes)
|
||||
{
|
||||
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||
FillState memory state;
|
||||
@@ -160,7 +182,11 @@ contract FillQuoteTransformer is
|
||||
context.data
|
||||
).rrevert();
|
||||
}
|
||||
if (data.orders.length != data.signatures.length) {
|
||||
|
||||
if (data.bridgeOrders.length
|
||||
+ data.limitOrders.length
|
||||
+ data.rfqOrders.length != data.fillSequence.length
|
||||
) {
|
||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
|
||||
context.data
|
||||
@@ -168,76 +194,58 @@ contract FillQuoteTransformer is
|
||||
}
|
||||
|
||||
state.takerTokenBalanceRemaining = data.sellToken.getTokenBalanceOf(address(this));
|
||||
if (data.side == Side.Sell && data.fillAmount == MAX_UINT256) {
|
||||
// If `sellAmount == -1 then we are selling
|
||||
// the entire balance of `sellToken`. This is useful in cases where
|
||||
// the exact sell amount is not exactly known in advance, like when
|
||||
// unwrapping Chai/cUSDC/cDAI.
|
||||
data.fillAmount = state.takerTokenBalanceRemaining;
|
||||
if (data.side == Side.Sell) {
|
||||
data.fillAmount = _normalizeFillAmount(data.fillAmount, state.takerTokenBalanceRemaining);
|
||||
}
|
||||
|
||||
// Approve the ERC20 proxy to spend `sellToken`.
|
||||
data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
|
||||
// Approve the exchange proxy to spend our sell tokens if native orders
|
||||
// are present.
|
||||
if (data.limitOrders.length + data.rfqOrders.length != 0) {
|
||||
data.sellToken.approveIfBelow(address(zeroEx), data.fillAmount);
|
||||
// Compute the protocol fee if a limit order is present.
|
||||
if (data.limitOrders.length != 0) {
|
||||
state.protocolFee = uint256(zeroEx.getProtocolFeeMultiplier())
|
||||
.safeMul(tx.gasprice);
|
||||
}
|
||||
}
|
||||
|
||||
state.protocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
|
||||
state.ethRemaining = address(this).balance;
|
||||
// RFQT orders can only be filled if the actual taker matches the RFQT
|
||||
// taker (if set).
|
||||
state.isRfqtAllowed = data.rfqtTakerAddress == address(0)
|
||||
|| context.taker == data.rfqtTakerAddress;
|
||||
|
||||
// Fill the orders.
|
||||
for (uint256 i = 0; i < data.orders.length; ++i) {
|
||||
for (uint256 i = 0; i < data.fillSequence.length; ++i) {
|
||||
// Check if we've hit our targets.
|
||||
if (data.side == Side.Sell) {
|
||||
// Market sell check.
|
||||
if (state.soldAmount >= data.fillAmount) {
|
||||
break;
|
||||
}
|
||||
if (state.soldAmount >= data.fillAmount) { break; }
|
||||
} else {
|
||||
// Market buy check.
|
||||
if (state.boughtAmount >= data.fillAmount) {
|
||||
break;
|
||||
}
|
||||
if (state.boughtAmount >= data.fillAmount) { break; }
|
||||
}
|
||||
|
||||
state.currentOrderType = OrderType(data.fillSequence[i]);
|
||||
uint256 orderIndex = state.currentIndices[uint256(state.currentOrderType)];
|
||||
// Fill the order.
|
||||
FillOrderResults memory results;
|
||||
if (data.side == Side.Sell) {
|
||||
// Market sell.
|
||||
results = _sellToOrder(
|
||||
data.buyToken,
|
||||
data.sellToken,
|
||||
data.orders[i],
|
||||
data.signatures[i],
|
||||
data.fillAmount.safeSub(state.soldAmount).min256(
|
||||
data.maxOrderFillAmounts.length > i
|
||||
? data.maxOrderFillAmounts[i]
|
||||
: MAX_UINT256
|
||||
),
|
||||
state
|
||||
);
|
||||
if (state.currentOrderType == OrderType.Bridge) {
|
||||
results = _fillBridgeOrder(data.bridgeOrders[orderIndex], data, state);
|
||||
} else if (state.currentOrderType == OrderType.Limit) {
|
||||
results = _fillLimitOrder(data.limitOrders[orderIndex], data, state);
|
||||
} else if (state.currentOrderType == OrderType.Rfq) {
|
||||
results = _fillRfqOrder(data.rfqOrders[orderIndex], data, state);
|
||||
} else {
|
||||
// Market buy.
|
||||
results = _buyFromOrder(
|
||||
data.buyToken,
|
||||
data.sellToken,
|
||||
data.orders[i],
|
||||
data.signatures[i],
|
||||
data.fillAmount.safeSub(state.boughtAmount).min256(
|
||||
data.maxOrderFillAmounts.length > i
|
||||
? data.maxOrderFillAmounts[i]
|
||||
: MAX_UINT256
|
||||
),
|
||||
state
|
||||
);
|
||||
revert("INVALID_ORDER_TYPE");
|
||||
}
|
||||
|
||||
// Accumulate totals.
|
||||
state.soldAmount = state.soldAmount.safeAdd(results.takerTokenSoldAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(results.makerTokenBoughtAmount);
|
||||
state.ethRemaining = state.ethRemaining.safeSub(results.protocolFeePaid);
|
||||
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining.safeSub(results.takerTokenSoldAmount);
|
||||
state.soldAmount = state.soldAmount
|
||||
.safeAdd(results.takerTokenSoldAmount);
|
||||
state.boughtAmount = state.boughtAmount
|
||||
.safeAdd(results.makerTokenBoughtAmount);
|
||||
state.ethRemaining = state.ethRemaining
|
||||
.safeSub(results.protocolFeePaid);
|
||||
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining
|
||||
.safeSub(results.takerTokenSoldAmount);
|
||||
state.currentIndices[uint256(state.currentOrderType)]++;
|
||||
}
|
||||
|
||||
// Ensure we hit our targets.
|
||||
@@ -276,223 +284,174 @@ contract FillQuoteTransformer is
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Try to sell up to `sellAmount` from an order.
|
||||
/// @param makerToken The maker/buy token.
|
||||
/// @param takerToken The taker/sell token.
|
||||
/// @param order The order to fill.
|
||||
/// @param signature The signature for `order`.
|
||||
/// @param sellAmount Amount of taker token to sell.
|
||||
/// @param state Intermediate state variables to get around stack limits.
|
||||
function _sellToOrder(
|
||||
IERC20TokenV06 makerToken,
|
||||
IERC20TokenV06 takerToken,
|
||||
IExchange.Order memory order,
|
||||
bytes memory signature,
|
||||
uint256 sellAmount,
|
||||
// Fill a single bridge order.
|
||||
function _fillBridgeOrder(
|
||||
IBridgeAdapter.BridgeOrder memory order,
|
||||
TransformData memory data,
|
||||
FillState memory state
|
||||
)
|
||||
private
|
||||
returns (FillOrderResults memory results)
|
||||
{
|
||||
IERC20TokenV06 takerFeeToken =
|
||||
_getTokenFromERC20AssetData(order.takerFeeAssetData);
|
||||
|
||||
uint256 takerTokenFillAmount = sellAmount;
|
||||
|
||||
if (order.takerFee != 0) {
|
||||
if (takerFeeToken == makerToken) {
|
||||
// Taker fee is payable in the maker token, so we need to
|
||||
// approve the proxy to spend the maker token.
|
||||
// It isn't worth computing the actual taker fee
|
||||
// since `approveIfBelow()` will set the allowance to infinite. We
|
||||
// just need a reasonable upper bound to avoid unnecessarily re-approving.
|
||||
takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
|
||||
} else if (takerFeeToken == takerToken){
|
||||
// Taker fee is payable in the taker token, so we need to
|
||||
// reduce the fill amount to cover the fee.
|
||||
// takerTokenFillAmount' =
|
||||
// (takerTokenFillAmount * order.takerAssetAmount) /
|
||||
// (order.takerAssetAmount + order.takerFee)
|
||||
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
|
||||
order.takerAssetAmount,
|
||||
order.takerAssetAmount.safeAdd(order.takerFee),
|
||||
sellAmount
|
||||
);
|
||||
} else {
|
||||
// Only support taker or maker asset denominated taker fees.
|
||||
LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
|
||||
address(takerFeeToken)
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the fill.
|
||||
return _fillOrder(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
uint256 takerTokenFillAmount = _computeTakerTokenFillAmount(
|
||||
data,
|
||||
state,
|
||||
takerFeeToken == takerToken
|
||||
order.takerTokenAmount,
|
||||
order.makerTokenAmount,
|
||||
0
|
||||
);
|
||||
|
||||
(bool success, bytes memory resultData) = address(bridgeAdapter).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IBridgeAdapter.trade.selector,
|
||||
order,
|
||||
data.sellToken,
|
||||
data.buyToken,
|
||||
takerTokenFillAmount
|
||||
)
|
||||
);
|
||||
if (success) {
|
||||
results.makerTokenBoughtAmount = abi.decode(resultData, (uint256));
|
||||
results.takerTokenSoldAmount = takerTokenFillAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Try to buy up to `buyAmount` from an order.
|
||||
/// @param makerToken The maker/buy token.
|
||||
/// @param takerToken The taker/sell token.
|
||||
/// @param order The order to fill.
|
||||
/// @param signature The signature for `order`.
|
||||
/// @param buyAmount Amount of maker token to buy.
|
||||
/// @param state Intermediate state variables to get around stack limits.
|
||||
function _buyFromOrder(
|
||||
IERC20TokenV06 makerToken,
|
||||
IERC20TokenV06 takerToken,
|
||||
IExchange.Order memory order,
|
||||
bytes memory signature,
|
||||
uint256 buyAmount,
|
||||
// Fill a single limit order.
|
||||
function _fillLimitOrder(
|
||||
LimitOrderInfo memory orderInfo,
|
||||
TransformData memory data,
|
||||
FillState memory state
|
||||
)
|
||||
private
|
||||
returns (FillOrderResults memory results)
|
||||
{
|
||||
IERC20TokenV06 takerFeeToken =
|
||||
_getTokenFromERC20AssetData(order.takerFeeAssetData);
|
||||
// Compute the default taker token fill amount.
|
||||
uint256 takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
|
||||
buyAmount,
|
||||
order.makerAssetAmount,
|
||||
order.takerAssetAmount
|
||||
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
|
||||
_computeTakerTokenFillAmount(
|
||||
data,
|
||||
state,
|
||||
orderInfo.order.takerAmount,
|
||||
orderInfo.order.makerAmount,
|
||||
orderInfo.order.takerTokenFeeAmount
|
||||
),
|
||||
orderInfo.maxTakerTokenFillAmount
|
||||
);
|
||||
|
||||
if (order.takerFee != 0) {
|
||||
if (takerFeeToken == makerToken) {
|
||||
// Taker fee is payable in the maker token.
|
||||
// Adjust the taker token fill amount to account for maker
|
||||
// tokens being lost to the taker fee.
|
||||
// takerTokenFillAmount' =
|
||||
// (order.takerAssetAmount * buyAmount) /
|
||||
// (order.makerAssetAmount - order.takerFee)
|
||||
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
|
||||
buyAmount,
|
||||
order.makerAssetAmount.safeSub(order.takerFee),
|
||||
order.takerAssetAmount
|
||||
);
|
||||
// Approve the proxy to spend the maker token.
|
||||
// It isn't worth computing the actual taker fee
|
||||
// since `approveIfBelow()` will set the allowance to infinite. We
|
||||
// just need a reasonable upper bound to avoid unnecessarily re-approving.
|
||||
takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
|
||||
} else if (takerFeeToken != takerToken) {
|
||||
// Only support taker or maker asset denominated taker fees.
|
||||
LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
|
||||
address(takerFeeToken)
|
||||
).rrevert();
|
||||
}
|
||||
// Emit an event if we do not have sufficient ETH to cover the protocol fee.
|
||||
if (state.ethRemaining < state.protocolFee) {
|
||||
bytes32 orderHash = zeroEx.getLimitOrderHash(orderInfo.order);
|
||||
emit ProtocolFeeUnfunded(orderHash);
|
||||
return results; // Empty results.
|
||||
}
|
||||
|
||||
// Perform the fill.
|
||||
return _fillOrder(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
state,
|
||||
takerFeeToken == takerToken
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Attempt to fill an order. If the fill reverts, the revert will be
|
||||
/// swallowed and `results` will be zeroed out.
|
||||
/// @param order The order to fill.
|
||||
/// @param signature The order signature.
|
||||
/// @param takerAssetFillAmount How much taker asset to fill.
|
||||
/// @param state Intermediate state variables to get around stack limits.
|
||||
/// @param isTakerFeeInTakerToken Whether the taker fee token is the same as the
|
||||
/// taker token.
|
||||
function _fillOrder(
|
||||
IExchange.Order memory order,
|
||||
bytes memory signature,
|
||||
uint256 takerAssetFillAmount,
|
||||
FillState memory state,
|
||||
bool isTakerFeeInTakerToken
|
||||
)
|
||||
private
|
||||
returns (FillOrderResults memory results)
|
||||
{
|
||||
// Clamp to remaining taker asset amount or order size.
|
||||
uint256 availableTakerAssetFillAmount =
|
||||
takerAssetFillAmount.min256(order.takerAssetAmount);
|
||||
availableTakerAssetFillAmount =
|
||||
availableTakerAssetFillAmount.min256(state.takerTokenBalanceRemaining);
|
||||
|
||||
// If it is a Bridge order we fill this directly through the BridgeAdapter
|
||||
if (order.makerAssetData.readBytes4(0) == ERC20_BRIDGE_PROXY_ID) {
|
||||
(bool success, bytes memory resultData) = address(bridgeAdapter).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IBridgeAdapter.trade.selector,
|
||||
order.makerAssetData,
|
||||
address(_getTokenFromERC20AssetData(order.takerAssetData)),
|
||||
availableTakerAssetFillAmount
|
||||
try
|
||||
zeroEx.fillLimitOrder
|
||||
{value: state.protocolFee}
|
||||
(
|
||||
orderInfo.order,
|
||||
orderInfo.signature,
|
||||
takerTokenFillAmount.safeDowncastToUint128()
|
||||
)
|
||||
);
|
||||
if (success) {
|
||||
results.makerTokenBoughtAmount = abi.decode(resultData, (uint256));
|
||||
results.takerTokenSoldAmount = availableTakerAssetFillAmount;
|
||||
// protocol fee paid remains 0
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
// If the order taker address is set to this contract's address then
|
||||
// this is an RFQT order, and we will only fill it if allowed to.
|
||||
if (order.takerAddress == address(this) && !state.isRfqtAllowed) {
|
||||
return results; // Empty results.
|
||||
}
|
||||
// Emit an event if we do not have sufficient ETH to cover the protocol fee.
|
||||
if (state.ethRemaining < state.protocolFee) {
|
||||
bytes32 orderHash = LibOrderHash.getTypedDataHash(
|
||||
order,
|
||||
exchange.EIP712_EXCHANGE_DOMAIN_HASH()
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
if (orderInfo.order.takerTokenFeeAmount > 0) {
|
||||
takerTokenFilledAmount = takerTokenFilledAmount.safeAdd128(
|
||||
LibMathV06.getPartialAmountFloor(
|
||||
takerTokenFilledAmount,
|
||||
orderInfo.order.takerAmount,
|
||||
orderInfo.order.takerTokenFeeAmount
|
||||
).safeDowncastToUint128()
|
||||
);
|
||||
emit ProtocolFeeUnfunded(orderHash);
|
||||
return results;
|
||||
}
|
||||
try
|
||||
exchange.fillOrder
|
||||
{value: state.protocolFee}
|
||||
(order, availableTakerAssetFillAmount, signature)
|
||||
returns (IExchange.FillResults memory fillResults)
|
||||
{
|
||||
results.makerTokenBoughtAmount = fillResults.makerAssetFilledAmount;
|
||||
results.takerTokenSoldAmount = fillResults.takerAssetFilledAmount;
|
||||
results.protocolFeePaid = fillResults.protocolFeePaid;
|
||||
// If the taker fee is payable in the taker asset, include the
|
||||
// taker fee in the total amount sold.
|
||||
if (isTakerFeeInTakerToken) {
|
||||
results.takerTokenSoldAmount =
|
||||
results.takerTokenSoldAmount.safeAdd(fillResults.takerFeePaid);
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
}
|
||||
}
|
||||
results.takerTokenSoldAmount = takerTokenFilledAmount;
|
||||
results.makerTokenBoughtAmount = makerTokenFilledAmount;
|
||||
results.protocolFeePaid = state.protocolFee;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/// @dev Extract the token from plain ERC20 asset data.
|
||||
/// If the asset-data is empty, a zero token address will be returned.
|
||||
/// @param assetData The order asset data.
|
||||
function _getTokenFromERC20AssetData(bytes memory assetData)
|
||||
// Fill a single RFQ order.
|
||||
function _fillRfqOrder(
|
||||
RfqOrderInfo memory orderInfo,
|
||||
TransformData memory data,
|
||||
FillState memory state
|
||||
)
|
||||
private
|
||||
returns (FillOrderResults memory results)
|
||||
{
|
||||
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
|
||||
_computeTakerTokenFillAmount(
|
||||
data,
|
||||
state,
|
||||
orderInfo.order.takerAmount,
|
||||
orderInfo.order.makerAmount,
|
||||
0
|
||||
),
|
||||
orderInfo.maxTakerTokenFillAmount
|
||||
);
|
||||
|
||||
try
|
||||
zeroEx.fillRfqOrder
|
||||
(
|
||||
orderInfo.order,
|
||||
orderInfo.signature,
|
||||
takerTokenFillAmount.safeDowncastToUint128()
|
||||
)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
results.takerTokenSoldAmount = takerTokenFilledAmount;
|
||||
results.makerTokenBoughtAmount = makerTokenFilledAmount;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Compute the next taker token fill amount of a generic order.
|
||||
function _computeTakerTokenFillAmount(
|
||||
TransformData memory data,
|
||||
FillState memory state,
|
||||
uint256 orderTakerAmount,
|
||||
uint256 orderMakerAmount,
|
||||
uint256 orderTakerTokenFeeAmount
|
||||
)
|
||||
private
|
||||
pure
|
||||
returns (IERC20TokenV06 token)
|
||||
returns (uint256 takerTokenFillAmount)
|
||||
{
|
||||
if (assetData.length == 0) {
|
||||
return IERC20TokenV06(address(0));
|
||||
if (data.side == Side.Sell) {
|
||||
takerTokenFillAmount = data.fillAmount.safeSub(state.soldAmount);
|
||||
if (orderTakerTokenFeeAmount != 0) {
|
||||
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
|
||||
takerTokenFillAmount,
|
||||
orderTakerAmount.safeAdd(orderTakerTokenFeeAmount),
|
||||
orderTakerAmount
|
||||
);
|
||||
}
|
||||
} else { // Buy
|
||||
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
|
||||
data.fillAmount.safeSub(state.boughtAmount),
|
||||
orderMakerAmount,
|
||||
orderTakerAmount
|
||||
);
|
||||
}
|
||||
if (assetData.length != 36 ||
|
||||
LibBytesV06.readBytes4(assetData, 0) != ERC20_ASSET_PROXY_ID)
|
||||
{
|
||||
LibTransformERC20RichErrors
|
||||
.InvalidERC20AssetDataError(assetData)
|
||||
.rrevert();
|
||||
return LibSafeMathV06.min256(
|
||||
LibSafeMathV06.min256(takerTokenFillAmount, orderTakerAmount),
|
||||
state.takerTokenBalanceRemaining
|
||||
);
|
||||
}
|
||||
|
||||
// Convert possible proportional values to absolute quantities.
|
||||
function _normalizeFillAmount(uint256 rawAmount, uint256 balance)
|
||||
private
|
||||
pure
|
||||
returns (uint256 normalized)
|
||||
{
|
||||
if ((rawAmount & HIGH_BIT) == HIGH_BIT) {
|
||||
// If the high bit of `rawAmount` is set then the lower 255 bits
|
||||
// specify a fraction of `balance`.
|
||||
return LibSafeMathV06.min256(
|
||||
balance
|
||||
* LibSafeMathV06.min256(rawAmount & LOWER_255_BITS, 1e18)
|
||||
/ 1e18,
|
||||
balance
|
||||
);
|
||||
}
|
||||
return IERC20TokenV06(LibBytesV06.readAddress(assetData, 16));
|
||||
return rawAmount;
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,8 @@
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./mixins/MixinAdapterAddresses.sol";
|
||||
import "./IBridgeAdapter.sol";
|
||||
import "./BridgeSource.sol";
|
||||
import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinCoFiX.sol";
|
||||
@@ -38,7 +39,7 @@ import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract BridgeAdapter is
|
||||
MixinAdapterAddresses,
|
||||
IBridgeAdapter,
|
||||
MixinBalancer,
|
||||
MixinBancor,
|
||||
MixinCoFiX,
|
||||
@@ -55,203 +56,146 @@ contract BridgeAdapter is
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
|
||||
/// @dev Emitted when a trade occurs.
|
||||
/// @param inputToken The token the bridge is converting from.
|
||||
/// @param outputToken The token the bridge is converting to.
|
||||
/// @param inputTokenAmount Amount of input token.
|
||||
/// @param outputTokenAmount Amount of output token.
|
||||
/// @param from The bridge address, indicating the underlying source of the fill.
|
||||
/// @param to The `to` address, currrently `address(this)`
|
||||
event ERC20BridgeTransfer(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount,
|
||||
address from,
|
||||
address to
|
||||
);
|
||||
|
||||
address private immutable BALANCER_BRIDGE_ADDRESS;
|
||||
address private immutable BANCOR_BRIDGE_ADDRESS;
|
||||
address private immutable COFIX_BRIDGE_ADDRESS;
|
||||
address private immutable CREAM_BRIDGE_ADDRESS;
|
||||
address private immutable CURVE_BRIDGE_ADDRESS;
|
||||
address private immutable CRYPTO_COM_BRIDGE_ADDRESS;
|
||||
address private immutable DODO_BRIDGE_ADDRESS;
|
||||
address private immutable KYBER_BRIDGE_ADDRESS;
|
||||
address private immutable MOONISWAP_BRIDGE_ADDRESS;
|
||||
address private immutable MSTABLE_BRIDGE_ADDRESS;
|
||||
address private immutable OASIS_BRIDGE_ADDRESS;
|
||||
address private immutable SHELL_BRIDGE_ADDRESS;
|
||||
address private immutable SNOW_SWAP_BRIDGE_ADDRESS;
|
||||
address private immutable SUSHISWAP_BRIDGE_ADDRESS;
|
||||
address private immutable SWERVE_BRIDGE_ADDRESS;
|
||||
address private immutable UNISWAP_BRIDGE_ADDRESS;
|
||||
address private immutable UNISWAP_V2_BRIDGE_ADDRESS;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinBalancer()
|
||||
MixinBancor(addresses)
|
||||
MixinBancor(weth)
|
||||
MixinCoFiX()
|
||||
MixinCurve()
|
||||
MixinCryptoCom()
|
||||
MixinDodo(addresses)
|
||||
MixinKyber(addresses)
|
||||
MixinMooniswap(addresses)
|
||||
MixinMStable(addresses)
|
||||
MixinOasis(addresses)
|
||||
MixinDodo()
|
||||
MixinKyber(weth)
|
||||
MixinMooniswap(weth)
|
||||
MixinMStable()
|
||||
MixinOasis()
|
||||
MixinShell()
|
||||
MixinSushiswap(addresses)
|
||||
MixinUniswap(addresses)
|
||||
MixinUniswapV2(addresses)
|
||||
MixinSushiswap()
|
||||
MixinUniswap(weth)
|
||||
MixinUniswapV2()
|
||||
MixinZeroExBridge()
|
||||
{
|
||||
BALANCER_BRIDGE_ADDRESS = addresses.balancerBridge;
|
||||
BANCOR_BRIDGE_ADDRESS = addresses.bancorBridge;
|
||||
COFIX_BRIDGE_ADDRESS = addresses.cofixBridge;
|
||||
CURVE_BRIDGE_ADDRESS = addresses.curveBridge;
|
||||
CRYPTO_COM_BRIDGE_ADDRESS = addresses.cryptoComBridge;
|
||||
KYBER_BRIDGE_ADDRESS = addresses.kyberBridge;
|
||||
MOONISWAP_BRIDGE_ADDRESS = addresses.mooniswapBridge;
|
||||
MSTABLE_BRIDGE_ADDRESS = addresses.mStableBridge;
|
||||
OASIS_BRIDGE_ADDRESS = addresses.oasisBridge;
|
||||
SHELL_BRIDGE_ADDRESS = addresses.shellBridge;
|
||||
SUSHISWAP_BRIDGE_ADDRESS = addresses.sushiswapBridge;
|
||||
SWERVE_BRIDGE_ADDRESS = addresses.swerveBridge;
|
||||
UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge;
|
||||
UNISWAP_V2_BRIDGE_ADDRESS = addresses.uniswapV2Bridge;
|
||||
CREAM_BRIDGE_ADDRESS = addresses.creamBridge;
|
||||
SNOW_SWAP_BRIDGE_ADDRESS = addresses.snowSwapBridge;
|
||||
DODO_BRIDGE_ADDRESS = addresses.dodoBridge;
|
||||
}
|
||||
{}
|
||||
|
||||
function trade(
|
||||
bytes calldata makerAssetData,
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(
|
||||
IERC20TokenV06 buyToken,
|
||||
address bridgeAddress,
|
||||
bytes memory bridgeData
|
||||
) = abi.decode(
|
||||
makerAssetData[4:],
|
||||
(IERC20TokenV06, address, bytes)
|
||||
);
|
||||
require(
|
||||
bridgeAddress != address(this) && bridgeAddress != address(0),
|
||||
"BridgeAdapter/INVALID_BRIDGE_ADDRESS"
|
||||
);
|
||||
|
||||
if (bridgeAddress == CURVE_BRIDGE_ADDRESS ||
|
||||
bridgeAddress == SWERVE_BRIDGE_ADDRESS ||
|
||||
bridgeAddress == SNOW_SWAP_BRIDGE_ADDRESS) {
|
||||
if (order.source == BridgeSource.CURVE ||
|
||||
order.source == BridgeSource.SWERVE ||
|
||||
order.source == BridgeSource.SNOWSWAP) {
|
||||
boughtAmount = _tradeCurve(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == SUSHISWAP_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeSushiswap(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == UNISWAP_V2_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == UNISWAP_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeUniswap(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS ||
|
||||
bridgeAddress == CREAM_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeBalancer(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == KYBER_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeKyber(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == MOONISWAP_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeMooniswap(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == MSTABLE_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeMStable(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == OASIS_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeOasis(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == SHELL_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeShell(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == DODO_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeDodo(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == CRYPTO_COM_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeCryptoCom(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == BANCOR_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeBancor(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else if (bridgeAddress == COFIX_BRIDGE_ADDRESS) {
|
||||
boughtAmount = _tradeCoFiX(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
);
|
||||
} else {
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
bridgeAddress,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
bridgeData
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.SUSHISWAP) {
|
||||
boughtAmount = _tradeSushiswap(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.UNISWAPV2) {
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.UNISWAP) {
|
||||
boughtAmount = _tradeUniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.BALANCER ||
|
||||
order.source == BridgeSource.CREAM) {
|
||||
boughtAmount = _tradeBalancer(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.KYBER) {
|
||||
boughtAmount = _tradeKyber(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.MOONISWAP) {
|
||||
boughtAmount = _tradeMooniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.MSTABLE) {
|
||||
boughtAmount = _tradeMStable(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.OASIS) {
|
||||
boughtAmount = _tradeOasis(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.SHELL) {
|
||||
boughtAmount = _tradeShell(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.DODO) {
|
||||
boughtAmount = _tradeDodo(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.CRYPTOCOM) {
|
||||
boughtAmount = _tradeCryptoCom(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.BANCOR) {
|
||||
boughtAmount = _tradeBancor(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.COFIX) {
|
||||
boughtAmount = _tradeCoFiX(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else {
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit ERC20BridgeTransfer(
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount,
|
||||
bridgeAddress,
|
||||
address(this)
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
library BridgeSource {
|
||||
uint256 constant internal BALANCER = 0;
|
||||
uint256 constant internal BANCOR = 1;
|
||||
uint256 constant internal COFIX = 2;
|
||||
uint256 constant internal CURVE = 3;
|
||||
uint256 constant internal CREAM = 4;
|
||||
uint256 constant internal CRYPTOCOM = 5;
|
||||
uint256 constant internal DODO = 6;
|
||||
uint256 constant internal KYBER = 7;
|
||||
uint256 constant internal LIQUIDITYPROVIDER = 8;
|
||||
uint256 constant internal MOONISWAP = 9;
|
||||
uint256 constant internal MSTABLE = 10;
|
||||
uint256 constant internal OASIS = 11;
|
||||
uint256 constant internal SHELL = 12;
|
||||
uint256 constant internal SNOWSWAP = 13;
|
||||
uint256 constant internal SUSHISWAP = 14;
|
||||
uint256 constant internal SWERVE = 15;
|
||||
uint256 constant internal UNISWAP = 16;
|
||||
uint256 constant internal UNISWAPV2 = 17;
|
||||
// New sources should be APPENDED to this list, taking the next highest
|
||||
// integer value.
|
||||
}
|
@@ -18,12 +18,38 @@
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
interface IBridgeAdapter {
|
||||
|
||||
struct BridgeOrder {
|
||||
uint256 source;
|
||||
uint256 takerTokenAmount;
|
||||
uint256 makerTokenAmount;
|
||||
bytes bridgeData;
|
||||
}
|
||||
|
||||
/// @dev Emitted when tokens are swapped with an external source.
|
||||
/// @param source The unique ID for the source. See `BridgeSource.sol`
|
||||
/// @param inputToken The token the bridge is converting from.
|
||||
/// @param outputToken The token the bridge is converting to.
|
||||
/// @param inputTokenAmount Amount of input token sold.
|
||||
/// @param outputTokenAmount Amount of output token bought.
|
||||
event BridgeFill(
|
||||
uint256 source,
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount
|
||||
);
|
||||
|
||||
function trade(
|
||||
bytes calldata makerAssetData,
|
||||
address fromTokenAddress,
|
||||
BridgeOrder calldata order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
|
@@ -1,55 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
|
||||
contract MixinAdapterAddresses
|
||||
{
|
||||
|
||||
struct AdapterAddresses {
|
||||
// Bridges
|
||||
address balancerBridge;
|
||||
address bancorBridge;
|
||||
address cofixBridge;
|
||||
address creamBridge;
|
||||
address curveBridge;
|
||||
address cryptoComBridge;
|
||||
address dodoBridge;
|
||||
address kyberBridge;
|
||||
address mooniswapBridge;
|
||||
address mStableBridge;
|
||||
address oasisBridge;
|
||||
address shellBridge;
|
||||
address snowSwapBridge;
|
||||
address swerveBridge;
|
||||
address sushiswapBridge;
|
||||
address uniswapBridge;
|
||||
address uniswapV2Bridge;
|
||||
// Exchanges
|
||||
address kyberNetworkProxy;
|
||||
address oasis;
|
||||
address sushiswapRouter;
|
||||
address uniswapV2Router;
|
||||
address uniswapExchangeFactory;
|
||||
address mStable;
|
||||
address dodoHelper;
|
||||
// Other
|
||||
address weth;
|
||||
}
|
||||
}
|
@@ -46,6 +46,7 @@ contract MixinBalancer {
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
function _tradeBalancer(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -54,9 +55,9 @@ contract MixinBalancer {
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data.
|
||||
(IERC20TokenV06 sellToken, IBalancerPool pool) = abi.decode(
|
||||
(IBalancerPool pool) = abi.decode(
|
||||
bridgeData,
|
||||
(IERC20TokenV06, IBalancerPool)
|
||||
(IBalancerPool)
|
||||
);
|
||||
sellToken.approveIfBelow(
|
||||
address(pool),
|
||||
|
@@ -24,12 +24,12 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
|
||||
interface IBancorNetwork {
|
||||
function convertByPath(
|
||||
address[] calldata _path,
|
||||
IERC20TokenV06[] calldata _path,
|
||||
uint256 _amount,
|
||||
uint256 _minReturn,
|
||||
address _beneficiary,
|
||||
@@ -42,17 +42,17 @@ interface IBancorNetwork {
|
||||
}
|
||||
|
||||
|
||||
contract MixinBancor is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinBancor {
|
||||
|
||||
/// @dev Bancor ETH pseudo-address.
|
||||
address constant public BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
IERC20TokenV06 constant public BANCOR_ETH_ADDRESS =
|
||||
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = IEtherTokenV06(addresses.weth);
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeBancor(
|
||||
@@ -64,17 +64,22 @@ contract MixinBancor is
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data.
|
||||
(
|
||||
address[] memory path,
|
||||
address bancorNetworkAddress
|
||||
// solhint-disable indent
|
||||
) = abi.decode(bridgeData, (address[], address));
|
||||
// solhint-enable indent
|
||||
IBancorNetwork bancorNetworkAddress;
|
||||
IERC20TokenV06[] memory path;
|
||||
{
|
||||
address[] memory _path;
|
||||
(
|
||||
bancorNetworkAddress,
|
||||
_path
|
||||
) = abi.decode(bridgeData, (IBancorNetwork, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "MixinBancor/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == address(buyToken) ||
|
||||
(path[path.length - 1] == BANCOR_ETH_ADDRESS && address(buyToken) == address(WETH)),
|
||||
path[path.length - 1] == buyToken ||
|
||||
(path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == WETH),
|
||||
"MixinBancor/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
|
||||
@@ -88,14 +93,14 @@ contract MixinBancor is
|
||||
} else {
|
||||
// Grant an allowance to the Bancor Network.
|
||||
LibERC20TokenV06.approveIfBelow(
|
||||
IERC20TokenV06(path[0]),
|
||||
bancorNetworkAddress,
|
||||
path[0],
|
||||
address(bancorNetworkAddress),
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
|
||||
// Convert the tokens
|
||||
boughtAmount = IBancorNetwork(bancorNetworkAddress).convertByPath{value: payableAmount}(
|
||||
boughtAmount = bancorNetworkAddress.convertByPath{value: payableAmount}(
|
||||
path, // path originating with source token and terminating in destination token
|
||||
sellAmount, // amount of source token to trade
|
||||
1, // minimum amount of destination token expected to receive
|
||||
|
@@ -23,7 +23,6 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
|
||||
|
||||
interface ICoFiXRouter {
|
||||
@@ -53,15 +52,20 @@ interface ICoFiXPair {
|
||||
function swapWithExact(address outToken, address to)
|
||||
external
|
||||
payable
|
||||
returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo);
|
||||
returns (
|
||||
uint amountIn,
|
||||
uint amountOut,
|
||||
uint oracleFeeChange,
|
||||
uint256[4] memory tradeInfo
|
||||
);
|
||||
}
|
||||
|
||||
contract MixinCoFiX is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinCoFiX {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
function _tradeCoFiX(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -69,15 +73,16 @@ contract MixinCoFiX is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(address fromTokenAddress, uint256 fee, address pool) = abi.decode(bridgeData, (address, uint256, address));
|
||||
(uint256 fee, ICoFiXPair pool) = abi.decode(bridgeData, (uint256, ICoFiXPair));
|
||||
// Transfer tokens into the pool
|
||||
LibERC20TokenV06.compatTransfer(
|
||||
IERC20TokenV06(fromTokenAddress),
|
||||
pool,
|
||||
sellAmount);
|
||||
sellToken,
|
||||
address(pool),
|
||||
sellAmount
|
||||
);
|
||||
// Call the swap exact with the tokens now in the pool
|
||||
// pay the NEST Oracle fee with ETH
|
||||
(/* In */, boughtAmount, , ) = ICoFiXPair(pool).swapWithExact{value: fee}(
|
||||
(/* In */, boughtAmount, , ) = pool.swapWithExact{value: fee}(
|
||||
address(buyToken),
|
||||
address(this)
|
||||
);
|
||||
|
@@ -37,21 +37,24 @@ contract MixinCryptoCom
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// solhint-disable indent
|
||||
address[] memory path;
|
||||
address router;
|
||||
(path, router) = abi.decode(bridgeData, (address[], address));
|
||||
// solhint-enable indent
|
||||
IUniswapV2Router02 router;
|
||||
IERC20TokenV06[] memory path;
|
||||
{
|
||||
address[] memory _path;
|
||||
(router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(path.length >= 2, "MixinCryptoCom/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == address(buyToken),
|
||||
"CryptoComBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
path[path.length - 1] == buyToken,
|
||||
"MixinCryptoCom/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
// Grant the CryptoCom router an allowance to sell the first token.
|
||||
IERC20TokenV06(path[0]).approveIfBelow(router, sellAmount);
|
||||
path[0].approveIfBelow(address(router), sellAmount);
|
||||
|
||||
uint[] memory amounts = IUniswapV2Router02(router).swapExactTokensForTokens(
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
|
@@ -35,12 +35,12 @@ contract MixinCurve {
|
||||
struct CurveBridgeData {
|
||||
address curveAddress;
|
||||
bytes4 exchangeFunctionSelector;
|
||||
IERC20TokenV06 sellToken;
|
||||
int128 fromCoinIdx;
|
||||
int128 toCoinIdx;
|
||||
}
|
||||
|
||||
function _tradeCurve(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -50,7 +50,7 @@ contract MixinCurve {
|
||||
{
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
||||
data.sellToken.approveIfBelow(data.curveAddress, sellAmount);
|
||||
sellToken.approveIfBelow(data.curveAddress, sellAmount);
|
||||
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||
(bool success, bytes memory resultData) =
|
||||
data.curveAddress.call(abi.encodeWithSelector(
|
||||
|
@@ -23,55 +23,60 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
|
||||
interface IDODOHelper {
|
||||
|
||||
function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
|
||||
}
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
|
||||
interface IDODO {
|
||||
function sellBaseToken(
|
||||
uint256 amount,
|
||||
uint256 minReceiveQuote,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns (uint256);
|
||||
|
||||
function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256);
|
||||
|
||||
function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256);
|
||||
|
||||
function buyBaseToken(
|
||||
uint256 amount,
|
||||
uint256 maxPayQuote,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract MixinDodo is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
interface IDODOHelper {
|
||||
function querySellQuoteToken(
|
||||
IDODO dodo,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract MixinDodo {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the `DOODO Helper` contract.
|
||||
IDODOHelper private immutable DODO_HELPER;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
public
|
||||
{
|
||||
DODO_HELPER = IDODOHelper(addresses.dodoHelper);
|
||||
}
|
||||
|
||||
function _tradeDodo(
|
||||
IERC20TokenV06 /* buyToken */,
|
||||
IERC20TokenV06 sellToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(address fromTokenAddress,
|
||||
address pool,
|
||||
bool isSellBase) = abi.decode(bridgeData, (address, address, bool));
|
||||
(IDODOHelper helper, IDODO pool, bool isSellBase) =
|
||||
abi.decode(bridgeData, (IDODOHelper, IDODO, bool));
|
||||
|
||||
// Grant the Dodo pool contract an allowance to sell the first token.
|
||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(pool, sellAmount);
|
||||
sellToken.approveIfBelow(address(pool), sellAmount);
|
||||
|
||||
if (isSellBase) {
|
||||
// Sell the Base token directly against the contract
|
||||
boughtAmount = IDODO(pool).sellBaseToken(
|
||||
boughtAmount = pool.sellBaseToken(
|
||||
// amount to sell
|
||||
sellAmount,
|
||||
// min receive amount
|
||||
@@ -80,11 +85,11 @@ contract MixinDodo is
|
||||
);
|
||||
} else {
|
||||
// Need to re-calculate the sell quote amount into buyBase
|
||||
boughtAmount = DODO_HELPER.querySellQuoteToken(
|
||||
boughtAmount = helper.querySellQuoteToken(
|
||||
pool,
|
||||
sellAmount
|
||||
);
|
||||
IDODO(pool).buyBaseToken(
|
||||
pool.buyBaseToken(
|
||||
// amount to buy
|
||||
boughtAmount,
|
||||
// max pay amount
|
||||
|
@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
interface IKyberNetworkProxy {
|
||||
|
||||
@@ -54,26 +54,24 @@ interface IKyberNetworkProxy {
|
||||
returns (uint256 boughtAmount);
|
||||
}
|
||||
|
||||
contract MixinKyber is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinKyber {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Address indicating the trade is using ETH
|
||||
address private immutable KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
IERC20TokenV06 private immutable KYBER_ETH_ADDRESS =
|
||||
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
/// @dev Mainnet address of the WETH contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
/// @dev Mainnet address of the KyberNetworkProxy contract.
|
||||
IKyberNetworkProxy private immutable KYBER_NETWORK_PROXY;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = IEtherTokenV06(addresses.weth);
|
||||
KYBER_NETWORK_PROXY = IKyberNetworkProxy(addresses.kyberNetworkProxy);
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeKyber(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -81,15 +79,15 @@ contract MixinKyber is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(IERC20TokenV06 sellToken, bytes memory hint) =
|
||||
abi.decode(bridgeData, (IERC20TokenV06, bytes));
|
||||
(IKyberNetworkProxy kyber, bytes memory hint) =
|
||||
abi.decode(bridgeData, (IKyberNetworkProxy, bytes));
|
||||
|
||||
uint256 payableAmount = 0;
|
||||
if (sellToken != WETH) {
|
||||
// If the input token is not WETH, grant an allowance to the exchange
|
||||
// to spend them.
|
||||
sellToken.approveIfBelow(
|
||||
address(KYBER_NETWORK_PROXY),
|
||||
address(kyber),
|
||||
sellAmount
|
||||
);
|
||||
} else {
|
||||
@@ -100,13 +98,13 @@ contract MixinKyber is
|
||||
|
||||
// Try to sell all of this contract's input token balance through
|
||||
// `KyberNetworkProxy.trade()`.
|
||||
boughtAmount = KYBER_NETWORK_PROXY.tradeWithHint{ value: payableAmount }(
|
||||
boughtAmount = kyber.tradeWithHint{ value: payableAmount }(
|
||||
// Input token.
|
||||
sellToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : sellToken,
|
||||
sellToken == WETH ? KYBER_ETH_ADDRESS : sellToken,
|
||||
// Sell amount.
|
||||
sellAmount,
|
||||
// Output token.
|
||||
buyToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : buyToken,
|
||||
buyToken == WETH ? KYBER_ETH_ADDRESS : buyToken,
|
||||
// Transfer to this contract
|
||||
address(uint160(address(this))),
|
||||
// Buy as much as possible.
|
||||
|
@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
|
||||
interface IMStable {
|
||||
@@ -37,21 +37,12 @@ interface IMStable {
|
||||
returns (uint256 boughtAmount);
|
||||
}
|
||||
|
||||
contract MixinMStable is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinMStable {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the mStable mUSD contract.
|
||||
IMStable private immutable MSTABLE;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
public
|
||||
{
|
||||
MSTABLE = IMStable(addresses.mStable);
|
||||
}
|
||||
|
||||
function _tradeMStable(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -59,12 +50,12 @@ contract MixinMStable is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data to get the `sellToken`.
|
||||
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||
// Grant an allowance to the exchange to spend `sellToken` token.
|
||||
sellToken.approveIfBelow(address(MSTABLE), sellAmount);
|
||||
(IMStable mstable) = abi.decode(bridgeData, (IMStable));
|
||||
|
||||
boughtAmount = MSTABLE.swap(
|
||||
// Grant an allowance to the exchange to spend `sellToken` token.
|
||||
sellToken.approveIfBelow(address(mstable), sellAmount);
|
||||
|
||||
boughtAmount = mstable.swap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
|
@@ -24,7 +24,7 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
|
||||
/// @dev Moooniswap pool interface.
|
||||
@@ -43,22 +43,22 @@ interface IMooniswapPool {
|
||||
}
|
||||
|
||||
/// @dev BridgeAdapter mixin for mooniswap.
|
||||
contract MixinMooniswap is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinMooniswap {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibERC20TokenV06 for IEtherTokenV06;
|
||||
|
||||
/// @dev WETH token.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = IEtherTokenV06(addresses.weth);
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeMooniswap(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -66,8 +66,7 @@ contract MixinMooniswap is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(IERC20TokenV06 sellToken, IMooniswapPool pool) =
|
||||
abi.decode(bridgeData, (IERC20TokenV06, IMooniswapPool));
|
||||
(IMooniswapPool pool) = abi.decode(bridgeData, (IMooniswapPool));
|
||||
|
||||
// Convert WETH to ETH.
|
||||
uint256 ethValue = 0;
|
||||
|
@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
interface IOasis {
|
||||
|
||||
@@ -42,21 +42,12 @@ interface IOasis {
|
||||
returns (uint256 boughtAmount);
|
||||
}
|
||||
|
||||
contract MixinOasis is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinOasis {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the Oasis `MatchingMarket` contract.
|
||||
IOasis private immutable OASIS;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
public
|
||||
{
|
||||
OASIS = IOasis(addresses.oasis);
|
||||
}
|
||||
|
||||
function _tradeOasis(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -64,15 +55,16 @@ contract MixinOasis is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data to get the `sellToken`.
|
||||
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||
|
||||
(IOasis oasis) = abi.decode(bridgeData, (IOasis));
|
||||
|
||||
// Grant an allowance to the exchange to spend `sellToken` token.
|
||||
sellToken.approveIfBelow(
|
||||
address(OASIS),
|
||||
address(oasis),
|
||||
sellAmount
|
||||
);
|
||||
// Try to sell all of this contract's `sellToken` token balance.
|
||||
boughtAmount = OASIS.sellAllAmount(
|
||||
boughtAmount = oasis.sellAllAmount(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
buyToken,
|
||||
|
@@ -23,13 +23,12 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
|
||||
interface IShell {
|
||||
|
||||
function originSwap(
|
||||
address from,
|
||||
address to,
|
||||
IERC20TokenV06 from,
|
||||
IERC20TokenV06 to,
|
||||
uint256 fromAmount,
|
||||
uint256 minTargetAmount,
|
||||
uint256 deadline
|
||||
@@ -38,12 +37,12 @@ interface IShell {
|
||||
returns (uint256 toAmount);
|
||||
}
|
||||
|
||||
contract MixinShell is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinShell {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
function _tradeShell(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -51,17 +50,17 @@ contract MixinShell is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(address fromTokenAddress, address pool) = abi.decode(bridgeData, (address, address));
|
||||
IShell pool = abi.decode(bridgeData, (IShell));
|
||||
|
||||
// Grant the Shell contract an allowance to sell the first token.
|
||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
||||
pool,
|
||||
IERC20TokenV06(sellToken).approveIfBelow(
|
||||
address(pool),
|
||||
sellAmount
|
||||
);
|
||||
|
||||
boughtAmount = IShell(pool).originSwap(
|
||||
fromTokenAddress,
|
||||
address(buyToken),
|
||||
boughtAmount = pool.originSwap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
|
@@ -23,23 +23,12 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "./MixinUniswapV2.sol";
|
||||
|
||||
contract MixinSushiswap is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinSushiswap {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the `SushiswapRouter` contract.
|
||||
IUniswapV2Router02 private immutable SUSHISWAP_ROUTER;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
public
|
||||
{
|
||||
SUSHISWAP_ROUTER = IUniswapV2Router02(addresses.sushiswapRouter);
|
||||
}
|
||||
|
||||
function _tradeSushiswap(
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
@@ -48,22 +37,28 @@ contract MixinSushiswap is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// solhint-disable indent
|
||||
address[] memory path = abi.decode(bridgeData, (address[]));
|
||||
// solhint-enable indent
|
||||
IERC20TokenV06[] memory path;
|
||||
IUniswapV2Router02 router;
|
||||
{
|
||||
address[] memory _path;
|
||||
(router, _path) =
|
||||
abi.decode(bridgeData, (IUniswapV2Router02, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "SushiswapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(path.length >= 2, "MixinSushiswap/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == address(buyToken),
|
||||
"SushiswapBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
path[path.length - 1] == buyToken,
|
||||
"MixinSushiswap/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
// Grant the Uniswap router an allowance to sell the first token.
|
||||
IERC20TokenV06(path[0]).approveIfBelow(
|
||||
address(SUSHISWAP_ROUTER),
|
||||
path[0].approveIfBelow(
|
||||
address(router),
|
||||
sellAmount
|
||||
);
|
||||
|
||||
uint[] memory amounts = SUSHISWAP_ROUTER.swapExactTokensForTokens(
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
|
@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
interface IUniswapExchangeFactory {
|
||||
|
||||
@@ -103,24 +103,21 @@ interface IUniswapExchange {
|
||||
returns (uint256 tokensBought);
|
||||
}
|
||||
|
||||
contract MixinUniswap is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinUniswap {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the WETH contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
/// @dev Mainnet address of the `UniswapExchangeFactory` contract.
|
||||
IUniswapExchangeFactory private immutable UNISWAP_EXCHANGE_FACTORY;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = IEtherTokenV06(addresses.weth);
|
||||
UNISWAP_EXCHANGE_FACTORY = IUniswapExchangeFactory(addresses.uniswapExchangeFactory);
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeUniswap(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
@@ -128,11 +125,12 @@ contract MixinUniswap is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data to get the `sellToken`.
|
||||
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||
IUniswapExchangeFactory exchangeFactory =
|
||||
abi.decode(bridgeData, (IUniswapExchangeFactory));
|
||||
|
||||
// Get the exchange for the token pair.
|
||||
IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
|
||||
exchangeFactory,
|
||||
sellToken,
|
||||
buyToken
|
||||
);
|
||||
@@ -197,10 +195,12 @@ contract MixinUniswap is
|
||||
/// @dev Retrieves the uniswap exchange for a given token pair.
|
||||
/// In the case of a WETH-token exchange, this will be the non-WETH token.
|
||||
/// In th ecase of a token-token exchange, this will be the first token.
|
||||
/// @param exchangeFactory The exchange factory.
|
||||
/// @param sellToken The address of the token we are converting from.
|
||||
/// @param buyToken The address of the token we are converting to.
|
||||
/// @return exchange The uniswap exchange.
|
||||
function _getUniswapExchangeForTokenPair(
|
||||
IUniswapExchangeFactory exchangeFactory,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken
|
||||
)
|
||||
@@ -210,8 +210,8 @@ contract MixinUniswap is
|
||||
{
|
||||
// Whichever isn't WETH is the exchange token.
|
||||
exchange = sellToken == WETH
|
||||
? UNISWAP_EXCHANGE_FACTORY.getExchange(buyToken)
|
||||
: UNISWAP_EXCHANGE_FACTORY.getExchange(sellToken);
|
||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||
? exchangeFactory.getExchange(buyToken)
|
||||
: exchangeFactory.getExchange(sellToken);
|
||||
require(address(exchange) != address(0), "MixinUniswap/NO_EXCHANGE");
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinAdapterAddresses.sol";
|
||||
import "../IBridgeAdapter.sol";
|
||||
|
||||
/*
|
||||
UniswapV2
|
||||
@@ -42,26 +42,16 @@ interface IUniswapV2Router02 {
|
||||
function swapExactTokensForTokens(
|
||||
uint amountIn,
|
||||
uint amountOutMin,
|
||||
address[] calldata path,
|
||||
IERC20TokenV06[] calldata path,
|
||||
address to,
|
||||
uint deadline
|
||||
) external returns (uint[] memory amounts);
|
||||
}
|
||||
|
||||
contract MixinUniswapV2 is
|
||||
MixinAdapterAddresses
|
||||
{
|
||||
contract MixinUniswapV2 {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
/// @dev Mainnet address of the `UniswapV2Router02` contract.
|
||||
IUniswapV2Router02 private immutable UNISWAP_V2_ROUTER;
|
||||
|
||||
constructor(AdapterAddresses memory addresses)
|
||||
public
|
||||
{
|
||||
UNISWAP_V2_ROUTER = IUniswapV2Router02(addresses.uniswapV2Router);
|
||||
}
|
||||
|
||||
function _tradeUniswapV2(
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
@@ -70,22 +60,24 @@ contract MixinUniswapV2 is
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// solhint-disable indent
|
||||
address[] memory path = abi.decode(bridgeData, (address[]));
|
||||
// solhint-enable indent
|
||||
IUniswapV2Router02 router;
|
||||
IERC20TokenV06[] memory path;
|
||||
{
|
||||
address[] memory _path;
|
||||
(router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(path.length >= 2, "MixinUniswapV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == address(buyToken),
|
||||
"UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
path[path.length - 1] == buyToken,
|
||||
"MixinUniswapV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
// Grant the Uniswap router an allowance to sell the first token.
|
||||
IERC20TokenV06(path[0]).approveIfBelow(
|
||||
address(UNISWAP_V2_ROUTER),
|
||||
sellAmount
|
||||
);
|
||||
path[0].approveIfBelow(address(router), sellAmount);
|
||||
|
||||
uint[] memory amounts = UNISWAP_V2_ROUTER.swapExactTokensForTokens(
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
|
@@ -31,7 +31,6 @@ contract MixinZeroExBridge {
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
function _tradeZeroExBridge(
|
||||
address bridgeAddress,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
@@ -40,17 +39,19 @@ contract MixinZeroExBridge {
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(ILiquidityProvider provider, bytes memory lpData) =
|
||||
abi.decode(bridgeData, (ILiquidityProvider, bytes));
|
||||
// Trade the good old fashioned way
|
||||
sellToken.compatTransfer(
|
||||
bridgeAddress,
|
||||
address(provider),
|
||||
sellAmount
|
||||
);
|
||||
boughtAmount = ILiquidityProvider(bridgeAddress).sellTokenForToken(
|
||||
boughtAmount = provider.sellTokenForToken(
|
||||
address(sellToken),
|
||||
address(buyToken),
|
||||
address(this), // recipient
|
||||
1, // minBuyAmount
|
||||
bridgeData
|
||||
lpData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,113 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
/// @dev Interface to the V3 Exchange.
|
||||
interface IExchange {
|
||||
|
||||
/// @dev V3 Order structure.
|
||||
struct Order {
|
||||
// Address that created the order.
|
||||
address makerAddress;
|
||||
// Address that is allowed to fill the order.
|
||||
// If set to 0, any address is allowed to fill the order.
|
||||
address takerAddress;
|
||||
// Address that will recieve fees when order is filled.
|
||||
address feeRecipientAddress;
|
||||
// Address that is allowed to call Exchange contract methods that affect this order.
|
||||
// If set to 0, any address is allowed to call these methods.
|
||||
address senderAddress;
|
||||
// Amount of makerAsset being offered by maker. Must be greater than 0.
|
||||
uint256 makerAssetAmount;
|
||||
// Amount of takerAsset being bid on by maker. Must be greater than 0.
|
||||
uint256 takerAssetAmount;
|
||||
// Fee paid to feeRecipient by maker when order is filled.
|
||||
uint256 makerFee;
|
||||
// Fee paid to feeRecipient by taker when order is filled.
|
||||
uint256 takerFee;
|
||||
// Timestamp in seconds at which order expires.
|
||||
uint256 expirationTimeSeconds;
|
||||
// Arbitrary number to facilitate uniqueness of the order's hash.
|
||||
uint256 salt;
|
||||
// Encoded data that can be decoded by a specified proxy contract when transferring makerAsset.
|
||||
// The leading bytes4 references the id of the asset proxy.
|
||||
bytes makerAssetData;
|
||||
// Encoded data that can be decoded by a specified proxy contract when transferring takerAsset.
|
||||
// The leading bytes4 references the id of the asset proxy.
|
||||
bytes takerAssetData;
|
||||
// Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset.
|
||||
// The leading bytes4 references the id of the asset proxy.
|
||||
bytes makerFeeAssetData;
|
||||
// Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset.
|
||||
// The leading bytes4 references the id of the asset proxy.
|
||||
bytes takerFeeAssetData;
|
||||
}
|
||||
|
||||
/// @dev V3 `fillOrder()` results.`
|
||||
struct FillResults {
|
||||
// Total amount of makerAsset(s) filled.
|
||||
uint256 makerAssetFilledAmount;
|
||||
// Total amount of takerAsset(s) filled.
|
||||
uint256 takerAssetFilledAmount;
|
||||
// Total amount of fees paid by maker(s) to feeRecipient(s).
|
||||
uint256 makerFeePaid;
|
||||
// Total amount of fees paid by taker to feeRecipients(s).
|
||||
uint256 takerFeePaid;
|
||||
// Total amount of fees paid by taker to the staking contract.
|
||||
uint256 protocolFeePaid;
|
||||
}
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
Order calldata order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (FillResults memory fillResults);
|
||||
|
||||
/// @dev Returns the protocolFeeMultiplier
|
||||
/// @return multiplier The multiplier for protocol fees.
|
||||
function protocolFeeMultiplier()
|
||||
external
|
||||
view
|
||||
returns (uint256 multiplier);
|
||||
|
||||
/// @dev Gets an asset proxy.
|
||||
/// @param assetProxyId Id of the asset proxy.
|
||||
/// @return proxyAddress The asset proxy registered to assetProxyId.
|
||||
/// Returns 0x0 if no proxy is registered.
|
||||
function getAssetProxy(bytes4 assetProxyId)
|
||||
external
|
||||
view
|
||||
returns (address proxyAddress);
|
||||
|
||||
function EIP712_EXCHANGE_DOMAIN_HASH()
|
||||
external
|
||||
view
|
||||
returns (bytes32 domainHash);
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
|
||||
interface IGasToken {
|
||||
|
||||
/// @dev Frees up to `value` sub-tokens
|
||||
/// @param value The amount of tokens to free
|
||||
/// @return freed How many tokens were freed
|
||||
function freeUpTo(uint256 value) external returns (uint256 freed);
|
||||
|
||||
/// @dev Frees up to `value` sub-tokens owned by `from`
|
||||
/// @param from The owner of tokens to spend
|
||||
/// @param value The amount of tokens to free
|
||||
/// @return freed How many tokens were freed
|
||||
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
|
||||
|
||||
/// @dev Mints `value` amount of tokens
|
||||
/// @param value The amount of tokens to mint
|
||||
function mint(uint256 value) external;
|
||||
}
|
@@ -1,168 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
|
||||
import "./IExchange.sol";
|
||||
|
||||
|
||||
library LibOrderHash {
|
||||
|
||||
using LibOrderHash for IExchange.Order;
|
||||
|
||||
// Hash for the EIP712 Order Schema:
|
||||
// keccak256(abi.encodePacked(
|
||||
// "Order(",
|
||||
// "address makerAddress,",
|
||||
// "address takerAddress,",
|
||||
// "address feeRecipientAddress,",
|
||||
// "address senderAddress,",
|
||||
// "uint256 makerAssetAmount,",
|
||||
// "uint256 takerAssetAmount,",
|
||||
// "uint256 makerFee,",
|
||||
// "uint256 takerFee,",
|
||||
// "uint256 expirationTimeSeconds,",
|
||||
// "uint256 salt,",
|
||||
// "bytes makerAssetData,",
|
||||
// "bytes takerAssetData,",
|
||||
// "bytes makerFeeAssetData,",
|
||||
// "bytes takerFeeAssetData",
|
||||
// ")"
|
||||
// ))
|
||||
bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
|
||||
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
|
||||
|
||||
/// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
|
||||
/// @param order The order structure.
|
||||
/// @param eip712ExchangeDomainHash Domain hash for the Exchange.
|
||||
/// @return orderHash EIP712 typed data hash of the order.
|
||||
function getTypedDataHash(IExchange.Order memory order, bytes32 eip712ExchangeDomainHash)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 orderHash)
|
||||
{
|
||||
orderHash = _hashEIP712Message(
|
||||
eip712ExchangeDomainHash,
|
||||
order.getStructHash()
|
||||
);
|
||||
return orderHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 hash of the order struct.
|
||||
/// @param order The order structure.
|
||||
/// @return result EIP712 hash of the order struct.
|
||||
function getStructHash(IExchange.Order memory order)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
|
||||
bytes memory makerAssetData = order.makerAssetData;
|
||||
bytes memory takerAssetData = order.takerAssetData;
|
||||
bytes memory makerFeeAssetData = order.makerFeeAssetData;
|
||||
bytes memory takerFeeAssetData = order.takerFeeAssetData;
|
||||
|
||||
// Assembly for more efficiently computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP712_ORDER_SCHEMA_HASH,
|
||||
// uint256(order.makerAddress),
|
||||
// uint256(order.takerAddress),
|
||||
// uint256(order.feeRecipientAddress),
|
||||
// uint256(order.senderAddress),
|
||||
// order.makerAssetAmount,
|
||||
// order.takerAssetAmount,
|
||||
// order.makerFee,
|
||||
// order.takerFee,
|
||||
// order.expirationTimeSeconds,
|
||||
// order.salt,
|
||||
// keccak256(order.makerAssetData),
|
||||
// keccak256(order.takerAssetData),
|
||||
// keccak256(order.makerFeeAssetData),
|
||||
// keccak256(order.takerFeeAssetData)
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Assert order offset (this is an internal error that should never be triggered)
|
||||
if lt(order, 32) {
|
||||
invalid()
|
||||
}
|
||||
|
||||
// Calculate memory addresses that will be swapped out before hashing
|
||||
let pos1 := sub(order, 32)
|
||||
let pos2 := add(order, 320)
|
||||
let pos3 := add(order, 352)
|
||||
let pos4 := add(order, 384)
|
||||
let pos5 := add(order, 416)
|
||||
|
||||
// Backup
|
||||
let temp1 := mload(pos1)
|
||||
let temp2 := mload(pos2)
|
||||
let temp3 := mload(pos3)
|
||||
let temp4 := mload(pos4)
|
||||
let temp5 := mload(pos5)
|
||||
|
||||
// Hash in place
|
||||
mstore(pos1, schemaHash)
|
||||
mstore(pos2, keccak256(add(makerAssetData, 32), mload(makerAssetData))) // store hash of makerAssetData
|
||||
mstore(pos3, keccak256(add(takerAssetData, 32), mload(takerAssetData))) // store hash of takerAssetData
|
||||
mstore(pos4, keccak256(add(makerFeeAssetData, 32), mload(makerFeeAssetData))) // store hash of makerFeeAssetData
|
||||
mstore(pos5, keccak256(add(takerFeeAssetData, 32), mload(takerFeeAssetData))) // store hash of takerFeeAssetData
|
||||
result := keccak256(pos1, 480)
|
||||
|
||||
// Restore
|
||||
mstore(pos1, temp1)
|
||||
mstore(pos2, temp2)
|
||||
mstore(pos3, temp3)
|
||||
mstore(pos4, temp4)
|
||||
mstore(pos5, temp5)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||
/// @param eip712DomainHash Hash of the domain domain separator data, computed
|
||||
/// with getDomainHash().
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return result EIP712 hash applied to the given EIP712 Domain.
|
||||
function _hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
// Assembly for more efficient computing:
|
||||
// keccak256(abi.encodePacked(
|
||||
// EIP191_HEADER,
|
||||
// EIP712_DOMAIN_HASH,
|
||||
// hashStruct
|
||||
// ));
|
||||
|
||||
assembly {
|
||||
// Load free memory pointer
|
||||
let memPtr := mload(64)
|
||||
|
||||
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
|
||||
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
|
||||
mstore(add(memPtr, 34), hashStruct) // Hash of struct
|
||||
|
||||
// Compute hash
|
||||
result := keccak256(memPtr, 66)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -27,39 +27,25 @@ import "./TestMintableERC20Token.sol";
|
||||
|
||||
contract TestFillQuoteTransformerBridge {
|
||||
|
||||
struct FillBehavior {
|
||||
// Scaling for maker assets minted, in 1e18.
|
||||
uint256 makerAssetMintRatio;
|
||||
uint256 amount;
|
||||
}
|
||||
uint256 private constant REVERT_AMOUNT = 0xdeadbeef;
|
||||
|
||||
function sellTokenForToken(
|
||||
address takerToken,
|
||||
address /* takerToken */,
|
||||
address makerToken,
|
||||
address recipient,
|
||||
uint256 minBuyAmount,
|
||||
uint256 /* minBuyAmount */,
|
||||
bytes calldata auxiliaryData
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
FillBehavior memory behavior = abi.decode(auxiliaryData, (FillBehavior));
|
||||
boughtAmount = LibMathV06.getPartialAmountFloor(
|
||||
behavior.makerAssetMintRatio,
|
||||
1e18,
|
||||
behavior.amount
|
||||
);
|
||||
boughtAmount = abi.decode(auxiliaryData, (uint256));
|
||||
if (REVERT_AMOUNT == boughtAmount) {
|
||||
revert("REVERT_AMOUNT");
|
||||
}
|
||||
TestMintableERC20Token(makerToken).mint(
|
||||
recipient,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
|
||||
function encodeBehaviorData(FillBehavior calldata behavior)
|
||||
external
|
||||
pure
|
||||
returns (bytes memory encoded)
|
||||
{
|
||||
return abi.encode(behavior);
|
||||
}
|
||||
}
|
||||
|
@@ -23,106 +23,109 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../src/vendor/v3/IExchange.sol";
|
||||
import "./TestMintableERC20Token.sol";
|
||||
import "../src/features/libs/LibNativeOrder.sol";
|
||||
import "../src/features/libs/LibSignature.sol";
|
||||
|
||||
|
||||
contract TestFillQuoteTransformerExchange {
|
||||
|
||||
struct FillBehavior {
|
||||
// How much of the order is filled, in taker asset amount.
|
||||
uint256 filledTakerAssetAmount;
|
||||
// Scaling for maker assets minted, in 1e18.
|
||||
uint256 makerAssetMintRatio;
|
||||
}
|
||||
|
||||
bytes32 public constant EIP712_EXCHANGE_DOMAIN_HASH = 0xaa81d881b1adbbf115e15b849cb9cdc643cad3c6a90f30eb505954af943247e6;
|
||||
|
||||
uint256 private constant REVERT_AMOUNT = 0xdeadbeef;
|
||||
uint256 private constant PROTOCOL_FEE_MULTIPLIER = 1337;
|
||||
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
function fillOrder(
|
||||
IExchange.Order calldata order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes calldata signature
|
||||
function fillLimitOrder(
|
||||
LibNativeOrder.LimitOrder calldata order,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (IExchange.FillResults memory fillResults)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
require(
|
||||
signature.length != 0,
|
||||
"TestFillQuoteTransformerExchange/INVALID_SIGNATURE"
|
||||
);
|
||||
// The signature is the ABI-encoded FillBehavior data.
|
||||
FillBehavior memory behavior = abi.decode(signature, (FillBehavior));
|
||||
|
||||
// The r field of the signature is the pre-filled amount.
|
||||
uint128 takerTokenPreFilledAmount = uint128(uint256(signature.r));
|
||||
if (REVERT_AMOUNT == takerTokenPreFilledAmount) {
|
||||
revert("REVERT_AMOUNT");
|
||||
}
|
||||
if (takerTokenPreFilledAmount >= order.takerAmount) {
|
||||
revert('FILLED');
|
||||
}
|
||||
uint256 protocolFee = PROTOCOL_FEE_MULTIPLIER * tx.gasprice;
|
||||
require(
|
||||
msg.value == protocolFee,
|
||||
"TestFillQuoteTransformerExchange/INSUFFICIENT_PROTOCOL_FEE"
|
||||
);
|
||||
// Return excess protocol fee.
|
||||
msg.sender.transfer(msg.value - protocolFee);
|
||||
takerTokenFilledAmount = LibSafeMathV06.min128(
|
||||
order.takerAmount - takerTokenPreFilledAmount,
|
||||
takerTokenFillAmount
|
||||
);
|
||||
|
||||
// Take taker tokens.
|
||||
TestMintableERC20Token takerToken = _getTokenFromAssetData(order.takerAssetData);
|
||||
takerAssetFillAmount = LibSafeMathV06.min256(
|
||||
order.takerAssetAmount.safeSub(behavior.filledTakerAssetAmount),
|
||||
takerAssetFillAmount
|
||||
order.takerToken.transferFrom(
|
||||
msg.sender,
|
||||
order.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
require(
|
||||
takerToken.getSpendableAmount(msg.sender, address(this)) >= takerAssetFillAmount,
|
||||
"TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FUNDS"
|
||||
);
|
||||
takerToken.transferFrom(msg.sender, order.makerAddress, takerAssetFillAmount);
|
||||
|
||||
// Mint maker tokens.
|
||||
uint256 makerAssetFilledAmount = LibMathV06.getPartialAmountFloor(
|
||||
takerAssetFillAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount
|
||||
);
|
||||
TestMintableERC20Token makerToken = _getTokenFromAssetData(order.makerAssetData);
|
||||
makerToken.mint(
|
||||
msg.sender,
|
||||
LibMathV06.getPartialAmountFloor(
|
||||
behavior.makerAssetMintRatio,
|
||||
1e18,
|
||||
makerAssetFilledAmount
|
||||
)
|
||||
makerTokenFilledAmount = LibSafeMathV06.safeDowncastToUint128(
|
||||
uint256(takerTokenFilledAmount)
|
||||
* uint256(order.makerAmount)
|
||||
/ uint256(order.takerAmount)
|
||||
);
|
||||
TestMintableERC20Token(address(order.makerToken))
|
||||
.mint(msg.sender, makerTokenFilledAmount);
|
||||
|
||||
// Take taker fee.
|
||||
TestMintableERC20Token takerFeeToken = _getTokenFromAssetData(order.takerFeeAssetData);
|
||||
uint256 takerFee = LibMathV06.getPartialAmountFloor(
|
||||
takerAssetFillAmount,
|
||||
order.takerAssetAmount,
|
||||
order.takerFee
|
||||
// Take taker token fee.
|
||||
uint128 takerFee = LibSafeMathV06.safeDowncastToUint128(
|
||||
uint256(takerTokenFilledAmount)
|
||||
* uint256(order.takerTokenFeeAmount)
|
||||
/ uint256(order.takerAmount)
|
||||
);
|
||||
require(
|
||||
takerFeeToken.getSpendableAmount(msg.sender, address(this)) >= takerFee,
|
||||
"TestFillQuoteTransformerExchange/INSUFFICIENT_TAKER_FEE_FUNDS"
|
||||
);
|
||||
takerFeeToken.transferFrom(msg.sender, order.feeRecipientAddress, takerFee);
|
||||
|
||||
fillResults.makerAssetFilledAmount = makerAssetFilledAmount;
|
||||
fillResults.takerAssetFilledAmount = takerAssetFillAmount;
|
||||
fillResults.makerFeePaid = uint256(-1);
|
||||
fillResults.takerFeePaid = takerFee;
|
||||
fillResults.protocolFeePaid = protocolFee;
|
||||
order.takerToken.transferFrom(msg.sender, order.feeRecipient, takerFee);
|
||||
}
|
||||
|
||||
function encodeBehaviorData(FillBehavior calldata behavior)
|
||||
function fillRfqOrder(
|
||||
LibNativeOrder.RfqOrder calldata order,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
external
|
||||
pure
|
||||
returns (bytes memory encoded)
|
||||
payable
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
return abi.encode(behavior);
|
||||
// The r field of the signature is the pre-filled amount.
|
||||
uint128 takerTokenPreFilledAmount = uint128(uint256(signature.r));
|
||||
if (REVERT_AMOUNT == takerTokenPreFilledAmount) {
|
||||
revert("REVERT_AMOUNT");
|
||||
}
|
||||
if (takerTokenPreFilledAmount >= order.takerAmount) {
|
||||
revert('FILLED');
|
||||
}
|
||||
takerTokenFilledAmount = LibSafeMathV06.min128(
|
||||
order.takerAmount - takerTokenPreFilledAmount,
|
||||
takerTokenFillAmount
|
||||
);
|
||||
|
||||
// Take taker tokens.
|
||||
order.takerToken.transferFrom(
|
||||
msg.sender,
|
||||
order.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
|
||||
// Mint maker tokens.
|
||||
makerTokenFilledAmount = LibSafeMathV06.safeDowncastToUint128(
|
||||
uint256(takerTokenFilledAmount)
|
||||
* uint256(order.makerAmount)
|
||||
/ uint256(order.takerAmount)
|
||||
);
|
||||
TestMintableERC20Token(address(order.makerToken))
|
||||
.mint(msg.sender, makerTokenFilledAmount);
|
||||
}
|
||||
|
||||
function protocolFeeMultiplier()
|
||||
function getProtocolFeeMultiplier()
|
||||
external
|
||||
pure
|
||||
returns (uint256)
|
||||
@@ -130,19 +133,11 @@ contract TestFillQuoteTransformerExchange {
|
||||
return PROTOCOL_FEE_MULTIPLIER;
|
||||
}
|
||||
|
||||
function getAssetProxy(bytes4)
|
||||
function getLimitOrderHash(LibNativeOrder.LimitOrder calldata order)
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(this);
|
||||
}
|
||||
|
||||
function _getTokenFromAssetData(bytes memory assetData)
|
||||
private
|
||||
pure
|
||||
returns (TestMintableERC20Token token)
|
||||
returns (bytes32)
|
||||
{
|
||||
return TestMintableERC20Token(LibBytesV06.readAddress(assetData, 16));
|
||||
return bytes32(order.salt);
|
||||
}
|
||||
}
|
||||
|
@@ -65,14 +65,13 @@ contract TestLiquidityProvider {
|
||||
/// @param outputToken The token being bought.
|
||||
/// @param recipient The recipient of the bought tokens.
|
||||
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
|
||||
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function sellTokenForToken(
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
address recipient,
|
||||
uint256 minBuyAmount,
|
||||
bytes calldata auxiliaryData
|
||||
bytes calldata // auxiliaryData
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount)
|
||||
@@ -91,13 +90,12 @@ contract TestLiquidityProvider {
|
||||
/// @param outputToken The token being bought.
|
||||
/// @param recipient The recipient of the bought tokens.
|
||||
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
|
||||
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function sellEthForToken(
|
||||
address outputToken,
|
||||
address recipient,
|
||||
uint256 minBuyAmount,
|
||||
bytes calldata auxiliaryData
|
||||
bytes calldata // auxiliaryData
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount)
|
||||
@@ -115,13 +113,12 @@ contract TestLiquidityProvider {
|
||||
/// @param inputToken The token being sold.
|
||||
/// @param recipient The recipient of the bought tokens.
|
||||
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
|
||||
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
|
||||
/// @return boughtAmount The amount of ETH bought.
|
||||
function sellTokenForEth(
|
||||
address inputToken,
|
||||
address payable recipient,
|
||||
uint256 minBuyAmount,
|
||||
bytes calldata auxiliaryData
|
||||
bytes calldata // auxiliaryData
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount)
|
||||
|
@@ -43,7 +43,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOrderHash|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|BridgeSource|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -9,6 +9,7 @@ import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateF
|
||||
import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json';
|
||||
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
||||
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
||||
import * as BridgeSource from '../test/generated-artifacts/BridgeSource.json';
|
||||
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
|
||||
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
|
||||
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
||||
@@ -24,10 +25,8 @@ import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeatur
|
||||
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
|
||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||
import * as IERC20Transformer from '../test/generated-artifacts/IERC20Transformer.json';
|
||||
import * as IExchange from '../test/generated-artifacts/IExchange.json';
|
||||
import * as IFeature from '../test/generated-artifacts/IFeature.json';
|
||||
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
|
||||
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
||||
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
|
||||
import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.json';
|
||||
import * as ILiquidityProviderSandbox from '../test/generated-artifacts/ILiquidityProviderSandbox.json';
|
||||
@@ -53,7 +52,6 @@ import * as LibMigrate from '../test/generated-artifacts/LibMigrate.json';
|
||||
import * as LibNativeOrder from '../test/generated-artifacts/LibNativeOrder.json';
|
||||
import * as LibNativeOrdersRichErrors from '../test/generated-artifacts/LibNativeOrdersRichErrors.json';
|
||||
import * as LibNativeOrdersStorage from '../test/generated-artifacts/LibNativeOrdersStorage.json';
|
||||
import * as LibOrderHash from '../test/generated-artifacts/LibOrderHash.json';
|
||||
import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRichErrors.json';
|
||||
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
||||
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
||||
@@ -73,7 +71,6 @@ import * as LiquidityProviderFeature from '../test/generated-artifacts/Liquidity
|
||||
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
|
||||
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
|
||||
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
|
||||
import * as MixinAdapterAddresses from '../test/generated-artifacts/MixinAdapterAddresses.json';
|
||||
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
|
||||
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
|
||||
@@ -211,8 +208,8 @@ export const artifacts = {
|
||||
Transformer: Transformer as ContractArtifact,
|
||||
WethTransformer: WethTransformer as ContractArtifact,
|
||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||
BridgeSource: BridgeSource as ContractArtifact,
|
||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||
MixinAdapterAddresses: MixinAdapterAddresses as ContractArtifact,
|
||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||
MixinBancor: MixinBancor as ContractArtifact,
|
||||
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
||||
@@ -230,10 +227,7 @@ export const artifacts = {
|
||||
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
IExchange: IExchange as ContractArtifact,
|
||||
IGasToken: IGasToken as ContractArtifact,
|
||||
IStaking: IStaking as ContractArtifact,
|
||||
LibOrderHash: LibOrderHash as ContractArtifact,
|
||||
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
|
||||
TestBridge: TestBridge as ContractArtifact,
|
||||
TestCallTarget: TestCallTarget as ContractArtifact,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
||||
export * from '../test/generated-wrappers/allowance_target';
|
||||
export * from '../test/generated-wrappers/bootstrap_feature';
|
||||
export * from '../test/generated-wrappers/bridge_adapter';
|
||||
export * from '../test/generated-wrappers/bridge_source';
|
||||
export * from '../test/generated-wrappers/fee_collector';
|
||||
export * from '../test/generated-wrappers/fee_collector_controller';
|
||||
export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||
@@ -22,10 +23,8 @@ export * from '../test/generated-wrappers/i_bootstrap_feature';
|
||||
export * from '../test/generated-wrappers/i_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||
export * from '../test/generated-wrappers/i_erc20_transformer';
|
||||
export * from '../test/generated-wrappers/i_exchange';
|
||||
export * from '../test/generated-wrappers/i_feature';
|
||||
export * from '../test/generated-wrappers/i_flash_wallet';
|
||||
export * from '../test/generated-wrappers/i_gas_token';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_feature';
|
||||
export * from '../test/generated-wrappers/i_liquidity_provider_sandbox';
|
||||
@@ -51,7 +50,6 @@ export * from '../test/generated-wrappers/lib_migrate';
|
||||
export * from '../test/generated-wrappers/lib_native_order';
|
||||
export * from '../test/generated-wrappers/lib_native_orders_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_native_orders_storage';
|
||||
export * from '../test/generated-wrappers/lib_order_hash';
|
||||
export * from '../test/generated-wrappers/lib_ownable_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_ownable_storage';
|
||||
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
||||
@@ -71,7 +69,6 @@ export * from '../test/generated-wrappers/liquidity_provider_feature';
|
||||
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
|
||||
export * from '../test/generated-wrappers/log_metadata_transformer';
|
||||
export * from '../test/generated-wrappers/meta_transactions_feature';
|
||||
export * from '../test/generated-wrappers/mixin_adapter_addresses';
|
||||
export * from '../test/generated-wrappers/mixin_balancer';
|
||||
export * from '../test/generated-wrappers/mixin_bancor';
|
||||
export * from '../test/generated-wrappers/mixin_co_fi_x';
|
||||
|
@@ -35,6 +35,7 @@
|
||||
"test/generated-artifacts/AllowanceTarget.json",
|
||||
"test/generated-artifacts/BootstrapFeature.json",
|
||||
"test/generated-artifacts/BridgeAdapter.json",
|
||||
"test/generated-artifacts/BridgeSource.json",
|
||||
"test/generated-artifacts/FeeCollector.json",
|
||||
"test/generated-artifacts/FeeCollectorController.json",
|
||||
"test/generated-artifacts/FillQuoteTransformer.json",
|
||||
@@ -50,10 +51,8 @@
|
||||
"test/generated-artifacts/IBridgeAdapter.json",
|
||||
"test/generated-artifacts/IERC20Bridge.json",
|
||||
"test/generated-artifacts/IERC20Transformer.json",
|
||||
"test/generated-artifacts/IExchange.json",
|
||||
"test/generated-artifacts/IFeature.json",
|
||||
"test/generated-artifacts/IFlashWallet.json",
|
||||
"test/generated-artifacts/IGasToken.json",
|
||||
"test/generated-artifacts/ILiquidityProvider.json",
|
||||
"test/generated-artifacts/ILiquidityProviderFeature.json",
|
||||
"test/generated-artifacts/ILiquidityProviderSandbox.json",
|
||||
@@ -79,7 +78,6 @@
|
||||
"test/generated-artifacts/LibNativeOrder.json",
|
||||
"test/generated-artifacts/LibNativeOrdersRichErrors.json",
|
||||
"test/generated-artifacts/LibNativeOrdersStorage.json",
|
||||
"test/generated-artifacts/LibOrderHash.json",
|
||||
"test/generated-artifacts/LibOwnableRichErrors.json",
|
||||
"test/generated-artifacts/LibOwnableStorage.json",
|
||||
"test/generated-artifacts/LibProxyRichErrors.json",
|
||||
@@ -99,7 +97,6 @@
|
||||
"test/generated-artifacts/LiquidityProviderSandbox.json",
|
||||
"test/generated-artifacts/LogMetadataTransformer.json",
|
||||
"test/generated-artifacts/MetaTransactionsFeature.json",
|
||||
"test/generated-artifacts/MixinAdapterAddresses.json",
|
||||
"test/generated-artifacts/MixinBalancer.json",
|
||||
"test/generated-artifacts/MixinBancor.json",
|
||||
"test/generated-artifacts/MixinCoFiX.json",
|
||||
|
Reference in New Issue
Block a user