UNPKG

caver-js

Version:

caver-js is a JavaScript API library that allows developers to interact with a Klaytn node

660 lines (594 loc) 29.1 kB
/* Copyright 2020 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 { validateDeployParameterForKIP7, determineSendParams, kip7JsonInterface, kip7ByteCode, formatParamForUint256, interfaceIds, } = require('./kctHelper') const { isAddress, toBuffer, isHexStrict, toHex } = require('../../caver-utils') const KIP13 = require('./kip13') /** * The KIP7 class that helps you easily handle a smart contract that implements KIP-7 as a JavaScript object on the Klaytn blockchain platform (Klaytn). * @hideconstructor * @class */ class KIP7 extends Contract { /** * Deploys a KIP-7 token contract to Klaytn network. * * By default, it returns a KIP7 instance when the deployment is finished. * If you define a custom function in the `contractDeployFormatter` field in {@link Contract.SendOptions|SendOptions}, you can control return type. * * @example * const tokenInfo = { name: 'TokenName', symbol: 'TKN', decimals: 18, initialSupply: new BigNumber(1000000000000000000) } * * // Below example will use `caver.wallet`. * const deployed = await caver.kct.kip7.deploy(tokenInfo, '0x{deployer address}') * * // Use sendOptions instead of deployer address. * const sendOptions = { from: '0x{deployer address}', feeDelegation: true, feePayer: '0x{fee payer address}' } * const deployed = await caver.kct.kip7.deploy(tokenInfo, sendOptions) * * // If you want to use your own wallet that implements the 'IWallet' interface, pass it into the last parameter. * const deployed = await caver.kct.kip7.deploy(tokenInfo, '0x{deployer address}', wallet) * * @ignore * @param {KIP7.KIP7DeployParams} tokenInfo The object that defines the name, symbol, decimals, and initialSupply of the token to deploy. * @param {Contract.SendOptions|string} sendOptions The address of the account to deploy the KIP-7 token contract or an object holding parameters that are required for sending a transaction. * @return {Promise<*>} */ static deploy(tokenInfo, sendOptions) { validateDeployParameterForKIP7(tokenInfo) const { name, symbol, decimals, initialSupply } = tokenInfo const kip7 = new KIP7() // If sendOptions is string type, sendOptions means deployer's address if (_.isString(sendOptions)) sendOptions = { from: sendOptions, gas: 4000000, value: 0 } sendOptions.gas = sendOptions.gas !== undefined ? sendOptions.gas : 4000000 return kip7 .deploy({ data: kip7ByteCode, arguments: [name, symbol, decimals, initialSupply], }) .send(sendOptions) } /** * An object that defines the parameters required to deploy the KIP-7 contract. * * @typedef {object} KIP7.KIP7DetectedObject * @property {boolean} IKIP7 - Whether to implement `IKIP7` interface. * @property {boolean} IKIP7Metadata - Whether to implement `IKIP7Metadata` interface. * @property {boolean} IKIP7Mintable - Whether to implement `IKIP7Mintable` interface. * @property {boolean} IKIP7Burnable - Whether to implement `IKIP7Burnable` interface. * @property {boolean} IKIP7Pausable - Whether to implement `IKIP7Pausable` interface. */ /** * Returns the information of the interface implemented by the token contract. * * @example * const detected = await caver.kct.kip7.detectInterface('0x{address in hex}') * * @param {string} contractAddress The address of the KIP-7 token contract to detect. * @return {Promise<KIP7.KIP7DetectedObject>} */ static detectInterface(contractAddress) { const kip7 = new KIP7(contractAddress) return kip7.detectInterface() } /** * KIP7 class represents the KIP-7 token contract. * * @constructor * @param {string} tokenAddress - The KIP-7 token contract address. * @param {Array} [abi] - The Contract Application Binary Interface (ABI) of the KIP-7. */ constructor(tokenAddress, abi = kip7JsonInterface) { if (tokenAddress) { if (_.isString(tokenAddress)) { if (!isAddress(tokenAddress)) throw new Error(`Invalid token address ${tokenAddress}`) } else { abi = tokenAddress tokenAddress = undefined } } super(abi, tokenAddress) } /** * Clones the current KIP7 instance. * * @example * const cloned = kip7.clone() * const cloned = kip7.clone('0x{new kip7 address}') * * @param {string} [tokenAddress] The address of the token contract. * @return {KIP7} */ clone(tokenAddress = this.options.address) { const cloned = new this.constructor(tokenAddress, this.options.jsonInterface) cloned.setWallet(this._wallet) return cloned } /** * Returns the information of the interface implemented by the token contract. * * @example * const detected = await kip7.detectInterface() * * @return {Promise<KIP7.KIP7DetectedObject>} */ async detectInterface() { const detected = { IKIP7: false, IKIP7Metadata: false, IKIP7Mintable: false, IKIP7Burnable: false, IKIP7Pausable: 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-7 contract. detected.IKIP7 = await this.supportsInterface(interfaceIds.kip7.IKIP7) if (detected.IKIP7 === false) return detected await Promise.all( Object.keys(interfaceIds.kip7).map(async interfaceName => { if (interfaceIds.kip7[interfaceName] !== interfaceIds.kip7.IKIP7) detected[interfaceName] = await this.supportsInterface(interfaceIds.kip7[interfaceName]) }) ) return detected } catch (e) { throw new Error(notSupportedMsg) } } /** * Returns `true` if this contract implements the interface defined by `interfaceId`. * * @example * const supported = await kip7.supportsInterface('0x65787371') * * @param {string} interfaceId The interface id to be checked. * @return {Promise<boolean>} */ async supportsInterface(interfaceId) { const supported = await this.methods.supportsInterface(interfaceId).call() return supported } /** * Returns the name of the token. * * @example * const name = await kip7.name() * * @return {Promise<string>} */ async name() { const name = await this.methods.name().call() return name } /** * Returns the symbol of the token. * * @example * const symbol = await kip7.symbol() * * @return {Promise<string>} */ async symbol() { const symbol = await this.methods.symbol().call() return symbol } /** * Returns the decimals of the token. * * @example * const decimals = await kip7.decimals() * * @return {Promise<number>} */ async decimals() { const decimals = await this.methods.decimals().call() return Number(decimals) } /** * Returns the total supply of the token. * * @example * const totalSupply = await kip7.totalSupply() * * @return {Promise<BigNumber>} */ async totalSupply() { const totalSupply = await this.methods.totalSupply().call() return new BigNumber(totalSupply) } /** * Returns the balance of the given account address. * * @example * const balance = await kip7.balanceOf('0x{address in hex}') * * @param {string} account The address of the account to be checked for its balance. * @return {Promise<BigNumber>} */ async balanceOf(account) { const balance = await this.methods.balanceOf(account).call() return new BigNumber(balance) } /** * Returns the amount of token that `spender` is allowed to withdraw from `owner`. * * @example * const allowance = await kip7.allowance('0x{address in hex}', '0x{spender address}') * * @param {string} owner The address of the token owner's account. * @param {string} spender The address of the account that spends tokens in place of the owner. * @return {Promise<BigNumber>} */ async allowance(owner, spender) { const allowance = await this.methods.allowance(owner, spender).call() return new BigNumber(allowance) } /** * Returns `true` if the given account is a minter who can issue new KIP7 tokens. * * @example * const isMinter = await kip7.isMinter('0x{address in hex}') * * @param {string} account The address of the account to be checked for having the minting right. * @return {Promise<boolean>} */ async isMinter(account) { const isMinter = await this.methods.isMinter(account).call() return isMinter } /** * Returns `true` if the given account is a pauser who can suspend transferring tokens. * * @example * const isPauser = await kip7.isPauser('0x{address in hex}') * * @param {string} account The address of the account to be checked for having the right to suspend transferring tokens. * @return {Promise<boolean>} */ async isPauser(account) { const isPauser = await this.methods.isPauser(account).call() return isPauser } /** * Returns `true` if the contract is paused, and `false` otherwise. * * @example * const isPaused = await kip7.paused() * * @method paused * @return {Promise<boolean>} */ async paused() { const isPaused = await this.methods.paused().call() return isPaused } /** * Sets the amount of the tokens of the token owner to be spent by the spender. * * @example * const receipt = await kip7.approve('0x{spender address}', 10, { from: '0x{address in hex}' }) * * @param {string} spender The address of the account who spends tokens in place of the owner. * @param {BigNumber|string|number} amount The amount of token the spender is allowed to use. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 approve(spender, amount, sendParam = {}) { const executableObj = this.methods.approve(spender, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Transfers the given amount of the token from the token owner's balance to the recipient. * * The token owner should execute this token transfer with its own hands. * Thus, the token owner should be the sender of this transaction whose address must be given at `sendParam.from` or `kip7.options.from`. * Without `sendParam.from` nor `kip7.options.from` being provided, an error would occur. * * @example * const receipt = await kip7.transfer('0x{address in hex}', 10, { from: '0x{address in hex}' }) * * @param {string} recipient The address of the account to receive the token. * @param {BigNumber|string|number} amount The amount of tokens you want to transfer. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 transfer(recipient, amount, sendParam = {}) { const executableObj = this.methods.transfer(recipient, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Transfers the given amount of the token from the token owner's balance to the recipient. * * The address who was approved to send the token owner's tokens is expected to execute this token transferring transaction. * Thus, the approved one should be the sender of this transaction whose address must be given at `sendParam.from` or `kip7.options.from`. * Without `sendParam.from` nor `kip7.options.from` being provided, an error would occur. * * @example * const receipt = await kip7.transferFrom('0x{address in hex}', '0x{address in hex}', 10000, { from: '0x{address in hex}' }) * * @param {string} owner The address of the account that owns the token to be sent with allowance mechanism. * @param {string} recipient The address of the account to receive the token. * @param {BigNumber|string|number} amount The amount of tokens you want to transfer. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 transferFrom(owner, recipient, amount, sendParam = {}) { const executableObj = this.methods.transferFrom(owner, recipient, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Safely transfers the given amount of the token from the token owner's balance to the recipient. * * The token owner should execute this token transfer with its own hands. * Thus, the token owner should be the sender of this transaction whose address must be given at `sendParam.from` or `kip7.options.from`. * Without `sendParam.from` nor `kip7.options.from` being provided, an error would occur. * * @example * const receipt = await kip7.safeTransfer('0x{address in hex}', 10, { from: '0x{address in hex}' }) * * @param {string} recipient The address of the account to receive the token. * @param {BigNumber|string|number} amount The amount of tokens you want to transfer. * @param {Buffer|string|number} [data] The optional data to send along with the call. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 safeTransfer(recipient, 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 = undefined } } if (data && !_.isBuffer(data)) { if (_.isString(data) && !isHexStrict(data)) data = toHex(data) data = toBuffer(data) } const executableObj = data ? this.methods.safeTransfer(recipient, formatParamForUint256(amount), data) : this.methods.safeTransfer(recipient, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Safely transfers the given amount of the token from the token owner's balance to the recipient. * * The address who was approved to send the token owner's tokens is expected to execute this token transferring transaction. * Thus, the approved one should be the sender of this transaction whose address must be given at `sendParam.from` or `kip7.options.from`. * Without `sendParam.from` nor `kip7.options.from` being provided, an error would occur. * * @example * const receipt = await kip7.safeTransferFrom('0x{address in hex}', '0x{address in hex}', 10000, { from: '0x{address in hex}' }) * * @param {string} owner The address of the account that owns the token to be sent with allowance mechanism. * @param {string} recipient The address of the account to receive the token. * @param {BigNumber|string|number} amount The amount of tokens you want to transfer. * @param {Buffer|string|number} [data] The optional data to send along with the call. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 safeTransferFrom(owner, recipient, 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 = undefined } } if (data && !_.isBuffer(data)) { if (_.isString(data) && !isHexStrict(data)) data = toHex(data) data = toBuffer(data) } const executableObj = data ? this.methods.safeTransferFrom(owner, recipient, formatParamForUint256(amount), data) : this.methods.safeTransferFrom(owner, recipient, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Creates the `amount` of token and issues it to the `account`, increasing the total supply of token. * The account sending transaction to execute the mint must be a Minter with a MinterRole. * * @example * const receipt = await kip7.mint('0x{address in hex}', 10000, { from: '0x{minter address}' }) * * @param {string} account The address of the account to which the minted token will be allocated. * @param {BigNumber|string|number} amount The amount of tokens to mint. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 mint(account, amount, sendParam = {}) { const executableObj = this.methods.mint(account, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Adds an account as a minter, who are permitted to mint tokens. * The account sending transaction to execute the addMinter must be a Minter with a MinterRole. * * @example * const receipt = await kip7.addMinter('0x{address in hex}', { from: '0x{minter address}' }) * * @param {string} account The address of account to add as minter. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 addMinter(account, sendParam = {}) { const executableObj = this.methods.addMinter(account) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Renounces the right to mint tokens. Only a minter address can renounce the minting right. * The account sending transaction to execute the renounceMinter must be a Minter with a MinterRole. * * @example * const receipt = await kip7.renounceMinter({ from: '0x{minter address}' }) * * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 renounceMinter(sendParam = {}) { const executableObj = this.methods.renounceMinter() sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Destroys the `amount` of tokens in the sender's balance. * Without `sendParam.from` nor `kip7.options.from` being provided, an error would occur. * * @example * const receipt = await kip7.burn(1000, { from: '0x{address in hex}' }) * * @param {BigNumber|string|number} amount The amount of tokens to destroy. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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(amount, sendParam = {}) { const executableObj = this.methods.burn(formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Destroys the given number of tokens from `account`. * The allowance of the sender specified in `sendParam.from` or `kip7.options.from` is reduced alongside the balance of account. * * @example * const receipt = await kip7.burnFrom('0x{address in hex}', 1000, { from: '0x{address in hex}' }) * * @param {string} account The address of the account that owns the token to be burned with allowance mechanism. * @param {BigNumber|string|number} amount The amount of tokens to destroy. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 burnFrom(account, amount, sendParam = {}) { const executableObj = this.methods.burnFrom(account, formatParamForUint256(amount)) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Adds an account as a pauser that has the right to suspend the contract. * The account sending transaction to execute the addPauser must be a Pauser with a PauserRole. * * @example * const receipt = await kip7.addPauser('0x{address in hex}', { from: '0x{address in hex}' }) * * @param {string} account The address of account to add as pauser. * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 addPauser(account, sendParam = {}) { const executableObj = this.methods.addPauser(account) sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Suspends functions related to sending tokens. * The account sending transaction to execute the pause must be a Pauser with a PauserRole. * * @example * const receipt = await kip7.pause({ from: '0x{address in hex}' }) * * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 pause(sendParam = {}) { const executableObj = this.methods.pause() sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Resumes the paused contract. * The account sending transaction to execute the unpause must be a Pauser with a PauserRole. * * @example * const receipt = await kip7.unpause({ from: '0x{address in hex}' }) * * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 unpause(sendParam = {}) { const executableObj = this.methods.unpause() sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } /** * Renounces the right to pause the contract. Only a pauser address can renounce the pausing right. * The account sending transaction to execute the renouncePauser must be a Pauser with a PauserRole. * * @example * const receipt = await kip7.renouncePauser({ from: '0x{address in hex}' }) * * @param {Contract.SendOptions} [sendParam] An object holding parameters that are required for sending a transaction. * @return {Promise<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 renouncePauser(sendParam = {}) { const executableObj = this.methods.renouncePauser() sendParam = await determineSendParams(executableObj, sendParam, this.options) return executableObj.send(sendParam) } } /** * The byte code of the KIP-7 token contract. * * @example * caver.kct.kip7.byteCode * * @static * @type {string} */ KIP7.byteCode = kip7ByteCode /** * The abi of the KIP-7 token contract. * * @example * caver.kct.kip7.abi * * @static * @type {Array.<object>} */ KIP7.abi = kip7JsonInterface module.exports = KIP7