six-caver-js
Version:
caver-js is a JavaScript API library that allows developers to interact with a Klaytn node
626 lines (546 loc) • 27.9 kB
JavaScript
/*
Copyright 2021 The caver-js Authors
This file is part of the caver-js library.
The caver-js library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The caver-js library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the caver-js. If not, see <http://www.gnu.org/licenses/>.
*/
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const Contract = require('../../caver-contract')
const {
kip37JsonInterface,
kip37ByteCode,
determineSendParams,
formatParamForUint256,
validateDeployParameterForKIP37,
interfaceIds,
} = require('./kctHelper')
const { isAddress, toBuffer, isHexStrict, toHex, stripHexPrefix, leftPad } = require('../../caver-utils')
const KIP13 = require('../src/kip13')
class KIP37 extends Contract {
/**
* Creates an instance of KIP37.
* @method create
* @param {string} tokenAddress - The KIP-73 token contract address.
* @param {Array} [abi] - The Contract Application Binary Interface (ABI) of the KIP-37.
* @return {object}
*/
static create(tokenAddress, abi) {
return new KIP37(tokenAddress, abi)
}
/**
* deploy deploys a KIP-37 token contract to Klaytn network.
* `const deployedContract = await caver.kct.kip37.deploy({
* uri: ''
* }, '0x{address in hex}')`
*
* @method deploy
* @param {Object} tokenInfo The object that defines the uri to deploy.
* @param {Object|String} sendOptions The address of the account to deploy the KIP-37 token contract or an object holding parameters that are required for sending a transaction.
* @param {IWallet} [wallet] The wallet instance to sign and send a transaction.
* @return {Object}
*/
static deploy(tokenInfo, sendOptions, wallet) {
validateDeployParameterForKIP37(tokenInfo)
const { uri } = tokenInfo
const kip37 = new KIP37()
if (wallet !== undefined) kip37.setWallet(wallet)
// If sendOptions is string type, sendOptions means deployer's address
if (_.isString(sendOptions)) sendOptions = { from: sendOptions, gas: 7000000, value: 0 }
sendOptions.gas = sendOptions.gas !== undefined ? sendOptions.gas : 7000000
return kip37
.deploy({
data: kip37ByteCode,
arguments: [uri],
})
.send(sendOptions)
}
/**
* detectInterface detects which interface the KIP-37 token contract supports.
*
* @method detectInterface
* @param {string} contractAddress The address of the KIP-37 token contract to detect.
* @return {object}
*/
static detectInterface(contractAddress) {
const kip37 = new KIP37(contractAddress)
return kip37.detectInterface()
}
constructor(tokenAddress, abi = kip37JsonInterface) {
if (tokenAddress) {
if (_.isString(tokenAddress)) {
if (!isAddress(tokenAddress)) throw new Error(`Invalid token address ${tokenAddress}`)
} else {
abi = tokenAddress
tokenAddress = undefined
}
}
super(abi, tokenAddress)
this.setWallet(KIP37.wallet)
}
clone(tokenAddress = this.options.address) {
const cloned = new this.constructor(tokenAddress, this.options.jsonInterface)
cloned.setWallet(this._wallet)
return cloned
}
/**
* detectInterface detects which interface the KIP-37 token contract supports.
*
* @method detectInterface
* @return {object}
*/
async detectInterface() {
const detected = {
IKIP37: false,
IKIP37Metadata: false,
IKIP37Mintable: false,
IKIP37Burnable: false,
IKIP37Pausable: false,
}
const notSupportedMsg = `This contract does not support KIP-13.`
const contractAddress = this._address
try {
const isSupported = await KIP13.isImplementedKIP13Interface(contractAddress)
if (isSupported !== true) throw new Error(notSupportedMsg)
// Since there is an extension that has the same interface id even though it is a different KCT,
// it must be checked first whether the contract is a KIP-37 contract.
detected.IKIP37 = await this.supportsInterface(interfaceIds.kip37.IKIP37)
if (detected.IKIP37 === false) return detected
await Promise.all(
Object.keys(interfaceIds.kip37).map(async interfaceName => {
if (interfaceIds.kip37[interfaceName] !== interfaceIds.kip37.IKIP37)
detected[interfaceName] = await this.supportsInterface(interfaceIds.kip37[interfaceName])
})
)
return detected
} catch (e) {
throw new Error(notSupportedMsg)
}
}
/**
* supportsInterface checks whether interface is supported or not.
*
* @method supportsInterface
* @param {string} interfaceId The interface id to check.
* @return {boolean}
*/
async supportsInterface(interfaceId) {
const isSupported = await this.methods.supportsInterface(interfaceId).call()
return isSupported
}
/**
* uri returns distinct Uniform Resource Identifier (URI) for a given token.
* If the string {id} exists in any URI, this function will replace this with the actual token ID in hexadecimal form.
* Please refer to http://kips.klaytn.com/KIPs/kip-37#metadata
*
* @method uri
* @param {BigNumber|string|number} id The token id to get uri.
* @return {string}
*/
async uri(id) {
let uri = await this.methods.uri(formatParamForUint256(id)).call()
// Replace {id} to token id in hexadecimal form.
if (uri.includes('{id}')) {
let tokenIdInHex = stripHexPrefix(toHex(id))
tokenIdInHex = leftPad(tokenIdInHex, 64, '0')
uri = uri.replace('{id}', tokenIdInHex)
}
return uri
}
/**
* totalSupply returns the total supply of the specific token.
*
* @param {BigNumber|string|number} id The token id to see the total supply.
* @method totalSupply
* @return {BigNumber}
*/
async totalSupply(id) {
const totalSupply = await this.methods.totalSupply(formatParamForUint256(id)).call()
return new BigNumber(totalSupply)
}
/**
* balanceOf returns the balance of the account.
*
* @method balanceOf
* @param {string} account The address of the account for which you want to see balance.
* @param {BigNumber|string|number} id The token id to see balance.
* @return {BigNumber}
*/
async balanceOf(account, id) {
const balance = await this.methods.balanceOf(account, formatParamForUint256(id)).call()
return new BigNumber(balance)
}
/**
* Batch returns the balance of multiple account/token pairs. `accounts` and `ids` must have the same length.
*
* @method balanceOfBatch
* @param {Array.<string>} accounts The address of the accounts for which you want to see balance.
* @param {Array.<BigNumber|string|number>} ids An array of ids of token you want to see balance.
* @return {BigNumber}
*/
async balanceOfBatch(accounts, ids) {
if (ids.length !== accounts.length) throw new Error(`ids and accounts must have the same length.`)
const formattedTokenIds = []
for (let i = 0; i < ids.length; i++) {
formattedTokenIds.push(formatParamForUint256(ids[i]))
}
const balances = await this.methods.balanceOfBatch(accounts, formattedTokenIds).call()
const ret = []
for (const bal of balances) {
ret.push(new BigNumber(bal))
}
return ret
}
/**
* isApprovedForAll returns true if an operator is approved by a given owner.
*
* @method isApprovedForAll
* @param {string} owner The address of the owner.
* @param {string} operator The address of the operator.
* @return {boolean}
*/
async isApprovedForAll(owner, operator) {
const isApprovedForAll = await this.methods.isApprovedForAll(owner, operator).call()
return isApprovedForAll
}
/**
* paused returns whether or not the token contract's transaction (or specific token) is paused.
* If id is not defined, checks whether the token contract's transaction is paused.
* If id is defined, checks whether the specific token is paused.
*
* @param {BigNumber|string|number} [id] The id of token to check wether paused or not.
* @method paused
* @return {boolean}
*/
async paused(id) {
const callObject = id !== undefined ? this.methods.paused(formatParamForUint256(id)) : this.methods.paused()
const isPaused = await callObject.call()
return isPaused
}
/**
* isPauser returns whether the account is pauser or not.
*
* @method isPauser
* @param {string} account The address of the account you want to check pauser or not.
* @return {boolean}
*/
async isPauser(account) {
const isPauser = await this.methods.isPauser(account).call()
return isPauser
}
/**
* isMinter returns whether the account is minter or not.
*
* @method isMinter
* @param {string} account The address of the account you want to check minter or not.
* @return {boolean}
*/
async isMinter(account) {
const isMinter = await this.methods.isMinter(account).call()
return isMinter
}
/**
* create creates token and assigns them to account, increasing the total supply.
*
* @method mint
* @param {BigNumber|string|number} id The id of token to mint.
* @param {BigNumber|string|number} initialSupply The amount of tokens being minted.
* @param {string} [uri] The token URI of the created token.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async create(id, initialSupply, uri, sendParam = {}) {
if (uri && _.isObject(uri)) {
if (uri.gas !== undefined || uri.from !== undefined) {
if (Object.keys(sendParam).length > 0) throw new Error(`Invalid parameters`)
sendParam = uri
uri = ''
}
}
const executableObj = this.methods.create(formatParamForUint256(id), formatParamForUint256(initialSupply), uri)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* setApprovalForAll enables or disables approval for a third party ("operator") to manage all of the caller's tokens.
* An operator is allowed to transfer all tokens of the sender on their behalf.
*
* @method setApprovalForAll
* @param {string} operator The address to add to the set of authorized operators.
* @param {boolean} approved True if the operator is approved, false to revoke approval.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async setApprovalForAll(operator, approved, sendParam = {}) {
const executableObj = this.methods.setApprovalForAll(operator, approved)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* safeTransferFrom safely transfers the ownership of a given token id to another address.
*
* @method safeTransferFrom
* @param {string} from The address of the owner or approved of the given token.
* @param {string} to The address of the account to receive the token.
* @param {BigNumber|string|number} id The id of token you want to transfer.
* @param {BigNumber|string|number} amount The amount of tokens you want to transfer.
* @param {Buffer|string|number} [data] (optional) The data to send along with the call.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async safeTransferFrom(from, to, id, amount, data, sendParam = {}) {
if (data && _.isObject(data)) {
if (data.gas !== undefined || data.from !== undefined) {
if (Object.keys(sendParam).length > 0) throw new Error(`Invalid parameters`)
sendParam = data
data = Buffer.from('')
}
}
if (data && !_.isBuffer(data)) {
if (_.isString(data) && !isHexStrict(data)) data = toHex(data)
data = toBuffer(data)
}
const executableObj = this.methods.safeTransferFrom(from, to, formatParamForUint256(id), formatParamForUint256(amount), data)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* safeBatchTransferFrom safely transfers the ownership of given token ids to another address.
*
* @method safeBatchTransferFrom
* @param {string} from The address of the owner or approved of the given token.
* @param {string} to The address of the account to receive the token.
* @param {Array.<BigNumber|string|number>} ids An array of ids of token you want to transfer.
* @param {Array.<BigNumber|string|number>} amounts An array of amount of tokens you want to transfer.
* @param {Buffer|string|number} [data] (optional) The data to send along with the call.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async safeBatchTransferFrom(from, to, ids, amounts, data, sendParam = {}) {
if (data && _.isObject(data)) {
if (data.gas !== undefined || data.from !== undefined) {
if (Object.keys(sendParam).length > 0) throw new Error(`Invalid parameters`)
sendParam = data
data = Buffer.from('')
}
}
if (data && !_.isBuffer(data)) {
if (_.isString(data) && !isHexStrict(data)) data = toHex(data)
data = toBuffer(data)
}
if (ids.length !== amounts.length) throw new Error(`ids and amounts must have the same length.`)
const formattedTokenIds = []
const formattedTokenAmounts = []
for (let i = 0; i < ids.length; i++) {
formattedTokenIds.push(formatParamForUint256(ids[i]))
formattedTokenAmounts.push(formatParamForUint256(amounts[i]))
}
const executableObj = this.methods.safeBatchTransferFrom(from, to, formattedTokenIds, formattedTokenAmounts, data)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* mint mints tokens of the specific token type `id` and assigns the tokens according to the variables `to` and `value`.
*
* @method mint
* @param {string|Array.<string>} toList The address that will receive the minted tokens. If there are multiple to accounts, the values mapped to each to must also be an array.
* @param {BigNumber|string|number} id The id of token to mint.
* @param {BigNumber|string|number|Array.<BigNumber|string|number>} values The quantity of tokens being minted.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async mint(toList, id, values, sendParam = {}) {
if (_.isArray(toList) !== _.isArray(values))
throw new Error(`If you want to minting a specific token to multiple accounts, both toList and values both must be arrays.`)
let executableObj
if (_.isArray(toList)) {
if (toList.length !== values.length) throw new Error(`toList and values must have the same length.`)
const formattedTokenValues = []
for (const val of values) {
formattedTokenValues.push(formatParamForUint256(val))
}
executableObj = this.methods.mint(formatParamForUint256(id), toList, formattedTokenValues)
} else {
executableObj = this.methods.mint(formatParamForUint256(id), toList, formatParamForUint256(values))
}
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* mintBatch mints multiple KIP-37 tokens of the specific token types `ids` in a batch and assigns the tokens according to the variables `to` and `values`.
*
* @method mintBatch
* @param {string} to The address that will receive the minted tokens.
* @param {Array.<BigNumber|string|number>} ids The list of the token ids to mint.
* @param {Array.<BigNumber|string|number>} values The list of the token amounts to mint.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async mintBatch(to, ids, values, sendParam = {}) {
if (ids.length !== values.length) throw new Error(`ids and values must have the same length.`)
const formattedTokenIds = []
const formattedTokenValues = []
for (let i = 0; i < ids.length; i++) {
formattedTokenIds.push(formatParamForUint256(ids[i]))
formattedTokenValues.push(formatParamForUint256(values[i]))
}
const executableObj = this.methods.mintBatch(to, formattedTokenIds, formattedTokenValues)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* addMinter adds an account as a minter that has the permission of MinterRole and can mint.
* The account sending transaction to execute the addMinter must be a Minter with a MinterRole.
*
* @method addMinter
* @param {string} account The address of account to add as minter.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async addMinter(account, sendParam = {}) {
const executableObj = this.methods.addMinter(account)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* renounceMinter renounces privilege of MinterRole.
* The account sending transaction to execute the renounceMinter must be a Minter with a MinterRole.
*
* @method renounceMinter
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async renounceMinter(sendParam = {}) {
const executableObj = this.methods.renounceMinter()
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* burn destroys specific KIP37 tokens.
*
* @method burn
* @param {string} account The account that owns tokens.
* @param {BigNumber|string|number} id The token id to burn.
* @param {BigNumber|string|number} value The token amount to burn.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-7 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-7 abi.
*/
async burn(account, id, value, sendParam = {}) {
const executableObj = this.methods.burn(account, formatParamForUint256(id), formatParamForUint256(value))
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* burnBatch burns multiple KIP37 tokens.
*
* @method burnBatch
* @param {string} account The account that owns tokens.
* @param {Array.<BigNumber|string|number>} ids The list of the token ids to burn.
* @param {Array.<BigNumber|string|number>} values The list of the token amounts to burn.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async burnBatch(account, ids, values, sendParam = {}) {
if (ids.length !== values.length) throw new Error(`ids and values must have the same length.`)
const formattedTokenIds = []
const formattedTokenValues = []
for (let i = 0; i < ids.length; i++) {
formattedTokenIds.push(formatParamForUint256(ids[i]))
formattedTokenValues.push(formatParamForUint256(values[i]))
}
const executableObj = this.methods.burnBatch(account, formattedTokenIds, formattedTokenValues)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* pause pauses actions related to transfer and approval.
* If id is not defined, pauses the token contract.
* If id is defined, pauses the specific token.
* The account sending transaction to execute the pause must be a Pauser with a PauserRole.
*
* @method pause
* @param {BigNumber|string|number} [id] The id of token to pause.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async pause(id, sendParam = {}) {
if (Object.keys(sendParam).length === 0 && _.isObject(id)) {
sendParam = id
id = undefined
}
const executableObj = id !== undefined ? this.methods.pause(formatParamForUint256(id)) : this.methods.pause()
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* unpause resumes from the paused state.
* If id is not defined, unpauses the token contract.
* If id is defined, unpauses the specific token.
* The account sending transaction to execute the unpause must be a Pauser with a PauserRole.
*
* @method unpause
* @param {BigNumber|string|number} [id] The id of token to unpause.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async unpause(id, sendParam = {}) {
if (Object.keys(sendParam).length === 0 && _.isObject(id)) {
sendParam = id
id = undefined
}
const executableObj = id !== undefined ? this.methods.unpause(formatParamForUint256(id)) : this.methods.unpause()
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* addPauser adds an account as a pauser that has the permission of PauserRole and can pause.
* The account sending transaction to execute the addPauser must be a Pauser with a PauserRole.
*
* @method addPauser
* @param {string} account The address of account to add as pauser.
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async addPauser(account, sendParam = {}) {
const executableObj = this.methods.addPauser(account)
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
/**
* renouncePauser renounces privilege of PauserRole.
* The account sending transaction to execute the renouncePauser must be a Pauser with a PauserRole.
*
* @method renouncePauser
* @param {Object} [sendParam] (optional) An object with defined parameters for sending a transaction.
* @return {Object} A receipt containing the execution result of the transaction for executing the KIP-37 token contract.
* In this receipt, instead of the logs property, there is an events property parsed by KIP-37 abi.
*/
async renouncePauser(sendParam = {}) {
const executableObj = this.methods.renouncePauser()
sendParam = await determineSendParams(executableObj, sendParam, this.options)
return executableObj.send(sendParam)
}
}
KIP37.byteCode = kip37ByteCode
module.exports = KIP37