diff --git a/package.json b/package.json index 273a808a..ab6a1001 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "test:pool": "npm run mocha -- 'test/unit/pools/balancer/Pool.test.ts'", "test:dispenser": "npm run mocha -- 'test/unit/pools/dispenser/Dispenser.test.ts'", "test:dt": "npm run mocha -- 'test/unit/tokens/Datatoken.test.ts'", - "test:nftDt": "npm run mocha -- 'test/unit/Nft.test.ts'", + "test:nftDt": "npm run mocha -- 'test/unit/tokens/Nft.test.ts'", "test:factory": "npm run mocha -- 'test/unit/NftFactory.test.ts'", "test:router": "npm run mocha -- 'test/unit/pools/Router.test.ts'", "test:publishAll": "npm run mocha -- 'test/integration/PublishFlows.test.ts'", diff --git a/src/interfaces/Erc721Interface.ts b/src/interfaces/Erc721Interface.ts new file mode 100644 index 00000000..dbdd99fb --- /dev/null +++ b/src/interfaces/Erc721Interface.ts @@ -0,0 +1,13 @@ +import { MetadataProof } from '../@types' + +export interface MetadataAndTokenURI { + metaDataState: number + metaDataDecryptorUrl: string + metaDataDecryptorAddress: string + flags: string + data: string + metaDataHash: string + tokenId: number + tokenURI: string + metadataProofs: MetadataProof[] +} diff --git a/src/tokens/NFT.ts b/src/tokens/NFT.ts index cb2f61da..7ff7856e 100644 --- a/src/tokens/NFT.ts +++ b/src/tokens/NFT.ts @@ -12,6 +12,7 @@ import { import { Contract } from 'web3-eth-contract' import { MetadataProof } from '../../src/@types' import { Config } from '../models/index.js' +import { MetadataAndTokenURI } from '../interfaces/Erc721Interface' /** * ERC721 ROLES @@ -991,7 +992,7 @@ export class Nft { * @param {String} nftAddress erc721 contract adress * @param {String} metadataUpdater metadataUpdater address * @param {Number} metadataState User which will receive the NFT, will also be set as Manager - * @param {String} metaDataDecryptorUrl + * @param {String} metadataDecryptorUrl * @param {Number} tokenId The id of the token to be transfered * @param {Contract} nftContract optional contract instance * @return {Promise} @@ -1000,8 +1001,8 @@ export class Nft { nftAddress: string, metadataUpdater: string, metadataState: number, - metaDataDecryptorUrl: string, - metaDataDecryptorAddress: string, + metadataDecryptorUrl: string, + metadataDecryptorAddress: string, flags: string, data: string, metadataHash: string, @@ -1021,8 +1022,8 @@ export class Nft { estGas = await nftContract.methods .setMetaData( metadataState, - metaDataDecryptorUrl, - metaDataDecryptorAddress, + metadataDecryptorUrl, + metadataDecryptorAddress, flags, data, metadataHash, @@ -1049,8 +1050,8 @@ export class Nft { nftAddress: string, address: string, metadataState: number, - metaDataDecryptorUrl: string, - metaDataDecryptorAddress: string, + metadataDecryptorUrl: string, + metadataDecryptorAddress: string, flags: string, data: string, metadataHash: string, @@ -1068,8 +1069,8 @@ export class Nft { nftAddress, address, metadataState, - metaDataDecryptorUrl, - metaDataDecryptorAddress, + metadataDecryptorUrl, + metadataDecryptorAddress, flags, data, metadataHash, @@ -1079,8 +1080,8 @@ export class Nft { const trxReceipt = await nftContract.methods .setMetaData( metadataState, - metaDataDecryptorUrl, - metaDataDecryptorAddress, + metadataDecryptorUrl, + metadataDecryptorAddress, flags, data, metadataHash, @@ -1095,6 +1096,77 @@ export class Nft { return trxReceipt } + /** + * Estimate gas cost for setMetadata method + * @param {String} nftAddress erc721 contract adress + * @param {String} metadataUpdater metadataUpdater address + * @param {MetaDataAndTokenURI} metadataAndTokenURI metaDataAndTokenURI object + * @param {Contract} nftContract optional contract instance + * @return {Promise} + */ + public async estGasSetMetadataAndTokenURI( + nftAddress: string, + metadataUpdater: string, + metadataAndTokenURI: MetadataAndTokenURI, + contractInstance?: Contract + ): Promise { + const nftContract = + contractInstance || + setContractDefaults( + new this.web3.eth.Contract(this.nftAbi, nftAddress), + this.config + ) + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await nftContract.methods + .setMetaDataAndTokenURI(metadataAndTokenURI) + .estimateGas({ from: metadataUpdater }, (err, estGas) => + err ? gasLimitDefault : estGas + ) + } catch (e) { + estGas = gasLimitDefault + } + + return estGas + } + + /** + * Helper function to improve UX sets both MetaData & TokenURI in one tx + * @param {String} nftAddress erc721 contract adress + * @param {String} address Caller address + * @param {MetadataAndTokenURI} metadataAndTokenURI metaDataAndTokenURI object + * @return {Promise} trxReceipt + */ + public async setMetadataAndTokenURI( + nftAddress: string, + metadataUpdater: string, + metadataAndTokenURI: MetadataAndTokenURI + ): Promise { + const nftContract = setContractDefaults( + new this.web3.eth.Contract(this.nftAbi, nftAddress), + this.config + ) + if (!(await this.getNftPermissions(nftAddress, metadataUpdater)).updateMetadata) { + throw new Error(`Caller is not Metadata updater`) + } + const estGas = await this.estGasSetMetadataAndTokenURI( + nftAddress, + metadataUpdater, + metadataAndTokenURI, + nftContract + ) + const trxReceipt = await nftContract.methods + .setMetaDataAndTokenURI(metadataAndTokenURI) + .send({ + from: metadataUpdater, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3, this.config) + }) + + return trxReceipt + } + /** * Estimate gas cost for setMetadataState method * @param {String} nftAddress erc721 contract adress diff --git a/test/unit/tokens/Nft.test.ts b/test/unit/tokens/Nft.test.ts index e792b8e5..a15eab74 100644 --- a/test/unit/tokens/Nft.test.ts +++ b/test/unit/tokens/Nft.test.ts @@ -14,6 +14,7 @@ import { NftFactory, NftCreateData } from '../../../src/factories/NFTFactory' import { Nft } from '../../../src/tokens/NFT' import { AbiItem } from 'web3-utils' import sha256 from 'crypto-js/sha256' +import { MetadataAndTokenURI } from '../../../src/interfaces/Erc721Interface' const web3 = new Web3('http://127.0.0.1:8545') @@ -418,4 +419,34 @@ describe('NFT', () => { const tx = await nftDatatoken.setTokenURI(nftAddress, user1, 'test') assert(tx.events.TokenURIUpdate) }) + + it('#setMetaDataAndTokenURI - should update tokenURI and set metadata', async () => { + const data = web3.utils.asciiToHex(user2) + const metadataAndTokenURI: MetadataAndTokenURI = { + metaDataState: 1, + metaDataDecryptorUrl: 'http://myprovider:8030', + metaDataDecryptorAddress: '0x123', + flags: web3.utils.asciiToHex(user1), + data: web3.utils.asciiToHex(user1), + metaDataHash: '0x' + sha256(data).toString(), + tokenId: 1, + tokenURI: 'https://anothernewurl.com/nft/', + metadataProofs: [] + } + assert( + (await nftDatatoken.getNftPermissions(nftAddress, user1)).updateMetadata === true + ) + + const tx = await nftDatatoken.setMetadataAndTokenURI( + nftAddress, + user1, + metadataAndTokenURI + ) + assert(tx.events.TokenURIUpdate) + assert(tx.events.MetadataUpdated) + + const metadata = await nftDatatoken.getMetadata(nftAddress) + assert(metadata[0] === 'http://myprovider:8030') + assert(metadata[1] === '0x123') + }) })