Skip to content

Commit

Permalink
feat(CollectibleV1): add safeBatchTransferFrom capabilities
Browse files Browse the repository at this point in the history
This is to allow batch transfers of community collectibles as discussed
in #41.

Closes #41
  • Loading branch information
0x-r4bbit committed Feb 20, 2024
1 parent 986bbaa commit bc5eb99
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 2 deletions.
42 changes: 40 additions & 2 deletions contracts/tokens/BaseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ abstract contract BaseToken is Context, ERC721Enumerable, CommunityOwnable {
error BaseToken_MaxSupplyReached();
error BaseToken_NotRemoteBurnable();
error BaseToken_NotTransferable();
error BaseToken_NotAuthorized();

/// @notice Emits a custom mint event for Status applications to listen to
/// @dev This is doubling the {Transfer} event from ERC721 but we need to emit this
Expand All @@ -25,6 +26,16 @@ abstract contract BaseToken is Context, ERC721Enumerable, CommunityOwnable {
/// @param tokenId The token ID that was minted
event StatusMint(address indexed from, address indexed to, uint256 indexed tokenId);

/// @notice Emits a batch transfer event
/// @dev This is similar to {TransferBatch} from ERC1155 but we don't need
/// the `amounts` argument. Also, this is being emitted after multiple
/// `safeTransferFrom()` calls, which will emit a `{Transfer}` event themselves.
/// @param operator The address that initiated the batch transfer
/// @param from The address that sent the tokens
/// @param to The address that received the tokens
/// @param ids The list of token IDs that were transferred
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids);

// State variables

Counters.Counter private _tokenIdTracker;
Expand Down Expand Up @@ -134,8 +145,8 @@ abstract contract BaseToken is Context, ERC721Enumerable, CommunityOwnable {
}

/**
* @notice
* @dev
* @notice Overrides `ERC721Enumerable` to add a check for transferability
* @dev See {ERC721-_beforeTokenTransfer}.
*/
function _beforeTokenTransfer(
address from,
Expand All @@ -153,5 +164,32 @@ abstract contract BaseToken is Context, ERC721Enumerable, CommunityOwnable {
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
}

/**
* @dev See {IERC1155-safeBatchTransferFrom}.
*/
function safeBatchTransferFrom(address from, address to, uint256[] memory ids, bytes memory data) public virtual {
if (from != _msgSender() && !isApprovedForAll(from, _msgSender())) {
revert BaseToken_NotAuthorized();
}
_safeBatchTransferFrom(from, to, ids, data);
}

function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
bytes memory data
)
internal
virtual
{
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
_safeTransfer(from, to, id, data);
}
address operator = _msgSender();
emit TransferBatch(operator, from, to, ids);
}

// Private functions
}
47 changes: 47 additions & 0 deletions test/CollectibleV1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,50 @@ contract RemoteBurnTest is CollectibleV1Test {
assertEq(collectibleV1.totalSupply(), maxSupply - 1);
}
}

contract SafeBatchTransferFromTest is CollectibleV1Test {
function setUp() public virtual override {
CollectibleV1Test.setUp();
}

function test_RevertWhen_NotAuthorized() public {
uint256[] memory ids = new uint256[](1);
ids[0] = 0;
vm.expectRevert(BaseToken.BaseToken_NotAuthorized.selector);
collectibleV1.safeBatchTransferFrom(accounts[0], accounts[1], ids, "");
}

function test_RevertWhen_ReceiverAddressIsZero() public {
// ensure sender owns a token
vm.prank(deployer);
collectibleV1.mintTo(accounts);

uint256[] memory ids = new uint256[](1);
ids[0] = 0;

vm.prank(accounts[0]);
vm.expectRevert(bytes("ERC721: transfer to the zero address"));
collectibleV1.safeBatchTransferFrom(accounts[0], address(0), ids, "");
}

function test_SafeBatchTransferFrom() public {
// ensure sender owns a token
vm.prank(deployer);
address[] memory sameAddresses = new address[](3);
sameAddresses[0] = accounts[0];
sameAddresses[1] = accounts[0];
sameAddresses[2] = accounts[0];
collectibleV1.mintTo(sameAddresses);

uint256[] memory ids = new uint256[](3);
ids[0] = 0;
ids[1] = 1;
ids[2] = 2;

vm.prank(accounts[0]);
collectibleV1.safeBatchTransferFrom(accounts[0], accounts[1], ids, "");

assertEq(collectibleV1.balanceOf(accounts[0]), 0);
assertEq(collectibleV1.balanceOf(accounts[1]), 3);
}
}

0 comments on commit bc5eb99

Please sign in to comment.