diff --git a/contracts/token/NFTBase.sol b/contracts/token/NFTBase.sol index 9edde4fb..a526e1f2 100644 --- a/contracts/token/NFTBase.sol +++ b/contracts/token/NFTBase.sol @@ -243,4 +243,17 @@ abstract contract NFTBase is IERC2981Upgradeable, CommonOwnable, AccessControlUp function nftType() external pure virtual returns (bytes32) { return keccak256(''); } + + error BatchError(bytes innerError); + + function _getRevertMsg(bytes memory _returnData) internal pure{ + // If the _res length is less than 68, then + // the transaction failed with custom error or silently (without a revert message) + if (_returnData.length < 68) revert BatchError(_returnData); + + // Slice the sighash. + // solhint-disable-next-line + assembly { _returnData := add(_returnData, 0x04) } + revert(abi.decode(_returnData, (string))); // All that remains is the revert string + } } diff --git a/contracts/token/erc1155/NFT1155SubscriptionUpgradeable.sol b/contracts/token/erc1155/NFT1155SubscriptionUpgradeable.sol index 5dd163d3..bbe1815e 100644 --- a/contracts/token/erc1155/NFT1155SubscriptionUpgradeable.sol +++ b/contracts/token/erc1155/NFT1155SubscriptionUpgradeable.sol @@ -166,12 +166,25 @@ contract NFT1155SubscriptionUpgradeable is NFT1155Upgradeable { function burnBatchFromHolders( address[] memory from, uint256[] memory ids, - uint256[] memory amounts - ) external { - require(ids.length == amounts.length, 'burnBatch: lengths do not match'); - require(ids.length == from.length, 'burnBatch: lengths do not match'); - for (uint i = 0; i < ids.length; i++) { - burn(from[i], ids[i], amounts[i]); + uint256[] memory amounts, + bool revertOnFail + ) external returns (bool[] memory successes, bytes[] memory results) { + uint256 _length = ids.length; + require(_length == amounts.length && _length == from.length, 'lengths do not match'); + + successes = new bool[](_length); + results = new bytes[](_length); + + for (uint256 i = 0; i < _length; ++i) { + // solhint-disable-next-line + (bool success, bytes memory result) = address(this).delegatecall(abi.encodeWithSignature('burn(address,uint256,uint256)', from[i], ids[i], amounts[i])); + + if (!success && revertOnFail) { + _getRevertMsg(result); + } + successes[i] = success; + results[i] = result; } + return (successes, results); } } diff --git a/contracts/token/erc1155/NFT1155SubscriptionWithoutBlocks.sol b/contracts/token/erc1155/NFT1155SubscriptionWithoutBlocks.sol index 9588b282..40a650d6 100644 --- a/contracts/token/erc1155/NFT1155SubscriptionWithoutBlocks.sol +++ b/contracts/token/erc1155/NFT1155SubscriptionWithoutBlocks.sol @@ -75,41 +75,7 @@ contract NFT1155SubscriptionWithoutBlocks is NFT1155Upgradeable { */ function balanceOf(address account, uint256 tokenId) public view virtual override returns (uint256) { return super.balanceOf(account, tokenId); -// bytes32 _key = _getTokenKey(account, tokenId); -// uint256 _amountBurned; -// uint256 _amountMinted; -// for (uint index = 0; index < _tokens[_key].length; index++) { -// if (_tokens[_key][index].mintBlock > 0 && -// (_tokens[_key][index].expirationBlock == 0 || _tokens[_key][index].expirationBlock > block.number)) { -// if (_tokens[_key][index].isMintOps) -// _amountMinted += _tokens[_key][index].amountMinted; -// else -// _amountBurned += _tokens[_key][index].amountMinted; -// } -// } -// -// if (_amountBurned >= _amountMinted) -// return 0; -// else -// return _amountMinted - _amountBurned; - } - -// function whenWasMinted(address owner, uint256 tokenId) public view returns (uint256[] memory) { -// bytes32 _key = _getTokenKey(owner, tokenId); -// uint256[] memory _whenMinted = new uint256[](_tokens[_key].length); -// for (uint index = 0; index < _tokens[_key].length; index++) { -// _whenMinted[index] = _tokens[_key][index].mintBlock; -// } -// return _whenMinted; -// } -// -// function getMintedEntries(address owner, uint256 tokenId) public view returns (MintedTokens[] memory) { -// return _tokens[_getTokenKey(owner, tokenId)]; -// } -// -// function _getTokenKey(address account, uint256 tokenId) internal pure returns (bytes32) { -// return keccak256(abi.encode(account, tokenId)); -// } + } function mintBatch( address to, @@ -117,7 +83,7 @@ contract NFT1155SubscriptionWithoutBlocks is NFT1155Upgradeable { uint256[] memory amounts, bytes memory data ) external { - require(ids.length == amounts.length, 'mintBatch: lengths do not match'); + require(ids.length == amounts.length, 'lengths do not match'); for (uint i = 0; i < ids.length; i++) { mint(to, ids[i], amounts[i], data); } @@ -128,7 +94,7 @@ contract NFT1155SubscriptionWithoutBlocks is NFT1155Upgradeable { uint256[] memory ids, uint256[] memory amounts ) external { - require(ids.length == amounts.length, 'burnBatch: lengths do not match'); + require(ids.length == amounts.length, 'lengths do not match'); for (uint i = 0; i < ids.length; i++) { burn(from, ids[i], amounts[i]); } @@ -137,12 +103,26 @@ contract NFT1155SubscriptionWithoutBlocks is NFT1155Upgradeable { function burnBatchFromHolders( address[] memory from, uint256[] memory ids, - uint256[] memory amounts - ) external { - require(ids.length == amounts.length, 'burnBatch: lengths do not match'); - require(ids.length == from.length, 'burnBatch: lengths do not match'); - for (uint i = 0; i < ids.length; i++) { - burn(from[i], ids[i], amounts[i]); + uint256[] memory amounts, + bool revertOnFail + ) external returns (bool[] memory successes, bytes[] memory results) { + uint256 _length = ids.length; + require(_length == amounts.length && _length == from.length, 'lengths do not match'); + + successes = new bool[](_length); + results = new bytes[](_length); + + for (uint256 i = 0; i < _length; ++i) { + // solhint-disable-next-line + (bool success, bytes memory result) = address(this).delegatecall(abi.encodeWithSignature('burn(address,uint256,uint256)', from[i], ids[i], amounts[i])); + + if (!success && revertOnFail) { + _getRevertMsg(result); + } + successes[i] = success; + results[i] = result; } + return (successes, results); } + } diff --git a/package.json b/package.json index d41c1bfd..bf6e5d8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nevermined-io/contracts", - "version": "3.5.9", + "version": "3.5.10", "description": "Nevermined implementation of Nevermined in Solidity", "bugs": { "url": "https://github.com/nevermined-io/contracts/issues" diff --git a/test/unit/token/NFT1155SubscriptionWithoutBlocks.Test.js b/test/unit/token/NFT1155SubscriptionWithoutBlocks.Test.js index 7d9a5b27..3459c0fb 100644 --- a/test/unit/token/NFT1155SubscriptionWithoutBlocks.Test.js +++ b/test/unit/token/NFT1155SubscriptionWithoutBlocks.Test.js @@ -25,7 +25,8 @@ contract('NFT1155 Subscription', (accounts) => { deployer, minter, account1, - account2 + account2, + account3 ] = accounts let nft @@ -154,7 +155,9 @@ contract('NFT1155 Subscription', (accounts) => { assert.strictEqual(balances[0], 23) assert.strictEqual(balances[1], 15) - await nft.burnBatchFromHolders([account2, account2], [tokenId3, tokenId4], [22, 14], { from: minter }) + await nft.mintBatch(account3, [tokenId3, tokenId4], [50, 50], data, { from: minter }) + + await nft.burnBatchFromHolders([account2, account3, account2], [tokenId3, tokenId3, tokenId4], [22, 10, 14], { from: minter }) balance = new BigNumber(await nft.balanceOf(account2, tokenId3)) balance2 = new BigNumber(await nft.balanceOf(account2, tokenId4)) assert.strictEqual(balance.toNumber(), 1)