Skip to content

Commit

Permalink
add batch burning
Browse files Browse the repository at this point in the history
  • Loading branch information
thurendous committed Sep 12, 2024
1 parent 945b592 commit abc022a
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 1 deletion.
29 changes: 28 additions & 1 deletion src/AmbassadorNft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ contract AmbassadorNft is ERC1155, AccessControl, ERC1155Burnable {
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

/// @dev Initializes the contract by setting the roles, which including the DEFAULT_ADMIN_ROLE, MINTER_ROLE, BURNER_ROLE, and URI_SETTER_ROLE.
/// @param defaultAdmin The address of the default admin.
/// @param minter The address of the minter.
/// @param burner The address of the burner.
/// @param uriSetter The address of the URI setter.
constructor(address defaultAdmin, address minter, address burner, address uriSetter) ERC1155("") {
if (defaultAdmin == address(0)) revert DefaultAdminCannotBeZero();
_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
Expand All @@ -24,14 +28,29 @@ contract AmbassadorNft is ERC1155, AccessControl, ERC1155Burnable {
_grantRole(URI_SETTER_ROLE, uriSetter);
}

/// @notice Set the URI of the token.
/// @dev This function can only be called by accounts with the URI_SETTER_ROLE.
/// @param newuri The new URI to set.
function setURI(string memory newuri) public onlyRole(URI_SETTER_ROLE) {
_setURI(newuri);
}

/// @notice Mint the token to the specified account.
/// @dev This function can only be called by accounts with the MINTER_ROLE.
/// @param account The address to mint the token to.
/// @param id The id of the token to mint.
/// @param amount The amount of the token to mint.
/// @param data Additional data with no specified format.
function mint(address account, uint256 id, uint256 amount, bytes memory data) public onlyRole(MINTER_ROLE) {
_mint(account, id, amount, data);
}

/// @notice Mint the token to the specified account.
/// @dev This function can only be called by accounts with the MINTER_ROLE.
/// @param to The address to mint the token to.
/// @param ids The ids of the token to mint.
/// @param amounts The amounts of the token to mint.
/// @param data Additional data with no specified format.
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyRole(MINTER_ROLE)
Expand All @@ -50,7 +69,15 @@ contract AmbassadorNft is ERC1155, AccessControl, ERC1155Burnable {
_burn(account, id, amount);
}

// The following functions are overrides required by Solidity.
/// @notice Batch burning is not supported.
function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual override onlyRole(BURNER_ROLE) {
_burnBatch(account, ids, values);
}

/// @notice Check if the contract supports the interface.
/// @dev This function is required by Solidity.
/// @param interfaceId The interface ID to check.
/// @return True if the interface is supported, false otherwise.
function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}
Expand Down
116 changes: 116 additions & 0 deletions test/unit/AmbassadorNft.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ pragma solidity 0.8.24;

import {Test, console} from "forge-std/Test.sol";

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {AmbassadorNft} from "src/AmbassadorNft.sol";
import {IERC1155, IERC1155MetadataURI, ERC165} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract AmbassadorTest is Test {
// instances
Expand Down Expand Up @@ -54,6 +56,14 @@ contract AmbassadorTest is Test {
assertTrue(ambassadorNft.hasRole(ambassadorNft.URI_SETTER_ROLE(), uriSetter));
}

///
/// test the constructor validation
///
function testConstructorWillRevertIfAdminIsZero() public {
vm.expectRevert();
new AmbassadorNft(address(0), minter, burner, uriSetter);
}

///
/// test the minting process of tokens
///
Expand All @@ -71,6 +81,9 @@ contract AmbassadorTest is Test {
assertEq(ambassadorNft.balanceOf(minter, 5), 1);
}

///
/// test the minting process of tokens by non-minter
///
function testMintingTokenWillFailByNonMinter() public {
// mint some tokens
vm.expectRevert();
Expand All @@ -81,6 +94,9 @@ contract AmbassadorTest is Test {
ambassadorNft.mint(blueUser, 2, 1, "");
}

///
/// test the minting process of tokens by new minter
///
function testMintingTokenWillSucceedByNewMinter() public {
// when non-minter mint some tokens
vm.startPrank(blackUser);
Expand Down Expand Up @@ -129,6 +145,9 @@ contract AmbassadorTest is Test {
assertEq(ambassadorNft.balanceOf(minter, 5), 0);
}

///
/// test the burning process of tokens by non-burner
///
function testBurningTokenWillFailByNonBurner() public {
// mint some tokens
vm.startPrank(minter);
Expand All @@ -146,6 +165,9 @@ contract AmbassadorTest is Test {
ambassadorNft.burn(blueUser, 2, 1);
}

///
/// test the burning process of tokens by new burner
///
function testBurningTokenWillSucceedByNewBurner() public {
// mint some tokens
vm.startPrank(minter);
Expand All @@ -171,10 +193,65 @@ contract AmbassadorTest is Test {
assertEq(ambassadorNft.balanceOf(blueUser, 2), 0);
}

function testBurnBatchCanBeCalledByBurner() public {
// prepare test data
address testAccount = address(3);
uint256[] memory ids = new uint256[](2);
ids[0] = 3;
ids[1] = 4;
uint256[] memory values = new uint256[](2);
values[0] = 1;
values[1] = 12;
uint256[] memory burningValues = new uint256[](2);
burningValues[0] = 1;
burningValues[1] = 11;

// mint some tokens
vm.startPrank(minter);
ambassadorNft.mintBatch(testAccount, ids, values, "");
vm.stopPrank();

// try to call burnBatch
vm.startPrank(burner);
ambassadorNft.burnBatch(testAccount, ids, burningValues);
vm.stopPrank();

// check the balances
assertEq(ambassadorNft.balanceOf(testAccount, 3), 0);
assertEq(ambassadorNft.balanceOf(testAccount, 4), 1);
}

function testBurnBatchWillFailByNonBurner() public {
// prepare test data
address testAccount = address(3);
uint256[] memory ids = new uint256[](2);
ids[0] = 3;
ids[1] = 4;
uint256[] memory values = new uint256[](2);
values[0] = 1;
values[1] = 12;
uint256[] memory burningValues = new uint256[](2);
burningValues[0] = 1;
burningValues[1] = 11;

// mint some tokens
vm.startPrank(minter);
ambassadorNft.mintBatch(testAccount, ids, values, "");
vm.stopPrank();
// try to call burnBatch
vm.startPrank(blackUser);
vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, blackUser, ambassadorNft.BURNER_ROLE()));
ambassadorNft.burnBatch(testAccount, ids, burningValues);
vm.stopPrank();
}

/////////////////////////////////////////////////////
///////////// ERC1155 NORMAL TEST CASES /////////////
/////////////////////////////////////////////////////

///
/// test the setting process of URI
///
function testSetURI() public {
// set the URI
vm.prank(uriSetter);
Expand All @@ -200,6 +277,9 @@ contract AmbassadorTest is Test {
assertEq(ambassadorNft.balanceOf(user2, 1), 1);
}

///
/// test the balanceOfBatch function
///
function testBalanceOfBatchWorks() public view {
// check the balances
uint256[] memory ids = new uint256[](2);
Expand All @@ -215,6 +295,9 @@ contract AmbassadorTest is Test {
assertEq(balances[1], 1);
}

///
/// test the transfer process of tokens
///
function testTransferWillWork() public {
// check the balances
assertEq(ambassadorNft.balanceOf(user, 0), 1);
Expand All @@ -228,4 +311,37 @@ contract AmbassadorTest is Test {
assertEq(ambassadorNft.balanceOf(user, 0), 0);
assertEq(ambassadorNft.balanceOf(user3, 0), 1);
}

///
/// test the mintBatch function
///
function testMintBatchWillWork() public {
// construct the ids and amounts arrays
uint256[] memory ids = new uint256[](2);
uint256[] memory amounts = new uint256[](2);
ids[0] = 3;
amounts[0] = 1;
ids[1] = 4;
amounts[1] = 1;

// mint some tokens
vm.startPrank(minter);
ambassadorNft.mintBatch(user, ids, amounts, "");
vm.stopPrank();

// check the balances
assertEq(ambassadorNft.balanceOf(user, 3), 1);
assertEq(ambassadorNft.balanceOf(user, 4), 1);
}

///
/// test supportsInterface function
///
function testSupportsInterface() public view {
// check the supportsInterface function
assertEq(ambassadorNft.supportsInterface(type(IERC1155).interfaceId), true);
assertEq(ambassadorNft.supportsInterface(type(IERC1155MetadataURI).interfaceId), true);
assertEq(ambassadorNft.supportsInterface(type(IAccessControl).interfaceId), true);
assertEq(ambassadorNft.supportsInterface(type(ERC165).interfaceId), true);
}
}

0 comments on commit abc022a

Please sign in to comment.