UNPKG

@skalenetwork/ima-js

Version:

Simple TS/JS library to interact with SKALE IMA

190 lines (168 loc) 7.05 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, type ILogObj } from 'tslog'; import { ethers, type Contract, type TransactionResponse } from 'ethers'; import { BaseContract } from '../BaseContract'; import { type ContractsStringMap } from '../../BaseChain'; import type TxOpts from '../../TxOpts'; import TimeoutException from '../../exceptions/TimeoutException'; import * as constants from '../../constants'; import * as transactions from '../../transactions'; import * as helper from '../../helper'; const log = new Logger<ILogObj>(); export abstract class TokenManager extends BaseContract { abstract tokenMappingLenghtSlot: number | null; tokens: ContractsStringMap = {}; addToken (tokenName: string, contract: Contract): void { this.tokens[tokenName] = contract; } abstract getTokenCloneAddress ( originTokenAddress: string, originChainName: string ): Promise<string>; async enableAutomaticDeploy (opts: TxOpts): Promise<TransactionResponse> { const txData = await this.contract.enableAutomaticDeploy.populateTransaction(); return await transactions.send( this.provider, txData, opts, this.txName('enableAutomaticDeploy') ); } async disableAutomaticDeploy (opts: TxOpts): Promise<TransactionResponse> { const txData = await this.contract.disableAutomaticDeploy.populateTransaction(); return await transactions.send( this.provider, txData, opts, this.txName('disableAutomaticDeploy') ); } async automaticDeploy (): Promise<string> { return await this.contract.automaticDeploy(); } async grantRole ( role: any, address: string, opts: TxOpts ): Promise<TransactionResponse> { const txData = await this.contract.grantRole.populateTransaction(role, address); return await transactions.send(this.provider, txData, opts, this.txName('grantRole')); } async AUTOMATIC_DEPLOY_ROLE (): Promise<string> { return await this.contract.AUTOMATIC_DEPLOY_ROLE(); } async TOKEN_REGISTRAR_ROLE (): Promise<string> { return await this.contract.TOKEN_REGISTRAR_ROLE(); } async hasTokenManager (chainName: string): Promise<boolean> { return await this.contract.hasTokenManager(chainName); } async ownerOf (tokenName: string, tokenId: number | string): Promise<string> { 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: string, originChainName: string, sleepInterval: number = constants.DEFAULT_SLEEP, iterations: number = constants.DEFAULT_ITERATIONS ): Promise<any> { 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: string): string { 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: string): Promise<number> { 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: string, from: number, to: number ): Promise<string[]> { 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. */ private async getMappedTokenAddress (chainName: string, index: number): Promise<string> { 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); } }