Skip to content

Commit

Permalink
add example configure and deploy script
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanio committed Jun 30, 2023
1 parent 0bb36f5 commit 88412d6
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 129 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
# Ignores broadcast logs
/broadcast

# Docs
docs/
Expand Down
69 changes: 69 additions & 0 deletions script/DeployAndConfigureExampleCampaign.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Script.sol";

import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {OfferItem, ConsiderationItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {RedeemableContractOffererV0} from "../src/RedeemableContractOffererV0.sol";
import {CampaignParamsV0} from "../src/lib/RedeemableStructs.sol";
import {ERC721RedemptionMintable} from "../src/lib/ERC721RedemptionMintable.sol";
import {TestERC721} from "../test/utils/mocks/TestERC721.sol";

contract DeployAndConfigureExampleCampaign is Script {
// Addresses: Seaport
address seaport = 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC;
address conduit = 0x1E0049783F008A0085193E00003D00cd54003c71;

address constant _BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;

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

RedeemableContractOffererV0 offerer = new RedeemableContractOffererV0(seaport);
TestERC721 redeemableToken = new TestERC721();
ERC721RedemptionMintable redemptionToken = new ERC721RedemptionMintable(address(offerer));

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

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

CampaignParamsV0 memory params = CampaignParamsV0({
offer: offer,
consideration: consideration,
signer: address(0),
startTime: uint32(block.timestamp),
endTime: uint32(block.timestamp + 1_000_000),
maxTotalRedemptions: 1_000,
manager: address(this)
});
offerer.updateCampaign(0, params, "");

// Mint tokens 1 and 5 to redeem for tokens 1 and 5.
redeemableToken.mint(msg.sender, 1);
redeemableToken.mint(msg.sender, 5);

// Let's redeem them!
uint256 campaignId = 1;
bytes32 redemptionHash = bytes32(0);
bytes memory data = abi.encode(campaignId, redemptionHash);
redeemableToken.safeTransferFrom(msg.sender, address(offerer), 1, data);
redeemableToken.safeTransferFrom(msg.sender, address(offerer), 5, data);
}
}
26 changes: 10 additions & 16 deletions src/RedeemableContractOffererV0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
Schema,
SpentItem
} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {IERC721} from "forge-std/interfaces/IERC721.sol";
import {IERC1155} from "forge-std/interfaces/IERC1155.sol";
import {ERC721} from "solady/src/tokens/ERC721.sol";
import {ERC1155} from "solady/src/tokens/ERC1155.sol";
import {IERC721Receiver} from "seaport-types/src/interfaces/IERC721Receiver.sol";
import {IERC1155Receiver} from "./interfaces/IERC1155Receiver.sol";
import {IERC721RedemptionMintable} from "./interfaces/IERC721RedemptionMintable.sol";
Expand Down Expand Up @@ -50,6 +50,8 @@ contract RedeemableContractOffererV0 is
}

function updateCampaign(uint256 campaignId, CampaignParamsV0 calldata params, string calldata uri) external {
if (campaignId >= _nextCampaignId) revert InvalidCampaignId();

if (campaignId == 0) {
campaignId = _nextCampaignId;
unchecked {
Expand All @@ -65,7 +67,7 @@ contract RedeemableContractOffererV0 is

// Revert if msg.sender is not the manager.
address existingManager = _campaignParams[campaignId].manager;
if (params.manager != msg.sender || (existingManager != address(0) && existingManager != params.manager)) {
if (params.manager != msg.sender && (existingManager != address(0) && existingManager != params.manager)) {
revert NotManager();
}

Expand Down Expand Up @@ -299,17 +301,9 @@ contract RedeemableContractOffererV0 is
RedemptionContextV0 memory redemptionContext = RedemptionContextV0({spent: maximumSpent});
address fulfiller_ = fulfiller;
if (offerItem.itemType == ItemType.ERC721) {
IERC721RedemptionMintable(offerItem.token).mintWithRedemptionContext(
fulfiller_, offerItem.startAmount, redemptionContext
);
IERC721RedemptionMintable(offerItem.token).mintWithRedemptionContext(fulfiller_, redemptionContext);
} else if (offerItem.itemType == ItemType.ERC1155) {
uint256[] memory ids = new uint256[](1);
uint256[] memory amounts = new uint256[](1);
ids[0] = offerItem.identifierOrCriteria;
amounts[0] = offerItem.startAmount;
IERC1155RedemptionMintable(offerItem.token).mintWithRedemptionContext(
fulfiller_, ids, amounts, redemptionContext
);
IERC1155RedemptionMintable(offerItem.token).mintWithRedemptionContext(fulfiller_, redemptionContext);
}
}
unchecked {
Expand Down Expand Up @@ -352,7 +346,7 @@ contract RedeemableContractOffererV0 is

// Transfer the token to the consideration item recipient.
address recipient = _getConsiderationRecipient(params.consideration, msg.sender);
IERC721(msg.sender).transferFrom(address(this), payable(recipient), tokenId);
ERC721(msg.sender).transferFrom(address(this), payable(recipient), tokenId);

return IERC721Receiver.onERC721Received.selector;
}
Expand All @@ -375,7 +369,7 @@ contract RedeemableContractOffererV0 is

// Transfer the token to the consideration item recipient.
address recipient = _getConsiderationRecipient(params.consideration, msg.sender);
IERC1155(msg.sender).safeTransferFrom(address(this), recipient, id, value, "");
ERC1155(msg.sender).safeTransferFrom(address(this), recipient, id, value, "");

return IERC1155Receiver.onERC1155Received.selector;
}
Expand Down Expand Up @@ -409,7 +403,7 @@ contract RedeemableContractOffererV0 is

// Transfer the tokens to the consideration item recipient.
address recipient = _getConsiderationRecipient(params.consideration, msg.sender);
IERC1155(msg.sender).safeBatchTransferFrom(address(this), recipient, ids, values, "");
ERC1155(msg.sender).safeBatchTransferFrom(address(this), recipient, ids, values, "");

return IERC1155Receiver.onERC1155BatchReceived.selector;
}
Expand Down
10 changes: 2 additions & 8 deletions src/interfaces/IERC1155RedemptionMintable.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {IERC1155} from "forge-std/interfaces/IERC1155.sol";
import {RedemptionContextV0} from "../lib/RedeemableStructs.sol";

interface IERC1155RedemptionMintable is IERC1155 {
function mintWithRedemptionContext(
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
RedemptionContextV0 calldata context
) external;
interface IERC1155RedemptionMintable {
function mintWithRedemptionContext(address to, RedemptionContextV0 calldata context) external;
}
5 changes: 2 additions & 3 deletions src/interfaces/IERC721RedemptionMintable.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {IERC721} from "forge-std/interfaces/IERC721.sol";
import {RedemptionContextV0} from "../lib/RedeemableStructs.sol";

interface IERC721RedemptionMintable is IERC721 {
function mintWithRedemptionContext(address to, uint256 quantity, RedemptionContextV0 calldata context) external;
interface IERC721RedemptionMintable {
function mintWithRedemptionContext(address to, RedemptionContextV0 calldata context) external;
}
42 changes: 42 additions & 0 deletions src/lib/ERC721RedemptionMintable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {ERC721} from "solady/src/tokens/ERC721.sol";
import {IERC721RedemptionMintable} from "../interfaces/IERC721RedemptionMintable.sol";
import {RedemptionContextV0} from "../lib/RedeemableStructs.sol";

contract ERC721RedemptionMintable is ERC721, IERC721RedemptionMintable {
address private _redeemableContractOfferer;

/// @dev Revert with an error if the redeemable contract offerer is not the sender of mintWithRedemptionContext.
error InvalidSender();

constructor(address redeemableContractOfferer) {
_redeemableContractOfferer = redeemableContractOfferer;
}

function mintWithRedemptionContext(address to, RedemptionContextV0 calldata context) external {
if (msg.sender != _redeemableContractOfferer) revert InvalidSender();

// Mint the same token IDs redeemed.
for (uint256 i = 0; i < context.spent.length;) {
_mint(to, context.spent[i].identifier);

unchecked {
++i;
}
}
}

function name() public pure override returns (string memory) {
return "ERC721RedemptionMintable";
}

function symbol() public pure override returns (string memory) {
return "721RM";
}

function tokenURI(uint256 tokenId) public pure override returns (string memory) {
return string(abi.encodePacked("https://example.com/", tokenId));
}
}
1 change: 1 addition & 0 deletions src/lib/RedeemableErrorsAndEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface RedeemableErrorsAndEvents {
error InvalidConsiderationLength(uint256 got, uint256 want);
error InvalidConsiderationItem(address got, address want);
error InvalidOfferLength(uint256 got, uint256 want);
error InvalidCampaignId();
error NoConsiderationItems();
error InvalidTime();
error ConsiderationItemRecipientCannotBeZeroAddress();
Expand Down
1 change: 0 additions & 1 deletion src/lib/RedeemableStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ struct CampaignParamsV0 {
uint32 startTime;
uint32 endTime;
uint32 maxTotalRedemptions;
// Additional parameters for registry functionality
address manager;
}

Expand Down
11 changes: 5 additions & 6 deletions test/RedeemableContractOffererV0.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ contract TestRedeemableContractOffererV0 is BaseOrderTest, RedeemableErrorsAndEv
params.consideration[0].recipient = payable(_BURN_ADDRESS);

uint256 campaignId = 1;

vm.expectEmit(true, true, true, true);
emit CampaignUpdated(campaignId, params, "http://test.com");

offerer.updateCampaign(campaignId, params, "http://test.com");
offerer.updateCampaign(0, params, "http://test.com");

(CampaignParamsV0 memory storedParams, string memory storedURI, uint256 totalRedemptions) =
offerer.getCampaign(campaignId);
Expand Down Expand Up @@ -86,8 +85,8 @@ contract TestRedeemableContractOffererV0 is BaseOrderTest, RedeemableErrorsAndEv
itemType: ItemType.ERC721,
token: address(token),
identifierOrCriteria: 0,
startAmount: 0,
endAmount: 0,
startAmount: 1,
endAmount: 1,
recipient: payable(_BURN_ADDRESS)
});

Expand All @@ -101,13 +100,13 @@ contract TestRedeemableContractOffererV0 is BaseOrderTest, RedeemableErrorsAndEv
manager: address(this)
});

uint256 campaignId = 1;
offerer.updateCampaign(campaignId, params, "");
offerer.updateCampaign(0, params, "");

ConsiderationItem[] memory expectedConsideration = consideration;
expectedConsideration[0].identifierOrCriteria = tokenId;
expectedConsideration[0].startAmount = 1;
expectedConsideration[0].endAmount = 1;
uint256 campaignId = 1;
bytes32 redemptionHash = bytes32(0);
vm.expectEmit(true, true, true, true);
emit Redemption(
Expand Down
89 changes: 0 additions & 89 deletions test/Redeemables.t.sol.txt

This file was deleted.

3 changes: 1 addition & 2 deletions test/utils/mocks/TestERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import {ERC721} from "solady/src/tokens/ERC721.sol";

// Used for minting test ERC721s in our tests
contract TestERC721 is ERC721 {
function mint(address to, uint256 tokenId) public returns (bool) {
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
return true;
}

function tokenURI(uint256) public pure override returns (string memory) {
Expand Down

0 comments on commit 88412d6

Please sign in to comment.