Skip to content

Commit

Permalink
more coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanio committed Nov 16, 2023
1 parent ed195d5 commit f20a066
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 28 deletions.
7 changes: 4 additions & 3 deletions script/DeployAndRedeemTrait.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ contract DeployAndRedeemTrait is Script, Test {
address[] memory allowedTraitSetters = new address[](1);
allowedTraitSetters[0] = address(receiveToken);

// deploy the redeem token with the receive token as an allowed trait setter
// deploy the redeem token
ERC721ShipyardRedeemableTraitSetters redeemToken = new ERC721ShipyardRedeemableTraitSetters(
"DynamicTraitsRedeemToken",
"TEST",
allowedTraitSetters
"TEST"
);
// set the receive token as an allowed trait setter
redeemToken.setAllowedTraitSetters(allowedTraitSetters);

// configure the campaign.
OfferItem[] memory offer = new OfferItem[](1);
Expand Down
5 changes: 3 additions & 2 deletions src/lib/ERC7498NFTRedeemables.sol
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,13 @@ contract ERC7498NFTRedeemables is IERC165, IERC7498, DynamicTraits, RedeemablesE
// Decrement the trait by the trait value.
IERC7496(token).setTrait(identifier, traitRedemptions[i].traitKey, bytes32(newTraitValue));
} else if (substandard == 4) {
// Revert if the current trait value is not equal to the substandard value.
if (currentTraitValue != substandardValue) {
// Revert if the current trait value is not equal to the trait value.
if (currentTraitValue != traitValue) {
revert InvalidRequiredTraitValue(
token, identifier, traitKey, currentTraitValue, substandardValue
);
}
// No-op: substandard 4 has no set trait action.
}
}

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {ERC7498NFTRedeemables} from "../lib/ERC7498NFTRedeemables.sol";
import {ERC721ShipyardRedeemableOwnerMintable} from "./ERC721ShipyardRedeemableOwnerMintable.sol";

contract ERC721ShipyardRedeemableOwnerMintableWithoutInternalBurn is ERC721ShipyardRedeemableOwnerMintable {
constructor(string memory name_, string memory symbol_) ERC721ShipyardRedeemableOwnerMintable(name_, symbol_) {}

function _useInternalBurn() internal pure virtual override returns (bool) {
// For coverage of ERC7498NFTRedeemables._useInternalBurn, return default value of false.
return ERC7498NFTRedeemables._useInternalBurn();
}
}
14 changes: 9 additions & 5 deletions src/test/ERC721ShipyardRedeemableTraitSetters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@ contract ERC721ShipyardRedeemableTraitSetters is ERC721ShipyardRedeemableOwnerMi
// ERC721ShipyardRedeemable and ERC721SeaDropRedeemable contracts with onlyOwner on setAllowedTraitSetters().
address[] _allowedTraitSetters;

constructor(string memory name_, string memory symbol_, address[] memory allowedTraitSetters)
ERC721ShipyardRedeemableOwnerMintable(name_, symbol_)
{
_allowedTraitSetters = allowedTraitSetters;
}
constructor(string memory name_, string memory symbol_) ERC721ShipyardRedeemableOwnerMintable(name_, symbol_) {}

function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 value) public virtual override {
if (!_exists(tokenId)) revert TokenDoesNotExist();
Expand All @@ -26,6 +22,14 @@ contract ERC721ShipyardRedeemableTraitSetters is ERC721ShipyardRedeemableOwnerMi
DynamicTraits.setTrait(tokenId, traitKey, value);
}

function getAllowedTraitSetters() public view returns (address[] memory) {
return _allowedTraitSetters;
}

function setAllowedTraitSetters(address[] memory allowedTraitSetters) public onlyOwner {
_allowedTraitSetters = allowedTraitSetters;
}

function _requireAllowedTraitSetter() internal view {
// Allow the contract to call itself.
if (msg.sender == address(this)) return;
Expand Down
263 changes: 258 additions & 5 deletions test/ERC7498-TraitRedemption.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ contract ERC7498_TraitRedemption is BaseRedeemablesTest {
assertEq(traitValues[0], bytes32(uint256(1)));
}

function testErc721TraitRedemptionForErc721() public {
function testErc721TraitRedemptionSubstandardOneForErc721() public {
for (uint256 i; i < erc7498Tokens.length; i++) {
testRedeemable(
this.erc721TraitRedemptionSubstandardOneForErc721,
Expand All @@ -65,13 +65,14 @@ contract ERC7498_TraitRedemption is BaseRedeemablesTest {
}

function erc721TraitRedemptionSubstandardOneForErc721(RedeemablesContext memory context) public {
address[] memory allowedTraitSetters = new address[](1);
allowedTraitSetters[0] = address(context.erc7498Token);
address[] memory allowedTraitSetters = Solarray.addresses(address(context.erc7498Token));
ERC721ShipyardRedeemableTraitSetters redeemToken = new ERC721ShipyardRedeemableTraitSetters(
"",
"",
allowedTraitSetters
""
);
assertEq(redeemToken.getAllowedTraitSetters(), new address[](0));
redeemToken.setAllowedTraitSetters(allowedTraitSetters);
assertEq(redeemToken.getAllowedTraitSetters(), allowedTraitSetters);
_mintToken(address(redeemToken), tokenId);
TraitRedemption[] memory traitRedemptions = new TraitRedemption[](1);
// previous trait value (`substandardValue`) should be 0
Expand Down Expand Up @@ -144,4 +145,256 @@ contract ERC7498_TraitRedemption is BaseRedeemablesTest {
);
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);
}

function testErc721TraitRedemptionSubstandardTwoForErc721() public {
for (uint256 i; i < erc7498Tokens.length; i++) {
testRedeemable(
this.erc721TraitRedemptionSubstandardTwoForErc721,
RedeemablesContext({erc7498Token: IERC7498(erc7498Tokens[i])})
);
}
}

function erc721TraitRedemptionSubstandardTwoForErc721(RedeemablesContext memory context) public {
address[] memory allowedTraitSetters = Solarray.addresses(address(context.erc7498Token), address(this));
ERC721ShipyardRedeemableTraitSetters redeemToken = new ERC721ShipyardRedeemableTraitSetters(
"",
""
);
redeemToken.setAllowedTraitSetters(allowedTraitSetters);
_mintToken(address(redeemToken), tokenId);
redeemToken.setTrait(tokenId, traitKey, bytes32(uint256(1)));
TraitRedemption[] memory traitRedemptions = new TraitRedemption[](1);
// previous trait value should not be greater than 1 (`substandardValue`)
// new trait value should be 2 (adding traitValue of 1)
traitRedemptions[0] = TraitRedemption({
substandard: 2,
token: address(redeemToken),
traitKey: traitKey,
traitValue: bytes32(uint256(1)),
substandardValue: bytes32(uint256(1))
});
CampaignRequirements[] memory requirements = new CampaignRequirements[](
1
);
// consideration is empty
ConsiderationItem[] memory consideration = new ConsiderationItem[](0);
requirements[0] = CampaignRequirements({
offer: defaultCampaignOffer,
consideration: consideration,
traitRedemptions: traitRedemptions
});
CampaignParams memory params = CampaignParams({
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1000),
maxCampaignRedemptions: 5,
manager: address(this),
signer: address(0)
});
Campaign memory campaign = Campaign({params: params, requirements: requirements});
context.erc7498Token.createCampaign(campaign, "");

uint256[] memory considerationTokenIds;
uint256[] memory traitRedemptionTokenIds = Solarray.uint256s(tokenId);
bytes memory extraData = abi.encode(
1, // campaignId
0, // requirementsIndex
bytes32(0), // redemptionHash
traitRedemptionTokenIds,
uint256(0), // salt
bytes("") // signature
);
vm.expectEmit(true, true, true, true);
emit Redemption(1, 0, bytes32(0), considerationTokenIds, traitRedemptionTokenIds, address(this));
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);

bytes32 actualTraitValue = redeemToken.getTraitValue(tokenId, traitKey);
assertEq(bytes32(uint256(2)), actualTraitValue);
assertEq(receiveToken721.ownerOf(1), address(this));

// Redeeming one more time should fail with InvalidRequiredTraitValue since it is already 2.
vm.expectRevert(
abi.encodeWithSelector(
InvalidRequiredTraitValue.selector,
redeemToken,
tokenId,
traitKey,
bytes32(uint256(2)),
bytes32(uint256(1))
)
);
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);
}

function testErc721TraitRedemptionSubstandardThreeForErc721() public {
for (uint256 i; i < erc7498Tokens.length; i++) {
testRedeemable(
this.erc721TraitRedemptionSubstandardThreeForErc721,
RedeemablesContext({erc7498Token: IERC7498(erc7498Tokens[i])})
);
}
}

function erc721TraitRedemptionSubstandardThreeForErc721(RedeemablesContext memory context) public {
address[] memory allowedTraitSetters = Solarray.addresses(address(context.erc7498Token), address(this));
ERC721ShipyardRedeemableTraitSetters redeemToken = new ERC721ShipyardRedeemableTraitSetters(
"",
""
);
redeemToken.setAllowedTraitSetters(allowedTraitSetters);
_mintToken(address(redeemToken), tokenId);
redeemToken.setTrait(tokenId, traitKey, bytes32(uint256(5)));
TraitRedemption[] memory traitRedemptions = new TraitRedemption[](1);
// previous trait value should not be less than 4 (`substandardValue`)
// new trait value should be 4 (adding traitValue of 1)
traitRedemptions[0] = TraitRedemption({
substandard: 3,
token: address(redeemToken),
traitKey: traitKey,
traitValue: bytes32(uint256(1)),
substandardValue: bytes32(uint256(5))
});
CampaignRequirements[] memory requirements = new CampaignRequirements[](
1
);
// consideration is empty
ConsiderationItem[] memory consideration = new ConsiderationItem[](0);
requirements[0] = CampaignRequirements({
offer: defaultCampaignOffer,
consideration: consideration,
traitRedemptions: traitRedemptions
});
CampaignParams memory params = CampaignParams({
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1000),
maxCampaignRedemptions: 5,
manager: address(this),
signer: address(0)
});
Campaign memory campaign = Campaign({params: params, requirements: requirements});
context.erc7498Token.createCampaign(campaign, "");

uint256[] memory considerationTokenIds;
uint256[] memory traitRedemptionTokenIds = Solarray.uint256s(tokenId);
bytes memory extraData = abi.encode(
1, // campaignId
0, // requirementsIndex
bytes32(0), // redemptionHash
traitRedemptionTokenIds,
uint256(0), // salt
bytes("") // signature
);
vm.expectEmit(true, true, true, true);
emit Redemption(1, 0, bytes32(0), considerationTokenIds, traitRedemptionTokenIds, address(this));
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);

bytes32 actualTraitValue = redeemToken.getTraitValue(tokenId, traitKey);
assertEq(bytes32(uint256(4)), actualTraitValue);
assertEq(receiveToken721.ownerOf(1), address(this));

// Redeeming one more time should fail with InvalidRequiredTraitValue since it is now 4.
vm.expectRevert(
abi.encodeWithSelector(
InvalidRequiredTraitValue.selector,
redeemToken,
tokenId,
traitKey,
bytes32(uint256(4)),
bytes32(uint256(5))
)
);
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);
}

function testErc721TraitRedemptionSubstandardFourForErc721() public {
for (uint256 i; i < erc7498Tokens.length; i++) {
testRedeemable(
this.erc721TraitRedemptionSubstandardFourForErc721,
RedeemablesContext({erc7498Token: IERC7498(erc7498Tokens[i])})
);
}
}

function erc721TraitRedemptionSubstandardFourForErc721(RedeemablesContext memory context) public {
address[] memory allowedTraitSetters = Solarray.addresses(address(context.erc7498Token), address(this));
ERC721ShipyardRedeemableTraitSetters redeemToken = new ERC721ShipyardRedeemableTraitSetters(
"",
""
);
redeemToken.setAllowedTraitSetters(allowedTraitSetters);
_mintToken(address(redeemToken), tokenId);
redeemToken.setTrait(tokenId, traitKey, bytes32(uint256(4)));
TraitRedemption[] memory traitRedemptions = new TraitRedemption[](1);
// previous trait value should be the trait value
// trait value does not change in substandard 4
traitRedemptions[0] = TraitRedemption({
substandard: 4,
token: address(redeemToken),
traitKey: traitKey,
traitValue: bytes32(uint256(5)),
substandardValue: bytes32(0) // unused in substandard 4
});
CampaignRequirements[] memory requirements = new CampaignRequirements[](
1
);
// consideration is empty
ConsiderationItem[] memory consideration = new ConsiderationItem[](0);
requirements[0] = CampaignRequirements({
offer: defaultCampaignOffer,
consideration: consideration,
traitRedemptions: traitRedemptions
});
CampaignParams memory params = CampaignParams({
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1000),
maxCampaignRedemptions: 5,
manager: address(this),
signer: address(0)
});
Campaign memory campaign = Campaign({params: params, requirements: requirements});
context.erc7498Token.createCampaign(campaign, "");

uint256[] memory considerationTokenIds;
uint256[] memory traitRedemptionTokenIds = Solarray.uint256s(tokenId);
bytes memory extraData = abi.encode(
1, // campaignId
0, // requirementsIndex
bytes32(0), // redemptionHash
traitRedemptionTokenIds,
uint256(0), // salt
bytes("") // signature
);

// Redeeming should fail since the trait value does not match.
vm.expectRevert(
abi.encodeWithSelector(
InvalidRequiredTraitValue.selector,
redeemToken,
tokenId,
traitKey,
bytes32(uint256(4)),
bytes32(uint256(0))
)
);
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);

// Update the trait value, now it should match.
redeemToken.setTrait(tokenId, traitKey, bytes32(uint256(5)));

vm.expectEmit(true, true, true, true);
emit Redemption(1, 0, bytes32(0), considerationTokenIds, traitRedemptionTokenIds, address(this));
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);

bytes32 actualTraitValue = redeemToken.getTraitValue(tokenId, traitKey);
assertEq(bytes32(uint256(5)), actualTraitValue);
assertEq(receiveToken721.ownerOf(1), address(this));

// Redeeming one more time should succeed since it has not changed.
emit Redemption(1, 0, bytes32(0), considerationTokenIds, traitRedemptionTokenIds, address(this));
context.erc7498Token.redeem(considerationTokenIds, address(this), extraData);

actualTraitValue = redeemToken.getTraitValue(tokenId, traitKey);
assertEq(bytes32(uint256(5)), actualTraitValue);
assertEq(receiveToken721.ownerOf(2), address(this));
}
}
Loading

0 comments on commit f20a066

Please sign in to comment.