0xProject/0x-monorepo

@0x/contract-wrappers: `marketBuyOrdersFillOrKill` incorrect ABI encoding

andr11111 opened this issue · 2 comments

This issues is applicable to all batch- and market- fill methods.

Expected Behavior

contractWrappers.exchange.marketBuyOrdersFillOrKill should correctly ABI-encode the calldata.

Current Behavior

    const tx = await contractWrappers.exchange
      .marketBuyOrdersFillOrKill([signedOrder1, signedOrder2], new BigNumber(2), [signedOrder1.signature, signedOrder2.signature])
    console.log('getABIEncodedTransactionData', tx.getABIEncodedTransactionData());

The result of the ABI-encoding is 0x8bc8efb3000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000060, which is not correct and missing all of the orders information.

Trying to send such transaction onchain results in an error.

Steps to Reproduce (for bugs)

truffle exec script:

const {
  RPCSubprovider,
  Web3ProviderEngine,
} = require('@0x/subproviders');

const { generatePseudoRandomSalt, Order, orderHashUtils, signatureUtils, SignedOrder, assetDataUtils, orderUtils } = require('@0x/order-utils');
const { ContractWrappers } = require('@0x/contract-wrappers');
const { Web3Wrapper } = require('@0x/web3-wrapper');
const { BigNumber } = require('@0x/utils');

const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';

module.exports = async function() {
  try {
    const provider = web3.currentProvider;

    // Then use the provider
    const chainId = 1337;
    const contractWrappers = new ContractWrappers(provider, { chainId });
    const web3Wrapper = new Web3Wrapper(provider);

    const token1 = { address: '0x1Ff4e2A3330F8a7Eb2de6Cc50f4569cC4fDC77d8' };
    const token2 = { address: '0x973E5AFDDE356C2ae5c061FDD056Fb7E2F919b11' };

    const addresses = await web3Wrapper.getAvailableAddressesAsync();
    const makerAddress = addresses[0];
    const takerAddress = addresses[1];

    const makerAssetData = await contractWrappers.devUtils.encodeERC20AssetData(token1.address).callAsync();
    const takerAssetData = await contractWrappers.devUtils.encodeERC20AssetData(token2.address).callAsync();
    const makerAssetAmount = new BigNumber(1);
    const takerAssetAmount = new BigNumber(1);
    const exchangeAddress = contractWrappers.exchange.address;

    const order1 = {
      makerAddress, // maker is the first address
      takerAddress: NULL_ADDRESS, // taker is open and can be filled by anyone
      makerAssetAmount, // The maker asset amount
      takerAssetAmount, // The taker asset amount
      expirationTimeSeconds: new BigNumber(Math.round(Date.now() / 1000) + 10 * 60), // Time when this order expires
      makerFee: new BigNumber(0), // 0 maker fees
      takerFee: new BigNumber(0), // 0 taker fees
      feeRecipientAddress: NULL_ADDRESS, // No fee recipient
      senderAddress: NULL_ADDRESS, // Sender address is open and can be submitted by anyone
      salt: generatePseudoRandomSalt(), // Random value to provide uniqueness
      makerAssetData,
      takerAssetData,
      exchangeAddress,
      makerFeeAssetData: '0x',
      takerFeeAssetData: '0x',
      chainId,
    };

    const order2 = {
      makerAddress, // maker is the first address
      takerAddress: NULL_ADDRESS, // taker is open and can be filled by anyone
      makerAssetAmount, // The maker asset amount
      takerAssetAmount, // The taker asset amount
      expirationTimeSeconds: new BigNumber(Math.round(Date.now() / 1000) + 10 * 60), // Time when this order expires
      makerFee: new BigNumber(0), // 0 maker fees
      takerFee: new BigNumber(0), // 0 taker fees
      feeRecipientAddress: NULL_ADDRESS, // No fee recipient
      senderAddress: NULL_ADDRESS, // Sender address is open and can be submitted by anyone
      salt: generatePseudoRandomSalt(), // Random value to provide uniqueness
      makerAssetData,
      takerAssetData,
      exchangeAddress,
      makerFeeAssetData: '0x',
      takerFeeAssetData: '0x',
      chainId,
    };

    const signedOrder1 = await signatureUtils.ecSignOrderAsync(provider, order1, makerAddress);
    const signedOrder2 = await signatureUtils.ecSignOrderAsync(provider, order2, makerAddress);

    const tx = await contractWrappers.exchange
      .marketBuyOrdersFillOrKill([signedOrder1, signedOrder2], new BigNumber(2), [signedOrder1.signature, signedOrder2.signature])
    console.log('getABIEncodedTransactionData', tx.getABIEncodedTransactionData());
  } catch(e) {
    console.log(e);
  }

  process.exit();
}

Context

I can successfully call the exchange methods directly using web3:

    const plainOrder1 = JSON.parse(JSON.stringify(signedOrder1));
    const plainOrder2 = JSON.parse(JSON.stringify(signedOrder2));
    const exchange = await IExchange.at(contractWrappers.exchange.address);
    const gas = await exchange.marketBuyOrdersFillOrKill.estimateGas([plainOrder1, plainOrder2], 1, [plainOrder1.signature, plainOrder2.signature],
      { from: takerAddress, value: 7000000000000000 });
    console.log('estimated gas', gas);
    const result = await exchange.marketBuyOrdersFillOrKill([plainOrder1, plainOrder2], 2, [plainOrder1.signature, plainOrder2.signature],
      { from: takerAddress, gas: 6700000, value: 7000000000000000 });

Your Environment

0x dependencies versions:

    "@0x/contract-wrappers": "^13.6.3",
    "@0x/contracts-erc20": "^3.1.5",
    "@0x/contracts-test-utils": "^5.3.2",
    "@0x/order-utils": "^10.2.4",
    "@0x/subproviders": "^6.0.8",
    "@0x/utils": "^5.4.1",
    "@0x/web3-wrapper": "^7.0.7",
Network
1337

Using 0x Ganache Docker image https://hub.docker.com/r/0xorg/ganache-cli

hysz commented

Hi @andrei-anisimov, thanks for raising this. I'm having trouble reproducing the bug. The only thing that looks "off" is this line, which doesn't need an await.

const tx = await contractWrappers.exchange
      .marketBuyOrdersFillOrKill([signedOrder1, signedOrder2], new BigNumber(2), [signedOrder1.signature, signedOrder2.signature])

Here's my output from running the code snippet:
0x8bc8efb30000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000005200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a6310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ea38429929d1ae9a6bed3a65a231d8e1b1f82df3a10ece926f66050f00210668df3f2e4000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e0000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000004400000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a6310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ea38429ca06e4dc6442d40fa60faf0a6f19fa415635ae6182bdb5cb83aab1019a21e9ec00000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001ff4e2a3330f8a7eb2de6cc50f4569cc4fdc77d8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000973e5afdde356c2ae5c061fdd056fb7e2f919b110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000421babea28cddd87f8be08d75d223c50a5eb36ac957ae6f426aa1847ef95ddb0c2952e027ecc1e50ab7c00afedc5bc03d04eaf9821d77ca12ce459534607f6892e1a0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421c49e33ff3981f7ec8bc56d394ad9ac98980e6b2bfdf408eb5b434fbc47898d38301727a9d524a572118b0f80ef30e08d7cb5653c9321b591962cf6c339d436dc302000000000000000000000000000000000000000000000000000000000000

I uploaded the repro attempt here, which can be run using yarn && yarn build && yarn test.

Yes, super weird, I can still consistently reproduce it with your script above. You can find the repo here: https://github.com/andrei-anisimov/0x-market-poc. To run to npm install and truffle exec 0x-test.js. The output I'm getting:

 **********
 0x8bc8efb3000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000060 
**********