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 dbc1165
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 33 deletions.
81 changes: 50 additions & 31 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ AddEntryTest:test_AddEntry() (gas: 44392)
AddEntryTest:test_RevertWhen_EntryAlreadyExists() (gas: 42644)
AddEntryTest:test_RevertWhen_InvalidAddress() (gas: 25133)
AddEntryTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 14827)
CollectibleV1Test:test_Deployment() (gas: 36386)
CollectibleV1Test:test_Deployment() (gas: 36430)
CommunityERC20Test:test_Deployment() (gas: 35198)
CommunityTokenDeployerTest:test_Deployment() (gas: 14805)
CommunityVaultBaseERC20Test:test_Deployment() (gas: 10436)
CommunityVaultBaseERC721Test:test_Deployment() (gas: 10436)
CommunityVaultTest:test_Deployment() (gas: 10436)
CreateTest:test_Create() (gas: 2269916)
CreateTest:test_Create() (gas: 2568994)
CreateTest:test_Create() (gas: 2376601)
CreateTest:test_Create() (gas: 2670783)
CreateTest:test_RevertWhen_InvalidOwnerTokenAddress() (gas: 15523)
CreateTest:test_RevertWhen_InvalidReceiverAddress() (gas: 15656)
CreateTest:test_RevertWhen_InvalidSignerPublicKey() (gas: 17057)
Expand All @@ -17,34 +19,38 @@ CreateTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 16421)
CreateTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 16524)
DeployContracts:test() (gas: 120)
DeployOwnerAndMasterToken:test() (gas: 120)
DeployTest:test_Deploy() (gas: 4911563)
DeployTest:test_Deploy() (gas: 5119997)
DeployTest:test_Deployment() (gas: 14947)
DeployTest:test_RevertWhen_AlreadyDeployed() (gas: 4907793)
DeployTest:test_RevertWhen_AlreadyDeployed() (gas: 5116273)
DeployTest:test_RevertWhen_InvalidCommunityAddress() (gas: 51385)
DeployTest:test_RevertWhen_InvalidDeployerAddress() (gas: 55272)
DeployTest:test_RevertWhen_InvalidDeploymentSignature() (gas: 65617)
DeployTest:test_RevertWhen_InvalidSignerPublicKey() (gas: 53433)
DeployTest:test_RevertWhen_InvalidTokenMetadata() (gas: 2694728)
DeployTest:test_RevertWhen_InvalidTokenMetadata() (gas: 2796562)
DeploymentTest:test_Deployment() (gas: 14671)
DeploymentTest:test_Deployment() (gas: 14671)
DeploymentTest:test_Deployment() (gas: 17295)
GetEntryTest:test_ReturnZeroAddressIfEntryDoesNotExist() (gas: 11906)
MintToTest:test_Deployment() (gas: 35220)
MintToTest:test_Deployment() (gas: 36386)
MintToTest:test_Deployment() (gas: 83220)
MintToTest:test_MintTo() (gas: 178063)
MintToTest:test_MintTo() (gas: 526242)
MintToTest:test_RevertWhen_AddressesAndAmountsAreNotEqualLength() (gas: 29673)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 20653)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 511039)
MintToTest:test_RevertWhen_MaxSupplyReached() (gas: 134799)
MintToTest:test_RevertWhen_SenderIsNotOwner() (gas: 31544)
OwnerTokenTest:test_Deployment() (gas: 83220)
RemoteBurnTest:test_Deployment() (gas: 36386)
RemoteBurnTest:test_Deployment() (gas: 83242)
RemoteBurnTest:test_RemoteBurn() (gas: 459164)
RemoteBurnTest:test_RevertWhen_RemoteBurn() (gas: 14768)
RemoteBurnTest:test_RevertWhen_SenderIsNotOwner() (gas: 20379)
MintToTest:test_Deployment() (gas: 36430)
MintToTest:test_Deployment() (gas: 83308)
MintToTest:test_MintTo() (gas: 178018)
MintToTest:test_MintTo() (gas: 525837)
MintToTest:test_RevertWhen_AddressesAndAmountsAreNotEqualLength() (gas: 29628)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 20630)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 510949)
MintToTest:test_RevertWhen_MaxSupplyReached() (gas: 134754)
MintToTest:test_RevertWhen_SenderIsNotOwner() (gas: 31454)
OwnerTokenTest:test_Deployment() (gas: 83308)
RemoteBurnTest:test_Deployment() (gas: 36430)
RemoteBurnTest:test_Deployment() (gas: 83330)
RemoteBurnTest:test_RemoteBurn() (gas: 459089)
RemoteBurnTest:test_RevertWhen_RemoteBurn() (gas: 14783)
RemoteBurnTest:test_RevertWhen_SenderIsNotOwner() (gas: 20349)
SafeBatchTransferFromTest:test_Deployment() (gas: 36524)
SafeBatchTransferFromTest:test_RevertWhen_NotAuthorized() (gas: 18993)
SafeBatchTransferFromTest:test_RevertWhen_ReceiverAddressIsZero() (gas: 506871)
SafeBatchTransferFromTest:test_SafeBatchTransferFrom() (gas: 423357)
SetCommunityTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12941)
SetCommunityTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12482)
SetCommunityTokenDeployerAddressTest:test_SetCommunityTokenDeployerAddress() (gas: 22808)
Expand All @@ -57,22 +63,35 @@ SetMasterTokenFactoryAddressTest:test_RevertWhen_InvalidTokenFactoryAddress() (g
SetMasterTokenFactoryAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12465)
SetMasterTokenFactoryAddressTest:test_SetOwnerTokenFactoryAddress() (gas: 22861)
SetMaxSupplyTest:test_Deployment() (gas: 35198)
SetMaxSupplyTest:test_Deployment() (gas: 83242)
SetMaxSupplyTest:test_RevertWhen_CalledBecauseMaxSupplyIsLocked() (gas: 14327)
SetMaxSupplyTest:test_RevertWhen_MaxSupplyLowerThanTotalSupply() (gas: 163641)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 12527)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 21445)
SetMaxSupplyTest:test_SetMaxSupply() (gas: 23998)
SetMaxSupplyTest:test_Deployment() (gas: 83330)
SetMaxSupplyTest:test_RevertWhen_CalledBecauseMaxSupplyIsLocked() (gas: 14304)
SetMaxSupplyTest:test_RevertWhen_MaxSupplyLowerThanTotalSupply() (gas: 163551)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 12459)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 21355)
SetMaxSupplyTest:test_SetMaxSupply() (gas: 23953)
SetOwnerTokenFactoryAddressTest:test_Deployment() (gas: 14805)
SetOwnerTokenFactoryAddressTest:test_RevertWhen_InvalidTokenFactoryAddress() (gas: 12970)
SetOwnerTokenFactoryAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12443)
SetOwnerTokenFactoryAddressTest:test_SetOwnerTokenFactoryAddress() (gas: 22840)
SetSignerPublicKeyTest:test_Deployment() (gas: 83220)
SetSignerPublicKeyTest:test_RevertWhen_SenderIsNotOwner() (gas: 13222)
SetSignerPublicKeyTest:test_SetSignerPublicKey() (gas: 24163)
SetSignerPublicKeyTest:test_Deployment() (gas: 83308)
SetSignerPublicKeyTest:test_RevertWhen_SenderIsNotOwner() (gas: 13154)
SetSignerPublicKeyTest:test_SetSignerPublicKey() (gas: 24162)
SetTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12964)
SetTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12964)
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
TransferERC20ByAdminTest:test_AdminCanTransferERC20() (gas: 84406)
TransferERC20ByAdminTest:test_Deployment() (gas: 10556)
TransferERC20ByAdminTest:test_LengthMismatch() (gas: 31872)
TransferERC20ByAdminTest:test_NoRecipients() (gas: 25198)
TransferERC20ByAdminTest:test_TransferAmountZero() (gas: 61686)
TransferERC20ByNonAdminTest:test_Deployment() (gas: 10458)
TransferERC20ByNonAdminTest:test_revertIfCalledByNonAdmin() (gas: 35570)
TransferERC721ByAdminTest:test_AdminCanTransferERC721() (gas: 107114)
TransferERC721ByAdminTest:test_Deployment() (gas: 10556)
TransferERC721ByAdminTest:test_LengthMismatch() (gas: 31875)
TransferERC721ByAdminTest:test_NoRecipients() (gas: 25213)
TransferERC721ByNonAdminTest:test_Deployment() (gas: 10458)
TransferERC721ByNonAdminTest:test_RevertIfCalledByNonAdmin() (gas: 35563)
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 dbc1165

Please sign in to comment.