-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- 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
Showing
9 changed files
with
191 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
contracts/mocks/interfaces/manifoldxyz/ManifoldClientMock.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/behaviors/utility/introspection/SupportsInterfacesManifold.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}` | ||
); | ||
} | ||
} | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
36
test/contracts/interfaces/manifoldxyz/ManifoldClientMock.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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']); | ||
}); |