protocol/contracts/asset-proxy/test/erc1155_proxy.ts
F. Eugene Aumson f11d8a5bd8
@0x/order-utils refactors for v3: orderParsingUtils, signatureUtils, orderHashUtils, RevertErrors, transactionHashUtils (#2321)
* move orderParsingUtils from order-utils to connect

* Remove many functions from signatureUtils

Removed from the exported object, that is.  All of them are used in
other existing code, so they were all moved to be as local to their
usage as possible.

* remove orderHashUtils.isValidOrderHash()

* Move all *RevertErrors from order-utils...

...into their respective @0x/contracts- packages.

* Refactor @0x/order-utils' orderHashUtils away

- Move existing routines into @0x/contracts-test-utils

- Migrate non-contract-test callers to a newly-exposed getOrderHash()
method in DevUtils.

* Move all *RevertErrors from @0x/utils...

...into their respective @0x/contracts- packages.

* rm transactionHashUtils.isValidTransactionHash()

* DevUtils.sol: Fail yarn test if too big to deploy

* Refactor @0x/order-utils transactionHashUtils away

- Move existing routines into @0x/contracts-test-utils

- Migrate non-contract-test callers to a newly-exposed
getTransactionHash() method in DevUtils.

* Consolidate `Removed export...` CHANGELOG entries

* Rm EthBalanceChecker from devutils wrapper exports

* Stop importing from '.' or '.../src'

* fix builds

* fix prettier; dangling promise

* increase max bundle size
2019-11-14 17:14:24 -05:00

1922 lines
104 KiB
TypeScript

import { DevUtilsContract } from '@0x/contracts-dev-utils';
import {
artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
DummyERC1155ReceiverContract,
ERC1155MintableContract,
Erc1155Wrapper,
} from '@0x/contracts-erc1155';
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-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 ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { ERC1155ProxyWrapper } from '../src/erc1155_proxy_wrapper';
import { ERC1155ProxyContract, IAssetDataContract } from '../src/wrappers';
import { artifacts } from './artifacts';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('ERC1155Proxy', () => {
// constant values used in transfer tests
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverContractInitialFungibleBalance = new BigNumber(0);
const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100);
const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4);
const valueMultiplierSmall = new BigNumber(2);
const valueMultiplierNft = new BigNumber(1);
const nonFungibleValueToTransfer = nftOwnerBalance;
const receiverCallbackData = '0x01020304';
// addresses
let owner: string;
let notAuthorized: string;
let authorized: string;
let spender: string;
let receiver: string;
let receiverContract: string;
// contracts & wrappers
let erc1155Proxy: ERC1155ProxyContract;
let erc1155Receiver: DummyERC1155ReceiverContract;
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
let erc1155Contract: ERC1155MintableContract;
let erc1155Wrapper: Erc1155Wrapper;
// tokens
let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[];
// devUtils for encoding and decoding assetData
let devUtils: DevUtilsContract;
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
/// deploy & configure ERC1155Proxy
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await erc1155Proxy.addAuthorizedAddress(authorized).awaitTransactionSuccessAsync({ from: owner });
await erc1155Proxy.addAuthorizedAddress(erc1155Proxy.address).awaitTransactionSuccessAsync({ from: owner });
// deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract();
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
erc1155Artifacts.DummyERC1155Receiver,
provider,
txDefaults,
artifacts,
);
receiverContract = erc1155Receiver.address;
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
nonFungibleTokensOwnedBySpender = [];
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
const nonFungibleTokenAsString = nonFungibleToken.toString();
const nonFungibleTokenHeldBySpender =
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
});
// set up devUtils
devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, { from: owner });
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('general', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc1155Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0xa7cb5fb7', async () => {
const proxyId = await erc1155Proxy.getProxyId().callAsync();
const expectedProxyId = AssetProxyId.ERC1155;
expect(proxyId).to.equal(expectedProxyId);
});
});
describe('transferFrom', () => {
it('should successfully transfer value for a single, fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier);
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(totalValueTransferred),
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for the same fungible token several times', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleTokens[0];
const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer];
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => {
return sum.plus(value);
}) as BigNumber;
totalValueTransferred = totalValueTransferred.times(valueMultiplier);
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValueTransferred),
// receiver
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should successfully transfer value for several fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 3);
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValuesTransferred[0]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[1]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[2]),
// receiver
receiverInitialFungibleBalance.plus(totalValuesTransferred[0]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[1]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[2]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer a non-fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const valuesToTransfer = [
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const fungibleTokensToTransfer = fungibleTokens.slice(0, 3);
const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2);
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
const valuesToTransfer = [
fungibleValueToTransferLarge,
fungibleValueToTransferSmall,
fungibleValueToTransferSmall,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
expectedInitialBalances[3].minus(totalValuesTransferred[3]),
expectedInitialBalances[4].minus(totalValuesTransferred[4]),
// receiver
expectedInitialBalances[5].plus(totalValuesTransferred[0]),
expectedInitialBalances[6].plus(totalValuesTransferred[1]),
expectedInitialBalances[7].plus(totalValuesTransferred[2]),
expectedInitialBalances[8].plus(totalValuesTransferred[3]),
expectedInitialBalances[9].plus(totalValuesTransferred[4]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is NULL', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const nullReceiverCallbackData = '0x';
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
nullReceiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(nullReceiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is one word', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// create word of callback data
const customReceiverCallbackData = '0x0102030405060708091001020304050607080910010203040506070809100102';
const customReceiverCallbackDataAsBuffer = ethUtil.toBuffer(customReceiverCallbackData);
const oneWordInBytes = 32;
expect(customReceiverCallbackDataAsBuffer.byteLength).to.be.equal(oneWordInBytes);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
customReceiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(customReceiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is multiple words', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// create word of callback data
const scalar = 5;
const customReceiverCallbackData = `0x${'0102030405060708091001020304050607080910010203040506070809100102'.repeat(
scalar,
)}`;
const customReceiverCallbackDataAsBuffer = ethUtil.toBuffer(customReceiverCallbackData);
const oneWordInBytes = 32;
expect(customReceiverCallbackDataAsBuffer.byteLength).to.be.equal(oneWordInBytes * scalar);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
customReceiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(customReceiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is multiple words but not word-aligned', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// create word of callback data
const scalar = 5;
const customReceiverCallbackData = `0x${'0102030405060708091001020304050607080910010203040506070809100102'.repeat(
scalar,
)}090807`;
const customReceiverCallbackDataAsBuffer = ethUtil.toBuffer(customReceiverCallbackData);
const oneWordInBytes = 32;
expect(customReceiverCallbackDataAsBuffer.byteLength).to.be.greaterThan(oneWordInBytes * scalar);
expect(customReceiverCallbackDataAsBuffer.byteLength).to.be.lessThan(oneWordInBytes * (scalar + 1));
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
customReceiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(customReceiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value and ignore extra assetData', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const extraData = '0102030405060708091001020304050607080910010203040506070809100102';
const assetDataWithExtraData = `${assetData}${extraData}`;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithExtraData,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer if token ids and values are abi encoded to same entry in calldata', async () => {
/**
* Suppose the `tokensToTransfer` and `valuesToTransfer` are identical; their offsets in
* the ABI-encoded asset data may be the same. E.g. token IDs [1, 2] and values [1, 2].
* Suppose we scale by a factor of 2, then we expect to trade token IDs [1, 2] and values [2, 4].
* This test ensures that scaling the values does not simultaneously scale the token IDs.
*/
///// Step 1/5 /////
// Create tokens with ids [1, 2, 3, 4] and mint a balance of 4 for the `spender`
const tokensToCreate = [new BigNumber(1), new BigNumber(2), new BigNumber(3), new BigNumber(4)];
const spenderInitialBalance = new BigNumber(4);
const receiverInitialBalance = new BigNumber(0);
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper
.getContract()
.createWithType(tokenToCreate, tokenUri)
.awaitTransactionSuccessAsync({
from: owner,
});
// mint balance for spender
await erc1155Wrapper
.getContract()
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
.awaitTransactionSuccessAsync({
from: owner,
});
}
///// Step 2/5 /////
// Check balances before transfer
const balanceHolders = [spender, spender, spender, spender, receiver, receiver, receiver, receiver];
const balanceTokens = tokensToCreate.concat(tokensToCreate);
const initialBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedInitialBalances = [
spenderInitialBalance, // Token ID 1 / Spender Balance
spenderInitialBalance, // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance, // Token ID 1 / Receiver Balance
receiverInitialBalance, // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(initialBalances).to.be.deep.equal(expectedInitialBalances);
///// Step 3/5 /////
// Create optimized calldata. We expect it to be formatted like the table below.
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // ERC1155 contract address
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // Offset to token IDs
// 0x40 0000000000000000000000000000000000000000000000000000000000000080 // Offset to token values (same as IDs)
// 0x60 00000000000000000000000000000000000000000000000000000000000000e0 // Offset to data
// 0x80 0000000000000000000000000000000000000000000000000000000000000002 // Length of token Ids / token values
// 0xA0 0000000000000000000000000000000000000000000000000000000000000001 // First Token ID / Token value
// 0xC0 0000000000000000000000000000000000000000000000000000000000000002 // Second Token ID / Token value
// 0xE0 0000000000000000000000000000000000000000000000000000000000000004 // Length of callback data
// 0x100 0102030400000000000000000000000000000000000000000000000000000000 // Callback data
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
const valuesToTransfer = tokensToTransfer;
const valueMultiplier = new BigNumber(2);
// hand encode optimized assetData because our tooling (based on LibAssetData.sol/encodeERC1155AssetData) does not use optimized encoding
const assetDataContract = new IAssetDataContract(constants.NULL_ADDRESS, provider);
const selector = assetDataContract.getSelector('ERC1155Assets');
const assetDataWithoutContractAddress =
'0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000';
const assetData = `${selector}000000000000000000000000${erc1155ContractAddress.substr(
2,
)}${assetDataWithoutContractAddress}`;
///// Step 4/5 /////
// Transfer token IDs [1, 2] and amounts [1, 2] with a multiplier of 2;
// the expected trade will be token IDs [1, 2] and amounts [2, 4]
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData,
);
///// Step 5/5 /////
// Validate final balances
const finalBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedAmountsTransferred = _.map(valuesToTransfer, value => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
spenderInitialBalance.minus(expectedAmountsTransferred[0]), // Token ID 1 / Spender Balance
spenderInitialBalance.minus(expectedAmountsTransferred[1]), // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance.plus(expectedAmountsTransferred[0]), // Token ID 1 / Receiver Balance
receiverInitialBalance.plus(expectedAmountsTransferred[1]), // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(finalBalances).to.be.deep.equal(expectedFinalBalances);
});
it('should successfully transfer if token values and data are abi encoded to same entry in calldata', async () => {
/**
* This test ensures that scaling the values does not simultaneously scale the data.
* Note that this test is slightly more contrived than the test above, as asset data must be
* intentionally hand-modified to produce this result: a functioning abi encoder will not produce it.
*/
///// Step 1/5 /////
// Create tokens with ids [1, 2, 3, 4] and mint a balance of 4 for the `spender`
const tokensToCreate = [new BigNumber(1), new BigNumber(2), new BigNumber(3), new BigNumber(4)];
const spenderInitialBalance = new BigNumber(4);
const receiverInitialBalance = new BigNumber(0);
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper
.getContract()
.createWithType(tokenToCreate, tokenUri)
.awaitTransactionSuccessAsync({
from: owner,
});
// mint balance for spender
await erc1155Wrapper
.getContract()
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
.awaitTransactionSuccessAsync({
from: owner,
});
}
///// Step 2/5 /////
// Check balances before transfer
const balanceHolders = [
spender,
spender,
spender,
spender,
receiverContract,
receiverContract,
receiverContract,
receiverContract,
];
const balanceTokens = tokensToCreate.concat(tokensToCreate);
const initialBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedInitialBalances = [
spenderInitialBalance, // Token ID 1 / Spender Balance
spenderInitialBalance, // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance, // Token ID 1 / Receiver Balance
receiverInitialBalance, // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(initialBalances).to.be.deep.equal(expectedInitialBalances);
///// Step 3/5 /////
// Create optimized calldata. We format like the table below.
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // ERC1155 contract address
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // Offset to token IDs
// 0x40 00000000000000000000000000000000000000000000000000000000000000e0 // Offset to token values
// 0x60 00000000000000000000000000000000000000000000000000000000000000e0 // Offset to data (same as values)
// 0x80 0000000000000000000000000000000000000000000000000000000000000002 // Length of token Ids
// 0xA0 0000000000000000000000000000000000000000000000000000000000000001 // First Token ID
// 0xC0 0000000000000000000000000000000000000000000000000000000000000002 // Second Token ID
// 0xE0 0000000000000000000000000000000000000000000000000000000000000002 // Length of values (Length of data)
// 0x100 0000000000000000000000000000000000000000000000000000000000000002 // First Value
// 0x120 0000000000000000000000000000000000000000000000000000000000000002 // Second Value
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
const valuesToTransfer = [new BigNumber(2), new BigNumber(2)];
const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
const assetDataParameters =
'000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002';
const assetData = `${assetDataSelectorAndContractAddress}${assetDataParameters}`;
///// Step 4/5 /////
// Transfer tokens
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(2);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(tokensToTransfer[1]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(2);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(valuesToTransfer[0].times(valueMultiplier));
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(valuesToTransfer[1].times(valueMultiplier));
expect(receiverLog.args.data).to.be.deep.equal('0x0000');
///// Step 5/5 /////
// Validate final balances
const finalBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedAmountsTransferred = _.map(valuesToTransfer, value => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
spenderInitialBalance.minus(expectedAmountsTransferred[0]), // Token ID 1 / Spender Balance
spenderInitialBalance.minus(expectedAmountsTransferred[1]), // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance.plus(expectedAmountsTransferred[0]), // Token ID 1 / Receiver Balance
receiverInitialBalance.plus(expectedAmountsTransferred[1]), // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(finalBalances).to.be.deep.equal(expectedFinalBalances);
});
it('should successfully transfer if token ids, values and data are abi encoded to same entry in calldata', async () => {
/**
* This test combines the two tests above.
* Similar to the test above, the asset data must be manually constructed.
*/
///// Step 1/5 /////
// Create tokens with ids [1, 2, 3, 4] and mint a balance of 4 for the `spender`
const tokensToCreate = [new BigNumber(1), new BigNumber(2), new BigNumber(3), new BigNumber(4)];
const spenderInitialBalance = new BigNumber(4);
const receiverInitialBalance = new BigNumber(0);
const tokenUri = '';
for (const tokenToCreate of tokensToCreate) {
// create token
await erc1155Wrapper
.getContract()
.createWithType(tokenToCreate, tokenUri)
.awaitTransactionSuccessAsync({
from: owner,
});
// mint balance for spender
await erc1155Wrapper
.getContract()
.mintFungible(tokenToCreate, [spender], [spenderInitialBalance])
.awaitTransactionSuccessAsync({
from: owner,
});
}
///// Step 2/5 /////
// Check balances before transfer
const balanceHolders = [
spender,
spender,
spender,
spender,
receiverContract,
receiverContract,
receiverContract,
receiverContract,
];
const balanceTokens = tokensToCreate.concat(tokensToCreate);
const initialBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedInitialBalances = [
spenderInitialBalance, // Token ID 1 / Spender Balance
spenderInitialBalance, // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance, // Token ID 1 / Receiver Balance
receiverInitialBalance, // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(initialBalances).to.be.deep.equal(expectedInitialBalances);
///// Step 3/5 /////
// Create optimized calldata. We format like the table below.
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082 // ERC1155 contract address
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // Offset to token IDs
// 0x40 0000000000000000000000000000000000000000000000000000000000000080 // Offset to token values
// 0x60 0000000000000000000000000000000000000000000000000000000000000080 // Offset to data (same as values)
// 0x80 0000000000000000000000000000000000000000000000000000000000000002 // Length of token Ids (Length of values / data)
// 0xA0 0000000000000000000000000000000000000000000000000000000000000001 // First Token ID (First Value)
// 0xC0 0000000000000000000000000000000000000000000000000000000000000002 // Second Token ID (Second Value)
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const tokensToTransfer = [new BigNumber(1), new BigNumber(2)];
const valuesToTransfer = [new BigNumber(1), new BigNumber(2)];
const valueMultiplier = new BigNumber(2);
// create callback data that is the encoded version of `valuesToTransfer`
const generatedAssetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// remove the function selector and contract address from check, as these change on each test
const offsetToTokenIds = 74;
const assetDataSelectorAndContractAddress = generatedAssetData.substr(0, offsetToTokenIds);
const assetDataParameters =
'000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002';
const assetData = `${assetDataSelectorAndContractAddress}${assetDataParameters}`;
///// Step 4/5 /////
// Transfer tokens
const txReceipt = await erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(2);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(tokensToTransfer[1]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(2);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(valuesToTransfer[0].times(valueMultiplier));
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(valuesToTransfer[1].times(valueMultiplier));
expect(receiverLog.args.data).to.be.deep.equal('0x0000');
///// Step 5/5 /////
// Validate final balances
const finalBalances = await erc1155Wrapper.getBalancesAsync(balanceHolders, balanceTokens);
const expectedAmountsTransferred = _.map(valuesToTransfer, value => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
spenderInitialBalance.minus(expectedAmountsTransferred[0]), // Token ID 1 / Spender Balance
spenderInitialBalance.minus(expectedAmountsTransferred[1]), // Token ID 2 / Spender Balance
spenderInitialBalance, // Token ID 3 / Spender Balance
spenderInitialBalance, // Token ID 4 / Spender Balance
receiverInitialBalance.plus(expectedAmountsTransferred[0]), // Token ID 1 / Receiver Balance
receiverInitialBalance.plus(expectedAmountsTransferred[1]), // Token ID 2 / Receiver Balance
receiverInitialBalance, // Token ID 3 / Receiver Balance
receiverInitialBalance, // Token ID 4 / Receiver Balance
];
expect(finalBalances).to.be.deep.equal(expectedFinalBalances);
});
it('should revert if token ids resolves to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // offset to token ids
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token ids to point outside the calldata.
const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000080';
const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000180';
const assetDataWithBadTokenIdsOffset = assetData.replace(
encodedOffsetToTokenIds,
badEncodedOffsetToTokenIds,
);
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenIdsOffset,
),
);
});
it('should revert if an element of token ids lies to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // offset to token ids
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token ids to the end of calldata.
// Then we'll add an invalid length: we encode length of 2 but only add 1 element.
const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000080';
const newEcodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithNewTokenIdsOffset = assetData.replace(
encodedOffsetToTokenIds,
newEcodedOffsetToTokenIds,
);
const encodedTokenIdsLength = '0000000000000000000000000000000000000000000000000000000000000002';
const encodedTokenIdValues = '0000000000000000000000000000000000000000000000000000000000000001';
const assetDataWithBadTokenIds = `${assetDataWithNewTokenIdsOffset}${encodedTokenIdsLength}${encodedTokenIdValues}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenIds,
),
);
});
it('should revert token ids length overflows', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080 // offset to token ids
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token ids to point to the end of calldata
const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000080';
const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithBadTokenIdsOffset = assetData.replace(
encodedOffsetToTokenIds,
badEncodedOffsetToTokenIds,
);
// We want a length that will overflow when converted to bytes - ie, multiplied by 32.
const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001';
const buffer = '0'.repeat(64 * 10);
const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithOverflow,
),
);
});
it('should revert token values length overflows', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0 // offset to token values
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token values to point to the end of calldata
const encodedOffsetToTokenIds = '00000000000000000000000000000000000000000000000000000000000000c0';
const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithBadTokenIdsOffset = assetData.replace(
encodedOffsetToTokenIds,
badEncodedOffsetToTokenIds,
);
// We want a length that will overflow when converted to bytes - ie, multiplied by 32.
const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001';
const buffer = '0'.repeat(64 * 10);
const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithOverflow,
),
);
});
it('should revert token data length overflows', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100 // offset to token data
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token ids to point to the end of calldata,
// which we'll extend with a bad length.
const encodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000100';
const badEncodedOffsetToTokenIds = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithBadTokenIdsOffset = assetData.replace(
encodedOffsetToTokenIds,
badEncodedOffsetToTokenIds,
);
// We want a length that will overflow when converted to bytes - ie, multiplied by 32.
const encodedIdsLengthOverflow = '0800000000000000000000000000000000000000000000000000000000000001';
const buffer = '0'.repeat(64 * 10);
const assetDataWithOverflow = `${assetDataWithBadTokenIdsOffset}${encodedIdsLengthOverflow}${buffer}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithOverflow,
),
);
});
it('should revert if token values resolves to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0 // offset to token values
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token values to point outside the calldata.
const encodedOffsetToTokenValues = '00000000000000000000000000000000000000000000000000000000000000c0';
const badEncodedOffsetToTokenValues = '00000000000000000000000000000000000000000000000000000000000001c0';
const assetDataWithBadTokenIdsOffset = assetData.replace(
encodedOffsetToTokenValues,
badEncodedOffsetToTokenValues,
);
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenIdsOffset,
),
);
});
it('should revert if an element of token values lies to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0 // offset to token values
// 0x60 0000000000000000000000000000000000000000000000000000000000000100
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token values to the end of calldata.
// Then we'll add an invalid length: we encode length of 2 but only add 1 element.
const encodedOffsetToTokenValues = '00000000000000000000000000000000000000000000000000000000000000c0';
const newEcodedOffsetToTokenValues = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithNewTokenValuesOffset = assetData.replace(
encodedOffsetToTokenValues,
newEcodedOffsetToTokenValues,
);
const encodedTokenValuesLength = '0000000000000000000000000000000000000000000000000000000000000002';
const encodedTokenValuesElements = '0000000000000000000000000000000000000000000000000000000000000001';
const assetDataWithBadTokenIds = `${assetDataWithNewTokenValuesOffset}${encodedTokenValuesLength}${encodedTokenValuesElements}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenIds,
),
);
});
it('should revert if token data resolves to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100 // offset to token data
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token data to point outside the calldata.
const encodedOffsetToTokenData = '0000000000000000000000000000000000000000000000000000000000000100';
const badEncodedOffsetToTokenData = '00000000000000000000000000000000000000000000000000000000000001c0';
const assetDataWithBadTokenDataOffset = assetData.replace(
encodedOffsetToTokenData,
badEncodedOffsetToTokenData,
);
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenDataOffset,
),
);
});
it('should revert if an element of token data lies to outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
// The asset data we just generated will look like this:
// a7cb5fb7
// 0x 0000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082
// 0x20 0000000000000000000000000000000000000000000000000000000000000080
// 0x40 00000000000000000000000000000000000000000000000000000000000000c0
// 0x60 0000000000000000000000000000000000000000000000000000000000000100 // offset to token data
// 0x80 0000000000000000000000000000000000000000000000000000000000000001
// 0xA0 0000000000000000000000000000000100000000000000000000000000000000
// 0xC0 0000000000000000000000000000000000000000000000000000000000000001
// 0xE0 0000000000000000000000000000000000000000000000878678326eac900000
// 0x100 0000000000000000000000000000000000000000000000000000000000000004
// 0x120 0102030400000000000000000000000000000000000000000000000000000000
//
// We want to change the offset to token data to the end of calldata.
// Then we'll add an invalid length: we encode length of 33 but only add 32 elements.
const encodedOffsetToTokenData = '0000000000000000000000000000000000000000000000000000000000000100';
const newEcodedOffsetToTokenData = '0000000000000000000000000000000000000000000000000000000000000140';
const assetDataWithNewTokenDataOffset = assetData.replace(
encodedOffsetToTokenData,
newEcodedOffsetToTokenData,
);
const encodedTokenDataLength = '0000000000000000000000000000000000000000000000000000000000000021';
const encodedTokenDataElements = '0000000000000000000000000000000000000000000000000000000000000001';
const assetDataWithBadTokenData = `${assetDataWithNewTokenDataOffset}${encodedTokenDataLength}${encodedTokenDataElements}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetDataWithBadTokenData,
),
);
});
it('should revert if asset data lies outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData,
);
const offsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000080';
const invalidOffsetToAssetData = '0000000000000000000000000000000000000000000000000000000000000180';
const badTxData = txData.replace(offsetToAssetData, invalidOffsetToAssetData);
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromRawAsync(badTxData, authorized),
);
});
it('should revert if asset data lies outside the bounds of calldata', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const erc1155ContractAddress = erc1155Wrapper.getContract().address;
const assetData = await devUtils
.encodeERC1155AssetData(
erc1155ContractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
)
.callAsync();
const txData = await erc1155ProxyWrapper.getTransferFromAbiEncodedTxDataAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData,
);
// 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 = '0000000000000000000000000000000000000000000000000000000000000200';
const newAssetData = '0000000000000000000000000000000000000000000000000000000000000304';
const badTxData = `${txData.replace(offsetToAssetData, invalidOffsetToAssetData)}${newAssetData}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromRawAsync(badTxData, authorized),
);
});
it('should revert if length of assetData is less than 132 bytes', async () => {
// setup test parameters
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// we'll construct asset data that has a 4 byte selector plus
// 96 byte payload. This results in asset data that is 100 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 zeros96Bytes = '0'.repeat(188);
const assetData131Bytes = `${AssetProxyId.ERC1155}${zeros96Bytes}`;
// execute transfer
await expectTransactionFailedWithoutReasonAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
assetData131Bytes,
),
);
});
it('should transfer nothing if value is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [new BigNumber(0)];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if value multiplier is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = new BigNumber(0);
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if there are no tokens in asset data', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer: BigNumber[] = [];
const valuesToTransfer: BigNumber[] = [];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should propagate revert reason from erc1155 contract failure', async () => {
// disable transfers
const shouldRejectTransfer = true;
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).awaitTransactionSuccessAsync({
from: owner,
});
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.TransferRejected,
);
});
it('should revert if transferring the same non-fungible token more than once', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const nftToTransfer = nonFungibleTokensOwnedBySpender[0];
const tokensToTransfer = [nftToTransfer, nftToTransfer];
const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.NFTNotOwnedByFromAddress,
);
});
it('should revert if there is a multiplication overflow', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const maxUintValue = new BigNumber(2).pow(256).minus(1);
const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
maxUintValue,
valueMultiplier,
);
// execute transfer
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
await expect(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
).to.revertWith(expectedError);
});
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [new BigNumber(2)];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if sender balance is insufficient', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1);
const valuesToTransfer = [valueGreaterThanSpenderBalance];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
spenderInitialFungibleBalance,
valuesToTransfer[0].times(valueMultiplier),
);
// execute transfer
const tx = erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if sender allowance is insufficient', async () => {
// dremove allowance for ERC1155 proxy
const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address);
const isApproved = false;
await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved);
const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address);
expect(isApprovedActualValue).to.be.equal(isApproved);
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.InsufficientAllowance,
);
});
it('should revert if caller is not authorized', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
notAuthorized,
),
RevertReason.SenderNotAuthorized,
);
});
});
});
// tslint:enable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count