Skip to content

Commit

Permalink
fix(CON-1104),refactor: allow editing of supply to unlimited/unlimite…
Browse files Browse the repository at this point in the history
…d and minted claims; change name to serendipity (#96)

* docs: update docs with past serendipity contracts

* refactor: rename to serendipity tests

* test: fix test name

* test: move deprecation tests out; add test cases for toggling supply

* refactor: rename to serendipity

* refactor: rename emitted events

* test: fix wrong assertion in test

* fix: ensure we allow totalMax to be 0 if updating claim parameters, even if minted

* chore: bump action version to use node 20

* refactor: name as simply serendipity

* refactor: renamed the event for reserving a mint

* chore: make artifact names unique despite different folders

see https://github.com/actions/download-artifact/blob/main/docs/MIGRATION.md
  • Loading branch information
jaxonL authored Jul 2, 2024
1 parent af0a198 commit 0b86825
Show file tree
Hide file tree
Showing 9 changed files with 1,223 additions and 770 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ jobs:
- name: Setup LCOV
uses: hrishikesh-kadam/setup-lcov@v1
- name: Report code coverage
uses: zgosalvez/github-actions-report-lcov@v3
uses: zgosalvez/github-actions-report-lcov@v4
with:
coverage-files: packages/${{ matrix.folder }}/lcov.info
artifact-name: code-coverage-report
artifact-name: code-coverage-report-${{ matrix.folder }}
github-token: ${{ secrets.GITHUB_TOKEN }}
working-directory: packages/${{ matrix.folder }}
update-comment: true
9 changes: 9 additions & 0 deletions packages/manifold/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ These are the latest Burn Redeem extensions.
| 1 (Mainnet) | ERC1155 | 0xde659726CfD166aCa4867994d396EFeF386EAD68 |
| 2 (Goerli) | ERC1155 | 0x193bFD86F329508351ae899A92a963d5bfC77190 |

#### Manifold Serendipity Collection
These are the latest Serendipity (gacha) extensions.

| Network | Output Token Spec | Address | Deprecation notice |
| ----------- | ------------------------ | ------------------------------------------ | ------------------------------ |
| v0 (Sepolia) | ERC1155 | 0xa160a0a48b6ddb9ffd01a7b85e0c2b331c912e29 | Cannot deploy unlimited supply |
| v1 (Sepolia, Base) | ERC1155 | 0x53C37ccC1C48f63BD35FFc93952d0880d7529b9e | Cannot edit unlimited supply |
| v2 (Sepolia, Base) | ERC1155 | \<tbd\> | n/a (active) |


#### OperatorFilterer
Shared extension to support OpenSea's Operator Filter Registry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./GachaLazyClaim.sol";
import "./IERC1155GachaLazyClaim.sol";
import "./Serendipity.sol";
import "./IERC1155Serendipity.sol";

/**
* @title Gacha Lazy 1155 Payable Claim
* @title Serendipity Lazy Payable Claim - ERC-1155
* @author manifold.xyz
* @notice
*/
contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExtensionTokenURI, GachaLazyClaim {
contract ERC1155Serendipity is IERC165, IERC1155Serendipity, ICreatorExtensionTokenURI, Serendipity {
using Strings for uint256;

// stores mapping from contractAddress/instanceId to the claim it represents
Expand All @@ -30,17 +30,17 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten

function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AdminControl) returns (bool) {
return
interfaceId == type(IERC1155GachaLazyClaim).interfaceId ||
interfaceId == type(IGachaLazyClaim).interfaceId ||
interfaceId == type(IERC1155Serendipity).interfaceId ||
interfaceId == type(ISerendipity).interfaceId ||
interfaceId == type(ICreatorExtensionTokenURI).interfaceId ||
interfaceId == type(IAdminControl).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}

constructor(address initialOwner) GachaLazyClaim(initialOwner) {}
constructor(address initialOwner) Serendipity(initialOwner) {}

/**
* See {IERC1155GachaLazyClaim-initializeClaim}.
* See {IERC1155Serendipity-initializeClaim}.
*/
function initializeClaim(
address creatorContractAddress,
Expand All @@ -50,24 +50,24 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
if (deprecated) {
revert ContractDeprecated();
}
if (instanceId == 0 || instanceId > MAX_UINT_56) revert IGachaLazyClaim.InvalidInstance();
if (instanceId == 0 || instanceId > MAX_UINT_56) revert ISerendipity.InvalidInstance();
if (_claims[creatorContractAddress][instanceId].storageProtocol != StorageProtocol.INVALID)
revert IGachaLazyClaim.ClaimAlreadyInitialized();
revert ISerendipity.ClaimAlreadyInitialized();
// Checks
if (claimParameters.storageProtocol == StorageProtocol.INVALID) revert IGachaLazyClaim.InvalidStorageProtocol();
if (claimParameters.storageProtocol == StorageProtocol.INVALID) revert ISerendipity.InvalidStorageProtocol();
if (claimParameters.endDate != 0 && claimParameters.startDate >= claimParameters.endDate)
revert IGachaLazyClaim.InvalidDate();
if (claimParameters.totalMax > MAX_UINT_32) revert IGachaLazyClaim.InvalidInput();
if (claimParameters.tokenVariations > MAX_UINT_8) revert IGachaLazyClaim.InvalidInput();
if (claimParameters.cost > MAX_UINT_96) revert IGachaLazyClaim.InvalidInput();
revert ISerendipity.InvalidDate();
if (claimParameters.totalMax > MAX_UINT_32) revert ISerendipity.InvalidInput();
if (claimParameters.tokenVariations > MAX_UINT_8) revert ISerendipity.InvalidInput();
if (claimParameters.cost > MAX_UINT_96) revert ISerendipity.InvalidInput();

address[] memory receivers = new address[](1);
receivers[0] = msg.sender;
uint256[] memory amounts = new uint256[](claimParameters.tokenVariations);
string[] memory uris = new string[](claimParameters.tokenVariations);
uint256[] memory newTokenIds = IERC1155CreatorCore(creatorContractAddress).mintExtensionNew(receivers, amounts, uris);

if (newTokenIds[0] > MAX_UINT_80) revert IGachaLazyClaim.InvalidStartingTokenId();
if (newTokenIds[0] > MAX_UINT_80) revert ISerendipity.InvalidStartingTokenId();

// Create the claim
_claims[creatorContractAddress][instanceId] = Claim({
Expand All @@ -90,11 +90,11 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
}
}

emit GachaClaimInitialized(creatorContractAddress, instanceId, msg.sender);
emit SerendipityClaimInitialized(creatorContractAddress, instanceId, msg.sender);
}

/**
* See {IERC1155GachaLazyClaim-updateClaim}.
* See {IERC1155Serendipity-updateClaim}.
*/
function updateClaim(
address creatorContractAddress,
Expand All @@ -105,13 +105,13 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
revert ContractDeprecated();
}
Claim memory claim = _getClaim(creatorContractAddress, instanceId);
if (instanceId == 0 || instanceId > MAX_UINT_56) revert IGachaLazyClaim.InvalidInstance();
if (instanceId == 0 || instanceId > MAX_UINT_56) revert ISerendipity.InvalidInstance();
if (updateClaimParameters.endDate != 0 && updateClaimParameters.startDate >= updateClaimParameters.endDate)
revert IGachaLazyClaim.InvalidDate();
if (updateClaimParameters.totalMax < claim.total) revert IGachaLazyClaim.CannotLowerTotalMaxBeyondTotal();
if (updateClaimParameters.totalMax > MAX_UINT_32) revert IGachaLazyClaim.InvalidInput();
if (updateClaimParameters.storageProtocol == StorageProtocol.INVALID) revert IGachaLazyClaim.InvalidStorageProtocol();
if (updateClaimParameters.cost > MAX_UINT_96) revert IGachaLazyClaim.InvalidInput();
revert ISerendipity.InvalidDate();
if (updateClaimParameters.totalMax != 0 && updateClaimParameters.totalMax < claim.total) revert ISerendipity.CannotLowerTotalMaxBeyondTotal();
if (updateClaimParameters.totalMax > MAX_UINT_32) revert ISerendipity.InvalidInput();
if (updateClaimParameters.storageProtocol == StorageProtocol.INVALID) revert ISerendipity.InvalidStorageProtocol();
if (updateClaimParameters.cost > MAX_UINT_96) revert ISerendipity.InvalidInput();

// Overwrite the existing values
_claims[creatorContractAddress][instanceId] = Claim({
Expand All @@ -127,18 +127,18 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
cost: updateClaimParameters.cost,
erc20: claim.erc20
});
emit GachaClaimUpdated(creatorContractAddress, instanceId);
emit SerendipityClaimUpdated(creatorContractAddress, instanceId);
}

/**
* See {IERC1155GachaLazyClaim-getClaim}.
* See {IERC1155Serendipity-getClaim}.
*/
function getClaim(address creatorContractAddress, uint256 instanceId) public view override returns (Claim memory) {
return _getClaim(creatorContractAddress, instanceId);
}

/**
* See {IERC1155GachaLazyClaim-getClaimForToken}.
* See {IERC1155Serendipity-getClaimForToken}.
*/
function getClaimForToken(
address creatorContractAddress,
Expand All @@ -150,22 +150,22 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten

function _getClaim(address creatorContractAddress, uint256 instanceId) private view returns (Claim storage claim) {
claim = _claims[creatorContractAddress][instanceId];
if (claim.storageProtocol == StorageProtocol.INVALID) revert IGachaLazyClaim.ClaimNotInitialized();
if (claim.storageProtocol == StorageProtocol.INVALID) revert ISerendipity.ClaimNotInitialized();
}

/**
* See {IGachaLazyClaim-mintReserve}.
* See {ISerendipity-mintReserve}.
*/
function mintReserve(address creatorContractAddress, uint256 instanceId, uint32 mintCount) external payable override {
if (Address.isContract(msg.sender)) revert IGachaLazyClaim.CannotMintFromContract();
if (Address.isContract(msg.sender)) revert ISerendipity.CannotMintFromContract();
Claim storage claim = _getClaim(creatorContractAddress, instanceId);
// Checks for reserving
if (mintCount == 0 || mintCount >= MAX_UINT_32) revert IGachaLazyClaim.InvalidMintCount();
if (mintCount == 0 || mintCount >= MAX_UINT_32) revert ISerendipity.InvalidMintCount();
if (claim.startDate > block.timestamp || (claim.endDate > 0 && claim.endDate < block.timestamp))
revert IGachaLazyClaim.ClaimInactive();
if (claim.totalMax != 0 && claim.total == claim.totalMax) revert IGachaLazyClaim.ClaimSoldOut();
if (claim.total == MAX_UINT_32) revert IGachaLazyClaim.TooManyRequested();
if (msg.value != (claim.cost + MINT_FEE) * mintCount) revert IGachaLazyClaim.InvalidPayment();
revert ISerendipity.ClaimInactive();
if (claim.totalMax != 0 && claim.total == claim.totalMax) revert ISerendipity.ClaimSoldOut();
if (claim.total == MAX_UINT_32) revert ISerendipity.TooManyRequested();
if (msg.value != (claim.cost + MINT_FEE) * mintCount) revert ISerendipity.InvalidPayment();
// calculate the amount to reserve and update totals
uint32 amountToReserve = mintCount;
if (claim.totalMax != 0) {
Expand All @@ -181,13 +181,13 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
uint256 refundAmount = msg.value - (claim.cost + MINT_FEE) * amountToReserve;
_sendFunds(payable(msg.sender), refundAmount);
}
emit GachaClaimMintReserved(creatorContractAddress, instanceId, msg.sender, amountToReserve);
emit SerendipityMintReserved(creatorContractAddress, instanceId, msg.sender, amountToReserve);
}

/**
* See {IGachaLazyClaim-deliverMints}.
* See {ISerendipity-deliverMints}.
*/
function deliverMints(IGachaLazyClaim.ClaimMint[] calldata mints) external override {
function deliverMints(ISerendipity.ClaimMint[] calldata mints) external override {
_validateSigner();
for (uint256 i; i < mints.length; ) {
ClaimMint calldata mintData = mints[i];
Expand All @@ -198,19 +198,19 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten

for (uint256 j; j < mintData.variationMints.length; ) {
VariationMint calldata variationMint = mintData.variationMints[j];
if (variationMint.variationIndex > MAX_UINT_8) revert IGachaLazyClaim.InvalidVariationIndex();
if (variationMint.variationIndex > MAX_UINT_8) revert ISerendipity.InvalidVariationIndex();
uint8 variationIndex = variationMint.variationIndex;
if (variationIndex > claim.tokenVariations || variationIndex < 1) revert IGachaLazyClaim.InvalidVariationIndex();
if (variationIndex > claim.tokenVariations || variationIndex < 1) revert ISerendipity.InvalidVariationIndex();
address recipient = variationMint.recipient;
if (variationMint.amount > MAX_UINT_32) revert IGachaLazyClaim.TooManyRequested();
if (variationMint.amount > MAX_UINT_32) revert ISerendipity.TooManyRequested();
uint32 amount = variationMint.amount;
UserMintDetails storage userMintDetails = _mintDetailsPerWallet[mintData.creatorContractAddress][
mintData.instanceId
][recipient];

if (userMintDetails.deliveredCount + amount > userMintDetails.reservedCount)
revert IGachaLazyClaim.CannotMintMoreThanReserved();
if (claim.startingTokenId > MAX_UINT_80) revert IGachaLazyClaim.InvalidStartingTokenId();
revert ISerendipity.CannotMintMoreThanReserved();
if (claim.startingTokenId > MAX_UINT_80) revert ISerendipity.InvalidStartingTokenId();
tokenIds[j] = claim.startingTokenId + variationIndex - 1;
amounts[j] = amount;
receivers[j] = recipient;
Expand All @@ -228,7 +228,7 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
}

/**
* See {IGachaLazyClaim-getUserMints}.
* See {ISerendipity-getUserMints}.
*/
function getUserMints(
address minter,
Expand All @@ -243,7 +243,7 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
*/
function tokenURI(address creatorContractAddress, uint256 tokenId) external view override returns (string memory uri) {
uint256 instanceId = _tokenInstances[creatorContractAddress][tokenId];
if (instanceId == 0) revert IGachaLazyClaim.TokenDNE();
if (instanceId == 0) revert ISerendipity.TokenDNE();
Claim memory claim = _getClaim(creatorContractAddress, instanceId);

string memory prefix = "";
Expand All @@ -256,7 +256,7 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
}

/**
* See {IERC1155GachaLazyClaim-updateTokenURIParams}.
* See {IERC1155Serendipity-updateTokenURIParams}.
*/
function updateTokenURIParams(
address creatorContractAddress,
Expand All @@ -265,9 +265,9 @@ contract ERC1155GachaLazyClaim is IERC165, IERC1155GachaLazyClaim, ICreatorExten
string calldata location
) external override creatorAdminRequired(creatorContractAddress) {
Claim storage claim = _getClaim(creatorContractAddress, instanceId);
if (storageProtocol == StorageProtocol.INVALID) revert IGachaLazyClaim.InvalidStorageProtocol();
if (storageProtocol == StorageProtocol.INVALID) revert ISerendipity.InvalidStorageProtocol();
claim.storageProtocol = storageProtocol;
claim.location = location;
emit GachaClaimUpdated(creatorContractAddress, instanceId);
emit SerendipityClaimUpdated(creatorContractAddress, instanceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "./IGachaLazyClaim.sol";
import "./ISerendipity.sol";

/**
* Gacha 1155 Lazy Claim interface
* Serendipity Lazy Claim interface for ERC-1155
*/
interface IERC1155GachaLazyClaim is IGachaLazyClaim {
interface IERC1155Serendipity is ISerendipity {
struct Claim {
StorageProtocol storageProtocol;
uint32 total;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pragma solidity ^0.8.0;
/// @author: manifold.xyz

/**
* Gacha Lazy Claim interface
* Serendipity Lazy Claim interface
*/
interface IGachaLazyClaim {
interface ISerendipity {
enum StorageProtocol {
INVALID,
NONE,
Expand Down Expand Up @@ -39,9 +39,9 @@ interface IGachaLazyClaim {
error CannotMintMoreThanReserved();
error CannotMintFromContract();

event GachaClaimInitialized(address indexed creatorContract, uint256 indexed instanceId, address initializer);
event GachaClaimUpdated(address indexed creatorContract, uint256 indexed instanceId);
event GachaClaimMintReserved(
event SerendipityClaimInitialized(address indexed creatorContract, uint256 indexed instanceId, address initializer);
event SerendipityClaimUpdated(address indexed creatorContract, uint256 indexed instanceId);
event SerendipityMintReserved(
address indexed creatorContract,
uint256 indexed instanceId,
address indexed collector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ pragma solidity ^0.8.0;

import "@manifoldxyz/libraries-solidity/contracts/access/AdminControl.sol";

import "./IGachaLazyClaim.sol";
import "./ISerendipity.sol";

/**
* @title Gacha Lazy Claim
* @title Serendipity Lazy Claim
* @author manifold.xyz
*/
abstract contract GachaLazyClaim is IGachaLazyClaim, AdminControl {
abstract contract Serendipity is ISerendipity, AdminControl {
using EnumerableSet for EnumerableSet.AddressSet;

string internal constant ARWEAVE_PREFIX = "https://arweave.net/";
Expand Down Expand Up @@ -55,22 +55,22 @@ abstract contract GachaLazyClaim is IGachaLazyClaim, AdminControl {
}

/**
* See {IGachaLazyClaim-withdraw}.
* See {ISerendipity-withdraw}.
*/
function withdraw(address payable receiver, uint256 amount) external override adminRequired {
(bool sent, ) = receiver.call{ value: amount }("");
if (!sent) revert IGachaLazyClaim.FailedToTransfer();
if (!sent) revert ISerendipity.FailedToTransfer();
}

/**
* See {IGachaLazyClaim-setSigner}.
* See {ISerendipity-setSigner}.
*/
function setSigner(address signer) external override adminRequired {
_signer = signer;
}

function _validateSigner() internal view {
if (msg.sender != _signer) revert IGachaLazyClaim.InvalidSignature();
if (msg.sender != _signer) revert ISerendipity.InvalidSignature();
}

function _getUserMints(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "../contracts/gachaclaims/ERC1155GachaLazyClaim.sol";
import "../contracts/gachaclaims/ERC1155Serendipity.sol";

/**
Pro tip for testing! The private key can be whatever. This is what I did to test.
1. Uncomment the lines below and follow the instructions there to change the code.
2. Run the script, like `forge script script/ERC1155GachaLazyClaim.s.sol:DeployERC1155GachaLazyClaim --rpc-url https://eth-sepolia.g.alchemy.com/v2/xxx --broadcast`
2. Run the script, like `forge script script/ERC1155Serendipity.s.sol:DeployERC1155Serendipity --rpc-url https://eth-sepolia.g.alchemy.com/v2/xxx --broadcast`
3. It will print out the address, but give you an out of eth error.
4. Now you have the address, use your real wallet and send it some sepolia eth.
5. Now, run the script again. It will deploy and transfer the contract to your wallet.
In the end, you just basically used a random pk in the moment to deploy. You never had
to expose your personal pk to your mac's environment variable or anything.
*/
contract DeployERC1155GachaLazyClaim is Script {
contract DeployERC1155Serendipity is Script {
function run() external {
// address initialOwner = <your wallet address>; // uncomment this and put in your desired owner address
address initialOwner = vm.envAddress("INITIAL_OWNER"); // comment this out on sepolia
Expand All @@ -31,9 +31,9 @@ contract DeployERC1155GachaLazyClaim is Script {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); // comment this out when testing on goerli
vm.startBroadcast(deployerPrivateKey);

// forge script script/ERC1155GachaLazyClaim.s.sol:DeployERC1155GachaLazyClaim --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/gachaclaims/ERC1155GachaLazyClaim.sol:ERC1155GachaLazyClaim --constructor-args $(cast abi-encode "constructor(address)" "${INITIAL_OWNER}") --watch
new ERC1155GachaLazyClaim{salt: 0x16091cc3cd908d7d973f650f59bd476ac79090f0358f87c50a7f5caee0835a84}(initialOwner);
// forge script script/ERC1155Serendipity.s.sol:DeployERC1155Serendipity --optimizer-runs 1000 --rpc-url <YOUR_NODE> --broadcast
// forge verify-contract --compiler-version 0.8.17 --optimizer-runs 1000 --chain sepolia <DEPLOYED_ADDRESS> contracts/gachaclaims/ERC1155Serendipity.sol:ERC1155Serendipity --constructor-args $(cast abi-encode "constructor(address)" "${INITIAL_OWNER}") --watch
new ERC1155Serendipity{salt: 0x16091cc3cd908d7d973f650f59bd476ac79090f0358f87c50a7f5caee0835a84}(initialOwner);
vm.stopBroadcast();
}
}
Loading

0 comments on commit 0b86825

Please sign in to comment.