diff --git a/contracts/erc1155/test/erc1155_token.ts b/contracts/erc1155/test/erc1155_token.ts index a240c563e9..d556881749 100644 --- a/contracts/erc1155/test/erc1155_token.ts +++ b/contracts/erc1155/test/erc1155_token.ts @@ -38,8 +38,9 @@ describe('ERC1155Token', () => { // tokens & addresses let owner: string; let spender: string; + let delegatedSpender: string; let receiver: string; - let token: ERC1155MintableContract; + let erc1155Contract: ERC1155MintableContract; let erc1155Receiver: DummyERC1155ReceiverContract; let nonFungibleToken: BigNumber; let erc1155Wrapper: Erc1155Wrapper; @@ -54,8 +55,8 @@ describe('ERC1155Token', () => { before(async () => { // deploy erc1155 contract & receiver const accounts = await web3Wrapper.getAvailableAddressesAsync(); - [owner, spender] = accounts; - token = await ERC1155MintableContract.deployFrom0xArtifactAsync( + [owner, spender, delegatedSpender] = accounts; + erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync( artifacts.ERC1155Mintable, provider, txDefaults, @@ -67,7 +68,7 @@ describe('ERC1155Token', () => { ); receiver = erc1155Receiver.address; // create wrapper & mint erc1155 tokens - erc1155Wrapper = new Erc1155Wrapper(token, provider, owner); + erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, provider, owner); fungibleToken = await erc1155Wrapper.mintFungibleTokenAsync(spender, spenderInitialFungibleBalance); [, nonFungibleToken] = await erc1155Wrapper.mintNonFungibleTokenAsync(spender); }); @@ -170,7 +171,7 @@ describe('ERC1155Token', () => { const valueToTransfer = spenderInitialFungibleBalance.plus(1); // execute transfer await expectTransactionFailedAsync( - token.safeTransferFrom.sendTransactionAsync( + erc1155Contract.safeTransferFrom.sendTransactionAsync( spender, receiver, tokenToTransfer, @@ -193,7 +194,7 @@ describe('ERC1155Token', () => { ); // execute transfer await expectTransactionFailedAsync( - token.safeTransferFrom.sendTransactionAsync( + erc1155Contract.safeTransferFrom.sendTransactionAsync( spender, receiver, tokenToTransfer, @@ -339,7 +340,7 @@ describe('ERC1155Token', () => { const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)]; // execute transfer await expectTransactionFailedAsync( - token.safeBatchTransferFrom.sendTransactionAsync( + erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( spender, receiver, tokensToTransfer, @@ -362,7 +363,7 @@ describe('ERC1155Token', () => { ); // execute transfer await expectTransactionFailedAsync( - token.safeBatchTransferFrom.sendTransactionAsync( + erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( spender, receiver, tokensToTransfer, @@ -374,5 +375,113 @@ describe('ERC1155Token', () => { ); }); }); + 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 checkIsApprovedForAll = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); + expect(checkIsApprovedForAll).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 throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => { + // check approval not set + const checkIsApprovedForAll = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); + expect(checkIsApprovedForAll).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 + await expectTransactionFailedAsync( + erc1155Contract.safeTransferFrom.sendTransactionAsync( + spender, + receiver, + tokenToTransfer, + valueToTransfer, + receiverCallbackData, + { from: delegatedSpender }, + ), + 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 checkIsApprovedForAll = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); + expect(checkIsApprovedForAll).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 throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => { + // check approval not set + const checkIsApprovedForAll = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender); + expect(checkIsApprovedForAll).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 + await expectTransactionFailedAsync( + erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( + spender, + receiver, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + { from: delegatedSpender }, + ), + RevertReason.InsufficientAllowance, + ); + }); + }); }); // tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/erc1155/test/utils/erc1155_wrapper.ts b/contracts/erc1155/test/utils/erc1155_wrapper.ts index 5ccc96abb6..64d21cc316 100644 --- a/contracts/erc1155/test/utils/erc1155_wrapper.ts +++ b/contracts/erc1155/test/utils/erc1155_wrapper.ts @@ -32,10 +32,12 @@ export class Erc1155Wrapper { token: BigNumber, value: BigNumber, callbackData: string = '0x', + delegatedSpender: string = '' ): Promise { + const spender = _.isEmpty(delegatedSpender) ? from : delegatedSpender; const tx = await this._logDecoder.getTxWithDecodedLogsAsync( await this._erc1155Contract.safeTransferFrom.sendTransactionAsync(from, to, token, value, callbackData, { - from, + from: spender, }), ); return tx; @@ -46,7 +48,9 @@ export class Erc1155Wrapper { tokens: BigNumber[], values: BigNumber[], callbackData: string = '0x', + delegatedSpender: string = '' ): Promise { + const spender = _.isEmpty(delegatedSpender) ? from : delegatedSpender; const tx = await this._logDecoder.getTxWithDecodedLogsAsync( await this._erc1155Contract.safeBatchTransferFrom.sendTransactionAsync( from, @@ -54,7 +58,7 @@ export class Erc1155Wrapper { tokens, values, callbackData, - { from }, + { from: spender }, ), ); return tx; @@ -98,6 +102,23 @@ export class Erc1155Wrapper { const nftId = token.plus(1); return [token, nftId]; } + public async setApprovalForAllAsync(owner: string, beneficiary: string, isApproved: boolean): Promise { + const tx = await this._logDecoder.getTxWithDecodedLogsAsync( + await this._erc1155Contract.setApprovalForAll.sendTransactionAsync( + beneficiary, + isApproved, + { from: owner }, + ), + ); + return tx; + } + public async isApprovedForAllAsync(owner: string, beneficiary: string): Promise { + const isApprovedForAll = await this._erc1155Contract.isApprovedForAll.callAsync( + owner, + beneficiary, + ); + return isApprovedForAll; + } public async assertBalancesAsync( owners: string[], tokens: BigNumber[], diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 62f7d93970..65fe752365 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -3,7 +3,7 @@ "version": "2.1.2", "changes": [ { - "note": "Added `Uint256Underflow` and `TransferRejected` revert reasons", + "note": "Added `Uint256Underflow`, `TransferRejected`, and `InsufficientAllowance` revert reasons", "pr": 1657 } ] diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 35143dd026..55d36a0f93 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -261,6 +261,7 @@ export enum RevertReason { TxNotFullyConfirmed = 'TX_NOT_FULLY_CONFIRMED', TimeLockIncomplete = 'TIME_LOCK_INCOMPLETE', TransferRejected = 'TRANSFER_REJECTED', + InsufficientAllowance = 'INSUFFICIENT_ALLOWANCE', // DutchAuction AuctionInvalidAmount = 'INVALID_AMOUNT', AuctionExpired = 'AUCTION_EXPIRED',