1
0
mirror of https://github.com/oceanprotocol/ocean.js.git synced 2024-11-26 20:39:05 +01:00
ocean.js/src/factories/NFTFactory.ts
2021-10-21 10:47:21 -05:00

580 lines
17 KiB
TypeScript

import { Contract } from 'web3-eth-contract'
import Web3 from 'web3'
import { TransactionReceipt } from 'web3-core'
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
*/
export class NFTFactory {
public GASLIMIT_DEFAULT = 1000000
public factory721Address: string
public factory721ABI: AbiItem | AbiItem[]
public web3: Web3
private logger: Logger
public startBlock: number
public factory721: Contract
/**
* Instantiate DataTokens.
* @param {String} factory721Address
* @param {AbiItem | AbiItem[]} factory721ABI
* @param {Web3} web3
*/
constructor(
factory721Address: string,
web3: Web3,
logger: Logger,
factory721ABI?: AbiItem | AbiItem[],
startBlock?: number
) {
this.factory721Address = factory721Address
this.factory721ABI = factory721ABI || (defaultFactory721ABI.abi as AbiItem[])
this.web3 = web3
this.logger = logger
this.startBlock = startBlock || 0
this.factory721 = new this.web3.eth.Contract(
this.factory721ABI,
this.factory721Address
)
}
/**
* Create new NFT
* @param {String} address
* @param {String} name Token name
* @param {String} symbol Token symbol
* @param {Number} templateIndex NFT template index
* @return {Promise<string>} NFT datatoken address
*/
public async createNFT(
address: string,
name?: string,
symbol?: string,
templateIndex?: number
): Promise<string> {
if (!templateIndex) templateIndex = 1
// Generate name & symbol if not present
if (!name || !symbol) {
;({ name, symbol } = generateDtName())
}
// Get estimated gas value
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.deployERC721Contract(name, symbol, templateIndex, null)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.deployERC721Contract(name, symbol, templateIndex, null)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
let tokenAddress = null
try {
tokenAddress = trxReceipt.events.TokenCreated.returnValues[0]
} catch (e) {
this.logger.error(`ERROR: Failed to create datatoken : ${e.message}`)
}
return tokenAddress
}
/** Get Current NFT Count (NFT created)
* @return {Promise<number>} Number of NFT created from this factory
*/
public async getCurrentNFTCount(): Promise<number> {
const trxReceipt = await this.factory721.methods.getCurrentNFTCount().call()
return trxReceipt
}
/** Get Current Datatoken Count
* @return {Promise<number>} Number of DTs created from this factory
*/
public async getCurrentTokenCount(): Promise<number> {
const trxReceipt = await this.factory721.methods.getCurrentTokenCount().call()
return trxReceipt
}
/** Get Factory Owner
* @return {Promise<string>} Factory Owner address
*/
public async getOwner(): Promise<string> {
const trxReceipt = await this.factory721.methods.owner().call()
return trxReceipt
}
/** Get Current NFT Template Count
* @return {Promise<number>} Number of NFT Template added to this factory
*/
public async getCurrentNFTTemplateCount(): Promise<number> {
const count = await this.factory721.methods.getCurrentNFTTemplateCount().call()
return count
}
/** Get Current Template Datatoken (ERC20) Count
* @return {Promise<number>} Number of ERC20 Template added to this factory
*/
public async getCurrentTokenTemplateCount(): Promise<number> {
const count = await this.factory721.methods.getCurrentTemplateCount().call()
return count
}
/** Get NFT Template
* @param {Number} index Template index
* @return {Promise<Template>} Number of Template added to this factory
*/
public async getNFTTemplate(index: number): Promise<Template> {
const template = await this.factory721.methods.getNFTTemplate(index).call()
return template
}
/** Get Datatoken(erc20) Template
* @param {Number} index Template index
* @return {Promise<Template>} DT Template info
*/
public async getTokenTemplate(index: number): Promise<Template> {
const template = await this.factory721.methods.getTokenTemplate(index).call()
return template
}
/** Check if ERC20 is deployed from the factory
* @param {String} datatoken Datatoken address we want to check
* @return {Promise<Boolean>} return true if deployed from this factory
*/
public async checkDatatoken(datatoken: string): Promise<Boolean> {
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<String>} return address(0) if it's not, or the nftAddress if true
*/
public async checkNFT(nftAddress: string): Promise<String> {
const confirmAddress = await this.factory721.methods.erc721List(nftAddress).call()
return confirmAddress
}
/**
* Add a new erc721 token template - only factory Owner
* @param {String} address
* @param {String} templateAddress template address to add
* @return {Promise<TransactionReceipt>}
*/
public async addNFTTemplate(
address: string,
templateAddress: string
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.add721TokenTemplate(templateAddress)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.add721TokenTemplate(templateAddress)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
return trxReceipt
}
/**
* Disable token template - only factory Owner
* @param {String} address
* @param {Number} templateIndex index of the template we want to disable
* @return {Promise<TransactionReceipt>} current token template count
*/
public async disableNFTTemplate(
address: string,
templateIndex: number
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.disable721TokenTemplate(templateIndex)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.disable721TokenTemplate(templateIndex)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
return trxReceipt
}
/**
* Reactivate a previously disabled token template - only factory Owner
* @param {String} address
* @param {Number} templateIndex index of the template we want to reactivate
* @return {Promise<TransactionReceipt>} current token template count
*/
public async reactivateNFTTemplate(
address: string,
templateIndex: number
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.reactivate721TokenTemplate(templateIndex)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.reactivate721TokenTemplate(templateIndex)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
return trxReceipt
}
/**
* Add a new erc721 token template - only factory Owner
* @param {String} address
* @param {String} templateAddress template address to add
* @return {Promise<TransactionReceipt>}
*/
public async addTokenTemplate(
address: string,
templateAddress: string
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.addTokenTemplate(templateAddress)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.addTokenTemplate(templateAddress)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
return trxReceipt
}
/**
* Disable token template - only factory Owner
* @param {String} address
* @param {Number} templateIndex index of the template we want to disable
* @return {Promise<TransactionReceipt>} current token template count
*/
public async disableTokenTemplate(
address: string,
templateIndex: number
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.disableTokenTemplate(templateIndex)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.disableTokenTemplate(templateIndex)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
return trxReceipt
}
/**
* Reactivate a previously disabled token template - only factory Owner
* @param {String} address
* @param {Number} templateIndex index of the template we want to reactivate
* @return {Promise<TransactionReceipt>} current token template count
*/
public async reactivateTokenTemplate(
address: string,
templateIndex: number
): Promise<TransactionReceipt> {
if ((await this.getOwner()) !== address) {
throw new Error(`Caller is not Factory Owner`)
}
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.factory721.methods
.reactivateTokenTemplate(templateIndex)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createToken function of the contract
const trxReceipt = await this.factory721.methods
.reactivateTokenTemplate(templateIndex)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
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<TransactionReceipt>} transaction receipt
*/
public async startMultipleTokenOrder(
address: string,
orders: TokenOrder[]
): Promise<TransactionReceipt> {
// 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<TransactionReceipt>} transaction receipt
*/
public async createNftWithErc(
address: string,
nftCreateData: NFTCreateData,
ercCreateData: ErcCreateData
): Promise<TransactionReceipt> {
// 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<TransactionReceipt>} transaction receipt
*/
public async createNftErcWithPool(
address: string,
nftCreateData: NFTCreateData,
ercCreateData: ErcCreateData,
poolData: PoolData
): Promise<TransactionReceipt> {
// 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<TransactionReceipt>} transaction receipt
*/
public async createNftErcWithFixedRate(
address: string,
nftCreateData: NFTCreateData,
ercCreateData: ErcCreateData,
fixedData: FixedData
): Promise<TransactionReceipt> {
// 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
}
}