Skip to content

Commit

Permalink
Merge pull request #15 from ProjectOpenSea/stephan/dynamic-traits
Browse files Browse the repository at this point in the history
add trait redemptions to ERC7498Redeemables
  • Loading branch information
ryanio authored Nov 2, 2023
2 parents 432be56 + 6860365 commit 22e1e6b
Show file tree
Hide file tree
Showing 24 changed files with 704 additions and 333 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Run Forge build
run: |
forge --version
forge build --sizes
forge build
id: build

- name: Run Forge tests
Expand Down
8 changes: 3 additions & 5 deletions script/DeployAndConfigure1155Receive.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {Test} from "forge-std/Test.sol";
import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {CampaignParams, CampaignRequirements} from "../src/lib/RedeemablesStructs.sol";
import {BURN_ADDRESS} from "../src/lib/RedeemablesConstants.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {ERC721OwnerMintable} from "../src/test/ERC721OwnerMintable.sol";
import {ERC1155ShipyardRedeemableMintable} from "../src/extensions/ERC1155ShipyardRedeemableMintable.sol";

contract DeployAndConfigure1155Receive is Script, Test {
address constant _BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

function run() external {
vm.startBroadcast();

Expand Down Expand Up @@ -51,7 +50,7 @@ contract DeployAndConfigure1155Receive is Script, Test {
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(_BURN_ADDRESS)
recipient: payable(BURN_ADDRESS)
});

CampaignRequirements[] memory requirements = new CampaignRequirements[](
Expand All @@ -68,8 +67,7 @@ contract DeployAndConfigure1155Receive is Script, Test {
maxCampaignRedemptions: 1_000,
manager: msg.sender
});
uint256 campaignId =
receiveToken.createCampaign(params, "ipfs://QmQjubc6guHReNW5Es5ZrgDtJRwXk2Aia7BkVoLJGaCRqP");
receiveToken.createCampaign(params, "ipfs://QmQjubc6guHReNW5Es5ZrgDtJRwXk2Aia7BkVoLJGaCRqP");

// To test updateCampaign, update to proper start/end times.
params.startTime = uint32(block.timestamp);
Expand Down
5 changes: 2 additions & 3 deletions script/DeployAndConfigureExampleCampaign.s.sol.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {RedeemableContractOfferer} from "../src/RedeemableContractOfferer.sol";
import {CampaignParams} from "../src/lib/RedeemablesStructs.sol";
import {BURN_ADDRESS} from "../src/lib/RedeemablesConstants.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {TestERC721} from "../test/utils/mocks/TestERC721.sol";

Expand All @@ -16,8 +17,6 @@ contract DeployAndConfigureExampleCampaign is Script {
address conduit = 0x1E0049783F008A0085193E00003D00cd54003c71;
bytes32 conduitKey = 0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000;

address constant _BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

function run() external {
vm.startBroadcast();

Expand Down Expand Up @@ -49,7 +48,7 @@ contract DeployAndConfigureExampleCampaign is Script {
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(_BURN_ADDRESS)
recipient: payable(BURN_ADDRESS)
});

CampaignParams memory params = CampaignParams({
Expand Down
11 changes: 6 additions & 5 deletions script/DeployAndRedeemTokens-CampaignOnReceiveToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {Test} from "forge-std/Test.sol";
import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {CampaignParams, CampaignRequirements} from "../src/lib/RedeemablesStructs.sol";
import {BURN_ADDRESS} from "../src/lib/RedeemablesConstants.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {ERC721OwnerMintable} from "../src/test/ERC721OwnerMintable.sol";
import {ERC721ShipyardRedeemableMintable} from "../src/extensions/ERC721ShipyardRedeemableMintable.sol";

contract DeployAndRedeemTokens_CampaignOnReceiveToken is Script, Test {
address constant _BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

function run() external {
vm.startBroadcast();

Expand All @@ -37,7 +36,7 @@ contract DeployAndRedeemTokens_CampaignOnReceiveToken is Script, Test {
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(_BURN_ADDRESS)
recipient: payable(BURN_ADDRESS)
});

CampaignRequirements[] memory requirements = new CampaignRequirements[](
Expand All @@ -62,8 +61,10 @@ contract DeployAndRedeemTokens_CampaignOnReceiveToken is Script, Test {

// Let's redeem them!
uint256 requirementsIndex = 0;
bytes32 redemptionHash = bytes32(0);
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash);
bytes32 redemptionHash;
uint256 salt;
bytes memory signature;
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash, salt, signature);

uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = 1;
Expand Down
11 changes: 6 additions & 5 deletions script/DeployAndRedeemTokens.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import {Test} from "forge-std/Test.sol";
import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {CampaignParams, CampaignRequirements} from "../src/lib/RedeemablesStructs.sol";
import {BURN_ADDRESS} from "../src/lib/RedeemablesConstants.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {ERC721ShipyardRedeemableOwnerMintable} from "../src/test/ERC721ShipyardRedeemableOwnerMintable.sol";

contract DeployAndRedeemTokens is Script, Test {
address constant _BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

function run() external {
vm.startBroadcast();

Expand Down Expand Up @@ -44,7 +43,7 @@ contract DeployAndRedeemTokens is Script, Test {
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(_BURN_ADDRESS)
recipient: payable(BURN_ADDRESS)
});

CampaignRequirements[] memory requirements = new CampaignRequirements[](
Expand All @@ -69,8 +68,10 @@ contract DeployAndRedeemTokens is Script, Test {
// Let's redeem them!
uint256 campaignId = 1;
uint256 requirementsIndex = 0;
bytes32 redemptionHash = bytes32(0);
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash);
bytes32 redemptionHash;
uint256 salt;
bytes memory signature;
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash, salt, signature);

uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = 1;
Expand Down
112 changes: 112 additions & 0 deletions script/DeployAndRedeemTrait.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Script} from "forge-std/Script.sol";
import {Test} from "forge-std/Test.sol";
import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {IERC7496} from "shipyard-core/src/dynamic-traits/interfaces/IERC7496.sol";
import {CampaignParams, CampaignRequirements, TraitRedemption} from "../src/lib/RedeemablesStructs.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {ERC721ShipyardRedeemableMintable} from "../src/extensions/ERC721ShipyardRedeemableMintable.sol";
import {ERC721ShipyardRedeemablePreapprovedTraitSetters} from
"../src/test/ERC721ShipyardRedeemablePreapprovedTraitSetters.sol";

contract DeployAndRedeemTrait is Script, Test {
function run() external {
vm.startBroadcast();

// deploy the receive token first
ERC721ShipyardRedeemableMintable receiveToken = new ERC721ShipyardRedeemableMintable(
"TestRedeemablesRecieveToken",
"TEST"
);

// add the receive token address to allowed trait setters array
address[] memory allowedTraitSetters = new address[](1);
allowedTraitSetters[0] = address(receiveToken);

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

// configure the campaign.
OfferItem[] memory offer = new OfferItem[](1);

// offer is receive token
offer[0] = OfferItem({
itemType: ItemType.ERC721_WITH_CRITERIA,
token: address(receiveToken),
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1
});

// consideration is empty
ConsiderationItem[] memory consideration = new ConsiderationItem[](0);

TraitRedemption[] memory traitRedemptions = new TraitRedemption[](1);

// trait key is "hasRedeemed"
bytes32 traitKey = bytes32("hasRedeemed");

// previous trait value (`substandardValue`) should be 0
bytes32 substandardValue = bytes32(uint256(0));

// new trait value should be 1
bytes32 traitValue = bytes32(uint256(1));

traitRedemptions[0] = TraitRedemption({
substandard: 1,
token: address(redeemToken),
traitKey: traitKey,
traitValue: traitValue,
substandardValue: substandardValue
});

CampaignRequirements[] memory requirements = new CampaignRequirements[](
1
);
requirements[0].offer = offer;
requirements[0].consideration = consideration;
requirements[0].traitRedemptions = traitRedemptions;

CampaignParams memory params = CampaignParams({
requirements: requirements,
signer: address(0),
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1_000_000),
maxCampaignRedemptions: 1_000,
manager: msg.sender
});
receiveToken.createCampaign(params, "");

// Mint token 1 to redeem for token 1.
redeemToken.mint(msg.sender, 1);

// Let's redeem them!
uint256[] memory traitRedemptionTokenIds = new uint256[](1);
traitRedemptionTokenIds[0] = 1;

bytes memory data = abi.encode(
1, // campaignId
0, // requirementsIndex
bytes32(0), // redemptionHash
traitRedemptionTokenIds,
uint256(0), // salt
bytes("") // signature
);

receiveToken.redeem(new uint256[](0), msg.sender, data);

// Assert new trait has been set and redemption token is minted.
bytes32 actualTraitValue = IERC7496(address(redeemToken)).getTraitValue(1, traitKey);
// "hasRedeemed" should be 1 (true)
assertEq(bytes32(uint256(1)), actualTraitValue);
assertEq(receiveToken.ownerOf(1), msg.sender);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Script} from "forge-std/Script.sol";
import {Test} from "forge-std/Test.sol";
import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {CampaignParams, CampaignRequirements} from "../src/lib/RedeemablesStructs.sol";
import {BURN_ADDRESS} from "../src/lib/RedeemablesConstants.sol";
import {ERC721RedemptionMintable} from "../src/extensions/ERC721RedemptionMintable.sol";
import {ERC721OwnerMintable} from "../src/test/ERC721OwnerMintable.sol";
import {ERC721ShipyardRedeemableMintable} from "../src/extensions/ERC721ShipyardRedeemableMintable.sol";

contract DeployERC721ReceiveTokenWithPredeployedSeaDropRedeemToken is Script, Test {
function run() external {
vm.startBroadcast();

ERC721ShipyardRedeemableMintable redeemToken =
ERC721ShipyardRedeemableMintable(0xa1783E74857736b2AEE610A36b537B31CC333048);
ERC721ShipyardRedeemableMintable receiveToken =
ERC721ShipyardRedeemableMintable(0x343B9aEC7fAB02d07c6747Bace112920822334B4);

// Configure the campaign.
OfferItem[] memory offer = new OfferItem[](1);
offer[0] = OfferItem({
itemType: ItemType.ERC721_WITH_CRITERIA,
token: address(receiveToken),
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1
});

ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
consideration[0] = ConsiderationItem({
itemType: ItemType.ERC721_WITH_CRITERIA,
token: address(redeemToken),
identifierOrCriteria: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(BURN_ADDRESS)
});

CampaignRequirements[] memory requirements = new CampaignRequirements[](
1
);
requirements[0].offer = offer;
requirements[0].consideration = consideration;

CampaignParams memory params = CampaignParams({
requirements: requirements,
signer: address(0),
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1_000_000),
maxCampaignRedemptions: 1_000,
manager: msg.sender
});
uint256 campaignId =
receiveToken.createCampaign(params, "ipfs://QmQKc93y2Ev5k9Kz54mCw48ZM487bwGDktZYPLtrjJ3r1d");

// redeemToken.setBaseURI(
// "ipfs://QmYTSupCtriDLBHgPBBhZ98wYdp6N9S8jTL5sKSZwbASeT"
// );

// receiveToken.setBaseURI(
// "ipfs://QmWxgnz8T9wsMBmpCY4Cvanj3RR1obFD2hqDKPZhKN5Tsq/"
// );

// Let's redeem them!
uint256 requirementsIndex = 0;
bytes32 redemptionHash;
uint256 salt;
bytes memory signature;
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash, salt, signature);

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

redeemToken.setApprovalForAll(address(receiveToken), true);

receiveToken.redeem(tokenIds, msg.sender, data);

// Assert redeemable token is burned and redemption token is minted.
assertEq(redeemToken.balanceOf(msg.sender), 2);
assertEq(receiveToken.ownerOf(1), msg.sender);
}
}
8 changes: 5 additions & 3 deletions script/RedeemTokens.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ contract RedeemTokens is Script, Test {
function run() external {
vm.startBroadcast();

address redeemToken = 0x1eCC76De3f9E4e9f8378f6ade61A02A10f976c45;
// address redeemToken = 0x1eCC76De3f9E4e9f8378f6ade61A02A10f976c45;
ERC1155ShipyardRedeemableMintable receiveToken =
ERC1155ShipyardRedeemableMintable(0x3D0fa2a8D07dfe357905a4cB4ed51b0Aea8385B9);

// Let's redeem them!
uint256 campaignId = 1;
uint256 requirementsIndex = 0;
bytes32 redemptionHash = bytes32(0);
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash);
bytes32 redemptionHash;
uint256 salt;
bytes memory signature;
bytes memory data = abi.encode(campaignId, requirementsIndex, redemptionHash, salt, signature);

uint256[] memory redeemTokenIds = new uint256[](1);
redeemTokenIds[0] = 1;
Expand Down
7 changes: 6 additions & 1 deletion src/ERC721ShipyardRedeemable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ contract ERC721ShipyardRedeemable is ERC721ShipyardContractMetadata, ERC7498NFTR
return true;
}

function _internalBurn(address, /* from */ uint256 id, uint256 /* amount */ ) internal virtual override {
function _internalBurn(
address,
/* from */
uint256 id,
uint256 /* amount */
) internal virtual override {
_burn(id);
}

Expand Down
2 changes: 1 addition & 1 deletion src/extensions/ERC1155RedemptionMintable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract ERC1155RedemptionMintable is ERC1155ShipyardContractMetadata, IRedempti
function mintRedemption(
uint256, /* campaignId */
address recipient,
ConsiderationItem[] calldata consideration,
ConsiderationItem[] calldata, /* consideration */
TraitRedemption[] calldata /* traitRedemptions */
) external {
bool validSender;
Expand Down
Loading

0 comments on commit 22e1e6b

Please sign in to comment.