Xianny f0d7d10fe7
update abi-gen with new method interfaces (#2325)
* update abi-gen with new method interfaces

* wip: get all packages to build

* wip: get all packages to build

* Fix two contract wrapper calls

* Export necessary types part of the contract wrapper public interfaces

* Revive and fix wrapper_unit_tests

* Remove duplicate type

* Fix lib_exchange_rich_error_decoder tests

* Fix remaining test failures in contracts-* packages

* Prettier fixes

* remove transactionHelper

* lint and update changelogs

* Fix prettier

* Revert changes to reference docs

* Add back changelog already published and add revert changelog entry

* Add missing CHANGELOG entries

* Add missing comma

* Update mesh-rpc-client dep

* Update Mesh RPC logic in @0x/orderbook to v6.0.1-beta

* Align package versions
2019-11-14 11:22:29 -05:00

1623 lines
89 KiB
TypeScript

import { DevUtilsContract } from '@0x/contracts-dev-utils';
import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import {
artifacts as erc20Artifacts,
DummyERC20TokenContract,
DummyERC20TokenTransferEventArgs,
DummyMultipleReturnERC20TokenContract,
DummyNoReturnERC20TokenContract,
} from '@0x/contracts-erc20';
import {
artifacts as erc721Artifacts,
DummyERC721ReceiverContract,
DummyERC721TokenContract,
} from '@0x/contracts-erc721';
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
LogDecoder,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
import { ERC20Wrapper } from '../src/erc20_wrapper';
import { ERC721Wrapper } from '../src/erc721_wrapper';
import { ERC1155ProxyContract, ERC20ProxyContract, ERC721ProxyContract } from '../src/wrappers';
import { artifacts } from './artifacts';
import { IAssetDataContract, IAssetProxyContract, MultiAssetProxyContract } from './wrappers';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const assetProxyInterface = new IAssetProxyContract(constants.NULL_ADDRESS, provider);
const assetDataInterface = new IAssetDataContract(constants.NULL_ADDRESS, provider);
// tslint:disable:no-unnecessary-type-assertion
describe('Asset Transfer Proxies', () => {
let owner: string;
let notAuthorized: string;
let authorized: string;
let fromAddress: string;
let toAddress: string;
let devUtils: DevUtilsContract;
let erc20TokenA: DummyERC20TokenContract;
let erc20TokenB: DummyERC20TokenContract;
let erc721TokenA: DummyERC721TokenContract;
let erc721TokenB: DummyERC721TokenContract;
let erc721Receiver: DummyERC721ReceiverContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
let noReturnErc20Token: DummyNoReturnERC20TokenContract;
let multipleReturnErc20Token: DummyMultipleReturnERC20TokenContract;
let multiAssetProxy: MultiAssetProxyContract;
let erc20Wrapper: ERC20Wrapper;
let erc721Wrapper: ERC721Wrapper;
let erc721AFromTokenId: BigNumber;
let erc721BFromTokenId: BigNumber;
let erc1155Proxy: ERC1155ProxyContract;
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
let erc1155Contract: ERC1155MintableContract;
let erc1155Contract2: ERC1155MintableContract;
let erc1155Wrapper: Erc1155Wrapper;
let erc1155Wrapper2: Erc1155Wrapper;
let erc1155FungibleTokens: BigNumber[];
let erc1155NonFungibleTokensOwnedBySpender: BigNumber[];
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5));
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
// Deploy AssetProxies
erc20Proxy = await erc20Wrapper.deployProxyAsync();
erc721Proxy = await erc721Wrapper.deployProxyAsync();
multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
artifacts.MultiAssetProxy,
provider,
txDefaults,
artifacts,
);
// Configure ERC20Proxy
await erc20Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
await erc20Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync({ from: owner });
// Configure ERC721Proxy
await erc721Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
await erc721Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync({ from: owner });
// Configure ERC115Proxy
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
await erc1155Proxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync({ from: owner });
// Configure MultiAssetProxy
await multiAssetProxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
await multiAssetProxy.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({ from: owner });
await multiAssetProxy.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync({ from: owner });
await multiAssetProxy.registerAssetProxy(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner });
// Deploy and configure ERC20 tokens
const numDummyErc20ToDeploy = 2;
[erc20TokenA, erc20TokenB] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
);
noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyNoReturnERC20Token,
provider,
txDefaults,
artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyMultipleReturnERC20Token,
provider,
txDefaults,
artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
await erc20Wrapper.setBalancesAndAllowancesAsync();
await noReturnErc20Token.setBalance(fromAddress, constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({
from: owner,
});
await noReturnErc20Token
.approve(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: fromAddress });
await multipleReturnErc20Token
.setBalance(fromAddress, constants.INITIAL_ERC20_BALANCE)
.awaitTransactionSuccessAsync({
from: owner,
});
await multipleReturnErc20Token
.approve(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: fromAddress });
// Deploy and configure ERC721 tokens and receiver
[erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
erc721Artifacts.DummyERC721Receiver,
provider,
txDefaults,
artifacts,
);
await erc721Wrapper.setBalancesAndAllowancesAsync();
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
// Deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper, erc1155Wrapper2] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract();
erc1155Contract2 = erc1155Wrapper2.getContract();
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
erc1155NonFungibleTokensOwnedBySpender = [];
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
const nonFungibleTokenAsString = nonFungibleToken.toString();
const nonFungibleTokenHeldBySpender =
tokenBalances.nonFungible[fromAddress][erc1155Contract.address][nonFungibleTokenAsString][0];
erc1155NonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
});
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('ERC20Proxy', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc20Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0xf47261b0', async () => {
const proxyId = await erc20Proxy.getProxyId().callAsync();
const expectedProxyId = '0xf47261b0';
expect(proxyId).to.equal(expectedProxyId);
});
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(amount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(amount),
);
});
it('should successfully transfer tokens that do not return a value', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(noReturnErc20Token.address).callAsync();
// Perform a transfer from fromAddress to toAddress
const initialFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync();
const initialToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync();
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync();
const newToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync();
expect(newFromBalance).to.be.bignumber.equal(initialFromBalance.minus(amount));
expect(newToBalance).to.be.bignumber.equal(initialToBalance.plus(amount));
});
it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC20 asset data
const extraData = '0102030405060708';
const encodedAssetData = `${await devUtils
.encodeERC20AssetData(erc20TokenA.address)
.callAsync()}${extraData}`;
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(amount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(amount),
);
});
it('should do nothing if transferring 0 amount of a token', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
// Perform a transfer from fromAddress to toAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address],
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address],
);
});
it('should revert if allowances are too low', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await erc20TokenA.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
from: fromAddress,
});
const erc20Balances = await erc20Wrapper.getBalancesAsync();
// Perform a transfer; expect this to fail.
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
RevertReason.TransferFailed,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.deep.equal(erc20Balances);
});
it('should revert if allowances are too low and token does not return a value', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(noReturnErc20Token.address).callAsync();
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await noReturnErc20Token.approve(erc20Proxy.address, allowance).awaitTransactionSuccessAsync({
from: fromAddress,
});
const initialFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync();
const initialToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync();
// Perform a transfer; expect this to fail.
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
RevertReason.TransferFailed,
);
const newFromBalance = await noReturnErc20Token.balanceOf(fromAddress).callAsync();
const newToBalance = await noReturnErc20Token.balanceOf(toAddress).callAsync();
expect(newFromBalance).to.be.bignumber.equal(initialFromBalance);
expect(newToBalance).to.be.bignumber.equal(initialToBalance);
});
it('should revert if caller is not authorized', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: notAuthorized,
}),
RevertReason.SenderNotAuthorized,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.deep.equal(erc20Balances);
});
it('should revert if token returns more than 32 bytes', async () => {
// Construct ERC20 asset data
const encodedAssetData = await devUtils
.encodeERC20AssetData(multipleReturnErc20Token.address)
.callAsync();
const amount = new BigNumber(10);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
const initialFromBalance = await multipleReturnErc20Token.balanceOf(fromAddress).callAsync();
const initialToBalance = await multipleReturnErc20Token.balanceOf(toAddress).callAsync();
// Perform a transfer; expect this to fail.
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc20Proxy.address,
data,
from: authorized,
}),
RevertReason.TransferFailed,
);
const newFromBalance = await multipleReturnErc20Token.balanceOf(fromAddress).callAsync();
const newToBalance = await multipleReturnErc20Token.balanceOf(toAddress).callAsync();
expect(newFromBalance).to.be.bignumber.equal(initialFromBalance);
expect(newToBalance).to.be.bignumber.equal(initialToBalance);
});
});
});
describe('ERC721Proxy', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc721Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0x02571792', async () => {
const proxyId = await erc721Proxy.getProxyId().callAsync();
const expectedProxyId = '0x02571792';
expect(proxyId).to.equal(expectedProxyId);
});
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(1);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress);
});
it('should successfully transfer tokens and ignore extra assetData', async () => {
// Construct ERC721 asset data
const extraData = '0102030405060708';
const encodedAssetData = `${await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync()}${extraData}`;
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(1);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Verify transfer was successful
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress);
});
it('should not call onERC721Received when transferring to a smart contract', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(1);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, erc721Receiver.address, amount)
.getABIEncodedTransactionData();
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc721Artifacts });
const tx = await logDecoder.getTxWithDecodedLogsAsync(
await web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
gas: constants.MAX_TRANSFER_FROM_GAS,
}),
);
// Verify that no log was emitted by erc721 receiver
expect(tx.logs.length).to.be.equal(1);
// Verify transfer was successful
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.bignumber.equal(erc721Receiver.address);
});
it('should revert if transferring 0 amount of a token', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(0);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
}),
RevertReason.InvalidAmount,
);
const newOwner = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwner).to.be.equal(ownerFromAsset);
});
it('should revert if transferring > 1 amount of a token', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(500);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
}),
RevertReason.InvalidAmount,
);
const newOwner = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwner).to.be.equal(ownerFromAsset);
});
it('should revert if allowances are too low', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Remove blanket transfer approval for fromAddress.
await erc721TokenA.setApprovalForAll(erc721Proxy.address, false).awaitTransactionSuccessAsync({
from: fromAddress,
});
// Remove token transfer approval for fromAddress.
await erc721TokenA.approve(constants.NULL_ADDRESS, erc721AFromTokenId).awaitTransactionSuccessAsync({
from: fromAddress,
});
// Perform a transfer; expect this to fail.
const amount = new BigNumber(1);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: authorized,
}),
RevertReason.TransferFailed,
);
const newOwner = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwner).to.be.equal(ownerFromAsset);
});
it('should revert if caller is not authorized', async () => {
// Construct ERC721 asset data
const encodedAssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
// Verify pre-condition
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
// Perform a transfer from fromAddress to toAddress
const amount = new BigNumber(1);
const data = assetProxyInterface
.transferFrom(encodedAssetData, fromAddress, toAddress, amount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
data,
from: notAuthorized,
}),
RevertReason.SenderNotAuthorized,
);
const newOwner = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwner).to.be.equal(ownerFromAsset);
});
});
});
describe('MultiAssetProxy', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: multiAssetProxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0x94cfcdd7', async () => {
const proxyId = await multiAssetProxy.getProxyId().callAsync();
// first 4 bytes of `keccak256('MultiAsset(uint256[],bytes[])')`
const expectedProxyId = '0x94cfcdd7';
expect(proxyId).to.equal(expectedProxyId);
});
describe('transferFrom', () => {
it('should transfer a single ERC20 token', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalAmount = inputAmount.times(erc20Amount);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
);
});
it('should dispatch an ERC20 transfer when input amount is 0', async () => {
const inputAmount = constants.ZERO_AMOUNT;
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = assetDataInterface
.MultiAsset(amounts, nestedAssetData)
.getABIEncodedTransactionData();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...erc20Artifacts });
const tx = await logDecoder.getTxWithDecodedLogsAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
);
expect(tx.logs.length).to.be.equal(1);
const log = tx.logs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>;
const transferEventName = 'Transfer';
expect(log.event).to.equal(transferEventName);
expect(log.args._value).to.be.bignumber.equal(constants.ZERO_AMOUNT);
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.deep.equal(erc20Balances);
});
it('should successfully transfer multiple of the same ERC20 token', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalAmount = inputAmount.times(erc20Amount1).plus(inputAmount.times(erc20Amount2));
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
);
});
it('should successfully transfer multiple different ERC20 tokens', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync();
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalErc20AAmount = inputAmount.times(erc20Amount1);
const totalErc20BAmount = inputAmount.times(erc20Amount2);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalErc20AAmount),
);
expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
);
expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenB.address].plus(totalErc20BAmount),
);
});
it('should transfer a single ERC721 token', async () => {
const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc721Amount];
const nestedAssetData = [erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.equal(toAddress);
});
it('should successfully transfer multiple of the same ERC721 token', async () => {
const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721AssetData1 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const erc721AssetData2 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2)
.callAsync();
const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const ownerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset1).to.be.equal(fromAddress);
const ownerFromAsset2 = await erc721TokenA.ownerOf(erc721AFromTokenId2).callAsync();
expect(ownerFromAsset2).to.be.equal(fromAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
gas: constants.MAX_TRANSFER_FROM_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newOwnerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
const newOwnerFromAsset2 = await erc721TokenA.ownerOf(erc721AFromTokenId2).callAsync();
expect(newOwnerFromAsset1).to.be.equal(toAddress);
expect(newOwnerFromAsset2).to.be.equal(toAddress);
});
it('should successfully transfer multiple different ERC721 tokens', async () => {
const erc721AssetData1 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const erc721AssetData2 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId)
.callAsync();
const inputAmount = new BigNumber(1);
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const ownerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset1).to.be.equal(fromAddress);
const ownerFromAsset2 = await erc721TokenB.ownerOf(erc721BFromTokenId).callAsync();
expect(ownerFromAsset2).to.be.equal(fromAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
gas: constants.MAX_TRANSFER_FROM_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newOwnerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
const newOwnerFromAsset2 = await erc721TokenB.ownerOf(erc721BFromTokenId).callAsync();
expect(newOwnerFromAsset1).to.be.equal(toAddress);
expect(newOwnerFromAsset2).to.be.equal(toAddress);
});
it('should transfer a fungible ERC1155 token', async () => {
// setup test parameters
const tokenHolders = [fromAddress, toAddress];
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const valuesToTransfer = [new BigNumber(25)];
const valueMultiplier = new BigNumber(23);
const receiverCallbackData = '0x0102030405';
// check balances before transfer
const expectedInitialBalances = [
// from
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
// to
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = await devUtils
.encodeERC1155AssetData(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// encode multi-asset data
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData();
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
const expectedFinalBalances = [
// from
expectedInitialBalances[0].minus(totalValueTransferred),
// to
expectedInitialBalances[1].plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple fungible tokens of the same ERC1155 contract', async () => {
// setup test parameters
const tokenHolders = [fromAddress, toAddress];
const tokensToTransfer = erc1155FungibleTokens.slice(0, 3);
const valuesToTransfer = [new BigNumber(25), new BigNumber(35), new BigNumber(45)];
const valueMultiplier = new BigNumber(23);
const receiverCallbackData = '0x0102030405';
// check balances before transfer
const expectedInitialBalances = [
// from
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
// to
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = await devUtils
.encodeERC1155AssetData(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// encode multi-asset data
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData();
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier).times(multiAssetAmount);
});
const expectedFinalBalances = [
// from
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
// to
expectedInitialBalances[3].plus(totalValuesTransferred[0]),
expectedInitialBalances[4].plus(totalValuesTransferred[1]),
expectedInitialBalances[5].plus(totalValuesTransferred[2]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple fungible/non-fungible tokens of the same ERC1155 contract', async () => {
// setup test parameters
const tokenHolders = [fromAddress, toAddress];
const fungibleTokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const nonFungibleTokensToTransfer = erc1155NonFungibleTokensOwnedBySpender.slice(0, 1);
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
const valuesToTransfer = [new BigNumber(25), new BigNumber(1)];
const valueMultiplier = new BigNumber(1);
const receiverCallbackData = '0x0102030405';
// check balances before transfer
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
const expectedInitialBalances = [
// from
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
nftOwnerBalance,
// to
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData = await devUtils
.encodeERC1155AssetData(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// encode multi-asset data
const multiAssetAmount = new BigNumber(1);
const amounts = [valueMultiplier];
const nestedAssetData = [erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData();
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier).times(multiAssetAmount);
});
const expectedFinalBalances = [
// from
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
// to
expectedInitialBalances[2].plus(totalValuesTransferred[0]),
expectedInitialBalances[3].plus(totalValuesTransferred[1]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple different ERC1155 tokens', async () => {
// setup test parameters
const tokenHolders = [fromAddress, toAddress];
const tokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const valuesToTransfer = [new BigNumber(25)];
const valueMultiplier = new BigNumber(23);
const receiverCallbackData = '0x0102030405';
// check balances before transfer
const expectedInitialBalances = [
// from
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
// to
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// encode erc1155 asset data
const erc1155AssetData1 = await devUtils
.encodeERC1155AssetData(
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const erc1155AssetData2 = await devUtils
.encodeERC1155AssetData(
erc1155Contract2.address,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// encode multi-asset data
const multiAssetAmount = new BigNumber(5);
const amounts = [valueMultiplier, valueMultiplier];
const nestedAssetData = [erc1155AssetData1, erc1155AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, multiAssetAmount)
.getABIEncodedTransactionData();
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount);
const expectedFinalBalances = [
// from
expectedInitialBalances[0].minus(totalValueTransferred),
// to
expectedInitialBalances[1].plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer a combination of ERC20, ERC721, and ERC1155 tokens', async () => {
// setup test parameters
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const erc1155TokenHolders = [fromAddress, toAddress];
const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1);
const erc1155ValuesToTransfer = [new BigNumber(25)];
const erc1155Amount = new BigNumber(23);
const erc1155ReceiverCallbackData = '0x0102030405';
const erc1155AssetData = await devUtils
.encodeERC1155AssetData(
erc1155Contract.address,
erc1155TokensToTransfer,
erc1155ValuesToTransfer,
erc1155ReceiverCallbackData,
)
.callAsync();
const amounts = [erc20Amount, erc721Amount, erc1155Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
// check balances before transfer
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
const erc1155ExpectedInitialBalances = [
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
];
await erc1155Wrapper.assertBalancesAsync(
erc1155TokenHolders,
erc1155TokensToTransfer,
erc1155ExpectedInitialBalances,
);
// execute transfer
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
gas: 1000000,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// check balances after transfer
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalAmount = inputAmount.times(erc20Amount);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
);
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.equal(toAddress);
const erc1155TotalValueTransferred = erc1155ValuesToTransfer[0].times(erc1155Amount).times(inputAmount);
const expectedFinalBalances = [
erc1155ExpectedInitialBalances[0].minus(erc1155TotalValueTransferred),
erc1155ExpectedInitialBalances[1].plus(erc1155TotalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(
erc1155TokenHolders,
erc1155TokensToTransfer,
expectedFinalBalances,
);
});
it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalAmount = inputAmount.times(erc20Amount);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
);
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.equal(toAddress);
});
it('should successfully transfer tokens and ignore extra assetData', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const extraData = '0102030405060708090001020304050607080900010203040506070809000102';
const assetData = `${await devUtils
.encodeMultiAssetData(amounts, nestedAssetData)
.callAsync()}${extraData}`;
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const ownerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset).to.be.equal(fromAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalAmount = inputAmount.times(erc20Amount);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount),
);
const newOwnerFromAsset = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(newOwnerFromAsset).to.be.equal(toAddress);
});
it('should successfully transfer correct amounts when the `amount` > 1', async () => {
const inputAmount = new BigNumber(100);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync();
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalErc20AAmount = inputAmount.times(erc20Amount1);
const totalErc20BAmount = inputAmount.times(erc20Amount2);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalErc20AAmount),
);
expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
);
expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenB.address].plus(totalErc20BAmount),
);
});
it('should successfully transfer a large amount of tokens', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount1 = new BigNumber(10);
const erc20Amount2 = new BigNumber(20);
const erc20AssetData1 = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc20AssetData2 = await devUtils.encodeERC20AssetData(erc20TokenB.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721Balances = await erc721Wrapper.getBalancesAsync();
const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1];
const erc721AssetData1 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const erc721AssetData2 = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId2)
.callAsync();
const erc721AssetData3 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId)
.callAsync();
const erc721AssetData4 = await devUtils
.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId2)
.callAsync();
const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount];
const nestedAssetData = [
erc721AssetData1,
erc20AssetData1,
erc721AssetData2,
erc20AssetData2,
erc721AssetData3,
erc721AssetData4,
];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const ownerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
expect(ownerFromAsset1).to.be.equal(fromAddress);
const ownerFromAsset2 = await erc721TokenA.ownerOf(erc721AFromTokenId2).callAsync();
expect(ownerFromAsset2).to.be.equal(fromAddress);
const ownerFromAsset3 = await erc721TokenB.ownerOf(erc721BFromTokenId).callAsync();
expect(ownerFromAsset3).to.be.equal(fromAddress);
const ownerFromAsset4 = await erc721TokenB.ownerOf(erc721BFromTokenId2).callAsync();
expect(ownerFromAsset4).to.be.equal(fromAddress);
const erc20Balances = await erc20Wrapper.getBalancesAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newOwnerFromAsset1 = await erc721TokenA.ownerOf(erc721AFromTokenId).callAsync();
const newOwnerFromAsset2 = await erc721TokenA.ownerOf(erc721AFromTokenId2).callAsync();
const newOwnerFromAsset3 = await erc721TokenB.ownerOf(erc721BFromTokenId).callAsync();
const newOwnerFromAsset4 = await erc721TokenB.ownerOf(erc721BFromTokenId2).callAsync();
expect(newOwnerFromAsset1).to.be.equal(toAddress);
expect(newOwnerFromAsset2).to.be.equal(toAddress);
expect(newOwnerFromAsset3).to.be.equal(toAddress);
expect(newOwnerFromAsset4).to.be.equal(toAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
const totalErc20AAmount = inputAmount.times(erc20Amount1);
const totalErc20BAmount = inputAmount.times(erc20Amount2);
expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
);
expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenA.address].plus(totalErc20AAmount),
);
expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
);
expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
erc20Balances[toAddress][erc20TokenB.address].plus(totalErc20BAmount),
);
});
it('should revert if a single transfer fails', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
// 2 is an invalid erc721 amount
const erc721Amount = new BigNumber(2);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
RevertReason.InvalidAmount,
);
});
it('should revert if an AssetProxy is not registered', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const invalidProxyId = '0x12345678';
const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
RevertReason.AssetProxyDoesNotExist,
);
});
it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
RevertReason.LengthMismatch,
);
});
it('should revert if amounts multiplication results in an overflow', async () => {
const inputAmount = new BigNumber(2).pow(128);
const erc20Amount = new BigNumber(2).pow(128);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
RevertReason.Uint256Overflow,
);
});
it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = '0x123456';
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: authorized,
}),
RevertReason.LengthGreaterThan3Required,
);
});
it('should revert if caller is not authorized', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data,
from: notAuthorized,
}),
RevertReason.SenderNotAuthorized,
);
});
it('should revert if asset data overflows beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
// append asset data to end of tx data with a length of 0x300 bytes, which will extend past actual calldata.
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
const invalidOffsetToAssetData = '00000000000000000000000000000000000000000000000000000000000002a0';
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
const badData = `${data.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
// execute transfer
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data: badData,
from: authorized,
}),
RevertReason.InvalidAssetDataEnd,
);
});
it('should revert if asset data resolves to a location beyond the bounds of calldata', async () => {
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const data = assetProxyInterface
.transferFrom(assetData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
const invalidOffsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000400';
const badData = data.replace(offsetToAssetData, invalidOffsetToAssetData);
// execute transfer
// note that this triggers `InvalidAssetDataLength` because the length is zero, otherwise it would
// trigger `InvalidAssetDataEnd`.
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data: badData,
from: authorized,
}),
RevertReason.InvalidAssetDataLength,
);
});
it('should revert if length of assetData, excluding the selector, is not a multiple of 32', async () => {
// setup test parameters
const inputAmount = new BigNumber(1);
const erc20Amount = new BigNumber(10);
const erc20AssetData = await devUtils.encodeERC20AssetData(erc20TokenA.address).callAsync();
const erc721Amount = new BigNumber(1);
const erc721AssetData = await devUtils
.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId)
.callAsync();
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const assetData = await devUtils.encodeMultiAssetData(amounts, nestedAssetData).callAsync();
const extraData = '01';
const assetDataWithExtraData = `${assetData}${extraData}`;
const badData = assetProxyInterface
.transferFrom(assetDataWithExtraData, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
// execute transfer
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data: badData,
from: authorized,
}),
RevertReason.InvalidAssetDataLength,
);
});
it('should revert if length of assetData is less than 68 bytes', async () => {
// setup test parameters
const inputAmount = new BigNumber(1);
// we'll construct asset data that has a 4 byte selector plus
// 32 byte payload. This results in asset data that is 36 bytes
// long and will trigger the `invalid length` error.
// we must be sure to use a # of bytes that is still %32
// so that we know the error is not triggered by another check in the code.
const zeros32Bytes = '0'.repeat(64);
const assetData36Bytes = `${AssetProxyId.MultiAsset}${zeros32Bytes}`;
const badData = assetProxyInterface
.transferFrom(assetData36Bytes, fromAddress, toAddress, inputAmount)
.getABIEncodedTransactionData();
// execute transfer
await expectTransactionFailedAsync(
web3Wrapper.sendTransactionAsync({
to: multiAssetProxy.address,
data: badData,
from: authorized,
}),
RevertReason.InvalidAssetDataLength,
);
});
});
});
});
// tslint:enable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count