Files
protocol/contracts/integrations/test/forwarder/forwarder_mainnet_test.ts
Lawrence Forman 901d400d62 Address spot check feedback (#251)
* UniswapV3 VIP (#237)

* `@0x/contracts-zero-ex`: Add UniswapV3Feature

* `@0x/contracts-zero-ex`: Add UniswapV3 VIP
`@0x/contract-artifacts`: Regenerate.
`@0x/contract-wrappers`: Regenerate.
`@0x/asset-swapper`: Add UniswapV3 VIP support.

* address review comments and appease linter

* `@0x/contracts-zero-ex`: Add UniswapV3Feature tests

* Multiplex UniswapV3 (#241)

* Add UniswapV3 support to Multiplex batchFill

* Add AssetSwapper support for Multiplex UniswapV3

* fix repo scripts that use PKG= env var (#242)

Co-authored-by: Lawrence Forman <me@merklejerk.com>

* `@0x/asset-swapper`: Adjust uniswap gas overhead

Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

* OTC orders feature (#244)

* Add OTC orders feature contracts

* Address PR feedback

* Remove partial fills for takerSigned variant

* Add function to query the min valid nonce

* Add ETH support

* Tightly pack expiry, nonceBucket, and nonce

* Address PR feedback

* OTC orders unit tests

* Bump prettier version

* Skip unnecessary math if takerTokenFillAmount == order.takerAmount

* appease CI

* Update contract-artifacts and contract-wrappers and CHANGELOGs

* `@0x/contracts-zero-ex`: Address spot check feedback

* `regen wrappers

* prettier

* `@0x/asset-swapper`: prettier and tweak gas schedule slightly for uni3

Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>
2021-06-02 14:21:14 +10:00

213 lines
9.7 KiB
TypeScript

import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { ForwarderContract, IExchangeV2Contract } from '@0x/contracts-exchange-forwarder';
import {
blockchainTests,
constants,
expect,
getLatestBlockTimestampAsync,
orderHashUtils,
signingUtils,
} from '@0x/contracts-test-utils';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { Order, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as ethUtil from 'ethereumjs-util';
import { contractAddresses } from '../mainnet_fork_utils';
import { SignedV2Order } from './types';
blockchainTests.fork.resets('Forwarder mainnet tests', env => {
const forwarder = new ForwarderContract(contractAddresses.forwarder, env.provider, env.txDefaults);
const exchangeV2 = new IExchangeV2Contract(contractAddresses.exchangeV2, env.provider, env.txDefaults);
const wethAssetData = assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken);
const v2OrderId = '0x770501f8';
let makerAddress: string;
let takerAddress: string;
let makerAssetData: string;
let makerToken: DummyERC20TokenContract;
let makerPrivateKey: Buffer;
before(async () => {
[makerAddress, takerAddress] = await env.web3Wrapper.getAvailableAddressesAsync();
makerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
env.provider,
env.txDefaults,
erc20Artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
await makerToken.setBalance(makerAddress, constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync();
await makerToken
.approve(contractAddresses.erc20Proxy, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: makerAddress });
makerAssetData = assetDataUtils.encodeERC20AssetData(makerToken.address);
makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[0];
});
async function createOrderAsync(orderParams: Partial<Order> = {}): Promise<SignedOrder | SignedV2Order> {
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
const fifteenMinutesInSeconds = 15 * 60;
const order = {
chainId: 1,
exchangeAddress: contractAddresses.exchange,
makerAddress,
takerAddress: constants.NULL_ADDRESS,
senderAddress: constants.NULL_ADDRESS,
feeRecipientAddress: constants.NULL_ADDRESS,
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(fifteenMinutesInSeconds),
salt: generatePseudoRandomSalt(),
makerAssetData,
takerAssetData: wethAssetData,
makerFeeAssetData: makerAssetData,
takerFeeAssetData: wethAssetData,
makerAssetAmount: constants.INITIAL_ERC20_BALANCE.dividedToIntegerBy(2),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(0.001, 18),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
...orderParams,
};
const orderHashHex =
order.makerFeeAssetData === v2OrderId
? (await exchangeV2.getOrderInfo(order).callAsync()).orderHash
: orderHashUtils.getOrderHashHex(order);
const signature = `0x${signingUtils
.signMessage(ethUtil.toBuffer(orderHashHex), makerPrivateKey, SignatureType.EthSign)
.toString('hex')}`;
return {
...order,
signature,
};
}
describe('marketSellOrdersWithEth', () => {
it('should fill a single v2 order with no fees', async () => {
const order = await createOrderAsync({ makerFeeAssetData: v2OrderId });
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketSellOrdersWithEth([order], [order.signature], [], [])
.callAsync({
from: takerAddress,
value: order.takerAssetAmount,
});
expect(wethSpentAmount).to.bignumber.eq(order.takerAssetAmount);
expect(makerAssetAcquiredAmount).to.bignumber.eq(order.makerAssetAmount);
});
it('should fill multiple v2 orders', async () => {
const orders = [
await createOrderAsync({ makerFeeAssetData: v2OrderId }),
await createOrderAsync({ makerFeeAssetData: v2OrderId }),
];
const ethSellAmount = BigNumber.sum(
orders[0].takerAssetAmount,
orders[1].takerAssetAmount.dividedToIntegerBy(2),
);
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketSellOrdersWithEth(
orders,
orders.map(o => o.signature),
[],
[],
)
.callAsync({
from: takerAddress,
value: ethSellAmount,
});
expect(wethSpentAmount).to.bignumber.eq(ethSellAmount);
expect(makerAssetAcquiredAmount).to.bignumber.eq(
BigNumber.sum(orders[0].makerAssetAmount, orders[1].makerAssetAmount.dividedToIntegerBy(2)),
);
});
it.skip('should fill multiple v2/v3 orders', async () => {
const v2Order = await createOrderAsync({ makerFeeAssetData: v2OrderId });
const v3Order = await createOrderAsync();
const protocolFee = new BigNumber(150000).times(constants.DEFAULT_GAS_PRICE);
const ethSellAmount = BigNumber.sum(
v2Order.takerAssetAmount,
v3Order.takerAssetAmount.dividedToIntegerBy(2),
);
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketSellOrdersWithEth([v2Order, v3Order], [v2Order.signature, v3Order.signature], [], [])
.callAsync({
from: takerAddress,
value: ethSellAmount.plus(protocolFee),
gasPrice: constants.DEFAULT_GAS_PRICE,
});
expect(wethSpentAmount).to.bignumber.eq(ethSellAmount.plus(protocolFee));
expect(makerAssetAcquiredAmount).to.bignumber.eq(
BigNumber.sum(v2Order.makerAssetAmount, v3Order.makerAssetAmount.dividedToIntegerBy(2)),
);
});
});
describe('marketBuyOrdersWithEth', () => {
it('should fill a single v2 order', async () => {
const order = await createOrderAsync({ makerFeeAssetData: v2OrderId });
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketBuyOrdersWithEth([order], order.makerAssetAmount, [order.signature], [], [])
.callAsync({ from: takerAddress, value: order.takerAssetAmount });
expect(wethSpentAmount).to.bignumber.eq(order.takerAssetAmount);
expect(makerAssetAcquiredAmount).to.bignumber.eq(order.makerAssetAmount);
});
it('should fill multiple v2 orders', async () => {
const orders = [
await createOrderAsync({ makerFeeAssetData: v2OrderId }),
await createOrderAsync({ makerFeeAssetData: v2OrderId }),
];
const ethSellAmount = BigNumber.sum(
orders[0].takerAssetAmount,
orders[1].takerAssetAmount.dividedToIntegerBy(2),
);
const makerAssetBuyAmount = BigNumber.sum(
orders[0].makerAssetAmount,
orders[1].makerAssetAmount.dividedToIntegerBy(2),
);
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketBuyOrdersWithEth(
orders,
makerAssetBuyAmount,
orders.map(o => o.signature),
[],
[],
)
.callAsync({
from: takerAddress,
value: ethSellAmount,
});
expect(wethSpentAmount).to.bignumber.eq(ethSellAmount);
expect(makerAssetAcquiredAmount).to.bignumber.eq(makerAssetBuyAmount);
});
it.skip('should fill multiple v2/v3 orders', async () => {
const v2Order = await createOrderAsync({ makerFeeAssetData: v2OrderId });
const v3Order = await createOrderAsync();
const protocolFee = new BigNumber(150000).times(constants.DEFAULT_GAS_PRICE);
const ethSellAmount = BigNumber.sum(
v2Order.takerAssetAmount,
v3Order.takerAssetAmount.dividedToIntegerBy(2),
);
const makerAssetBuyAmount = BigNumber.sum(
v2Order.makerAssetAmount,
v3Order.makerAssetAmount.dividedToIntegerBy(2),
);
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
.marketBuyOrdersWithEth(
[v2Order, v3Order],
makerAssetBuyAmount,
[v2Order.signature, v3Order.signature],
[],
[],
)
.callAsync({
from: takerAddress,
value: ethSellAmount.plus(protocolFee),
gasPrice: constants.DEFAULT_GAS_PRICE,
});
expect(wethSpentAmount).to.bignumber.eq(ethSellAmount.plus(protocolFee));
expect(makerAssetAcquiredAmount).to.bignumber.eq(makerAssetBuyAmount);
});
});
});