1
0
mirror of https://github.com/oceanprotocol/ocean.js.git synced 2024-11-26 20:39:05 +01:00

wip v4 contract integration NFT creation flow

This commit is contained in:
Bogdan Fazakas 2021-10-18 15:29:37 +03:00
parent a6ce03439f
commit 252eca12e4
11 changed files with 529 additions and 0 deletions

204
src/data/words.json Normal file
View File

@ -0,0 +1,204 @@
{
"nouns": [
"Crab",
"Fish",
"Seal",
"Octopus",
"Shark",
"Seahorse",
"Walrus",
"Starfish",
"Whale",
"Orca",
"Penguin",
"Jellyfish",
"Squid",
"Lobster",
"Pelican",
"Shrimp",
"Oyster",
"Clam",
"Seagull",
"Dolphin",
"Shell",
"Cormorant",
"Otter",
"Anemone",
"Turtle",
"Coral",
"Ray",
"Barracuda",
"Krill",
"Anchovy",
"Angelfish",
"Barnacle",
"Clownfish",
"Cod",
"Cuttlefish",
"Eel",
"Fugu",
"Herring",
"Haddock",
"Ling",
"Mackerel",
"Manatee",
"Narwhal",
"Nautilus",
"Plankton",
"Porpoise",
"Prawn",
"Pufferfish",
"Swordfish",
"Tuna"
],
"adjectives": [
"adamant",
"adroit",
"amatory",
"ambitious",
"amused",
"animistic",
"antic",
"arcadian",
"artistic",
"astonishing",
"astounding",
"baleful",
"bellicose",
"bilious",
"blissful",
"boorish",
"brave",
"breathtaking",
"brilliant",
"calamitous",
"caustic",
"cerulean",
"clever",
"charming",
"comely",
"competent",
"concomitant",
"confident",
"contumacious",
"corpulent",
"crapulous",
"creative",
"dazzling",
"dedicated",
"defamatory",
"delighted",
"delightful",
"determined",
"didactic",
"dilatory",
"dowdy",
"efficacious",
"effulgent",
"egregious",
"empowered",
"endemic",
"enthusiastic",
"equanimous",
"exceptional",
"execrable",
"fabulous",
"fantastic",
"fastidious",
"feckless",
"fecund",
"friable",
"fulsome",
"garrulous",
"generous",
"gentle",
"guileless",
"gustatory",
"heuristic",
"histrionic",
"hubristic",
"incendiary",
"incredible",
"insidious",
"insolent",
"inspired",
"intransigent",
"inveterate",
"invidious",
"invigorated",
"irksome",
"jejune",
"juicy",
"jocular",
"joyful",
"judicious",
"kind",
"lachrymose",
"limpid",
"loquacious",
"lovely",
"luminous",
"mannered",
"marvelous",
"mendacious",
"meretricious",
"minatory",
"mordant",
"motivated",
"munificent",
"nefarious",
"noxious",
"obtuse",
"optimistic",
"parsimonious",
"pendulous",
"pernicious",
"pervasive",
"petulant",
"passionate",
"phenomenal",
"platitudinous",
"pleasant",
"powerful",
"precipitate",
"propitious",
"puckish",
"querulous",
"quiescent",
"rebarbative",
"recalcitant",
"redolent",
"rhadamanthine",
"risible",
"ruminative",
"sagacious",
"salubrious",
"sartorial",
"sclerotic",
"serpentine",
"smart",
"spasmodic",
"strident",
"stunning",
"stupendous",
"taciturn",
"tactful",
"tasty",
"tenacious",
"tremendous",
"tremulous",
"trenchant",
"turbulent",
"turgid",
"ubiquitous",
"uxorious",
"verdant",
"vibrant",
"voluble",
"voracious",
"wheedling",
"withering",
"wonderful",
"zealous"
]
}

View File

@ -0,0 +1,107 @@
import Web3 from 'web3'
import { AbiItem } from 'web3-utils'
import defaultNFTDatatokenABI from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC721Template.sol/ERC721Template.json'
import { Logger, getFairGasPrice, generateDtName } from '../utils'
/**
* ERC721 ROLES
*/
interface Roles {
manager: boolean
deployERC20: boolean
updateMetadata: boolean
store: boolean
}
export class NFTDataToken {
public GASLIMIT_DEFAULT = 1000000
public factory721Address: string
public factory721ABI: AbiItem | AbiItem[]
public nftDatatokenABI: AbiItem | AbiItem[]
public web3: Web3
private logger: Logger
public startBlock: number
constructor(
web3: Web3,
logger: Logger,
nftDatatokenABI?: AbiItem | AbiItem[],
startBlock?: number
) {
this.nftDatatokenABI = nftDatatokenABI || (defaultNFTDatatokenABI.abi as AbiItem[])
this.web3 = web3
this.logger = logger
this.startBlock = startBlock || 0
}
/**
* Create new ERC20 datatoken - only user with ERC20Deployer permission can succeed
* @param {String} address
* @param {String} nftAddress
* @param {String} minter User set as initial minter for the ERC20
* @param {String} name Token name
* @param {String} symbol Token symbol
* @param {String} cap Maximum cap (Number) - will be converted to wei
* @param {Number} templateIndex NFT template index
* @return {Promise<string>} ERC20 datatoken address
*/
public async createERC20(
nftAddress: string,
address: string,
minter: string,
cap: 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())
}
// Create 721contract object
const contract721 = new this.web3.eth.Contract(this.nftDatatokenABI, nftAddress)
// Estimate gas for ERC20 token creation
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await contract721.methods
.createERC20(
templateIndex,
[name, symbol],
[minter],
[this.web3.utils.toWei(cap)],
null
)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
// Invoke createERC20 token function of the contract
const trxReceipt = await contract721.methods
.createERC20(
templateIndex,
[name, symbol],
[minter],
[this.web3.utils.toWei(cap)],
null
)
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
let tokenAddress = null
try {
tokenAddress = trxReceipt.events.ERC20Created.returnValues[0]
} catch (e) {
this.logger.error(`ERROR: Failed to create datatoken : ${e.message}`)
}
return tokenAddress
}
}

1
src/datatokens/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './NFTDatatoken'

View File

@ -0,0 +1,91 @@
import { Contract } from 'web3-eth-contract'
import Web3 from 'web3'
import { AbiItem } from 'web3-utils'
import defaultFactory721ABI from '@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json'
import { Logger, getFairGasPrice, generateDtName } from '../utils'
/**
* 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
}
}

1
src/factories/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './NFTFactory'

View File

@ -0,0 +1,38 @@
import Web3 from 'web3'
import { AbiItem } from 'web3-utils'
import { Contract } from 'web3-eth-contract'
import defaultPoolABI from '@oceanprotocol/contracts/artifacts/contracts/interfaces/IPool.sol/IPool.json'
import defaultERC20ABI from '@oceanprotocol/contracts/artifacts/contracts/interfaces/IERC20.sol/IERC20.json'
import { PoolFactory } from './PoolFactory'
import { Logger } from '../../utils'
export class OceanPoolV4 extends PoolFactory {
public oceanAddress: string = null
public dtAddress: string = null
public startBlock: number
public vaultABI: AbiItem | AbiItem[]
public vaultAddress: string
public vault: Contract
public poolABI: AbiItem | AbiItem[]
public erc20ABI: AbiItem | AbiItem[]
constructor(
web3: Web3,
logger: Logger,
routerAddress: string = null,
oceanAddress: string = null,
startBlock?: number
) {
super(web3, logger, routerAddress)
this.poolABI = defaultPoolABI.abi as AbiItem[]
this.erc20ABI = defaultERC20ABI.abi as AbiItem[]
this.vault = new this.web3.eth.Contract(this.vaultABI, this.vaultAddress)
// if (oceanAddress) {
// this.oceanAddress = oceanAddress
// }
if (startBlock) this.startBlock = startBlock
else this.startBlock = 0
}
}

View File

@ -0,0 +1,57 @@
import Web3 from 'web3'
import { AbiItem } from 'web3-utils'
import { Contract } from 'web3-eth-contract'
import defaultRouterABI from '@oceanprotocol/contracts/artifacts/contracts/interfaces/IFactoryRouter.sol/IFactoryRouter.json'
import { Logger } from '../../utils'
import { TransactionReceipt } from 'web3-eth'
export class PoolFactory {
public GASLIMIT_DEFAULT = 1000000
public web3: Web3 = null
public routerABI: AbiItem | AbiItem[]
public routerAddress: string
public logger: Logger
public router: Contract
/**
* Instantiate PoolFactory.
* @param {String} routerAddress
* @param {AbiItem | AbiItem[]} routerABI
* @param {Web3} web3
*/
constructor(
web3: Web3,
logger: Logger,
routerAddress: string,
routerABI?: AbiItem | AbiItem[]
) {
this.web3 = web3
this.routerAddress = routerAddress
this.routerABI = routerABI || (defaultRouterABI.abi as AbiItem[])
this.logger = logger
this.router = new this.web3.eth.Contract(this.routerABI, this.routerAddress)
}
public async deployPool(
account: string,
tokens: string[],
weights: string[],
swapFeePercentage: number,
swapMarketFee: number,
owner: string
): Promise<TransactionReceipt> {
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.router.methods
.deployPool(tokens, weightsInWei, swapFeePercentage, swapMarketFee, owner)
.estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
this.logger.log('Error estimate gas deployPool')
this.logger.log(e)
estGas = gasLimitDefault
}
}
}

View File

@ -0,0 +1,2 @@
export * from './PoolFactory'
export * from './OceanPool'

View File

@ -0,0 +1,27 @@
import wordListDefault from '../data/words.json'
/**
* Generate new datatoken name & symbol from a word list
* @return {<{ name: String; symbol: String }>} datatoken name & symbol. Produces e.g. "Endemic Jellyfish Token" & "ENDJEL-45"
*/
export function generateDtName(wordList?: { nouns: string[]; adjectives: string[] }): {
name: string
symbol: string
} {
const list = wordList || wordListDefault
const random1 = Math.floor(Math.random() * list.adjectives.length)
const random2 = Math.floor(Math.random() * list.nouns.length)
const indexNumber = Math.floor(Math.random() * 100)
// Capitalized adjective & noun
const adjective = list.adjectives[random1].replace(/^\w/, (c) => c.toUpperCase())
const noun = list.nouns[random2].replace(/^\w/, (c) => c.toUpperCase())
const name = `${adjective} ${noun} Token`
// use first 3 letters of name, uppercase it, and add random number
const symbol = `${(
adjective.substring(0, 3) + noun.substring(0, 3)
).toUpperCase()}-${indexNumber}`
return { name, symbol }
}

View File

@ -1,3 +1,4 @@
export * from './Logger'
export * from './GasUtils'
export * from './Logger'
export * from './DatatokenName'

View File