Merge pull request #2657 from 0xProject/fix/ep/meta-transactions
EP: MetaTransactions fixes
This commit is contained in:
commit
08ae43aad3
@ -290,7 +290,7 @@ describe('AssetProxyDispatcher', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHash,
|
orderHash,
|
||||||
encodedAssetData,
|
encodedAssetData,
|
||||||
nestedError.toString(),
|
nestedError.encode(),
|
||||||
);
|
);
|
||||||
const tx = assetProxyDispatcher
|
const tx = assetProxyDispatcher
|
||||||
.dispatchTransferFrom(orderHash, encodedAssetData, makerAddress, takerAddress, amount)
|
.dispatchTransferFrom(orderHash, encodedAssetData, makerAddress, takerAddress, amount)
|
||||||
@ -313,7 +313,7 @@ describe('AssetProxyDispatcher', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
transferIndexAsBytes32,
|
transferIndexAsBytes32,
|
||||||
assetDataB,
|
assetDataB,
|
||||||
nestedError.toString(),
|
nestedError.encode(),
|
||||||
);
|
);
|
||||||
const tx = assetProxyDispatcher
|
const tx = assetProxyDispatcher
|
||||||
.simulateDispatchTransferFromCalls(
|
.simulateDispatchTransferFromCalls(
|
||||||
|
@ -300,7 +300,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
|
|||||||
hashHex,
|
hashHex,
|
||||||
validatorWallet.address,
|
validatorWallet.address,
|
||||||
signatureHex,
|
signatureHex,
|
||||||
new StringRevertError(validatorWalletRevertReason).toString(),
|
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||||
);
|
);
|
||||||
const tx = validateAsync(hashHex, validatorWallet.address, signatureHex, ValidatorWalletAction.Revert);
|
const tx = validateAsync(hashHex, validatorWallet.address, signatureHex, ValidatorWalletAction.Revert);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -562,7 +562,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
|
|||||||
validatorWallet.address,
|
validatorWallet.address,
|
||||||
data,
|
data,
|
||||||
signatureHex,
|
signatureHex,
|
||||||
new StringRevertError(validatorWalletRevertReason).toString(),
|
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||||
);
|
);
|
||||||
const tx = validateAsync(signedOrder, signatureHex, ValidatorWalletAction.Revert);
|
const tx = validateAsync(signedOrder, signatureHex, ValidatorWalletAction.Revert);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -693,7 +693,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
|
|||||||
validatorWallet.address,
|
validatorWallet.address,
|
||||||
data,
|
data,
|
||||||
signatureHex,
|
signatureHex,
|
||||||
new StringRevertError(validatorWalletRevertReason).toString(),
|
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||||
);
|
);
|
||||||
const tx = validateAsync(signedOrder, signatureHex, ValidatorWalletAction.Revert);
|
const tx = validateAsync(signedOrder, signatureHex, ValidatorWalletAction.Revert);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -916,7 +916,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
|
|||||||
validatorWallet.address,
|
validatorWallet.address,
|
||||||
data,
|
data,
|
||||||
signatureHex,
|
signatureHex,
|
||||||
new StringRevertError(validatorWalletRevertReason).toString(),
|
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||||
);
|
);
|
||||||
const tx = validateAsync(signedTransaction, signatureHex, ValidatorWalletAction.Revert);
|
const tx = validateAsync(signedTransaction, signatureHex, ValidatorWalletAction.Revert);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
@ -1041,7 +1041,7 @@ blockchainTests.resets('MixinSignatureValidator', env => {
|
|||||||
validatorWallet.address,
|
validatorWallet.address,
|
||||||
data,
|
data,
|
||||||
signatureHex,
|
signatureHex,
|
||||||
new StringRevertError(validatorWalletRevertReason).toString(),
|
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||||
);
|
);
|
||||||
const tx = validateAsync(signedTransaction, signatureHex, ValidatorWalletAction.Revert);
|
const tx = validateAsync(signedTransaction, signatureHex, ValidatorWalletAction.Revert);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
@ -101,7 +101,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
executableError.toString(),
|
executableError.encode(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
||||||
@ -123,7 +123,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
executableError.toString(),
|
executableError.encode(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
||||||
@ -145,7 +145,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
executableError.toString(),
|
executableError.encode(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
// Call the `batchExecuteTransactions()` function and ensure that it reverts with the expected revert error.
|
||||||
@ -280,7 +280,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const outerExecuteTransactionHash = transactionHashUtils.getTransactionHashHex(outerExecuteTransaction);
|
const outerExecuteTransactionHash = transactionHashUtils.getTransactionHashHex(outerExecuteTransaction);
|
||||||
const outerExpectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const outerExpectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
outerExecuteTransactionHash,
|
outerExecuteTransactionHash,
|
||||||
innerExpectedError.toString(),
|
innerExpectedError.encode(),
|
||||||
);
|
);
|
||||||
const tx = transactionsContract
|
const tx = transactionsContract
|
||||||
.batchExecuteTransactions([outerExecuteTransaction], [randomSignature()])
|
.batchExecuteTransactions([outerExecuteTransaction], [randomSignature()])
|
||||||
@ -363,7 +363,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const errorData = new ExchangeRevertErrors.TransactionInvalidContextError(
|
const errorData = new ExchangeRevertErrors.TransactionInvalidContextError(
|
||||||
innerTransactionHash,
|
innerTransactionHash,
|
||||||
accounts[0],
|
accounts[0],
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(outerTransactionHash, errorData);
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(outerTransactionHash, errorData);
|
||||||
const tx = transactionsContract
|
const tx = transactionsContract
|
||||||
.executeTransaction(outerTransaction, validSignature)
|
.executeTransaction(outerTransaction, validSignature)
|
||||||
@ -385,7 +385,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const errorData = new ExchangeRevertErrors.TransactionInvalidContextError(
|
const errorData = new ExchangeRevertErrors.TransactionInvalidContextError(
|
||||||
innerTransactionHash,
|
innerTransactionHash,
|
||||||
accounts[0],
|
accounts[0],
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(outerTransactionHash, errorData);
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(outerTransactionHash, errorData);
|
||||||
const tx = transactionsContract
|
const tx = transactionsContract
|
||||||
.executeTransaction(outerTransaction, validSignature)
|
.executeTransaction(outerTransaction, validSignature)
|
||||||
@ -466,7 +466,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
executableError.toString(),
|
executableError.encode(),
|
||||||
);
|
);
|
||||||
const tx = transactionsContract
|
const tx = transactionsContract
|
||||||
.executeTransaction(transaction, randomSignature())
|
.executeTransaction(transaction, randomSignature())
|
||||||
@ -486,7 +486,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
|
|||||||
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
const executableError = new StringRevertError('EXECUTABLE_FAILED');
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
executableError.toString(),
|
executableError.encode(),
|
||||||
);
|
);
|
||||||
const tx = transactionsContract
|
const tx = transactionsContract
|
||||||
.executeTransaction(transaction, validSignature)
|
.executeTransaction(transaction, validSignature)
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update curveBridge tests",
|
"note": "Update curveBridge tests",
|
||||||
"pr": 2633
|
"pr": 2633
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add EP RFQT + MTX tests",
|
||||||
|
"pr": 2692
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
"@0x/contracts-multisig": "^4.1.7",
|
"@0x/contracts-multisig": "^4.1.7",
|
||||||
"@0x/contracts-staking": "^2.0.14",
|
"@0x/contracts-staking": "^2.0.14",
|
||||||
"@0x/contracts-test-utils": "^5.3.4",
|
"@0x/contracts-test-utils": "^5.3.4",
|
||||||
|
"@0x/contracts-zero-ex": "^0.2.0",
|
||||||
"@0x/subproviders": "^6.1.1",
|
"@0x/subproviders": "^6.1.1",
|
||||||
"@0x/types": "^3.2.0",
|
"@0x/types": "^3.2.0",
|
||||||
"@0x/typescript-typings": "^5.1.1",
|
"@0x/typescript-typings": "^5.1.1",
|
||||||
|
426
contracts/integrations/test/exchange-proxy/mtx_test.ts
Normal file
426
contracts/integrations/test/exchange-proxy/mtx_test.ts
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
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,
|
||||||
|
signCallData,
|
||||||
|
} from '@0x/contracts-zero-ex';
|
||||||
|
import { migrateOnceAsync } from '@0x/migrations';
|
||||||
|
import {
|
||||||
|
assetDataUtils,
|
||||||
|
encodeFillQuoteTransformerData,
|
||||||
|
encodePayTakerTransformerData,
|
||||||
|
ETH_TOKEN_ADDRESS,
|
||||||
|
FillQuoteTransformerSide,
|
||||||
|
findTransformerNonce,
|
||||||
|
signatureUtils,
|
||||||
|
SignedExchangeProxyMetaTransaction,
|
||||||
|
} from '@0x/order-utils';
|
||||||
|
import { AssetProxyId, Order, SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
||||||
|
import * as ethjs from 'ethereumjs-util';
|
||||||
|
|
||||||
|
const { MAX_UINT256, NULL_ADDRESS, NULL_BYTES, NULL_BYTES32, ZERO_AMOUNT } = constants;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSignedSwapData(swap: SwapInfo, signerKey?: string): string {
|
||||||
|
return signCallData(
|
||||||
|
zeroEx
|
||||||
|
.transformERC20(
|
||||||
|
swap.inputTokenAddress,
|
||||||
|
swap.outputTokenAddress,
|
||||||
|
swap.inputTokenAmount,
|
||||||
|
swap.minOutputTokenAmount,
|
||||||
|
swap.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
signerKey ? signerKey : quoteSignerKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 signed calldata and no relayer fee', async () => {
|
||||||
|
const swap = await generateSwapAsync();
|
||||||
|
const callDataHash = hexUtils.hash(getSwapData(swap));
|
||||||
|
const signedSwapData = getSignedSwapData(swap);
|
||||||
|
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||||
|
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0);
|
||||||
|
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||||
|
const receipt = await zeroEx
|
||||||
|
.executeMetaTransaction(mtx, 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,
|
||||||
|
callDataHash,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
data: NULL_BYTES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'TransformerMetadata',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can call `transformERC20()` with signed calldata and a relayer fee', async () => {
|
||||||
|
const swap = await generateSwapAsync();
|
||||||
|
const callDataHash = hexUtils.hash(getSwapData(swap));
|
||||||
|
const signedSwapData = getSignedSwapData(swap);
|
||||||
|
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||||
|
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee);
|
||||||
|
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||||
|
const receipt = await zeroEx
|
||||||
|
.executeMetaTransaction(mtx, 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,
|
||||||
|
callDataHash,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
data: NULL_BYTES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'TransformerMetadata',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can call `transformERC20()` with wrongly signed calldata and a relayer fee', async () => {
|
||||||
|
const swap = await generateSwapAsync();
|
||||||
|
const signedSwapData = getSignedSwapData(swap, hexUtils.random());
|
||||||
|
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||||
|
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee);
|
||||||
|
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||||
|
const receipt = await zeroEx
|
||||||
|
.executeMetaTransaction(mtx, 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,
|
||||||
|
// Only signed calldata should have a nonzero hash.
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
data: NULL_BYTES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'TransformerMetadata',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('`transformERC20()` can fill RFQT order if calldata is signed', async () => {
|
||||||
|
const swap = await generateSwapAsync({}, true);
|
||||||
|
const callDataHash = hexUtils.hash(getSwapData(swap));
|
||||||
|
const signedSwapData = getSignedSwapData(swap);
|
||||||
|
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
|
||||||
|
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0);
|
||||||
|
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
|
||||||
|
const receipt = await zeroEx
|
||||||
|
.executeMetaTransaction(mtx, 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,
|
||||||
|
callDataHash,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
data: NULL_BYTES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'TransformerMetadata',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('`transformERC20()` can fill RFQT order if calldata is not signed but no quote signer configured', async () => {
|
||||||
|
const swap = await generateSwapAsync({}, true);
|
||||||
|
const callData = getSwapData(swap);
|
||||||
|
const callDataHash = hexUtils.hash(callData);
|
||||||
|
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, 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,
|
||||||
|
callDataHash,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
data: NULL_BYTES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'TransformerMetadata',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('`transformERC20()` cannot fill RFQT order if calldata is not signed', 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 tx = zeroEx
|
||||||
|
.executeMetaTransaction(mtx, mtx.signature)
|
||||||
|
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
|
||||||
|
return expect(tx).to.revertWith(new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError());
|
||||||
|
});
|
||||||
|
});
|
@ -558,7 +558,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
signedOrder.makerAssetData,
|
signedOrder.makerAssetData,
|
||||||
new StringRevertError(RevertReason.TransferFailed).toString(),
|
new StringRevertError(RevertReason.TransferFailed).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
||||||
@ -587,7 +587,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
signedOrder.takerAssetData,
|
signedOrder.takerAssetData,
|
||||||
new StringRevertError(RevertReason.TransferFailed).toString(),
|
new StringRevertError(RevertReason.TransferFailed).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
||||||
@ -616,7 +616,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
signedOrder.makerAssetData,
|
signedOrder.makerAssetData,
|
||||||
new StringRevertError(RevertReason.InvalidAmount).toString(),
|
new StringRevertError(RevertReason.InvalidAmount).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
||||||
@ -645,7 +645,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
signedOrder.takerAssetData,
|
signedOrder.takerAssetData,
|
||||||
new StringRevertError(RevertReason.InvalidAmount).toString(),
|
new StringRevertError(RevertReason.InvalidAmount).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
.fillOrder(signedOrder, takerAssetFillAmount, signedOrder.signature)
|
||||||
@ -980,7 +980,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
assetData,
|
assetData,
|
||||||
new StringRevertError(RevertReason.TargetNotEven).toString(),
|
new StringRevertError(RevertReason.TargetNotEven).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, signedOrder.takerAssetAmount, signedOrder.signature)
|
.fillOrder(signedOrder, signedOrder.takerAssetAmount, signedOrder.signature)
|
||||||
@ -1015,7 +1015,7 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashHex,
|
orderHashHex,
|
||||||
assetData,
|
assetData,
|
||||||
new StringRevertError(RevertReason.TargetNotEven).toString(),
|
new StringRevertError(RevertReason.TargetNotEven).encode(),
|
||||||
);
|
);
|
||||||
const tx = exchange
|
const tx = exchange
|
||||||
.fillOrder(signedOrder, signedOrder.takerAssetAmount, signedOrder.signature)
|
.fillOrder(signedOrder, signedOrder.takerAssetAmount, signedOrder.signature)
|
||||||
|
@ -278,7 +278,7 @@ blockchainTests.resets('Exchange fills dydx orders', env => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
assetProxyError = decodeThrownErrorAsRevertError(e).values.errorData;
|
assetProxyError = decodeThrownErrorAsRevertError(e).values.errorData;
|
||||||
}
|
}
|
||||||
expect(assetProxyError).to.deep.equal(new StringRevertError(expectedAssetProxyError.toString()));
|
expect(assetProxyError).to.deep.equal(new StringRevertError(expectedAssetProxyError.encode()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -121,7 +121,7 @@ blockchainTests.resets('Transaction <> protocol fee integration tests', env => {
|
|||||||
maker.address,
|
maker.address,
|
||||||
wethless.address,
|
wethless.address,
|
||||||
'0x',
|
'0x',
|
||||||
).toString();
|
).encode();
|
||||||
return new ExchangeRevertErrors.TransactionExecutionError(
|
return new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashUtils.getTransactionHashHex(failedTransaction),
|
transactionHashUtils.getTransactionHashHex(failedTransaction),
|
||||||
nestedError,
|
nestedError,
|
||||||
@ -252,7 +252,7 @@ blockchainTests.resets('Transaction <> protocol fee integration tests', env => {
|
|||||||
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
|
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashUtils.getTransactionHashHex(recursiveTransaction),
|
transactionHashUtils.getTransactionHashHex(recursiveTransaction),
|
||||||
protocolFeeError(order, transaction).toString(),
|
protocolFeeError(order, transaction).encode(),
|
||||||
);
|
);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
@ -280,7 +280,7 @@ blockchainTests.resets('Transaction integration tests', env => {
|
|||||||
const noReentrancyError = new ExchangeRevertErrors.TransactionInvalidContextError(
|
const noReentrancyError = new ExchangeRevertErrors.TransactionInvalidContextError(
|
||||||
transactionHashHex,
|
transactionHashHex,
|
||||||
transaction.signerAddress,
|
transaction.signerAddress,
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
recursiveTransactionHashHex,
|
recursiveTransactionHashHex,
|
||||||
noReentrancyError,
|
noReentrancyError,
|
||||||
@ -330,7 +330,7 @@ blockchainTests.resets('Transaction integration tests', env => {
|
|||||||
orderHashUtils.getOrderHashHex(order),
|
orderHashUtils.getOrderHashHex(order),
|
||||||
order.makerAddress,
|
order.makerAddress,
|
||||||
order.signature,
|
order.signature,
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashHex,
|
transactionHashHex,
|
||||||
nestedError,
|
nestedError,
|
||||||
@ -353,7 +353,7 @@ blockchainTests.resets('Transaction integration tests', env => {
|
|||||||
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
|
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
|
||||||
orderHashUtils.getOrderHashHex(order),
|
orderHashUtils.getOrderHashHex(order),
|
||||||
takers[0].address,
|
takers[0].address,
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashHex,
|
transactionHashHex,
|
||||||
nestedError,
|
nestedError,
|
||||||
@ -403,7 +403,7 @@ blockchainTests.resets('Transaction integration tests', env => {
|
|||||||
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
|
ExchangeRevertErrors.ExchangeContextErrorCodes.InvalidMaker,
|
||||||
orderHashUtils.getOrderHashHex(orders[0]),
|
orderHashUtils.getOrderHashHex(orders[0]),
|
||||||
takers[0].address,
|
takers[0].address,
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashHex,
|
transactionHashHex,
|
||||||
nestedError,
|
nestedError,
|
||||||
@ -771,7 +771,7 @@ blockchainTests.resets('Transaction integration tests', env => {
|
|||||||
const nestedError = new ExchangeRevertErrors.OrderStatusError(
|
const nestedError = new ExchangeRevertErrors.OrderStatusError(
|
||||||
orderHashUtils.getOrderHashHex(order),
|
orderHashUtils.getOrderHashHex(order),
|
||||||
OrderStatus.Cancelled,
|
OrderStatus.Cancelled,
|
||||||
).toString();
|
).encode();
|
||||||
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
|
||||||
transactionHashUtils.getTransactionHashHex(transaction2),
|
transactionHashUtils.getTransactionHashHex(transaction2),
|
||||||
nestedError,
|
nestedError,
|
||||||
|
@ -123,7 +123,7 @@ blockchainTests.resets('Chainlink stop-limit order tests', env => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashUtils.getOrderHashHex(order),
|
orderHashUtils.getOrderHashHex(order),
|
||||||
order.makerAssetData,
|
order.makerAssetData,
|
||||||
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').toString(),
|
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').encode(),
|
||||||
);
|
);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
@ -133,7 +133,7 @@ blockchainTests.resets('Chainlink stop-limit order tests', env => {
|
|||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
||||||
orderHashUtils.getOrderHashHex(order),
|
orderHashUtils.getOrderHashHex(order),
|
||||||
order.makerAssetData,
|
order.makerAssetData,
|
||||||
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').toString(),
|
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').encode(),
|
||||||
);
|
);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,57 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.3.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Internal audit fixes",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add refund mechanism to meta-transactions",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Pass sender address to transformers",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refund unused protocol fees to `refundReceiver` in FQT",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix `TransformerDeployer.kill()` calling the wrong `die()` interface.",
|
||||||
|
"pr": 2624
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Address CD post-audit feedback",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `LogMetadataTransformer`",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Rename all feature contracts to have `Feature` suffix",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Return `IZeroExContract` in `fullMigrateAsync()`",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add taker address enforcement to RFQT orders in FQT",
|
||||||
|
"pr": 2692
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "All calldata is valid if quote signer is unset in `TransformERC20`",
|
||||||
|
"pr": 2692
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add updated Kyber and Mooniswap rollup to FQT",
|
||||||
|
"pr": 2692
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -19,22 +19,22 @@
|
|||||||
pragma solidity ^0.6.5;
|
pragma solidity ^0.6.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./features/IOwnable.sol";
|
import "./features/IOwnableFeature.sol";
|
||||||
import "./features/ISimpleFunctionRegistry.sol";
|
import "./features/ISimpleFunctionRegistryFeature.sol";
|
||||||
import "./features/ITokenSpender.sol";
|
import "./features/ITokenSpenderFeature.sol";
|
||||||
import "./features/ISignatureValidator.sol";
|
import "./features/ISignatureValidatorFeature.sol";
|
||||||
import "./features/ITransformERC20.sol";
|
import "./features/ITransformERC20Feature.sol";
|
||||||
import "./features/IMetaTransactions.sol";
|
import "./features/IMetaTransactionsFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Interface for a fully featured Exchange Proxy.
|
/// @dev Interface for a fully featured Exchange Proxy.
|
||||||
interface IZeroEx is
|
interface IZeroEx is
|
||||||
IOwnable,
|
IOwnableFeature,
|
||||||
ISimpleFunctionRegistry,
|
ISimpleFunctionRegistryFeature,
|
||||||
ITokenSpender,
|
ITokenSpenderFeature,
|
||||||
ISignatureValidator,
|
ISignatureValidatorFeature,
|
||||||
ITransformERC20,
|
ITransformERC20Feature,
|
||||||
IMetaTransactions
|
IMetaTransactionsFeature
|
||||||
{
|
{
|
||||||
// solhint-disable state-visibility
|
// solhint-disable state-visibility
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||||
import "./migrations/LibBootstrap.sol";
|
import "./migrations/LibBootstrap.sol";
|
||||||
import "./features/Bootstrap.sol";
|
import "./features/BootstrapFeature.sol";
|
||||||
import "./storage/LibProxyStorage.sol";
|
import "./storage/LibProxyStorage.sol";
|
||||||
import "./errors/LibProxyRichErrors.sol";
|
import "./errors/LibProxyRichErrors.sol";
|
||||||
|
|
||||||
@ -32,14 +32,14 @@ contract ZeroEx {
|
|||||||
// solhint-disable separate-by-one-line-in-contract,indent,var-name-mixedcase
|
// solhint-disable separate-by-one-line-in-contract,indent,var-name-mixedcase
|
||||||
using LibBytesV06 for bytes;
|
using LibBytesV06 for bytes;
|
||||||
|
|
||||||
/// @dev Construct this contract and register the `Bootstrap` feature.
|
/// @dev Construct this contract and register the `BootstrapFeature` feature.
|
||||||
/// After constructing this contract, `bootstrap()` should be called
|
/// After constructing this contract, `bootstrap()` should be called
|
||||||
/// by `bootstrap()` to seed the initial feature set.
|
/// by `bootstrap()` to seed the initial feature set.
|
||||||
/// @param bootstrapper Who can call `bootstrap()`.
|
/// @param bootstrapper Who can call `bootstrap()`.
|
||||||
constructor(address bootstrapper) public {
|
constructor(address bootstrapper) public {
|
||||||
// Temporarily create and register the bootstrap feature.
|
// Temporarily create and register the bootstrap feature.
|
||||||
// It will deregister itself after `bootstrap()` has been called.
|
// It will deregister itself after `bootstrap()` has been called.
|
||||||
Bootstrap bootstrap = new Bootstrap(bootstrapper);
|
BootstrapFeature bootstrap = new BootstrapFeature(bootstrapper);
|
||||||
LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
|
LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
|
||||||
address(bootstrap);
|
address(bootstrap);
|
||||||
}
|
}
|
||||||
|
@ -34,13 +34,15 @@ library LibCommonRichErrors {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function IllegalReentrancyError()
|
function IllegalReentrancyError(bytes4 selector, uint256 reentrancyFlags)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (bytes memory)
|
returns (bytes memory)
|
||||||
{
|
{
|
||||||
return abi.encodeWithSelector(
|
return abi.encodeWithSelector(
|
||||||
bytes4(keccak256("IllegalReentrancyError()"))
|
bytes4(keccak256("IllegalReentrancyError(bytes4,uint256)")),
|
||||||
|
selector,
|
||||||
|
reentrancyFlags
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
|
|||||||
|
|
||||||
/// @dev A contract with a `die()` function.
|
/// @dev A contract with a `die()` function.
|
||||||
interface IKillable {
|
interface IKillable {
|
||||||
function die() external;
|
function die(address payable ethRecipient) external;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Deployer contract for ERC20 transformers.
|
/// @dev Deployer contract for ERC20 transformers.
|
||||||
@ -48,9 +48,9 @@ contract TransformerDeployer is
|
|||||||
mapping (address => uint256) public toDeploymentNonce;
|
mapping (address => uint256) public toDeploymentNonce;
|
||||||
|
|
||||||
/// @dev Create this contract and register authorities.
|
/// @dev Create this contract and register authorities.
|
||||||
constructor(address[] memory authorities) public {
|
constructor(address[] memory initialAuthorities) public {
|
||||||
for (uint256 i = 0; i < authorities.length; ++i) {
|
for (uint256 i = 0; i < initialAuthorities.length; ++i) {
|
||||||
_addAuthorizedAddress(authorities[i]);
|
_addAuthorizedAddress(initialAuthorities[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +67,19 @@ contract TransformerDeployer is
|
|||||||
assembly {
|
assembly {
|
||||||
deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
|
deployedAddress := create(callvalue(), add(bytecode, 32), mload(bytecode))
|
||||||
}
|
}
|
||||||
|
require(deployedAddress != address(0), 'TransformerDeployer/DEPLOY_FAILED');
|
||||||
toDeploymentNonce[deployedAddress] = deploymentNonce;
|
toDeploymentNonce[deployedAddress] = deploymentNonce;
|
||||||
emit Deployed(deployedAddress, deploymentNonce, msg.sender);
|
emit Deployed(deployedAddress, deploymentNonce, msg.sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Call `die()` on a contract. Only callable by an authority.
|
/// @dev Call `die()` on a contract. Only callable by an authority.
|
||||||
function kill(IKillable target)
|
/// @param target The target contract to call `die()` on.
|
||||||
|
/// @param ethRecipient The Recipient of any ETH locked in `target`.
|
||||||
|
function kill(IKillable target, address payable ethRecipient)
|
||||||
public
|
public
|
||||||
onlyAuthorized
|
onlyAuthorized
|
||||||
{
|
{
|
||||||
target.die();
|
target.die(ethRecipient);
|
||||||
emit Killed(address(target), msg.sender);
|
emit Killed(address(target), msg.sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,12 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
import "../migrations/LibBootstrap.sol";
|
import "../migrations/LibBootstrap.sol";
|
||||||
import "../storage/LibProxyStorage.sol";
|
import "../storage/LibProxyStorage.sol";
|
||||||
import "./IBootstrap.sol";
|
import "./IBootstrapFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Detachable `bootstrap()` feature.
|
/// @dev Detachable `bootstrap()` feature.
|
||||||
contract Bootstrap is
|
contract BootstrapFeature is
|
||||||
IBootstrap
|
IBootstrapFeature
|
||||||
{
|
{
|
||||||
// solhint-disable state-visibility,indent
|
// solhint-disable state-visibility,indent
|
||||||
/// @dev The ZeroEx contract.
|
/// @dev The ZeroEx contract.
|
||||||
@ -69,7 +69,7 @@ contract Bootstrap is
|
|||||||
// Deregister.
|
// Deregister.
|
||||||
LibProxyStorage.getStorage().impls[this.bootstrap.selector] = address(0);
|
LibProxyStorage.getStorage().impls[this.bootstrap.selector] = address(0);
|
||||||
// Self-destruct.
|
// Self-destruct.
|
||||||
Bootstrap(_implementation).die();
|
BootstrapFeature(_implementation).die();
|
||||||
// Call the bootstrapper.
|
// Call the bootstrapper.
|
||||||
LibBootstrap.delegatecallBootstrapFunction(target, callData);
|
LibBootstrap.delegatecallBootstrapFunction(target, callData);
|
||||||
}
|
}
|
||||||
@ -77,6 +77,7 @@ contract Bootstrap is
|
|||||||
/// @dev Self-destructs this contract.
|
/// @dev Self-destructs this contract.
|
||||||
/// Can only be called by the deployer.
|
/// Can only be called by the deployer.
|
||||||
function die() external {
|
function die() external {
|
||||||
|
assert(address(this) == _implementation);
|
||||||
if (msg.sender != _deployer) {
|
if (msg.sender != _deployer) {
|
||||||
LibProxyRichErrors.InvalidDieCallerError(msg.sender, _deployer).rrevert();
|
LibProxyRichErrors.InvalidDieCallerError(msg.sender, _deployer).rrevert();
|
||||||
}
|
}
|
@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Detachable `bootstrap()` feature.
|
/// @dev Detachable `bootstrap()` feature.
|
||||||
interface IBootstrap {
|
interface IBootstrapFeature {
|
||||||
|
|
||||||
/// @dev Bootstrap the initial feature set of this contract by delegatecalling
|
/// @dev Bootstrap the initial feature set of this contract by delegatecalling
|
||||||
/// into `target`. Before exiting the `bootstrap()` function will
|
/// into `target`. Before exiting the `bootstrap()` function will
|
@ -23,7 +23,7 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Meta-transactions feature.
|
/// @dev Meta-transactions feature.
|
||||||
interface IMetaTransactions {
|
interface IMetaTransactionsFeature {
|
||||||
|
|
||||||
/// @dev Describes an exchange proxy meta transaction.
|
/// @dev Describes an exchange proxy meta transaction.
|
||||||
struct MetaTransactionData {
|
struct MetaTransactionData {
|
@ -24,7 +24,7 @@ import "@0x/contracts-utils/contracts/src/v06/interfaces/IOwnableV06.sol";
|
|||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
/// @dev Owner management and migration features.
|
/// @dev Owner management and migration features.
|
||||||
interface IOwnable is
|
interface IOwnableFeature is
|
||||||
IOwnableV06
|
IOwnableV06
|
||||||
{
|
{
|
||||||
/// @dev Emitted when `migrate()` is called.
|
/// @dev Emitted when `migrate()` is called.
|
@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Feature for validating signatures.
|
/// @dev Feature for validating signatures.
|
||||||
interface ISignatureValidator {
|
interface ISignatureValidatorFeature {
|
||||||
|
|
||||||
/// @dev Allowed signature types.
|
/// @dev Allowed signature types.
|
||||||
enum SignatureType {
|
enum SignatureType {
|
@ -21,7 +21,7 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Basic registry management features.
|
/// @dev Basic registry management features.
|
||||||
interface ISimpleFunctionRegistry {
|
interface ISimpleFunctionRegistryFeature {
|
||||||
|
|
||||||
/// @dev A function implementation was updated via `extend()` or `rollback()`.
|
/// @dev A function implementation was updated via `extend()` or `rollback()`.
|
||||||
/// @param selector The function selector.
|
/// @param selector The function selector.
|
@ -23,7 +23,7 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Feature that allows spending token allowances.
|
/// @dev Feature that allows spending token allowances.
|
||||||
interface ITokenSpender {
|
interface ITokenSpenderFeature {
|
||||||
|
|
||||||
/// @dev Transfers ERC20 tokens from `owner` to `to`.
|
/// @dev Transfers ERC20 tokens from `owner` to `to`.
|
||||||
/// Only callable from within.
|
/// Only callable from within.
|
@ -25,7 +25,7 @@ import "../external/IFlashWallet.sol";
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Feature to composably transform between ERC20 tokens.
|
/// @dev Feature to composably transform between ERC20 tokens.
|
||||||
interface ITransformERC20 {
|
interface ITransformERC20Feature {
|
||||||
|
|
||||||
/// @dev Defines a transformation to run in `transformERC20()`.
|
/// @dev Defines a transformation to run in `transformERC20()`.
|
||||||
struct Transformation {
|
struct Transformation {
|
@ -21,24 +21,27 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||||
import "../errors/LibMetaTransactionsRichErrors.sol";
|
import "../errors/LibMetaTransactionsRichErrors.sol";
|
||||||
import "../fixins/FixinCommon.sol";
|
import "../fixins/FixinCommon.sol";
|
||||||
|
import "../fixins/FixinReentrancyGuard.sol";
|
||||||
import "../fixins/FixinEIP712.sol";
|
import "../fixins/FixinEIP712.sol";
|
||||||
import "../migrations/LibMigrate.sol";
|
import "../migrations/LibMigrate.sol";
|
||||||
import "../storage/LibMetaTransactionsStorage.sol";
|
import "../storage/LibMetaTransactionsStorage.sol";
|
||||||
import "./libs/LibSignedCallData.sol";
|
import "./libs/LibSignedCallData.sol";
|
||||||
import "./IMetaTransactions.sol";
|
import "./IMetaTransactionsFeature.sol";
|
||||||
import "./ITransformERC20.sol";
|
import "./ITransformERC20Feature.sol";
|
||||||
import "./ISignatureValidator.sol";
|
import "./ISignatureValidatorFeature.sol";
|
||||||
import "./ITokenSpender.sol";
|
import "./ITokenSpenderFeature.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev MetaTransactions feature.
|
/// @dev MetaTransactions feature.
|
||||||
contract MetaTransactions is
|
contract MetaTransactionsFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
IMetaTransactions,
|
IMetaTransactionsFeature,
|
||||||
FixinCommon,
|
FixinCommon,
|
||||||
|
FixinReentrancyGuard,
|
||||||
FixinEIP712
|
FixinEIP712
|
||||||
{
|
{
|
||||||
using LibBytesV06 for bytes;
|
using LibBytesV06 for bytes;
|
||||||
@ -69,7 +72,7 @@ contract MetaTransactions is
|
|||||||
IERC20TokenV06 outputToken;
|
IERC20TokenV06 outputToken;
|
||||||
uint256 inputTokenAmount;
|
uint256 inputTokenAmount;
|
||||||
uint256 minOutputTokenAmount;
|
uint256 minOutputTokenAmount;
|
||||||
ITransformERC20.Transformation[] transformations;
|
ITransformERC20Feature.Transformation[] transformations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
@ -92,6 +95,16 @@ contract MetaTransactions is
|
|||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @dev Refunds up to `msg.value` leftover ETH at the end of the call.
|
||||||
|
modifier refundsAttachedEth() {
|
||||||
|
_;
|
||||||
|
uint256 remainingBalance =
|
||||||
|
LibSafeMathV06.min256(msg.value, address(this).balance);
|
||||||
|
if (remainingBalance > 0) {
|
||||||
|
msg.sender.transfer(remainingBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(address zeroExAddress)
|
constructor(address zeroExAddress)
|
||||||
public
|
public
|
||||||
FixinCommon()
|
FixinCommon()
|
||||||
@ -127,9 +140,11 @@ contract MetaTransactions is
|
|||||||
public
|
public
|
||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
|
nonReentrant(REENTRANCY_MTX)
|
||||||
|
refundsAttachedEth
|
||||||
returns (bytes memory returnResult)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
return _executeMetaTransactionPrivate(
|
returnResult = _executeMetaTransactionPrivate(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
mtx,
|
mtx,
|
||||||
signature
|
signature
|
||||||
@ -147,6 +162,8 @@ contract MetaTransactions is
|
|||||||
public
|
public
|
||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
|
nonReentrant(REENTRANCY_MTX)
|
||||||
|
refundsAttachedEth
|
||||||
returns (bytes[] memory returnResults)
|
returns (bytes[] memory returnResults)
|
||||||
{
|
{
|
||||||
if (mtxs.length != signatures.length) {
|
if (mtxs.length != signatures.length) {
|
||||||
@ -254,29 +271,31 @@ contract MetaTransactions is
|
|||||||
|
|
||||||
_validateMetaTransaction(state);
|
_validateMetaTransaction(state);
|
||||||
|
|
||||||
// Mark the transaction executed.
|
// Mark the transaction executed by storing the block at which it was executed.
|
||||||
assert(block.number > 0);
|
// Currently the block number just indicates that the mtx was executed and
|
||||||
|
// serves no other purpose from within this contract.
|
||||||
LibMetaTransactionsStorage.getStorage()
|
LibMetaTransactionsStorage.getStorage()
|
||||||
.mtxHashToExecutedBlockNumber[state.hash] = block.number;
|
.mtxHashToExecutedBlockNumber[state.hash] = block.number;
|
||||||
|
|
||||||
// Execute the call based on the selector.
|
|
||||||
state.selector = mtx.callData.readBytes4(0);
|
|
||||||
if (state.selector == ITransformERC20.transformERC20.selector) {
|
|
||||||
returnResult = _executeTransformERC20Call(state);
|
|
||||||
} else {
|
|
||||||
LibMetaTransactionsRichErrors
|
|
||||||
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
|
||||||
.rrevert();
|
|
||||||
}
|
|
||||||
// Pay the fee to the sender.
|
// Pay the fee to the sender.
|
||||||
if (mtx.feeAmount > 0) {
|
if (mtx.feeAmount > 0) {
|
||||||
ITokenSpender(address(this))._spendERC20Tokens(
|
ITokenSpenderFeature(address(this))._spendERC20Tokens(
|
||||||
mtx.feeToken,
|
mtx.feeToken,
|
||||||
mtx.signer, // From the signer.
|
mtx.signer, // From the signer.
|
||||||
sender, // To the sender.
|
sender, // To the sender.
|
||||||
mtx.feeAmount
|
mtx.feeAmount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the call based on the selector.
|
||||||
|
state.selector = mtx.callData.readBytes4(0);
|
||||||
|
if (state.selector == ITransformERC20Feature.transformERC20.selector) {
|
||||||
|
returnResult = _executeTransformERC20Call(state);
|
||||||
|
} else {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
||||||
|
.rrevert();
|
||||||
|
}
|
||||||
emit MetaTransactionExecuted(
|
emit MetaTransactionExecuted(
|
||||||
state.hash,
|
state.hash,
|
||||||
state.selector,
|
state.selector,
|
||||||
@ -330,7 +349,7 @@ contract MetaTransactions is
|
|||||||
}
|
}
|
||||||
// Must be signed by signer.
|
// Must be signed by signer.
|
||||||
try
|
try
|
||||||
ISignatureValidator(address(this))
|
ISignatureValidatorFeature(address(this))
|
||||||
.validateHashSignature(state.hash, state.mtx.signer, state.signature)
|
.validateHashSignature(state.hash, state.mtx.signer, state.signature)
|
||||||
{}
|
{}
|
||||||
catch (bytes memory err) {
|
catch (bytes memory err) {
|
||||||
@ -353,9 +372,9 @@ contract MetaTransactions is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Execute a `ITransformERC20.transformERC20()` meta-transaction call
|
/// @dev Execute a `ITransformERC20Feature.transformERC20()` meta-transaction call
|
||||||
/// by decoding the call args and translating the call to the internal
|
/// by decoding the call args and translating the call to the internal
|
||||||
/// `ITransformERC20._transformERC20()` variant, where we can override
|
/// `ITransformERC20Feature._transformERC20()` variant, where we can override
|
||||||
/// the taker address.
|
/// the taker address.
|
||||||
function _executeTransformERC20Call(ExecuteState memory state)
|
function _executeTransformERC20Call(ExecuteState memory state)
|
||||||
private
|
private
|
||||||
@ -367,7 +386,7 @@ contract MetaTransactions is
|
|||||||
// since decoding a single struct arg consumes far less stack space than
|
// since decoding a single struct arg consumes far less stack space than
|
||||||
// decoding multiple struct args.
|
// decoding multiple struct args.
|
||||||
|
|
||||||
// Where the encoding for multiple args (with the seleector ommitted)
|
// Where the encoding for multiple args (with the selector ommitted)
|
||||||
// would typically look like:
|
// would typically look like:
|
||||||
// | argument | offset |
|
// | argument | offset |
|
||||||
// |--------------------------|---------|
|
// |--------------------------|---------|
|
||||||
@ -394,7 +413,7 @@ contract MetaTransactions is
|
|||||||
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
||||||
// Copy the args data from the original, after the new struct offset prefix.
|
// Copy the args data from the original, after the new struct offset prefix.
|
||||||
bytes memory fromCallData = state.mtx.callData;
|
bytes memory fromCallData = state.mtx.callData;
|
||||||
assert(fromCallData.length >= 4);
|
assert(fromCallData.length >= 160);
|
||||||
uint256 fromMem;
|
uint256 fromMem;
|
||||||
uint256 toMem;
|
uint256 toMem;
|
||||||
assembly {
|
assembly {
|
||||||
@ -407,19 +426,19 @@ contract MetaTransactions is
|
|||||||
toMem := add(encodedStructArgs, 64)
|
toMem := add(encodedStructArgs, 64)
|
||||||
}
|
}
|
||||||
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
|
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
|
||||||
// Decode call args for `ITransformERC20.transformERC20()` as a struct.
|
// Decode call args for `ITransformERC20Feature.transformERC20()` as a struct.
|
||||||
args = abi.decode(encodedStructArgs, (ExternalTransformERC20Args));
|
args = abi.decode(encodedStructArgs, (ExternalTransformERC20Args));
|
||||||
}
|
}
|
||||||
// Parse the signature and hash out of the calldata so `_transformERC20()`
|
// Parse the signature and hash out of the calldata so `_transformERC20()`
|
||||||
// can authenticate it.
|
// can authenticate it.
|
||||||
(bytes32 callDataHash, bytes memory callDataSignature) =
|
(bytes32 callDataHash, bytes memory callDataSignature) =
|
||||||
LibSignedCallData.parseCallData(state.mtx.callData);
|
LibSignedCallData.parseCallData(state.mtx.callData);
|
||||||
// Call `ITransformERC20._transformERC20()` (internal variant).
|
// Call `ITransformERC20Feature._transformERC20()` (internal variant).
|
||||||
return _callSelf(
|
return _callSelf(
|
||||||
state.hash,
|
state.hash,
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
ITransformERC20._transformERC20.selector,
|
ITransformERC20Feature._transformERC20.selector,
|
||||||
ITransformERC20.TransformERC20Args({
|
ITransformERC20Feature.TransformERC20Args({
|
||||||
taker: state.mtx.signer, // taker is mtx signer
|
taker: state.mtx.signer, // taker is mtx signer
|
||||||
inputToken: args.inputToken,
|
inputToken: args.inputToken,
|
||||||
outputToken: args.outputToken,
|
outputToken: args.outputToken,
|
@ -26,14 +26,14 @@ import "../storage/LibOwnableStorage.sol";
|
|||||||
import "../migrations/LibBootstrap.sol";
|
import "../migrations/LibBootstrap.sol";
|
||||||
import "../migrations/LibMigrate.sol";
|
import "../migrations/LibMigrate.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
import "./IOwnable.sol";
|
import "./IOwnableFeature.sol";
|
||||||
import "./SimpleFunctionRegistry.sol";
|
import "./SimpleFunctionRegistryFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Owner management features.
|
/// @dev Owner management features.
|
||||||
contract Ownable is
|
contract OwnableFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
IOwnable,
|
IOwnableFeature,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -44,10 +44,6 @@ contract Ownable is
|
|||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public FixinCommon() {
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
|
/// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
|
||||||
/// to allow the bootstrappers to call `extend()`. Ownership should be
|
/// to allow the bootstrappers to call `extend()`. Ownership should be
|
||||||
/// transferred to the real owner by the bootstrapper after
|
/// transferred to the real owner by the bootstrapper after
|
||||||
@ -58,9 +54,9 @@ contract Ownable is
|
|||||||
LibOwnableStorage.getStorage().owner = address(this);
|
LibOwnableStorage.getStorage().owner = address(this);
|
||||||
|
|
||||||
// Register feature functions.
|
// Register feature functions.
|
||||||
SimpleFunctionRegistry(address(this))._extendSelf(this.transferOwnership.selector, _implementation);
|
SimpleFunctionRegistryFeature(address(this))._extendSelf(this.transferOwnership.selector, _implementation);
|
||||||
SimpleFunctionRegistry(address(this))._extendSelf(this.owner.selector, _implementation);
|
SimpleFunctionRegistryFeature(address(this))._extendSelf(this.owner.selector, _implementation);
|
||||||
SimpleFunctionRegistry(address(this))._extendSelf(this.migrate.selector, _implementation);
|
SimpleFunctionRegistryFeature(address(this))._extendSelf(this.migrate.selector, _implementation);
|
||||||
return LibBootstrap.BOOTSTRAP_SUCCESS;
|
return LibBootstrap.BOOTSTRAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -24,28 +24,31 @@ import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
|||||||
import "../errors/LibSignatureRichErrors.sol";
|
import "../errors/LibSignatureRichErrors.sol";
|
||||||
import "../fixins/FixinCommon.sol";
|
import "../fixins/FixinCommon.sol";
|
||||||
import "../migrations/LibMigrate.sol";
|
import "../migrations/LibMigrate.sol";
|
||||||
import "./ISignatureValidator.sol";
|
import "./ISignatureValidatorFeature.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Feature for validating signatures.
|
/// @dev Feature for validating signatures.
|
||||||
contract SignatureValidator is
|
contract SignatureValidatorFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
ISignatureValidator,
|
ISignatureValidatorFeature,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
using LibBytesV06 for bytes;
|
using LibBytesV06 for bytes;
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
|
/// @dev Exclusive upper limit on ECDSA signatures 'R' values.
|
||||||
|
/// The valid range is given by fig (282) of the yellow paper.
|
||||||
|
uint256 private constant ECDSA_SIGNATURE_R_LIMIT =
|
||||||
|
uint256(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141);
|
||||||
|
/// @dev Exclusive upper limit on ECDSA signatures 'S' values.
|
||||||
|
/// The valid range is given by fig (283) of the yellow paper.
|
||||||
|
uint256 private constant ECDSA_SIGNATURE_S_LIMIT = ECDSA_SIGNATURE_R_LIMIT / 2 + 1;
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "SignatureValidator";
|
string public constant override FEATURE_NAME = "SignatureValidator";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
|
|
||||||
constructor() public FixinCommon() {
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Initialize and register this feature.
|
/// @dev Initialize and register this feature.
|
||||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||||
/// @return success `LibMigrate.SUCCESS` on success.
|
/// @return success `LibMigrate.SUCCESS` on success.
|
||||||
@ -160,12 +163,14 @@ contract SignatureValidator is
|
|||||||
uint8 v = uint8(signature[0]);
|
uint8 v = uint8(signature[0]);
|
||||||
bytes32 r = signature.readBytes32(1);
|
bytes32 r = signature.readBytes32(1);
|
||||||
bytes32 s = signature.readBytes32(33);
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
if (uint256(r) < ECDSA_SIGNATURE_R_LIMIT && uint256(s) < ECDSA_SIGNATURE_S_LIMIT) {
|
||||||
recovered = ecrecover(
|
recovered = ecrecover(
|
||||||
hash,
|
hash,
|
||||||
v,
|
v,
|
||||||
r,
|
r,
|
||||||
s
|
s
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (signatureType == SignatureType.EthSign) {
|
} else if (signatureType == SignatureType.EthSign) {
|
||||||
// Signed using `eth_sign`
|
// Signed using `eth_sign`
|
||||||
if (signature.length != 66) {
|
if (signature.length != 66) {
|
||||||
@ -179,6 +184,7 @@ contract SignatureValidator is
|
|||||||
uint8 v = uint8(signature[0]);
|
uint8 v = uint8(signature[0]);
|
||||||
bytes32 r = signature.readBytes32(1);
|
bytes32 r = signature.readBytes32(1);
|
||||||
bytes32 s = signature.readBytes32(33);
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
if (uint256(r) < ECDSA_SIGNATURE_R_LIMIT && uint256(s) < ECDSA_SIGNATURE_S_LIMIT) {
|
||||||
recovered = ecrecover(
|
recovered = ecrecover(
|
||||||
keccak256(abi.encodePacked(
|
keccak256(abi.encodePacked(
|
||||||
"\x19Ethereum Signed Message:\n32",
|
"\x19Ethereum Signed Message:\n32",
|
||||||
@ -188,6 +194,7 @@ contract SignatureValidator is
|
|||||||
r,
|
r,
|
||||||
s
|
s
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
revert('SignatureValidator/ILLEGAL_CODE_PATH');
|
revert('SignatureValidator/ILLEGAL_CODE_PATH');
|
@ -26,13 +26,13 @@ import "../storage/LibSimpleFunctionRegistryStorage.sol";
|
|||||||
import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
|
import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
|
||||||
import "../migrations/LibBootstrap.sol";
|
import "../migrations/LibBootstrap.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
import "./ISimpleFunctionRegistry.sol";
|
import "./ISimpleFunctionRegistryFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Basic registry management features.
|
/// @dev Basic registry management features.
|
||||||
contract SimpleFunctionRegistry is
|
contract SimpleFunctionRegistryFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
ISimpleFunctionRegistry,
|
ISimpleFunctionRegistryFeature,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
@ -42,10 +42,6 @@ contract SimpleFunctionRegistry is
|
|||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public FixinCommon() {
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Initializes this feature, registering its own functions.
|
/// @dev Initializes this feature, registering its own functions.
|
||||||
/// @return success Magic bytes if successful.
|
/// @return success Magic bytes if successful.
|
||||||
function bootstrap()
|
function bootstrap()
|
@ -28,15 +28,14 @@ import "../fixins/FixinCommon.sol";
|
|||||||
import "../migrations/LibMigrate.sol";
|
import "../migrations/LibMigrate.sol";
|
||||||
import "../external/IAllowanceTarget.sol";
|
import "../external/IAllowanceTarget.sol";
|
||||||
import "../storage/LibTokenSpenderStorage.sol";
|
import "../storage/LibTokenSpenderStorage.sol";
|
||||||
import "./ITokenSpender.sol";
|
import "./ITokenSpenderFeature.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
import "./ISimpleFunctionRegistry.sol";
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev Feature that allows spending token allowances.
|
/// @dev Feature that allows spending token allowances.
|
||||||
contract TokenSpender is
|
contract TokenSpenderFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
ITokenSpender,
|
ITokenSpenderFeature,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
// solhint-disable
|
// solhint-disable
|
||||||
@ -48,10 +47,6 @@ contract TokenSpender is
|
|||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public FixinCommon() {
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Initialize and register this feature. Should be delegatecalled
|
/// @dev Initialize and register this feature. Should be delegatecalled
|
||||||
/// into during a `Migrate.migrate()`.
|
/// into during a `Migrate.migrate()`.
|
||||||
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
|
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
|
@ -32,17 +32,16 @@ import "../storage/LibTransformERC20Storage.sol";
|
|||||||
import "../transformers/IERC20Transformer.sol";
|
import "../transformers/IERC20Transformer.sol";
|
||||||
import "../transformers/LibERC20Transformer.sol";
|
import "../transformers/LibERC20Transformer.sol";
|
||||||
import "./libs/LibSignedCallData.sol";
|
import "./libs/LibSignedCallData.sol";
|
||||||
import "./ITransformERC20.sol";
|
import "./ITransformERC20Feature.sol";
|
||||||
import "./ITokenSpender.sol";
|
import "./ITokenSpenderFeature.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
import "./ISignatureValidator.sol";
|
import "./ISignatureValidatorFeature.sol";
|
||||||
import "./ISimpleFunctionRegistry.sol";
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev Feature to composably transform between ERC20 tokens.
|
/// @dev Feature to composably transform between ERC20 tokens.
|
||||||
contract TransformERC20 is
|
contract TransformERC20Feature is
|
||||||
IFeature,
|
IFeature,
|
||||||
ITransformERC20,
|
ITransformERC20Feature,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
using LibSafeMathV06 for uint256;
|
using LibSafeMathV06 for uint256;
|
||||||
@ -61,10 +60,6 @@ contract TransformERC20 is
|
|||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
|
||||||
|
|
||||||
constructor() public FixinCommon() {
|
|
||||||
// solhint-disable-next-line no-empty-blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Initialize and register this feature.
|
/// @dev Initialize and register this feature.
|
||||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||||
/// @param transformerDeployer The trusted deployer for transformers.
|
/// @param transformerDeployer The trusted deployer for transformers.
|
||||||
@ -81,7 +76,10 @@ contract TransformERC20 is
|
|||||||
_registerFeatureFunction(this.getQuoteSigner.selector);
|
_registerFeatureFunction(this.getQuoteSigner.selector);
|
||||||
_registerFeatureFunction(this.transformERC20.selector);
|
_registerFeatureFunction(this.transformERC20.selector);
|
||||||
_registerFeatureFunction(this._transformERC20.selector);
|
_registerFeatureFunction(this._transformERC20.selector);
|
||||||
|
if (this.getTransformWallet() == IFlashWallet(address(0))) {
|
||||||
|
// Create the transform wallet if it doesn't exist.
|
||||||
this.createTransformWallet();
|
this.createTransformWallet();
|
||||||
|
}
|
||||||
LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
|
LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
|
||||||
return LibMigrate.MIGRATE_SUCCESS;
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -213,7 +211,7 @@ contract TransformERC20 is
|
|||||||
// If the input token amount is -1, transform the taker's entire
|
// If the input token amount is -1, transform the taker's entire
|
||||||
// spendable balance.
|
// spendable balance.
|
||||||
if (args.inputTokenAmount == uint256(-1)) {
|
if (args.inputTokenAmount == uint256(-1)) {
|
||||||
args.inputTokenAmount = ITokenSpender(address(this))
|
args.inputTokenAmount = ITokenSpenderFeature(address(this))
|
||||||
.getSpendableERC20BalanceOf(args.inputToken, args.taker);
|
.getSpendableERC20BalanceOf(args.inputToken, args.taker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,16 +255,15 @@ contract TransformERC20 is
|
|||||||
// Compute how much output token has been transferred to the taker.
|
// Compute how much output token has been transferred to the taker.
|
||||||
state.takerOutputTokenBalanceAfter =
|
state.takerOutputTokenBalanceAfter =
|
||||||
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.taker);
|
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.taker);
|
||||||
if (state.takerOutputTokenBalanceAfter > state.takerOutputTokenBalanceBefore) {
|
if (state.takerOutputTokenBalanceAfter < state.takerOutputTokenBalanceBefore) {
|
||||||
outputTokenAmount = state.takerOutputTokenBalanceAfter.safeSub(
|
|
||||||
state.takerOutputTokenBalanceBefore
|
|
||||||
);
|
|
||||||
} else if (state.takerOutputTokenBalanceAfter < state.takerOutputTokenBalanceBefore) {
|
|
||||||
LibTransformERC20RichErrors.NegativeTransformERC20OutputError(
|
LibTransformERC20RichErrors.NegativeTransformERC20OutputError(
|
||||||
address(args.outputToken),
|
address(args.outputToken),
|
||||||
state.takerOutputTokenBalanceBefore - state.takerOutputTokenBalanceAfter
|
state.takerOutputTokenBalanceBefore - state.takerOutputTokenBalanceAfter
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
|
outputTokenAmount = state.takerOutputTokenBalanceAfter.safeSub(
|
||||||
|
state.takerOutputTokenBalanceBefore
|
||||||
|
);
|
||||||
// Ensure enough output token has been sent to the taker.
|
// Ensure enough output token has been sent to the taker.
|
||||||
if (outputTokenAmount < args.minOutputTokenAmount) {
|
if (outputTokenAmount < args.minOutputTokenAmount) {
|
||||||
LibTransformERC20RichErrors.IncompleteTransformERC20Error(
|
LibTransformERC20RichErrors.IncompleteTransformERC20Error(
|
||||||
@ -318,7 +315,7 @@ contract TransformERC20 is
|
|||||||
// Transfer input tokens.
|
// Transfer input tokens.
|
||||||
if (!LibERC20Transformer.isTokenETH(inputToken)) {
|
if (!LibERC20Transformer.isTokenETH(inputToken)) {
|
||||||
// Token is not ETH, so pull ERC20 tokens.
|
// Token is not ETH, so pull ERC20 tokens.
|
||||||
ITokenSpender(address(this))._spendERC20Tokens(
|
ITokenSpenderFeature(address(this))._spendERC20Tokens(
|
||||||
inputToken,
|
inputToken,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
@ -360,9 +357,12 @@ contract TransformERC20 is
|
|||||||
// Call data.
|
// Call data.
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
IERC20Transformer.transform.selector,
|
IERC20Transformer.transform.selector,
|
||||||
callDataHash,
|
IERC20Transformer.TransformContext({
|
||||||
taker,
|
callDataHash: callDataHash,
|
||||||
transformation.data
|
sender: msg.sender,
|
||||||
|
taker: taker,
|
||||||
|
data: transformation.data
|
||||||
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// Ensure the transformer returned the magic bytes.
|
// Ensure the transformer returned the magic bytes.
|
||||||
@ -389,13 +389,19 @@ contract TransformERC20 is
|
|||||||
view
|
view
|
||||||
returns (bytes32 validCallDataHash)
|
returns (bytes32 validCallDataHash)
|
||||||
{
|
{
|
||||||
|
address quoteSigner = getQuoteSigner();
|
||||||
|
if (quoteSigner == address(0)) {
|
||||||
|
// If no quote signer is configured, then all calldata hashes are
|
||||||
|
// valid.
|
||||||
|
return callDataHash;
|
||||||
|
}
|
||||||
if (signature.length == 0) {
|
if (signature.length == 0) {
|
||||||
return bytes32(0);
|
return bytes32(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ISignatureValidator(address(this)).isValidHashSignature(
|
if (ISignatureValidatorFeature(address(this)).isValidHashSignature(
|
||||||
callDataHash,
|
callDataHash,
|
||||||
getQuoteSigner(),
|
quoteSigner,
|
||||||
signature
|
signature
|
||||||
)) {
|
)) {
|
||||||
return callDataHash;
|
return callDataHash;
|
@ -22,8 +22,8 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
import "../errors/LibCommonRichErrors.sol";
|
import "../errors/LibCommonRichErrors.sol";
|
||||||
import "../errors/LibOwnableRichErrors.sol";
|
import "../errors/LibOwnableRichErrors.sol";
|
||||||
import "../features/IOwnable.sol";
|
import "../features/IOwnableFeature.sol";
|
||||||
import "../features/ISimpleFunctionRegistry.sol";
|
import "../features/ISimpleFunctionRegistryFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Common feature utilities.
|
/// @dev Common feature utilities.
|
||||||
@ -45,7 +45,7 @@ abstract contract FixinCommon {
|
|||||||
/// @dev The caller of this function must be the owner.
|
/// @dev The caller of this function must be the owner.
|
||||||
modifier onlyOwner() virtual {
|
modifier onlyOwner() virtual {
|
||||||
{
|
{
|
||||||
address owner = IOwnable(address(this)).owner();
|
address owner = IOwnableFeature(address(this)).owner();
|
||||||
if (msg.sender != owner) {
|
if (msg.sender != owner) {
|
||||||
LibOwnableRichErrors.OnlyOwnerError(
|
LibOwnableRichErrors.OnlyOwnerError(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
@ -68,7 +68,7 @@ abstract contract FixinCommon {
|
|||||||
function _registerFeatureFunction(bytes4 selector)
|
function _registerFeatureFunction(bytes4 selector)
|
||||||
internal
|
internal
|
||||||
{
|
{
|
||||||
ISimpleFunctionRegistry(address(this)).extend(selector, _implementation);
|
ISimpleFunctionRegistryFeature(address(this)).extend(selector, _implementation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Encode a feature version as a `uint256`.
|
/// @dev Encode a feature version as a `uint256`.
|
||||||
|
@ -22,7 +22,6 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
import "../errors/LibCommonRichErrors.sol";
|
import "../errors/LibCommonRichErrors.sol";
|
||||||
import "../errors/LibOwnableRichErrors.sol";
|
import "../errors/LibOwnableRichErrors.sol";
|
||||||
import "../features/IOwnable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
/// @dev EIP712 helpers for features.
|
/// @dev EIP712 helpers for features.
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
|
import "../errors/LibCommonRichErrors.sol";
|
||||||
|
import "../storage/LibReentrancyGuardStorage.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Common feature utilities.
|
||||||
|
abstract contract FixinReentrancyGuard {
|
||||||
|
|
||||||
|
using LibRichErrorsV06 for bytes;
|
||||||
|
using LibBytesV06 for bytes;
|
||||||
|
|
||||||
|
// Combinable reentrancy flags.
|
||||||
|
/// @dev Reentrancy guard flag for meta-transaction functions.
|
||||||
|
uint256 constant internal REENTRANCY_MTX = 0x1;
|
||||||
|
|
||||||
|
/// @dev Cannot reenter a function with the same reentrancy guard flags.
|
||||||
|
modifier nonReentrant(uint256 reentrancyFlags) virtual {
|
||||||
|
LibReentrancyGuardStorage.Storage storage stor =
|
||||||
|
LibReentrancyGuardStorage.getStorage();
|
||||||
|
{
|
||||||
|
uint256 currentFlags = stor.reentrancyFlags;
|
||||||
|
// Revert if any bits in `reentrancyFlags` has already been set.
|
||||||
|
if ((currentFlags & reentrancyFlags) != 0) {
|
||||||
|
LibCommonRichErrors.IllegalReentrancyError(
|
||||||
|
msg.data.readBytes4(0),
|
||||||
|
reentrancyFlags
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Update reentrancy flags.
|
||||||
|
stor.reentrancyFlags = currentFlags | reentrancyFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
_;
|
||||||
|
|
||||||
|
// Clear reentrancy flags.
|
||||||
|
stor.reentrancyFlags = stor.reentrancyFlags & (~reentrancyFlags);
|
||||||
|
}
|
||||||
|
}
|
@ -20,11 +20,11 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../ZeroEx.sol";
|
import "../ZeroEx.sol";
|
||||||
import "../features/IOwnable.sol";
|
import "../features/IOwnableFeature.sol";
|
||||||
import "../features/TokenSpender.sol";
|
import "../features/TokenSpenderFeature.sol";
|
||||||
import "../features/TransformERC20.sol";
|
import "../features/TransformERC20Feature.sol";
|
||||||
import "../features/SignatureValidator.sol";
|
import "../features/SignatureValidatorFeature.sol";
|
||||||
import "../features/MetaTransactions.sol";
|
import "../features/MetaTransactionsFeature.sol";
|
||||||
import "../external/AllowanceTarget.sol";
|
import "../external/AllowanceTarget.sol";
|
||||||
import "./InitialMigration.sol";
|
import "./InitialMigration.sol";
|
||||||
|
|
||||||
@ -36,12 +36,12 @@ contract FullMigration {
|
|||||||
|
|
||||||
/// @dev Features to add the the proxy contract.
|
/// @dev Features to add the the proxy contract.
|
||||||
struct Features {
|
struct Features {
|
||||||
SimpleFunctionRegistry registry;
|
SimpleFunctionRegistryFeature registry;
|
||||||
Ownable ownable;
|
OwnableFeature ownable;
|
||||||
TokenSpender tokenSpender;
|
TokenSpenderFeature tokenSpender;
|
||||||
TransformERC20 transformERC20;
|
TransformERC20Feature transformERC20;
|
||||||
SignatureValidator signatureValidator;
|
SignatureValidatorFeature signatureValidator;
|
||||||
MetaTransactions metaTransactions;
|
MetaTransactionsFeature metaTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parameters needed to initialize features.
|
/// @dev Parameters needed to initialize features.
|
||||||
@ -109,7 +109,7 @@ contract FullMigration {
|
|||||||
_addFeatures(zeroEx, owner, features, migrateOpts);
|
_addFeatures(zeroEx, owner, features, migrateOpts);
|
||||||
|
|
||||||
// Transfer ownership to the real owner.
|
// Transfer ownership to the real owner.
|
||||||
IOwnable(address(zeroEx)).transferOwnership(owner);
|
IOwnableFeature(address(zeroEx)).transferOwnership(owner);
|
||||||
|
|
||||||
// Self-destruct.
|
// Self-destruct.
|
||||||
this.die(owner);
|
this.die(owner);
|
||||||
@ -142,8 +142,8 @@ contract FullMigration {
|
|||||||
)
|
)
|
||||||
private
|
private
|
||||||
{
|
{
|
||||||
IOwnable ownable = IOwnable(address(zeroEx));
|
IOwnableFeature ownable = IOwnableFeature(address(zeroEx));
|
||||||
// TokenSpender
|
// TokenSpenderFeature
|
||||||
{
|
{
|
||||||
// Create the allowance target.
|
// Create the allowance target.
|
||||||
AllowanceTarget allowanceTarget = new AllowanceTarget();
|
AllowanceTarget allowanceTarget = new AllowanceTarget();
|
||||||
@ -155,42 +155,42 @@ contract FullMigration {
|
|||||||
ownable.migrate(
|
ownable.migrate(
|
||||||
address(features.tokenSpender),
|
address(features.tokenSpender),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
TokenSpender.migrate.selector,
|
TokenSpenderFeature.migrate.selector,
|
||||||
allowanceTarget
|
allowanceTarget
|
||||||
),
|
),
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// TransformERC20
|
// TransformERC20Feature
|
||||||
{
|
{
|
||||||
// Register the feature.
|
// Register the feature.
|
||||||
ownable.migrate(
|
ownable.migrate(
|
||||||
address(features.transformERC20),
|
address(features.transformERC20),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
TransformERC20.migrate.selector,
|
TransformERC20Feature.migrate.selector,
|
||||||
migrateOpts.transformerDeployer
|
migrateOpts.transformerDeployer
|
||||||
),
|
),
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// SignatureValidator
|
// SignatureValidatorFeature
|
||||||
{
|
{
|
||||||
// Register the feature.
|
// Register the feature.
|
||||||
ownable.migrate(
|
ownable.migrate(
|
||||||
address(features.signatureValidator),
|
address(features.signatureValidator),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
SignatureValidator.migrate.selector
|
SignatureValidatorFeature.migrate.selector
|
||||||
),
|
),
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// MetaTransactions
|
// MetaTransactionsFeature
|
||||||
{
|
{
|
||||||
// Register the feature.
|
// Register the feature.
|
||||||
ownable.migrate(
|
ownable.migrate(
|
||||||
address(features.metaTransactions),
|
address(features.metaTransactions),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
MetaTransactions.migrate.selector
|
MetaTransactionsFeature.migrate.selector
|
||||||
),
|
),
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
|
@ -20,9 +20,9 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../ZeroEx.sol";
|
import "../ZeroEx.sol";
|
||||||
import "../features/IBootstrap.sol";
|
import "../features/IBootstrapFeature.sol";
|
||||||
import "../features/SimpleFunctionRegistry.sol";
|
import "../features/SimpleFunctionRegistryFeature.sol";
|
||||||
import "../features/Ownable.sol";
|
import "../features/OwnableFeature.sol";
|
||||||
import "./LibBootstrap.sol";
|
import "./LibBootstrap.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -31,8 +31,8 @@ contract InitialMigration {
|
|||||||
|
|
||||||
/// @dev Features to bootstrap into the the proxy contract.
|
/// @dev Features to bootstrap into the the proxy contract.
|
||||||
struct BootstrapFeatures {
|
struct BootstrapFeatures {
|
||||||
SimpleFunctionRegistry registry;
|
SimpleFunctionRegistryFeature registry;
|
||||||
Ownable ownable;
|
OwnableFeature ownable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev The allowed caller of `initializeZeroEx()`. In production, this would be
|
/// @dev The allowed caller of `initializeZeroEx()`. In production, this would be
|
||||||
@ -70,7 +70,7 @@ contract InitialMigration {
|
|||||||
require(msg.sender == initializeCaller, "InitialMigration/INVALID_SENDER");
|
require(msg.sender == initializeCaller, "InitialMigration/INVALID_SENDER");
|
||||||
|
|
||||||
// Bootstrap the initial feature set.
|
// Bootstrap the initial feature set.
|
||||||
IBootstrap(address(zeroEx)).bootstrap(
|
IBootstrapFeature(address(zeroEx)).bootstrap(
|
||||||
address(this),
|
address(this),
|
||||||
abi.encodeWithSelector(this.bootstrap.selector, owner, features)
|
abi.encodeWithSelector(this.bootstrap.selector, owner, features)
|
||||||
);
|
);
|
||||||
@ -99,26 +99,26 @@ contract InitialMigration {
|
|||||||
LibBootstrap.delegatecallBootstrapFunction(
|
LibBootstrap.delegatecallBootstrapFunction(
|
||||||
address(features.registry),
|
address(features.registry),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
SimpleFunctionRegistry.bootstrap.selector
|
SimpleFunctionRegistryFeature.bootstrap.selector
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize Ownable.
|
// Initialize OwnableFeature.
|
||||||
LibBootstrap.delegatecallBootstrapFunction(
|
LibBootstrap.delegatecallBootstrapFunction(
|
||||||
address(features.ownable),
|
address(features.ownable),
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
Ownable.bootstrap.selector
|
OwnableFeature.bootstrap.selector
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// De-register `SimpleFunctionRegistry._extendSelf`.
|
// De-register `SimpleFunctionRegistryFeature._extendSelf`.
|
||||||
SimpleFunctionRegistry(address(this)).rollback(
|
SimpleFunctionRegistryFeature(address(this)).rollback(
|
||||||
SimpleFunctionRegistry._extendSelf.selector,
|
SimpleFunctionRegistryFeature._extendSelf.selector,
|
||||||
address(0)
|
address(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transfer ownership to the real owner.
|
// Transfer ownership to the real owner.
|
||||||
Ownable(address(this)).transferOwnership(owner);
|
OwnableFeature(address(this)).transferOwnership(owner);
|
||||||
|
|
||||||
success = LibBootstrap.BOOTSTRAP_SUCCESS;
|
success = LibBootstrap.BOOTSTRAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import "./LibStorage.sol";
|
||||||
|
import "../external/IFlashWallet.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Storage helpers for the `FixinReentrancyGuard` mixin.
|
||||||
|
library LibReentrancyGuardStorage {
|
||||||
|
|
||||||
|
/// @dev Storage bucket for this feature.
|
||||||
|
struct Storage {
|
||||||
|
// Reentrancy flags set whenever a non-reentrant function is entered
|
||||||
|
// and cleared when it is exited.
|
||||||
|
uint256 reentrancyFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Get the storage bucket for this contract.
|
||||||
|
function getStorage() internal pure returns (Storage storage stor) {
|
||||||
|
uint256 storageSlot = LibStorage.getStorageSlot(
|
||||||
|
LibStorage.StorageId.ReentrancyGuard
|
||||||
|
);
|
||||||
|
// Dip into assembly to change the slot pointed to by the local
|
||||||
|
// variable `stor`.
|
||||||
|
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
|
||||||
|
assembly { stor_slot := storageSlot }
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,8 @@ library LibStorage {
|
|||||||
Ownable,
|
Ownable,
|
||||||
TokenSpender,
|
TokenSpender,
|
||||||
TransformERC20,
|
TransformERC20,
|
||||||
MetaTransactions
|
MetaTransactions,
|
||||||
|
ReentrancyGuard
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
|
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
|
||||||
|
@ -32,7 +32,6 @@ import "./LibERC20Transformer.sol";
|
|||||||
contract AffiliateFeeTransformer is
|
contract AffiliateFeeTransformer is
|
||||||
Transformer
|
Transformer
|
||||||
{
|
{
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
using LibSafeMathV06 for uint256;
|
using LibSafeMathV06 for uint256;
|
||||||
using LibERC20Transformer for IERC20TokenV06;
|
using LibERC20Transformer for IERC20TokenV06;
|
||||||
@ -51,25 +50,15 @@ contract AffiliateFeeTransformer is
|
|||||||
|
|
||||||
uint256 private constant MAX_UINT256 = uint256(-1);
|
uint256 private constant MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
/// @dev Create this contract.
|
|
||||||
constructor()
|
|
||||||
public
|
|
||||||
Transformer()
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @dev Transfers tokens to recipients.
|
/// @dev Transfers tokens to recipients.
|
||||||
/// @param data ABI-encoded `TokenFee[]`, indicating which tokens to transfer.
|
/// @param context Context information.
|
||||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32, // callDataHash,
|
|
||||||
address payable, // taker,
|
|
||||||
bytes calldata data
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
TokenFee[] memory fees = abi.decode(data, (TokenFee[]));
|
TokenFee[] memory fees = abi.decode(context.data, (TokenFee[]));
|
||||||
|
|
||||||
// Transfer tokens to recipients.
|
// Transfer tokens to recipients.
|
||||||
for (uint256 i = 0; i < fees.length; ++i) {
|
for (uint256 i = 0; i < fees.length; ++i) {
|
||||||
|
@ -27,7 +27,7 @@ import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||||
import "../errors/LibTransformERC20RichErrors.sol";
|
import "../errors/LibTransformERC20RichErrors.sol";
|
||||||
import "../vendor/v3/IExchange.sol";
|
import "../vendor/v3/IExchange.sol";
|
||||||
import "../bridges/IBridgeAdapter.sol";
|
import "./bridges/IBridgeAdapter.sol";
|
||||||
import "./Transformer.sol";
|
import "./Transformer.sol";
|
||||||
import "./LibERC20Transformer.sol";
|
import "./LibERC20Transformer.sol";
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ contract FillQuoteTransformer is
|
|||||||
|
|
||||||
/// @dev Transform data to ABI-encode and pass into `transform()`.
|
/// @dev Transform data to ABI-encode and pass into `transform()`.
|
||||||
struct TransformData {
|
struct TransformData {
|
||||||
// Whether we aer performing a market sell or buy.
|
// Whether we are performing a market sell or buy.
|
||||||
Side side;
|
Side side;
|
||||||
// The token being sold.
|
// The token being sold.
|
||||||
// This should be an actual token, not the ETH pseudo-token.
|
// This should be an actual token, not the ETH pseudo-token.
|
||||||
@ -71,6 +71,15 @@ contract FillQuoteTransformer is
|
|||||||
// For sells, this may be `uint256(-1)` to sell the entire balance of
|
// For sells, this may be `uint256(-1)` to sell the entire balance of
|
||||||
// `sellToken`.
|
// `sellToken`.
|
||||||
uint256 fillAmount;
|
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()`.
|
/// @dev Results of a call to `_fillOrder()`.
|
||||||
@ -90,6 +99,7 @@ contract FillQuoteTransformer is
|
|||||||
uint256 soldAmount;
|
uint256 soldAmount;
|
||||||
uint256 protocolFee;
|
uint256 protocolFee;
|
||||||
uint256 takerTokenBalanceRemaining;
|
uint256 takerTokenBalanceRemaining;
|
||||||
|
bool isRfqtAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Emitted when a trade is skipped due to a lack of funds
|
/// @dev Emitted when a trade is skipped due to a lack of funds
|
||||||
@ -108,6 +118,12 @@ contract FillQuoteTransformer is
|
|||||||
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
|
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
|
||||||
/// @dev Maximum uint256 value.
|
/// @dev Maximum uint256 value.
|
||||||
uint256 private constant MAX_UINT256 = uint256(-1);
|
uint256 private constant MAX_UINT256 = uint256(-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);
|
||||||
|
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||||
|
/// protocol fees will be sent to the sender.
|
||||||
|
address private constant REFUND_RECEIVER_SENDER = address(2);
|
||||||
|
|
||||||
/// @dev The Exchange contract.
|
/// @dev The Exchange contract.
|
||||||
IExchange public immutable exchange;
|
IExchange public immutable exchange;
|
||||||
@ -130,31 +146,27 @@ contract FillQuoteTransformer is
|
|||||||
/// @dev Sell this contract's entire balance of of `sellToken` in exchange
|
/// @dev Sell this contract's entire balance of of `sellToken` in exchange
|
||||||
/// for `buyToken` by filling `orders`. Protocol fees should be attached
|
/// for `buyToken` by filling `orders`. Protocol fees should be attached
|
||||||
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
|
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
|
||||||
/// @param data_ ABI-encoded `TransformData`.
|
/// @param context Context information.
|
||||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32, // callDataHash,
|
|
||||||
address payable, // taker,
|
|
||||||
bytes calldata data_
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
TransformData memory data = abi.decode(data_, (TransformData));
|
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||||
FillState memory state;
|
FillState memory state;
|
||||||
|
|
||||||
// Validate data fields.
|
// Validate data fields.
|
||||||
if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
|
if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
||||||
data_
|
context.data
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
if (data.orders.length != data.signatures.length) {
|
if (data.orders.length != data.signatures.length) {
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
|
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
|
||||||
data_
|
context.data
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +182,14 @@ contract FillQuoteTransformer is
|
|||||||
// Approve the ERC20 proxy to spend `sellToken`.
|
// Approve the ERC20 proxy to spend `sellToken`.
|
||||||
data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
|
data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
|
||||||
|
|
||||||
// Fill the orders.
|
|
||||||
state.protocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
|
state.protocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
|
||||||
state.ethRemaining = address(this).balance;
|
state.ethRemaining = address(this).balance;
|
||||||
|
// RFQT orders can only be filled if we have a valid calldata hash
|
||||||
|
// (calldata was signed), and the actual taker matches the RFQT taker (if set).
|
||||||
|
state.isRfqtAllowed = context.callDataHash != bytes32(0)
|
||||||
|
&& (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.orders.length; ++i) {
|
||||||
// Check if we've hit our targets.
|
// Check if we've hit our targets.
|
||||||
if (data.side == Side.Sell) {
|
if (data.side == Side.Sell) {
|
||||||
@ -248,6 +265,17 @@ contract FillQuoteTransformer is
|
|||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refund unspent protocol fees.
|
||||||
|
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
|
||||||
|
if (data.refundReceiver == REFUND_RECEIVER_TAKER) {
|
||||||
|
context.taker.transfer(state.ethRemaining);
|
||||||
|
} else if (data.refundReceiver == REFUND_RECEIVER_SENDER) {
|
||||||
|
context.sender.transfer(state.ethRemaining);
|
||||||
|
} else {
|
||||||
|
data.refundReceiver.transfer(state.ethRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,6 +443,11 @@ contract FillQuoteTransformer is
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} 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.
|
// Emit an event if we do not have sufficient ETH to cover the protocol fee.
|
||||||
if (state.ethRemaining < state.protocolFee) {
|
if (state.ethRemaining < state.protocolFee) {
|
||||||
emit ProtocolFeeUnfunded(state.ethRemaining, state.protocolFee);
|
emit ProtocolFeeUnfunded(state.ethRemaining, state.protocolFee);
|
||||||
|
@ -25,17 +25,25 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|||||||
/// @dev A transformation callback used in `TransformERC20.transformERC20()`.
|
/// @dev A transformation callback used in `TransformERC20.transformERC20()`.
|
||||||
interface IERC20Transformer {
|
interface IERC20Transformer {
|
||||||
|
|
||||||
|
/// @dev Context information to pass into `transform()` by `TransformERC20.transformERC20()`.
|
||||||
|
struct TransformContext {
|
||||||
|
// The hash of the `TransformERC20.transformERC20()` calldata.
|
||||||
|
// Will be null if the calldata is not signed.
|
||||||
|
bytes32 callDataHash;
|
||||||
|
// The caller of `TransformERC20.transformERC20()`.
|
||||||
|
address payable sender;
|
||||||
|
// taker The taker address, which may be distinct from `sender` in the case
|
||||||
|
// meta-transactions.
|
||||||
|
address payable taker;
|
||||||
|
// Arbitrary data to pass to the transformer.
|
||||||
|
bytes data;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Called from `TransformERC20.transformERC20()`. This will be
|
/// @dev Called from `TransformERC20.transformERC20()`. This will be
|
||||||
/// delegatecalled in the context of the FlashWallet instance being used.
|
/// delegatecalled in the context of the FlashWallet instance being used.
|
||||||
/// @param callDataHash The hash of the `TransformERC20.transformERC20()` calldata.
|
/// @param context Context information.
|
||||||
/// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
|
|
||||||
/// @param data Arbitrary data to pass to the transformer.
|
|
||||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32 callDataHash,
|
|
||||||
address payable taker,
|
|
||||||
bytes calldata data
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
returns (bytes4 success);
|
returns (bytes4 success);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import "./Transformer.sol";
|
||||||
|
import "./LibERC20Transformer.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev A transformer that just emits an event with an arbitrary byte payload.
|
||||||
|
contract LogMetadataTransformer is
|
||||||
|
Transformer
|
||||||
|
{
|
||||||
|
event TransformerMetadata(bytes32 callDataHash, address sender, address taker, bytes data);
|
||||||
|
|
||||||
|
/// @dev Maximum uint256 value.
|
||||||
|
uint256 private constant MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
|
/// @dev Emits an event.
|
||||||
|
/// @param context Context information.
|
||||||
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
|
function transform(TransformContext calldata context)
|
||||||
|
external
|
||||||
|
override
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
emit TransformerMetadata(context.callDataHash, context.sender, context.taker, context.data);
|
||||||
|
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@ -56,19 +56,14 @@ contract PayTakerTransformer is
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Forwards tokens to the taker.
|
/// @dev Forwards tokens to the taker.
|
||||||
/// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
|
/// @param context Context information.
|
||||||
/// @param data_ ABI-encoded `TransformData`, indicating which tokens to transfer.
|
|
||||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32, // callDataHash,
|
|
||||||
address payable taker,
|
|
||||||
bytes calldata data_
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
TransformData memory data = abi.decode(data_, (TransformData));
|
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||||
|
|
||||||
// Transfer tokens directly to the taker.
|
// Transfer tokens directly to the taker.
|
||||||
for (uint256 i = 0; i < data.tokens.length; ++i) {
|
for (uint256 i = 0; i < data.tokens.length; ++i) {
|
||||||
@ -79,7 +74,7 @@ contract PayTakerTransformer is
|
|||||||
amount = data.tokens[i].getTokenBalanceOf(address(this));
|
amount = data.tokens[i].getTokenBalanceOf(address(this));
|
||||||
}
|
}
|
||||||
if (amount != 0) {
|
if (amount != 0) {
|
||||||
data.tokens[i].transformerTransfer(taker, amount);
|
data.tokens[i].transformerTransfer(context.taker, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||||
|
@ -59,22 +59,18 @@ contract WethTransformer is
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Wraps and unwraps WETH.
|
/// @dev Wraps and unwraps WETH.
|
||||||
/// @param data_ ABI-encoded `TransformData`, indicating which token to wrap/umwrap.
|
/// @param context Context information.
|
||||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32, // callDataHash,
|
|
||||||
address payable, // taker,
|
|
||||||
bytes calldata data_
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
TransformData memory data = abi.decode(data_, (TransformData));
|
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||||
if (!data.token.isTokenETH() && data.token != weth) {
|
if (!data.token.isTokenETH() && data.token != weth) {
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
||||||
data_
|
context.data
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import "./mixins/MixinAdapterAddresses.sol";
|
|||||||
import "./mixins/MixinBalancer.sol";
|
import "./mixins/MixinBalancer.sol";
|
||||||
import "./mixins/MixinCurve.sol";
|
import "./mixins/MixinCurve.sol";
|
||||||
import "./mixins/MixinKyber.sol";
|
import "./mixins/MixinKyber.sol";
|
||||||
|
import "./mixins/MixinMooniswap.sol";
|
||||||
import "./mixins/MixinMStable.sol";
|
import "./mixins/MixinMStable.sol";
|
||||||
import "./mixins/MixinOasis.sol";
|
import "./mixins/MixinOasis.sol";
|
||||||
import "./mixins/MixinUniswap.sol";
|
import "./mixins/MixinUniswap.sol";
|
||||||
@ -34,6 +35,7 @@ contract BridgeAdapter is
|
|||||||
MixinBalancer,
|
MixinBalancer,
|
||||||
MixinCurve,
|
MixinCurve,
|
||||||
MixinKyber,
|
MixinKyber,
|
||||||
|
MixinMooniswap,
|
||||||
MixinMStable,
|
MixinMStable,
|
||||||
MixinOasis,
|
MixinOasis,
|
||||||
MixinUniswap,
|
MixinUniswap,
|
||||||
@ -44,6 +46,7 @@ contract BridgeAdapter is
|
|||||||
address private immutable BALANCER_BRIDGE_ADDRESS;
|
address private immutable BALANCER_BRIDGE_ADDRESS;
|
||||||
address private immutable CURVE_BRIDGE_ADDRESS;
|
address private immutable CURVE_BRIDGE_ADDRESS;
|
||||||
address private immutable KYBER_BRIDGE_ADDRESS;
|
address private immutable KYBER_BRIDGE_ADDRESS;
|
||||||
|
address private immutable MOONISWAP_BRIDGE_ADDRESS;
|
||||||
address private immutable MSTABLE_BRIDGE_ADDRESS;
|
address private immutable MSTABLE_BRIDGE_ADDRESS;
|
||||||
address private immutable OASIS_BRIDGE_ADDRESS;
|
address private immutable OASIS_BRIDGE_ADDRESS;
|
||||||
address private immutable UNISWAP_BRIDGE_ADDRESS;
|
address private immutable UNISWAP_BRIDGE_ADDRESS;
|
||||||
@ -57,8 +60,8 @@ contract BridgeAdapter is
|
|||||||
/// @param from The bridge address, indicating the underlying source of the fill.
|
/// @param from The bridge address, indicating the underlying source of the fill.
|
||||||
/// @param to The `to` address, currrently `address(this)`
|
/// @param to The `to` address, currrently `address(this)`
|
||||||
event ERC20BridgeTransfer(
|
event ERC20BridgeTransfer(
|
||||||
address inputToken,
|
IERC20TokenV06 inputToken,
|
||||||
address outputToken,
|
IERC20TokenV06 outputToken,
|
||||||
uint256 inputTokenAmount,
|
uint256 inputTokenAmount,
|
||||||
uint256 outputTokenAmount,
|
uint256 outputTokenAmount,
|
||||||
address from,
|
address from,
|
||||||
@ -70,6 +73,7 @@ contract BridgeAdapter is
|
|||||||
MixinBalancer()
|
MixinBalancer()
|
||||||
MixinCurve()
|
MixinCurve()
|
||||||
MixinKyber(addresses)
|
MixinKyber(addresses)
|
||||||
|
MixinMooniswap(addresses)
|
||||||
MixinMStable(addresses)
|
MixinMStable(addresses)
|
||||||
MixinOasis(addresses)
|
MixinOasis(addresses)
|
||||||
MixinUniswap(addresses)
|
MixinUniswap(addresses)
|
||||||
@ -79,6 +83,7 @@ contract BridgeAdapter is
|
|||||||
BALANCER_BRIDGE_ADDRESS = addresses.balancerBridge;
|
BALANCER_BRIDGE_ADDRESS = addresses.balancerBridge;
|
||||||
CURVE_BRIDGE_ADDRESS = addresses.curveBridge;
|
CURVE_BRIDGE_ADDRESS = addresses.curveBridge;
|
||||||
KYBER_BRIDGE_ADDRESS = addresses.kyberBridge;
|
KYBER_BRIDGE_ADDRESS = addresses.kyberBridge;
|
||||||
|
MOONISWAP_BRIDGE_ADDRESS = addresses.mooniswapBridge;
|
||||||
MSTABLE_BRIDGE_ADDRESS = addresses.mStableBridge;
|
MSTABLE_BRIDGE_ADDRESS = addresses.mStableBridge;
|
||||||
OASIS_BRIDGE_ADDRESS = addresses.oasisBridge;
|
OASIS_BRIDGE_ADDRESS = addresses.oasisBridge;
|
||||||
UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge;
|
UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge;
|
||||||
@ -87,19 +92,19 @@ contract BridgeAdapter is
|
|||||||
|
|
||||||
function trade(
|
function trade(
|
||||||
bytes calldata makerAssetData,
|
bytes calldata makerAssetData,
|
||||||
address fromTokenAddress,
|
IERC20TokenV06 sellToken,
|
||||||
uint256 sellAmount
|
uint256 sellAmount
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
address bridgeAddress,
|
address bridgeAddress,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
) = abi.decode(
|
) = abi.decode(
|
||||||
makerAssetData[4:],
|
makerAssetData[4:],
|
||||||
(address, address, bytes)
|
(IERC20TokenV06, address, bytes)
|
||||||
);
|
);
|
||||||
require(
|
require(
|
||||||
bridgeAddress != address(this) && bridgeAddress != address(0),
|
bridgeAddress != address(this) && bridgeAddress != address(0),
|
||||||
@ -108,65 +113,71 @@ contract BridgeAdapter is
|
|||||||
|
|
||||||
if (bridgeAddress == CURVE_BRIDGE_ADDRESS) {
|
if (bridgeAddress == CURVE_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeCurve(
|
boughtAmount = _tradeCurve(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == UNISWAP_V2_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == UNISWAP_V2_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeUniswapV2(
|
boughtAmount = _tradeUniswapV2(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == UNISWAP_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == UNISWAP_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeUniswap(
|
boughtAmount = _tradeUniswap(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeBalancer(
|
boughtAmount = _tradeBalancer(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == KYBER_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == KYBER_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeKyber(
|
boughtAmount = _tradeKyber(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
bridgeData
|
||||||
|
);
|
||||||
|
} else if (bridgeAddress == MOONISWAP_BRIDGE_ADDRESS) {
|
||||||
|
boughtAmount = _tradeMooniswap(
|
||||||
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == MSTABLE_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == MSTABLE_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeMStable(
|
boughtAmount = _tradeMStable(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else if (bridgeAddress == OASIS_BRIDGE_ADDRESS) {
|
} else if (bridgeAddress == OASIS_BRIDGE_ADDRESS) {
|
||||||
boughtAmount = _tradeOasis(
|
boughtAmount = _tradeOasis(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
boughtAmount = _tradeZeroExBridge(
|
boughtAmount = _tradeZeroExBridge(
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
fromTokenAddress,
|
sellToken,
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
|
// Do not emit an event. The bridge contract should emit one itself.
|
||||||
|
return boughtAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit ERC20BridgeTransfer(
|
emit ERC20BridgeTransfer(
|
||||||
fromTokenAddress,
|
sellToken,
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
boughtAmount,
|
boughtAmount,
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
return boughtAmount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,6 +26,7 @@ contract MixinAdapterAddresses
|
|||||||
address balancerBridge;
|
address balancerBridge;
|
||||||
address curveBridge;
|
address curveBridge;
|
||||||
address kyberBridge;
|
address kyberBridge;
|
||||||
|
address mooniswapBridge;
|
||||||
address mStableBridge;
|
address mStableBridge;
|
||||||
address oasisBridge;
|
address oasisBridge;
|
||||||
address uniswapBridge;
|
address uniswapBridge;
|
@ -32,9 +32,9 @@ interface IBalancerPool {
|
|||||||
/// @return spotPriceAfter The new marginal spot price of the given
|
/// @return spotPriceAfter The new marginal spot price of the given
|
||||||
/// token pair for this pool.
|
/// token pair for this pool.
|
||||||
function swapExactAmountIn(
|
function swapExactAmountIn(
|
||||||
address tokenIn,
|
IERC20TokenV06 tokenIn,
|
||||||
uint tokenAmountIn,
|
uint tokenAmountIn,
|
||||||
address tokenOut,
|
IERC20TokenV06 tokenOut,
|
||||||
uint minAmountOut,
|
uint minAmountOut,
|
||||||
uint maxPrice
|
uint maxPrice
|
||||||
) external returns (uint tokenAmountOut, uint spotPriceAfter);
|
) external returns (uint tokenAmountOut, uint spotPriceAfter);
|
||||||
@ -45,7 +45,7 @@ contract MixinBalancer {
|
|||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
function _tradeBalancer(
|
function _tradeBalancer(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
@ -53,19 +53,19 @@ contract MixinBalancer {
|
|||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data.
|
// Decode the bridge data.
|
||||||
(address fromTokenAddress, address poolAddress) = abi.decode(
|
(IERC20TokenV06 sellToken, IBalancerPool pool) = abi.decode(
|
||||||
bridgeData,
|
bridgeData,
|
||||||
(address, address)
|
(IERC20TokenV06, IBalancerPool)
|
||||||
);
|
);
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
sellToken.approveIfBelow(
|
||||||
poolAddress,
|
address(pool),
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
// Sell all of this contract's `fromTokenAddress` token balance.
|
// Sell all of this contract's `sellToken` token balance.
|
||||||
(boughtAmount,) = IBalancerPool(poolAddress).swapExactAmountIn(
|
(boughtAmount,) = pool.swapExactAmountIn(
|
||||||
fromTokenAddress, // tokenIn
|
sellToken, // tokenIn
|
||||||
sellAmount, // tokenAmountIn
|
sellAmount, // tokenAmountIn
|
||||||
toTokenAddress, // tokenOut
|
buyToken, // tokenOut
|
||||||
1, // minAmountOut
|
1, // minAmountOut
|
||||||
uint256(-1) // maxPrice
|
uint256(-1) // maxPrice
|
||||||
);
|
);
|
@ -34,13 +34,13 @@ contract MixinCurve {
|
|||||||
struct CurveBridgeData {
|
struct CurveBridgeData {
|
||||||
address curveAddress;
|
address curveAddress;
|
||||||
bytes4 exchangeFunctionSelector;
|
bytes4 exchangeFunctionSelector;
|
||||||
address fromTokenAddress;
|
IERC20TokenV06 sellToken;
|
||||||
int128 fromCoinIdx;
|
int128 fromCoinIdx;
|
||||||
int128 toCoinIdx;
|
int128 toCoinIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _tradeCurve(
|
function _tradeCurve(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
@ -49,8 +49,8 @@ contract MixinCurve {
|
|||||||
{
|
{
|
||||||
// Decode the bridge data to get the Curve metadata.
|
// Decode the bridge data to get the Curve metadata.
|
||||||
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
|
||||||
IERC20TokenV06(data.fromTokenAddress).approveIfBelow(data.curveAddress, sellAmount);
|
data.sellToken.approveIfBelow(data.curveAddress, sellAmount);
|
||||||
uint256 beforeBalance = IERC20TokenV06(toTokenAddress).balanceOf(address(this));
|
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||||
(bool success, bytes memory resultData) =
|
(bool success, bytes memory resultData) =
|
||||||
data.curveAddress.call(abi.encodeWithSelector(
|
data.curveAddress.call(abi.encodeWithSelector(
|
||||||
data.exchangeFunctionSelector,
|
data.exchangeFunctionSelector,
|
||||||
@ -64,7 +64,6 @@ contract MixinCurve {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
resultData.rrevert();
|
resultData.rrevert();
|
||||||
}
|
}
|
||||||
|
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
|
||||||
return IERC20TokenV06(toTokenAddress).balanceOf(address(this)).safeSub(beforeBalance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,24 +26,27 @@ import "./MixinAdapterAddresses.sol";
|
|||||||
|
|
||||||
interface IKyberNetworkProxy {
|
interface IKyberNetworkProxy {
|
||||||
|
|
||||||
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
|
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens
|
||||||
/// @param sellTokenAddress Token to sell.
|
/// using a hint for the reserve.
|
||||||
|
/// @param sellToken Token to sell.
|
||||||
/// @param sellAmount Amount of tokens to sell.
|
/// @param sellAmount Amount of tokens to sell.
|
||||||
/// @param buyTokenAddress Token to buy.
|
/// @param buyToken Token to buy.
|
||||||
/// @param recipientAddress Address to send bought tokens to.
|
/// @param recipientAddress Address to send bought tokens to.
|
||||||
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
||||||
/// @param minConversionRate The minimal conversion rate. If actual rate
|
/// @param minConversionRate The minimal conversion rate. If actual rate
|
||||||
/// is lower, trade is canceled.
|
/// is lower, trade is canceled.
|
||||||
/// @param walletId The wallet ID to send part of the fees
|
/// @param walletId The wallet ID to send part of the fees
|
||||||
|
/// @param hint The hint for the selective inclusion (or exclusion) of reserves
|
||||||
/// @return boughtAmount Amount of tokens bought.
|
/// @return boughtAmount Amount of tokens bought.
|
||||||
function trade(
|
function tradeWithHint(
|
||||||
address sellTokenAddress,
|
IERC20TokenV06 sellToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
address buyTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
address payable recipientAddress,
|
address payable recipientAddress,
|
||||||
uint256 maxBuyTokenAmount,
|
uint256 maxBuyTokenAmount,
|
||||||
uint256 minConversionRate,
|
uint256 minConversionRate,
|
||||||
address walletId
|
address payable walletId,
|
||||||
|
bytes calldata hint
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
@ -53,7 +56,6 @@ interface IKyberNetworkProxy {
|
|||||||
contract MixinKyber is
|
contract MixinKyber is
|
||||||
MixinAdapterAddresses
|
MixinAdapterAddresses
|
||||||
{
|
{
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
/// @dev Address indicating the trade is using ETH
|
/// @dev Address indicating the trade is using ETH
|
||||||
@ -71,41 +73,39 @@ contract MixinKyber is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _tradeKyber(
|
function _tradeKyber(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
(IERC20TokenV06 sellToken, bytes memory hint) =
|
||||||
address fromTokenAddress = abi.decode(bridgeData, (address));
|
abi.decode(bridgeData, (IERC20TokenV06, bytes));
|
||||||
uint256 payableAmount;
|
|
||||||
|
|
||||||
if (fromTokenAddress != address(WETH)) {
|
uint256 payableAmount = 0;
|
||||||
|
if (sellToken != WETH) {
|
||||||
// If the input token is not WETH, grant an allowance to the exchange
|
// If the input token is not WETH, grant an allowance to the exchange
|
||||||
// to spend them.
|
// to spend them.
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
sellToken.approveIfBelow(
|
||||||
address(KYBER_NETWORK_PROXY),
|
address(KYBER_NETWORK_PROXY),
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// If the input token is WETH, unwrap it and attach it to the call.
|
// If the input token is WETH, unwrap it and attach it to the call.
|
||||||
fromTokenAddress = KYBER_ETH_ADDRESS;
|
|
||||||
payableAmount = sellAmount;
|
payableAmount = sellAmount;
|
||||||
WETH.withdraw(payableAmount);
|
WETH.withdraw(payableAmount);
|
||||||
}
|
}
|
||||||
bool isToTokenWeth = toTokenAddress == address(WETH);
|
|
||||||
|
|
||||||
// Try to sell all of this contract's input token balance through
|
// Try to sell all of this contract's input token balance through
|
||||||
// `KyberNetworkProxy.trade()`.
|
// `KyberNetworkProxy.trade()`.
|
||||||
boughtAmount = KYBER_NETWORK_PROXY.trade{ value: payableAmount }(
|
boughtAmount = KYBER_NETWORK_PROXY.tradeWithHint{ value: payableAmount }(
|
||||||
// Input token.
|
// Input token.
|
||||||
fromTokenAddress,
|
sellToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : sellToken,
|
||||||
// Sell amount.
|
// Sell amount.
|
||||||
sellAmount,
|
sellAmount,
|
||||||
// Output token.
|
// Output token.
|
||||||
isToTokenWeth ? KYBER_ETH_ADDRESS : toTokenAddress,
|
buyToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : buyToken,
|
||||||
// Transfer to this contract
|
// Transfer to this contract
|
||||||
address(uint160(address(this))),
|
address(uint160(address(this))),
|
||||||
// Buy as much as possible.
|
// Buy as much as possible.
|
||||||
@ -113,9 +113,11 @@ contract MixinKyber is
|
|||||||
// Lowest minimum conversion rate
|
// Lowest minimum conversion rate
|
||||||
1,
|
1,
|
||||||
// No affiliate address.
|
// No affiliate address.
|
||||||
address(0)
|
address(0),
|
||||||
|
hint
|
||||||
);
|
);
|
||||||
if (isToTokenWeth) {
|
// If receving ETH, wrap it to WETH.
|
||||||
|
if (buyToken == WETH) {
|
||||||
WETH.deposit{ value: boughtAmount }();
|
WETH.deposit{ value: boughtAmount }();
|
||||||
}
|
}
|
||||||
return boughtAmount;
|
return boughtAmount;
|
@ -27,19 +27,18 @@ import "./MixinAdapterAddresses.sol";
|
|||||||
interface IMStable {
|
interface IMStable {
|
||||||
|
|
||||||
function swap(
|
function swap(
|
||||||
address _input,
|
IERC20TokenV06 sellToken,
|
||||||
address _output,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 _quantity,
|
uint256 sellAmount,
|
||||||
address _recipient
|
address recipient
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 output);
|
returns (uint256 boughtAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
contract MixinMStable is
|
contract MixinMStable is
|
||||||
MixinAdapterAddresses
|
MixinAdapterAddresses
|
||||||
{
|
{
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
/// @dev Mainnet address of the mStable mUSD contract.
|
/// @dev Mainnet address of the mStable mUSD contract.
|
||||||
@ -52,21 +51,21 @@ contract MixinMStable is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _tradeMStable(
|
function _tradeMStable(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
// Decode the bridge data to get the `sellToken`.
|
||||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
// Grant an allowance to the exchange to spend `sellToken` token.
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(address(MSTABLE), sellAmount);
|
sellToken.approveIfBelow(address(MSTABLE), sellAmount);
|
||||||
|
|
||||||
boughtAmount = MSTABLE.swap(
|
boughtAmount = MSTABLE.swap(
|
||||||
fromTokenAddress,
|
sellToken,
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Moooniswap pool interface.
|
||||||
|
interface IMooniswapPool {
|
||||||
|
|
||||||
|
function swap(
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
uint256 minBoughtAmount,
|
||||||
|
address referrer
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint256 boughtAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev BridgeAdapter mixin for mooniswap.
|
||||||
|
contract MixinMooniswap is
|
||||||
|
MixinAdapterAddresses
|
||||||
|
{
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
using LibERC20TokenV06 for IEtherTokenV06;
|
||||||
|
|
||||||
|
/// @dev WETH token.
|
||||||
|
IEtherTokenV06 private immutable WETH;
|
||||||
|
|
||||||
|
constructor(AdapterAddresses memory addresses)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
WETH = IEtherTokenV06(addresses.weth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tradeMooniswap(
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
(IERC20TokenV06 sellToken, IMooniswapPool pool) =
|
||||||
|
abi.decode(bridgeData, (IERC20TokenV06, IMooniswapPool));
|
||||||
|
|
||||||
|
// Convert WETH to ETH.
|
||||||
|
uint256 ethValue = 0;
|
||||||
|
if (sellToken == WETH) {
|
||||||
|
WETH.withdraw(sellAmount);
|
||||||
|
ethValue = sellAmount;
|
||||||
|
} else {
|
||||||
|
// Grant the pool an allowance.
|
||||||
|
sellToken.approveIfBelow(
|
||||||
|
address(pool),
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
boughtAmount = pool.swap{value: ethValue}(
|
||||||
|
sellToken == WETH ? IERC20TokenV06(0) : sellToken,
|
||||||
|
buyToken == WETH ? IERC20TokenV06(0) : buyToken,
|
||||||
|
sellAmount,
|
||||||
|
1,
|
||||||
|
address(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wrap ETH to WETH.
|
||||||
|
if (buyToken == WETH) {
|
||||||
|
WETH.deposit{value:boughtAmount}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,26 +25,25 @@ import "./MixinAdapterAddresses.sol";
|
|||||||
|
|
||||||
interface IOasis {
|
interface IOasis {
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
/// @dev Sell `sellAmount` of `sellToken` token and receive `buyToken` token.
|
||||||
/// @param fromToken The token being sold.
|
/// @param sellToken The token being sold.
|
||||||
/// @param sellAmount The amount of `fromToken` token being sold.
|
/// @param sellAmount The amount of `sellToken` token being sold.
|
||||||
/// @param toToken The token being bought.
|
/// @param buyToken The token being bought.
|
||||||
/// @param minFillAmount Minimum amount of `toToken` token to buy.
|
/// @param minBoughtAmount Minimum amount of `buyToken` token to buy.
|
||||||
/// @return fillAmount Amount of `toToken` bought.
|
/// @return boughtAmount Amount of `buyToken` bought.
|
||||||
function sellAllAmount(
|
function sellAllAmount(
|
||||||
address fromToken,
|
IERC20TokenV06 sellToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
address toToken,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 minFillAmount
|
uint256 minBoughtAmount
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 fillAmount);
|
returns (uint256 boughtAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
contract MixinOasis is
|
contract MixinOasis is
|
||||||
MixinAdapterAddresses
|
MixinAdapterAddresses
|
||||||
{
|
{
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
/// @dev Mainnet address of the Oasis `MatchingMarket` contract.
|
/// @dev Mainnet address of the Oasis `MatchingMarket` contract.
|
||||||
@ -57,25 +56,25 @@ contract MixinOasis is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _tradeOasis(
|
function _tradeOasis(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
// Decode the bridge data to get the `sellToken`.
|
||||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
// Grant an allowance to the exchange to spend `sellToken` token.
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
sellToken.approveIfBelow(
|
||||||
address(OASIS),
|
address(OASIS),
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
// Try to sell all of this contract's `sellToken` token balance.
|
||||||
boughtAmount = OASIS.sellAllAmount(
|
boughtAmount = OASIS.sellAllAmount(
|
||||||
fromTokenAddress,
|
sellToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
// min fill amount
|
// min fill amount
|
||||||
1
|
1
|
||||||
);
|
);
|
@ -27,11 +27,11 @@ import "./MixinAdapterAddresses.sol";
|
|||||||
interface IUniswapExchangeFactory {
|
interface IUniswapExchangeFactory {
|
||||||
|
|
||||||
/// @dev Get the exchange for a token.
|
/// @dev Get the exchange for a token.
|
||||||
/// @param tokenAddress The address of the token contract.
|
/// @param token The token contract.
|
||||||
function getExchange(address tokenAddress)
|
function getExchange(IERC20TokenV06 token)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (address);
|
returns (IUniswapExchange exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUniswapExchange {
|
interface IUniswapExchange {
|
||||||
@ -71,7 +71,7 @@ interface IUniswapExchange {
|
|||||||
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
||||||
/// @param deadline Time when this order expires.
|
/// @param deadline Time when this order expires.
|
||||||
/// @param recipient Who to transfer the tokens to.
|
/// @param recipient Who to transfer the tokens to.
|
||||||
/// @param toTokenAddress The token being bought.
|
/// @param buyToken The token being bought.
|
||||||
/// @return tokensBought Amount of tokens bought.
|
/// @return tokensBought Amount of tokens bought.
|
||||||
function tokenToTokenTransferInput(
|
function tokenToTokenTransferInput(
|
||||||
uint256 tokensSold,
|
uint256 tokensSold,
|
||||||
@ -79,7 +79,7 @@ interface IUniswapExchange {
|
|||||||
uint256 minEthBought,
|
uint256 minEthBought,
|
||||||
uint256 deadline,
|
uint256 deadline,
|
||||||
address recipient,
|
address recipient,
|
||||||
address toTokenAddress
|
IERC20TokenV06 buyToken
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 tokensBought);
|
returns (uint256 tokensBought);
|
||||||
@ -89,14 +89,14 @@ interface IUniswapExchange {
|
|||||||
/// @param minTokensBought The minimum number of tokens to buy.
|
/// @param minTokensBought The minimum number of tokens to buy.
|
||||||
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
/// @param minEthBought The minimum amount of intermediate ETH to buy.
|
||||||
/// @param deadline Time when this order expires.
|
/// @param deadline Time when this order expires.
|
||||||
/// @param toTokenAddress The token being bought.
|
/// @param buyToken The token being bought.
|
||||||
/// @return tokensBought Amount of tokens bought.
|
/// @return tokensBought Amount of tokens bought.
|
||||||
function tokenToTokenSwapInput(
|
function tokenToTokenSwapInput(
|
||||||
uint256 tokensSold,
|
uint256 tokensSold,
|
||||||
uint256 minTokensBought,
|
uint256 minTokensBought,
|
||||||
uint256 minEthBought,
|
uint256 minEthBought,
|
||||||
uint256 deadline,
|
uint256 deadline,
|
||||||
address toTokenAddress
|
IERC20TokenV06 buyToken
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
returns (uint256 tokensBought);
|
returns (uint256 tokensBought);
|
||||||
@ -105,7 +105,6 @@ interface IUniswapExchange {
|
|||||||
contract MixinUniswap is
|
contract MixinUniswap is
|
||||||
MixinAdapterAddresses
|
MixinAdapterAddresses
|
||||||
{
|
{
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
/// @dev Mainnet address of the WETH contract.
|
/// @dev Mainnet address of the WETH contract.
|
||||||
@ -121,27 +120,27 @@ contract MixinUniswap is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _tradeUniswap(
|
function _tradeUniswap(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
// Decode the bridge data to get the `sellToken`.
|
||||||
(address fromTokenAddress) = abi.decode(bridgeData, (address));
|
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
|
||||||
|
|
||||||
// Get the exchange for the token pair.
|
// Get the exchange for the token pair.
|
||||||
IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
|
IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
|
||||||
fromTokenAddress,
|
sellToken,
|
||||||
toTokenAddress
|
buyToken
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert from WETH to a token.
|
// Convert from WETH to a token.
|
||||||
if (fromTokenAddress == address(WETH)) {
|
if (sellToken == WETH) {
|
||||||
// Unwrap the WETH.
|
// Unwrap the WETH.
|
||||||
WETH.withdraw(sellAmount);
|
WETH.withdraw(sellAmount);
|
||||||
// Buy as much of `toTokenAddress` token with ETH as possible
|
// Buy as much of `buyToken` token with ETH as possible
|
||||||
boughtAmount = exchange.ethToTokenTransferInput{ value: sellAmount }(
|
boughtAmount = exchange.ethToTokenTransferInput{ value: sellAmount }(
|
||||||
// Minimum buy amount.
|
// Minimum buy amount.
|
||||||
1,
|
1,
|
||||||
@ -152,13 +151,13 @@ contract MixinUniswap is
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Convert from a token to WETH.
|
// Convert from a token to WETH.
|
||||||
} else if (toTokenAddress == address(WETH)) {
|
} else if (buyToken == WETH) {
|
||||||
// Grant the exchange an allowance.
|
// Grant the exchange an allowance.
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
sellToken.approveIfBelow(
|
||||||
address(exchange),
|
address(exchange),
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
// Buy as much ETH with `fromTokenAddress` token as possible.
|
// Buy as much ETH with `sellToken` token as possible.
|
||||||
boughtAmount = exchange.tokenToEthSwapInput(
|
boughtAmount = exchange.tokenToEthSwapInput(
|
||||||
// Sell all tokens we hold.
|
// Sell all tokens we hold.
|
||||||
sellAmount,
|
sellAmount,
|
||||||
@ -172,11 +171,11 @@ contract MixinUniswap is
|
|||||||
// Convert from one token to another.
|
// Convert from one token to another.
|
||||||
} else {
|
} else {
|
||||||
// Grant the exchange an allowance.
|
// Grant the exchange an allowance.
|
||||||
IERC20TokenV06(fromTokenAddress).approveIfBelow(
|
sellToken.approveIfBelow(
|
||||||
address(exchange),
|
address(exchange),
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
|
// Buy as much `buyToken` token with `sellToken` token
|
||||||
boughtAmount = exchange.tokenToTokenSwapInput(
|
boughtAmount = exchange.tokenToTokenSwapInput(
|
||||||
// Sell all tokens we hold.
|
// Sell all tokens we hold.
|
||||||
sellAmount,
|
sellAmount,
|
||||||
@ -186,8 +185,8 @@ contract MixinUniswap is
|
|||||||
1,
|
1,
|
||||||
// Expires after this block.
|
// Expires after this block.
|
||||||
block.timestamp,
|
block.timestamp,
|
||||||
// Convert to `toTokenAddress`.
|
// Convert to `buyToken`.
|
||||||
toTokenAddress
|
buyToken
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,24 +196,21 @@ contract MixinUniswap is
|
|||||||
/// @dev Retrieves the uniswap exchange for a given token pair.
|
/// @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 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.
|
/// In th ecase of a token-token exchange, this will be the first token.
|
||||||
/// @param fromTokenAddress The address of the token we are converting from.
|
/// @param sellToken The address of the token we are converting from.
|
||||||
/// @param toTokenAddress The address of the token we are converting to.
|
/// @param buyToken The address of the token we are converting to.
|
||||||
/// @return exchange The uniswap exchange.
|
/// @return exchange The uniswap exchange.
|
||||||
function _getUniswapExchangeForTokenPair(
|
function _getUniswapExchangeForTokenPair(
|
||||||
address fromTokenAddress,
|
IERC20TokenV06 sellToken,
|
||||||
address toTokenAddress
|
IERC20TokenV06 buyToken
|
||||||
)
|
)
|
||||||
private
|
private
|
||||||
view
|
view
|
||||||
returns (IUniswapExchange exchange)
|
returns (IUniswapExchange exchange)
|
||||||
{
|
{
|
||||||
address exchangeTokenAddress = fromTokenAddress;
|
|
||||||
// Whichever isn't WETH is the exchange token.
|
// Whichever isn't WETH is the exchange token.
|
||||||
if (fromTokenAddress == address(WETH)) {
|
exchange = sellToken == WETH
|
||||||
exchangeTokenAddress = toTokenAddress;
|
? UNISWAP_EXCHANGE_FACTORY.getExchange(buyToken)
|
||||||
}
|
: UNISWAP_EXCHANGE_FACTORY.getExchange(sellToken);
|
||||||
exchange = IUniswapExchange(UNISWAP_EXCHANGE_FACTORY.getExchange(exchangeTokenAddress));
|
|
||||||
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
|
||||||
return exchange;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,7 +50,6 @@ interface IUniswapV2Router02 {
|
|||||||
contract MixinUniswapV2 is
|
contract MixinUniswapV2 is
|
||||||
MixinAdapterAddresses
|
MixinAdapterAddresses
|
||||||
{
|
{
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
/// @dev Mainnet address of the `UniswapV2Router02` contract.
|
/// @dev Mainnet address of the `UniswapV2Router02` contract.
|
||||||
@ -63,21 +62,23 @@ contract MixinUniswapV2 is
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _tradeUniswapV2(
|
function _tradeUniswapV2(
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
// Decode the bridge data to get the `fromTokenAddress`.
|
|
||||||
// solhint-disable indent
|
// solhint-disable indent
|
||||||
address[] memory path = abi.decode(bridgeData, (address[]));
|
address[] memory path = abi.decode(bridgeData, (address[]));
|
||||||
// solhint-enable indent
|
// solhint-enable indent
|
||||||
|
|
||||||
require(path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
require(path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||||
require(path[path.length - 1] == toTokenAddress, "UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
|
require(
|
||||||
// Grant the Uniswap router an allowance.
|
path[path.length - 1] == address(buyToken),
|
||||||
|
"UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||||
|
);
|
||||||
|
// Grant the Uniswap router an allowance to sell the first token.
|
||||||
IERC20TokenV06(path[0]).approveIfBelow(
|
IERC20TokenV06(path[0]).approveIfBelow(
|
||||||
address(UNISWAP_V2_ROUTER),
|
address(UNISWAP_V2_ROUTER),
|
||||||
sellAmount
|
sellAmount
|
||||||
@ -88,7 +89,7 @@ contract MixinUniswapV2 is
|
|||||||
sellAmount,
|
sellAmount,
|
||||||
// Minimum buy amount.
|
// Minimum buy amount.
|
||||||
1,
|
1,
|
||||||
// Convert `fromTokenAddress` to `toTokenAddress`.
|
// Convert to `buyToken` along this path.
|
||||||
path,
|
path,
|
||||||
// Recipient is `this`.
|
// Recipient is `this`.
|
||||||
address(this),
|
address(this),
|
@ -24,15 +24,15 @@ import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
|||||||
|
|
||||||
interface IERC20Bridge {
|
interface IERC20Bridge {
|
||||||
|
|
||||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
/// @dev Transfers `amount` of the ERC20 `buyToken` from `from` to `to`.
|
||||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
/// @param buyToken The address of the ERC20 token to transfer.
|
||||||
/// @param from Address to transfer asset from.
|
/// @param from Address to transfer asset from.
|
||||||
/// @param to Address to transfer asset to.
|
/// @param to Address to transfer asset to.
|
||||||
/// @param amount Amount of asset to transfer.
|
/// @param amount Amount of asset to transfer.
|
||||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||||
function bridgeTransferFrom(
|
function bridgeTransferFrom(
|
||||||
address tokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
@ -49,28 +49,27 @@ contract MixinZeroExBridge {
|
|||||||
|
|
||||||
function _tradeZeroExBridge(
|
function _tradeZeroExBridge(
|
||||||
address bridgeAddress,
|
address bridgeAddress,
|
||||||
address fromTokenAddress,
|
IERC20TokenV06 sellToken,
|
||||||
address toTokenAddress,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount,
|
uint256 sellAmount,
|
||||||
bytes memory bridgeData
|
bytes memory bridgeData
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
uint256 balanceBefore = IERC20TokenV06(toTokenAddress).balanceOf(address(this));
|
uint256 balanceBefore = buyToken.balanceOf(address(this));
|
||||||
// Trade the good old fashioned way
|
// Trade the good old fashioned way
|
||||||
IERC20TokenV06(fromTokenAddress).compatTransfer(
|
sellToken.compatTransfer(
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
sellAmount
|
sellAmount
|
||||||
);
|
);
|
||||||
IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
IERC20Bridge(bridgeAddress).bridgeTransferFrom(
|
||||||
toTokenAddress,
|
buyToken,
|
||||||
bridgeAddress,
|
address(bridgeAddress),
|
||||||
address(this),
|
address(this),
|
||||||
1, // amount to transfer back from the bridge
|
1, // amount to transfer back from the bridge
|
||||||
bridgeData
|
bridgeData
|
||||||
);
|
);
|
||||||
|
boughtAmount = buyToken.balanceOf(address(this)).safeSub(balanceBefore);
|
||||||
boughtAmount = IERC20TokenV06(toTokenAddress).balanceOf(address(this)).safeSub(balanceBefore);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,6 +31,8 @@ contract TestFillQuoteTransformerHost is
|
|||||||
IERC20Transformer transformer,
|
IERC20Transformer transformer,
|
||||||
TestMintableERC20Token inputToken,
|
TestMintableERC20Token inputToken,
|
||||||
uint256 inputTokenAmount,
|
uint256 inputTokenAmount,
|
||||||
|
address payable sender,
|
||||||
|
address payable taker,
|
||||||
bytes calldata data
|
bytes calldata data
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
@ -40,6 +42,14 @@ contract TestFillQuoteTransformerHost is
|
|||||||
inputToken.mint(address(this), inputTokenAmount);
|
inputToken.mint(address(this), inputTokenAmount);
|
||||||
}
|
}
|
||||||
// Have to make this call externally because transformers aren't payable.
|
// Have to make this call externally because transformers aren't payable.
|
||||||
this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
|
this.rawExecuteTransform(
|
||||||
|
transformer,
|
||||||
|
IERC20Transformer.TransformContext({
|
||||||
|
callDataHash: bytes32(0),
|
||||||
|
sender: sender,
|
||||||
|
taker: taker,
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/ZeroEx.sol";
|
import "../src/ZeroEx.sol";
|
||||||
import "../src/features/IBootstrap.sol";
|
|
||||||
import "../src/migrations/FullMigration.sol";
|
import "../src/migrations/FullMigration.sol";
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/ZeroEx.sol";
|
import "../src/ZeroEx.sol";
|
||||||
import "../src/features/IBootstrap.sol";
|
import "../src/features/IBootstrapFeature.sol";
|
||||||
import "../src/migrations/InitialMigration.sol";
|
import "../src/migrations/InitialMigration.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ contract TestInitialMigration is
|
|||||||
constructor(address deployer) public InitialMigration(deployer) {}
|
constructor(address deployer) public InitialMigration(deployer) {}
|
||||||
|
|
||||||
function callBootstrap(ZeroEx zeroEx) external {
|
function callBootstrap(ZeroEx zeroEx) external {
|
||||||
IBootstrap(address(zeroEx)).bootstrap(address(this), new bytes(0));
|
IBootstrapFeature(address(zeroEx)).bootstrap(address(this), new bytes(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
function bootstrap(address owner, BootstrapFeatures memory features)
|
function bootstrap(address owner, BootstrapFeatures memory features)
|
||||||
@ -45,7 +45,7 @@ contract TestInitialMigration is
|
|||||||
success = InitialMigration.bootstrap(owner, features);
|
success = InitialMigration.bootstrap(owner, features);
|
||||||
// Snoop the bootstrap feature contract.
|
// Snoop the bootstrap feature contract.
|
||||||
bootstrapFeature = ZeroEx(address(uint160(address(this))))
|
bootstrapFeature = ZeroEx(address(uint160(address(this))))
|
||||||
.getFunctionImplementation(IBootstrap.bootstrap.selector);
|
.getFunctionImplementation(IBootstrapFeature.bootstrap.selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
function die(address payable ethRecipient) public override {
|
function die(address payable ethRecipient) public override {
|
||||||
|
@ -19,11 +19,12 @@
|
|||||||
pragma solidity ^0.6.5;
|
pragma solidity ^0.6.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/features/TransformERC20.sol";
|
import "../src/features/TransformERC20Feature.sol";
|
||||||
|
import "../src/features/IMetaTransactionsFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestMetaTransactionsTransformERC20Feature is
|
contract TestMetaTransactionsTransformERC20Feature is
|
||||||
TransformERC20
|
TransformERC20Feature
|
||||||
{
|
{
|
||||||
event TransformERC20Called(
|
event TransformERC20Called(
|
||||||
address sender,
|
address sender,
|
||||||
@ -48,6 +49,49 @@ contract TestMetaTransactionsTransformERC20Feature is
|
|||||||
revert('FAIL');
|
revert('FAIL');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.value == 777) {
|
||||||
|
// Try to reenter `executeMetaTransaction()`
|
||||||
|
IMetaTransactionsFeature(address(this)).executeMetaTransaction(
|
||||||
|
IMetaTransactionsFeature.MetaTransactionData({
|
||||||
|
signer: address(0),
|
||||||
|
sender: address(0),
|
||||||
|
minGasPrice: 0,
|
||||||
|
maxGasPrice: 0,
|
||||||
|
expirationTimeSeconds: 0,
|
||||||
|
salt: 0,
|
||||||
|
callData: "",
|
||||||
|
value: 0,
|
||||||
|
feeToken: IERC20TokenV06(0),
|
||||||
|
feeAmount: 0
|
||||||
|
}),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.value == 888) {
|
||||||
|
// Try to reenter `batchExecuteMetaTransactions()`
|
||||||
|
IMetaTransactionsFeature.MetaTransactionData[] memory mtxs =
|
||||||
|
new IMetaTransactionsFeature.MetaTransactionData[](1);
|
||||||
|
bytes[] memory signatures = new bytes[](1);
|
||||||
|
mtxs[0] = IMetaTransactionsFeature.MetaTransactionData({
|
||||||
|
signer: address(0),
|
||||||
|
sender: address(0),
|
||||||
|
minGasPrice: 0,
|
||||||
|
maxGasPrice: 0,
|
||||||
|
expirationTimeSeconds: 0,
|
||||||
|
salt: 0,
|
||||||
|
callData: "",
|
||||||
|
value: 0,
|
||||||
|
feeToken: IERC20TokenV06(0),
|
||||||
|
feeAmount: 0
|
||||||
|
});
|
||||||
|
signatures[0] = "";
|
||||||
|
IMetaTransactionsFeature(address(this)).batchExecuteMetaTransactions(
|
||||||
|
mtxs,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
emit TransformERC20Called(
|
emit TransformERC20Called(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
msg.value,
|
msg.value,
|
||||||
|
@ -20,7 +20,7 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/migrations/LibMigrate.sol";
|
import "../src/migrations/LibMigrate.sol";
|
||||||
import "../src/features/IOwnable.sol";
|
import "../src/features/IOwnableFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestMigrator {
|
contract TestMigrator {
|
||||||
@ -32,7 +32,7 @@ contract TestMigrator {
|
|||||||
function succeedingMigrate() external returns (bytes4 success) {
|
function succeedingMigrate() external returns (bytes4 success) {
|
||||||
emit TestMigrateCalled(
|
emit TestMigrateCalled(
|
||||||
msg.data,
|
msg.data,
|
||||||
IOwnable(address(this)).owner()
|
IOwnableFeature(address(this)).owner()
|
||||||
);
|
);
|
||||||
return LibMigrate.MIGRATE_SUCCESS;
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ contract TestMigrator {
|
|||||||
function failingMigrate() external returns (bytes4 success) {
|
function failingMigrate() external returns (bytes4 success) {
|
||||||
emit TestMigrateCalled(
|
emit TestMigrateCalled(
|
||||||
msg.data,
|
msg.data,
|
||||||
IOwnable(address(this)).owner()
|
IOwnableFeature(address(this)).owner()
|
||||||
);
|
);
|
||||||
return 0xdeadbeef;
|
return 0xdeadbeef;
|
||||||
}
|
}
|
||||||
|
@ -40,28 +40,26 @@ contract TestMintTokenERC20Transformer is
|
|||||||
address context,
|
address context,
|
||||||
address caller,
|
address caller,
|
||||||
bytes32 callDataHash,
|
bytes32 callDataHash,
|
||||||
|
address sender,
|
||||||
address taker,
|
address taker,
|
||||||
bytes data,
|
bytes data,
|
||||||
uint256 inputTokenBalance,
|
uint256 inputTokenBalance,
|
||||||
uint256 ethBalance
|
uint256 ethBalance
|
||||||
);
|
);
|
||||||
|
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32 callDataHash,
|
|
||||||
address payable taker,
|
|
||||||
bytes calldata data_
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
TransformData memory data = abi.decode(data_, (TransformData));
|
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||||
emit MintTransform(
|
emit MintTransform(
|
||||||
address(this),
|
address(this),
|
||||||
msg.sender,
|
msg.sender,
|
||||||
callDataHash,
|
context.callDataHash,
|
||||||
taker,
|
context.sender,
|
||||||
data_,
|
context.taker,
|
||||||
|
context.data,
|
||||||
data.inputToken.balanceOf(address(this)),
|
data.inputToken.balanceOf(address(this)),
|
||||||
address(this).balance
|
address(this).balance
|
||||||
);
|
);
|
||||||
@ -69,14 +67,14 @@ contract TestMintTokenERC20Transformer is
|
|||||||
data.inputToken.transfer(address(0), data.burnAmount);
|
data.inputToken.transfer(address(0), data.burnAmount);
|
||||||
// Mint output tokens.
|
// Mint output tokens.
|
||||||
if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
|
if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
|
||||||
taker.transfer(data.mintAmount);
|
context.taker.transfer(data.mintAmount);
|
||||||
} else {
|
} else {
|
||||||
data.outputToken.mint(
|
data.outputToken.mint(
|
||||||
taker,
|
context.taker,
|
||||||
data.mintAmount
|
data.mintAmount
|
||||||
);
|
);
|
||||||
// Burn fees from output.
|
// Burn fees from output.
|
||||||
data.outputToken.burn(taker, data.feeAmount);
|
data.outputToken.burn(context.taker, data.feeAmount);
|
||||||
}
|
}
|
||||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
pragma solidity ^0.6.5;
|
pragma solidity ^0.6.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/features/TokenSpender.sol";
|
import "../src/features/TokenSpenderFeature.sol";
|
||||||
|
|
||||||
contract TestTokenSpender is
|
contract TestTokenSpender is
|
||||||
TokenSpender
|
TokenSpenderFeature
|
||||||
{
|
{
|
||||||
modifier onlySelf() override {
|
modifier onlySelf() override {
|
||||||
_;
|
_;
|
||||||
|
@ -19,18 +19,12 @@
|
|||||||
pragma solidity ^0.6.5;
|
pragma solidity ^0.6.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/features/TransformERC20.sol";
|
import "../src/features/TransformERC20Feature.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestTransformERC20 is
|
contract TestTransformERC20 is
|
||||||
TransformERC20
|
TransformERC20Feature
|
||||||
{
|
{
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
constructor()
|
|
||||||
TransformERC20()
|
|
||||||
public
|
|
||||||
{}
|
|
||||||
|
|
||||||
modifier onlySelf() override {
|
modifier onlySelf() override {
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,15 @@ pragma solidity ^0.6.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "../src/transformers/Transformer.sol";
|
import "../src/transformers/Transformer.sol";
|
||||||
|
import "../src/transformers/IERC20Transformer.sol";
|
||||||
import "../src/transformers/LibERC20Transformer.sol";
|
import "../src/transformers/LibERC20Transformer.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestTransformerBase is
|
contract TestTransformerBase is
|
||||||
|
IERC20Transformer,
|
||||||
Transformer
|
Transformer
|
||||||
{
|
{
|
||||||
function transform(
|
function transform(TransformContext calldata context)
|
||||||
bytes32,
|
|
||||||
address payable,
|
|
||||||
bytes calldata
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
|
@ -24,10 +24,15 @@ import "../src/transformers/LibERC20Transformer.sol";
|
|||||||
|
|
||||||
contract TestTransformerDeployerTransformer {
|
contract TestTransformerDeployerTransformer {
|
||||||
|
|
||||||
|
uint256 public constant CONSTRUCTOR_FAIL_VALUE = 3333;
|
||||||
address payable public immutable deployer;
|
address payable public immutable deployer;
|
||||||
|
|
||||||
constructor() public payable {
|
constructor() public payable {
|
||||||
deployer = msg.sender;
|
deployer = msg.sender;
|
||||||
|
require(
|
||||||
|
msg.value != CONSTRUCTOR_FAIL_VALUE,
|
||||||
|
"TestTransformerDeployerTransformer/CONSTRUCTOR_FAIL"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier onlyDeployer() {
|
modifier onlyDeployer() {
|
||||||
@ -35,11 +40,11 @@ contract TestTransformerDeployerTransformer {
|
|||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
function die()
|
function die(address payable ethRecipient)
|
||||||
external
|
external
|
||||||
onlyDeployer
|
onlyDeployer
|
||||||
{
|
{
|
||||||
selfdestruct(deployer);
|
selfdestruct(ethRecipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDeployedByDeployer(uint32 nonce)
|
function isDeployedByDeployer(uint32 nonce)
|
||||||
|
@ -32,18 +32,14 @@ contract TestTransformerHost {
|
|||||||
|
|
||||||
function rawExecuteTransform(
|
function rawExecuteTransform(
|
||||||
IERC20Transformer transformer,
|
IERC20Transformer transformer,
|
||||||
bytes32 callDataHash,
|
IERC20Transformer.TransformContext calldata context
|
||||||
address taker,
|
|
||||||
bytes calldata data
|
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
{
|
{
|
||||||
(bool _success, bytes memory resultData) =
|
(bool _success, bytes memory resultData) =
|
||||||
address(transformer).delegatecall(abi.encodeWithSelector(
|
address(transformer).delegatecall(abi.encodeWithSelector(
|
||||||
transformer.transform.selector,
|
transformer.transform.selector,
|
||||||
callDataHash,
|
context
|
||||||
taker,
|
|
||||||
data
|
|
||||||
));
|
));
|
||||||
if (!_success) {
|
if (!_success) {
|
||||||
resultData.rrevert();
|
resultData.rrevert();
|
||||||
|
@ -48,6 +48,14 @@ contract TestWethTransformerHost is
|
|||||||
_weth.deposit{value: wethAmount}();
|
_weth.deposit{value: wethAmount}();
|
||||||
}
|
}
|
||||||
// Have to make this call externally because transformers aren't payable.
|
// Have to make this call externally because transformers aren't payable.
|
||||||
this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
|
this.rawExecuteTransform(
|
||||||
|
transformer,
|
||||||
|
IERC20Transformer.TransformContext({
|
||||||
|
callDataHash: bytes32(0),
|
||||||
|
sender: msg.sender,
|
||||||
|
taker: msg.sender,
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@
|
|||||||
"publish:private": "yarn build && gitpkg publish"
|
"publish:private": "yarn build && gitpkg publish"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnable,ISimpleFunctionRegistry,ITokenSpender,ITransformERC20,FillQuoteTransformer,PayTakerTransformer,WethTransformer,Ownable,SimpleFunctionRegistry,TransformERC20,TokenSpender,AffiliateFeeTransformer,SignatureValidator,MetaTransactions,BridgeAdapter",
|
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|BridgeAdapter|FillQuoteTransformer|FixinCommon|FixinEIP712|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactions|IOwnable|ISignatureValidator|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|MetaTransactions|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|Ownable|PayTakerTransformer|SignatureValidator|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -13,18 +13,19 @@ import * as IAllowanceTarget from '../generated-artifacts/IAllowanceTarget.json'
|
|||||||
import * as IERC20Transformer from '../generated-artifacts/IERC20Transformer.json';
|
import * as IERC20Transformer from '../generated-artifacts/IERC20Transformer.json';
|
||||||
import * as IFlashWallet from '../generated-artifacts/IFlashWallet.json';
|
import * as IFlashWallet from '../generated-artifacts/IFlashWallet.json';
|
||||||
import * as InitialMigration from '../generated-artifacts/InitialMigration.json';
|
import * as InitialMigration from '../generated-artifacts/InitialMigration.json';
|
||||||
import * as IOwnable from '../generated-artifacts/IOwnable.json';
|
import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json';
|
||||||
import * as ISimpleFunctionRegistry from '../generated-artifacts/ISimpleFunctionRegistry.json';
|
import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json';
|
||||||
import * as ITokenSpender from '../generated-artifacts/ITokenSpender.json';
|
import * as ITokenSpenderFeature from '../generated-artifacts/ITokenSpenderFeature.json';
|
||||||
import * as ITransformERC20 from '../generated-artifacts/ITransformERC20.json';
|
import * as ITransformERC20Feature from '../generated-artifacts/ITransformERC20Feature.json';
|
||||||
import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
|
import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
|
||||||
import * as MetaTransactions from '../generated-artifacts/MetaTransactions.json';
|
import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTransformer.json';
|
||||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
|
||||||
|
import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
|
||||||
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
||||||
import * as SignatureValidator from '../generated-artifacts/SignatureValidator.json';
|
import * as SignatureValidatorFeature from '../generated-artifacts/SignatureValidatorFeature.json';
|
||||||
import * as SimpleFunctionRegistry from '../generated-artifacts/SimpleFunctionRegistry.json';
|
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||||
import * as TokenSpender from '../generated-artifacts/TokenSpender.json';
|
import * as TokenSpenderFeature from '../generated-artifacts/TokenSpenderFeature.json';
|
||||||
import * as TransformERC20 from '../generated-artifacts/TransformERC20.json';
|
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
|
||||||
import * as WethTransformer from '../generated-artifacts/WethTransformer.json';
|
import * as WethTransformer from '../generated-artifacts/WethTransformer.json';
|
||||||
import * as ZeroEx from '../generated-artifacts/ZeroEx.json';
|
import * as ZeroEx from '../generated-artifacts/ZeroEx.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
@ -35,19 +36,20 @@ export const artifacts = {
|
|||||||
IFlashWallet: IFlashWallet as ContractArtifact,
|
IFlashWallet: IFlashWallet as ContractArtifact,
|
||||||
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
||||||
IERC20Transformer: IERC20Transformer as ContractArtifact,
|
IERC20Transformer: IERC20Transformer as ContractArtifact,
|
||||||
IOwnable: IOwnable as ContractArtifact,
|
IOwnableFeature: IOwnableFeature as ContractArtifact,
|
||||||
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
|
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
|
||||||
ITokenSpender: ITokenSpender as ContractArtifact,
|
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
|
||||||
ITransformERC20: ITransformERC20 as ContractArtifact,
|
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
|
||||||
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
||||||
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
|
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
|
||||||
WethTransformer: WethTransformer as ContractArtifact,
|
WethTransformer: WethTransformer as ContractArtifact,
|
||||||
Ownable: Ownable as ContractArtifact,
|
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||||
SimpleFunctionRegistry: SimpleFunctionRegistry as ContractArtifact,
|
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
|
||||||
TransformERC20: TransformERC20 as ContractArtifact,
|
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
|
||||||
TokenSpender: TokenSpender as ContractArtifact,
|
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
|
||||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||||
SignatureValidator: SignatureValidator as ContractArtifact,
|
SignatureValidatorFeature: SignatureValidatorFeature as ContractArtifact,
|
||||||
MetaTransactions: MetaTransactions as ContractArtifact,
|
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||||
|
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
||||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -36,13 +36,14 @@ export {
|
|||||||
AffiliateFeeTransformerContract,
|
AffiliateFeeTransformerContract,
|
||||||
BridgeAdapterContract,
|
BridgeAdapterContract,
|
||||||
FillQuoteTransformerContract,
|
FillQuoteTransformerContract,
|
||||||
IOwnableContract,
|
IOwnableFeatureContract,
|
||||||
IOwnableEvents,
|
IOwnableFeatureEvents,
|
||||||
ISimpleFunctionRegistryContract,
|
ISimpleFunctionRegistryFeatureContract,
|
||||||
ISimpleFunctionRegistryEvents,
|
ISimpleFunctionRegistryFeatureEvents,
|
||||||
ITokenSpenderContract,
|
ITokenSpenderFeatureContract,
|
||||||
ITransformERC20Contract,
|
ITransformERC20FeatureContract,
|
||||||
IZeroExContract,
|
IZeroExContract,
|
||||||
|
LogMetadataTransformerContract,
|
||||||
PayTakerTransformerContract,
|
PayTakerTransformerContract,
|
||||||
WethTransformerContract,
|
WethTransformerContract,
|
||||||
ZeroExContract,
|
ZeroExContract,
|
||||||
|
@ -6,12 +6,13 @@ import { artifacts } from './artifacts';
|
|||||||
import {
|
import {
|
||||||
FullMigrationContract,
|
FullMigrationContract,
|
||||||
InitialMigrationContract,
|
InitialMigrationContract,
|
||||||
MetaTransactionsContract,
|
IZeroExContract,
|
||||||
OwnableContract,
|
MetaTransactionsFeatureContract,
|
||||||
SignatureValidatorContract,
|
OwnableFeatureContract,
|
||||||
SimpleFunctionRegistryContract,
|
SignatureValidatorFeatureContract,
|
||||||
TokenSpenderContract,
|
SimpleFunctionRegistryFeatureContract,
|
||||||
TransformERC20Contract,
|
TokenSpenderFeatureContract,
|
||||||
|
TransformERC20FeatureContract,
|
||||||
ZeroExContract,
|
ZeroExContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
|
|
||||||
@ -36,16 +37,20 @@ export async function deployBootstrapFeaturesAsync(
|
|||||||
return {
|
return {
|
||||||
registry:
|
registry:
|
||||||
features.registry ||
|
features.registry ||
|
||||||
(await SimpleFunctionRegistryContract.deployFrom0xArtifactAsync(
|
(await SimpleFunctionRegistryFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.SimpleFunctionRegistry,
|
artifacts.SimpleFunctionRegistryFeature,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
ownable:
|
ownable:
|
||||||
features.ownable ||
|
features.ownable ||
|
||||||
(await OwnableContract.deployFrom0xArtifactAsync(artifacts.Ownable, provider, txDefaults, artifacts))
|
(await OwnableFeatureContract.deployFrom0xArtifactAsync(
|
||||||
.address,
|
artifacts.OwnableFeature,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
)).address,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,32 +112,32 @@ export async function deployFullFeaturesAsync(
|
|||||||
...(await deployBootstrapFeaturesAsync(provider, txDefaults)),
|
...(await deployBootstrapFeaturesAsync(provider, txDefaults)),
|
||||||
tokenSpender:
|
tokenSpender:
|
||||||
features.tokenSpender ||
|
features.tokenSpender ||
|
||||||
(await TokenSpenderContract.deployFrom0xArtifactAsync(
|
(await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TokenSpender,
|
artifacts.TokenSpenderFeature,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
transformERC20:
|
transformERC20:
|
||||||
features.transformERC20 ||
|
features.transformERC20 ||
|
||||||
(await TransformERC20Contract.deployFrom0xArtifactAsync(
|
(await TransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TransformERC20,
|
artifacts.TransformERC20Feature,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
signatureValidator:
|
signatureValidator:
|
||||||
features.signatureValidator ||
|
features.signatureValidator ||
|
||||||
(await SignatureValidatorContract.deployFrom0xArtifactAsync(
|
(await SignatureValidatorFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.SignatureValidator,
|
artifacts.SignatureValidatorFeature,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
metaTransactions:
|
metaTransactions:
|
||||||
features.metaTransactions ||
|
features.metaTransactions ||
|
||||||
(await MetaTransactionsContract.deployFrom0xArtifactAsync(
|
(await MetaTransactionsFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.MetaTransactions,
|
artifacts.MetaTransactionsFeature,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
@ -150,7 +155,7 @@ export async function fullMigrateAsync(
|
|||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
features: Partial<FullFeatures> = {},
|
features: Partial<FullFeatures> = {},
|
||||||
opts: Partial<FullMigrationOpts> = {},
|
opts: Partial<FullMigrationOpts> = {},
|
||||||
): Promise<ZeroExContract> {
|
): Promise<IZeroExContract> {
|
||||||
const migrator = await FullMigrationContract.deployFrom0xArtifactAsync(
|
const migrator = await FullMigrationContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.FullMigration,
|
artifacts.FullMigration,
|
||||||
provider,
|
provider,
|
||||||
@ -171,5 +176,5 @@ export async function fullMigrateAsync(
|
|||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
await migrator.initializeZeroEx(owner, zeroEx.address, _features, _opts).awaitTransactionSuccessAsync();
|
await migrator.initializeZeroEx(owner, zeroEx.address, _features, _opts).awaitTransactionSuccessAsync();
|
||||||
return zeroEx;
|
return new IZeroExContract(zeroEx.address, provider, txDefaults);
|
||||||
}
|
}
|
||||||
|
@ -10,18 +10,19 @@ export * from '../generated-wrappers/full_migration';
|
|||||||
export * from '../generated-wrappers/i_allowance_target';
|
export * from '../generated-wrappers/i_allowance_target';
|
||||||
export * from '../generated-wrappers/i_erc20_transformer';
|
export * from '../generated-wrappers/i_erc20_transformer';
|
||||||
export * from '../generated-wrappers/i_flash_wallet';
|
export * from '../generated-wrappers/i_flash_wallet';
|
||||||
export * from '../generated-wrappers/i_ownable';
|
export * from '../generated-wrappers/i_ownable_feature';
|
||||||
export * from '../generated-wrappers/i_simple_function_registry';
|
export * from '../generated-wrappers/i_simple_function_registry_feature';
|
||||||
export * from '../generated-wrappers/i_token_spender';
|
export * from '../generated-wrappers/i_token_spender_feature';
|
||||||
export * from '../generated-wrappers/i_transform_erc20';
|
export * from '../generated-wrappers/i_transform_erc20_feature';
|
||||||
export * from '../generated-wrappers/i_zero_ex';
|
export * from '../generated-wrappers/i_zero_ex';
|
||||||
export * from '../generated-wrappers/initial_migration';
|
export * from '../generated-wrappers/initial_migration';
|
||||||
export * from '../generated-wrappers/meta_transactions';
|
export * from '../generated-wrappers/log_metadata_transformer';
|
||||||
export * from '../generated-wrappers/ownable';
|
export * from '../generated-wrappers/meta_transactions_feature';
|
||||||
|
export * from '../generated-wrappers/ownable_feature';
|
||||||
export * from '../generated-wrappers/pay_taker_transformer';
|
export * from '../generated-wrappers/pay_taker_transformer';
|
||||||
export * from '../generated-wrappers/signature_validator';
|
export * from '../generated-wrappers/signature_validator_feature';
|
||||||
export * from '../generated-wrappers/simple_function_registry';
|
export * from '../generated-wrappers/simple_function_registry_feature';
|
||||||
export * from '../generated-wrappers/token_spender';
|
export * from '../generated-wrappers/token_spender_feature';
|
||||||
export * from '../generated-wrappers/transform_erc20';
|
export * from '../generated-wrappers/transform_erc20_feature';
|
||||||
export * from '../generated-wrappers/weth_transformer';
|
export * from '../generated-wrappers/weth_transformer';
|
||||||
export * from '../generated-wrappers/zero_ex';
|
export * from '../generated-wrappers/zero_ex';
|
||||||
|
@ -7,15 +7,16 @@ import { ContractArtifact } from 'ethereum-types';
|
|||||||
|
|
||||||
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
||||||
import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json';
|
import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json';
|
||||||
import * as Bootstrap from '../test/generated-artifacts/Bootstrap.json';
|
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
||||||
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
||||||
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
||||||
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
|
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
|
||||||
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
|
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
|
||||||
|
import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentrancyGuard.json';
|
||||||
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
||||||
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
||||||
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
||||||
import * as IBootstrap from '../test/generated-artifacts/IBootstrap.json';
|
import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeature.json';
|
||||||
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
|
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
|
||||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||||
import * as IERC20Transformer from '../test/generated-artifacts/IERC20Transformer.json';
|
import * as IERC20Transformer from '../test/generated-artifacts/IERC20Transformer.json';
|
||||||
@ -23,14 +24,14 @@ import * as IExchange from '../test/generated-artifacts/IExchange.json';
|
|||||||
import * as IFeature from '../test/generated-artifacts/IFeature.json';
|
import * as IFeature from '../test/generated-artifacts/IFeature.json';
|
||||||
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
|
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
|
||||||
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
||||||
import * as IMetaTransactions from '../test/generated-artifacts/IMetaTransactions.json';
|
import * as IMetaTransactionsFeature from '../test/generated-artifacts/IMetaTransactionsFeature.json';
|
||||||
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
|
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
|
||||||
import * as IOwnable from '../test/generated-artifacts/IOwnable.json';
|
import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json';
|
||||||
import * as ISignatureValidator from '../test/generated-artifacts/ISignatureValidator.json';
|
import * as ISignatureValidatorFeature from '../test/generated-artifacts/ISignatureValidatorFeature.json';
|
||||||
import * as ISimpleFunctionRegistry from '../test/generated-artifacts/ISimpleFunctionRegistry.json';
|
import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISimpleFunctionRegistryFeature.json';
|
||||||
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
||||||
import * as ITokenSpender from '../test/generated-artifacts/ITokenSpender.json';
|
import * as ITokenSpenderFeature from '../test/generated-artifacts/ITokenSpenderFeature.json';
|
||||||
import * as ITransformERC20 from '../test/generated-artifacts/ITransformERC20.json';
|
import * as ITransformERC20Feature from '../test/generated-artifacts/ITransformERC20Feature.json';
|
||||||
import * as IZeroEx from '../test/generated-artifacts/IZeroEx.json';
|
import * as IZeroEx from '../test/generated-artifacts/IZeroEx.json';
|
||||||
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
|
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
|
||||||
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
|
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
|
||||||
@ -42,6 +43,7 @@ import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRic
|
|||||||
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
||||||
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
||||||
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
|
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
|
||||||
|
import * as LibReentrancyGuardStorage from '../test/generated-artifacts/LibReentrancyGuardStorage.json';
|
||||||
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
|
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
|
||||||
import * as LibSignedCallData from '../test/generated-artifacts/LibSignedCallData.json';
|
import * as LibSignedCallData from '../test/generated-artifacts/LibSignedCallData.json';
|
||||||
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
||||||
@ -52,20 +54,22 @@ import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpe
|
|||||||
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
|
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
|
||||||
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
|
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
|
||||||
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
|
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
|
||||||
import * as MetaTransactions from '../test/generated-artifacts/MetaTransactions.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 MixinAdapterAddresses from '../test/generated-artifacts/MixinAdapterAddresses.json';
|
||||||
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||||
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
||||||
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
|
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
|
||||||
|
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
|
||||||
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
||||||
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
|
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
|
||||||
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
||||||
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
||||||
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
||||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
|
||||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||||
import * as SignatureValidator from '../test/generated-artifacts/SignatureValidator.json';
|
import * as SignatureValidatorFeature from '../test/generated-artifacts/SignatureValidatorFeature.json';
|
||||||
import * as SimpleFunctionRegistry from '../test/generated-artifacts/SimpleFunctionRegistry.json';
|
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||||
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
||||||
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
||||||
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
|
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
|
||||||
@ -88,26 +92,15 @@ import * as TestTransformerHost from '../test/generated-artifacts/TestTransforme
|
|||||||
import * as TestWeth from '../test/generated-artifacts/TestWeth.json';
|
import * as TestWeth from '../test/generated-artifacts/TestWeth.json';
|
||||||
import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json';
|
import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json';
|
||||||
import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json';
|
import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json';
|
||||||
import * as TokenSpender from '../test/generated-artifacts/TokenSpender.json';
|
import * as TokenSpenderFeature from '../test/generated-artifacts/TokenSpenderFeature.json';
|
||||||
import * as Transformer from '../test/generated-artifacts/Transformer.json';
|
import * as Transformer from '../test/generated-artifacts/Transformer.json';
|
||||||
import * as TransformERC20 from '../test/generated-artifacts/TransformERC20.json';
|
import * as TransformERC20Feature from '../test/generated-artifacts/TransformERC20Feature.json';
|
||||||
import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json';
|
import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json';
|
||||||
import * as WethTransformer from '../test/generated-artifacts/WethTransformer.json';
|
import * as WethTransformer from '../test/generated-artifacts/WethTransformer.json';
|
||||||
import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
|
import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
IZeroEx: IZeroEx as ContractArtifact,
|
IZeroEx: IZeroEx as ContractArtifact,
|
||||||
ZeroEx: ZeroEx as ContractArtifact,
|
ZeroEx: ZeroEx as ContractArtifact,
|
||||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
|
||||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
|
||||||
MixinAdapterAddresses: MixinAdapterAddresses as ContractArtifact,
|
|
||||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
|
||||||
MixinCurve: MixinCurve as ContractArtifact,
|
|
||||||
MixinKyber: MixinKyber as ContractArtifact,
|
|
||||||
MixinMStable: MixinMStable as ContractArtifact,
|
|
||||||
MixinOasis: MixinOasis as ContractArtifact,
|
|
||||||
MixinUniswap: MixinUniswap as ContractArtifact,
|
|
||||||
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
|
||||||
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
|
||||||
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
|
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
|
||||||
LibMetaTransactionsRichErrors: LibMetaTransactionsRichErrors as ContractArtifact,
|
LibMetaTransactionsRichErrors: LibMetaTransactionsRichErrors as ContractArtifact,
|
||||||
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
|
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
|
||||||
@ -122,24 +115,25 @@ export const artifacts = {
|
|||||||
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
||||||
IFlashWallet: IFlashWallet as ContractArtifact,
|
IFlashWallet: IFlashWallet as ContractArtifact,
|
||||||
TransformerDeployer: TransformerDeployer as ContractArtifact,
|
TransformerDeployer: TransformerDeployer as ContractArtifact,
|
||||||
Bootstrap: Bootstrap as ContractArtifact,
|
BootstrapFeature: BootstrapFeature as ContractArtifact,
|
||||||
IBootstrap: IBootstrap as ContractArtifact,
|
IBootstrapFeature: IBootstrapFeature as ContractArtifact,
|
||||||
IFeature: IFeature as ContractArtifact,
|
IFeature: IFeature as ContractArtifact,
|
||||||
IMetaTransactions: IMetaTransactions as ContractArtifact,
|
IMetaTransactionsFeature: IMetaTransactionsFeature as ContractArtifact,
|
||||||
IOwnable: IOwnable as ContractArtifact,
|
IOwnableFeature: IOwnableFeature as ContractArtifact,
|
||||||
ISignatureValidator: ISignatureValidator as ContractArtifact,
|
ISignatureValidatorFeature: ISignatureValidatorFeature as ContractArtifact,
|
||||||
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
|
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
|
||||||
ITokenSpender: ITokenSpender as ContractArtifact,
|
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
|
||||||
ITransformERC20: ITransformERC20 as ContractArtifact,
|
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
|
||||||
MetaTransactions: MetaTransactions as ContractArtifact,
|
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||||
Ownable: Ownable as ContractArtifact,
|
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||||
SignatureValidator: SignatureValidator as ContractArtifact,
|
SignatureValidatorFeature: SignatureValidatorFeature as ContractArtifact,
|
||||||
SimpleFunctionRegistry: SimpleFunctionRegistry as ContractArtifact,
|
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
|
||||||
TokenSpender: TokenSpender as ContractArtifact,
|
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
|
||||||
TransformERC20: TransformERC20 as ContractArtifact,
|
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
|
||||||
LibSignedCallData: LibSignedCallData as ContractArtifact,
|
LibSignedCallData: LibSignedCallData as ContractArtifact,
|
||||||
FixinCommon: FixinCommon as ContractArtifact,
|
FixinCommon: FixinCommon as ContractArtifact,
|
||||||
FixinEIP712: FixinEIP712 as ContractArtifact,
|
FixinEIP712: FixinEIP712 as ContractArtifact,
|
||||||
|
FixinReentrancyGuard: FixinReentrancyGuard as ContractArtifact,
|
||||||
FullMigration: FullMigration as ContractArtifact,
|
FullMigration: FullMigration as ContractArtifact,
|
||||||
InitialMigration: InitialMigration as ContractArtifact,
|
InitialMigration: InitialMigration as ContractArtifact,
|
||||||
LibBootstrap: LibBootstrap as ContractArtifact,
|
LibBootstrap: LibBootstrap as ContractArtifact,
|
||||||
@ -147,6 +141,7 @@ export const artifacts = {
|
|||||||
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
|
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
|
||||||
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
|
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
|
||||||
LibProxyStorage: LibProxyStorage as ContractArtifact,
|
LibProxyStorage: LibProxyStorage as ContractArtifact,
|
||||||
|
LibReentrancyGuardStorage: LibReentrancyGuardStorage as ContractArtifact,
|
||||||
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
||||||
LibStorage: LibStorage as ContractArtifact,
|
LibStorage: LibStorage as ContractArtifact,
|
||||||
LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact,
|
LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact,
|
||||||
@ -155,9 +150,22 @@ export const artifacts = {
|
|||||||
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
||||||
IERC20Transformer: IERC20Transformer as ContractArtifact,
|
IERC20Transformer: IERC20Transformer as ContractArtifact,
|
||||||
LibERC20Transformer: LibERC20Transformer as ContractArtifact,
|
LibERC20Transformer: LibERC20Transformer as ContractArtifact,
|
||||||
|
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
||||||
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
|
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
|
||||||
Transformer: Transformer as ContractArtifact,
|
Transformer: Transformer as ContractArtifact,
|
||||||
WethTransformer: WethTransformer as ContractArtifact,
|
WethTransformer: WethTransformer as ContractArtifact,
|
||||||
|
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||||
|
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||||
|
MixinAdapterAddresses: MixinAdapterAddresses as ContractArtifact,
|
||||||
|
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||||
|
MixinCurve: MixinCurve as ContractArtifact,
|
||||||
|
MixinKyber: MixinKyber as ContractArtifact,
|
||||||
|
MixinMStable: MixinMStable as ContractArtifact,
|
||||||
|
MixinMooniswap: MixinMooniswap as ContractArtifact,
|
||||||
|
MixinOasis: MixinOasis as ContractArtifact,
|
||||||
|
MixinUniswap: MixinUniswap as ContractArtifact,
|
||||||
|
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
||||||
|
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
||||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
IExchange: IExchange as ContractArtifact,
|
IExchange: IExchange as ContractArtifact,
|
||||||
IGasToken: IGasToken as ContractArtifact,
|
IGasToken: IGasToken as ContractArtifact,
|
||||||
|
@ -12,12 +12,12 @@ import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { generateCallDataSignature, signCallData } from '../../src/signed_call_data';
|
import { generateCallDataSignature, signCallData } from '../../src/signed_call_data';
|
||||||
import { MetaTransactionsContract, ZeroExContract } from '../../src/wrappers';
|
import { IZeroExContract, MetaTransactionsFeatureContract } from '../../src/wrappers';
|
||||||
import { artifacts } from '../artifacts';
|
import { artifacts } from '../artifacts';
|
||||||
import { abis } from '../utils/abis';
|
import { abis } from '../utils/abis';
|
||||||
import { fullMigrateAsync } from '../utils/migration';
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
import {
|
import {
|
||||||
ITokenSpenderContract,
|
ITokenSpenderFeatureContract,
|
||||||
TestMetaTransactionsTransformERC20FeatureContract,
|
TestMetaTransactionsTransformERC20FeatureContract,
|
||||||
TestMetaTransactionsTransformERC20FeatureEvents,
|
TestMetaTransactionsTransformERC20FeatureEvents,
|
||||||
TestMintableERC20TokenContract,
|
TestMintableERC20TokenContract,
|
||||||
@ -29,13 +29,17 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
let owner: string;
|
let owner: string;
|
||||||
let sender: string;
|
let sender: string;
|
||||||
let signers: string[];
|
let signers: string[];
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: MetaTransactionsContract;
|
let feature: MetaTransactionsFeatureContract;
|
||||||
let feeToken: TestMintableERC20TokenContract;
|
let feeToken: TestMintableERC20TokenContract;
|
||||||
let transformERC20Feature: TestMetaTransactionsTransformERC20FeatureContract;
|
let transformERC20Feature: TestMetaTransactionsTransformERC20FeatureContract;
|
||||||
let allowanceTarget: string;
|
let allowanceTarget: string;
|
||||||
|
|
||||||
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
||||||
|
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
|
||||||
|
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
|
||||||
|
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
|
||||||
|
const REENTRANCY_FLAG_MTX = 0x1;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner, sender, ...signers] = await env.getAccountAddressesAsync();
|
[owner, sender, ...signers] = await env.getAccountAddressesAsync();
|
||||||
@ -48,14 +52,19 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||||
transformERC20: transformERC20Feature.address,
|
transformERC20: transformERC20Feature.address,
|
||||||
});
|
});
|
||||||
feature = new MetaTransactionsContract(zeroEx.address, env.provider, { ...env.txDefaults, from: sender }, abis);
|
feature = new MetaTransactionsFeatureContract(
|
||||||
|
zeroEx.address,
|
||||||
|
env.provider,
|
||||||
|
{ ...env.txDefaults, from: sender },
|
||||||
|
abis,
|
||||||
|
);
|
||||||
feeToken = await TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
|
feeToken = await TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestMintableERC20Token,
|
artifacts.TestMintableERC20Token,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
allowanceTarget = await new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults)
|
allowanceTarget = await new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults)
|
||||||
.getAllowanceTarget()
|
.getAllowanceTarget()
|
||||||
.callAsync();
|
.callAsync();
|
||||||
// Fund signers with fee tokens.
|
// Fund signers with fee tokens.
|
||||||
@ -263,7 +272,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
it('fails if the translated call fails', async () => {
|
it('fails if the translated call fails', async () => {
|
||||||
const args = getRandomTransformERC20Args();
|
const args = getRandomTransformERC20Args();
|
||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
value: new BigNumber(666),
|
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||||
callData: transformERC20Feature
|
callData: transformERC20Feature
|
||||||
.transformERC20(
|
.transformERC20(
|
||||||
args.inputToken,
|
args.inputToken,
|
||||||
@ -297,7 +306,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
mtxHash,
|
mtxHash,
|
||||||
actualCallData,
|
actualCallData,
|
||||||
new StringRevertError('FAIL').toString(),
|
new StringRevertError('FAIL').encode(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -465,7 +474,139 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
mtxHash,
|
mtxHash,
|
||||||
signers[0],
|
signers[0],
|
||||||
signature,
|
signature,
|
||||||
).toString(),
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('executeMetaTransaction'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('batchExecuteMetaTransactions'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('executeMetaTransaction'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('batchExecuteMetaTransactions'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -526,6 +667,102 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(mtxHash, block),
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(mtxHash, block),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fails if a meta-transaction fails', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).callAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new StringRevertError('FAIL').encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('executeMetaTransaction'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||||
|
feature.getSelector('batchExecuteMetaTransactions'),
|
||||||
|
REENTRANCY_FLAG_MTX,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getMetaTransactionExecutedBlock()', () => {
|
describe('getMetaTransactionExecutedBlock()', () => {
|
||||||
|
@ -3,12 +3,12 @@ import { hexUtils, OwnableRevertErrors, StringRevertError, ZeroExRevertErrors }
|
|||||||
|
|
||||||
import { artifacts } from '../artifacts';
|
import { artifacts } from '../artifacts';
|
||||||
import { initialMigrateAsync } from '../utils/migration';
|
import { initialMigrateAsync } from '../utils/migration';
|
||||||
import { IOwnableContract, IOwnableEvents, TestMigratorContract, TestMigratorEvents } from '../wrappers';
|
import { IOwnableFeatureContract, IOwnableFeatureEvents, TestMigratorContract, TestMigratorEvents } from '../wrappers';
|
||||||
|
|
||||||
blockchainTests.resets('Ownable feature', env => {
|
blockchainTests.resets('Ownable feature', env => {
|
||||||
const notOwner = randomAddress();
|
const notOwner = randomAddress();
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let ownable: IOwnableContract;
|
let ownable: IOwnableFeatureContract;
|
||||||
let testMigrator: TestMigratorContract;
|
let testMigrator: TestMigratorContract;
|
||||||
let succeedingMigrateFnCallData: string;
|
let succeedingMigrateFnCallData: string;
|
||||||
let failingMigrateFnCallData: string;
|
let failingMigrateFnCallData: string;
|
||||||
@ -19,7 +19,7 @@ blockchainTests.resets('Ownable feature', env => {
|
|||||||
[owner] = await env.getAccountAddressesAsync();
|
[owner] = await env.getAccountAddressesAsync();
|
||||||
logDecoder = new LogDecoder(env.web3Wrapper, artifacts);
|
logDecoder = new LogDecoder(env.web3Wrapper, artifacts);
|
||||||
const zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
const zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
||||||
ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
|
ownable = new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
testMigrator = await TestMigratorContract.deployFrom0xArtifactAsync(
|
testMigrator = await TestMigratorContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestMigrator,
|
artifacts.TestMigrator,
|
||||||
env.provider,
|
env.provider,
|
||||||
@ -49,7 +49,7 @@ blockchainTests.resets('Ownable feature', env => {
|
|||||||
newOwner,
|
newOwner,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
IOwnableEvents.OwnershipTransferred,
|
IOwnableFeatureEvents.OwnershipTransferred,
|
||||||
);
|
);
|
||||||
expect(await ownable.owner().callAsync()).to.eq(newOwner);
|
expect(await ownable.owner().callAsync()).to.eq(newOwner);
|
||||||
});
|
});
|
||||||
@ -102,7 +102,7 @@ blockchainTests.resets('Ownable feature', env => {
|
|||||||
return expect(tx).to.revertWith(
|
return expect(tx).to.revertWith(
|
||||||
new ZeroExRevertErrors.Ownable.MigrateCallFailedError(
|
new ZeroExRevertErrors.Ownable.MigrateCallFailedError(
|
||||||
testMigrator.address,
|
testMigrator.address,
|
||||||
new StringRevertError('OOPSIE').toString(),
|
new StringRevertError('OOPSIE').encode(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
|||||||
import * as ethjs from 'ethereumjs-util';
|
import * as ethjs from 'ethereumjs-util';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { SignatureValidatorContract, ZeroExContract } from '../../src/wrappers';
|
import { IZeroExContract, SignatureValidatorFeatureContract } from '../../src/wrappers';
|
||||||
import { abis } from '../utils/abis';
|
import { abis } from '../utils/abis';
|
||||||
import { fullMigrateAsync } from '../utils/migration';
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
|
|
||||||
@ -14,13 +14,13 @@ const { NULL_BYTES } = constants;
|
|||||||
blockchainTests.resets('SignatureValidator feature', env => {
|
blockchainTests.resets('SignatureValidator feature', env => {
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let signers: string[];
|
let signers: string[];
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: SignatureValidatorContract;
|
let feature: SignatureValidatorFeatureContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner, ...signers] = await env.getAccountAddressesAsync();
|
[owner, ...signers] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults);
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults);
|
||||||
feature = new SignatureValidatorContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
feature = new SignatureValidatorFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateHashSignature()', () => {
|
describe('validateHashSignature()', () => {
|
||||||
|
@ -5,8 +5,8 @@ import { ZeroExContract } from '../../src/wrappers';
|
|||||||
import { artifacts } from '../artifacts';
|
import { artifacts } from '../artifacts';
|
||||||
import { initialMigrateAsync } from '../utils/migration';
|
import { initialMigrateAsync } from '../utils/migration';
|
||||||
import {
|
import {
|
||||||
ISimpleFunctionRegistryContract,
|
ISimpleFunctionRegistryFeatureContract,
|
||||||
ISimpleFunctionRegistryEvents,
|
ISimpleFunctionRegistryFeatureEvents,
|
||||||
ITestSimpleFunctionRegistryFeatureContract,
|
ITestSimpleFunctionRegistryFeatureContract,
|
||||||
TestSimpleFunctionRegistryFeatureImpl1Contract,
|
TestSimpleFunctionRegistryFeatureImpl1Contract,
|
||||||
TestSimpleFunctionRegistryFeatureImpl2Contract,
|
TestSimpleFunctionRegistryFeatureImpl2Contract,
|
||||||
@ -17,7 +17,7 @@ blockchainTests.resets('SimpleFunctionRegistry feature', env => {
|
|||||||
const notOwner = randomAddress();
|
const notOwner = randomAddress();
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: ZeroExContract;
|
||||||
let registry: ISimpleFunctionRegistryContract;
|
let registry: ISimpleFunctionRegistryFeatureContract;
|
||||||
let testFnSelector: string;
|
let testFnSelector: string;
|
||||||
let testFeature: ITestSimpleFunctionRegistryFeatureContract;
|
let testFeature: ITestSimpleFunctionRegistryFeatureContract;
|
||||||
let testFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1Contract;
|
let testFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1Contract;
|
||||||
@ -26,7 +26,7 @@ blockchainTests.resets('SimpleFunctionRegistry feature', env => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
[owner] = await env.getAccountAddressesAsync();
|
[owner] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
||||||
registry = new ISimpleFunctionRegistryContract(zeroEx.address, env.provider, {
|
registry = new ISimpleFunctionRegistryFeatureContract(zeroEx.address, env.provider, {
|
||||||
...env.txDefaults,
|
...env.txDefaults,
|
||||||
from: owner,
|
from: owner,
|
||||||
});
|
});
|
||||||
@ -75,7 +75,7 @@ blockchainTests.resets('SimpleFunctionRegistry feature', env => {
|
|||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
logs,
|
logs,
|
||||||
[{ selector: testFnSelector, oldImpl: NULL_ADDRESS, newImpl: testFeatureImpl1.address }],
|
[{ selector: testFnSelector, oldImpl: NULL_ADDRESS, newImpl: testFeatureImpl1.address }],
|
||||||
ISimpleFunctionRegistryEvents.ProxyFunctionUpdated,
|
ISimpleFunctionRegistryFeatureEvents.ProxyFunctionUpdated,
|
||||||
);
|
);
|
||||||
const r = await testFeature.testFn().callAsync();
|
const r = await testFeature.testFn().callAsync();
|
||||||
expect(r).to.bignumber.eq(1337);
|
expect(r).to.bignumber.eq(1337);
|
||||||
@ -117,7 +117,7 @@ blockchainTests.resets('SimpleFunctionRegistry feature', env => {
|
|||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
logs,
|
logs,
|
||||||
[{ selector: testFnSelector, oldImpl: testFeatureImpl2.address, newImpl: NULL_ADDRESS }],
|
[{ selector: testFnSelector, oldImpl: testFeatureImpl2.address, newImpl: NULL_ADDRESS }],
|
||||||
ISimpleFunctionRegistryEvents.ProxyFunctionUpdated,
|
ISimpleFunctionRegistryFeatureEvents.ProxyFunctionUpdated,
|
||||||
);
|
);
|
||||||
const rollbackLength = await registry.getRollbackLength(testFnSelector).callAsync();
|
const rollbackLength = await registry.getRollbackLength(testFnSelector).callAsync();
|
||||||
expect(rollbackLength).to.bignumber.eq(0);
|
expect(rollbackLength).to.bignumber.eq(0);
|
||||||
|
@ -7,29 +7,29 @@ import {
|
|||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
import { TokenSpenderContract, ZeroExContract } from '../../src/wrappers';
|
import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers';
|
||||||
import { artifacts } from '../artifacts';
|
import { artifacts } from '../artifacts';
|
||||||
import { abis } from '../utils/abis';
|
import { abis } from '../utils/abis';
|
||||||
import { fullMigrateAsync } from '../utils/migration';
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
|
import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
|
||||||
|
|
||||||
blockchainTests.resets('TokenSpender feature', env => {
|
blockchainTests.resets('TokenSpender feature', env => {
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: TokenSpenderContract;
|
let feature: TokenSpenderFeatureContract;
|
||||||
let token: TestTokenSpenderERC20TokenContract;
|
let token: TestTokenSpenderERC20TokenContract;
|
||||||
let allowanceTarget: string;
|
let allowanceTarget: string;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const [owner] = await env.getAccountAddressesAsync();
|
const [owner] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||||
tokenSpender: (await TokenSpenderContract.deployFrom0xArtifactAsync(
|
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTokenSpender,
|
artifacts.TestTokenSpender,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
});
|
});
|
||||||
feature = new TokenSpenderContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTokenSpenderERC20Token,
|
artifacts.TestTokenSpenderERC20Token,
|
||||||
env.provider,
|
env.provider,
|
||||||
@ -98,7 +98,7 @@ blockchainTests.resets('TokenSpender feature', env => {
|
|||||||
tokenFrom,
|
tokenFrom,
|
||||||
tokenTo,
|
tokenTo,
|
||||||
tokenAmount,
|
tokenAmount,
|
||||||
new StringRevertError('TestTokenSpenderERC20Token/Revert').toString(),
|
new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(),
|
||||||
);
|
);
|
||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
@ -14,21 +14,21 @@ import { DecodedLogEntry } from 'ethereum-types';
|
|||||||
import * as ethjs from 'ethereumjs-util';
|
import * as ethjs from 'ethereumjs-util';
|
||||||
|
|
||||||
import { generateCallDataHashSignature, signCallData } from '../../src/signed_call_data';
|
import { generateCallDataHashSignature, signCallData } from '../../src/signed_call_data';
|
||||||
import { TransformERC20Contract, ZeroExContract } from '../../src/wrappers';
|
import { IZeroExContract, TransformERC20FeatureContract } from '../../src/wrappers';
|
||||||
import { artifacts } from '../artifacts';
|
import { artifacts } from '../artifacts';
|
||||||
import { abis } from '../utils/abis';
|
import { abis } from '../utils/abis';
|
||||||
import { fullMigrateAsync } from '../utils/migration';
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
import {
|
import {
|
||||||
FlashWalletContract,
|
FlashWalletContract,
|
||||||
ITokenSpenderContract,
|
ITokenSpenderFeatureContract,
|
||||||
TestMintableERC20TokenContract,
|
TestMintableERC20TokenContract,
|
||||||
TestMintTokenERC20TransformerContract,
|
TestMintTokenERC20TransformerContract,
|
||||||
TestMintTokenERC20TransformerEvents,
|
TestMintTokenERC20TransformerEvents,
|
||||||
TestMintTokenERC20TransformerMintTransformEventArgs,
|
TestMintTokenERC20TransformerMintTransformEventArgs,
|
||||||
TransformERC20Events,
|
TransformERC20FeatureEvents,
|
||||||
} from '../wrappers';
|
} from '../wrappers';
|
||||||
|
|
||||||
const { NULL_BYTES, NULL_BYTES32 } = constants;
|
const { NULL_ADDRESS, NULL_BYTES, NULL_BYTES32 } = constants;
|
||||||
|
|
||||||
type MintTokenTransformerEvent = DecodedLogEntry<TestMintTokenERC20TransformerMintTransformEventArgs>;
|
type MintTokenTransformerEvent = DecodedLogEntry<TestMintTokenERC20TransformerMintTransformEventArgs>;
|
||||||
|
|
||||||
@ -37,20 +37,21 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
const callDataSigner = ethjs.bufferToHex(ethjs.privateToAddress(ethjs.toBuffer(callDataSignerKey)));
|
const callDataSigner = ethjs.bufferToHex(ethjs.privateToAddress(ethjs.toBuffer(callDataSignerKey)));
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let taker: string;
|
let taker: string;
|
||||||
|
let sender: string;
|
||||||
let transformerDeployer: string;
|
let transformerDeployer: string;
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: TransformERC20Contract;
|
let feature: TransformERC20FeatureContract;
|
||||||
let wallet: FlashWalletContract;
|
let wallet: FlashWalletContract;
|
||||||
let allowanceTarget: string;
|
let allowanceTarget: string;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner, taker, transformerDeployer] = await env.getAccountAddressesAsync();
|
[owner, taker, sender, transformerDeployer] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await fullMigrateAsync(
|
zeroEx = await fullMigrateAsync(
|
||||||
owner,
|
owner,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{
|
{
|
||||||
transformERC20: (await TransformERC20Contract.deployFrom0xArtifactAsync(
|
transformERC20: (await TransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTransformERC20,
|
artifacts.TestTransformERC20,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
@ -59,12 +60,17 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
},
|
},
|
||||||
{ transformerDeployer },
|
{ transformerDeployer },
|
||||||
);
|
);
|
||||||
feature = new TransformERC20Contract(zeroEx.address, env.provider, env.txDefaults, abis);
|
feature = new TransformERC20FeatureContract(
|
||||||
|
zeroEx.address,
|
||||||
|
env.provider,
|
||||||
|
{ ...env.txDefaults, from: sender },
|
||||||
|
abis,
|
||||||
|
);
|
||||||
wallet = new FlashWalletContract(await feature.getTransformWallet().callAsync(), env.provider, env.txDefaults);
|
wallet = new FlashWalletContract(await feature.getTransformWallet().callAsync(), env.provider, env.txDefaults);
|
||||||
allowanceTarget = await new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults)
|
allowanceTarget = await new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults)
|
||||||
.getAllowanceTarget()
|
.getAllowanceTarget()
|
||||||
.callAsync();
|
.callAsync();
|
||||||
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync();
|
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync({ from: owner });
|
||||||
});
|
});
|
||||||
|
|
||||||
const { MAX_UINT256, ZERO_AMOUNT } = constants;
|
const { MAX_UINT256, ZERO_AMOUNT } = constants;
|
||||||
@ -73,7 +79,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
it('createTransformWallet() replaces the current wallet', async () => {
|
it('createTransformWallet() replaces the current wallet', async () => {
|
||||||
const newWalletAddress = await feature.createTransformWallet().callAsync({ from: owner });
|
const newWalletAddress = await feature.createTransformWallet().callAsync({ from: owner });
|
||||||
expect(newWalletAddress).to.not.eq(wallet.address);
|
expect(newWalletAddress).to.not.eq(wallet.address);
|
||||||
await feature.createTransformWallet().awaitTransactionSuccessAsync();
|
await feature.createTransformWallet().awaitTransactionSuccessAsync({ from: owner });
|
||||||
return expect(feature.getTransformWallet().callAsync()).to.eventually.eq(newWalletAddress);
|
return expect(feature.getTransformWallet().callAsync()).to.eventually.eq(newWalletAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,7 +104,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
[{ transformerDeployer: newDeployer }],
|
[{ transformerDeployer: newDeployer }],
|
||||||
TransformERC20Events.TransformerDeployerUpdated,
|
TransformERC20FeatureEvents.TransformerDeployerUpdated,
|
||||||
);
|
);
|
||||||
const actualDeployer = await feature.getTransformerDeployer().callAsync();
|
const actualDeployer = await feature.getTransformerDeployer().callAsync();
|
||||||
expect(actualDeployer).to.eq(newDeployer);
|
expect(actualDeployer).to.eq(newDeployer);
|
||||||
@ -121,7 +127,11 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
it('owner can set the quote signer with `setQuoteSigner()`', async () => {
|
it('owner can set the quote signer with `setQuoteSigner()`', async () => {
|
||||||
const newSigner = randomAddress();
|
const newSigner = randomAddress();
|
||||||
const receipt = await feature.setQuoteSigner(newSigner).awaitTransactionSuccessAsync({ from: owner });
|
const receipt = await feature.setQuoteSigner(newSigner).awaitTransactionSuccessAsync({ from: owner });
|
||||||
verifyEventsFromLogs(receipt.logs, [{ quoteSigner: newSigner }], TransformERC20Events.QuoteSignerUpdated);
|
verifyEventsFromLogs(
|
||||||
|
receipt.logs,
|
||||||
|
[{ quoteSigner: newSigner }],
|
||||||
|
TransformERC20FeatureEvents.QuoteSignerUpdated,
|
||||||
|
);
|
||||||
const actualSigner = await feature.getQuoteSigner().callAsync();
|
const actualSigner = await feature.getQuoteSigner().callAsync();
|
||||||
expect(actualSigner).to.eq(newSigner);
|
expect(actualSigner).to.eq(newSigner);
|
||||||
});
|
});
|
||||||
@ -258,14 +268,15 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
outputToken: outputToken.address,
|
outputToken: outputToken.address,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
TransformERC20Events.TransformedERC20,
|
TransformERC20FeatureEvents.TransformedERC20,
|
||||||
);
|
);
|
||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
callDataHash: NULL_BYTES32,
|
sender,
|
||||||
taker,
|
taker,
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
context: wallet.address,
|
context: wallet.address,
|
||||||
caller: zeroEx.address,
|
caller: zeroEx.address,
|
||||||
data: transformation.data,
|
data: transformation.data,
|
||||||
@ -314,14 +325,15 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
outputToken: ETH_TOKEN_ADDRESS,
|
outputToken: ETH_TOKEN_ADDRESS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
TransformERC20Events.TransformedERC20,
|
TransformERC20FeatureEvents.TransformedERC20,
|
||||||
);
|
);
|
||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
callDataHash: NULL_BYTES32,
|
|
||||||
taker,
|
taker,
|
||||||
|
sender,
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
context: wallet.address,
|
context: wallet.address,
|
||||||
caller: zeroEx.address,
|
caller: zeroEx.address,
|
||||||
data: transformation.data,
|
data: transformation.data,
|
||||||
@ -373,14 +385,15 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
outputToken: outputToken.address,
|
outputToken: outputToken.address,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
TransformERC20Events.TransformedERC20,
|
TransformERC20FeatureEvents.TransformedERC20,
|
||||||
);
|
);
|
||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
callDataHash: NULL_BYTES32,
|
sender,
|
||||||
taker,
|
taker,
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
context: wallet.address,
|
context: wallet.address,
|
||||||
caller: zeroEx.address,
|
caller: zeroEx.address,
|
||||||
data: transformation.data,
|
data: transformation.data,
|
||||||
@ -496,8 +509,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
receipt.logs,
|
receipt.logs,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
callDataHash: NULL_BYTES32,
|
sender,
|
||||||
taker,
|
taker,
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
context: wallet.address,
|
context: wallet.address,
|
||||||
caller: zeroEx.address,
|
caller: zeroEx.address,
|
||||||
data: transformations[0].data,
|
data: transformations[0].data,
|
||||||
@ -505,8 +519,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
ethBalance: callValue,
|
ethBalance: callValue,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
callDataHash: NULL_BYTES32,
|
sender,
|
||||||
taker,
|
taker,
|
||||||
|
callDataHash: NULL_BYTES32,
|
||||||
context: wallet.address,
|
context: wallet.address,
|
||||||
caller: zeroEx.address,
|
caller: zeroEx.address,
|
||||||
data: transformations[1].data,
|
data: transformations[1].data,
|
||||||
@ -672,6 +687,37 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
expect(actualCallDataHash).to.eq(hexUtils.hash(callData));
|
expect(actualCallDataHash).to.eq(hexUtils.hash(callData));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes the calldata hash to transformer when no quote signer configured', async () => {
|
||||||
|
const startingOutputTokenBalance = getRandomInteger(0, '100e18');
|
||||||
|
const startingInputTokenBalance = getRandomInteger(0, '100e18');
|
||||||
|
await outputToken.mint(taker, startingOutputTokenBalance).awaitTransactionSuccessAsync();
|
||||||
|
await inputToken.mint(taker, startingInputTokenBalance).awaitTransactionSuccessAsync();
|
||||||
|
const inputTokenAmount = getRandomPortion(startingInputTokenBalance);
|
||||||
|
const minOutputTokenAmount = getRandomInteger(1, '1e18');
|
||||||
|
const outputTokenMintAmount = minOutputTokenAmount;
|
||||||
|
const callValue = getRandomInteger(1, '1e18');
|
||||||
|
const transformation = createMintTokenTransformation({
|
||||||
|
outputTokenMintAmount,
|
||||||
|
inputTokenBurnAmunt: inputTokenAmount,
|
||||||
|
});
|
||||||
|
const bakedCall = feature.transformERC20(
|
||||||
|
inputToken.address,
|
||||||
|
outputToken.address,
|
||||||
|
inputTokenAmount,
|
||||||
|
minOutputTokenAmount,
|
||||||
|
[transformation],
|
||||||
|
);
|
||||||
|
const callData = bakedCall.getABIEncodedTransactionData();
|
||||||
|
await feature.setQuoteSigner(NULL_ADDRESS).awaitTransactionSuccessAsync({ from: owner });
|
||||||
|
const receipt = await bakedCall.awaitTransactionSuccessAsync({
|
||||||
|
from: taker,
|
||||||
|
value: callValue,
|
||||||
|
data: callData,
|
||||||
|
});
|
||||||
|
const { callDataHash: actualCallDataHash } = (receipt.logs[0] as MintTokenTransformerEvent).args;
|
||||||
|
expect(actualCallDataHash).to.eq(hexUtils.hash(callData));
|
||||||
|
});
|
||||||
|
|
||||||
it('passes empty calldata hash to transformer with improperly signed calldata', async () => {
|
it('passes empty calldata hash to transformer with improperly signed calldata', async () => {
|
||||||
const startingOutputTokenBalance = getRandomInteger(0, '100e18');
|
const startingOutputTokenBalance = getRandomInteger(0, '100e18');
|
||||||
const startingInputTokenBalance = getRandomInteger(0, '100e18');
|
const startingInputTokenBalance = getRandomInteger(0, '100e18');
|
||||||
|
@ -127,7 +127,7 @@ blockchainTests.resets('FlashWallet', env => {
|
|||||||
callTarget.address,
|
callTarget.address,
|
||||||
REVERTING_DATA,
|
REVERTING_DATA,
|
||||||
constants.ZERO_AMOUNT,
|
constants.ZERO_AMOUNT,
|
||||||
new StringRevertError('TestCallTarget/REVERT').toString(),
|
new StringRevertError('TestCallTarget/REVERT'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -203,7 +203,7 @@ blockchainTests.resets('FlashWallet', env => {
|
|||||||
wallet.address,
|
wallet.address,
|
||||||
callTarget.address,
|
callTarget.address,
|
||||||
REVERTING_DATA,
|
REVERTING_DATA,
|
||||||
new StringRevertError('TestCallTarget/REVERT').toString(),
|
new StringRevertError('TestCallTarget/REVERT'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -9,11 +9,11 @@ import { abis } from './utils/abis';
|
|||||||
import { deployFullFeaturesAsync, FullFeatures } from './utils/migration';
|
import { deployFullFeaturesAsync, FullFeatures } from './utils/migration';
|
||||||
import {
|
import {
|
||||||
AllowanceTargetContract,
|
AllowanceTargetContract,
|
||||||
IMetaTransactionsContract,
|
IMetaTransactionsFeatureContract,
|
||||||
IOwnableContract,
|
IOwnableFeatureContract,
|
||||||
ISignatureValidatorContract,
|
ISignatureValidatorFeatureContract,
|
||||||
ITokenSpenderContract,
|
ITokenSpenderFeatureContract,
|
||||||
ITransformERC20Contract,
|
ITransformERC20FeatureContract,
|
||||||
TestFullMigrationContract,
|
TestFullMigrationContract,
|
||||||
ZeroExContract,
|
ZeroExContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
@ -50,7 +50,7 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ZeroEx has the correct owner', async () => {
|
it('ZeroEx has the correct owner', async () => {
|
||||||
const ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
|
const ownable = new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
const actualOwner = await ownable.owner().callAsync();
|
const actualOwner = await ownable.owner().callAsync();
|
||||||
expect(actualOwner).to.eq(owner);
|
expect(actualOwner).to.eq(owner);
|
||||||
});
|
});
|
||||||
@ -70,11 +70,11 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
|
|
||||||
const FEATURE_FNS = {
|
const FEATURE_FNS = {
|
||||||
TokenSpender: {
|
TokenSpender: {
|
||||||
contractType: ITokenSpenderContract,
|
contractType: ITokenSpenderFeatureContract,
|
||||||
fns: ['_spendERC20Tokens'],
|
fns: ['_spendERC20Tokens'],
|
||||||
},
|
},
|
||||||
TransformERC20: {
|
TransformERC20: {
|
||||||
contractType: ITransformERC20Contract,
|
contractType: ITransformERC20FeatureContract,
|
||||||
fns: [
|
fns: [
|
||||||
'transformERC20',
|
'transformERC20',
|
||||||
'_transformERC20',
|
'_transformERC20',
|
||||||
@ -86,11 +86,11 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
SignatureValidator: {
|
SignatureValidator: {
|
||||||
contractType: ISignatureValidatorContract,
|
contractType: ISignatureValidatorFeatureContract,
|
||||||
fns: ['isValidHashSignature', 'validateHashSignature'],
|
fns: ['isValidHashSignature', 'validateHashSignature'],
|
||||||
},
|
},
|
||||||
MetaTransactions: {
|
MetaTransactions: {
|
||||||
contractType: IMetaTransactionsContract,
|
contractType: IMetaTransactionsFeatureContract,
|
||||||
fns: [
|
fns: [
|
||||||
'executeMetaTransaction',
|
'executeMetaTransaction',
|
||||||
'batchExecuteMetaTransactions',
|
'batchExecuteMetaTransactions',
|
||||||
@ -181,7 +181,7 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
let allowanceTarget: AllowanceTargetContract;
|
let allowanceTarget: AllowanceTargetContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const contract = new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults);
|
const contract = new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
allowanceTarget = new AllowanceTargetContract(
|
allowanceTarget = new AllowanceTargetContract(
|
||||||
await contract.getAllowanceTarget().callAsync(),
|
await contract.getAllowanceTarget().callAsync(),
|
||||||
env.provider,
|
env.provider,
|
||||||
@ -199,10 +199,10 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('TransformERC20', () => {
|
describe('TransformERC20', () => {
|
||||||
let feature: ITransformERC20Contract;
|
let feature: ITransformERC20FeatureContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
feature = new ITransformERC20Contract(zeroEx.address, env.provider, env.txDefaults);
|
feature = new ITransformERC20FeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has the correct transformer deployer', async () => {
|
it('has the correct transformer deployer', async () => {
|
||||||
|
@ -4,10 +4,10 @@ import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
|||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { BootstrapFeatures, deployBootstrapFeaturesAsync } from './utils/migration';
|
import { BootstrapFeatures, deployBootstrapFeaturesAsync } from './utils/migration';
|
||||||
import {
|
import {
|
||||||
IBootstrapContract,
|
IBootstrapFeatureContract,
|
||||||
InitialMigrationContract,
|
InitialMigrationContract,
|
||||||
IOwnableContract,
|
IOwnableFeatureContract,
|
||||||
SimpleFunctionRegistryContract,
|
SimpleFunctionRegistryFeatureContract,
|
||||||
TestInitialMigrationContract,
|
TestInitialMigrationContract,
|
||||||
ZeroExContract,
|
ZeroExContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
@ -16,7 +16,7 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
let owner: string;
|
let owner: string;
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: ZeroExContract;
|
||||||
let migrator: TestInitialMigrationContract;
|
let migrator: TestInitialMigrationContract;
|
||||||
let bootstrapFeature: IBootstrapContract;
|
let bootstrapFeature: IBootstrapFeatureContract;
|
||||||
let features: BootstrapFeatures;
|
let features: BootstrapFeatures;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
@ -29,7 +29,7 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
artifacts,
|
artifacts,
|
||||||
env.txDefaults.from as string,
|
env.txDefaults.from as string,
|
||||||
);
|
);
|
||||||
bootstrapFeature = new IBootstrapContract(
|
bootstrapFeature = new IBootstrapFeatureContract(
|
||||||
await migrator.bootstrapFeature().callAsync(),
|
await migrator.bootstrapFeature().callAsync(),
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
@ -82,10 +82,10 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Ownable feature', () => {
|
describe('Ownable feature', () => {
|
||||||
let ownable: IOwnableContract;
|
let ownable: IOwnableFeatureContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
|
ownable = new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has the correct owner', async () => {
|
it('has the correct owner', async () => {
|
||||||
@ -95,10 +95,10 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('SimpleFunctionRegistry feature', () => {
|
describe('SimpleFunctionRegistry feature', () => {
|
||||||
let registry: SimpleFunctionRegistryContract;
|
let registry: SimpleFunctionRegistryFeatureContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
registry = new SimpleFunctionRegistryContract(zeroEx.address, env.provider, env.txDefaults);
|
registry = new SimpleFunctionRegistryFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('_extendSelf() is deregistered', async () => {
|
it('_extendSelf() is deregistered', async () => {
|
||||||
|
@ -59,6 +59,12 @@ blockchainTests.resets('TransformerDeployer', env => {
|
|||||||
expect(await env.web3Wrapper.getBalanceInWeiAsync(targetAddress)).to.bignumber.eq(1);
|
expect(await env.web3Wrapper.getBalanceInWeiAsync(targetAddress)).to.bignumber.eq(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reverts if constructor throws', async () => {
|
||||||
|
const CONSTRUCTOR_FAIL_VALUE = new BigNumber(3333);
|
||||||
|
const tx = deployer.deploy(deployBytes).callAsync({ value: CONSTRUCTOR_FAIL_VALUE, from: authority });
|
||||||
|
return expect(tx).to.revertWith('TransformerDeployer/DEPLOY_FAILED');
|
||||||
|
});
|
||||||
|
|
||||||
it('updates nonce', async () => {
|
it('updates nonce', async () => {
|
||||||
expect(await deployer.nonce().callAsync()).to.bignumber.eq(1);
|
expect(await deployer.nonce().callAsync()).to.bignumber.eq(1);
|
||||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||||
@ -82,6 +88,7 @@ blockchainTests.resets('TransformerDeployer', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('kill()', () => {
|
describe('kill()', () => {
|
||||||
|
const ethRecipient = randomAddress();
|
||||||
let target: TestTransformerDeployerTransformerContract;
|
let target: TestTransformerDeployerTransformerContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
@ -90,14 +97,16 @@ blockchainTests.resets('TransformerDeployer', env => {
|
|||||||
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
await deployer.deploy(deployBytes).awaitTransactionSuccessAsync({ from: authority });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authority cannot call', async () => {
|
it('non-authority cannot call', async () => {
|
||||||
const nonAuthority = randomAddress();
|
const nonAuthority = randomAddress();
|
||||||
const tx = deployer.kill(target.address).callAsync({ from: nonAuthority });
|
const tx = deployer.kill(target.address, ethRecipient).callAsync({ from: nonAuthority });
|
||||||
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(nonAuthority));
|
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(nonAuthority));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authority can kill a contract', async () => {
|
it('authority can kill a contract', async () => {
|
||||||
const receipt = await deployer.kill(target.address).awaitTransactionSuccessAsync({ from: authority });
|
const receipt = await deployer
|
||||||
|
.kill(target.address, ethRecipient)
|
||||||
|
.awaitTransactionSuccessAsync({ from: authority });
|
||||||
verifyEventsFromLogs(
|
verifyEventsFromLogs(
|
||||||
receipt.logs,
|
receipt.logs,
|
||||||
[{ target: target.address, sender: authority }],
|
[{ target: target.address, sender: authority }],
|
||||||
|
@ -86,7 +86,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
sender: randomAddress(),
|
||||||
|
taker: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||||
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
||||||
@ -112,7 +117,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
sender: randomAddress(),
|
||||||
|
taker: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||||
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
||||||
@ -138,7 +148,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
sender: randomAddress(),
|
||||||
|
taker: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
||||||
tokenBalance: new BigNumber(1),
|
tokenBalance: new BigNumber(1),
|
||||||
|
@ -32,6 +32,8 @@ const { NULL_ADDRESS, NULL_BYTES, MAX_UINT256, ZERO_AMOUNT } = constants;
|
|||||||
blockchainTests.resets('FillQuoteTransformer', env => {
|
blockchainTests.resets('FillQuoteTransformer', env => {
|
||||||
let maker: string;
|
let maker: string;
|
||||||
let feeRecipient: string;
|
let feeRecipient: string;
|
||||||
|
let sender: string;
|
||||||
|
let taker: string;
|
||||||
let exchange: TestFillQuoteTransformerExchangeContract;
|
let exchange: TestFillQuoteTransformerExchangeContract;
|
||||||
let bridge: TestFillQuoteTransformerBridgeContract;
|
let bridge: TestFillQuoteTransformerBridgeContract;
|
||||||
let transformer: FillQuoteTransformerContract;
|
let transformer: FillQuoteTransformerContract;
|
||||||
@ -44,7 +46,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
const GAS_PRICE = 1337;
|
const GAS_PRICE = 1337;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[maker, feeRecipient] = await env.getAccountAddressesAsync();
|
[maker, feeRecipient, sender, taker] = await env.getAccountAddressesAsync();
|
||||||
exchange = await TestFillQuoteTransformerExchangeContract.deployFrom0xArtifactAsync(
|
exchange = await TestFillQuoteTransformerExchangeContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestFillQuoteTransformerExchange,
|
artifacts.TestFillQuoteTransformerExchange,
|
||||||
env.provider,
|
env.provider,
|
||||||
@ -60,6 +62,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
balancerBridge: NULL_ADDRESS,
|
balancerBridge: NULL_ADDRESS,
|
||||||
curveBridge: NULL_ADDRESS,
|
curveBridge: NULL_ADDRESS,
|
||||||
kyberBridge: NULL_ADDRESS,
|
kyberBridge: NULL_ADDRESS,
|
||||||
|
mooniswapBridge: NULL_ADDRESS,
|
||||||
mStableBridge: NULL_ADDRESS,
|
mStableBridge: NULL_ADDRESS,
|
||||||
oasisBridge: NULL_ADDRESS,
|
oasisBridge: NULL_ADDRESS,
|
||||||
uniswapBridge: NULL_ADDRESS,
|
uniswapBridge: NULL_ADDRESS,
|
||||||
@ -92,7 +95,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
bridge = await TestFillQuoteTransformerBridgeContract.deployFrom0xArtifactAsync(
|
bridge = await TestFillQuoteTransformerBridgeContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestFillQuoteTransformerBridge,
|
artifacts.TestFillQuoteTransformerBridge,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
{ ...env.txDefaults, from: sender },
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
[makerToken, takerToken, takerFeeToken] = await Promise.all(
|
[makerToken, takerToken, takerFeeToken] = await Promise.all(
|
||||||
@ -270,6 +273,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
signatures: [],
|
signatures: [],
|
||||||
maxOrderFillAmounts: [],
|
maxOrderFillAmounts: [],
|
||||||
fillAmount: MAX_UINT256,
|
fillAmount: MAX_UINT256,
|
||||||
|
refundReceiver: NULL_ADDRESS,
|
||||||
|
rfqtTakerAddress: NULL_ADDRESS,
|
||||||
...fields,
|
...fields,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -313,6 +318,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -334,6 +341,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -358,6 +367,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -380,6 +391,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -404,6 +417,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -430,6 +445,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -462,6 +479,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -486,6 +505,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -511,6 +532,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
takerTokenBalance,
|
takerTokenBalance,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -535,6 +558,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
takerTokenBalance,
|
takerTokenBalance,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -564,6 +589,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -586,6 +613,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -608,6 +637,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -627,6 +658,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -640,6 +673,80 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
makerAssetBalance: qfr.makerAssetBought,
|
makerAssetBalance: qfr.makerAssetBought,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can refund unspent protocol fee to the `refundReceiver`', async () => {
|
||||||
|
const orders = _.times(2, () => createOrder());
|
||||||
|
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||||
|
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||||
|
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||||
|
const refundReceiver = randomAddress();
|
||||||
|
await host
|
||||||
|
.executeTransform(
|
||||||
|
transformer.address,
|
||||||
|
takerToken.address,
|
||||||
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
|
encodeTransformData({
|
||||||
|
orders,
|
||||||
|
signatures,
|
||||||
|
refundReceiver,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||||
|
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||||
|
expect(receiverBalancer).to.bignumber.eq(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can refund unspent protocol fee to the taker', async () => {
|
||||||
|
const orders = _.times(2, () => createOrder());
|
||||||
|
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||||
|
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||||
|
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||||
|
const refundReceiver = randomAddress();
|
||||||
|
await host
|
||||||
|
.executeTransform(
|
||||||
|
transformer.address,
|
||||||
|
takerToken.address,
|
||||||
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
refundReceiver, // taker = refundReceiver
|
||||||
|
encodeTransformData({
|
||||||
|
orders,
|
||||||
|
signatures,
|
||||||
|
// address(1) indicates taker
|
||||||
|
refundReceiver: hexUtils.leftPad(1, 20),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||||
|
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||||
|
expect(receiverBalancer).to.bignumber.eq(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can refund unspent protocol fee to the sender', async () => {
|
||||||
|
const orders = _.times(2, () => createOrder());
|
||||||
|
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||||
|
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||||
|
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||||
|
const refundReceiver = randomAddress();
|
||||||
|
await host
|
||||||
|
.executeTransform(
|
||||||
|
transformer.address,
|
||||||
|
takerToken.address,
|
||||||
|
qfr.takerAssetSpent,
|
||||||
|
refundReceiver, // sender = refundReceiver
|
||||||
|
taker,
|
||||||
|
encodeTransformData({
|
||||||
|
orders,
|
||||||
|
signatures,
|
||||||
|
// address(2) indicates sender
|
||||||
|
refundReceiver: hexUtils.leftPad(2, 20),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||||
|
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||||
|
expect(receiverBalancer).to.bignumber.eq(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buy quotes', () => {
|
describe('buy quotes', () => {
|
||||||
@ -652,6 +759,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -675,6 +784,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -701,6 +812,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -725,6 +838,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -751,6 +866,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -774,6 +891,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -804,6 +923,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -828,6 +949,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -852,6 +975,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -873,6 +998,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -900,6 +1027,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -926,6 +1055,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -952,6 +1083,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
@ -980,6 +1113,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
transformer.address,
|
transformer.address,
|
||||||
takerToken.address,
|
takerToken.address,
|
||||||
qfr.takerAssetSpent,
|
qfr.takerAssetSpent,
|
||||||
|
sender,
|
||||||
|
taker,
|
||||||
encodeTransformData({
|
encodeTransformData({
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
|
@ -78,7 +78,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
taker,
|
||||||
|
sender: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||||
@ -96,7 +101,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
taker,
|
||||||
|
sender: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||||
@ -114,7 +124,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
taker,
|
||||||
|
sender: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||||
@ -132,7 +147,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
|||||||
await mintHostTokensAsync(amounts[0]);
|
await mintHostTokensAsync(amounts[0]);
|
||||||
await sendEtherAsync(host.address, amounts[1]);
|
await sendEtherAsync(host.address, amounts[1]);
|
||||||
await host
|
await host
|
||||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
.rawExecuteTransform(transformer.address, {
|
||||||
|
data,
|
||||||
|
callDataHash: hexUtils.random(),
|
||||||
|
taker,
|
||||||
|
sender: randomAddress(),
|
||||||
|
})
|
||||||
.awaitTransactionSuccessAsync();
|
.awaitTransactionSuccessAsync();
|
||||||
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
||||||
tokenBalance: amounts[0].minus(amounts[0].dividedToIntegerBy(2)),
|
tokenBalance: amounts[0].minus(amounts[0].dividedToIntegerBy(2)),
|
||||||
|
@ -5,15 +5,16 @@
|
|||||||
*/
|
*/
|
||||||
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
||||||
export * from '../test/generated-wrappers/allowance_target';
|
export * from '../test/generated-wrappers/allowance_target';
|
||||||
export * from '../test/generated-wrappers/bootstrap';
|
export * from '../test/generated-wrappers/bootstrap_feature';
|
||||||
export * from '../test/generated-wrappers/bridge_adapter';
|
export * from '../test/generated-wrappers/bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/fill_quote_transformer';
|
export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||||
export * from '../test/generated-wrappers/fixin_common';
|
export * from '../test/generated-wrappers/fixin_common';
|
||||||
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
||||||
|
export * from '../test/generated-wrappers/fixin_reentrancy_guard';
|
||||||
export * from '../test/generated-wrappers/flash_wallet';
|
export * from '../test/generated-wrappers/flash_wallet';
|
||||||
export * from '../test/generated-wrappers/full_migration';
|
export * from '../test/generated-wrappers/full_migration';
|
||||||
export * from '../test/generated-wrappers/i_allowance_target';
|
export * from '../test/generated-wrappers/i_allowance_target';
|
||||||
export * from '../test/generated-wrappers/i_bootstrap';
|
export * from '../test/generated-wrappers/i_bootstrap_feature';
|
||||||
export * from '../test/generated-wrappers/i_bridge_adapter';
|
export * from '../test/generated-wrappers/i_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||||
export * from '../test/generated-wrappers/i_erc20_transformer';
|
export * from '../test/generated-wrappers/i_erc20_transformer';
|
||||||
@ -21,13 +22,13 @@ export * from '../test/generated-wrappers/i_exchange';
|
|||||||
export * from '../test/generated-wrappers/i_feature';
|
export * from '../test/generated-wrappers/i_feature';
|
||||||
export * from '../test/generated-wrappers/i_flash_wallet';
|
export * from '../test/generated-wrappers/i_flash_wallet';
|
||||||
export * from '../test/generated-wrappers/i_gas_token';
|
export * from '../test/generated-wrappers/i_gas_token';
|
||||||
export * from '../test/generated-wrappers/i_meta_transactions';
|
export * from '../test/generated-wrappers/i_meta_transactions_feature';
|
||||||
export * from '../test/generated-wrappers/i_ownable';
|
export * from '../test/generated-wrappers/i_ownable_feature';
|
||||||
export * from '../test/generated-wrappers/i_signature_validator';
|
export * from '../test/generated-wrappers/i_signature_validator_feature';
|
||||||
export * from '../test/generated-wrappers/i_simple_function_registry';
|
export * from '../test/generated-wrappers/i_simple_function_registry_feature';
|
||||||
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
||||||
export * from '../test/generated-wrappers/i_token_spender';
|
export * from '../test/generated-wrappers/i_token_spender_feature';
|
||||||
export * from '../test/generated-wrappers/i_transform_erc20';
|
export * from '../test/generated-wrappers/i_transform_erc20_feature';
|
||||||
export * from '../test/generated-wrappers/i_zero_ex';
|
export * from '../test/generated-wrappers/i_zero_ex';
|
||||||
export * from '../test/generated-wrappers/initial_migration';
|
export * from '../test/generated-wrappers/initial_migration';
|
||||||
export * from '../test/generated-wrappers/lib_bootstrap';
|
export * from '../test/generated-wrappers/lib_bootstrap';
|
||||||
@ -40,6 +41,7 @@ export * from '../test/generated-wrappers/lib_ownable_rich_errors';
|
|||||||
export * from '../test/generated-wrappers/lib_ownable_storage';
|
export * from '../test/generated-wrappers/lib_ownable_storage';
|
||||||
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_proxy_storage';
|
export * from '../test/generated-wrappers/lib_proxy_storage';
|
||||||
|
export * from '../test/generated-wrappers/lib_reentrancy_guard_storage';
|
||||||
export * from '../test/generated-wrappers/lib_signature_rich_errors';
|
export * from '../test/generated-wrappers/lib_signature_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_signed_call_data';
|
export * from '../test/generated-wrappers/lib_signed_call_data';
|
||||||
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
||||||
@ -50,20 +52,22 @@ export * from '../test/generated-wrappers/lib_token_spender_storage';
|
|||||||
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
|
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
|
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
|
||||||
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
|
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
|
||||||
export * from '../test/generated-wrappers/meta_transactions';
|
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_adapter_addresses';
|
||||||
export * from '../test/generated-wrappers/mixin_balancer';
|
export * from '../test/generated-wrappers/mixin_balancer';
|
||||||
export * from '../test/generated-wrappers/mixin_curve';
|
export * from '../test/generated-wrappers/mixin_curve';
|
||||||
export * from '../test/generated-wrappers/mixin_kyber';
|
export * from '../test/generated-wrappers/mixin_kyber';
|
||||||
export * from '../test/generated-wrappers/mixin_m_stable';
|
export * from '../test/generated-wrappers/mixin_m_stable';
|
||||||
|
export * from '../test/generated-wrappers/mixin_mooniswap';
|
||||||
export * from '../test/generated-wrappers/mixin_oasis';
|
export * from '../test/generated-wrappers/mixin_oasis';
|
||||||
export * from '../test/generated-wrappers/mixin_uniswap';
|
export * from '../test/generated-wrappers/mixin_uniswap';
|
||||||
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
||||||
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
||||||
export * from '../test/generated-wrappers/ownable';
|
export * from '../test/generated-wrappers/ownable_feature';
|
||||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||||
export * from '../test/generated-wrappers/signature_validator';
|
export * from '../test/generated-wrappers/signature_validator_feature';
|
||||||
export * from '../test/generated-wrappers/simple_function_registry';
|
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
||||||
export * from '../test/generated-wrappers/test_call_target';
|
export * from '../test/generated-wrappers/test_call_target';
|
||||||
export * from '../test/generated-wrappers/test_delegate_caller';
|
export * from '../test/generated-wrappers/test_delegate_caller';
|
||||||
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';
|
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';
|
||||||
@ -86,8 +90,8 @@ export * from '../test/generated-wrappers/test_transformer_host';
|
|||||||
export * from '../test/generated-wrappers/test_weth';
|
export * from '../test/generated-wrappers/test_weth';
|
||||||
export * from '../test/generated-wrappers/test_weth_transformer_host';
|
export * from '../test/generated-wrappers/test_weth_transformer_host';
|
||||||
export * from '../test/generated-wrappers/test_zero_ex_feature';
|
export * from '../test/generated-wrappers/test_zero_ex_feature';
|
||||||
export * from '../test/generated-wrappers/token_spender';
|
export * from '../test/generated-wrappers/token_spender_feature';
|
||||||
export * from '../test/generated-wrappers/transform_erc20';
|
export * from '../test/generated-wrappers/transform_erc20_feature';
|
||||||
export * from '../test/generated-wrappers/transformer';
|
export * from '../test/generated-wrappers/transformer';
|
||||||
export * from '../test/generated-wrappers/transformer_deployer';
|
export * from '../test/generated-wrappers/transformer_deployer';
|
||||||
export * from '../test/generated-wrappers/weth_transformer';
|
export * from '../test/generated-wrappers/weth_transformer';
|
||||||
|
@ -7,8 +7,8 @@ import { artifacts } from './artifacts';
|
|||||||
import { initialMigrateAsync } from './utils/migration';
|
import { initialMigrateAsync } from './utils/migration';
|
||||||
import {
|
import {
|
||||||
IFeatureContract,
|
IFeatureContract,
|
||||||
IOwnableContract,
|
IOwnableFeatureContract,
|
||||||
ISimpleFunctionRegistryContract,
|
ISimpleFunctionRegistryFeatureContract,
|
||||||
TestZeroExFeatureContract,
|
TestZeroExFeatureContract,
|
||||||
TestZeroExFeatureEvents,
|
TestZeroExFeatureEvents,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
@ -16,15 +16,15 @@ import {
|
|||||||
blockchainTests.resets('ZeroEx contract', env => {
|
blockchainTests.resets('ZeroEx contract', env => {
|
||||||
let owner: string;
|
let owner: string;
|
||||||
let zeroEx: ZeroExContract;
|
let zeroEx: ZeroExContract;
|
||||||
let ownable: IOwnableContract;
|
let ownable: IOwnableFeatureContract;
|
||||||
let registry: ISimpleFunctionRegistryContract;
|
let registry: ISimpleFunctionRegistryFeatureContract;
|
||||||
let testFeature: TestZeroExFeatureContract;
|
let testFeature: TestZeroExFeatureContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner] = await env.getAccountAddressesAsync();
|
[owner] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
zeroEx = await initialMigrateAsync(owner, env.provider, env.txDefaults);
|
||||||
ownable = new IOwnableContract(zeroEx.address, env.provider, env.txDefaults);
|
ownable = new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
registry = new ISimpleFunctionRegistryContract(zeroEx.address, env.provider, env.txDefaults);
|
registry = new ISimpleFunctionRegistryFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
testFeature = new TestZeroExFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
testFeature = new TestZeroExFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||||
// Register test features.
|
// Register test features.
|
||||||
const testFeatureImpl = await TestZeroExFeatureContract.deployFrom0xArtifactAsync(
|
const testFeatureImpl = await TestZeroExFeatureContract.deployFrom0xArtifactAsync(
|
||||||
|
@ -10,32 +10,34 @@
|
|||||||
"generated-artifacts/IAllowanceTarget.json",
|
"generated-artifacts/IAllowanceTarget.json",
|
||||||
"generated-artifacts/IERC20Transformer.json",
|
"generated-artifacts/IERC20Transformer.json",
|
||||||
"generated-artifacts/IFlashWallet.json",
|
"generated-artifacts/IFlashWallet.json",
|
||||||
"generated-artifacts/IOwnable.json",
|
"generated-artifacts/IOwnableFeature.json",
|
||||||
"generated-artifacts/ISimpleFunctionRegistry.json",
|
"generated-artifacts/ISimpleFunctionRegistryFeature.json",
|
||||||
"generated-artifacts/ITokenSpender.json",
|
"generated-artifacts/ITokenSpenderFeature.json",
|
||||||
"generated-artifacts/ITransformERC20.json",
|
"generated-artifacts/ITransformERC20Feature.json",
|
||||||
"generated-artifacts/IZeroEx.json",
|
"generated-artifacts/IZeroEx.json",
|
||||||
"generated-artifacts/InitialMigration.json",
|
"generated-artifacts/InitialMigration.json",
|
||||||
"generated-artifacts/MetaTransactions.json",
|
"generated-artifacts/LogMetadataTransformer.json",
|
||||||
"generated-artifacts/Ownable.json",
|
"generated-artifacts/MetaTransactionsFeature.json",
|
||||||
|
"generated-artifacts/OwnableFeature.json",
|
||||||
"generated-artifacts/PayTakerTransformer.json",
|
"generated-artifacts/PayTakerTransformer.json",
|
||||||
"generated-artifacts/SignatureValidator.json",
|
"generated-artifacts/SignatureValidatorFeature.json",
|
||||||
"generated-artifacts/SimpleFunctionRegistry.json",
|
"generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||||
"generated-artifacts/TokenSpender.json",
|
"generated-artifacts/TokenSpenderFeature.json",
|
||||||
"generated-artifacts/TransformERC20.json",
|
"generated-artifacts/TransformERC20Feature.json",
|
||||||
"generated-artifacts/WethTransformer.json",
|
"generated-artifacts/WethTransformer.json",
|
||||||
"generated-artifacts/ZeroEx.json",
|
"generated-artifacts/ZeroEx.json",
|
||||||
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
||||||
"test/generated-artifacts/AllowanceTarget.json",
|
"test/generated-artifacts/AllowanceTarget.json",
|
||||||
"test/generated-artifacts/Bootstrap.json",
|
"test/generated-artifacts/BootstrapFeature.json",
|
||||||
"test/generated-artifacts/BridgeAdapter.json",
|
"test/generated-artifacts/BridgeAdapter.json",
|
||||||
"test/generated-artifacts/FillQuoteTransformer.json",
|
"test/generated-artifacts/FillQuoteTransformer.json",
|
||||||
"test/generated-artifacts/FixinCommon.json",
|
"test/generated-artifacts/FixinCommon.json",
|
||||||
"test/generated-artifacts/FixinEIP712.json",
|
"test/generated-artifacts/FixinEIP712.json",
|
||||||
|
"test/generated-artifacts/FixinReentrancyGuard.json",
|
||||||
"test/generated-artifacts/FlashWallet.json",
|
"test/generated-artifacts/FlashWallet.json",
|
||||||
"test/generated-artifacts/FullMigration.json",
|
"test/generated-artifacts/FullMigration.json",
|
||||||
"test/generated-artifacts/IAllowanceTarget.json",
|
"test/generated-artifacts/IAllowanceTarget.json",
|
||||||
"test/generated-artifacts/IBootstrap.json",
|
"test/generated-artifacts/IBootstrapFeature.json",
|
||||||
"test/generated-artifacts/IBridgeAdapter.json",
|
"test/generated-artifacts/IBridgeAdapter.json",
|
||||||
"test/generated-artifacts/IERC20Bridge.json",
|
"test/generated-artifacts/IERC20Bridge.json",
|
||||||
"test/generated-artifacts/IERC20Transformer.json",
|
"test/generated-artifacts/IERC20Transformer.json",
|
||||||
@ -43,13 +45,13 @@
|
|||||||
"test/generated-artifacts/IFeature.json",
|
"test/generated-artifacts/IFeature.json",
|
||||||
"test/generated-artifacts/IFlashWallet.json",
|
"test/generated-artifacts/IFlashWallet.json",
|
||||||
"test/generated-artifacts/IGasToken.json",
|
"test/generated-artifacts/IGasToken.json",
|
||||||
"test/generated-artifacts/IMetaTransactions.json",
|
"test/generated-artifacts/IMetaTransactionsFeature.json",
|
||||||
"test/generated-artifacts/IOwnable.json",
|
"test/generated-artifacts/IOwnableFeature.json",
|
||||||
"test/generated-artifacts/ISignatureValidator.json",
|
"test/generated-artifacts/ISignatureValidatorFeature.json",
|
||||||
"test/generated-artifacts/ISimpleFunctionRegistry.json",
|
"test/generated-artifacts/ISimpleFunctionRegistryFeature.json",
|
||||||
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
||||||
"test/generated-artifacts/ITokenSpender.json",
|
"test/generated-artifacts/ITokenSpenderFeature.json",
|
||||||
"test/generated-artifacts/ITransformERC20.json",
|
"test/generated-artifacts/ITransformERC20Feature.json",
|
||||||
"test/generated-artifacts/IZeroEx.json",
|
"test/generated-artifacts/IZeroEx.json",
|
||||||
"test/generated-artifacts/InitialMigration.json",
|
"test/generated-artifacts/InitialMigration.json",
|
||||||
"test/generated-artifacts/LibBootstrap.json",
|
"test/generated-artifacts/LibBootstrap.json",
|
||||||
@ -62,6 +64,7 @@
|
|||||||
"test/generated-artifacts/LibOwnableStorage.json",
|
"test/generated-artifacts/LibOwnableStorage.json",
|
||||||
"test/generated-artifacts/LibProxyRichErrors.json",
|
"test/generated-artifacts/LibProxyRichErrors.json",
|
||||||
"test/generated-artifacts/LibProxyStorage.json",
|
"test/generated-artifacts/LibProxyStorage.json",
|
||||||
|
"test/generated-artifacts/LibReentrancyGuardStorage.json",
|
||||||
"test/generated-artifacts/LibSignatureRichErrors.json",
|
"test/generated-artifacts/LibSignatureRichErrors.json",
|
||||||
"test/generated-artifacts/LibSignedCallData.json",
|
"test/generated-artifacts/LibSignedCallData.json",
|
||||||
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
||||||
@ -72,20 +75,22 @@
|
|||||||
"test/generated-artifacts/LibTransformERC20RichErrors.json",
|
"test/generated-artifacts/LibTransformERC20RichErrors.json",
|
||||||
"test/generated-artifacts/LibTransformERC20Storage.json",
|
"test/generated-artifacts/LibTransformERC20Storage.json",
|
||||||
"test/generated-artifacts/LibWalletRichErrors.json",
|
"test/generated-artifacts/LibWalletRichErrors.json",
|
||||||
"test/generated-artifacts/MetaTransactions.json",
|
"test/generated-artifacts/LogMetadataTransformer.json",
|
||||||
|
"test/generated-artifacts/MetaTransactionsFeature.json",
|
||||||
"test/generated-artifacts/MixinAdapterAddresses.json",
|
"test/generated-artifacts/MixinAdapterAddresses.json",
|
||||||
"test/generated-artifacts/MixinBalancer.json",
|
"test/generated-artifacts/MixinBalancer.json",
|
||||||
"test/generated-artifacts/MixinCurve.json",
|
"test/generated-artifacts/MixinCurve.json",
|
||||||
"test/generated-artifacts/MixinKyber.json",
|
"test/generated-artifacts/MixinKyber.json",
|
||||||
"test/generated-artifacts/MixinMStable.json",
|
"test/generated-artifacts/MixinMStable.json",
|
||||||
|
"test/generated-artifacts/MixinMooniswap.json",
|
||||||
"test/generated-artifacts/MixinOasis.json",
|
"test/generated-artifacts/MixinOasis.json",
|
||||||
"test/generated-artifacts/MixinUniswap.json",
|
"test/generated-artifacts/MixinUniswap.json",
|
||||||
"test/generated-artifacts/MixinUniswapV2.json",
|
"test/generated-artifacts/MixinUniswapV2.json",
|
||||||
"test/generated-artifacts/MixinZeroExBridge.json",
|
"test/generated-artifacts/MixinZeroExBridge.json",
|
||||||
"test/generated-artifacts/Ownable.json",
|
"test/generated-artifacts/OwnableFeature.json",
|
||||||
"test/generated-artifacts/PayTakerTransformer.json",
|
"test/generated-artifacts/PayTakerTransformer.json",
|
||||||
"test/generated-artifacts/SignatureValidator.json",
|
"test/generated-artifacts/SignatureValidatorFeature.json",
|
||||||
"test/generated-artifacts/SimpleFunctionRegistry.json",
|
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||||
"test/generated-artifacts/TestCallTarget.json",
|
"test/generated-artifacts/TestCallTarget.json",
|
||||||
"test/generated-artifacts/TestDelegateCaller.json",
|
"test/generated-artifacts/TestDelegateCaller.json",
|
||||||
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",
|
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",
|
||||||
@ -108,8 +113,8 @@
|
|||||||
"test/generated-artifacts/TestWeth.json",
|
"test/generated-artifacts/TestWeth.json",
|
||||||
"test/generated-artifacts/TestWethTransformerHost.json",
|
"test/generated-artifacts/TestWethTransformerHost.json",
|
||||||
"test/generated-artifacts/TestZeroExFeature.json",
|
"test/generated-artifacts/TestZeroExFeature.json",
|
||||||
"test/generated-artifacts/TokenSpender.json",
|
"test/generated-artifacts/TokenSpenderFeature.json",
|
||||||
"test/generated-artifacts/TransformERC20.json",
|
"test/generated-artifacts/TransformERC20Feature.json",
|
||||||
"test/generated-artifacts/Transformer.json",
|
"test/generated-artifacts/Transformer.json",
|
||||||
"test/generated-artifacts/TransformerDeployer.json",
|
"test/generated-artifacts/TransformerDeployer.json",
|
||||||
"test/generated-artifacts/WethTransformer.json",
|
"test/generated-artifacts/WethTransformer.json",
|
||||||
|
@ -85,6 +85,22 @@
|
|||||||
{
|
{
|
||||||
"note": "Enable Quote Report to be generated with an option `shouldGenerateQuoteReport`. Default is `false`",
|
"note": "Enable Quote Report to be generated with an option `shouldGenerateQuoteReport`. Default is `false`",
|
||||||
"pr": 2687
|
"pr": 2687
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `refundReceiver` to `ExchangeProxySwapQuoteConsumer` options.",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Use `IZeroExContract` in EP swap quote consumer.",
|
||||||
|
"pr": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Set `rfqtTakerAddress` to null in EP consumer",
|
||||||
|
"pr": 2692
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Return Mooniswap pool in sampler and encode it in bridge data",
|
||||||
|
"pr": 2692
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -46,50 +46,69 @@ contract MooniswapSampler is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (uint256[] memory makerTokenAmounts)
|
returns (IMooniswap pool, uint256[] memory makerTokenAmounts)
|
||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
address _takerToken = takerToken == _getWethAddress() ? address(0) : takerToken;
|
address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken;
|
||||||
address _makerToken = makerToken == _getWethAddress() ? address(0) : makerToken;
|
address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken;
|
||||||
// Find the pool for the pair, ETH is represented
|
|
||||||
// as address(0)
|
|
||||||
IMooniswap pool = IMooniswap(
|
|
||||||
IMooniswapRegistry(_getMooniswapAddress()).pools(_takerToken, _makerToken)
|
|
||||||
);
|
|
||||||
// If there is no pool then return early
|
|
||||||
if (address(pool) == address(0)) {
|
|
||||||
return makerTokenAmounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 poolBalance = _takerToken == address(0) ? address(pool).balance : IERC20Token(_takerToken).balanceOf(address(pool));
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
// If the pool balance is smaller than the sell amount
|
uint256 buyAmount = sampleSingleSellFromMooniswapPool(
|
||||||
// don't sample to avoid multiplication overflow in buys
|
mooniswapTakerToken,
|
||||||
if (poolBalance < takerTokenAmounts[i]) {
|
mooniswapMakerToken,
|
||||||
break;
|
|
||||||
}
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
address(pool).staticcall.gas(MOONISWAP_CALL_GAS)(
|
|
||||||
abi.encodeWithSelector(
|
|
||||||
IMooniswap(0).getReturn.selector,
|
|
||||||
_takerToken,
|
|
||||||
_makerToken,
|
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
));
|
);
|
||||||
uint256 buyAmount = 0;
|
|
||||||
if (didSucceed) {
|
|
||||||
buyAmount = abi.decode(resultData, (uint256));
|
|
||||||
}
|
|
||||||
// Exit early if the amount is too high for the source to serve
|
// Exit early if the amount is too high for the source to serve
|
||||||
if (buyAmount == 0) {
|
if (buyAmount == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] = buyAmount;
|
makerTokenAmounts[i] = buyAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool = IMooniswap(
|
||||||
|
IMooniswapRegistry(_getMooniswapAddress()).pools(mooniswapTakerToken, mooniswapMakerToken)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleSingleSellFromMooniswapPool(
|
||||||
|
address mooniswapTakerToken,
|
||||||
|
address mooniswapMakerToken,
|
||||||
|
uint256 takerTokenAmount
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256 makerTokenAmount)
|
||||||
|
{
|
||||||
|
// Find the pool for the pair.
|
||||||
|
IMooniswap pool = IMooniswap(
|
||||||
|
IMooniswapRegistry(_getMooniswapAddress()).pools(mooniswapTakerToken, mooniswapMakerToken)
|
||||||
|
);
|
||||||
|
// If there is no pool then return early
|
||||||
|
if (address(pool) == address(0)) {
|
||||||
|
return makerTokenAmount;
|
||||||
|
}
|
||||||
|
uint256 poolBalance = mooniswapTakerToken == address(0)
|
||||||
|
? address(pool).balance
|
||||||
|
: IERC20Token(mooniswapTakerToken).balanceOf(address(pool));
|
||||||
|
// If the pool balance is smaller than the sell amount
|
||||||
|
// don't sample to avoid multiplication overflow in buys
|
||||||
|
if (poolBalance < takerTokenAmount) {
|
||||||
|
return makerTokenAmount;
|
||||||
|
}
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
address(pool).staticcall.gas(MOONISWAP_CALL_GAS)(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
pool.getReturn.selector,
|
||||||
|
mooniswapTakerToken,
|
||||||
|
mooniswapMakerToken,
|
||||||
|
takerTokenAmount
|
||||||
|
));
|
||||||
|
if (didSucceed) {
|
||||||
|
makerTokenAmount = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Mooniswap.
|
/// @dev Sample buy quotes from Mooniswap.
|
||||||
@ -105,16 +124,27 @@ contract MooniswapSampler is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
returns (IMooniswap pool, uint256[] memory takerTokenAmounts)
|
||||||
{
|
{
|
||||||
return _sampleApproximateBuys(
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
address mooniswapTakerToken = takerToken == _getWethAddress() ? address(0) : takerToken;
|
||||||
|
address mooniswapMakerToken = makerToken == _getWethAddress() ? address(0) : makerToken;
|
||||||
|
|
||||||
|
takerTokenAmounts = _sampleApproximateBuys(
|
||||||
ApproximateBuyQuoteOpts({
|
ApproximateBuyQuoteOpts({
|
||||||
makerTokenData: abi.encode(makerToken),
|
makerTokenData: abi.encode(mooniswapMakerToken),
|
||||||
takerTokenData: abi.encode(takerToken),
|
takerTokenData: abi.encode(mooniswapTakerToken),
|
||||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
|
||||||
}),
|
}),
|
||||||
makerTokenAmounts
|
makerTokenAmounts
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pool = IMooniswap(
|
||||||
|
IMooniswapRegistry(_getMooniswapAddress()).pools(mooniswapTakerToken, mooniswapMakerToken)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sampleSellForApproximateBuyFromMooniswap(
|
function _sampleSellForApproximateBuyFromMooniswap(
|
||||||
@ -126,21 +156,12 @@ contract MooniswapSampler is
|
|||||||
view
|
view
|
||||||
returns (uint256 buyAmount)
|
returns (uint256 buyAmount)
|
||||||
{
|
{
|
||||||
(address takerToken) =
|
address mooniswapTakerToken = abi.decode(takerTokenData, (address));
|
||||||
abi.decode(takerTokenData, (address));
|
address mooniswapMakerToken = abi.decode(makerTokenData, (address));
|
||||||
(address makerToken) =
|
return sampleSingleSellFromMooniswapPool(
|
||||||
abi.decode(makerTokenData, (address));
|
mooniswapTakerToken,
|
||||||
(bool success, bytes memory resultData) =
|
mooniswapMakerToken,
|
||||||
address(this).staticcall(abi.encodeWithSelector(
|
sellAmount
|
||||||
this.sampleSellsFromMooniswap.selector,
|
);
|
||||||
takerToken,
|
|
||||||
makerToken,
|
|
||||||
_toSingleValueArray(sellAmount)
|
|
||||||
));
|
|
||||||
if (!success) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// solhint-disable-next-line indent
|
|
||||||
return abi.decode(resultData, (uint256[]))[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts
|
|||||||
buyTokenFeeAmount: ZERO_AMOUNT,
|
buyTokenFeeAmount: ZERO_AMOUNT,
|
||||||
sellTokenFeeAmount: ZERO_AMOUNT,
|
sellTokenFeeAmount: ZERO_AMOUNT,
|
||||||
},
|
},
|
||||||
|
refundReceiver: NULL_ADDRESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;
|
||||||
|
@ -89,6 +89,7 @@ export {
|
|||||||
AffiliateFee,
|
AffiliateFee,
|
||||||
CalldataInfo,
|
CalldataInfo,
|
||||||
ExchangeProxyContractOpts,
|
ExchangeProxyContractOpts,
|
||||||
|
ExchangeProxyRefundReceiver,
|
||||||
ExtensionContractType,
|
ExtensionContractType,
|
||||||
ForwarderExtensionContractOpts,
|
ForwarderExtensionContractOpts,
|
||||||
GetExtensionContractTypeOpts,
|
GetExtensionContractTypeOpts,
|
||||||
@ -139,6 +140,7 @@ export {
|
|||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
MarketDepth,
|
MarketDepth,
|
||||||
MarketDepthSide,
|
MarketDepthSide,
|
||||||
|
MooniswapFillData,
|
||||||
MultiBridgeFillData,
|
MultiBridgeFillData,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
NativeCollapsedFill,
|
NativeCollapsedFill,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ContractAddresses } from '@0x/contract-addresses';
|
import { ContractAddresses } from '@0x/contract-addresses';
|
||||||
import { ITransformERC20Contract } from '@0x/contract-wrappers';
|
import { IZeroExContract } from '@0x/contract-wrappers';
|
||||||
import {
|
import {
|
||||||
encodeAffiliateFeeTransformerData,
|
encodeAffiliateFeeTransformerData,
|
||||||
encodeFillQuoteTransformerData,
|
encodeFillQuoteTransformerData,
|
||||||
@ -7,10 +7,10 @@ import {
|
|||||||
encodeWethTransformerData,
|
encodeWethTransformerData,
|
||||||
ETH_TOKEN_ADDRESS,
|
ETH_TOKEN_ADDRESS,
|
||||||
FillQuoteTransformerSide,
|
FillQuoteTransformerSide,
|
||||||
|
findTransformerNonce,
|
||||||
} from '@0x/order-utils';
|
} from '@0x/order-utils';
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
|
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
|
||||||
import * as ethjs from 'ethereumjs-util';
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
@ -32,7 +32,6 @@ import { getTokenFromAssetData } from '../utils/utils';
|
|||||||
// tslint:disable-next-line:custom-no-magic-numbers
|
// tslint:disable-next-line:custom-no-magic-numbers
|
||||||
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
|
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
|
||||||
const { NULL_ADDRESS } = constants;
|
const { NULL_ADDRESS } = constants;
|
||||||
const MAX_NONCE_GUESSES = 2048;
|
|
||||||
|
|
||||||
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||||
public readonly provider: ZeroExProvider;
|
public readonly provider: ZeroExProvider;
|
||||||
@ -44,7 +43,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
affiliateFeeTransformer: number;
|
affiliateFeeTransformer: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _transformFeature: ITransformERC20Contract;
|
private readonly _exchangeProxy: IZeroExContract;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
@ -57,7 +56,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.chainId = chainId;
|
this.chainId = chainId;
|
||||||
this.contractAddresses = contractAddresses;
|
this.contractAddresses = contractAddresses;
|
||||||
this._transformFeature = new ITransformERC20Contract(contractAddresses.exchangeProxy, supportedProvider);
|
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, supportedProvider);
|
||||||
this.transformerNonces = {
|
this.transformerNonces = {
|
||||||
wethTransformer: findTransformerNonce(
|
wethTransformer: findTransformerNonce(
|
||||||
contractAddresses.transformers.wethTransformer,
|
contractAddresses.transformers.wethTransformer,
|
||||||
@ -84,7 +83,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
): Promise<CalldataInfo> {
|
): Promise<CalldataInfo> {
|
||||||
assert.isValidSwapQuote('quote', quote);
|
assert.isValidSwapQuote('quote', quote);
|
||||||
// tslint:disable-next-line:no-object-literal-type-assertion
|
// tslint:disable-next-line:no-object-literal-type-assertion
|
||||||
const { affiliateFee, isFromETH, isToETH } = {
|
const { refundReceiver, affiliateFee, isFromETH, isToETH } = {
|
||||||
...constants.DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
...constants.DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
||||||
...opts.extensionContractOpts,
|
...opts.extensionContractOpts,
|
||||||
} as ExchangeProxyContractOpts;
|
} as ExchangeProxyContractOpts;
|
||||||
@ -116,8 +115,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
sellToken,
|
sellToken,
|
||||||
buyToken: intermediateToken,
|
buyToken: intermediateToken,
|
||||||
side: FillQuoteTransformerSide.Sell,
|
side: FillQuoteTransformerSide.Sell,
|
||||||
|
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||||
fillAmount: firstHopOrder.takerAssetAmount,
|
fillAmount: firstHopOrder.takerAssetAmount,
|
||||||
maxOrderFillAmounts: [],
|
maxOrderFillAmounts: [],
|
||||||
|
rfqtTakerAddress: NULL_ADDRESS,
|
||||||
orders: [firstHopOrder],
|
orders: [firstHopOrder],
|
||||||
signatures: [firstHopOrder.signature],
|
signatures: [firstHopOrder.signature],
|
||||||
}),
|
}),
|
||||||
@ -125,11 +126,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
transforms.push({
|
transforms.push({
|
||||||
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
|
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
|
||||||
data: encodeFillQuoteTransformerData({
|
data: encodeFillQuoteTransformerData({
|
||||||
sellToken: intermediateToken,
|
|
||||||
buyToken,
|
buyToken,
|
||||||
|
sellToken: intermediateToken,
|
||||||
|
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||||
side: FillQuoteTransformerSide.Sell,
|
side: FillQuoteTransformerSide.Sell,
|
||||||
fillAmount: MAX_UINT256,
|
fillAmount: MAX_UINT256,
|
||||||
maxOrderFillAmounts: [],
|
maxOrderFillAmounts: [],
|
||||||
|
rfqtTakerAddress: NULL_ADDRESS,
|
||||||
orders: [secondHopOrder],
|
orders: [secondHopOrder],
|
||||||
signatures: [secondHopOrder.signature],
|
signatures: [secondHopOrder.signature],
|
||||||
}),
|
}),
|
||||||
@ -140,9 +143,11 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
data: encodeFillQuoteTransformerData({
|
data: encodeFillQuoteTransformerData({
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
|
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||||
side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell,
|
side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell,
|
||||||
fillAmount: isBuyQuote(quote) ? quote.makerAssetFillAmount : quote.takerAssetFillAmount,
|
fillAmount: isBuyQuote(quote) ? quote.makerAssetFillAmount : quote.takerAssetFillAmount,
|
||||||
maxOrderFillAmounts: [],
|
maxOrderFillAmounts: [],
|
||||||
|
rfqtTakerAddress: NULL_ADDRESS,
|
||||||
orders: quote.orders,
|
orders: quote.orders,
|
||||||
signatures: quote.orders.map(o => o.signature),
|
signatures: quote.orders.map(o => o.signature),
|
||||||
}),
|
}),
|
||||||
@ -192,7 +197,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const minBuyAmount = BigNumber.max(0, quote.worstCaseQuoteInfo.makerAssetAmount.minus(buyTokenFeeAmount));
|
const minBuyAmount = BigNumber.max(0, quote.worstCaseQuoteInfo.makerAssetAmount.minus(buyTokenFeeAmount));
|
||||||
const calldataHexString = this._transformFeature
|
const calldataHexString = this._exchangeProxy
|
||||||
.transformERC20(
|
.transformERC20(
|
||||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
||||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||||
@ -210,7 +215,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
return {
|
return {
|
||||||
calldataHexString,
|
calldataHexString,
|
||||||
ethAmount,
|
ethAmount,
|
||||||
toAddress: this._transformFeature.address,
|
toAddress: this._exchangeProxy.address,
|
||||||
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
|
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -227,32 +232,3 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote {
|
function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote {
|
||||||
return quote.type === MarketOperation.Buy;
|
return quote.type === MarketOperation.Buy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the nonce for a transformer given its deployer.
|
|
||||||
* If `deployer` is the null address, zero will always be returned.
|
|
||||||
*/
|
|
||||||
export function findTransformerNonce(transformer: string, deployer: string = NULL_ADDRESS): number {
|
|
||||||
if (deployer === NULL_ADDRESS) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const lowercaseTransformer = transformer.toLowerCase();
|
|
||||||
// Try to guess the nonce.
|
|
||||||
for (let nonce = 0; nonce < MAX_NONCE_GUESSES; ++nonce) {
|
|
||||||
const deployedAddress = getTransformerAddress(deployer, nonce);
|
|
||||||
if (deployedAddress === lowercaseTransformer) {
|
|
||||||
return nonce;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(`${deployer} did not deploy ${transformer}!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the deployed address for a transformer given a deployer and nonce.
|
|
||||||
*/
|
|
||||||
export function getTransformerAddress(deployer: string, nonce: number): string {
|
|
||||||
return ethjs.bufferToHex(
|
|
||||||
// tslint:disable-next-line: custom-no-magic-numbers
|
|
||||||
ethjs.rlphash([deployer, nonce] as any).slice(12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -135,15 +135,31 @@ export interface AffiliateFee {
|
|||||||
sellTokenFeeAmount: BigNumber;
|
sellTokenFeeAmount: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically resolved protocol fee refund receiver addresses.
|
||||||
|
*/
|
||||||
|
export enum ExchangeProxyRefundReceiver {
|
||||||
|
// Refund to the taker address.
|
||||||
|
Taker = '0x0000000000000000000000000000000000000001',
|
||||||
|
// Refund to the sender address.
|
||||||
|
Sender = '0x0000000000000000000000000000000000000002',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param isFromETH Whether the input token is ETH.
|
* @param isFromETH Whether the input token is ETH.
|
||||||
* @param isToETH Whether the output token is ETH.
|
* @param isToETH Whether the output token is ETH.
|
||||||
* @param affiliateFee Fee denominated in taker or maker asset to send to specified recipient.
|
* @param affiliateFee Fee denominated in taker or maker asset to send to specified recipient.
|
||||||
|
* @param refundReceiver The receiver of unspent protocol fees.
|
||||||
|
* 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()`).
|
||||||
*/
|
*/
|
||||||
export interface ExchangeProxyContractOpts {
|
export interface ExchangeProxyContractOpts {
|
||||||
isFromETH: boolean;
|
isFromETH: boolean;
|
||||||
isToETH: boolean;
|
isToETH: boolean;
|
||||||
affiliateFee: AffiliateFee;
|
affiliateFee: AffiliateFee;
|
||||||
|
refundReceiver: string | ExchangeProxyRefundReceiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetExtensionContractTypeOpts {
|
export interface GetExtensionContractTypeOpts {
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
Fill,
|
Fill,
|
||||||
KyberFillData,
|
KyberFillData,
|
||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
|
MooniswapFillData,
|
||||||
MultiBridgeFillData,
|
MultiBridgeFillData,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
NativeCollapsedFill,
|
NativeCollapsedFill,
|
||||||
@ -304,6 +305,14 @@ function createBridgeOrder(
|
|||||||
createKyberBridgeData(takerToken, kyberFillData.hint),
|
createKyberBridgeData(takerToken, kyberFillData.hint),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case ERC20BridgeSource.Mooniswap:
|
||||||
|
const mooniswapFillData = (fill as CollapsedFill<MooniswapFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
|
||||||
|
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
|
makerToken,
|
||||||
|
bridgeAddress,
|
||||||
|
createMooniswapBridgeData(takerToken, mooniswapFillData.poolAddress),
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
makerToken,
|
makerToken,
|
||||||
@ -407,6 +416,14 @@ function createKyberBridgeData(fromTokenAddress: string, hint: string): string {
|
|||||||
return encoder.encode({ fromTokenAddress, hint });
|
return encoder.encode({ fromTokenAddress, hint });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createMooniswapBridgeData(takerToken: string, poolAddress: string): string {
|
||||||
|
const encoder = AbiEncoder.create([
|
||||||
|
{ name: 'takerToken', type: 'address' },
|
||||||
|
{ name: 'poolAddress', type: 'address' },
|
||||||
|
]);
|
||||||
|
return encoder.encode({ takerToken, poolAddress });
|
||||||
|
}
|
||||||
|
|
||||||
function createCurveBridgeData(
|
function createCurveBridgeData(
|
||||||
curveAddress: string,
|
curveAddress: string,
|
||||||
exchangeFunctionSelector: string,
|
exchangeFunctionSelector: string,
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
HopInfo,
|
HopInfo,
|
||||||
KyberFillData,
|
KyberFillData,
|
||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
|
MooniswapFillData,
|
||||||
MultiBridgeFillData,
|
MultiBridgeFillData,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
SourceQuoteOperation,
|
SourceQuoteOperation,
|
||||||
@ -446,6 +447,14 @@ export class SamplerOperations {
|
|||||||
contract: this._samplerContract,
|
contract: this._samplerContract,
|
||||||
function: this._samplerContract.sampleSellsFromMooniswap,
|
function: this._samplerContract.sampleSellsFromMooniswap,
|
||||||
params: [takerToken, makerToken, takerFillAmounts],
|
params: [takerToken, makerToken, takerFillAmounts],
|
||||||
|
callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => {
|
||||||
|
const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
|
||||||
|
'sampleSellsFromMooniswap',
|
||||||
|
callResults,
|
||||||
|
);
|
||||||
|
fillData.poolAddress = poolAddress;
|
||||||
|
return samples;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,6 +468,14 @@ export class SamplerOperations {
|
|||||||
contract: this._samplerContract,
|
contract: this._samplerContract,
|
||||||
function: this._samplerContract.sampleBuysFromMooniswap,
|
function: this._samplerContract.sampleBuysFromMooniswap,
|
||||||
params: [takerToken, makerToken, makerFillAmounts],
|
params: [takerToken, makerToken, makerFillAmounts],
|
||||||
|
callback: (callResults: string, fillData: MooniswapFillData): BigNumber[] => {
|
||||||
|
const [poolAddress, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
|
||||||
|
'sampleBuysFromMooniswap',
|
||||||
|
callResults,
|
||||||
|
);
|
||||||
|
fillData.poolAddress = poolAddress;
|
||||||
|
return samples;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,10 @@ export interface KyberFillData extends FillData {
|
|||||||
reserveId: string;
|
reserveId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MooniswapFillData extends FillData {
|
||||||
|
poolAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Quote<TFillData = FillData> {
|
export interface Quote<TFillData = FillData> {
|
||||||
amount: BigNumber;
|
amount: BigNumber;
|
||||||
fillData?: TFillData;
|
fillData?: TFillData;
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
decodeWethTransformerData,
|
decodeWethTransformerData,
|
||||||
ETH_TOKEN_ADDRESS,
|
ETH_TOKEN_ADDRESS,
|
||||||
FillQuoteTransformerSide,
|
FillQuoteTransformerSide,
|
||||||
|
getTransformerAddress,
|
||||||
} from '@0x/order-utils';
|
} from '@0x/order-utils';
|
||||||
import { Order } from '@0x/types';
|
import { Order } from '@0x/types';
|
||||||
import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils';
|
import { AbiEncoder, BigNumber, hexUtils } from '@0x/utils';
|
||||||
@ -16,10 +17,7 @@ import * as _ from 'lodash';
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
import { constants } from '../src/constants';
|
import { constants } from '../src/constants';
|
||||||
import {
|
import { ExchangeProxySwapQuoteConsumer } from '../src/quote_consumers/exchange_proxy_swap_quote_consumer';
|
||||||
ExchangeProxySwapQuoteConsumer,
|
|
||||||
getTransformerAddress,
|
|
||||||
} from '../src/quote_consumers/exchange_proxy_swap_quote_consumer';
|
|
||||||
import { MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types';
|
import { MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types';
|
||||||
import { OptimizedMarketOrder } from '../src/utils/market_operation_utils/types';
|
import { OptimizedMarketOrder } from '../src/utils/market_operation_utils/types';
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ describe('quote_simulation tests', async () => {
|
|||||||
const DEFAULT_TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN);
|
const DEFAULT_TAKER_ASSET_DATA = assetDataUtils.encodeERC20AssetData(TAKER_TOKEN);
|
||||||
const GAS_SCHEDULE = { [ERC20BridgeSource.Native]: _.constant(1) };
|
const GAS_SCHEDULE = { [ERC20BridgeSource.Native]: _.constant(1) };
|
||||||
|
|
||||||
// Check if two numbers are within `maxError` error rate within each other (default 1 bps).
|
// Check if two numbers are within `maxError` error rate within each other.
|
||||||
function assertRoughlyEquals(n1: BigNumber, n2: BigNumber, maxError: BigNumber | number = 1e-12): void {
|
function assertRoughlyEquals(n1: BigNumber, n2: BigNumber, maxError: BigNumber | number = 1e-10): void {
|
||||||
// |n2-n1| / max(|n1|, |n2|)
|
// |n2-n1| / max(|n1|, |n2|)
|
||||||
const err = n2
|
const err = n2
|
||||||
.minus(n1)
|
.minus(n1)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user