460 lines
23 KiB
TypeScript
460 lines
23 KiB
TypeScript
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
|
import { SafeMathRevertErrors } from '@0x/contracts-utils';
|
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
import { RevertReason } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import * as chai from 'chai';
|
|
import { LogWithDecodedArgs } from 'ethereum-types';
|
|
import * as _ from 'lodash';
|
|
|
|
import { Erc1155Wrapper } from '../src/erc1155_wrapper';
|
|
import { ERC1155MintableContract } from '../src/wrappers';
|
|
|
|
import { artifacts } from './artifacts';
|
|
import { DummyERC1155ReceiverBatchTokenReceivedEventArgs, DummyERC1155ReceiverContract } from './wrappers';
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
// tslint:disable:no-unnecessary-type-assertion
|
|
describe('ERC1155Token', () => {
|
|
// constant values used in transfer tests
|
|
const nftOwnerBalance = new BigNumber(1);
|
|
const nftNotOwnerBalance = new BigNumber(0);
|
|
const spenderInitialFungibleBalance = new BigNumber(500);
|
|
const receiverInitialFungibleBalance = new BigNumber(0);
|
|
const fungibleValueToTransfer = spenderInitialFungibleBalance.div(2);
|
|
const nonFungibleValueToTransfer = nftOwnerBalance;
|
|
const receiverCallbackData = '0x01020304';
|
|
// tokens & addresses
|
|
let owner: string;
|
|
let spender: string;
|
|
let delegatedSpender: string;
|
|
let receiver: string;
|
|
let erc1155Contract: ERC1155MintableContract;
|
|
let erc1155Receiver: DummyERC1155ReceiverContract;
|
|
let nonFungibleToken: BigNumber;
|
|
let erc1155Wrapper: Erc1155Wrapper;
|
|
let fungibleToken: BigNumber;
|
|
// tests
|
|
before(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
after(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
before(async () => {
|
|
// deploy erc1155 contract & receiver
|
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
[owner, spender, delegatedSpender] = accounts;
|
|
erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
|
|
artifacts.ERC1155Mintable,
|
|
provider,
|
|
txDefaults,
|
|
artifacts,
|
|
);
|
|
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
|
|
artifacts.DummyERC1155Receiver,
|
|
provider,
|
|
txDefaults,
|
|
artifacts,
|
|
);
|
|
receiver = erc1155Receiver.address;
|
|
// create wrapper & mint erc1155 tokens
|
|
erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, owner);
|
|
fungibleToken = await erc1155Wrapper.mintFungibleTokensAsync([spender], spenderInitialFungibleBalance);
|
|
let nonFungibleTokens: BigNumber[];
|
|
[, nonFungibleTokens] = await erc1155Wrapper.mintNonFungibleTokensAsync([spender]);
|
|
nonFungibleToken = nonFungibleTokens[0];
|
|
});
|
|
beforeEach(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
afterEach(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
describe('safeTransferFrom', () => {
|
|
it('should transfer fungible token if called by token owner', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = fungibleValueToTransfer;
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
fungibleToken,
|
|
valueToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
spenderInitialFungibleBalance.minus(valueToTransfer),
|
|
receiverInitialFungibleBalance.plus(valueToTransfer),
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
|
});
|
|
it('should transfer non-fungible token if called by token owner', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokenToTransfer = nonFungibleToken;
|
|
const valueToTransfer = nonFungibleValueToTransfer;
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokenToTransfer,
|
|
valueToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
|
});
|
|
it('should trigger callback if transferring to a contract', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = fungibleValueToTransfer;
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [
|
|
spenderInitialFungibleBalance,
|
|
receiverInitialFungibleBalance,
|
|
nftOwnerBalance,
|
|
nftNotOwnerBalance,
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
|
// execute transfer
|
|
const tx = await erc1155Wrapper.safeTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokenToTransfer,
|
|
valueToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
expect(tx.logs.length).to.be.equal(2);
|
|
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
|
// check callback logs
|
|
const expectedCallbackLog = {
|
|
operator: spender,
|
|
from: spender,
|
|
tokenId: tokenToTransfer,
|
|
tokenValue: valueToTransfer,
|
|
data: receiverCallbackData,
|
|
};
|
|
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
|
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
|
expect(receiverLog.args.tokenId).to.be.bignumber.equal(expectedCallbackLog.tokenId);
|
|
expect(receiverLog.args.tokenValue).to.be.bignumber.equal(expectedCallbackLog.tokenValue);
|
|
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
spenderInitialFungibleBalance.minus(valueToTransfer),
|
|
receiverInitialFungibleBalance.plus(valueToTransfer),
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
|
});
|
|
it('should revert if transfer reverts', async () => {
|
|
// setup test parameters
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
|
|
// create the expected error (a uint256 underflow)
|
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
|
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
|
|
spenderInitialFungibleBalance,
|
|
valueToTransfer,
|
|
);
|
|
// execute transfer
|
|
const tx = erc1155Contract
|
|
.safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData)
|
|
.sendTransactionAsync({ from: spender });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if callback reverts', async () => {
|
|
// setup test parameters
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = fungibleValueToTransfer;
|
|
// set receiver to reject balances
|
|
const shouldRejectTransfer = true;
|
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).sendTransactionAsync(),
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
// execute transfer
|
|
return expect(
|
|
erc1155Contract
|
|
.safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData)
|
|
.awaitTransactionSuccessAsync({ from: spender }),
|
|
).to.revertWith(RevertReason.TransferRejected);
|
|
});
|
|
});
|
|
describe('batchSafeTransferFrom', () => {
|
|
it('should transfer fungible tokens if called by token owner', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [fungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeBatchTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokensToTransfer,
|
|
valuesToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
|
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
|
});
|
|
it('should transfer non-fungible token if called by token owner', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [nonFungibleToken];
|
|
const valuesToTransfer = [nonFungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [nftOwnerBalance, nftNotOwnerBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeBatchTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokensToTransfer,
|
|
valuesToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [nftNotOwnerBalance, nftOwnerBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
|
});
|
|
it('should transfer mix of fungible / non-fungible tokens if called by token owner', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [
|
|
// spender
|
|
spenderInitialFungibleBalance,
|
|
nftOwnerBalance,
|
|
// receiver
|
|
receiverInitialFungibleBalance,
|
|
nftNotOwnerBalance,
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeBatchTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokensToTransfer,
|
|
valuesToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
// spender
|
|
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
|
nftNotOwnerBalance,
|
|
// receiver
|
|
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
|
nftOwnerBalance,
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
|
});
|
|
it('should trigger callback if transferring to a contract', async () => {
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [fungibleToken, nonFungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer, nonFungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [
|
|
// spender
|
|
spenderInitialFungibleBalance,
|
|
nftOwnerBalance,
|
|
// receiver
|
|
receiverInitialFungibleBalance,
|
|
nftNotOwnerBalance,
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
const tx = await erc1155Wrapper.safeBatchTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokensToTransfer,
|
|
valuesToTransfer,
|
|
receiverCallbackData,
|
|
);
|
|
expect(tx.logs.length).to.be.equal(2);
|
|
const receiverLog = tx.logs[1] as LogWithDecodedArgs<DummyERC1155ReceiverBatchTokenReceivedEventArgs>;
|
|
// check callback logs
|
|
const expectedCallbackLog = {
|
|
operator: spender,
|
|
from: spender,
|
|
tokenIds: tokensToTransfer,
|
|
tokenValues: valuesToTransfer,
|
|
data: receiverCallbackData,
|
|
};
|
|
expect(receiverLog.args.operator).to.be.equal(expectedCallbackLog.operator);
|
|
expect(receiverLog.args.from).to.be.equal(expectedCallbackLog.from);
|
|
expect(receiverLog.args.tokenIds.length).to.be.equal(2);
|
|
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[0]);
|
|
expect(receiverLog.args.tokenIds[1]).to.be.bignumber.equal(expectedCallbackLog.tokenIds[1]);
|
|
expect(receiverLog.args.tokenValues.length).to.be.equal(2);
|
|
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[0]);
|
|
expect(receiverLog.args.tokenValues[1]).to.be.bignumber.equal(expectedCallbackLog.tokenValues[1]);
|
|
expect(receiverLog.args.data).to.be.deep.equal(expectedCallbackLog.data);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
// spender
|
|
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
|
nftNotOwnerBalance,
|
|
// receiver
|
|
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
|
nftOwnerBalance,
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
|
});
|
|
it('should revert if transfer reverts', async () => {
|
|
// setup test parameters
|
|
const tokensToTransfer = [fungibleToken];
|
|
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
|
|
// create the expected error (a uint256 underflow)
|
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
|
SafeMathRevertErrors.BinOpErrorCodes.SubtractionUnderflow,
|
|
spenderInitialFungibleBalance,
|
|
valuesToTransfer[0],
|
|
);
|
|
// execute transfer
|
|
const tx = erc1155Contract
|
|
.safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
|
.sendTransactionAsync({ from: spender });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if callback reverts', async () => {
|
|
// setup test parameters
|
|
const tokensToTransfer = [fungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer];
|
|
// set receiver to reject balances
|
|
const shouldRejectTransfer = true;
|
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
await erc1155Receiver.setRejectTransferFlag(shouldRejectTransfer).sendTransactionAsync(),
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
// execute transfer
|
|
return expect(
|
|
erc1155Contract
|
|
.safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
|
.awaitTransactionSuccessAsync({ from: spender }),
|
|
).to.revertWith(RevertReason.TransferRejected);
|
|
});
|
|
});
|
|
describe('setApprovalForAll', () => {
|
|
it('should transfer token via safeTransferFrom if called by approved account', async () => {
|
|
// set approval
|
|
const isApprovedForAll = true;
|
|
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
|
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
|
expect(isApprovedForAllCheck).to.be.true();
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = fungibleValueToTransfer;
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokenToTransfer,
|
|
valueToTransfer,
|
|
receiverCallbackData,
|
|
delegatedSpender,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
spenderInitialFungibleBalance.minus(valueToTransfer),
|
|
receiverInitialFungibleBalance.plus(valueToTransfer),
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
|
|
});
|
|
it('should revert if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
|
|
// check approval not set
|
|
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
|
expect(isApprovedForAllCheck).to.be.false();
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokenToTransfer = fungibleToken;
|
|
const valueToTransfer = fungibleValueToTransfer;
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
|
|
// execute transfer
|
|
return expect(
|
|
erc1155Contract
|
|
.safeTransferFrom(spender, receiver, tokenToTransfer, valueToTransfer, receiverCallbackData)
|
|
.awaitTransactionSuccessAsync({ from: delegatedSpender }),
|
|
).to.revertWith(RevertReason.InsufficientAllowance);
|
|
});
|
|
it('should transfer token via safeBatchTransferFrom if called by approved account', async () => {
|
|
// set approval
|
|
const isApprovedForAll = true;
|
|
await erc1155Wrapper.setApprovalForAllAsync(spender, delegatedSpender, isApprovedForAll);
|
|
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
|
expect(isApprovedForAllCheck).to.be.true();
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [fungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
await erc1155Wrapper.safeBatchTransferFromAsync(
|
|
spender,
|
|
receiver,
|
|
tokensToTransfer,
|
|
valuesToTransfer,
|
|
receiverCallbackData,
|
|
delegatedSpender,
|
|
);
|
|
// check balances after transfer
|
|
const expectedFinalBalances = [
|
|
spenderInitialFungibleBalance.minus(valuesToTransfer[0]),
|
|
receiverInitialFungibleBalance.plus(valuesToTransfer[0]),
|
|
];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
|
|
});
|
|
it('should revert if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
|
|
// check approval not set
|
|
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
|
|
expect(isApprovedForAllCheck).to.be.false();
|
|
// setup test parameters
|
|
const tokenHolders = [spender, receiver];
|
|
const tokensToTransfer = [fungibleToken];
|
|
const valuesToTransfer = [fungibleValueToTransfer];
|
|
// check balances before transfer
|
|
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
|
|
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
|
|
// execute transfer
|
|
return expect(
|
|
erc1155Contract
|
|
.safeBatchTransferFrom(spender, receiver, tokensToTransfer, valuesToTransfer, receiverCallbackData)
|
|
.awaitTransactionSuccessAsync({ from: delegatedSpender }),
|
|
).to.revertWith(RevertReason.InsufficientAllowance);
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable:max-file-line-count
|
|
// tslint:enable:no-unnecessary-type-assertion
|