UNPKG

@skalenetwork/ima-js

Version:

Simple TS/JS library to interact with SKALE IMA

130 lines (129 loc) 5.88 kB
/** * @license * SKALE ima-js * * This program 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. * * This program 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 this program. If not, see <https://www.gnu.org/licenses/>. */ /** * @file TokenManager.ts * @copyright SKALE Labs 2022-Present */ import { Logger } from 'tslog'; import { ethers } from 'ethers'; import { BaseContract } from '../BaseContract'; import TimeoutException from '../../exceptions/TimeoutException'; import * as constants from '../../constants'; import * as transactions from '../../transactions'; import * as helper from '../../helper'; const log = new Logger(); export class TokenManager extends BaseContract { tokens = {}; addToken(tokenName, contract) { this.tokens[tokenName] = contract; } async enableAutomaticDeploy(opts) { const txData = await this.contract.enableAutomaticDeploy.populateTransaction(); return await transactions.send(this.provider, txData, opts, this.txName('enableAutomaticDeploy')); } async disableAutomaticDeploy(opts) { const txData = await this.contract.disableAutomaticDeploy.populateTransaction(); return await transactions.send(this.provider, txData, opts, this.txName('disableAutomaticDeploy')); } async automaticDeploy() { return await this.contract.automaticDeploy(); } async grantRole(role, address, opts) { const txData = await this.contract.grantRole.populateTransaction(role, address); return await transactions.send(this.provider, txData, opts, this.txName('grantRole')); } async AUTOMATIC_DEPLOY_ROLE() { return await this.contract.AUTOMATIC_DEPLOY_ROLE(); } async TOKEN_REGISTRAR_ROLE() { return await this.contract.TOKEN_REGISTRAR_ROLE(); } async hasTokenManager(chainName) { return await this.contract.hasTokenManager(chainName); } async ownerOf(tokenName, tokenId) { const contract = this.tokens[tokenName]; try { if (typeof tokenId === 'string') tokenId = Number(tokenId); return await contract.ownerOf(tokenId); } catch (err) { return constants.ZERO_ADDRESS; // todo: replace with IMA-ERC721 exception: no such token } } async waitForTokenClone(originTokenAddress, originChainName, sleepInterval = constants.DEFAULT_SLEEP, iterations = constants.DEFAULT_ITERATIONS) { let address; const logData = 'origin token: ' + originTokenAddress + ' origin chain: ' + originChainName; for (let i = 1; i <= iterations; i++) { address = await this.getTokenCloneAddress(originTokenAddress, originChainName); if (constants.ZERO_ADDRESS !== address) { return address; } log.info('Waiting for token clone - ' + logData); await helper.sleep(sleepInterval); } throw new TimeoutException('waitForTokenClone timeout - ' + logData); } /** * Returns the solidityPackedKeccak256 hash of a concatenation of the chainHash and the * tokenMappingLenghtSlot. Internal function. * @param {string} chainName - The name of the chain to use in the hash. * @returns {string} - The resulting hash. */ _getMappingLengthSlot(chainName) { const chainHash = ethers.id(chainName); return ethers.solidityPackedKeccak256(['bytes32', 'uint256'], [chainHash, this.tokenMappingLenghtSlot]); } /** * Returns the number of token mappings for a given chain name by reading the storage at the * corresponding mapping length slot. * @param {string} chainName - The name of the chain for which to get the token mapping length. * @returns {Promise<number>} - The number of token mappings. */ async getTokenMappingsLength(chainName) { const length = await this.provider.getStorage(this.address, this._getMappingLengthSlot(chainName)); return parseInt(length, 16); } /** * Fetches an array of token addresses mapped to a specific chain. * * @param chainName The name of the chain to fetch token addresses for. * @param from The starting index in the token mapping. * @param to The ending index in the token mapping. * @returns A Promise that resolves to an array of token addresses. */ async getTokenMappings(chainName, from, to) { const getAddressPromises = Array.from({ length: to - from }, async (_, i) => await this.getMappedTokenAddress(chainName, from + i)); return await Promise.all(getAddressPromises); } /** * Fetches a token address mapped to a specific chain at the given index. * * @param chainName The name of the chain to fetch the token address for. * @param index The index in the token mapping. * @returns A Promise that resolves to the token address. */ async getMappedTokenAddress(chainName, index) { const encoded = BigInt(ethers.solidityPackedKeccak256(['bytes32'], [this._getMappingLengthSlot(chainName)])); const addressSlot = (encoded + BigInt(index)).toString(16); const addressData = await this.provider.getStorage(this.address, addressSlot); // const addressRaw = ethers.hexStripZeros(addressData); return ethers.zeroPadValue(addressData, constants.ADDRESS_LENGTH_BYTES); } }