Skip to content

Commit

Permalink
## Version -- 1.5.18
Browse files Browse the repository at this point in the history
- Add manifold marketplace API interfaces
- Create a new version of nftc spec checker just for manifold stuff, and implement tests.
  • Loading branch information
mvillere committed Feb 8, 2024
1 parent c037333 commit 61f638d
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ As of 12/13/2022, this repo has been renamed from "nftc-open-contracts" to "nftc
## Version -- 1.5.next [Not published]
- TODO

## Version -- 1.5.18
- Add manifold marketplace API interfaces
- Create a new version of nftc spec checker just for manifold stuff, and implement tests.

## Version -- 1.5.17
- attempt to improve erc7572 implementation.
- add additional unit tests to nft spec checker to validate interface codes
Expand Down
41 changes: 41 additions & 0 deletions contracts/interfaces/manifoldxyz/ILazyDelivery.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
// Lifted from: https://github.com/delegatecash/delegation-registry/blob/main/src/IDelegationRegistry.sol
pragma solidity ^0.8.11;

interface ILazyDelivery {
/**
* @dev Deliver an asset and deliver to the specified party
* When implementing this interface, please ensure you restrict access.
* If using LazyDeliver.sol, you can use authorizedDelivererRequired modifier to restrict access.
* Delivery can be for an existing asset or newly minted assets.
*
* @param listingId The listingId associated with this delivery. Useful for permissioning.
* @param to The address to deliver the asset to
* @param assetId The assetId to deliver
* @param payableCount The number of assets to deliver
* @param payableAmount The amount seller will receive upon delivery of asset
* @param payableERC20 The erc20 token address of the amount (0x0 if ETH)
* @param index (Optional): Index value for certain sales methods
*
* Suggestion: If determining a refund amount based on total sales data, do not enable this function
* until the sales data is finalized and recorded in contract
*
* Exploit Prevention for dynamic/random assignment
* 1. Ensure attributes are not assigned until AFTER underlying mint if using _safeMint.
* This is to ensure a receiver cannot check attribute values on receive and revert transaction.
* However, even if this is the case, the recipient can wrap its mint in a contract that checks
* post mint completion and reverts if unsuccessful.
* 2. Ensure that "to" is not a contract address. This prevents a contract from doing the lazy
* mint, which could exploit random assignment by reverting if they do not receive the desired
* item post mint.
*/
function deliver(
uint40 listingId,
address to,
uint256 assetId,
uint24 payableCount,
uint256 payableAmount,
address payableERC20,
uint256 index
) external;
}
9 changes: 9 additions & 0 deletions contracts/interfaces/manifoldxyz/ILazyDeliveryMetadata.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
* Metadata for lazy delivery tokens
*/
interface ILazyDeliveryMetadata {
function assetURI(uint256 assetId) external view returns (string memory);
}
10 changes: 10 additions & 0 deletions contracts/interfaces/manifoldxyz/IPriceEngine.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

interface IPriceEngine {
/**
* @dev Determine price of an asset given the number
* already minted.
*/
function price(uint256 assetId, uint256 alreadyMinted, uint24 count) external view returns (uint256);
}
2 changes: 1 addition & 1 deletion contracts/utility/introspection/NFTSpecChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import '../../meta/IERC7572.sol';

// OZ Libraries
import '@openzeppelin/contracts/interfaces/IERC2981.sol';
import '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol';
import '@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol';
import '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';

/**
* @title NFTSpecChecker
Expand Down
81 changes: 81 additions & 0 deletions contracts/utility/introspection/NFTSpecCheckerManifold.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../../interfaces/manifoldxyz/ILazyDelivery.sol';
import '../../interfaces/manifoldxyz/ILazyDeliveryMetadata.sol';
import '../../interfaces/manifoldxyz/IPriceEngine.sol';

// OZ Libraries
import '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';

/**
* @title NFTSpecCheckerManifold
* @author @NiftyMike | @NFTCulture
* @dev Functionality to check a deployed contract against a list of Manifold specs.
*/
contract NFTSpecCheckerManifold {
using ERC165Checker for address;

/// @dev See {ERC165Checker-supportsInterface}
///> 0x01ffc9a7
bytes4 constant _ERC165_CONTRACT = type(IERC165).interfaceId;

/// @dev See {ERC165Checker-supportsInterface}
///> 0x8fb5170e
bytes4 constant _ILAZYDELIVERY_CONTRACT = type(ILazyDelivery).interfaceId;
///> 0xf1c68982
bytes4 constant _ILAZYDELIVERY_METADATA_CONTRACT = type(ILazyDeliveryMetadata).interfaceId;
///> 0x8e027244
bytes4 constant _IPRICEENGINE_CONTRACT = type(IPriceEngine).interfaceId;

function checkLazyDelivery(address targetContract) external view returns (bool) {
return _checkContract(targetContract, _ILAZYDELIVERY_CONTRACT);
}

function getLazyDeliveryCode() external pure returns (string memory) {
return bytes4ToString(_ILAZYDELIVERY_CONTRACT);
}

function checkLazyDeliveryMetadata(address targetContract) external view returns (bool) {
return _checkContract(targetContract, _ILAZYDELIVERY_METADATA_CONTRACT);
}

function getLazyDeliveryMetadataCode() external pure returns (string memory) {
return bytes4ToString(_ILAZYDELIVERY_METADATA_CONTRACT);
}

function checkPriceEngine(address targetContract) external view returns (bool) {
return _checkContract(targetContract, _IPRICEENGINE_CONTRACT);
}

function getPriceEngineCode() external pure returns (string memory) {
return bytes4ToString(_IPRICEENGINE_CONTRACT);
}

function _checkContract(address _contract, bytes4 interfaceId) internal view returns (bool) {
if (_contract.supportsInterface(interfaceId)) {
return true;
} else {
return false;
}
}

function bytes4ToString(bytes4 _bytes) internal pure returns (string memory) {
bytes memory byteArray = new bytes(8);
for (uint256 i = 0; i < 4; i++) {
uint8 currentByte = uint8(_bytes[i]);
byteArray[i * 2] = toHexChar(currentByte / 16);
byteArray[i * 2 + 1] = toHexChar(currentByte % 16);
}
return string(abi.encodePacked('0x', byteArray));
}

function toHexChar(uint8 _value) internal pure returns (bytes1) {
if (_value < 10) {
return bytes1(uint8(bytes1('0')) + _value);
} else {
return bytes1(uint8(bytes1('a')) + _value - 10);
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nftculture/nftc-contracts",
"version": "1.5.17",
"version": "1.5.18",
"description": "NFTCulture Open Source Contracts Project",
"author": "@NFTCulture",
"license": "MIT",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Contract } from '@ethersproject/contracts';
import { expect } from 'chai';
import * as dotenv from 'dotenv';
import type * as ethers from 'ethers';
import hre from 'hardhat';

import { addHardhatSignersToContext } from '../../../../src';

dotenv.config();

const TESTHARNESS_CONTRACT_NAME = 'TemplateTestHarness';
const SPEC_CHECKER_CONTRACT_NAME = 'NFTSpecCheckerManifold';

let _testHarnessContractFactory: ethers.ContractFactory;
let _specCheckerContractFactory: ethers.ContractFactory;

let _testHarnessInstance: Contract;
let _specCheckerInstance: Contract;

// Start test block
describe(`File:${__filename}\nContract: ${TESTHARNESS_CONTRACT_NAME}\n`, function () {
before(async function () {
_testHarnessContractFactory = await hre.ethers.getContractFactory(TESTHARNESS_CONTRACT_NAME);
_specCheckerContractFactory = await hre.ethers.getContractFactory(SPEC_CHECKER_CONTRACT_NAME);
});

beforeEach(async function () {
_testHarnessInstance = await _testHarnessContractFactory.deploy();
_specCheckerInstance = await _specCheckerContractFactory.deploy();
});

addHardhatSignersToContext();

context('NFTC Spec Checker Manifold:', function () {
it('uses valid interface codes', async function () {
const lazyDeliveryCode = await _specCheckerInstance.connect(this.owner).getLazyDeliveryCode();
expect(lazyDeliveryCode).to.equal('0x8fb5170e');

const lazyDeliveryMetadataCode = await _specCheckerInstance
.connect(this.owner)
.getLazyDeliveryMetadataCode();
expect(lazyDeliveryMetadataCode).to.equal('0xf1c68982');

const priceEngineCode = await _specCheckerInstance.connect(this.owner).getPriceEngineCode();
expect(priceEngineCode).to.equal('0x8e027244');
});
});
});

0 comments on commit 61f638d

Please sign in to comment.