Skip to content

Commit

Permalink
Update sale contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
ScreamingHawk committed Nov 26, 2023
1 parent 87eea5e commit 1f92176
Show file tree
Hide file tree
Showing 19 changed files with 650 additions and 1,149 deletions.
11 changes: 6 additions & 5 deletions src/tokens/ERC1155/ERC1155BaseToken.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155, ERC1155MintBurn} from "@0xsequence/erc-1155/contracts/tokens/ERC1155/ERC1155MintBurn.sol";
import {ERC1155Supply, ERC1155} from "@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/ERC1155Supply.sol";
import {ERC1155Meta} from "@0xsequence/erc-1155/contracts/tokens/ERC1155/ERC1155Meta.sol";
import {ERC1155Metadata} from "@0xsequence/erc-1155/contracts/tokens/ERC1155/ERC1155Metadata.sol";
import {ERC2981Controlled} from "@0xsequence/contracts-library/tokens/common/ERC2981Controlled.sol";
Expand All @@ -11,7 +11,7 @@ error InvalidInitialization();
/**
* A standard base implementation of ERC-1155 for use in Sequence library contracts.
*/
abstract contract ERC1155BaseToken is ERC1155MintBurn, ERC1155Meta, ERC1155Metadata, ERC2981Controlled {
abstract contract ERC1155BaseToken is ERC1155Supply, ERC1155Meta, ERC1155Metadata, ERC2981Controlled {
bytes32 internal constant METADATA_ADMIN_ROLE = keccak256("METADATA_ADMIN_ROLE");

string private _contractURI;
Expand Down Expand Up @@ -119,10 +119,11 @@ abstract contract ERC1155BaseToken is ERC1155MintBurn, ERC1155Meta, ERC1155Metad
public
view
virtual
override (ERC1155, ERC1155Metadata, ERC2981Controlled)
override (ERC1155Supply, ERC1155Metadata, ERC2981Controlled, ERC1155)
returns (bool)
{
return ERC1155.supportsInterface(interfaceId) || ERC1155Metadata.supportsInterface(interfaceId)
|| ERC2981Controlled.supportsInterface(interfaceId) || super.supportsInterface(interfaceId);
return ERC1155Supply.supportsInterface(interfaceId) || ERC1155Metadata.supportsInterface(interfaceId)
|| ERC1155.supportsInterface(interfaceId) || ERC2981Controlled.supportsInterface(interfaceId)
|| super.supportsInterface(interfaceId);
}
}
60 changes: 33 additions & 27 deletions src/tokens/ERC1155/extensions/supply/ERC1155Supply.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155BaseToken} from "@0xsequence/contracts-library/tokens/ERC1155/ERC1155BaseToken.sol";
import {IERC1155Supply} from "@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/IERC1155Supply.sol";
import {ERC1155} from "@0xsequence/erc-1155/contracts/tokens/ERC1155/ERC1155.sol";
import {
IERC1155Supply,
IERC1155SupplyFunctions
} from "@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/IERC1155Supply.sol";

/**
* An ERC-1155 extension that tracks token supply.
*/
abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
// Maximum supply globaly and per token. 0 indicates unlimited supply
uint256 internal totalSupplyCap;
mapping(uint256 => uint256) internal tokenSupplyCap;

abstract contract ERC1155Supply is ERC1155, IERC1155Supply {
// Current supply
uint256 public totalSupply;
mapping(uint256 => uint256) public tokenSupply;
Expand All @@ -23,18 +22,14 @@ abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
* @param _amount The amount to be minted
* @param _data Data to pass if receiver is contract
*/
function _mint(address _to, uint256 _id, uint256 _amount, bytes memory _data) internal virtual override {
// Check supply cap
if (totalSupplyCap > 0 && totalSupply + _amount > totalSupplyCap) {
revert InsufficientSupply(totalSupply, _amount, totalSupplyCap);
}
function _mint(address _to, uint256 _id, uint256 _amount, bytes memory _data) internal virtual {
totalSupply += _amount;
if (tokenSupplyCap[_id] > 0 && tokenSupply[_id] + _amount > tokenSupplyCap[_id]) {
revert InsufficientSupply(tokenSupply[_id], _amount, tokenSupplyCap[_id]);
}
tokenSupply[_id] += _amount;
balances[_to][_id] += _amount;

emit TransferSingle(msg.sender, address(0x0), _to, _id, _amount);

_mint(_to, _id, _amount, _data);
_callonERC1155Received(address(0x0), _to, _id, _amount, gasleft(), _data);
}

/**
Expand All @@ -44,7 +39,10 @@ abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
* @param _amounts Array of amount of tokens to mint per id
* @param _data Data to pass if receiver is contract
*/
function _batchMint(address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data) internal virtual override {
function _batchMint(address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data)
internal
virtual
{
uint256 nMint = _ids.length;
if (nMint != _amounts.length) {
revert InvalidArrayLength();
Expand All @@ -54,19 +52,12 @@ abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
uint256 totalAmount = 0;
for (uint256 i = 0; i < nMint; i++) {
// Update storage balance
if (tokenSupplyCap[_ids[i]] > 0 && tokenSupply[_ids[i]] + _amounts[i] > tokenSupplyCap[_ids[i]]) {
revert InsufficientSupply(tokenSupply[_ids[i]], _amounts[i], tokenSupplyCap[_ids[i]]);
}
balances[_to][_ids[i]] += _amounts[i];
tokenSupply[_ids[i]] += _amounts[i];
totalAmount += _amounts[i];
}
if (totalSupplyCap > 0 && totalSupply + totalAmount > totalSupplyCap) {
revert InsufficientSupply(totalSupply, totalAmount, totalSupplyCap);
}
totalSupply += totalAmount;

// Emit batch mint event
emit TransferBatch(msg.sender, address(0x0), _to, _ids, _amounts);

// Calling onReceive method if recipient is contract
Expand All @@ -79,7 +70,7 @@ abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
* @param _id Token id to burn
* @param _amount The amount to be burned
*/
function _burn(address _from, uint256 _id, uint256 _amount) internal virtual override {
function _burn(address _from, uint256 _id, uint256 _amount) internal virtual {
// Supply
totalSupply -= _amount;
tokenSupply[_id] -= _amount;
Expand All @@ -97,20 +88,35 @@ abstract contract ERC1155Supply is ERC1155BaseToken, IERC1155Supply {
* @param _ids Array of token ids to burn
* @param _amounts Array of the amount to be burned
*/
function _batchBurn(address _from, uint256[] memory _ids, uint256[] memory _amounts) internal virtual override {
function _batchBurn(address _from, uint256[] memory _ids, uint256[] memory _amounts) internal virtual {
uint256 nBurn = _ids.length;
if (nBurn != _amounts.length) {
revert InvalidArrayLength();
}

uint256 totalAmount = 0;
for (uint256 i = 0; i < nBurn; i++) {
// Update balances
balances[_from][_ids[i]] -= _amounts[i];
totalSupply -= _amounts[i];
tokenSupply[_ids[i]] -= _amounts[i];
totalAmount += _amounts[i];
}
totalSupply -= totalAmount;

// Emit batch mint event
emit TransferBatch(msg.sender, _from, address(0x0), _ids, _amounts);
}

//
// Views
//

/**
* Check interface support.
* @param interfaceId Interface id
* @return True if supported
*/
function supportsInterface(bytes4 interfaceId) public view virtual override (ERC1155) returns (bool) {
return type(IERC1155SupplyFunctions).interfaceId == interfaceId || super.supportsInterface(interfaceId);
}
}
15 changes: 12 additions & 3 deletions src/tokens/ERC1155/extensions/supply/IERC1155Supply.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

interface IERC1155SupplySignals {
interface IERC1155SupplyFunctions {

/**
* Returns the total supply of ERC1155 tokens.
*/
function totalSupply() external returns (uint256);

/**
* Insufficient supply of tokens.
* Returns the total supply of a given ERC1155 token.
* @param tokenId The ERC1155 token id.
*/
error InsufficientSupply(uint256 currentSupply, uint256 requestedAmount, uint256 maxSupply);
function tokenSupply(uint256 tokenId) external returns (uint256);
}

interface IERC1155SupplySignals {

/**
* Invalid array input length.
Expand Down
8 changes: 4 additions & 4 deletions src/tokens/ERC1155/presets/items/ERC1155Items.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155MintBurn, ERC1155} from "@0xsequence/erc-1155/contracts/tokens/ERC1155/ERC1155MintBurn.sol";
import {
IERC1155Items,
IERC1155ItemsFunctions
Expand All @@ -12,7 +11,7 @@ import {ERC2981Controlled} from "@0xsequence/contracts-library/tokens/common/ERC
/**
* An implementation of ERC-1155 capable of minting when role provided.
*/
contract ERC1155Items is ERC1155MintBurn, ERC1155BaseToken, IERC1155Items {
contract ERC1155Items is ERC1155BaseToken, IERC1155Items {
bytes32 internal constant MINTER_ROLE = keccak256("MINTER_ROLE");

address private immutable initializer;
Expand Down Expand Up @@ -93,7 +92,8 @@ contract ERC1155Items is ERC1155MintBurn, ERC1155BaseToken, IERC1155Items {
* @param interfaceId Interface id
* @return True if supported
*/
function supportsInterface(bytes4 interfaceId) public view override (ERC1155BaseToken, ERC1155) returns (bool) {
return type(IERC1155ItemsFunctions).interfaceId == interfaceId || ERC1155BaseToken.supportsInterface(interfaceId);
function supportsInterface(bytes4 interfaceId) public view override (ERC1155BaseToken) returns (bool) {
return type(IERC1155ItemsFunctions).interfaceId == interfaceId || ERC1155BaseToken.supportsInterface(interfaceId)
|| super.supportsInterface(interfaceId);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {IERC1155Sale, IERC1155SaleFunctions} from "@0xsequence/contracts-library/tokens/ERC1155/presets/sale/IERC1155Sale.sol";
import {
ERC1155Supply,
ERC1155BaseToken
} from "@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/ERC1155Supply.sol";
IERC1155Sale,
IERC1155SaleFunctions
} from "@0xsequence/contracts-library/tokens/ERC1155/utility/sale/IERC1155Sale.sol";
import {ERC1155Supply} from "@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/ERC1155Supply.sol";
import {
WithdrawControlled,
AccessControl,
Expand All @@ -14,48 +14,44 @@ import {
} from "@0xsequence/contracts-library/tokens/common/WithdrawControlled.sol";
import {MerkleProofSingleUse} from "@0xsequence/contracts-library/tokens/common/MerkleProofSingleUse.sol";

contract ERC1155Sale is IERC1155Sale, ERC1155Supply, WithdrawControlled, MerkleProofSingleUse {
import {IERC1155} from "@0xsequence/erc-1155/contracts/interfaces/IERC1155.sol";
import {IERC1155SupplyFunctions} from
"@0xsequence/contracts-library/tokens/ERC1155/extensions/supply/IERC1155Supply.sol";
import {IERC1155ItemsFunctions} from "@0xsequence/contracts-library/tokens/ERC1155/presets/items/IERC1155Items.sol";

contract ERC1155Sale is IERC1155Sale, WithdrawControlled, MerkleProofSingleUse {
bytes32 internal constant MINT_ADMIN_ROLE = keccak256("MINT_ADMIN_ROLE");

bytes4 private constant _ERC20_TRANSFERFROM_SELECTOR =
bytes4(keccak256(bytes("transferFrom(address,address,uint256)")));

bool private _initialized;
address private _items;

// ERC20 token address for payment. address(0) indicated payment in ETH.
address private _paymentToken;

SaleDetails private _globalSaleDetails;
mapping(uint256 => SaleDetails) private _tokenSaleDetails;

// Maximum supply globaly and per token. 0 indicates unlimited supply
uint256 internal totalSupplyCap;
mapping(uint256 => uint256) internal tokenSupplyCap;

/**
* Initialize the contract.
* @param owner Owner address
* @param tokenName Token name
* @param tokenBaseURI Base URI for token metadata
* @param tokenContractURI Contract URI for token metadata
* @param royaltyReceiver Address of who should be sent the royalty payment
* @param royaltyFeeNumerator The royalty fee numerator in basis points (e.g. 15% would be 1500)
* @param items The ERC-1155 Items contract address
* @dev This should be called immediately after deployment.
*/
function initialize(
address owner,
string memory tokenName,
string memory tokenBaseURI,
string memory tokenContractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
)
public
virtual
{
function initialize(address owner, address items) public virtual {
if (_initialized) {
revert InvalidInitialization();
}

ERC1155BaseToken._initialize(owner, tokenName, tokenBaseURI, tokenContractURI);
_setDefaultRoyalty(royaltyReceiver, royaltyFeeNumerator);
_items = items;

_setupRole(DEFAULT_ADMIN_ROLE, owner);
_setupRole(MINT_ADMIN_ROLE, owner);
_setupRole(WITHDRAW_ROLE, owner);

Expand Down Expand Up @@ -155,22 +151,24 @@ contract ERC1155Sale is IERC1155Sale, ERC1155Supply, WithdrawControlled, MerkleP
payable
{
_payForActiveMint(tokenIds, amounts, proof);
_batchMint(to, tokenIds, amounts, data);
}

/**
* Mint tokens as admin.
* @param to Address to mint tokens to.
* @param tokenIds Token IDs to mint.
* @param amounts Amounts of tokens to mint.
* @param data Data to pass if receiver is contract.
* @notice Only callable by mint admin.
*/
function mintAdmin(address to, uint256[] memory tokenIds, uint256[] memory amounts, bytes memory data)
public
onlyRole(MINT_ADMIN_ROLE)
{
_batchMint(to, tokenIds, amounts, data);
IERC1155SupplyFunctions items = IERC1155SupplyFunctions(_items);
uint256 totalAmount = 0;
uint256 nMint = tokenIds.length;
for (uint256 i = 0; i < nMint; i++) {
// Update storage balance
if (
tokenSupplyCap[tokenIds[i]] > 0 && items.tokenSupply(tokenIds[i]) + amounts[i] > tokenSupplyCap[tokenIds[i]]
) {
revert InsufficientSupply(items.tokenSupply(tokenIds[i]), amounts[i], tokenSupplyCap[tokenIds[i]]);
}
totalAmount += amounts[i];
}
if (totalSupplyCap > 0 && items.totalSupply() + totalAmount > totalSupplyCap) {
revert InsufficientSupply(items.totalSupply(), totalAmount, totalSupplyCap);
}

IERC1155ItemsFunctions(_items).batchMint(to, tokenIds, amounts, data);
}

/**
Expand Down Expand Up @@ -265,13 +263,7 @@ contract ERC1155Sale is IERC1155Sale, ERC1155Supply, WithdrawControlled, MerkleP
* @param interfaceId Interface id
* @return True if supported
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override (ERC1155BaseToken, AccessControl)
returns (bool)
{
function supportsInterface(bytes4 interfaceId) public view virtual override (AccessControl) returns (bool) {
return type(IERC1155SaleFunctions).interfaceId == interfaceId || super.supportsInterface(interfaceId);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155Sale} from "@0xsequence/contracts-library/tokens/ERC1155/presets/sale/ERC1155Sale.sol";
import {IERC1155SaleFactory} from "@0xsequence/contracts-library/tokens/ERC1155/presets/sale/IERC1155SaleFactory.sol";
import {ERC1155Sale} from "@0xsequence/contracts-library/tokens/ERC1155/utility/sale/ERC1155Sale.sol";
import {IERC1155SaleFactory} from "@0xsequence/contracts-library/tokens/ERC1155/utility/sale/IERC1155SaleFactory.sol";
import {SequenceProxyFactory} from "@0xsequence/contracts-library/proxies/SequenceProxyFactory.sol";

/**
Expand All @@ -22,30 +22,15 @@ contract ERC1155SaleFactory is IERC1155SaleFactory, SequenceProxyFactory {
* Creates an ERC-1155 Sale proxy contract
* @param proxyOwner The owner of the ERC-1155 Sale proxy
* @param tokenOwner The owner of the ERC-1155 Sale implementation
* @param name The name of the ERC-1155 Sale token
* @param baseURI The base URI of the ERC-1155 Sale token
* @param contractURI The contract URI of the ERC-1155 Sale token
* @param royaltyReceiver Address of who should be sent the royalty payment
* @param royaltyFeeNumerator The royalty fee numerator in basis points (e.g. 15% would be 1500)
* @param items The ERC-1155 Items contract address
* @return proxyAddr The address of the ERC-1155 Sale Proxy
* @dev As `proxyOwner` owns the proxy, it will be unable to call the ERC-1155 Sale functions.
* @notice The deployed contract must be granted the MINTER_ROLE on the ERC-1155 Items contract.
*/
function deploy(
address proxyOwner,
address tokenOwner,
string memory name,
string memory baseURI,
string memory contractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
)
external
returns (address proxyAddr)
{
bytes32 salt =
keccak256(abi.encodePacked(tokenOwner, name, baseURI, contractURI, royaltyReceiver, royaltyFeeNumerator));
function deploy(address proxyOwner, address tokenOwner, address items) external returns (address proxyAddr) {
bytes32 salt = keccak256(abi.encodePacked(tokenOwner, items));
proxyAddr = _createProxy(salt, proxyOwner, "");
ERC1155Sale(proxyAddr).initialize(tokenOwner, name, baseURI, contractURI, royaltyReceiver, royaltyFeeNumerator);
ERC1155Sale(proxyAddr).initialize(tokenOwner, items);
emit ERC1155SaleDeployed(proxyAddr);
return proxyAddr;
}
Expand Down
Loading

0 comments on commit 1f92176

Please sign in to comment.