Skip to content

Commit

Permalink
Add ERC1155Soulbound
Browse files Browse the repository at this point in the history
  • Loading branch information
ScreamingHawk committed Sep 11, 2024
1 parent 2e0bb25 commit 8fbefab
Show file tree
Hide file tree
Showing 6 changed files with 458 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/tokens/ERC1155/presets/items/ERC1155Items.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ contract ERC1155Items is ERC1155BaseToken, IERC1155Items {
* @param interfaceId Interface id
* @return True if supported
*/
function supportsInterface(bytes4 interfaceId) public view override (ERC1155BaseToken) returns (bool) {
function supportsInterface(bytes4 interfaceId) public view virtual override (ERC1155BaseToken) returns (bool) {
return type(IERC1155ItemsFunctions).interfaceId == interfaceId || ERC1155BaseToken.supportsInterface(interfaceId)
|| super.supportsInterface(interfaceId);
}
Expand Down
85 changes: 85 additions & 0 deletions src/tokens/ERC1155/presets/soulbound/ERC1155Soulbound.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155Items} from "@0xsequence/contracts-library/tokens/ERC1155/presets/items/ERC1155Items.sol";
import {
IERC1155Soulbound,
IERC1155SoulboundFunctions
} from "@0xsequence/contracts-library/tokens/ERC1155/presets/soulbound/IERC1155Soulbound.sol";

/**
* An implementation of ERC-1155 that prevents transfers.
*/
contract ERC1155Soulbound is ERC1155Items, IERC1155Soulbound {
bytes32 public constant TRANSFER_ADMIN_ROLE = keccak256("TRANSFER_ADMIN_ROLE");

bool internal _transferLocked;

constructor() ERC1155Items() {}

/// @inheritdoc ERC1155Items
function initialize(
address owner,
string memory tokenName,
string memory tokenBaseURI,
string memory tokenContractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
) public virtual override {
_transferLocked = true;
_grantRole(TRANSFER_ADMIN_ROLE, owner);
super.initialize(owner, tokenName, tokenBaseURI, tokenContractURI, royaltyReceiver, royaltyFeeNumerator);
}

/// @inheritdoc IERC1155SoulboundFunctions
function setTransferLocked(bool locked) external override onlyRole(TRANSFER_ADMIN_ROLE) {
_transferLocked = locked;
}

/// @inheritdoc IERC1155SoulboundFunctions
function getTransferLocked() external view override returns (bool) {
return _transferLocked;
}

// Transfer hooks

function _safeTransferFrom(address from, address to, uint256 id, uint256 amount) internal virtual override {
// Mint transactions allowed
if (_transferLocked && from != address(0)) {
revert TransfersLocked();
}
super._safeTransferFrom(from, to, id, amount);
}

function _safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory amounts)
internal
virtual
override
{
// Mint transactions allowed
if (_transferLocked && from != address(0)) {
revert TransfersLocked();
}
super._safeBatchTransferFrom(from, to, ids, amounts);
}

function _burn(address from, uint256 id, uint256 amount) internal virtual override {
if (_transferLocked) {
revert TransfersLocked();
}
super._burn(from, id, amount);
}

function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual override {
if (_transferLocked) {
revert TransfersLocked();
}
super._batchBurn(from, ids, amounts);
}

// Views

function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return type(IERC1155SoulboundFunctions).interfaceId == interfaceId || super.supportsInterface(interfaceId);
}
}
58 changes: 58 additions & 0 deletions src/tokens/ERC1155/presets/soulbound/ERC1155SoulboundFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import {ERC1155Soulbound} from "@0xsequence/contracts-library/tokens/ERC1155/presets/soulbound/ERC1155Soulbound.sol";
import {
IERC1155SoulboundFactory,
IERC1155SoulboundFactoryFunctions
} from "@0xsequence/contracts-library/tokens/ERC1155/presets/soulbound/IERC1155SoulboundFactory.sol";
import {SequenceProxyFactory} from "@0xsequence/contracts-library/proxies/SequenceProxyFactory.sol";

/**
* Deployer of ERC-1155 Soulbound proxies.
*/
contract ERC1155SoulboundFactory is IERC1155SoulboundFactory, SequenceProxyFactory {
/**
* Creates an ERC-1155 Soulbound Factory.
* @param factoryOwner The owner of the ERC-1155 Soulbound Factory
*/
constructor(address factoryOwner) {
ERC1155Soulbound impl = new ERC1155Soulbound();
SequenceProxyFactory._initialize(address(impl), factoryOwner);
}

/// @inheritdoc IERC1155SoulboundFactoryFunctions
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.encode(tokenOwner, name, baseURI, contractURI, royaltyReceiver, royaltyFeeNumerator));
proxyAddr = _createProxy(salt, proxyOwner, "");
ERC1155Soulbound(proxyAddr).initialize(
tokenOwner, name, baseURI, contractURI, royaltyReceiver, royaltyFeeNumerator
);
emit ERC1155SoulboundDeployed(proxyAddr);
return proxyAddr;
}

/// @inheritdoc IERC1155SoulboundFactoryFunctions
function determineAddress(
address proxyOwner,
address tokenOwner,
string memory name,
string memory baseURI,
string memory contractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
) external view returns (address proxyAddr) {
bytes32 salt =
keccak256(abi.encode(tokenOwner, name, baseURI, contractURI, royaltyReceiver, royaltyFeeNumerator));
return _computeProxyAddress(salt, proxyOwner, "");
}
}
25 changes: 25 additions & 0 deletions src/tokens/ERC1155/presets/soulbound/IERC1155Soulbound.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

interface IERC1155SoulboundFunctions {
/**
* Sets the transfer lock.
* @param locked Whether or not transfers are locked.
*/
function setTransferLocked(bool locked) external;

/**
* Gets the transfer lock.
* @return Whether or not transfers are locked.
*/
function getTransferLocked() external view returns (bool);
}

interface IERC1155SoulboundSignals {
/**
* Transfers locked.
*/
error TransfersLocked();
}

interface IERC1155Soulbound is IERC1155SoulboundFunctions, IERC1155SoulboundSignals {}
56 changes: 56 additions & 0 deletions src/tokens/ERC1155/presets/soulbound/IERC1155SoulboundFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

interface IERC1155SoulboundFactoryFunctions {
/**
* Creates an ERC-1155 Soulbound proxy.
* @param proxyOwner The owner of the ERC-1155 Soulbound proxy
* @param tokenOwner The owner of the ERC-1155 Soulbound implementation
* @param name The name of the ERC-1155 Soulbound proxy
* @param baseURI The base URI of the ERC-1155 Soulbound proxy
* @param contractURI The contract URI of the ERC-1155 Soulbound proxy
* @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)
* @return proxyAddr The address of the ERC-1155 Soulbound Proxy
*/
function deploy(
address proxyOwner,
address tokenOwner,
string memory name,
string memory baseURI,
string memory contractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
) external returns (address proxyAddr);

/**
* Computes the address of a proxy instance.
* @param proxyOwner The owner of the ERC-1155 Soulbound proxy
* @param tokenOwner The owner of the ERC-1155 Soulbound implementation
* @param name The name of the ERC-1155 Soulbound proxy
* @param baseURI The base URI of the ERC-1155 Soulbound proxy
* @param contractURI The contract URI of the ERC-1155 Soulbound proxy
* @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)
* @return proxyAddr The address of the ERC-1155 Soulbound Proxy
*/
function determineAddress(
address proxyOwner,
address tokenOwner,
string memory name,
string memory baseURI,
string memory contractURI,
address royaltyReceiver,
uint96 royaltyFeeNumerator
) external returns (address proxyAddr);
}

interface IERC1155SoulboundFactorySignals {
/**
* Event emitted when a new ERC-1155 Soulbound proxy contract is deployed.
* @param proxyAddr The address of the deployed proxy.
*/
event ERC1155SoulboundDeployed(address proxyAddr);
}

interface IERC1155SoulboundFactory is IERC1155SoulboundFactoryFunctions, IERC1155SoulboundFactorySignals {}
Loading

0 comments on commit 8fbefab

Please sign in to comment.