From 42eb819765b64631d901b868ec0ce83a8952add6 Mon Sep 17 00:00:00 2001 From: lacoop6tu Date: Thu, 21 Oct 2021 10:47:21 -0500 Subject: [PATCH] complete NFTFactory class, add some tests --- src/factories/NFTFactory.ts | 209 +++++++++++++++++++++++++++++++++++ test/TestContractHandler.ts | 1 + test/unit/NFTFactory.test.ts | 44 ++++++-- 3 files changed, 246 insertions(+), 8 deletions(-) diff --git a/src/factories/NFTFactory.ts b/src/factories/NFTFactory.ts index 2fe5d04e..277d79ab 100644 --- a/src/factories/NFTFactory.ts +++ b/src/factories/NFTFactory.ts @@ -5,10 +5,48 @@ import { AbiItem } from 'web3-utils' import defaultFactory721ABI from '@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json' import { Logger, getFairGasPrice, generateDtName } from '../utils' + interface Template { templateAddress: string isActive: boolean } + +interface TokenOrder { + tokenAddress: string + consumer: string + amount: number + serviceId: number + consumeFeeAddress: string + consumeFeeToken: string // address of the token marketplace wants to add fee on top + consumeFeeAmount: number +} + +interface NFTCreateData { + name: string + symbol: string + templateIndex: number + baseURI: string +} + +interface ErcCreateData { + templateIndex: number + strings: string[] + addresses: string[] + uints:(string | number)[] + bytess: string[] +} + +interface PoolData { + addresses: string[] + ssParams: number[] + swapFees: number[] +} + +interface FixedData { + fixedPriceAddress: string + addresses: string[] + uints: number[] +} /** * Provides an interface for NFT DataTokens */ @@ -152,6 +190,24 @@ export class NFTFactory { return template } + /** Check if ERC20 is deployed from the factory + * @param {String} datatoken Datatoken address we want to check + * @return {Promise} return true if deployed from this factory + */ + public async checkDatatoken(datatoken: string): Promise { + const isDeployed = await this.factory721.methods.erc20List(datatoken).call() + return isDeployed + } + + /** Check if NFT is deployed from the factory + * @param {String} nftAddress nftAddress address we want to check + * @return {Promise} return address(0) if it's not, or the nftAddress if true + */ + public async checkNFT(nftAddress: string): Promise { + const confirmAddress = await this.factory721.methods.erc721List(nftAddress).call() + return confirmAddress + } + /** * Add a new erc721 token template - only factory Owner * @param {String} address @@ -367,4 +423,157 @@ export class NFTFactory { return trxReceipt } + + /** + * @dev startMultipleTokenOrder + * Used as a proxy to order multiple services + * Users can have inifinite approvals for fees for factory instead of having one approval/ erc20 contract + * Requires previous approval of all : + * - consumeFeeTokens + * - publishMarketFeeTokens + * - erc20 datatokens + * @param orders an array of struct tokenOrder + * @return {Promise} transaction receipt + */ + + public async startMultipleTokenOrder( + address: string, + orders: TokenOrder[] + ): Promise { + // Get estimated gas value + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await this.factory721.methods + .startMultipleTokenOrder(orders) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + + // Invoke createToken function of the contract + const trxReceipt = await this.factory721.methods + .startMultipleTokenOrder(orders) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + + return trxReceipt + } + + /** + * @dev createNftWithErc + * Creates a new NFT, then a ERC20,all in one call + * @param _NftCreateData input data for nft creation + * @param _ErcCreateData input data for erc20 creation + * @return {Promise} transaction receipt + */ + + public async createNftWithErc( + address: string, + nftCreateData: NFTCreateData, + ercCreateData: ErcCreateData + ): Promise { + // Get estimated gas value + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await this.factory721.methods + .createNftWithErc(nftCreateData, ercCreateData) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + + // Invoke createToken function of the contract + const trxReceipt = await this.factory721.methods + .createNftWithErc(nftCreateData, ercCreateData) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + + return trxReceipt + } + + /** + * @dev createNftErcWithPool + * Creates a new NFT, then a ERC20, then a Pool, all in one call + * Use this carefully, because if Pool creation fails, you are still going to pay a lot of gas + * @param _NftCreateData input data for NFT Creation + * @param _ErcCreateData input data for ERC20 Creation + * @param _PoolData input data for Pool Creation + * @return {Promise} transaction receipt + */ + + public async createNftErcWithPool( + address: string, + nftCreateData: NFTCreateData, + ercCreateData: ErcCreateData, + poolData: PoolData + ): Promise { + // Get estimated gas value + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await this.factory721.methods + .createNftErcWithPool(nftCreateData, ercCreateData, poolData) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + + // Invoke createToken function of the contract + const trxReceipt = await this.factory721.methods + .createNftErcWithPool(nftCreateData, ercCreateData, poolData) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + + return trxReceipt + } + + /** + * @dev createNftErcWithFixedRate + * Creates a new NFT, then a ERC20, then a FixedRateExchange, all in one call + * Use this carefully, because if Fixed Rate creation fails, you are still going to pay a lot of gas + * @param _NftCreateData input data for NFT Creation + * @param _ErcCreateData input data for ERC20 Creation + * @param _FixedData input data for FixedRate Creation + * @return {Promise} transaction receipt + */ + + public async createNftErcWithFixedRate( + address: string, + nftCreateData: NFTCreateData, + ercCreateData: ErcCreateData, + fixedData: FixedData + ): Promise { + // Get estimated gas value + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await this.factory721.methods + .createNftErcWithFixedRate(nftCreateData, ercCreateData) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + + // Invoke createToken function of the contract + const trxReceipt = await this.factory721.methods + .createNftErcWithFixedRate(nftCreateData, ercCreateData) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + + return trxReceipt + } } diff --git a/test/TestContractHandler.ts b/test/TestContractHandler.ts index 28f766b7..aef48025 100644 --- a/test/TestContractHandler.ts +++ b/test/TestContractHandler.ts @@ -289,6 +289,7 @@ export class TestContractHandler { .addSSContract(this.sideStakingAddress) .send({ from: owner }) // TODO: add OPF deployment and update argument + // TODO: how are we going to call those functions with an OPF contract? it should be a multisig the owner await RouterContract.methods .changeRouterOwner(communityCollector) .send({ from: owner }) diff --git a/test/unit/NFTFactory.test.ts b/test/unit/NFTFactory.test.ts index 8e6f6d9f..0cfec17b 100644 --- a/test/unit/NFTFactory.test.ts +++ b/test/unit/NFTFactory.test.ts @@ -1,4 +1,4 @@ -import { assert } from 'chai' +import { assert, expect } from 'chai' import { AbiItem } from 'web3-utils/types' import { TestContractHandler } from '../TestContractHandler' import Web3 from 'web3' @@ -13,7 +13,6 @@ import PoolTemplate from '@oceanprotocol/contracts/artifacts/contracts/pools/bal import { LoggerInstance } from '../../src/utils' // import { NFTDataToken } from '../../../src/datatokens/NFTDatatoken' import { NFTFactory } from '../../src/factories/NFTFactory' -// import { DT20Factory } from '../../../src/factories/DT20Factory' const web3 = new Web3('http://127.0.0.1:8545') @@ -22,6 +21,7 @@ describe('NFT Factory test', () => { let nftOwner: string let user1: string let user2: string + let user3: string let contracts: TestContractHandler let nftFactory: NFTFactory @@ -59,6 +59,8 @@ describe('NFT Factory test', () => { factoryOwner = contracts.accounts[0] nftOwner = contracts.accounts[1] user1 = contracts.accounts[2] + user2 = contracts.accounts[3] + user3 = contracts.accounts[4] console.log(factoryOwner) await contracts.deployContracts(factoryOwner, Router.abi as AbiItem[]) @@ -71,12 +73,12 @@ describe('NFT Factory test', () => { it('#getCurrentNFTCount - should return actual nft count (0)', async () => { const nftCount = await nftFactory.getCurrentNFTCount() - assert(nftCount === 0) + expect(nftCount).to.equal('0') }) it('#getCurrentTokenCount - should return actual token count (0)', async () => { const tokenCount = await nftFactory.getCurrentTokenCount() - assert(tokenCount === 0) + expect(tokenCount).to.equal('0') }) it('#getOwner - should return actual owner', async () => { const owner = await nftFactory.getOwner() @@ -84,11 +86,11 @@ describe('NFT Factory test', () => { }) it('#getCurrentNFTTemplateCount - should return actual nft template count (1)', async () => { const nftTemplateCount = await nftFactory.getCurrentNFTTemplateCount() - assert(nftTemplateCount === 1) + expect(nftTemplateCount).to.equal('1') }) it('#getCurrentTokenTemplateCount - should return actual token template count (1)', async () => { const tokenTemplateCount = await nftFactory.getCurrentTokenTemplateCount() - assert(tokenTemplateCount === 1) + expect(tokenTemplateCount).to.equal('1') }) it('#getNFTTemplate - should return NFT template struct', async () => { const nftTemplate = await nftFactory.getNFTTemplate(1) @@ -103,7 +105,7 @@ describe('NFT Factory test', () => { it('#addNFTTemplate - should add NFT template if factory owner', async () => { await nftFactory.addNFTTemplate(contracts.accounts[0], contracts.fixedRateAddress) // contracts.fixedRateAddress it's just a dummy contract in this case const nftTemplateCount = await nftFactory.getCurrentNFTTemplateCount() - assert(nftTemplateCount === 2) + expect(nftTemplateCount).to.equal('2') const nftTemplate = await nftFactory.getNFTTemplate(2) assert(nftTemplate.isActive === true) assert(nftTemplate.templateAddress === contracts.fixedRateAddress) @@ -127,7 +129,7 @@ describe('NFT Factory test', () => { it('#addTokenTemplate - should add Datatoken template if factory owner', async () => { await nftFactory.addTokenTemplate(contracts.accounts[0], contracts.fixedRateAddress) // contracts.fixedRateAddress it's just a dummy contract in this case const tokenTemplateCount = await nftFactory.getCurrentTokenTemplateCount() - assert(tokenTemplateCount === 2) + expect(tokenTemplateCount).to.equal('2') const nftTemplate = await nftFactory.getTokenTemplate(2) assert(nftTemplate.isActive === true) assert(nftTemplate.templateAddress === contracts.fixedRateAddress) @@ -149,4 +151,30 @@ describe('NFT Factory test', () => { tokenTemplate = await nftFactory.getTokenTemplate(2) assert(tokenTemplate.isActive === true) }) + + it('#createNFTwithErc - should create an NFT and a Datatoken', async () => { + const nftData = { + name: '72120Bundle', + symbol: '72Bundle', + templateIndex: 1, + baseURI: 'https://oceanprotocol.com/nft/' + } + const ercData = { + templateIndex: 1, + strings: ['ERC20B1', 'ERC20DT1Symbol'], + addresses: [user2, user3, user2, '0x0000000000000000000000000000000000000000'], + uints: [web3.utils.toWei('10000'), 0], + bytess: [] + } + + const txReceipt = await nftFactory.createNftWithErc( + contracts.accounts[0], + nftData, + ercData + ) + expect(txReceipt.events.NFTCreated.event === 'NFTCreated') + expect(txReceipt.events.TokenCreated.event === 'TokenCreated') + console.log(txReceipt.events.NFTCreated.returnValues.newTokenAddress) + console.log(txReceipt.events.TokenCreated.returnValues.newTokenAddress) + }) })