* 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
1922 lines
104 KiB
TypeScript
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
|