Skip to content

Commit

Permalink
## Version -- 1.5.21
Browse files Browse the repository at this point in the history
- Add ERC20 interfaces to the OZ Interface definition
- also write supports interface test for ERC20_165 Mock.
- Add manifold version of shouldSupportsInterface unit test generator.
  • Loading branch information
mvillere committed Feb 16, 2024
1 parent dba8e48 commit 0fde6d2
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 3 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ 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.21
- Add ERC20 interfaces to the OZ Interface definition
- also write supports interface test for ERC20_165 Mock.

- Add manifold version of shouldSupportsInterface unit test generator.

## Version -- 1.5.20
- Implement ERC165 compliant version of ERC20.
Expand Down
1 change: 0 additions & 1 deletion contracts/interfaces/manifoldxyz/ILazyDelivery.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: MIT
// Lifted from: https://github.com/delegatecash/delegation-registry/blob/main/src/IDelegationRegistry.sol
pragma solidity ^0.8.11;

interface ILazyDelivery {
Expand Down
47 changes: 47 additions & 0 deletions contracts/mocks/interfaces/manifoldxyz/ManifoldClientMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

// OZ References
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';

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

/**
* @title ManifoldClientMock
* @author @NiftyMike | @NFTCulture
* @dev Just an empty mock test harness for manifold.xyz interfaces.
*/
contract ManifoldClientMock is ILazyDelivery, ILazyDeliveryMetadata, IPriceEngine, ERC165 {
constructor() {
// Implementation version: 1
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
if (
interfaceId == type(ILazyDelivery).interfaceId ||
interfaceId == type(ILazyDeliveryMetadata).interfaceId ||
interfaceId == type(IPriceEngine).interfaceId
) {
return true;
}

return super.supportsInterface(interfaceId);
}

function deliver(
uint40 listingId,
address to,
uint256 assetId,
uint24 payableCount,
uint256 payableAmount,
address payableERC20,
uint256 index
) external override {}

function assetURI(uint256 assetId) external view override returns (string memory) {}

function price(uint256 assetId, uint256 alreadyMinted, uint24 count) external view override returns (uint256) {}
}
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.20",
"version": "1.5.21",
"description": "NFTCulture Open Source Contracts Project",
"author": "@NFTCulture",
"license": "MIT",
Expand Down
75 changes: 75 additions & 0 deletions src/behaviors/utility/introspection/SupportsInterfacesManifold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { expect } from 'chai';

import {
MXYZ_FN_SIGNATURES,
MXYZ_INTERFACE_IDS,
MXYZ_INTERFACES
} from '../../../for-tests/utils/introspection/MXYZ_Interfaces';
import { ERC165 } from '../../../for-tests/utils/introspection/makeInterfaceId';

const INVALID_ID = '0xffffffff';

export function shouldSupportInterfacesManifold(contractName: string, interfaces: string[] = []) {
describe(`Contract ${contractName} shouldSupportInterfaces ${interfaces.join(', ')}:`, function () {
before(async function () {
// TODO?
});

beforeEach(async function () {
expect(this).to.have.property('contractUnderTest'); // Must be set in caller
expect(this).to.have.property('owner'); // Must be set in caller
});

describe('when the Manifold interfaceId is supported', function () {
it('uses less than 30k gas', async function () {
for (const k of interfaces) {
const interfaceId = MXYZ_INTERFACE_IDS[k] ?? k;

expect(await this.contractUnderTest.estimateGas.supportsInterface(interfaceId)).to.be.lte(30000);
}
});

it('returns true', async function () {
for (const k of interfaces) {
const interfaceId = MXYZ_INTERFACE_IDS[k] ?? k;
expect(await this.contractUnderTest.supportsInterface(interfaceId)).to.equal(
true,
`does not support ${k}`
);
}
});
});

describe('when the Manifold interfaceId is not supported', function () {
it('uses less than 30k', async function () {
expect(await this.contractUnderTest.estimateGas.supportsInterface(INVALID_ID)).to.be.lte(30000);
});

it('returns false', async function () {
expect(await this.contractUnderTest.supportsInterface(INVALID_ID)).to.be.equal(
false,
`supports ${INVALID_ID}`
);
});
});

it('all Manifold interface functions are in ABI', async function () {
for (const k of interfaces) {
// skip interfaces for which we don't have a function list
if (MXYZ_INTERFACES[k] === undefined) continue;
for (const fnName of MXYZ_INTERFACES[k]) {
const fnSig = MXYZ_FN_SIGNATURES[fnName];
//console.log(`Signature: [${fnSig}] && Function: [${fnName}]`);
const abiFunctions = Object.keys(this.contractUnderTest.functions);

// MV: I'm not sure where in HardhatEthers the function interfaceIds are stored, so computing
// them on the fly from the function names that we do have.
expect(abiFunctions.filter((fn) => ERC165([fn]) === fnSig).length).to.equal(
1,
`did not find ${fnName}`
);
}
}
});
});
}
1 change: 1 addition & 0 deletions src/behaviors/utility/introspection/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// created from 'create-ts-index'

export * from './SupportsInterfaces';
export * from './SupportsInterfacesManifold';
27 changes: 27 additions & 0 deletions src/for-tests/utils/introspection/MXYZ_Interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Written by @NiftyMike | @NFTCulture. Copyright 2024. License: MIT.
// Based on Unknown version of Manifold.xyz interfaces, received from dev team.

import { ERC165 } from './makeInterfaceId';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const MXYZ_FN_SIGNATURES: { [k: string]: any } = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const MXYZ_INTERFACE_IDS: { [k: string]: any } = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const MXYZ_INTERFACES: { [k: string]: any } = {
LazyDelivery: ['deliver(uint40,address,uint256,uint24,uint256,address,uint256)'],
LazyDeliveryMetadata: ['assetURI(uint256)'],
PriceEngine: ['price(uint256,uint256,uint24)']
};

for (const k of Object.getOwnPropertyNames(MXYZ_INTERFACES)) {
MXYZ_INTERFACE_IDS[k] = ERC165(MXYZ_INTERFACES[k]);
let fnName = '';
for (fnName of MXYZ_INTERFACES[k]) {
// the interface id of a single function is equivalent to its function signature
const interfaceId = ERC165([fnName]);
//console.log(`Signature: [${fnName}] && InterfaceId: [${interfaceId}]`);

MXYZ_FN_SIGNATURES[fnName] = interfaceId;
}
}
1 change: 1 addition & 0 deletions src/for-tests/utils/introspection/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// created from 'create-ts-index'

export * from './MXYZ_Interfaces';
export * from './OZ_Interfaces';
export * from './makeInterfaceId';
36 changes: 36 additions & 0 deletions test/contracts/interfaces/manifoldxyz/ManifoldClientMock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as dotenv from 'dotenv';
import hre from 'hardhat';

import { shouldSupportInterfaces } from '../../../../src/behaviors/utility/introspection/SupportsInterfaces';
import { addHardhatSignersToContext } from '../../../../src/for-tests/context/HardhatHelpers';
import { ManifoldClientMock, ManifoldClientMock__factory } from '../../../../typechain-types';
import { shouldSupportInterfacesManifold } from '../../../../src/behaviors/utility/introspection/SupportsInterfacesManifold';

dotenv.config();

const TESTHARNESS_CONTRACT_NAME = 'ManifoldClientMock';

let _testHarnessContractFactory: ManifoldClientMock__factory;
let _testHarnessInstance: ManifoldClientMock;

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

beforeEach(async function () {
_testHarnessInstance = await _testHarnessContractFactory.deploy();
await _testHarnessInstance.deployed();

this.contractUnderTest = _testHarnessInstance;
});

addHardhatSignersToContext();

shouldSupportInterfaces(TESTHARNESS_CONTRACT_NAME, ['ERC165']);

shouldSupportInterfacesManifold(TESTHARNESS_CONTRACT_NAME, ['LazyDelivery', 'LazyDeliveryMetadata', 'PriceEngine']);
});

0 comments on commit 0fde6d2

Please sign in to comment.