From 514da32b4049a0cae98a1e3483f65d1e59b87f61 Mon Sep 17 00:00:00 2001 From: Felipe Faria Date: Wed, 13 Sep 2023 00:51:37 -0300 Subject: [PATCH] adding plugins and 1st test --- packages/marketplace/hardhat.config.ts | 2 + packages/marketplace/package.json | 8 +- .../marketplace/test/AssetMatcher.test.js | 216 ++++++++++++++++++ packages/marketplace/test/utils/EIP712.js | 78 +++++++ packages/marketplace/test/utils/assets.js | 131 +++++++++++ packages/marketplace/test/utils/order.js | 129 +++++++++++ 6 files changed, 562 insertions(+), 2 deletions(-) create mode 100644 packages/marketplace/test/AssetMatcher.test.js create mode 100644 packages/marketplace/test/utils/EIP712.js create mode 100644 packages/marketplace/test/utils/assets.js create mode 100644 packages/marketplace/test/utils/order.js diff --git a/packages/marketplace/hardhat.config.ts b/packages/marketplace/hardhat.config.ts index eff00c9059..36dcff8554 100644 --- a/packages/marketplace/hardhat.config.ts +++ b/packages/marketplace/hardhat.config.ts @@ -1,5 +1,7 @@ import {HardhatUserConfig} from 'hardhat/config'; +import "@nomiclabs/hardhat-truffle5"; + const config: HardhatUserConfig = { // solidity compiler version may be updated for new packages as required // to ensure packages use up-to-date dependencies diff --git a/packages/marketplace/package.json b/packages/marketplace/package.json index 120189de56..3e2f53b39a 100644 --- a/packages/marketplace/package.json +++ b/packages/marketplace/package.json @@ -6,14 +6,18 @@ "@sandbox-smart-contracts/dependency-metatx": "^0.0.2" }, "devDependencies": { + "@daonomic/tests-common": "^0.2.2", + "@nomiclabs/hardhat-truffle5": "^2.0.7", + "@nomiclabs/hardhat-web3": "^2.0.0", "@openzeppelin/contracts": "4.9.3", "@openzeppelin/contracts-upgradeable": "4.9.3", "hardhat": "^2.17.2", "ts-node": "^10.9.1", - "typescript": "^4.0.5" + "typescript": "^4.0.5", + "web3": "^1.10.2" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "hardhat test" }, "author": "", "license": "ISC" diff --git a/packages/marketplace/test/AssetMatcher.test.js b/packages/marketplace/test/AssetMatcher.test.js new file mode 100644 index 0000000000..5aa5ca0bf6 --- /dev/null +++ b/packages/marketplace/test/AssetMatcher.test.js @@ -0,0 +1,216 @@ +const AssetMatcher = artifacts.require('AssetMatcher.sol'); +const TestAssetMatcher = artifacts.require('TestAssetMatcher.sol'); + +const {expectThrow} = require('@daonomic/tests-common'); + +const order = require('./utils/order.js'); +const { + enc, + ETH, + ERC20, + ERC721, + ERC1155, + BUNDLE, + id, +} = require('./utils/assets.js'); + +contract('AssetMatcher', (accounts) => { + let testing; + + before(async function () { + testing = await AssetMatcher.new(); + }); + + it('setAssetMatcher works', async function () { + const encoded = enc(accounts[5]); + await expectThrow( + testing.matchAssets( + order.AssetType(ERC20, encoded), + order.AssetType(id('BLA'), encoded) + ) + ); + const testMatcher = await TestAssetMatcher.new(); + await testing.setAssetMatcher(id('BLA'), testMatcher.address); + const result = await testing.matchAssets( + order.AssetType(ERC20, encoded), + order.AssetType(id('BLA'), encoded) + ); + assert.equal(result[0], ERC20); + assert.equal(result[1], encoded); + }); + + describe('ETH', function () { + it('should extract ETH type if both are ETHs', async function () { + const result = await testing.matchAssets( + order.AssetType(ETH, '0x'), + order.AssetType(ETH, '0x') + ); + assert.equal(result[0], ETH); + }); + + it('should extract nothing if one is not ETH', async function () { + const result = await testing.matchAssets( + order.AssetType(ETH, '0x'), + order.AssetType(ERC20, '0x') + ); + assert.equal(result[0], 0); + }); + }); + + describe('ERC20', function () { + it('should extract ERC20 type if both are and addresses equal', async function () { + const encoded = enc(accounts[5]); + const result = await testing.matchAssets( + order.AssetType(ERC20, encoded), + order.AssetType(ERC20, encoded) + ); + assert.equal(result[0], ERC20); + assert.equal(result[1], encoded); + }); + + it("should extract nothing if erc20 don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(ERC20, enc(accounts[1])), + order.AssetType(ERC20, enc(accounts[2])) + ); + assert.equal(result[0], 0); + }); + + it('should extract nothing if other type is not ERC20', async function () { + const result = await testing.matchAssets( + order.AssetType(ERC20, enc(accounts[1])), + order.AssetType(ETH, '0x') + ); + assert.equal(result[0], 0); + }); + }); + + describe('ERC721', function () { + it('should extract ERC721 type if both are equal', async function () { + const encoded = enc(accounts[5], 100); + const result = await testing.matchAssets( + order.AssetType(ERC721, encoded), + order.AssetType(ERC721, encoded) + ); + assert.equal(result[0], ERC721); + assert.equal(result[1], encoded); + }); + + it("should extract nothing if tokenIds don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(ERC721, enc(accounts[5], 100)), + order.AssetType(ERC721, enc(accounts[5], 101)) + ); + assert.equal(result[0], 0); + }); + + it("should extract nothing if addresses don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(ERC721, enc(accounts[4], 100)), + order.AssetType(ERC721, enc(accounts[5], 100)) + ); + assert.equal(result[0], 0); + }); + + it('should extract nothing if other type is not ERC721', async function () { + const result = await testing.matchAssets( + order.AssetType(ERC721, enc(accounts[5], 100)), + order.AssetType(ETH, '0x') + ); + assert.equal(result[0], 0); + }); + }); + + describe('ERC1155', function () { + it('should extract ERC1155 type if both are equal', async function () { + const encoded = enc(accounts[5], 100); + const result = await testing.matchAssets( + order.AssetType(ERC1155, encoded), + order.AssetType(ERC1155, encoded) + ); + assert.equal(result[0], ERC1155); + assert.equal(result[1], encoded); + }); + + it("should extract nothing if tokenIds don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(ERC1155, enc(accounts[5], 100)), + order.AssetType(ERC1155, enc(accounts[5], 101)) + ); + assert.equal(result[0], 0); + }); + + it("should extract nothing if addresses don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(ERC1155, enc(accounts[4], 100)), + order.AssetType(ERC1155, enc(accounts[5], 100)) + ); + assert.equal(result[0], 0); + }); + + it('should extract nothing if other type is not erc1155', async function () { + const encoded = enc(accounts[5], 100); + const result = await testing.matchAssets( + order.AssetType(ERC1155, encoded), + order.AssetType(ERC721, encoded) + ); + assert.equal(result[0], 0); + }); + }); + + describe('BUNDLE', function () { + it('should extract BUNDLE type if both are equal', async function () { + const encoded = enc(accounts[5], 100); + const result = await testing.matchAssets( + order.AssetType(BUNDLE, encoded), + order.AssetType(BUNDLE, encoded) + ); + assert.equal(result[0], BUNDLE); + assert.equal(result[1], encoded); + }); + + it("should extract nothing if tokenIds don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(BUNDLE, enc(accounts[5], 100)), + order.AssetType(BUNDLE, enc(accounts[5], 101)) + ); + assert.equal(result[0], 0); + }); + + it("should extract nothing if addresses don't match", async function () { + const result = await testing.matchAssets( + order.AssetType(BUNDLE, enc(accounts[4], 100)), + order.AssetType(BUNDLE, enc(accounts[5], 100)) + ); + assert.equal(result[0], 0); + }); + + it('should extract nothing if other type is not a BUNDLE', async function () { + const encoded = enc(accounts[5], 100); + const result = await testing.matchAssets( + order.AssetType(BUNDLE, encoded), + order.AssetType(ERC721, encoded) + ); + assert.equal(result[0], 0); + }); + }); + + describe('generic', function () { + it('should extract left type if asset types are equal', async function () { + const result = await testing.matchAssets( + order.AssetType('0x00112233', '0x1122'), + order.AssetType('0x00112233', '0x1122') + ); + assert.equal(result[0], '0x00112233'); + assert.equal(result[1], '0x1122'); + }); + + it('should extract nothing single byte differs', async function () { + const result = await testing.matchAssets( + order.AssetType('0x00112233', '0x1122'), + order.AssetType('0x00112233', '0x1111') + ); + assert.equal(result[0], 0); + }); + }); +}); diff --git a/packages/marketplace/test/utils/EIP712.js b/packages/marketplace/test/utils/EIP712.js new file mode 100644 index 0000000000..c63fd686d6 --- /dev/null +++ b/packages/marketplace/test/utils/EIP712.js @@ -0,0 +1,78 @@ +// TODO: This is the same as the root folder scripts... fix it +const DOMAIN_TYPE = [ + { + type: 'string', + name: 'name', + }, + { + type: 'string', + name: 'version', + }, + { + type: 'uint256', + name: 'chainId', + }, + { + type: 'address', + name: 'verifyingContract', + }, +]; + +module.exports = { + createTypeData: function (domainData, primaryType, message, types) { + return { + types: Object.assign( + { + EIP712Domain: DOMAIN_TYPE, + }, + types + ), + domain: domainData, + primaryType: primaryType, + message: message, + }; + }, + + signTypedData: function (web3, from, data) { + return new Promise((resolve, reject) => { + function cb(err, result) { + if (result.error) { + return reject(result.error); + } + if (err) { + return reject(err); + } + + const sig = result.result; + const sig0 = sig.substring(2); + const r = '0x' + sig0.substring(0, 64); + const s = '0x' + sig0.substring(64, 128); + const v = parseInt(sig0.substring(128, 130), 16); + + resolve({ + data, + sig, + v, + r, + s, + }); + } + + let send = web3.currentProvider.sendAsync; + if (!send) send = web3.currentProvider.send; + send.bind(web3.currentProvider)( + { + jsonrpc: '2.0', + method: web3.currentProvider.isMetaMask + ? 'eth_signTypedData_v3' + : 'eth_signTypedData', + params: web3.currentProvider.isMetaMask + ? [from, JSON.stringify(data)] + : [from, data], + id: new Date().getTime(), + }, + cb + ); + }); + }, +}; diff --git a/packages/marketplace/test/utils/assets.js b/packages/marketplace/test/utils/assets.js new file mode 100644 index 0000000000..8acf03bd7a --- /dev/null +++ b/packages/marketplace/test/utils/assets.js @@ -0,0 +1,131 @@ +// TODO: This is the same as the root folder scripts... fix it +const ethUtil = require('ethereumjs-util'); +const Web3 = require('web3'); + +function id(str) { + return `0x${ethUtil + .keccak256(Buffer.from(str)) + .toString('hex') + .substring(0, 8)}`; +} + +function enc(token, tokenId) { + const web3 = new Web3(); + if (tokenId) { + return web3.eth.abi.encodeParameters( + ['address', 'uint256'], + [token, tokenId] + ); + } else { + return web3.eth.abi.encodeParameter('address', token); + } +} + +function encBundle(erc20, erc721, erc1155) { + const web3 = new Web3(); + return web3.eth.abi.encodeParameters( + [ + { + name: 'ERC20Details', + type: 'tupple[]', + components: [ + { + name: 'token', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + ], + }, + { + name: 'ERC721Details', + type: 'tupple[]', + components: [ + { + name: 'token', + type: 'address', + }, + { + name: 'id', + type: 'uint256', + }, + { + name: 'value', + type: 'uint256', + }, + ], + }, + { + name: 'ERC1155Details', + type: 'tupple[]', + components: [ + { + name: 'token', + type: 'address', + }, + { + name: 'id', + type: 'uint256', + }, + { + name: 'value', + type: 'uint256', + }, + ], + }, + ], + [erc20, erc721, erc1155] + ); +} + +function percentage(number, percentage) { + return (number * percentage) / 10000; +} + +const ETH = id('ETH'); +const ERC20 = id('ERC20'); +const ERC721 = id('ERC721'); +const ERC721_LAZY = id('ERC721_LAZY'); +const ERC1155 = id('ERC1155'); +const ERC1155_LAZY = id('ERC1155_LAZY'); +const BUNDLE = id('BUNDLE'); +const COLLECTION = id('COLLECTION'); +const ORDER_DATA_BUY = id('BUY'); +const ORDER_DATA_SELL = id('SELL'); +const TO_MAKER = id('TO_MAKER'); +const TO_TAKER = id('TO_TAKER'); +const PROTOCOL = id('PROTOCOL'); +const ROYALTY = id('ROYALTY'); +const ORIGIN = id('ORIGIN'); +const PAYOUT = id('PAYOUT'); +const LOCK = id('LOCK'); +const UNLOCK = id('UNLOCK'); +const TO_LOCK = id('TO_LOCK'); + +module.exports = { + id, + ETH, + ERC20, + ERC721, + ERC721_LAZY, + ERC1155, + ERC1155_LAZY, + BUNDLE, + ORDER_DATA_SELL, + ORDER_DATA_BUY, + TO_MAKER, + TO_TAKER, + PROTOCOL, + ROYALTY, + ORIGIN, + PAYOUT, + COLLECTION, + LOCK, + UNLOCK, + TO_LOCK, + enc, + encBundle, + percentage, +}; diff --git a/packages/marketplace/test/utils/order.js b/packages/marketplace/test/utils/order.js new file mode 100644 index 0000000000..b7f68ac683 --- /dev/null +++ b/packages/marketplace/test/utils/order.js @@ -0,0 +1,129 @@ +// TODO: This is the same as the root folder scripts... fix it +const EIP712 = require('./EIP712'); + +function AssetType(assetClass, data) { + return {assetClass, data}; +} + +function Asset(assetClass, assetData, value) { + return {assetType: AssetType(assetClass, assetData), value}; +} + +function Order( + maker, + makeAsset, + taker, + takeAsset, + salt, + start, + end, + dataType, + data +) { + return {maker, makeAsset, taker, takeAsset, salt, start, end, dataType, data}; +} + +function OrderBack( + buyer, + maker, + makeAsset, + taker, + takeAsset, + salt, + start, + end, + dataType, + data +) { + return { + buyer, + maker, + makeAsset, + taker, + takeAsset, + salt, + start, + end, + dataType, + data, + }; +} + +const Types = { + AssetType: [ + {name: 'assetClass', type: 'bytes4'}, + {name: 'data', type: 'bytes'}, + ], + Asset: [ + {name: 'assetType', type: 'AssetType'}, + {name: 'value', type: 'uint256'}, + ], + Order: [ + {name: 'maker', type: 'address'}, + {name: 'makeAsset', type: 'Asset'}, + {name: 'taker', type: 'address'}, + {name: 'takeAsset', type: 'Asset'}, + {name: 'salt', type: 'uint256'}, + {name: 'start', type: 'uint256'}, + {name: 'end', type: 'uint256'}, + {name: 'dataType', type: 'bytes4'}, + {name: 'data', type: 'bytes'}, + ], +}; + +const TypesBack = { + AssetType: [ + {name: 'assetClass', type: 'bytes4'}, + {name: 'data', type: 'bytes'}, + ], + Asset: [ + {name: 'assetType', type: 'AssetType'}, + {name: 'value', type: 'uint256'}, + ], + OrderBack: [ + {name: 'buyer', type: 'address'}, + {name: 'maker', type: 'address'}, + {name: 'makeAsset', type: 'Asset'}, + {name: 'taker', type: 'address'}, + {name: 'takeAsset', type: 'Asset'}, + {name: 'salt', type: 'uint256'}, + {name: 'start', type: 'uint256'}, + {name: 'end', type: 'uint256'}, + {name: 'dataType', type: 'bytes4'}, + {name: 'data', type: 'bytes'}, + ], +}; + +async function sign(web3, order, account, verifyingContract) { + const chainId = config.network_id; + const data = EIP712.createTypeData( + { + name: 'Exchange', + version: '1', + chainId, + verifyingContract, + }, + 'Order', + order, + Types + ); + return (await EIP712.signTypedData(web3, account, data)).sig; +} + +async function signBack(web3, order, account, verifyingContract) { + const chainId = config.network_id; + const data = EIP712.createTypeData( + { + name: 'Exchange', + version: '1', + chainId, + verifyingContract, + }, + 'OrderBack', + order, + TypesBack + ); + return (await EIP712.signTypedData(web3, account, data)).sig; +} + +module.exports = {AssetType, Asset, Order, OrderBack, sign, signBack};