diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml index 538a670..6a6a0e8 100644 --- a/.github/workflows/npm-publish.yaml +++ b/.github/workflows/npm-publish.yaml @@ -27,6 +27,7 @@ jobs: - name: Check if version changed run: | npm install -g pnpm + pnpm install for dir in packages/*/ packages/networks/*/; do if [ -d "$dir" ] && [ "$(basename "$dir")" != "boilerplate" ]; then diff --git a/README.md b/README.md index e69de29..d746267 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,7 @@ +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/index.example.html b/index.example.html deleted file mode 100644 index 4273020..0000000 --- a/index.example.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - MultipleChain JS - - - - - diff --git a/packages/networks/bitcoin/README.md b/packages/networks/bitcoin/README.md index e69de29..d746267 100644 --- a/packages/networks/bitcoin/README.md +++ b/packages/networks/bitcoin/README.md @@ -0,0 +1,7 @@ +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/networks/bitcoin/index.html b/packages/networks/bitcoin/index.example.html similarity index 99% rename from packages/networks/bitcoin/index.html rename to packages/networks/bitcoin/index.example.html index 2324a82..84be06a 100644 --- a/packages/networks/bitcoin/index.html +++ b/packages/networks/bitcoin/index.example.html @@ -224,7 +224,7 @@ } const wallet = new Bitcoin.browser.Wallet(adapter) - const adapterProvider = await wallet.connect(provider, { + const adapterProvider = await wallet.connect({ projectId: '113d9f5689edd84ff230c2a6d679c80c' }) diff --git a/packages/networks/bitcoin/package.json b/packages/networks/bitcoin/package.json index 7040f81..0dfe736 100644 --- a/packages/networks/bitcoin/package.json +++ b/packages/networks/bitcoin/package.json @@ -27,6 +27,16 @@ "types": "./dist/browser/index.d.ts" } }, + "typesVersions": { + "*": { + "node": [ + "./dist/index.d.ts" + ], + "browser": [ + "./dist/browser/index.d.ts" + ] + } + }, "files": [ "dist", "README.md", @@ -62,8 +72,8 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { - "@multiplechain/types": "^0.1.56", - "@multiplechain/utils": "^0.1.20", + "@multiplechain/types": "^0.1.63", + "@multiplechain/utils": "^0.1.21", "axios": "^1.6.8", "bitcore-lib": "^10.0.28", "sats-connect": "^2.3.1", diff --git a/packages/networks/bitcoin/pnpm-lock.yaml b/packages/networks/bitcoin/pnpm-lock.yaml index 3cc25a4..86ea398 100644 --- a/packages/networks/bitcoin/pnpm-lock.yaml +++ b/packages/networks/bitcoin/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: '@multiplechain/types': - specifier: ^0.1.56 - version: 0.1.56 + specifier: ^0.1.63 + version: 0.1.63 '@multiplechain/utils': - specifier: ^0.1.20 - version: 0.1.20 + specifier: ^0.1.21 + version: 0.1.21 axios: specifier: ^1.6.8 version: 1.6.8 @@ -31,12 +31,12 @@ devDependencies: packages: - /@multiplechain/types@0.1.56: - resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==} + /@multiplechain/types@0.1.63: + resolution: {integrity: sha512-4C201vUsN6F1S/M7vT+GZS0wTdKGZXHqn4YmC6ouC8n0uxMCEFdI2sSCadyHKFKG5OBPo16X1oFCX9MVwPqMoA==} dev: false - /@multiplechain/utils@0.1.20: - resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==} + /@multiplechain/utils@0.1.21: + resolution: {integrity: sha512-4mRlnhvXcS+7Hb1By5s2eoCH02kqOSQzV8qj0DHqZK20FtxtduaoGCtcClrFgEjN/HAXnHgdDc1pxTpveIkvRQ==} dependencies: '@types/ws': 8.5.10 bignumber.js: 9.1.2 diff --git a/packages/networks/bitcoin/src/assets/Coin.ts b/packages/networks/bitcoin/src/assets/Coin.ts index a50d1ea..aa981de 100644 --- a/packages/networks/bitcoin/src/assets/Coin.ts +++ b/packages/networks/bitcoin/src/assets/Coin.ts @@ -2,10 +2,15 @@ import axios from 'axios' import { Provider } from '../services/Provider.ts' import { fromSatoshi, toSatoshi } from '../utils.ts' import { Transaction, Script, Address } from 'bitcore-lib' -import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types' -import { CoinTransactionSigner } from '../services/TransactionSigner.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type CoinInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' -export class Coin implements CoinInterface { +export class Coin implements CoinInterface { /** * Blockchain network provider */ @@ -40,10 +45,10 @@ export class Coin implements CoinInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of COIN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const addressStatsApi = this.provider.createEndpoint('address/' + owner) const addressStats = await axios.get(addressStatsApi).then((res) => res.data) const balanceSat = @@ -52,16 +57,16 @@ export class Coin implements CoinInterface { } /** - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -99,7 +104,7 @@ export class Coin implements CoinInterface { transaction.change(sender) transaction.to(receiver, satoshiToSend) - return new CoinTransactionSigner({ + return new TransactionSigner({ sender, receiver, amount: satoshiToSend, diff --git a/packages/networks/bitcoin/src/assets/Contract.ts b/packages/networks/bitcoin/src/assets/Contract.ts deleted file mode 100644 index 14044c0..0000000 --- a/packages/networks/bitcoin/src/assets/Contract.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Provider } from '../services/Provider.ts' -import type { ContractInterface } from '@multiplechain/types' - -export class Contract implements ContractInterface { - /** - * Contract address - */ - address: string - - /** - * Blockchain network provider - */ - provider: Provider - - /** - * @param {string} address Contract address - * @param {Provider} provider Blockchain network provider - */ - constructor(address: string, provider?: Provider) { - this.address = address - this.provider = provider ?? Provider.instance - throw new Error('This class is not implemented for Bitcoin.') - } - - /** - * @returns {string} Contract address - */ - getAddress(): string { - return this.address - } - - /** - * @param {string} method Method name - * @param {any[]} args Method parameters - * @returns {Promise} Method result - */ - async callMethod(method: string, ...args: any[]): Promise { - return {} - } - - /** - * @param {string} method Method name - * @param {any[]} args Sender wallet address - * @returns {Promise} Encoded method data - */ - async getMethodData(method: string, ...args: any[]): Promise { - return {} - } - - /** - * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters - * @returns {Promise} Encoded method data - */ - async createTransactionData(method: string, from: string, ...args: any[]): Promise { - return '' - } -} diff --git a/packages/networks/bitcoin/src/assets/NFT.ts b/packages/networks/bitcoin/src/assets/NFT.ts deleted file mode 100644 index 0a89a51..0000000 --- a/packages/networks/bitcoin/src/assets/NFT.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Contract } from './Contract.ts' -import type { NftInterface } from '@multiplechain/types' -import { NftTransactionSigner } from '../services/TransactionSigner.ts' - -export class NFT extends Contract implements NftInterface { - /** - * @returns {Promise} NFT name - */ - async getName(): Promise { - return 'example' - } - - /** - * @returns {Promise} NFT symbol - */ - async getSymbol(): Promise { - return 'example' - } - - /** - * @param {string} owner Wallet address - * @returns {Promise} Wallet balance as currency of NFT - */ - async getBalance(owner: string): Promise { - return 0 - } - - /** - * @param {number | string} nftId NFT ID - * @returns {Promise} Wallet address of the owner of the NFT - */ - async getOwner(nftId: number | string): Promise { - return 'example' - } - - /** - * @param {number | string} nftId NFT ID - * @returns {Promise} URI of the NFT - */ - async getTokenURI(nftId: number | string): Promise { - return 'example' - } - - /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Wallet address of the approved spender - */ - async getApproved(nftId: number | string): Promise { - return 'example' - } - - /** - * @param {string} sender Sender address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer - */ - async transfer( - sender: string, - receiver: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') - } - - /** - * @param {string} spender Spender address - * @param {string} owner Owner address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer - */ - async transferFrom( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') - } - - /** - * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Transaction signer - */ - async approve( - owner: string, - spender: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') - } -} diff --git a/packages/networks/bitcoin/src/assets/Token.ts b/packages/networks/bitcoin/src/assets/Token.ts deleted file mode 100644 index a090488..0000000 --- a/packages/networks/bitcoin/src/assets/Token.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Contract } from './Contract.ts' -import type { TokenInterface } from '@multiplechain/types' -import { TokenTransactionSigner } from '../services/TransactionSigner.ts' - -export class Token extends Contract implements TokenInterface { - /** - * @returns {Promise} Token name - */ - async getName(): Promise { - return 'example' - } - - /** - * @returns {Promise} Token symbol - */ - async getSymbol(): Promise { - return 'example' - } - - /** - * @returns {Promise} Decimal value of the token - */ - async getDecimals(): Promise { - return 18 - } - - /** - * @param {string} owner Wallet address - * @returns {Promise} Wallet balance as currency of TOKEN - */ - async getBalance(owner: string): Promise { - return 0 - } - - /** - * @returns {Promise} Total supply of the token - */ - async getTotalSupply(): Promise { - return 0 - } - - /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner - * @returns {Promise} Amount of tokens that the spender is allowed to spend - */ - async getAllowance(owner: string, spender: string): Promise { - return 0 - } - - /** - * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred - * @returns {Promise} Transaction signer - */ - async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { - return new TokenTransactionSigner('example') - } - - /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred - * @returns {Promise} Transaction signer - */ - async transferFrom( - spender: string, - owner: string, - receiver: string, - amount: number - ): Promise { - return new TokenTransactionSigner('example') - } - - /** - * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number} amount Amount of the tokens that will be used - * @returns {Promise} Transaction signer - */ - async approve(owner: string, spender: string, amount: number): Promise { - return new TokenTransactionSigner('example') - } -} diff --git a/packages/networks/bitcoin/src/assets/index.ts b/packages/networks/bitcoin/src/assets/index.ts index 7ea522f..5742d2b 100644 --- a/packages/networks/bitcoin/src/assets/index.ts +++ b/packages/networks/bitcoin/src/assets/index.ts @@ -1,4 +1 @@ -export * from './NFT.ts' export * from './Coin.ts' -export * from './Token.ts' -export * from './Contract.ts' diff --git a/packages/networks/bitcoin/src/browser/Wallet.ts b/packages/networks/bitcoin/src/browser/Wallet.ts index cb8e8d0..7520be9 100644 --- a/packages/networks/bitcoin/src/browser/Wallet.ts +++ b/packages/networks/bitcoin/src/browser/Wallet.ts @@ -2,12 +2,15 @@ import { type WalletInterface, type WalletAdapterInterface, type WalletPlatformEnum, - type TransactionSignerInterface, - type ProviderInterface, - ErrorTypeEnum + ErrorTypeEnum, + type UnknownConfig, + type ConnectConfig, + type WalletAddress, + type SignedMessage, + type TransactionId } from '@multiplechain/types' import { Provider } from '../services/Provider.ts' -import type { TransactionData } from '../services/TransactionSigner.ts' +import type { TransactionSigner } from '../services/TransactionSigner.ts' export interface BitcoinWalletAdapter { getAddress: () => Promise @@ -30,18 +33,20 @@ const rejectMap = (error: any, reject: (a: any) => any): any => { return reject(error) } -export class Wallet implements WalletInterface { - adapter: WalletAdapterInterface +type WalletAdapter = WalletAdapterInterface + +export class Wallet implements WalletInterface { + adapter: WalletAdapter walletProvider: BitcoinWalletAdapter networkProvider: Provider /** - * @param {WalletAdapterInterface} adapter + * @param {WalletAdapter} adapter * @param {Provider} provider */ - constructor(adapter: WalletAdapterInterface, provider?: Provider) { + constructor(adapter: WalletAdapter, provider?: Provider) { this.adapter = adapter this.networkProvider = provider ?? Provider.instance } @@ -83,28 +88,27 @@ export class Wallet implements WalletInterface { /** * @param {string} url - * @param {object} ops + * @param {UnknownConfig} config * @returns {string} */ - createDeepLink(url: string, ops?: object): string | null { + createDeepLink(url: string, config?: UnknownConfig): string | null { if (this.adapter.createDeepLink === undefined) { return null } - return this.adapter.createDeepLink(url, ops) + return this.adapter.createDeepLink(url, config) } /** - * @param {ProviderInterface} provider - * @param {Object} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - async connect(provider?: ProviderInterface, ops?: object): Promise { + async connect(config?: ConnectConfig): Promise { return await new Promise((resolve, reject) => { this.adapter - .connect(provider, ops) + .connect(this.networkProvider, config) .then(async (provider) => { - this.walletProvider = provider as BitcoinWalletAdapter + this.walletProvider = provider resolve(await this.getAddress()) }) .catch((error) => { @@ -135,26 +139,45 @@ export class Wallet implements WalletInterface { } /** - * @returns {Promise} + * @returns {Promise} */ - async getAddress(): Promise { + async getAddress(): Promise { return await this.walletProvider.getAddress() } /** * @param {string} message + * @returns {Promise} */ - async signMessage(message: string): Promise { - return await this.walletProvider.signMessage(message) + async signMessage(message: string): Promise { + return await new Promise((resolve, reject) => { + this.walletProvider + .signMessage(message) + .then((signature) => { + resolve(signature) + }) + .catch((error) => { + rejectMap(error, reject) + }) + }) } /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - async sendTransaction(transactionSigner: TransactionSignerInterface): Promise { - const data = (await transactionSigner.getRawData()) as TransactionData - return await this.walletProvider.sendBitcoin(data.receiver, data.amount) + async sendTransaction(transactionSigner: TransactionSigner): Promise { + const data = transactionSigner.getRawData() + return await new Promise((resolve, reject) => { + this.walletProvider + .sendBitcoin(data.receiver, data.amount) + .then((txHash) => { + resolve(txHash) + }) + .catch((error) => { + rejectMap(error, reject) + }) + }) } /** diff --git a/packages/networks/bitcoin/src/browser/adapters/Leather.ts b/packages/networks/bitcoin/src/browser/adapters/Leather.ts index 3f363fa..f2863af 100644 --- a/packages/networks/bitcoin/src/browser/adapters/Leather.ts +++ b/packages/networks/bitcoin/src/browser/adapters/Leather.ts @@ -1,7 +1,8 @@ import icons from './icons.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' +import type { WalletAdapterInterface } from '@multiplechain/types' import type { BitcoinWalletAdapter } from '../Wallet.ts' +import type { Provider } from '../../services/Provider.ts' declare global { interface Window { @@ -16,15 +17,16 @@ declare global { let connected = false -const Leather: WalletAdapterInterface = { +const Leather: WalletAdapterInterface = { id: 'leather', name: 'Leather', icon: icons.Leather, + provider: window.LeatherProvider, platforms: [WalletPlatformEnum.BROWSER], downloadLink: 'https://leather.io/install-extension', isDetected: () => Boolean(window.LeatherProvider), isConnected: async () => connected, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const leather = window.LeatherProvider diff --git a/packages/networks/bitcoin/src/browser/adapters/UniSat.ts b/packages/networks/bitcoin/src/browser/adapters/UniSat.ts index b089c8c..a285d5c 100644 --- a/packages/networks/bitcoin/src/browser/adapters/UniSat.ts +++ b/packages/networks/bitcoin/src/browser/adapters/UniSat.ts @@ -1,7 +1,8 @@ import icons from './icons.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' import type { BitcoinWalletAdapter } from '../Wallet.ts' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' declare global { interface Window { @@ -17,15 +18,16 @@ declare global { } } -const UniSat: WalletAdapterInterface = { +const UniSat: WalletAdapterInterface = { id: 'unisat', name: 'UniSat', icon: icons.UniSat, + provider: window.unisat, platforms: [WalletPlatformEnum.BROWSER], downloadLink: 'https://unisat.io/download', isDetected: () => Boolean(window.unisat?.requestAccounts), isConnected: async () => window?.unisat?._isConnected ?? false, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const network = provider !== undefined && provider?.isTestnet() ? 'testnet' : 'livenet' diff --git a/packages/networks/bitcoin/src/browser/adapters/Xverse.ts b/packages/networks/bitcoin/src/browser/adapters/Xverse.ts index ef50592..a6cdde8 100644 --- a/packages/networks/bitcoin/src/browser/adapters/Xverse.ts +++ b/packages/networks/bitcoin/src/browser/adapters/Xverse.ts @@ -3,7 +3,6 @@ import type { BitcoinWalletAdapter } from '../Wallet.ts' import { ErrorTypeEnum, WalletPlatformEnum, - type ProviderInterface, type WalletAdapterInterface } from '@multiplechain/types' import { @@ -13,18 +12,20 @@ import { signMessage, AddressPurpose } from 'sats-connect' +import type { Provider } from '../../services/Provider.ts' let connected = false -const Xverse: WalletAdapterInterface = { +const Xverse: WalletAdapterInterface = { id: 'xverse', name: 'Xverse', icon: icons.Xverse, + provider: window.XverseProviders?.BitcoinProvider, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://www.xverse.app/download', isDetected: () => Boolean(window.XverseProviders?.BitcoinProvider), isConnected: async () => connected, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const type = provider !== undefined && provider?.isTestnet() diff --git a/packages/networks/bitcoin/src/browser/index.ts b/packages/networks/bitcoin/src/browser/index.ts index 787e9bd..0695fc8 100644 --- a/packages/networks/bitcoin/src/browser/index.ts +++ b/packages/networks/bitcoin/src/browser/index.ts @@ -1,4 +1,5 @@ -import { Wallet } from './Wallet.ts' +import type { Provider } from 'sats-connect' +import { Wallet, type BitcoinWalletAdapter } from './Wallet.ts' import * as adapterList from './adapters/index.ts' import type { WalletAdapterListType, @@ -6,9 +7,11 @@ import type { RegisterWalletAdapterType } from '@multiplechain/types' -const adapters: WalletAdapterListType = {} +const adapters: WalletAdapterListType = {} -const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => { +const registerAdapter: RegisterWalletAdapterType = ( + adapter: WalletAdapterInterface +): void => { if (Object.values(adapters).find((a) => a.id === adapter.id) !== undefined) { throw new Error(`Adapter with id ${adapter.id} already exists`) } diff --git a/packages/networks/bitcoin/src/models/CoinTransaction.ts b/packages/networks/bitcoin/src/models/CoinTransaction.ts index d681e05..353e75d 100644 --- a/packages/networks/bitcoin/src/models/CoinTransaction.ts +++ b/packages/networks/bitcoin/src/models/CoinTransaction.ts @@ -1,42 +1,42 @@ import { fromSatoshi } from '../utils.ts' import { Transaction } from './Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { WalletAddress, CoinTransactionInterface, TransferAmount } from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() return data?.vout[0].scriptpubkey_address ?? '' } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return await this.getSigner() } /** - * @returns {Promise} Amount of coin that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const data = await this.getData() return fromSatoshi(data?.vout[0].value ?? 0) } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/bitcoin/src/models/ContractTransaction.ts b/packages/networks/bitcoin/src/models/ContractTransaction.ts deleted file mode 100644 index 4813a5a..0000000 --- a/packages/networks/bitcoin/src/models/ContractTransaction.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Transaction } from './Transaction.ts' -import type { Provider } from '../services/Provider.ts' -import type { ContractTransactionInterface } from '@multiplechain/types' - -export class ContractTransaction extends Transaction implements ContractTransactionInterface { - /** - * @param {string} id Transaction id - * @param {Provider} provider Blockchain network provider - */ - constructor(id: string, provider?: Provider) { - super(id, provider) - throw new Error('This class is not implemented for Bitcoin.') - } - - /** - * @returns {Promise} Contract address of the transaction - */ - async getAddress(): Promise { - return 'example' - } -} diff --git a/packages/networks/bitcoin/src/models/NftTransaction.ts b/packages/networks/bitcoin/src/models/NftTransaction.ts deleted file mode 100644 index 09b8d80..0000000 --- a/packages/networks/bitcoin/src/models/NftTransaction.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ContractTransaction } from './ContractTransaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import type { NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' - -export class NftTransaction extends ContractTransaction implements NftTransactionInterface { - /** - * @returns {Promise} Receiver wallet address - */ - async getReceiver(): Promise { - return 'example' - } - - /** - * @returns {Promise} Wallet address of the sender of transaction - */ - async getSender(): Promise { - return 'example' - } - - /** - * @returns {Promise} NFT ID - */ - async getNftId(): Promise { - return 0 - } - - /** - * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} nftId ID of the NFT that will be transferred - * @override verifyTransfer() in AssetTransactionInterface - * @returns {Promise} Status of the transaction - */ - async verifyTransfer( - direction: AssetDirectionEnum, - address: string, - nftId: number | string - ): Promise { - return TransactionStatusEnum.PENDING - } -} diff --git a/packages/networks/bitcoin/src/models/TokenTransaction.ts b/packages/networks/bitcoin/src/models/TokenTransaction.ts deleted file mode 100644 index b1ea29e..0000000 --- a/packages/networks/bitcoin/src/models/TokenTransaction.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ContractTransaction } from './ContractTransaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetDirectionEnum, TokenTransactionInterface } from '@multiplechain/types' - -export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { - /** - * @returns {Promise} Wallet address of the receiver of transaction - */ - async getReceiver(): Promise { - return 'example' - } - - /** - * @returns {Promise} Wallet address of the sender of transaction - */ - async getSender(): Promise { - return 'example' - } - - /** - * @returns {Promise} Amount of tokens that will be transferred - */ - async getAmount(): Promise { - return 0 - } - - /** - * @param {AssetDirectionEnum} direction - Direction of the transaction (token) - * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param {number} amount Amount of tokens that will be approved - * @returns {Promise} Status of the transaction - */ - async verifyTransfer( - direction: AssetDirectionEnum, - address: string, - amount: number - ): Promise { - return TransactionStatusEnum.PENDING - } -} diff --git a/packages/networks/bitcoin/src/models/Transaction.ts b/packages/networks/bitcoin/src/models/Transaction.ts index 4cdeed8..2026094 100644 --- a/packages/networks/bitcoin/src/models/Transaction.ts +++ b/packages/networks/bitcoin/src/models/Transaction.ts @@ -1,8 +1,17 @@ import { fromSatoshi, sleep } from '../utils.ts' import axios, { type AxiosError } from 'axios' import { Provider } from '../services/Provider.ts' -import type { TransactionInterface } from '@multiplechain/types' import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types' +import { + TransactionTypeEnum, + type BlockConfirmationCount, + type BlockNumber, + type BlockTimestamp, + type TransactionFee, + type TransactionId, + type TransactionInterface, + type WalletAddress +} from '@multiplechain/types' export interface VinObject { txid: string @@ -48,11 +57,11 @@ export interface TransactionData { let counter = 0 -export class Transaction implements TransactionInterface { +export class Transaction implements TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId /** * Blockchain network provider @@ -65,10 +74,10 @@ export class Transaction implements TransactionInterface { data: TransactionData | null = null /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider */ - constructor(id: string, provider?: Provider) { + constructor(id: TransactionId, provider?: Provider) { this.id = id this.provider = provider ?? Provider.instance } @@ -129,12 +138,19 @@ export class Transaction implements TransactionInterface { } /** - * @returns {string} Transaction ID + * @returns {TransactionId} Transaction ID */ - getId(): string { + getId(): TransactionId { return this.id } + /** + * @returns {Promise} Transaction type + */ + async getType(): Promise { + return TransactionTypeEnum.COIN + } + /** * @returns {string} Transaction URL */ @@ -143,41 +159,41 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSigner(): Promise { + async getSigner(): Promise { const data = await this.getData() return data?.vin[0].prevout.scriptpubkey_address ?? '' } /** - * @returns {Promise} Transaction fee + * @returns {Promise} Transaction fee */ - async getFee(): Promise { + async getFee(): Promise { const data = await this.getData() return fromSatoshi(data?.fee ?? 0) } /** - * @returns {Promise} Block number that transaction + * @returns {Promise} Block number that transaction */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { const data = await this.getData() return data?.status?.block_height ?? 0 } /** - * @returns {Promise} Block timestamp that transaction + * @returns {Promise} Block timestamp that transaction */ - async getBlockTimestamp(): Promise { + async getBlockTimestamp(): Promise { const data = await this.getData() return data?.status?.block_time ?? 0 } /** - * @returns {Promise} Confirmation count of the block + * @returns {Promise} Confirmation count of the block */ - async getBlockConfirmationCount(): Promise { + async getBlockConfirmationCount(): Promise { const data = await this.getData() if (data === null) { return 0 diff --git a/packages/networks/bitcoin/src/models/index.ts b/packages/networks/bitcoin/src/models/index.ts index cc0a27c..f4c3f87 100644 --- a/packages/networks/bitcoin/src/models/index.ts +++ b/packages/networks/bitcoin/src/models/index.ts @@ -1,5 +1,2 @@ export * from './Transaction.ts' -export * from './NftTransaction.ts' export * from './CoinTransaction.ts' -export * from './TokenTransaction.ts' -export * from './ContractTransaction.ts' diff --git a/packages/networks/bitcoin/src/services/Provider.ts b/packages/networks/bitcoin/src/services/Provider.ts index 30086e2..1553561 100644 --- a/packages/networks/bitcoin/src/services/Provider.ts +++ b/packages/networks/bitcoin/src/services/Provider.ts @@ -7,7 +7,7 @@ export interface BitcoinNetworkConfigInterface { blockCypherToken?: string } -export class Provider implements Omit { +export class Provider implements ProviderInterface { /** * Network configuration of the provider */ @@ -113,7 +113,8 @@ export class Provider implements Omit { /** * Update network configuration of the provider - * @param network - Network configuration of the provider + * @param {BitcoinNetworkConfigInterface} network - Network configuration + * @returns {void} */ update(network: BitcoinNetworkConfigInterface): void { this.network = network @@ -148,7 +149,7 @@ export class Provider implements Omit { /** * Get the current network configuration is testnet or not - * @returns boolean + * @returns {boolean} Testnet or not */ isTestnet(): boolean { return this.network?.testnet ?? false diff --git a/packages/networks/bitcoin/src/services/TransactionListener.ts b/packages/networks/bitcoin/src/services/TransactionListener.ts index 04fa972..62feff9 100644 --- a/packages/networks/bitcoin/src/services/TransactionListener.ts +++ b/packages/networks/bitcoin/src/services/TransactionListener.ts @@ -2,16 +2,14 @@ import type { TransactionTypeEnum, DynamicTransactionType, TransactionListenerInterface, - DynamicTransactionListenerFilterType + DynamicTransactionListenerFilterType, + TransactionId } from '@multiplechain/types' import WebSocket from 'ws' import { Provider } from './Provider.ts' import { fromSatoshi } from '../utils.ts' import { Transaction } from '../models/Transaction.ts' -import type { NftTransaction } from '../models/NftTransaction.ts' import { CoinTransaction } from '../models/CoinTransaction.ts' -import type { TokenTransaction } from '../models/TokenTransaction.ts' -import type { ContractTransaction } from '../models/ContractTransaction.ts' import { checkWebSocket, objectsEqual } from '@multiplechain/utils' import { TransactionListenerProcessIndex } from '@multiplechain/types' @@ -25,10 +23,10 @@ interface Values { type TransactionListenerTriggerType = DynamicTransactionType< T, Transaction, - ContractTransaction, + Transaction, CoinTransaction, - TokenTransaction, - NftTransaction + Transaction, + Transaction > type TransactionListenerCallbackType< @@ -48,29 +46,29 @@ export class TransactionListener< type: T /** - * Transaction listener callback + * Provider */ - callbacks: CallBackType[] = [] + provider: Provider /** - * Transaction listener filter + * Listener status */ - filter?: DynamicTransactionListenerFilterType | Record + status: boolean = false /** - * Provider + * Transaction listener callback */ - provider: Provider + callbacks: CallBackType[] = [] /** - * Listener status + * Triggered transactions */ - status: boolean = false + triggeredTransactions: TransactionId[] = [] /** - * Triggered transactions + * Transaction listener filter */ - triggeredTransactions: string[] = [] + filter?: DynamicTransactionListenerFilterType | Record /** * WebSocket diff --git a/packages/networks/bitcoin/src/services/TransactionSigner.ts b/packages/networks/bitcoin/src/services/TransactionSigner.ts index 15ae13a..e0526c5 100644 --- a/packages/networks/bitcoin/src/services/TransactionSigner.ts +++ b/packages/networks/bitcoin/src/services/TransactionSigner.ts @@ -1,12 +1,8 @@ import axios from 'axios' import type { AxiosError } from 'axios' import { Provider } from '../services/Provider.ts' -import { Transaction } from '../models/Transaction.ts' -import { NftTransaction } from '../models/NftTransaction.ts' -import { CoinTransaction } from '../models/CoinTransaction.ts' -import { TokenTransaction } from '../models/TokenTransaction.ts' -import { type TransactionSignerInterface } from '@multiplechain/types' import type { Transaction as BitcoreLibTransactionData } from 'bitcore-lib' +import type { PrivateKey, TransactionId, TransactionSignerInterface } from '@multiplechain/types' export interface TransactionData { sender: string @@ -15,7 +11,7 @@ export interface TransactionData { bitcoreLib: BitcoreLibTransactionData } -export class TransactionSigner implements TransactionSignerInterface { +export class TransactionSigner implements TransactionSignerInterface { /** * Transaction data from the blockchain network */ @@ -33,6 +29,7 @@ export class TransactionSigner implements TransactionSignerInterface { /** * @param {TransactionData} rawData - Transaction data + * @param {Provider} provider - Blockchain network provider */ constructor(rawData: TransactionData, provider?: Provider) { this.rawData = rawData @@ -41,10 +38,10 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Sign the transaction - * @param {string} privateKey - Transaction data - * @returns {Promise} Signed transaction data + * @param {PrivateKey} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - async sign(privateKey: string): Promise { + async sign(privateKey: PrivateKey): Promise { this.rawData.bitcoreLib.sign(privateKey) this.signedData = this.rawData.bitcoreLib.serialize() return this @@ -52,16 +49,16 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Send the transaction to the blockchain network - * @returns {Promise} + * @returns {Promise} */ - async send(): Promise { + async send(): Promise { try { const result = await axios({ method: 'POST', url: `https://blockstream.info/testnet/api/tx`, data: this.signedData }) - return new Transaction(result.data as string) + return result.data as TransactionId } catch (error: any) { throw new Error(JSON.stringify((error as AxiosError).response?.data)) } @@ -69,7 +66,7 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Get the raw transaction data - * @returns Transaction data + * @returns {TransactionData} */ getRawData(): TransactionData { return this.rawData @@ -77,39 +74,9 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Get the signed transaction data - * @returns Signed transaction data + * @returns {string} */ getSignedData(): string { return this.signedData ?? '' } } - -export class CoinTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new CoinTransaction((await super.send()).getId()) - } -} - -export class TokenTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new TokenTransaction((await super.send()).getId()) - } -} - -export class NftTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new NftTransaction((await super.send()).getId()) - } -} diff --git a/packages/networks/bitcoin/tests/assets.spec.ts b/packages/networks/bitcoin/tests/assets.spec.ts index 36147df..5edb0a1 100644 --- a/packages/networks/bitcoin/tests/assets.spec.ts +++ b/packages/networks/bitcoin/tests/assets.spec.ts @@ -3,7 +3,7 @@ import { describe, it, expect, assert } from 'vitest' import { Coin } from '../src/assets/Coin.ts' import { math } from '@multiplechain/utils' import { Transaction } from '../src/models/Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' +import { TransactionStatusEnum, type TransactionId } from '@multiplechain/types' import { TransactionSigner } from '../src/services/TransactionSigner.ts' const testAmount = Number(process.env.BTC_TRANSFER_AMOUNT) @@ -24,8 +24,8 @@ const checkSigner = async (signer: TransactionSigner, privateKey?: string): Prom assert.isString(signer.getSignedData()) } -const checkTx = async (transaction: Transaction): Promise => { - expect(transaction).toBeInstanceOf(Transaction) +const checkTx = async (transactionId: TransactionId): Promise => { + const transaction = new Transaction(transactionId) const status = await transaction.wait(10 * 1000) expect(status).toBe(TransactionStatusEnum.CONFIRMED) } diff --git a/packages/networks/boilerplate/README.md b/packages/networks/boilerplate/README.md index e69de29..d746267 100644 --- a/packages/networks/boilerplate/README.md +++ b/packages/networks/boilerplate/README.md @@ -0,0 +1,7 @@ +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/networks/boilerplate/index.html b/packages/networks/boilerplate/index.example.html similarity index 99% rename from packages/networks/boilerplate/index.html rename to packages/networks/boilerplate/index.example.html index 4e508e4..cc91fa0 100644 --- a/packages/networks/boilerplate/index.html +++ b/packages/networks/boilerplate/index.example.html @@ -230,7 +230,7 @@ } const wallet = new Boilerplate.browser.Wallet(adapter) - const adapterProvider = await wallet.connect(provider, { + const adapterProvider = await wallet.connect({ projectId: '113d9f5689edd84ff230c2a6d679c80c' }) diff --git a/packages/networks/boilerplate/package.json b/packages/networks/boilerplate/package.json index 9e3607a..144af60 100644 --- a/packages/networks/boilerplate/package.json +++ b/packages/networks/boilerplate/package.json @@ -27,6 +27,16 @@ "types": "./dist/browser/index.d.ts" } }, + "typesVersions": { + "*": { + "node": [ + "./dist/index.d.ts" + ], + "browser": [ + "./dist/browser/index.d.ts" + ] + } + }, "files": [ "dist", "README.md", @@ -62,7 +72,7 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { - "@multiplechain/types": "^0.1.56", - "@multiplechain/utils": "^0.1.20" + "@multiplechain/types": "^0.1.63", + "@multiplechain/utils": "^0.1.21" } } \ No newline at end of file diff --git a/packages/networks/boilerplate/pnpm-lock.yaml b/packages/networks/boilerplate/pnpm-lock.yaml index 08e12c2..c63ee4e 100644 --- a/packages/networks/boilerplate/pnpm-lock.yaml +++ b/packages/networks/boilerplate/pnpm-lock.yaml @@ -6,20 +6,20 @@ settings: dependencies: '@multiplechain/types': - specifier: ^0.1.56 - version: 0.1.56 + specifier: ^0.1.63 + version: 0.1.63 '@multiplechain/utils': - specifier: ^0.1.20 - version: 0.1.20 + specifier: ^0.1.21 + version: 0.1.21 packages: - /@multiplechain/types@0.1.56: - resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==} + /@multiplechain/types@0.1.63: + resolution: {integrity: sha512-4C201vUsN6F1S/M7vT+GZS0wTdKGZXHqn4YmC6ouC8n0uxMCEFdI2sSCadyHKFKG5OBPo16X1oFCX9MVwPqMoA==} dev: false - /@multiplechain/utils@0.1.20: - resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==} + /@multiplechain/utils@0.1.21: + resolution: {integrity: sha512-4mRlnhvXcS+7Hb1By5s2eoCH02kqOSQzV8qj0DHqZK20FtxtduaoGCtcClrFgEjN/HAXnHgdDc1pxTpveIkvRQ==} dependencies: '@types/ws': 8.5.10 bignumber.js: 9.1.2 diff --git a/packages/networks/boilerplate/src/assets/Coin.ts b/packages/networks/boilerplate/src/assets/Coin.ts index 1c2d8b3..2b7df88 100644 --- a/packages/networks/boilerplate/src/assets/Coin.ts +++ b/packages/networks/boilerplate/src/assets/Coin.ts @@ -1,8 +1,8 @@ import { Provider } from '../services/Provider.ts' -import type { CoinInterface } from '@multiplechain/types' -import { CoinTransactionSigner } from '../services/TransactionSigner.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' +import type { CoinInterface, TransferAmount, WalletAddress } from '@multiplechain/types' -export class Coin implements CoinInterface { +export class Coin implements CoinInterface { /** * Blockchain network provider */ @@ -37,24 +37,24 @@ export class Coin implements CoinInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of COIN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return 0 } /** - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { - return new CoinTransactionSigner('example') + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { + return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/assets/Contract.ts b/packages/networks/boilerplate/src/assets/Contract.ts index ee3a295..5b63d07 100644 --- a/packages/networks/boilerplate/src/assets/Contract.ts +++ b/packages/networks/boilerplate/src/assets/Contract.ts @@ -1,11 +1,11 @@ import { Provider } from '../services/Provider.ts' -import type { ContractInterface } from '@multiplechain/types' +import type { ContractAddress, ContractInterface, WalletAddress } from '@multiplechain/types' export class Contract implements ContractInterface { /** * Contract address */ - address: string + address: ContractAddress /** * Blockchain network provider @@ -13,46 +13,50 @@ export class Contract implements ContractInterface { provider: Provider /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider */ - constructor(address: string, provider?: Provider) { + constructor(address: ContractAddress, provider?: Provider) { this.address = address this.provider = provider ?? Provider.instance } /** - * @returns {string} Contract address + * @returns {ContractAddress} Contract address */ - getAddress(): string { + getAddress(): ContractAddress { return this.address } /** * @param {string} method Method name - * @param {any[]} args Method parameters - * @returns {Promise} Method result + * @param {unknown[]} args Method parameters + * @returns {Promise} Method result */ - async callMethod(method: string, ...args: any[]): Promise { + async callMethod(method: string, ...args: unknown[]): Promise { return {} } /** * @param {string} method Method name - * @param {any[]} args Sender wallet address - * @returns {Promise} Encoded method data + * @param {unknown[]} args Sender wallet address + * @returns {Promise} Encoded method data */ - async getMethodData(method: string, ...args: any[]): Promise { + async getMethodData(method: string, ...args: unknown[]): Promise { return {} } /** * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters - * @returns {Promise} Encoded method data + * @param {WalletAddress} from Sender wallet address + * @param {unknown[]} args Method parameters + * @returns {Promise} Encoded method data */ - async createTransactionData(method: string, from: string, ...args: any[]): Promise { + async createTransactionData( + method: string, + from: WalletAddress, + ...args: any[] + ): Promise { return '' } } diff --git a/packages/networks/boilerplate/src/assets/NFT.ts b/packages/networks/boilerplate/src/assets/NFT.ts index 0a89a51..5452c21 100644 --- a/packages/networks/boilerplate/src/assets/NFT.ts +++ b/packages/networks/boilerplate/src/assets/NFT.ts @@ -1,8 +1,8 @@ import { Contract } from './Contract.ts' -import type { NftInterface } from '@multiplechain/types' -import { NftTransactionSigner } from '../services/TransactionSigner.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' +import type { NftId, NftInterface, WalletAddress } from '@multiplechain/types' -export class NFT extends Contract implements NftInterface { +export class NFT extends Contract implements NftInterface { /** * @returns {Promise} NFT name */ @@ -18,79 +18,79 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of NFT */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return 0 } /** - * @param {number | string} nftId NFT ID - * @returns {Promise} Wallet address of the owner of the NFT + * @param {NftId} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - async getOwner(nftId: number | string): Promise { + async getOwner(nftId: NftId): Promise { return 'example' } /** - * @param {number | string} nftId NFT ID + * @param {NftId} nftId NFT ID * @returns {Promise} URI of the NFT */ - async getTokenURI(nftId: number | string): Promise { + async getTokenURI(nftId: NftId): Promise { return 'example' } /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Wallet address of the approved spender + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - async getApproved(nftId: number | string): Promise { + async getApproved(nftId: NftId): Promise { return 'example' } /** - * @param {string} sender Sender address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer + * @param {WalletAddress} sender Sender address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID + * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') + sender: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { + return new TransactionSigner('example') } /** - * @param {string} spender Spender address - * @param {string} owner Owner address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer + * @param {WalletAddress} spender Spender address + * @param {WalletAddress} owner Owner address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID + * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { + return new TransactionSigner('example') } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {NftId} nftId ID of the NFT that will be transferred * @returns {Promise} Transaction signer */ async approve( - owner: string, - spender: string, - nftId: number | string - ): Promise { - return new NftTransactionSigner('example') + owner: WalletAddress, + spender: WalletAddress, + nftId: NftId + ): Promise { + return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/assets/Token.ts b/packages/networks/boilerplate/src/assets/Token.ts index 8d30a0d..0f640e4 100644 --- a/packages/networks/boilerplate/src/assets/Token.ts +++ b/packages/networks/boilerplate/src/assets/Token.ts @@ -1,8 +1,8 @@ import { Contract } from './Contract.ts' -import type { TokenInterface } from '@multiplechain/types' -import { TokenTransactionSigner } from '../services/TransactionSigner.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' +import type { TokenInterface, TransferAmount, WalletAddress } from '@multiplechain/types' -export class Token extends Contract implements TokenInterface { +export class Token extends Contract implements TokenInterface { /** * @returns {Promise} Token name */ @@ -25,10 +25,10 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of TOKEN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return 0 } @@ -40,57 +40,57 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner + * @param {WalletAddress} owner Address of owner of the tokens that is being used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner * @returns {Promise} Amount of tokens that the spender is allowed to spend */ - async getAllowance(owner: string, spender: string): Promise { + async getAllowance(owner: WalletAddress, spender: WalletAddress): Promise { return 0 } /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { - return new TokenTransactionSigner('example') + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { + return new TransactionSigner('example') } /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred - * @returns {Promise} Transaction signer + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - amount: number - ): Promise { - return new TokenTransactionSigner('example') + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { + return new TransactionSigner('example') } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number} amount Amount of the tokens that will be used + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {TransferAmount} amount Amount of the tokens that will be used * @returns {Promise} Transaction signer */ async approve( - owner: string, - spender: string, - amount: number - ): Promise { - return new TokenTransactionSigner('example') + owner: WalletAddress, + spender: WalletAddress, + amount: TransferAmount + ): Promise { + return new TransactionSigner('example') } } diff --git a/packages/networks/boilerplate/src/browser/Wallet.ts b/packages/networks/boilerplate/src/browser/Wallet.ts index 83ade3f..3d75a8a 100644 --- a/packages/networks/boilerplate/src/browser/Wallet.ts +++ b/packages/networks/boilerplate/src/browser/Wallet.ts @@ -1,24 +1,39 @@ +import { Provider } from '../services/Provider.ts' +import type { TransactionSigner } from '../services/TransactionSigner.ts' import type { WalletInterface, WalletAdapterInterface, WalletPlatformEnum, - TransactionSignerInterface, - ProviderInterface + TransactionId, + SignedMessage, + WalletAddress, + ConnectConfig, + UnknownConfig } from '@multiplechain/types' -import { Provider } from '../services/Provider.ts' -export class Wallet implements WalletInterface { - adapter: WalletAdapterInterface +type WalletAdapter = WalletAdapterInterface - walletProvider: object +export class Wallet implements WalletInterface { + /** + * WalletAdapter instance + */ + adapter: WalletAdapter + /** + * Wallet provider is the instance of the wallet connection + */ + walletProvider: unknown + + /** + * Network provider is the instance of the blockchain network connection + */ networkProvider: Provider /** - * @param {WalletAdapterInterface} adapter + * @param {WalletAdapter} adapter * @param {Provider} provider */ - constructor(adapter: WalletAdapterInterface, provider?: Provider) { + constructor(adapter: WalletAdapter, provider?: Provider) { this.adapter = adapter this.networkProvider = provider ?? Provider.instance } @@ -60,23 +75,22 @@ export class Wallet implements WalletInterface { /** * @param {string} url - * @param {object} ops + * @param {UnknownConfig} config * @returns {string} */ - createDeepLink(url: string, ops?: object): string | null { + createDeepLink(url: string, config?: UnknownConfig): string | null { if (this.adapter.createDeepLink === undefined) { return null } - return this.adapter.createDeepLink(url, ops) + return this.adapter.createDeepLink(url, config) } /** - * @param {ProviderInterface} provider - * @param {Object} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - async connect(provider?: ProviderInterface, ops?: object): Promise { + async connect(config?: ConnectConfig): Promise { await this.adapter.connect() return 'wallet address' } @@ -96,24 +110,25 @@ export class Wallet implements WalletInterface { } /** - * @returns {Promise} + * @returns {Promise} */ - async getAddress(): Promise { + async getAddress(): Promise { return 'wallet address' } /** * @param {string} message + * @returns {Promise} */ - async signMessage(message: string): Promise { + async signMessage(message: string): Promise { return 'signed message' } /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - async sendTransaction(transactionSigner: TransactionSignerInterface): Promise { + async sendTransaction(transactionSigner: TransactionSigner): Promise { return 'transaction hash' } diff --git a/packages/networks/boilerplate/src/browser/adapters/Example.ts b/packages/networks/boilerplate/src/browser/adapters/Example.ts index b7390b3..54fe875 100644 --- a/packages/networks/boilerplate/src/browser/adapters/Example.ts +++ b/packages/networks/boilerplate/src/browser/adapters/Example.ts @@ -1,5 +1,6 @@ +import type { Provider } from '../../services/Provider' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface } from '@multiplechain/types' +import type { ConnectConfig, UnknownConfig, WalletAdapterInterface } from '@multiplechain/types' declare global { interface Window { @@ -7,20 +8,20 @@ declare global { } } -const Example: WalletAdapterInterface = { +const Example: WalletAdapterInterface = { id: 'example', name: 'Example', icon: 'icon base64 string here', platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'wallet download link here', - createDeepLink(url: string, ops?: object): string { + createDeepLink(url: string, config?: UnknownConfig): string { return `https://example.com/dapp/${url}` }, isDetected: () => Boolean(window?.example), isConnected: async () => { return true // return true if connected }, - connect: async () => { + connect: async (provider?: Provider, config?: ConnectConfig) => { // connect wallet here return window.example } diff --git a/packages/networks/boilerplate/src/browser/index.ts b/packages/networks/boilerplate/src/browser/index.ts index 787e9bd..5bc8eb8 100644 --- a/packages/networks/boilerplate/src/browser/index.ts +++ b/packages/networks/boilerplate/src/browser/index.ts @@ -1,14 +1,17 @@ import { Wallet } from './Wallet.ts' import * as adapterList from './adapters/index.ts' +import type { Provider } from '../services/Provider.ts' import type { WalletAdapterListType, WalletAdapterInterface, RegisterWalletAdapterType } from '@multiplechain/types' -const adapters: WalletAdapterListType = {} +const adapters: WalletAdapterListType = {} -const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => { +const registerAdapter: RegisterWalletAdapterType = ( + adapter: WalletAdapterInterface +): void => { if (Object.values(adapters).find((a) => a.id === adapter.id) !== undefined) { throw new Error(`Adapter with id ${adapter.id} already exists`) } diff --git a/packages/networks/boilerplate/src/models/CoinTransaction.ts b/packages/networks/boilerplate/src/models/CoinTransaction.ts index 9857f2e..0f35f9b 100644 --- a/packages/networks/boilerplate/src/models/CoinTransaction.ts +++ b/packages/networks/boilerplate/src/models/CoinTransaction.ts @@ -1,39 +1,39 @@ import { Transaction } from './Transaction.ts' import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetDirectionEnum, CoinTransactionInterface } from '@multiplechain/types' +import type { AssetDirectionEnum, CoinTransactionInterface, TransferAmount, WalletAddress } from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { return 'example' } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return 'example' } /** - * @returns {Promise} Amount of coin that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { return 0 } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { return TransactionStatusEnum.PENDING } diff --git a/packages/networks/boilerplate/src/models/ContractTransaction.ts b/packages/networks/boilerplate/src/models/ContractTransaction.ts index ad57241..c4f15fc 100644 --- a/packages/networks/boilerplate/src/models/ContractTransaction.ts +++ b/packages/networks/boilerplate/src/models/ContractTransaction.ts @@ -1,11 +1,11 @@ import { Transaction } from './Transaction.ts' -import type { ContractTransactionInterface } from '@multiplechain/types' +import type { ContractAddress, ContractTransactionInterface } from '@multiplechain/types' export class ContractTransaction extends Transaction implements ContractTransactionInterface { /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { return 'example' } } diff --git a/packages/networks/boilerplate/src/models/NftTransaction.ts b/packages/networks/boilerplate/src/models/NftTransaction.ts index 09b8d80..ad5680c 100644 --- a/packages/networks/boilerplate/src/models/NftTransaction.ts +++ b/packages/networks/boilerplate/src/models/NftTransaction.ts @@ -1,40 +1,40 @@ import { ContractTransaction } from './ContractTransaction.ts' import { TransactionStatusEnum } from '@multiplechain/types' -import type { NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' +import type { NftTransactionInterface, AssetDirectionEnum, WalletAddress, NftId } from '@multiplechain/types' export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** - * @returns {Promise} Receiver wallet address + * @returns {Promise} Receiver wallet address */ - async getReceiver(): Promise { + async getReceiver(): Promise { return 'example' } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return 'example' } /** - * @returns {Promise} NFT ID + * @returns {Promise} NFT ID */ - async getNftId(): Promise { + async getNftId(): Promise { return 0 } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} nftId ID of the NFT that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {NftId} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - nftId: number | string + address: WalletAddress, + nftId: NftId ): Promise { return TransactionStatusEnum.PENDING } diff --git a/packages/networks/boilerplate/src/models/TokenTransaction.ts b/packages/networks/boilerplate/src/models/TokenTransaction.ts index b1ea29e..37cd45f 100644 --- a/packages/networks/boilerplate/src/models/TokenTransaction.ts +++ b/packages/networks/boilerplate/src/models/TokenTransaction.ts @@ -1,39 +1,39 @@ import { ContractTransaction } from './ContractTransaction.ts' import { TransactionStatusEnum } from '@multiplechain/types' -import type { AssetDirectionEnum, TokenTransactionInterface } from '@multiplechain/types' +import type { AssetDirectionEnum, TokenTransactionInterface, TransferAmount, WalletAddress } from '@multiplechain/types' export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { return 'example' } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return 'example' } /** - * @returns {Promise} Amount of tokens that will be transferred + * @returns {Promise} Amount of tokens that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { return 0 } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (token) - * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param {number} amount Amount of tokens that will be approved + * @param {WalletAddress} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of tokens that will be approved * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { return TransactionStatusEnum.PENDING } diff --git a/packages/networks/boilerplate/src/models/Transaction.ts b/packages/networks/boilerplate/src/models/Transaction.ts index bb7c04e..b5a238f 100644 --- a/packages/networks/boilerplate/src/models/Transaction.ts +++ b/packages/networks/boilerplate/src/models/Transaction.ts @@ -1,12 +1,29 @@ import { Provider } from '../services/Provider.ts' -import type { TransactionInterface } from '@multiplechain/types' import { TransactionStatusEnum } from '@multiplechain/types' +import { + TransactionTypeEnum, + type BlockConfirmationCount, + type BlockNumber, + type BlockTimestamp, + type TransactionFee, + type TransactionId, + type TransactionInterface, + type WalletAddress +} from '@multiplechain/types' -export class Transaction implements TransactionInterface { +// custom tx data for each blockchain +type TxData = {} + +export class Transaction implements TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId + + /** + * Transaction data + */ + data: TxData | null = null /** * Blockchain network provider @@ -14,18 +31,18 @@ export class Transaction implements TransactionInterface { provider: Provider /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider */ - constructor(id: string, provider?: Provider) { + constructor(id: TransactionId, provider?: Provider) { this.id = id this.provider = provider ?? Provider.instance } /** - * @returns {Promise} Transaction data + * @returns {Promise} Transaction data */ - async getData(): Promise { + async getData(): Promise { return {} } @@ -38,12 +55,19 @@ export class Transaction implements TransactionInterface { } /** - * @returns {string} Transaction ID + * @returns {TransactionId} Transaction ID */ - getId(): string { + getId(): TransactionId { return this.id } + /** + * @returns {Promise} Type of the transaction + */ + getType(): Promise { + return Promise.resolve(TransactionTypeEnum.GENERAL) + } + /** * @returns {string} Transaction URL */ @@ -52,37 +76,37 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSigner(): Promise { + async getSigner(): Promise { return 'example' } /** - * @returns {Promise} Transaction fee + * @returns {Promise} Transaction fee */ - async getFee(): Promise { + async getFee(): Promise { return 0 } /** - * @returns {Promise} Block number that transaction + * @returns {Promise} Block number that transaction */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { return 0 } /** - * @returns {Promise} Block timestamp that transaction + * @returns {Promise} Block timestamp that transaction */ - async getBlockTimestamp(): Promise { + async getBlockTimestamp(): Promise { return 0 } /** - * @returns {Promise} Confirmation count of the block + * @returns {Promise} Confirmation count of the block */ - async getBlockConfirmationCount(): Promise { + async getBlockConfirmationCount(): Promise { return 0 } diff --git a/packages/networks/boilerplate/src/services/Provider.ts b/packages/networks/boilerplate/src/services/Provider.ts index d92d40d..5b2834d 100644 --- a/packages/networks/boilerplate/src/services/Provider.ts +++ b/packages/networks/boilerplate/src/services/Provider.ts @@ -65,7 +65,8 @@ export class Provider implements ProviderInterface { /** * Update network configuration of the provider - * @param network - Network configuration of the provider + * @param {NetworkConfigInterface} network - Network configuration + * @returns {void} */ update(network: NetworkConfigInterface): void { this.network = network @@ -74,7 +75,7 @@ export class Provider implements ProviderInterface { /** * Get the current network configuration is testnet or not - * @returns boolean + * @returns {boolean} */ isTestnet(): boolean { return this.network?.testnet ?? false diff --git a/packages/networks/boilerplate/src/services/TransactionListener.ts b/packages/networks/boilerplate/src/services/TransactionListener.ts index 4581048..7b141f7 100644 --- a/packages/networks/boilerplate/src/services/TransactionListener.ts +++ b/packages/networks/boilerplate/src/services/TransactionListener.ts @@ -12,7 +12,8 @@ import type { NftTransactionListenerFilterInterface, TokenTransactionListenerFilterInterface, CoinTransactionListenerFilterInterface, - ContractTransactionListenerFilterInterface + ContractTransactionListenerFilterInterface, + TransactionId } from '@multiplechain/types' type TransactionListenerTriggerType = DynamicTransactionType< @@ -40,16 +41,6 @@ export class TransactionListener< */ type: T - /** - * Transaction listener callback - */ - callbacks: CallBackType[] = [] - - /** - * Transaction listener filter - */ - filter?: DynamicTransactionListenerFilterType | Record - /** * Provider */ @@ -59,11 +50,21 @@ export class TransactionListener< * Listener status */ status: boolean = false + + /** + * Transaction listener callback + */ + callbacks: CallBackType[] = [] /** * Triggered transactions */ - triggeredTransactions: string[] = [] + triggeredTransactions: TransactionId[] = [] + + /** + * Transaction listener filter + */ + filter?: DynamicTransactionListenerFilterType | Record /** * @param {T} type - Transaction type diff --git a/packages/networks/boilerplate/src/services/TransactionSigner.ts b/packages/networks/boilerplate/src/services/TransactionSigner.ts index c9d573a..96f0ef6 100644 --- a/packages/networks/boilerplate/src/services/TransactionSigner.ts +++ b/packages/networks/boilerplate/src/services/TransactionSigner.ts @@ -1,20 +1,16 @@ import { Provider } from '../services/Provider.ts' -import { Transaction } from '../models/Transaction.ts' -import { NftTransaction } from '../models/NftTransaction.ts' -import { CoinTransaction } from '../models/CoinTransaction.ts' -import { TokenTransaction } from '../models/TokenTransaction.ts' -import type { TransactionSignerInterface } from '@multiplechain/types' +import type { PrivateKey, TransactionId, TransactionSignerInterface } from '@multiplechain/types' -export class TransactionSigner implements TransactionSignerInterface { +export class TransactionSigner implements TransactionSignerInterface{ /** * Transaction data from the blockchain network */ - rawData: any + rawData: unknown /** * Signed transaction data */ - signedData: any + signedData: unknown /** * Blockchain network provider @@ -22,73 +18,44 @@ export class TransactionSigner implements TransactionSignerInterface { provider: Provider /** - * @param {any} rawData - Transaction data + * @param {unknown} rawData - Transaction data + * @param {Provider} provider - Blockchain network provider */ - constructor(rawData: any, provider?: Provider) { + constructor(rawData: unknown, provider?: Provider) { this.rawData = rawData this.provider = provider ?? Provider.instance } /** * Sign the transaction - * @param {string} privateKey - Transaction data - * @returns {Promise} Signed transaction data + * @param {PrivateKey} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - async sign(privateKey: string): Promise { + async sign(privateKey: PrivateKey): Promise { return await Promise.resolve(this) } /** * Send the transaction to the blockchain network - * @returns {Promise} + * @returns {Promise} */ - async send(): Promise { - return await Promise.resolve(new Transaction('example')) + async send(): Promise { + return await Promise.resolve('id') } /** * Get the raw transaction data - * @returns Transaction data + * @returns {unknown} */ - getRawData(): any { + getRawData(): unknown { return this.rawData } /** * Get the signed transaction data - * @returns Signed transaction data + * @returns {unknown} */ - getSignedData(): any { + getSignedData(): unknown { return this.signedData } } - -export class CoinTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new CoinTransaction((await super.send()).getId()) - } -} - -export class TokenTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new TokenTransaction((await super.send()).getId()) - } -} - -export class NftTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new NftTransaction((await super.send()).getId()) - } -} diff --git a/packages/networks/boilerplate/tests/assets.spec.ts b/packages/networks/boilerplate/tests/assets.spec.ts index 1902a05..ebafb17 100644 --- a/packages/networks/boilerplate/tests/assets.spec.ts +++ b/packages/networks/boilerplate/tests/assets.spec.ts @@ -5,7 +5,7 @@ import { Coin } from '../src/assets/Coin.ts' import { Token } from '../src/assets/Token.ts' import { math } from '@multiplechain/utils' import { Transaction } from '../src/models/Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' +import { TransactionStatusEnum, type TransactionId } from '@multiplechain/types' import { TransactionSigner } from '../src/services/TransactionSigner.ts' const coinBalanceTestAmount = Number(process.env.BLP_COIN_BALANCE_TEST_AMOUNT) @@ -50,8 +50,8 @@ const checkSigner = async (signer: TransactionSigner, privateKey?: string): Prom assert.isString(signer.getSignedData()) } -const checkTx = async (transaction: Transaction): Promise => { - expect(transaction).toBeInstanceOf(Transaction) +const checkTx = async (transactionId: TransactionId): Promise => { + const transaction = new Transaction(transactionId) const status = await transaction.wait(10000) expect(status).toBe(TransactionStatusEnum.CONFIRMED) } diff --git a/packages/networks/boilerplate/tests/services.spec.ts b/packages/networks/boilerplate/tests/services.spec.ts index aba2b6a..9ea4958 100644 --- a/packages/networks/boilerplate/tests/services.spec.ts +++ b/packages/networks/boilerplate/tests/services.spec.ts @@ -215,6 +215,6 @@ describe('Transaction Listener', () => { nftTransferId ) - void (await newSigner.sign(receiverPrivateKey)).send() + await (await newSigner.sign(receiverPrivateKey)).send() }) }) diff --git a/packages/networks/evm-chains/README.md b/packages/networks/evm-chains/README.md index 6ae2ea3..d746267 100644 --- a/packages/networks/evm-chains/README.md +++ b/packages/networks/evm-chains/README.md @@ -1 +1,7 @@ -Preparing \ No newline at end of file +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/networks/evm-chains/index.html b/packages/networks/evm-chains/index.example.html similarity index 99% rename from packages/networks/evm-chains/index.html rename to packages/networks/evm-chains/index.example.html index 64b37ec..4e3eb32 100644 --- a/packages/networks/evm-chains/index.html +++ b/packages/networks/evm-chains/index.example.html @@ -244,7 +244,7 @@ } const wallet = new EvmChains.browser.Wallet(adapter) - const adapterProvider = await wallet.connect(provider, { + const adapterProvider = await wallet.connect({ projectId: '113d9f5689edd84ff230c2a6d679c80c', metadata: { name: 'MultipleChain', diff --git a/packages/networks/evm-chains/package.json b/packages/networks/evm-chains/package.json index af19dc8..25493ca 100644 --- a/packages/networks/evm-chains/package.json +++ b/packages/networks/evm-chains/package.json @@ -27,6 +27,16 @@ "types": "./dist/browser/index.d.ts" } }, + "typesVersions": { + "*": { + "node": [ + "./dist/index.d.ts" + ], + "browser": [ + "./dist/browser/index.d.ts" + ] + } + }, "files": [ "dist", "README.md", @@ -65,8 +75,8 @@ "url": "https://github.com/MultipleChain/js/issues" }, "dependencies": { - "@multiplechain/types": "^0.1.56", - "@multiplechain/utils": "^0.1.20", + "@multiplechain/types": "^0.1.63", + "@multiplechain/utils": "^0.1.21", "@wagmi/chains": "^1.8.0", "@walletconnect/ethereum-provider": "^2.12.2", "@walletconnect/modal": "^2.6.2", diff --git a/packages/networks/evm-chains/pnpm-lock.yaml b/packages/networks/evm-chains/pnpm-lock.yaml index 6d97d08..9d29756 100644 --- a/packages/networks/evm-chains/pnpm-lock.yaml +++ b/packages/networks/evm-chains/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: '@multiplechain/types': - specifier: ^0.1.56 - version: 0.1.56 + specifier: ^0.1.63 + version: 0.1.63 '@multiplechain/utils': - specifier: ^0.1.20 - version: 0.1.20 + specifier: ^0.1.21 + version: 0.1.21 '@wagmi/chains': specifier: ^1.8.0 version: 1.8.0 @@ -239,12 +239,12 @@ packages: tslib: 2.4.0 dev: false - /@multiplechain/types@0.1.56: - resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==} + /@multiplechain/types@0.1.63: + resolution: {integrity: sha512-4C201vUsN6F1S/M7vT+GZS0wTdKGZXHqn4YmC6ouC8n0uxMCEFdI2sSCadyHKFKG5OBPo16X1oFCX9MVwPqMoA==} dev: false - /@multiplechain/utils@0.1.20: - resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==} + /@multiplechain/utils@0.1.21: + resolution: {integrity: sha512-4mRlnhvXcS+7Hb1By5s2eoCH02kqOSQzV8qj0DHqZK20FtxtduaoGCtcClrFgEjN/HAXnHgdDc1pxTpveIkvRQ==} dependencies: '@types/ws': 8.5.10 bignumber.js: 9.1.2 diff --git a/packages/networks/evm-chains/resources/erc1155.json b/packages/networks/evm-chains/resources/ERC1155.json similarity index 100% rename from packages/networks/evm-chains/resources/erc1155.json rename to packages/networks/evm-chains/resources/ERC1155.json diff --git a/packages/networks/evm-chains/resources/erc20.json b/packages/networks/evm-chains/resources/ERC20.json similarity index 100% rename from packages/networks/evm-chains/resources/erc20.json rename to packages/networks/evm-chains/resources/ERC20.json diff --git a/packages/networks/evm-chains/resources/erc721.json b/packages/networks/evm-chains/resources/ERC721.json similarity index 100% rename from packages/networks/evm-chains/resources/erc721.json rename to packages/networks/evm-chains/resources/ERC721.json diff --git a/packages/networks/evm-chains/src/assets/Coin.ts b/packages/networks/evm-chains/src/assets/Coin.ts index 878d554..043aa11 100644 --- a/packages/networks/evm-chains/src/assets/Coin.ts +++ b/packages/networks/evm-chains/src/assets/Coin.ts @@ -1,10 +1,15 @@ import { Provider } from '../services/Provider.ts' import { hexToNumber, numberToHex } from '@multiplechain/utils' -import { CoinTransactionSigner } from '../services/TransactionSigner.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' import type { TransactionData } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type CoinInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' -export class Coin implements CoinInterface { +export class Coin implements CoinInterface { /** * Blockchain network provider */ @@ -41,25 +46,25 @@ export class Coin implements CoinInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of COIN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const balance = await this.provider.ethers.getBalance(owner) return hexToNumber(balance.toString(), this.getDecimals()) } /** - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -88,6 +93,6 @@ export class Coin implements CoinInterface { txData.gasPrice = gasPrice txData.gasLimit = gasLimit - return new CoinTransactionSigner(txData) + return new TransactionSigner(txData) } } diff --git a/packages/networks/evm-chains/src/assets/Contract.ts b/packages/networks/evm-chains/src/assets/Contract.ts index a4b74f6..b65aabc 100644 --- a/packages/networks/evm-chains/src/assets/Contract.ts +++ b/packages/networks/evm-chains/src/assets/Contract.ts @@ -1,6 +1,6 @@ import { Provider } from '../services/Provider.ts' import type { Ethers } from '../services/Ethers.ts' -import type { ContractInterface } from '@multiplechain/types' +import type { ContractAddress, ContractInterface, WalletAddress } from '@multiplechain/types' import type { Contract as EthersContract, InterfaceAbi } from 'ethers' import type { TransactionData } from '../services/TransactionSigner.ts' @@ -8,7 +8,7 @@ export class Contract implements ContractInterface { /** * Contract address */ - address: string + address: ContractAddress /** * Contract ABI @@ -31,11 +31,11 @@ export class Contract implements ContractInterface { ethers: Ethers /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { this.ABI = ABI ?? [] this.address = address this.provider = provider ?? Provider.instance @@ -44,50 +44,54 @@ export class Contract implements ContractInterface { } /** - * @returns {string} Contract address + * @returns {ContractAddress} Contract address */ - getAddress(): string { + getAddress(): ContractAddress { return this.address } /** * @param {string} method Method name - * @param {any[]} args Method parameters - * @returns {Promise} Method result + * @param {unknown[]} args Method parameters + * @returns {Promise} Method result */ - async callMethod(method: string, ...args: any[]): Promise { + async callMethod(method: string, ...args: unknown[]): Promise { return this.ethersContract[method](...args) // eslint-disable-line } /** * @param {string} method Method name - * @param {any[]} args Sender wallet address + * @param {unknown[]} args Sender wallet address * @returns {Promise} Encoded method data */ - async getMethodData(method: string, ...args: any[]): Promise { + async getMethodData(method: string, ...args: unknown[]): Promise { return this.ethersContract.interface.encodeFunctionData(method, args) } /** * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters + * @param {WalletAddress} from Sender wallet address + * @param {unknown[]} args Method parameters * @returns {Promise} Gas limit */ - async getMethodEstimateGas(method: string, from: string, ...args: any[]): Promise { + async getMethodEstimateGas( + method: string, + from: WalletAddress, + ...args: unknown[] + ): Promise { return Number(await this.ethersContract[method].estimateGas(...args, { from })) // eslint-disable-line } /** * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters + * @param {WalletAddress} from Sender wallet address + * @param {unknown[]} args Method parameters * @returns {Promise} Transaction data */ async createTransactionData( method: string, - from: string, - ...args: any[] + from: WalletAddress, + ...args: unknown[] ): Promise { const [gasPrice, nonce, data, gasLimit] = await Promise.all([ this.provider.ethers.getGasPrice(), diff --git a/packages/networks/evm-chains/src/assets/NFT.ts b/packages/networks/evm-chains/src/assets/NFT.ts index 1e44b93..6e6227d 100644 --- a/packages/networks/evm-chains/src/assets/NFT.ts +++ b/packages/networks/evm-chains/src/assets/NFT.ts @@ -1,17 +1,23 @@ import { Contract } from './Contract.ts' import type { InterfaceAbi } from 'ethers' -import ERC721 from '../../resources/erc721.json' +import ERC721 from '../../resources/ERC721.json' import type { Provider } from '../services/Provider.ts' -import { NftTransactionSigner } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type NftInterface } from '@multiplechain/types' - -export class NFT extends Contract implements NftInterface { +import { TransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type ContractAddress, + type NftId, + type NftInterface, + type WalletAddress +} from '@multiplechain/types' + +export class NFT extends Contract implements NftInterface { /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { super(address, provider, ABI ?? ERC721) } @@ -19,76 +25,76 @@ export class NFT extends Contract implements NftInterface { * @returns {Promise} NFT name */ async getName(): Promise { - return await this.callMethod('name') + return (await this.callMethod('name')) as string } /** * @returns {Promise} NFT symbol */ async getSymbol(): Promise { - return await this.callMethod('symbol') + return (await this.callMethod('symbol')) as string } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of NFT */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return Number(await this.callMethod('balanceOf', owner)) } /** - * @param {number | string} nftId NFT ID - * @returns {Promise} Wallet address of the owner of the NFT + * @param {NftId} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - async getOwner(nftId: number | string): Promise { - return await this.callMethod('ownerOf', nftId) + async getOwner(nftId: NftId): Promise { + return (await this.callMethod('ownerOf', nftId)) as WalletAddress } /** - * @param {number | string} nftId NFT ID + * @param {NftId} nftId NFT ID * @returns {Promise} URI of the NFT */ - async getTokenURI(nftId: number | string): Promise { - return await this.callMethod('tokenURI', nftId) + async getTokenURI(nftId: NftId): Promise { + return (await this.callMethod('tokenURI', nftId)) as string } /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Wallet address of the approved spender + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - async getApproved(nftId: number | string): Promise { - const address = await this.callMethod('getApproved', nftId) + async getApproved(nftId: NftId): Promise { + const address = (await this.callMethod('getApproved', nftId)) as WalletAddress return address === '0x0000000000000000000000000000000000000000' ? null : address } /** - * @param {string} sender Sender address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID + * @param {WalletAddress} sender Sender address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - nftId: number | string - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { return await this.transferFrom(sender, sender, receiver, nftId) } /** - * @param {string} spender Spender address - * @param {string} owner Owner address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID + * @param {WalletAddress} spender Spender address + * @param {WalletAddress} owner Owner address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { // Check if tokens exist const balance = await this.getBalance(owner) @@ -110,23 +116,23 @@ export class NFT extends Contract implements NftInterface { } } - return new NftTransactionSigner( + return new TransactionSigner( await this.createTransactionData('transferFrom', spender, owner, receiver, nftId) ) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {NftId} nftId ID of the NFT that will be transferred * @returns {Promise} Transaction signer */ async approve( - owner: string, - spender: string, - nftId: number | string - ): Promise { + owner: WalletAddress, + spender: WalletAddress, + nftId: NftId + ): Promise { // Check if tokens exist const balance = await this.getBalance(owner) @@ -140,7 +146,7 @@ export class NFT extends Contract implements NftInterface { throw new Error(ErrorTypeEnum.UNAUTHORIZED_ADDRESS) } - return new NftTransactionSigner( + return new TransactionSigner( await this.createTransactionData('approve', owner, spender, nftId) ) } diff --git a/packages/networks/evm-chains/src/assets/Token.ts b/packages/networks/evm-chains/src/assets/Token.ts index cb74a17..f6a26d3 100644 --- a/packages/networks/evm-chains/src/assets/Token.ts +++ b/packages/networks/evm-chains/src/assets/Token.ts @@ -1,18 +1,24 @@ import { Contract } from './Contract.ts' import type { InterfaceAbi } from 'ethers' -import ERC20 from '../../resources/erc20.json' +import ERC20 from '../../resources/ERC20.json' import type { Provider } from '../services/Provider.ts' import { hexToNumber, numberToHex } from '@multiplechain/utils' -import { TokenTransactionSigner } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type TokenInterface } from '@multiplechain/types' - -export class Token extends Contract implements TokenInterface { +import { TransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type ContractAddress, + type TokenInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' + +export class Token extends Contract implements TokenInterface { /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { super(address, provider, ABI ?? ERC20) } @@ -20,14 +26,14 @@ export class Token extends Contract implements TokenInterface { * @returns {Promise} Token name */ async getName(): Promise { - return await this.callMethod('name') + return (await this.callMethod('name')) as string } /** * @returns {Promise} Token symbol */ async getSymbol(): Promise { - return await this.callMethod('symbol') + return (await this.callMethod('symbol')) as string } /** @@ -38,10 +44,10 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of TOKEN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const [decimals, balance] = await Promise.all([ this.getDecimals(), this.callMethod('balanceOf', owner) @@ -61,11 +67,11 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner + * @param {WalletAddress} owner Address of owner of the tokens that is being used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner * @returns {Promise} Amount of tokens that the spender is allowed to spend */ - async getAllowance(owner: string, spender: string): Promise { + async getAllowance(owner: WalletAddress, spender: WalletAddress): Promise { const [decimals, allowance] = await Promise.all([ this.getDecimals(), await this.callMethod('allowance', owner, spender) @@ -75,16 +81,16 @@ export class Token extends Contract implements TokenInterface { /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount <= 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -97,24 +103,24 @@ export class Token extends Contract implements TokenInterface { const hexAmount = numberToHex(amount, await this.getDecimals()) - return new TokenTransactionSigner( + return new TransactionSigner( await this.createTransactionData('transfer', sender, receiver, hexAmount) ) } /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of tokens that will be transferred * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - amount: number - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -137,19 +143,23 @@ export class Token extends Contract implements TokenInterface { const hexAmount = numberToHex(amount, await this.getDecimals()) - return new TokenTransactionSigner( + return new TransactionSigner( await this.createTransactionData('transferFrom', spender, owner, receiver, hexAmount) ) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number} amount Amount of the tokens that will be used + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {TransferAmount} amount Amount of the tokens that will be used * @returns {Promise} Transaction signer */ - async approve(owner: string, spender: string, amount: number): Promise { + async approve( + owner: WalletAddress, + spender: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -162,7 +172,7 @@ export class Token extends Contract implements TokenInterface { const hexAmount = numberToHex(amount, await this.getDecimals()) - return new TokenTransactionSigner( + return new TransactionSigner( await this.createTransactionData('approve', owner, spender, hexAmount) ) } diff --git a/packages/networks/evm-chains/src/browser/Wallet.ts b/packages/networks/evm-chains/src/browser/Wallet.ts index d87d0e4..ca74a78 100644 --- a/packages/networks/evm-chains/src/browser/Wallet.ts +++ b/packages/networks/evm-chains/src/browser/Wallet.ts @@ -1,16 +1,18 @@ import { - type WalletConnectOps, type WalletInterface, type WalletAdapterInterface, type WalletPlatformEnum, - type TransactionSignerInterface, ErrorTypeEnum, - type ProviderInterface + type ConnectConfig, + type UnknownConfig, + type TransactionId, + type SignedMessage, + type WalletAddress } from '@multiplechain/types' import { Provider } from '../services/Provider.ts' import type { EIP1193Provider } from './adapters/EIP6963.ts' import { toHex } from '@multiplechain/utils' -import type { Web3ModalAdapterInterface, Web3ModalOps } from './adapters/Web3Modal.ts' +import type { TransactionSigner } from '../services/TransactionSigner.ts' const rejectMap = (error: any, reject: (a: any) => any): any => { console.error('MultipleChain EVM Wallet Error:', error) @@ -69,27 +71,38 @@ const rejectMap = (error: any, reject: (a: any) => any): any => { return reject(error) } -export class Wallet implements Omit { - adapter: WalletAdapterInterface | Web3ModalAdapterInterface +type WalletAdapter = WalletAdapterInterface & { + provider?: + | EIP1193Provider + | { on: (eventName: string, callback: (...args: any[]) => void) => void } +} + +export interface RequestType { + method: string + params?: any[] | object +} + +export class Wallet implements WalletInterface { + adapter: WalletAdapter walletProvider: EIP1193Provider networkProvider: Provider /** - * @param {WalletAdapterInterface} adapter + * @param {WalletAdapter} adapter * @param {Provider} provider */ - constructor(adapter: WalletAdapterInterface | Web3ModalAdapterInterface, provider?: Provider) { + constructor(adapter: WalletAdapter, provider?: Provider) { this.adapter = adapter this.networkProvider = provider ?? Provider.instance } /** - * @param {{ method: string; params?: any[] | object }} payload + * @param {RequestType} payload * @returns {Promise} */ - async request(payload: { method: string; params?: any[] | object }): Promise { + async request(payload: RequestType): Promise { const res = await this.walletProvider.request(payload) if (res?.error !== undefined) { const error = res.error as { @@ -141,31 +154,27 @@ export class Wallet implements Omit { /** * @param {string} url - * @param {object} ops + * @param {UnknownConfig} config * @returns {string} */ - createDeepLink(url: string, ops?: object): string | null { + createDeepLink(url: string, config?: UnknownConfig): string | null { if (this.adapter.createDeepLink === undefined) { return null } - return this.adapter.createDeepLink(url, ops) + return this.adapter.createDeepLink(url, config) } /** - * @param {ProviderInterface} provider - * @param {Object | WalletConnectOps | Web3ModalOps} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - async connect( - provider?: ProviderInterface, - ops?: object | WalletConnectOps | Web3ModalOps - ): Promise { + async connect(config?: ConnectConfig): Promise { return await new Promise((resolve, reject) => { this.adapter - .connect(provider, ops) + .connect(this.networkProvider, config) .then(async (provider) => { - this.walletProvider = provider as EIP1193Provider + this.walletProvider = provider const chainId = await this.getChainId() @@ -212,16 +221,17 @@ export class Wallet implements Omit { } /** - * @returns {Promise} + * @returns {Promise} */ - async getAddress(): Promise { + async getAddress(): Promise { return (await this.walletProvider.request({ method: 'eth_accounts' }))[0] } /** * @param {string} message + * @returns {Promise} */ - async signMessage(message: string): Promise { + async signMessage(message: string): Promise { const address = await this.getAddress() return await new Promise((resolve, reject) => { this.walletProvider @@ -239,10 +249,10 @@ export class Wallet implements Omit { } /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - async sendTransaction(transactionSigner: TransactionSignerInterface): Promise { + async sendTransaction(transactionSigner: TransactionSigner): Promise { return await new Promise((resolve, reject) => { const data = transactionSigner.getRawData() data.gas = toHex(data.gasLimit as number) diff --git a/packages/networks/evm-chains/src/browser/adapters/BitgetWallet.ts b/packages/networks/evm-chains/src/browser/adapters/BitgetWallet.ts index b3eaa39..64443e6 100644 --- a/packages/networks/evm-chains/src/browser/adapters/BitgetWallet.ts +++ b/packages/networks/evm-chains/src/browser/adapters/BitgetWallet.ts @@ -2,9 +2,10 @@ import icons from './icons.ts' import { switcher } from './switcher.ts' import type { EIP1193Provider } from './EIP6963.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' -const BitgetWallet: WalletAdapterInterface = { +const BitgetWallet: WalletAdapterInterface = { id: 'bitgetwallet', name: 'BitgetWallet', icon: icons.bitgetWallet, @@ -18,7 +19,7 @@ const BitgetWallet: WalletAdapterInterface = { (await window?.bitkeep?.ethereum?.request({ method: 'eth_accounts' })).length ) }, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const bitget = window?.bitkeep?.ethereum try { diff --git a/packages/networks/evm-chains/src/browser/adapters/MetaMask.ts b/packages/networks/evm-chains/src/browser/adapters/MetaMask.ts index eb9cee5..061c8c4 100644 --- a/packages/networks/evm-chains/src/browser/adapters/MetaMask.ts +++ b/packages/networks/evm-chains/src/browser/adapters/MetaMask.ts @@ -3,9 +3,10 @@ import { switcher } from './switcher.ts' import type { WindowEthereum } from './types.ts' import type { EIP1193Provider } from './EIP6963.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' -const MetaMask: WalletAdapterInterface = { +const MetaMask: WalletAdapterInterface = { id: 'metamask', name: 'MetaMask', icon: icons.metaMask, @@ -23,7 +24,7 @@ const MetaMask: WalletAdapterInterface = { ).length ) }, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const metamaskProvider = window?.ethereum as unknown as WindowEthereum try { diff --git a/packages/networks/evm-chains/src/browser/adapters/OkxWallet.ts b/packages/networks/evm-chains/src/browser/adapters/OkxWallet.ts index 484b9e3..882fc8b 100644 --- a/packages/networks/evm-chains/src/browser/adapters/OkxWallet.ts +++ b/packages/networks/evm-chains/src/browser/adapters/OkxWallet.ts @@ -2,9 +2,10 @@ import icons from './icons.ts' import { switcher } from './switcher.ts' import type { EIP1193Provider } from './EIP6963.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' -const OkxWallet: WalletAdapterInterface = { +const OkxWallet: WalletAdapterInterface = { id: 'okxwallet', name: 'OkxWallet', icon: icons.okxWallet, @@ -16,7 +17,7 @@ const OkxWallet: WalletAdapterInterface = { isConnected: async () => { return Boolean((await window?.okxwallet?.request({ method: 'eth_accounts' })).length) }, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const okx = window?.okxwallet try { diff --git a/packages/networks/evm-chains/src/browser/adapters/Phantom.ts b/packages/networks/evm-chains/src/browser/adapters/Phantom.ts index 21d0d4c..80532d2 100644 --- a/packages/networks/evm-chains/src/browser/adapters/Phantom.ts +++ b/packages/networks/evm-chains/src/browser/adapters/Phantom.ts @@ -1,9 +1,10 @@ import icons from './icons.ts' import type { EIP1193Provider } from './EIP6963.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' import type { WalletAdapterInterface } from '@multiplechain/types' -const Phantom: WalletAdapterInterface = { +const Phantom: WalletAdapterInterface = { id: 'phantom', name: 'Phantom', icon: icons.phantom, diff --git a/packages/networks/evm-chains/src/browser/adapters/TrustWallet.ts b/packages/networks/evm-chains/src/browser/adapters/TrustWallet.ts index 00a7927..d632273 100644 --- a/packages/networks/evm-chains/src/browser/adapters/TrustWallet.ts +++ b/packages/networks/evm-chains/src/browser/adapters/TrustWallet.ts @@ -3,9 +3,10 @@ import { switcher } from './switcher.ts' import type { WindowEthereum } from './types.ts' import type { EIP1193Provider } from './EIP6963.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' -const TrustWallet: WalletAdapterInterface = { +const TrustWallet: WalletAdapterInterface = { id: 'trustwallet', name: 'TrustWallet', icon: icons.trustWallet, @@ -23,7 +24,7 @@ const TrustWallet: WalletAdapterInterface = { : window.trustwallet) as unknown as WindowEthereum return Boolean((await trustWalletProvider?.request({ method: 'eth_accounts' })).length) }, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { try { // eslint-disable-next-line diff --git a/packages/networks/evm-chains/src/browser/adapters/WalletConnect.ts b/packages/networks/evm-chains/src/browser/adapters/WalletConnect.ts index 8989a09..e7455db 100644 --- a/packages/networks/evm-chains/src/browser/adapters/WalletConnect.ts +++ b/packages/networks/evm-chains/src/browser/adapters/WalletConnect.ts @@ -1,20 +1,18 @@ import icons from './icons.ts' import type { EIP1193Provider } from './EIP6963.ts' -import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' import { EthereumProvider } from '@walletconnect/ethereum-provider' -import type { EvmNetworkConfigInterface } from '../../services/Provider.ts' -import type { - WalletConnectOps, - WalletAdapterInterface, - ProviderInterface -} from '@multiplechain/types' +import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' +import type { WalletConnectConfig, WalletAdapterInterface } from '@multiplechain/types' let isConnected = false +let walletProvider: EIP1193Provider | undefined -const WalletConnect: WalletAdapterInterface = { +const WalletConnect: WalletAdapterInterface = { id: 'walletconnect', name: 'WalletConnect', icon: icons.walletConnect, + provider: walletProvider, platforms: [WalletPlatformEnum.UNIVERSAL], isDetected: () => true, isConnected: () => isConnected, @@ -23,37 +21,35 @@ const WalletConnect: WalletAdapterInterface = { indexedDB.deleteDatabase('WALLET_CONNECT_V2_INDEXED_DB') }, connect: async ( - provider?: ProviderInterface, - _ops?: WalletConnectOps | object + provider?: Provider, + config?: WalletConnectConfig ): Promise => { - const ops = _ops as WalletConnectOps - if (provider === undefined) { throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) } - if (ops === undefined) { - throw new Error(ErrorTypeEnum.OPS_IS_REQUIRED) + if (config === undefined) { + throw new Error(ErrorTypeEnum.CONFIG_IS_REQUIRED) } - if (ops.projectId === undefined) { + if (config.projectId === undefined) { throw new Error(ErrorTypeEnum.PROJECT_ID_IS_REQUIRED) } const rpcIdMapping = {} - const network = provider.network as EvmNetworkConfigInterface + const network = provider.network // @ts-expect-error allow number index rpcIdMapping[network.id] = network.rpcUrl const connector = await EthereumProvider.init({ - projectId: ops.projectId, + projectId: config.projectId, relayUrl: 'wss://relay.walletconnect.com', optionalChains: [network.id], rpcMap: rpcIdMapping, chains: [network.id], showQrModal: true, qrModalOptions: { - themeMode: ops.themeMode, + themeMode: config.themeMode, themeVariables: { '--wcm-z-index': '999999999999' }, @@ -84,7 +80,7 @@ const WalletConnect: WalletAdapterInterface = { .enable() .then(() => { isConnected = true - resolve(connector as EIP1193Provider) + resolve((walletProvider = connector as EIP1193Provider)) }) .catch((error) => { reject(error) diff --git a/packages/networks/evm-chains/src/browser/adapters/Web3Modal.ts b/packages/networks/evm-chains/src/browser/adapters/Web3Modal.ts index f0ac810..1a3f804 100644 --- a/packages/networks/evm-chains/src/browser/adapters/Web3Modal.ts +++ b/packages/networks/evm-chains/src/browser/adapters/Web3Modal.ts @@ -1,14 +1,15 @@ import icons from './icons.ts' +import { networks } from '../../index.ts' import type { EIP1193Provider } from './EIP6963.ts' +import type { Provider } from '../../services/Provider.ts' import type { Chain } from '@web3modal/scaffold-utils/ethers' import type { CustomWallet, Metadata } from '@web3modal/core' import { createWeb3Modal, defaultConfig } from '@web3modal/ethers' import type { Web3Modal as Web3ModalType } from '@web3modal/ethers' +import type { WalletAdapterInterface } from '@multiplechain/types' import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' -import { networks, type EvmNetworkConfigInterface } from '../../index.ts' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' -export interface Web3ModalOps { +export interface Web3ModalConfig { projectId: string themeMode?: 'dark' | 'light' enableEIP6963?: boolean @@ -20,11 +21,8 @@ export interface Web3ModalOps { customWallets?: CustomWallet[] } -export interface Web3ModalAdapterInterface extends Omit { - connect: (provider?: ProviderInterface, ops?: Web3ModalOps | object) => Promise -} - let modal: Web3ModalType +let walletProvider: EIP1193Provider | undefined const chains: Chain[] = networks .getAll() @@ -44,24 +42,24 @@ let clickedAnyWallet = false let connectRejectMethod: (reason?: any) => void let connectResolveMethod: (value: EIP1193Provider | PromiseLike) => void -const web3Modal = (ops: Web3ModalOps): Web3ModalType => { +const web3Modal = (config: Web3ModalConfig): Web3ModalType => { if (modal !== undefined) { return modal } const ethersConfig = defaultConfig({ - metadata: ops.metadata, - enableEIP6963: ops.enableEIP6963, - enableInjected: ops.enableInjected, - enableCoinbase: ops.enableCoinbase + metadata: config.metadata, + enableEIP6963: config.enableEIP6963, + enableInjected: config.enableInjected, + enableCoinbase: config.enableCoinbase }) modal = createWeb3Modal({ chains, ethersConfig, - projectId: ops.projectId, - themeMode: ops.themeMode, - customWallets: ops.customWallets, + projectId: config.projectId, + themeMode: config.themeMode, + customWallets: config.customWallets, allowUnsupportedChain: true, themeVariables: { '--w3m-z-index': 999999999999 @@ -72,6 +70,7 @@ const web3Modal = (ops: Web3ModalOps): Web3ModalType => { if (event.data.event === 'SELECT_WALLET') { clickedAnyWallet = true } + if (event.data.event === 'MODAL_CLOSE') { if (clickedAnyWallet) { clickedAnyWallet = false @@ -85,21 +84,24 @@ const web3Modal = (ops: Web3ModalOps): Web3ModalType => { if (provider === undefined) { return } + if (currentNetwork.chainId !== chainId) { await modal.switchNetwork(currentNetwork.chainId).catch(() => { connectRejectMethod(new Error(ErrorTypeEnum.WALLET_CONNECT_REJECTED)) }) } - connectResolveMethod(provider as EIP1193Provider) + + connectResolveMethod((walletProvider = provider as EIP1193Provider)) }) return modal } -const Web3Modal: Web3ModalAdapterInterface = { +const Web3Modal: WalletAdapterInterface = { id: 'web3modal', name: 'Web3Modal', icon: icons.web3modal, + provider: walletProvider, platforms: [WalletPlatformEnum.UNIVERSAL], isDetected: () => true, isConnected: () => { @@ -127,28 +129,28 @@ const Web3Modal: Web3ModalAdapterInterface = { } }, connect: async ( - provider?: ProviderInterface, - _ops?: Web3ModalOps | object + provider?: Provider, + _config?: Web3ModalConfig | object ): Promise => { - const ops = _ops as Web3ModalOps + const config = _config as Web3ModalConfig if (provider === undefined) { throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) } - if (ops === undefined) { - throw new Error(ErrorTypeEnum.OPS_IS_REQUIRED) + if (config === undefined) { + throw new Error(ErrorTypeEnum.CONFIG_IS_REQUIRED) } - if (ops.projectId === undefined) { + if (config.projectId === undefined) { throw new Error(ErrorTypeEnum.PROJECT_ID_IS_REQUIRED) } - if (ops.metadata === undefined) { + if (config.metadata === undefined) { throw new Error(ErrorTypeEnum.METADATA_IS_REQUIRED) } - const network = provider.network as EvmNetworkConfigInterface + const network = provider.network currentNetwork = { chainId: network.id, @@ -160,7 +162,7 @@ const Web3Modal: Web3ModalAdapterInterface = { return await new Promise((resolve, reject) => { try { - const modal = web3Modal(ops) + const modal = web3Modal(config) connectRejectMethod = async (reason) => { await modal.disconnect() reject(reason) diff --git a/packages/networks/evm-chains/src/browser/adapters/XdefiWallet.ts b/packages/networks/evm-chains/src/browser/adapters/XdefiWallet.ts index 8aabf4d..9547fb7 100644 --- a/packages/networks/evm-chains/src/browser/adapters/XdefiWallet.ts +++ b/packages/networks/evm-chains/src/browser/adapters/XdefiWallet.ts @@ -1,10 +1,11 @@ import icons from './icons.ts' import { switcher } from './switcher.ts' import type { EIP1193Provider } from './EIP6963.ts' +import type { Provider } from '../../services/Provider.ts' import { WalletPlatformEnum } from '@multiplechain/types' -import type { WalletAdapterInterface, ProviderInterface } from '@multiplechain/types' +import type { WalletAdapterInterface } from '@multiplechain/types' -const XdefiWallet: WalletAdapterInterface = { +const XdefiWallet: WalletAdapterInterface = { id: 'xdefiwallet', name: 'XdefiWallet', icon: icons.xdefiWallet, @@ -15,7 +16,7 @@ const XdefiWallet: WalletAdapterInterface = { isConnected: async () => { return Boolean((await window?.xfi?.ethereum?.request({ method: 'eth_accounts' })).length) }, - connect: async (provider?: ProviderInterface): Promise => { + connect: async (provider?: Provider): Promise => { return await new Promise((resolve, reject) => { const xfi = window?.xfi?.ethereum try { diff --git a/packages/networks/evm-chains/src/browser/adapters/switcher.ts b/packages/networks/evm-chains/src/browser/adapters/switcher.ts index 10861ed..d034043 100644 --- a/packages/networks/evm-chains/src/browser/adapters/switcher.ts +++ b/packages/networks/evm-chains/src/browser/adapters/switcher.ts @@ -1,11 +1,13 @@ +import type { RequestType } from '../Wallet.ts' import networks from '../../services/Networks.ts' -import { ErrorTypeEnum, type ProviderInterface } from '@multiplechain/types' -import type { EvmNetworkConfigInterface } from '../../services/Provider.ts' +import type { EIP1193Provider } from './EIP6963.ts' +import { ErrorTypeEnum } from '@multiplechain/types' +import type { EvmNetworkConfigInterface, Provider } from '../../services/Provider.ts' -export const switcher = async (wallet: any, provider?: ProviderInterface): Promise => { - const network = provider?.network as EvmNetworkConfigInterface +export const switcher = async (wallet: EIP1193Provider, provider?: Provider): Promise => { + const network = provider?.network - const request = async (params: any): Promise => { + const request = async (params: RequestType): Promise => { const res = await wallet.request(params) if (res?.error !== undefined) { const error = res.error as { @@ -50,12 +52,7 @@ export const switcher = async (wallet: any, provider?: ProviderInterface): Promi const changeNetwork = async (_network: EvmNetworkConfigInterface): Promise => { return await new Promise((resolve, reject) => { - const network = networks.findById(_network.id) - if (network === undefined) { - resolve(true) - return - } - const chainId = `0x${network.id.toString(16)}` + const chainId = `0x${_network.id.toString(16)}` request({ method: 'wallet_switchEthereumChain', params: [{ chainId }] @@ -68,6 +65,11 @@ export const switcher = async (wallet: any, provider?: ProviderInterface): Promi error.code === 4902 || String(error.message).includes('wallet_addEthereumChain') ) { + const network = networks.findById(_network.id) + if (network === undefined) { + resolve(true) + return + } addNetwork(network) .then(() => { resolve(true) diff --git a/packages/networks/evm-chains/src/browser/index.ts b/packages/networks/evm-chains/src/browser/index.ts index 3faf3a1..703b083 100644 --- a/packages/networks/evm-chains/src/browser/index.ts +++ b/packages/networks/evm-chains/src/browser/index.ts @@ -8,6 +8,7 @@ import type { WalletAdapterListType, RegisterWalletAdapterType } from '@multiplechain/types' +import type { Provider } from '../services/Provider.ts' const EIP6963AdapterUUIDIndex: Record = { 'app.phantom': 'phantom', @@ -18,9 +19,11 @@ const EIP6963AdapterUUIDIndex: Record = { 'io.xdefi': 'xdefiwallet' } -const adapters: WalletAdapterListType = {} +const adapters: WalletAdapterListType = {} -const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => { +const registerAdapter: RegisterWalletAdapterType = ( + adapter: WalletAdapterInterface +): void => { if (EIP6963AdapterUUIDIndex[adapter.id] !== undefined) { console.warn( `Adapter is not registered, because it is already registered default: ${adapter.id}` @@ -35,7 +38,9 @@ const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterf adapters[adapter.id] = adapter } -const fromEIP6963ProviderDetail = (detail: EIP6963ProviderDetail): WalletAdapterInterface => { +const fromEIP6963ProviderDetail = ( + detail: EIP6963ProviderDetail +): WalletAdapterInterface => { return { name: detail.info.name, icon: detail.info.icon, @@ -65,7 +70,9 @@ const fromEIP6963ProviderDetail = (detail: EIP6963ProviderDetail): WalletAdapter } } -const toEIP6963ProviderDetail = (adapter: WalletAdapterInterface): EIP6963ProviderDetail => { +const toEIP6963ProviderDetail = ( + adapter: WalletAdapterInterface +): EIP6963ProviderDetail => { if (adapter.provider === undefined) { throw new Error('Cannot convert adapter without provider to EIP6963ProviderDetail') } @@ -76,7 +83,7 @@ const toEIP6963ProviderDetail = (adapter: WalletAdapterInterface): EIP6963Provid name: adapter.name, icon: adapter.icon }, - provider: adapter.provider + provider: adapter.provider as any } } diff --git a/packages/networks/evm-chains/src/models/CoinTransaction.ts b/packages/networks/evm-chains/src/models/CoinTransaction.ts index c1c5bca..535610f 100644 --- a/packages/networks/evm-chains/src/models/CoinTransaction.ts +++ b/packages/networks/evm-chains/src/models/CoinTransaction.ts @@ -1,28 +1,28 @@ import { Transaction } from './Transaction.ts' import { hexToNumber } from '@multiplechain/utils' -import { TransactionStatusEnum } from '@multiplechain/types' -import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { WalletAddress, CoinTransactionInterface, TransferAmount } from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() return data?.response.to ?? '' } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return await this.getSigner() } /** - * @returns {Promise} Amount of coin that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const data = await this.getData() const { decimals } = this.provider.network.nativeCurrency return hexToNumber((data?.response.value ?? 0).toString(), decimals) @@ -30,14 +30,14 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/evm-chains/src/models/ContractTransaction.ts b/packages/networks/evm-chains/src/models/ContractTransaction.ts index 6878378..cd41da3 100644 --- a/packages/networks/evm-chains/src/models/ContractTransaction.ts +++ b/packages/networks/evm-chains/src/models/ContractTransaction.ts @@ -1,5 +1,9 @@ import { Transaction } from './Transaction.ts' -import type { ContractTransactionInterface } from '@multiplechain/types' +import type { + ContractAddress, + ContractTransactionInterface, + TransactionId +} from '@multiplechain/types' import { Interface, type InterfaceAbi, @@ -15,19 +19,19 @@ export class ContractTransaction extends Transaction implements ContractTransact ABI: InterfaceAbi /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(id: TransactionId, provider?: Provider, ABI?: InterfaceAbi) { super(id, provider) this.ABI = ABI ?? [] } /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { const data = await this.getData() return data?.response.to ?? '' } diff --git a/packages/networks/evm-chains/src/models/NftTransaction.ts b/packages/networks/evm-chains/src/models/NftTransaction.ts index de09fc7..d42cd05 100644 --- a/packages/networks/evm-chains/src/models/NftTransaction.ts +++ b/packages/networks/evm-chains/src/models/NftTransaction.ts @@ -1,24 +1,29 @@ import type { InterfaceAbi } from 'ethers' -import ERC721 from '../../resources/erc721.json' +import ERC721 from '../../resources/ERC721.json' import type { Provider } from '../services/Provider.ts' import { ContractTransaction } from './ContractTransaction.ts' -import type { NftTransactionInterface } from '@multiplechain/types' +import type { + NftId, + NftTransactionInterface, + TransactionId, + WalletAddress +} from '@multiplechain/types' import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(id: TransactionId, provider?: Provider, ABI?: InterfaceAbi) { super(id, provider, ABI ?? (ERC721 as InterfaceAbi)) } /** - * @returns {Promise} Receiver wallet address + * @returns {Promise} Receiver wallet address */ - async getReceiver(): Promise { + async getReceiver(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -33,9 +38,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} Sender wallet address + * @returns {Promise} Sender wallet address */ - async getSender(): Promise { + async getSender(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -50,23 +55,23 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} NFT ID + * @returns {Promise} NFT ID */ - async getNftId(): Promise { + async getNftId(): Promise { return Number((await this.decodeData())?.args[2] ?? 0) } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {NftId} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - nftId: number | string + address: WalletAddress, + nftId: NftId ): Promise { const status = await this.getStatus() diff --git a/packages/networks/evm-chains/src/models/TokenTransaction.ts b/packages/networks/evm-chains/src/models/TokenTransaction.ts index da166cf..e2e5976 100644 --- a/packages/networks/evm-chains/src/models/TokenTransaction.ts +++ b/packages/networks/evm-chains/src/models/TokenTransaction.ts @@ -1,26 +1,31 @@ import { Token } from '../assets/Token.ts' import type { InterfaceAbi } from 'ethers' -import ERC20 from '../../resources/erc20.json' +import ERC20 from '../../resources/ERC20.json' import { hexToNumber } from '@multiplechain/utils' import type { Provider } from '../services/Provider.ts' -import { TransactionStatusEnum } from '@multiplechain/types' import { ContractTransaction } from './ContractTransaction.ts' -import { AssetDirectionEnum, type TokenTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { + TransactionId, + TokenTransactionInterface, + WalletAddress, + TransferAmount +} from '@multiplechain/types' export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(id: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(id: TransactionId, provider?: Provider, ABI?: InterfaceAbi) { super(id, provider, ABI ?? (ERC20 as InterfaceAbi)) } /** - * @return {Promise} Receiver wallet address + * @return {Promise} Receiver wallet address */ - async getReceiver(): Promise { + async getReceiver(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -35,9 +40,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -52,9 +57,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Amount of tokens that will be transferred + * @returns {Promise} Amount of tokens that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const token = new Token(await this.getAddress()) const decoded = await this.decodeData() if (decoded === null) { @@ -70,14 +75,14 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa /** * @param {AssetDirectionEnum} direction - Direction of the transaction (token) - * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param {number} amount Amount of tokens that will be approved + * @param {WalletAddress} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of tokens that will be approved * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/evm-chains/src/models/Transaction.ts b/packages/networks/evm-chains/src/models/Transaction.ts index 10223e9..3343916 100644 --- a/packages/networks/evm-chains/src/models/Transaction.ts +++ b/packages/networks/evm-chains/src/models/Transaction.ts @@ -1,20 +1,51 @@ import { Provider } from '../services/Provider.ts' import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types' -import type { TransactionInterface } from '@multiplechain/types' +import { + TransactionTypeEnum, + type BlockConfirmationCount, + type BlockNumber, + type BlockTimestamp, + type TransactionFee, + type TransactionId, + type TransactionInterface, + type WalletAddress +} from '@multiplechain/types' import type { TransactionReceipt, TransactionResponse } from 'ethers' import type { Ethers } from '../services/Ethers.ts' import { hexToNumber } from '@multiplechain/utils' +import { NFT } from '../assets/NFT.ts' + +const selectors = { + // ERC20 + [TransactionTypeEnum.TOKEN]: [ + '0xa9059cbb', // transfer(address,uint256) + '0x095ea7b3', // approve(address,uint256) + '0x23b872dd' // transferFrom(address,address,uint256) + ], + // ERC721, ERC1155 + [TransactionTypeEnum.NFT]: [ + // ERC721 + '0x23b872dd', // transferFrom(address,address,uint256) + '0x095ea7b3', // approve(address,uint256) + '0x42842e0e', // safeTransferFrom(address,address,uint256) + '0xb88d4fde', // safeTransferFrom(address,address,uint256,bytes) + // ERC1155 + '0xf242432a', // safeTransferFrom(address,address,uint256,uint256,bytes) + '0x2eb2c2d6', // safeBatchTransferFrom(address,address,uint256[],uint256[],bytes) + '0x29535c7e' // setApprovalForAll(address,bool) + ] +} interface TransactionData { response: TransactionResponse receipt: TransactionReceipt | null } -export class Transaction implements TransactionInterface { +export class Transaction implements TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId /** * Blockchain network provider @@ -32,10 +63,10 @@ export class Transaction implements TransactionInterface { data: TransactionData /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider */ - constructor(id: string, provider?: Provider) { + constructor(id: TransactionId, provider?: Provider) { this.id = id this.provider = provider ?? Provider.instance this.ethers = this.provider.ethers @@ -89,12 +120,45 @@ export class Transaction implements TransactionInterface { } /** - * @returns {string} Transaction id from the blockchain network + * @returns {TransactionId} Transaction id from the blockchain network */ - getId(): string { + getId(): TransactionId { return this.id } + /** + * @returns {Promise} Type of the transaction + */ + async getType(): Promise { + const txData = await this.getData() + + if (txData === null) { + return TransactionTypeEnum.GENERAL + } + + const contractBytecode = await this.provider.ethers.getByteCode(txData.response.to ?? '') + + if (contractBytecode === '0x' || txData.response.data === '0x') { + return TransactionTypeEnum.COIN + } + + const type = Object.entries(selectors).find(([_key, values]) => { + return values.includes(txData.response.data.slice(0, 10)) + }) + + if (type !== undefined) { + const tryNft = new NFT(txData.response.to ?? '') + try { + await tryNft.getApproved(1) + return TransactionTypeEnum.NFT + } catch { + return TransactionTypeEnum.TOKEN + } + } + + return TransactionTypeEnum.CONTRACT + } + /** * @returns {string} URL of the transaction on the blockchain explorer */ @@ -106,17 +170,17 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Signer wallet address of the transaction + * @returns {Promise} Signer wallet address of the transaction */ - async getSigner(): Promise { + async getSigner(): Promise { const data = await this.getData() return data?.response.from ?? '' } /** - * @returns {Promise} Fee of the transaction + * @returns {Promise} Fee of the transaction */ - async getFee(): Promise { + async getFee(): Promise { const data = await this.getData() if (data?.response?.gasPrice === undefined || data?.receipt?.gasUsed === undefined) { return 0 @@ -128,26 +192,26 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Block number that transaction + * @returns {Promise} Block number that transaction */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { const data = await this.getData() return data?.response.blockNumber ?? 0 } /** - * @returns {Promise} Timestamp of the block that transaction + * @returns {Promise} Timestamp of the block that transaction */ - async getBlockTimestamp(): Promise { + async getBlockTimestamp(): Promise { const blockNumber = await this.getBlockNumber() const block = await this.ethers.getBlock(blockNumber) return block?.timestamp ?? 0 } /** - * @returns {Promise} Confirmation count of the block that transaction + * @returns {Promise} Confirmation count of the block that transaction */ - async getBlockConfirmationCount(): Promise { + async getBlockConfirmationCount(): Promise { const blockNumber = await this.getBlockNumber() const blockCount = await this.ethers.getBlockNumber() const confirmations = blockCount - blockNumber diff --git a/packages/networks/evm-chains/src/services/Ethers.ts b/packages/networks/evm-chains/src/services/Ethers.ts index 55070c0..e84c88d 100644 --- a/packages/networks/evm-chains/src/services/Ethers.ts +++ b/packages/networks/evm-chains/src/services/Ethers.ts @@ -7,7 +7,14 @@ import type { TransactionReceipt, TransactionResponse } from 'ethers' -import { ErrorTypeEnum } from '@multiplechain/types' +import { + ErrorTypeEnum, + type BlockNumber, + type ContractAddress, + type PrivateKey, + type TransactionId, + type WalletAddress +} from '@multiplechain/types' import { sleep, checkWebSocket } from '@multiplechain/utils' import type { EvmNetworkConfigInterface } from './Provider.ts' import type { TransactionData } from '../services/TransactionSigner.ts' @@ -54,7 +61,7 @@ export class Ethers { } /** - * @returns {WebSocketProvider} + * @returns {Promise} */ public async connectWebSocket(): Promise { return await new Promise((resolve, reject) => { @@ -78,8 +85,8 @@ export class Ethers { /** * @param {String} address * @param {InterfaceAbi} abi - * @param {JsonRpcSigner} signer - * @returns {Promise} + * @param {JsonRpcSigner | JsonRpcProvider} signer + * @returns {Contract} */ public contract( address: string, @@ -90,19 +97,19 @@ export class Ethers { } /** - * @param {string} privateKey private key of the wallet + * @param {PrivateKey} privateKey private key of the wallet * @param {JsonRpcProvider} provider provider of the blockchain network * @returns {Wallet} */ - public wallet(privateKey: string, provider?: JsonRpcProvider): Wallet { + public wallet(privateKey: PrivateKey, provider?: JsonRpcProvider): Wallet { return new Wallet(privateKey, provider ?? this.jsonRpc) } /** * @param {InterfaceAbi} abi - * @param {String} bytecode - * @param {JsonRpcSigner} signer - * @returns {Promise} + * @param {string} bytecode + * @param {JsonRpcSigner | JsonRpcProvider} signer + * @returns {ContractFactory} */ public contractFactory( abi: InterfaceAbi, @@ -113,10 +120,10 @@ export class Ethers { } /** - * @param {string} address + * @param {ContractAddress} address * @returns {Promise} */ - async getByteCode(address: string): Promise { + async getByteCode(address: ContractAddress): Promise { try { return await this.jsonRpc.getCode(address) } catch (error) { @@ -131,7 +138,7 @@ export class Ethers { } /** - * @param {Object} data + * @param {TransactionData} data * @returns {Promise} */ public async getEstimateGas(data: TransactionData): Promise { @@ -146,10 +153,10 @@ export class Ethers { } /** - * @param {String} address + * @param {WalletAddress} address * @returns {Promise} */ - public async getNonce(address: string): Promise { + public async getNonce(address: WalletAddress): Promise { return await this.jsonRpcProvider.getTransactionCount(address) } @@ -163,34 +170,37 @@ export class Ethers { } /** - * @returns {Promise} + * @returns {Promise} */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { return await this.jsonRpcProvider.getBlockNumber() } /** - * @param {String} id - * @returns {Promise} + * @param {TransactionId} id + * @returns {Promise} */ - async getTransaction(id: string): Promise { + async getTransaction(id: TransactionId): Promise { return await this.jsonRpcProvider.getTransaction(id) } /** - * @param {String} id - * @returns {Promise} + * @param {TransactionId} id + * @returns {Promise} */ - async getTransactionReceipt(id: string): Promise { + async getTransactionReceipt(id: TransactionId): Promise { return await this.jsonRpcProvider.getTransactionReceipt(id) } /** - * @param {string} address + * @param {WalletAddress} address * @param {number} limit how many block to go back * @returns {Promise} */ - async getLastTransactions(address: string, limit: number = 0): Promise { + async getLastTransactions( + address: WalletAddress, + limit: number = 0 + ): Promise { const txPromises = [] const blockPromises = [] const transactions: TransactionResponse[] = [] @@ -227,12 +237,12 @@ export class Ethers { } /** - * @param {String} address + * @param {WalletAddress} address * @param {number} limit * @returns {Promise} */ async getLastTransaction( - address: string, + address: WalletAddress, limit: number = 0 ): Promise { const block = await this.getBlock('pending') @@ -257,10 +267,10 @@ export class Ethers { } /** - * @param {String} address + * @param {WalletAddress} address * @returns {Promise} */ - async getBalance(address: string): Promise { + async getBalance(address: WalletAddress): Promise { return await this.jsonRpcProvider.getBalance(address) } } diff --git a/packages/networks/evm-chains/src/services/Provider.ts b/packages/networks/evm-chains/src/services/Provider.ts index 9431d83..2ae1bd3 100644 --- a/packages/networks/evm-chains/src/services/Provider.ts +++ b/packages/networks/evm-chains/src/services/Provider.ts @@ -22,7 +22,7 @@ export interface EvmNetworkConfigInterface extends NetworkConfigInterface { } } -export class Provider implements Omit { +export class Provider implements ProviderInterface { /** * Network configuration of the provider */ @@ -42,9 +42,7 @@ export class Provider implements Omit { * @param network - Network configuration of the provider */ constructor(network: EvmNetworkConfigInterface) { - this.network = network - Provider._instance = this - this.ethers = new Ethers(network) + this.update(network) } /** diff --git a/packages/networks/evm-chains/src/services/TransactionListener.ts b/packages/networks/evm-chains/src/services/TransactionListener.ts index ec9561e..91342f8 100644 --- a/packages/networks/evm-chains/src/services/TransactionListener.ts +++ b/packages/networks/evm-chains/src/services/TransactionListener.ts @@ -2,7 +2,8 @@ import type { TransactionTypeEnum, DynamicTransactionType, TransactionListenerInterface, - DynamicTransactionListenerFilterType + DynamicTransactionListenerFilterType, + TransactionId } from '@multiplechain/types' import { Provider } from './Provider.ts' @@ -48,16 +49,6 @@ export class TransactionListener< */ type: T - /** - * Transaction listener callback - */ - callbacks: CallBackType[] = [] - - /** - * Transaction listener filter - */ - filter?: DynamicTransactionListenerFilterType | Record - /** * Provider */ @@ -83,6 +74,11 @@ export class TransactionListener< */ webSocket: WebSocketProvider + /** + * Transaction listener callback + */ + callbacks: CallBackType[] = [] + /** * Dynamic stop method */ @@ -91,7 +87,12 @@ export class TransactionListener< /** * Triggered transactions */ - triggeredTransactions: string[] = [] + triggeredTransactions: TransactionId[] = [] + + /** + * Transaction listener filter + */ + filter?: DynamicTransactionListenerFilterType | Record /** * @param {T} type - Transaction type diff --git a/packages/networks/evm-chains/src/services/TransactionSigner.ts b/packages/networks/evm-chains/src/services/TransactionSigner.ts index 00bd28f..c450881 100644 --- a/packages/networks/evm-chains/src/services/TransactionSigner.ts +++ b/packages/networks/evm-chains/src/services/TransactionSigner.ts @@ -1,18 +1,18 @@ import type { EthersError } from './Ethers.ts' import { Provider } from '../services/Provider.ts' -import { Transaction } from '../models/Transaction.ts' -import { NftTransaction } from '../models/NftTransaction.ts' -import { CoinTransaction } from '../models/CoinTransaction.ts' -import { TokenTransaction } from '../models/TokenTransaction.ts' -import { ContractTransaction } from '../models/ContractTransaction.ts' import type { TransactionRequest, Wallet, BigNumberish } from 'ethers' -import { ErrorTypeEnum, type TransactionSignerInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type PrivateKey, + type TransactionId, + type TransactionSignerInterface +} from '@multiplechain/types' export interface TransactionData extends TransactionRequest { gas?: BigNumberish } -export class TransactionSigner implements TransactionSignerInterface { +export class TransactionSigner implements TransactionSignerInterface { /** * Transaction data from the blockchain network */ @@ -44,10 +44,10 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Sign the transaction - * @param {string} privateKey - Transaction data - * @returns {Promise} Signed transaction data + * @param {PrivateKey} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - public async sign(privateKey: string): Promise { + public async sign(privateKey: PrivateKey): Promise { try { this.wallet = this.provider.ethers.wallet(privateKey) this.signedData = await this.wallet.signTransaction(this.rawData) @@ -64,14 +64,12 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Send the transaction to the blockchain network - * @returns {Promise} Transaction data + * @returns {Promise} Transaction data */ - async send(): Promise { - return new Transaction( - (await this.provider.ethers.jsonRpc.send('eth_sendRawTransaction', [ - this.signedData - ])) as string - ) + async send(): Promise { + return (await this.provider.ethers.jsonRpc.send('eth_sendRawTransaction', [ + this.signedData + ])) as TransactionId } /** @@ -90,43 +88,3 @@ export class TransactionSigner implements TransactionSignerInterface { return this.signedData } } - -export class ContractTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new ContractTransaction((await super.send()).getId()) - } -} - -export class CoinTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new CoinTransaction((await super.send()).getId()) - } -} - -export class TokenTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new TokenTransaction((await super.send()).getId()) - } -} - -export class NftTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new NftTransaction((await super.send()).getId()) - } -} diff --git a/packages/networks/evm-chains/tests/assets.spec.ts b/packages/networks/evm-chains/tests/assets.spec.ts index f88b749..d943f88 100644 --- a/packages/networks/evm-chains/tests/assets.spec.ts +++ b/packages/networks/evm-chains/tests/assets.spec.ts @@ -5,7 +5,7 @@ import { Coin } from '../src/assets/Coin.ts' import { Token } from '../src/assets/Token.ts' import { math } from '@multiplechain/utils' import { Transaction } from '../src/models/Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' +import { TransactionStatusEnum, type TransactionId } from '@multiplechain/types' import { TransactionSigner } from '../src/services/TransactionSigner.ts' const coinBalanceTestAmount = Number(process.env.EVM_COIN_BALANCE_TEST_AMOUNT) @@ -50,8 +50,8 @@ const checkSigner = async (signer: TransactionSigner, privateKey?: string): Prom assert.isString(signer.getSignedData()) } -const checkTx = async (transaction: Transaction): Promise => { - expect(transaction).toBeInstanceOf(Transaction) +const checkTx = async (transactionId: TransactionId): Promise => { + const transaction = new Transaction(transactionId) const status = await transaction.wait(10 * 1000) expect(status).toBe(TransactionStatusEnum.CONFIRMED) } @@ -88,7 +88,7 @@ describe('Coin', () => { await checkTx(await signer.send()) const afterBalance = await coin.getBalance(receiverTestAddress) - expect(afterBalance).toBe(math.add(beforeBalance, transferTestAmount)) + expect(afterBalance).toBe(math.add(beforeBalance, transferTestAmount, coin.getDecimals())) }) }) @@ -132,7 +132,9 @@ describe('Token', () => { await checkTx(await signer.send()) const afterBalance = await token.getBalance(receiverTestAddress) - expect(afterBalance).toBe(math.add(beforeBalance, tokenTransferTestAmount)) + expect(afterBalance).toBe( + math.add(beforeBalance, tokenTransferTestAmount, await token.getDecimals()) + ) }) it('Approve and Allowance', async () => { @@ -174,7 +176,7 @@ describe('Token', () => { await checkTx(await signer.send()) const afterBalance = await token.getBalance(receiverTestAddress) - expect(afterBalance).toBe(math.add(beforeBalance, 2)) + expect(afterBalance).toBe(math.add(beforeBalance, 2, await token.getDecimals())) }) }) diff --git a/packages/networks/evm-chains/tests/services.spec.ts b/packages/networks/evm-chains/tests/services.spec.ts index 1b9e63e..b6c489b 100644 --- a/packages/networks/evm-chains/tests/services.spec.ts +++ b/packages/networks/evm-chains/tests/services.spec.ts @@ -13,7 +13,7 @@ import { Transaction } from '../src/models/Transaction.ts' import { TokenTransaction } from '../src/models/TokenTransaction.ts' import { NftTransaction } from '../src/models/NftTransaction.ts' import { NFT } from '../src/assets/NFT.ts' -import ERC20 from '../resources/erc20.json' +import ERC20 from '../resources/ERC20.json' import type { InterfaceAbi } from 'ethers' import { ContractFactory, WebSocketProvider } from 'ethers' @@ -295,8 +295,6 @@ describe('Transaction Listener', () => { const newSigner = await nft.transfer(receiverTestAddress, senderTestAddress, 9) - const newTransaction = await (await newSigner.sign(receiverPrivateKey)).send() - - expect(newTransaction).toBeInstanceOf(NftTransaction) + await (await newSigner.sign(receiverPrivateKey)).send() }) }) diff --git a/packages/networks/solana/README.md b/packages/networks/solana/README.md index e69de29..d746267 100644 --- a/packages/networks/solana/README.md +++ b/packages/networks/solana/README.md @@ -0,0 +1,7 @@ +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/networks/solana/index.html b/packages/networks/solana/index.example.html similarity index 99% rename from packages/networks/solana/index.html rename to packages/networks/solana/index.example.html index b6e11ff..88551e5 100644 --- a/packages/networks/solana/index.html +++ b/packages/networks/solana/index.example.html @@ -230,7 +230,7 @@ } const wallet = new Solana.browser.Wallet(adapter) - const adapterProvider = await wallet.connect(provider, { + const adapterProvider = await wallet.connect({ projectId: '113d9f5689edd84ff230c2a6d679c80c', metadata: { name: 'MultipleChain', diff --git a/packages/networks/solana/package.json b/packages/networks/solana/package.json index 3c7990a..bfd85ef 100644 --- a/packages/networks/solana/package.json +++ b/packages/networks/solana/package.json @@ -27,6 +27,16 @@ "types": "./dist/browser/index.d.ts" } }, + "typesVersions": { + "*": { + "node": [ + "./dist/index.d.ts" + ], + "browser": [ + "./dist/browser/index.d.ts" + ] + } + }, "files": [ "dist", "README.md", @@ -36,7 +46,7 @@ "dev": "vite", "clean": "rm -rf dist", "watch": "tsc --watch", - "build:vite": "vite build", + "build:vite": "set NODE_OPTIONS=--max-old-space-size=4096 && vite build", "build:node": "tsx esbuild.ts", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .ts", @@ -64,8 +74,8 @@ "dependencies": { "@metaplex-foundation/js": "^0.20.1", "@multiplechain/solana-walletconnect": "^0.1.1", - "@multiplechain/types": "^0.1.56", - "@multiplechain/utils": "^0.1.20", + "@multiplechain/types": "^0.1.63", + "@multiplechain/utils": "^0.1.21", "@solana/spl-token": "^0.4.6", "@solana/spl-token-metadata": "^0.1.4", "@solana/wallet-adapter-base": "^0.9.23", diff --git a/packages/networks/solana/pnpm-lock.yaml b/packages/networks/solana/pnpm-lock.yaml index 45216c2..6244306 100644 --- a/packages/networks/solana/pnpm-lock.yaml +++ b/packages/networks/solana/pnpm-lock.yaml @@ -12,11 +12,11 @@ dependencies: specifier: ^0.1.1 version: 0.1.1(@solana/web3.js@1.91.8)(vite@5.2.11) '@multiplechain/types': - specifier: ^0.1.56 - version: 0.1.56 + specifier: ^0.1.63 + version: 0.1.63 '@multiplechain/utils': - specifier: ^0.1.20 - version: 0.1.20 + specifier: ^0.1.21 + version: 0.1.21 '@solana/spl-token': specifier: ^0.4.6 version: 0.4.6(@solana/web3.js@1.91.8)(fastestsmallesttextencoderdecoder@1.0.22) @@ -935,12 +935,12 @@ packages: - vite dev: false - /@multiplechain/types@0.1.56: - resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==} + /@multiplechain/types@0.1.63: + resolution: {integrity: sha512-4C201vUsN6F1S/M7vT+GZS0wTdKGZXHqn4YmC6ouC8n0uxMCEFdI2sSCadyHKFKG5OBPo16X1oFCX9MVwPqMoA==} dev: false - /@multiplechain/utils@0.1.20: - resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==} + /@multiplechain/utils@0.1.21: + resolution: {integrity: sha512-4mRlnhvXcS+7Hb1By5s2eoCH02kqOSQzV8qj0DHqZK20FtxtduaoGCtcClrFgEjN/HAXnHgdDc1pxTpveIkvRQ==} dependencies: '@types/ws': 8.5.10 bignumber.js: 9.1.2 diff --git a/packages/networks/solana/src/assets/Coin.ts b/packages/networks/solana/src/assets/Coin.ts index 31b533e..82b7911 100644 --- a/packages/networks/solana/src/assets/Coin.ts +++ b/packages/networks/solana/src/assets/Coin.ts @@ -1,10 +1,15 @@ import { Provider } from '../services/Provider.ts' import { fromLamports, toLamports } from '../utils.ts' +import { TransactionSigner } from '../services/TransactionSigner.ts' import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js' -import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types' -import { CoinTransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type CoinInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' -export class Coin implements CoinInterface { +export class Coin implements CoinInterface { /** * Blockchain network provider */ @@ -39,24 +44,24 @@ export class Coin implements CoinInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of COIN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return fromLamports(await this.provider.web3.getBalance(new PublicKey(owner))) } /** - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -79,6 +84,6 @@ export class Coin implements CoinInterface { transaction.feePayer = senderPubKey - return new CoinTransactionSigner(transaction) + return new TransactionSigner(transaction) } } diff --git a/packages/networks/solana/src/assets/Contract.ts b/packages/networks/solana/src/assets/Contract.ts index f3e0c35..d9bb2ae 100644 --- a/packages/networks/solana/src/assets/Contract.ts +++ b/packages/networks/solana/src/assets/Contract.ts @@ -1,61 +1,69 @@ import { PublicKey } from '@solana/web3.js' import { Provider } from '../services/Provider.ts' -import type { ContractInterface } from '@multiplechain/types' +import type { ContractAddress, ContractInterface, WalletAddress } from '@multiplechain/types' export class Contract implements ContractInterface { /** * Contract address */ - address: string + address: ContractAddress + /** + * Contract public key + */ pubKey: PublicKey + /** * Blockchain network provider */ provider: Provider /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider */ - constructor(address: string, provider?: Provider) { + constructor(address: ContractAddress, provider?: Provider) { this.address = address this.pubKey = new PublicKey(address) this.provider = provider ?? Provider.instance } /** - * @returns {string} Contract address + * @returns {ContractAddress} Contract address */ - getAddress(): string { + getAddress(): ContractAddress { return this.address } /** * @param {string} _method Method name - * @param {any[]} _args Method parameters - * @returns {Promise} Method result + * @param {unknown[]} _args Method parameters + * @returns {Promise} Method result */ - async callMethod(_method: string, ..._args: any[]): Promise { + async callMethod(_method: string, ..._args: unknown[]): Promise { throw new Error('Method not implemented.') } /** * @param {string} _method Method name - * @param {any[]} _args Sender wallet address - * @returns {Promise} Encoded method data + * @param {unknown[]} _args Sender wallet address + * @returns {Promise} Encoded method data */ - async getMethodData(_method: string, ..._args: any[]): Promise { + async getMethodData(_method: string, ..._args: unknown[]): Promise { throw new Error('Method not implemented.') } /** * @param {string} _method Method name - * @param {string} _from Sender wallet address - * @param {any[]} _args Method parameters - * @returns {Promise} Encoded method data + * @param {WalletAddress} _from Sender wallet address + * @param {unknown[]} _args Method parameters + * @returns {Promise} Encoded method data */ - async createTransactionData(_method: string, _from: string, ..._args: any[]): Promise { + async createTransactionData( + _method: string, + _from: WalletAddress, + ..._args: unknown[] + ): Promise { throw new Error('Method not implemented.') } } diff --git a/packages/networks/solana/src/assets/NFT.ts b/packages/networks/solana/src/assets/NFT.ts index 954a00c..d172afa 100644 --- a/packages/networks/solana/src/assets/NFT.ts +++ b/packages/networks/solana/src/assets/NFT.ts @@ -1,7 +1,11 @@ import { Contract } from './Contract.ts' import { Metaplex } from '@metaplex-foundation/js' -import { ErrorTypeEnum, type NftInterface } from '@multiplechain/types' -import { NftTransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type NftId, + type NftInterface, + type WalletAddress +} from '@multiplechain/types' import type { Sft, SftWithToken, Nft, NftWithToken } from '@metaplex-foundation/js' import { PublicKey, @@ -18,10 +22,11 @@ import { createTransferInstruction, createApproveInstruction } from '@solana/spl-token' +import { TransactionSigner } from '../services/TransactionSigner.ts' type Metadata = Sft | SftWithToken | Nft | NftWithToken -export class NFT extends Contract implements NftInterface { +export class NFT extends Contract implements NftInterface { metadata: Metadata /** @@ -68,10 +73,10 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of NFT */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const metaplex = Metaplex.make(this.provider.web3) const res = await metaplex.nfts().findAllByOwner({ owner: new PublicKey(owner) }) return res.filter((nft) => { @@ -81,10 +86,10 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {number | string} nftId NFT ID - * @returns {Promise} Wallet address of the owner of the NFT + * @param {NftId} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - async getOwner(nftId: number | string): Promise { + async getOwner(nftId: NftId): Promise { const accounts = await this.provider.web3.getTokenLargestAccounts(new PublicKey(nftId)) const accountInfo = (await this.provider.web3.getParsedAccountInfo( accounts.value[0].address @@ -93,18 +98,18 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {number | string} nftId NFT ID + * @param {NftId} nftId NFT ID * @returns {Promise} URI of the NFT */ - async getTokenURI(nftId: number | string): Promise { + async getTokenURI(nftId: NftId): Promise { return (await this.getMetadata(new PublicKey(nftId)))?.uri ?? '' } /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Wallet address of the approved spender + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - async getApproved(nftId: number | string): Promise { + async getApproved(nftId: NftId): Promise { const accounts = await this.provider.web3.getTokenLargestAccounts(new PublicKey(nftId)) const accountInfo = (await this.provider.web3.getParsedAccountInfo( accounts.value[0].address @@ -113,32 +118,32 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {string} sender Sender address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer + * @param {WalletAddress} sender Sender address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID + * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - nftId: number | string - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { return await this.transferFrom(sender, sender, receiver, nftId) } /** - * @param {string} spender Spender address - * @param {string} owner Owner address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID - * @returns {Promise} Transaction signer + * @param {WalletAddress} spender Spender address + * @param {WalletAddress} owner Owner address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID + * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { // Check if tokens exist const balance = await this.getBalance(owner) @@ -202,21 +207,21 @@ export class NFT extends Contract implements NftInterface { transaction.feePayer = spenderPubKey - return new NftTransactionSigner(transaction) + return new TransactionSigner(transaction) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {NftId} nftId ID of the NFT that will be transferred * @returns {Promise} Transaction signer */ async approve( - owner: string, - spender: string, - nftId: number | string - ): Promise { + owner: WalletAddress, + spender: WalletAddress, + nftId: NftId + ): Promise { // Check if tokens exist const balance = await this.getBalance(owner) @@ -244,6 +249,6 @@ export class NFT extends Contract implements NftInterface { transaction.feePayer = ownerPubKey - return new NftTransactionSigner(transaction) + return new TransactionSigner(transaction) } } diff --git a/packages/networks/solana/src/assets/Token.ts b/packages/networks/solana/src/assets/Token.ts index 4ebe7af..8561c34 100644 --- a/packages/networks/solana/src/assets/Token.ts +++ b/packages/networks/solana/src/assets/Token.ts @@ -1,8 +1,12 @@ import { Contract } from './Contract.ts' import { math } from '@multiplechain/utils' import { Metaplex } from '@metaplex-foundation/js' -import { ErrorTypeEnum, type TokenInterface } from '@multiplechain/types' -import { TokenTransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type TokenInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' import { PublicKey, Transaction, @@ -20,6 +24,7 @@ import { createTransferInstruction, createApproveInstruction } from '@solana/spl-token' +import { TransactionSigner } from '../services/TransactionSigner.ts' interface Metadata { name: string @@ -28,11 +33,12 @@ interface Metadata { decimals: number } -export class Token extends Contract implements TokenInterface { +export class Token extends Contract implements TokenInterface { metadata: Metadata /** * Token metadata + * @returns {Promise} Metadata of the token */ async getMetadata(): Promise { if (this.metadata !== undefined) return this.metadata @@ -71,6 +77,9 @@ export class Token extends Contract implements TokenInterface { } } + /** + * @returns {Promise} Program ID + */ async getProgramId(): Promise { const accountInfo = await this.provider.web3.getAccountInfo(this.pubKey) return accountInfo !== null ? accountInfo.owner : TOKEN_PROGRAM_ID @@ -101,10 +110,10 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of TOKEN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { try { const res = await this.provider.web3.getParsedTokenAccountsByOwner( new PublicKey(owner), @@ -129,11 +138,11 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner + * @param {WalletAddress} owner Address of owner of the tokens that is being used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner * @returns {Promise} Amount of tokens that the spender is allowed to spend */ - async getAllowance(owner: string, spender?: string): Promise { + async getAllowance(owner: WalletAddress, spender?: WalletAddress): Promise { try { const ownerResult = await this.provider.web3.getParsedTokenAccountsByOwner( new PublicKey(owner), @@ -172,32 +181,32 @@ export class Token extends Contract implements TokenInterface { /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { return await this.transferFrom(sender, sender, receiver, amount) } /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred - * @returns {Promise} Transaction signer + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - amount: number - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -269,17 +278,21 @@ export class Token extends Contract implements TokenInterface { transaction.feePayer = spenderPubKey - return new TokenTransactionSigner(transaction) + return new TransactionSigner(transaction) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number} amount Amount of the tokens that will be used + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {TransferAmount} amount Amount of the tokens that will be used * @returns {Promise} Transaction signer */ - async approve(owner: string, spender: string, amount: number): Promise { + async approve( + owner: WalletAddress, + spender: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -316,6 +329,6 @@ export class Token extends Contract implements TokenInterface { transaction.feePayer = ownerPubKey - return new TokenTransactionSigner(transaction) + return new TransactionSigner(transaction) } } diff --git a/packages/networks/solana/src/browser/Wallet.ts b/packages/networks/solana/src/browser/Wallet.ts index 156d929..1e3e2f7 100644 --- a/packages/networks/solana/src/browser/Wallet.ts +++ b/packages/networks/solana/src/browser/Wallet.ts @@ -1,10 +1,13 @@ import { type WalletInterface, - type WalletAdapterInterface, type WalletPlatformEnum, - type TransactionSignerInterface, - type ProviderInterface, - ErrorTypeEnum + type WalletAdapterInterface, + ErrorTypeEnum, + type ConnectConfig, + type UnknownConfig, + type WalletAddress, + type SignedMessage, + type TransactionId } from '@multiplechain/types' import { Provider } from '../services/Provider.ts' import type { @@ -73,20 +76,24 @@ const rejectMap = (error: any, reject: (a: any) => any): any => { return reject(error) } -export class Wallet implements WalletInterface { - adapter: WalletAdapterInterface +export type WalletAdapter = BaseMessageSignerWalletAdapter + +type WalletAdapterType = WalletAdapterInterface + +export class Wallet implements WalletInterface { + adapter: WalletAdapterType - walletProvider: BaseMessageSignerWalletAdapter + walletProvider: WalletAdapter networkProvider: Provider currentReject: (a: any) => any /** - * @param {WalletAdapterInterface} adapter + * @param {WalletAdapterType} adapter * @param {Provider} provider */ - constructor(adapter: WalletAdapterInterface, provider?: Provider) { + constructor(adapter: WalletAdapterType, provider?: Provider) { this.adapter = adapter this.networkProvider = provider ?? Provider.instance } @@ -128,27 +135,26 @@ export class Wallet implements WalletInterface { /** * @param {string} url - * @param {object} ops + * @param {UnknownConfig} config * @returns {string} */ - createDeepLink(url: string, ops?: object): string | null { + createDeepLink(url: string, config?: UnknownConfig): string | null { if (this.adapter.createDeepLink === undefined) { return null } - return this.adapter.createDeepLink(url, ops) + return this.adapter.createDeepLink(url, config) } /** - * @param {ProviderInterface} provider - * @param {Object} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - async connect(provider?: ProviderInterface, ops?: object): Promise { + async connect(config?: ConnectConfig): Promise { return await new Promise((resolve, reject) => { this.currentReject = reject this.adapter - .connect(provider, ops) + .connect(this.networkProvider, config) .then(async (provider) => { this.walletProvider = provider as BaseMessageSignerWalletAdapter this.on('error', (error) => rejectMap(error, this.currentReject)) @@ -182,16 +188,17 @@ export class Wallet implements WalletInterface { } /** - * @returns {Promise} + * @returns {Promise} */ - async getAddress(): Promise { + async getAddress(): Promise { return this.walletProvider.publicKey?.toBase58() ?? '' } /** * @param {string} message + * @returns {Promise} */ - async signMessage(message: string): Promise { + async signMessage(message: string): Promise { return await new Promise((resolve, reject) => { this.currentReject = reject this.walletProvider @@ -206,11 +213,10 @@ export class Wallet implements WalletInterface { } /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - async sendTransaction(_transactionSigner: TransactionSignerInterface): Promise { - const transactionSigner = _transactionSigner as TransactionSigner + async sendTransaction(transactionSigner: TransactionSigner): Promise { return await new Promise((resolve, reject) => { this.currentReject = reject try { diff --git a/packages/networks/solana/src/browser/adapters/BitgetWallet.ts b/packages/networks/solana/src/browser/adapters/BitgetWallet.ts index be1dead..8badf80 100644 --- a/packages/networks/solana/src/browser/adapters/BitgetWallet.ts +++ b/packages/networks/solana/src/browser/adapters/BitgetWallet.ts @@ -1,17 +1,20 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import { WalletReadyState } from '@solana/wallet-adapter-base' import { BitgetWalletAdapter } from '@solana/wallet-adapter-bitkeep' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { WalletReadyState, type BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' +import type { WalletAdapterInterface } from '@multiplechain/types' const bitget = new BitgetWalletAdapter() -const BitgetWallet: WalletAdapterInterface = { +const BitgetWallet: WalletAdapterInterface = { id: 'bitgetwallet', name: bitget.name, icon: bitget.icon, + provider: bitget, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://web3.bitget.com/en/wallet-download?type=3', - createDeepLink(url: string, _ops?: object): string { + createDeepLink(url: string): string { return `https://bkcode.vip?action=dapp&url=${url}` }, isDetected: () => bitget.readyState === WalletReadyState.Installed, @@ -19,12 +22,9 @@ const BitgetWallet: WalletAdapterInterface = { disconnect: async () => { await bitget.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await bitget.connect() - return bitget as BaseMessageSignerWalletAdapter + return bitget as WalletAdapter } } diff --git a/packages/networks/solana/src/browser/adapters/CoinbaseWallet.ts b/packages/networks/solana/src/browser/adapters/CoinbaseWallet.ts index a5205ac..c199ec4 100644 --- a/packages/networks/solana/src/browser/adapters/CoinbaseWallet.ts +++ b/packages/networks/solana/src/browser/adapters/CoinbaseWallet.ts @@ -1,17 +1,20 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import { WalletReadyState } from '@solana/wallet-adapter-base' +import type { WalletAdapterInterface } from '@multiplechain/types' import { CoinbaseWalletAdapter } from '@solana/wallet-adapter-coinbase' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { WalletReadyState, type BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' const coinbase = new CoinbaseWalletAdapter() -const CoinbaseWallet: WalletAdapterInterface = { +const CoinbaseWallet: WalletAdapterInterface = { id: 'coinbasewallet', name: coinbase.name, icon: coinbase.icon, + provider: coinbase, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://www.coinbase.com/wallet/downloads', - createDeepLink(url: string, _ops?: object): string { + createDeepLink(url: string): string { return `https://go.cb-w.com/dapp?cb_url=${url}` }, isDetected: () => coinbase.readyState === WalletReadyState.Installed, @@ -19,10 +22,7 @@ const CoinbaseWallet: WalletAdapterInterface = { disconnect: async () => { await coinbase.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await coinbase.connect() return coinbase } diff --git a/packages/networks/solana/src/browser/adapters/Phantom.ts b/packages/networks/solana/src/browser/adapters/Phantom.ts index 51a77ad..3ee1669 100644 --- a/packages/networks/solana/src/browser/adapters/Phantom.ts +++ b/packages/networks/solana/src/browser/adapters/Phantom.ts @@ -1,7 +1,8 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { PhantomWalletAdapter } from '@solana/wallet-adapter-phantom' -import type { BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' const phantomAdapter = new PhantomWalletAdapter() @@ -16,13 +17,14 @@ declare global { } } -const Phantom: WalletAdapterInterface = { +const Phantom: WalletAdapterInterface = { id: 'phantom', name: phantomAdapter.name, icon: phantomAdapter.icon, + provider: phantomAdapter, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://phantom.app/download', - createDeepLink(url: string, _ops?: object): string { + createDeepLink(url: string): string { return `https://phantom.app/ul/browse/${url}?ref=${url}` }, isDetected: () => @@ -33,10 +35,7 @@ const Phantom: WalletAdapterInterface = { disconnect: async () => { await phantomAdapter.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await phantomAdapter.connect() return phantomAdapter } diff --git a/packages/networks/solana/src/browser/adapters/Slope.ts b/packages/networks/solana/src/browser/adapters/Slope.ts index 1d7fbe6..23dcdac 100644 --- a/packages/networks/solana/src/browser/adapters/Slope.ts +++ b/packages/networks/solana/src/browser/adapters/Slope.ts @@ -1,14 +1,17 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import { WalletReadyState } from '@solana/wallet-adapter-base' import { SlopeWalletAdapter } from '@solana/wallet-adapter-slope' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { WalletReadyState, type BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' +import type { WalletAdapterInterface } from '@multiplechain/types' const slope = new SlopeWalletAdapter() -const Slope: WalletAdapterInterface = { +const Slope: WalletAdapterInterface = { id: 'slope', name: slope.name, icon: slope.icon, + provider: slope, platforms: [WalletPlatformEnum.BROWSER], downloadLink: 'https://www.slope.finance/', isDetected: () => slope.readyState === WalletReadyState.Installed, @@ -16,12 +19,9 @@ const Slope: WalletAdapterInterface = { disconnect: async () => { await slope.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await slope.connect() - return slope as BaseMessageSignerWalletAdapter + return slope as WalletAdapter } } diff --git a/packages/networks/solana/src/browser/adapters/Solflare.ts b/packages/networks/solana/src/browser/adapters/Solflare.ts index 69b453a..c9b1fe3 100644 --- a/packages/networks/solana/src/browser/adapters/Solflare.ts +++ b/packages/networks/solana/src/browser/adapters/Solflare.ts @@ -1,11 +1,9 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { - WalletReadyState, - WalletAdapterNetwork, - type BaseMessageSignerWalletAdapter -} from '@solana/wallet-adapter-base' +import { WalletReadyState, WalletAdapterNetwork } from '@solana/wallet-adapter-base' const solflare = new SolflareWalletAdapter() @@ -17,13 +15,14 @@ declare global { } } -const Solflare: WalletAdapterInterface = { +const Solflare: WalletAdapterInterface = { id: 'solflare', name: solflare.name, icon: solflare.icon, + provider: solflare, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://solflare.com/download#extension', - createDeepLink(url: string, _ops?: object): string { + createDeepLink(url: string): string { return `https://solflare.com/ul/v1/browse/${url}?ref=${url}` }, isDetected: () => solflare.readyState === WalletReadyState.Installed, @@ -31,13 +30,10 @@ const Solflare: WalletAdapterInterface = { disconnect: async () => { await solflare.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (provider?: Provider): Promise => { const solflare = new SolflareWalletAdapter({ network: - _provider !== undefined && _provider?.isTestnet() + provider !== undefined && provider?.isTestnet() ? WalletAdapterNetwork.Devnet : WalletAdapterNetwork.Mainnet }) diff --git a/packages/networks/solana/src/browser/adapters/TokenPocket.ts b/packages/networks/solana/src/browser/adapters/TokenPocket.ts index 12725d6..6e596eb 100644 --- a/packages/networks/solana/src/browser/adapters/TokenPocket.ts +++ b/packages/networks/solana/src/browser/adapters/TokenPocket.ts @@ -1,17 +1,20 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import { WalletReadyState } from '@solana/wallet-adapter-base' +import type { WalletAdapterInterface } from '@multiplechain/types' import { TokenPocketWalletAdapter } from '@solana/wallet-adapter-tokenpocket' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { WalletReadyState, type BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' const tokenPocket = new TokenPocketWalletAdapter() -const TokenPocket: WalletAdapterInterface = { +const TokenPocket: WalletAdapterInterface = { id: 'tokenpocket', name: tokenPocket.name, icon: tokenPocket.icon, + provider: tokenPocket, platforms: [WalletPlatformEnum.MOBILE], downloadLink: 'https://www.tokenpocket.pro/en/download/app', - createDeepLink(url: string, _ops?: object): string { + createDeepLink(url: string): string { return ( 'tpdapp://open?params=' + JSON.stringify({ @@ -26,12 +29,9 @@ const TokenPocket: WalletAdapterInterface = { disconnect: async () => { await tokenPocket.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await tokenPocket.connect() - return tokenPocket as BaseMessageSignerWalletAdapter + return tokenPocket as WalletAdapter } } diff --git a/packages/networks/solana/src/browser/adapters/TrustWallet.ts b/packages/networks/solana/src/browser/adapters/TrustWallet.ts index 3ed7a93..0dc4b89 100644 --- a/packages/networks/solana/src/browser/adapters/TrustWallet.ts +++ b/packages/networks/solana/src/browser/adapters/TrustWallet.ts @@ -1,17 +1,20 @@ +import type { WalletAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import { WalletReadyState } from '@solana/wallet-adapter-base' import { TrustWalletAdapter } from '@solana/wallet-adapter-trust' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' -import { WalletReadyState, type BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' +import type { WalletAdapterInterface } from '@multiplechain/types' const trustIcon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTgiIGhlaWdodD0iNjUiIHZpZXdCb3g9IjAgMCA1OCA2NSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgOS4zODk0OUwyOC44OTA3IDBWNjUuMDA0MkM4LjI1NDUgNTYuMzM2OSAwIDM5LjcyNDggMCAzMC4zMzUzVjkuMzg5NDlaIiBmaWxsPSIjMDUwMEZGIi8+CjxwYXRoIGQ9Ik01Ny43ODIyIDkuMzg5NDlMMjguODkxNSAwVjY1LjAwNDJDNDkuNTI3NyA1Ni4zMzY5IDU3Ljc4MjIgMzkuNzI0OCA1Ny43ODIyIDMwLjMzNTNWOS4zODk0OVoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8yMjAxXzY5NDIpIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjIwMV82OTQyIiB4MT0iNTEuMzYxNSIgeTE9Ii00LjE1MjkzIiB4Mj0iMjkuNTM4NCIgeTI9IjY0LjUxNDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjAyMTEyIiBzdG9wLWNvbG9yPSIjMDAwMEZGIi8+CjxzdG9wIG9mZnNldD0iMC4wNzYyNDIzIiBzdG9wLWNvbG9yPSIjMDA5NEZGIi8+CjxzdG9wIG9mZnNldD0iMC4xNjMwODkiIHN0b3AtY29sb3I9IiM0OEZGOTEiLz4KPHN0b3Agb2Zmc2V0PSIwLjQyMDA0OSIgc3RvcC1jb2xvcj0iIzAwOTRGRiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjgyODg2IiBzdG9wLWNvbG9yPSIjMDAzOEZGIi8+CjxzdG9wIG9mZnNldD0iMC45MDI0NjUiIHN0b3AtY29sb3I9IiMwNTAwRkYiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K' const trust = new TrustWalletAdapter() -const TrustWallet: WalletAdapterInterface = { +const TrustWallet: WalletAdapterInterface = { id: 'trustwallet', name: 'TrustWallet', icon: trustIcon, + provider: trust, platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE], downloadLink: 'https://trustwallet.com/download', isDetected: () => trust.readyState === WalletReadyState.Installed, @@ -19,12 +22,9 @@ const TrustWallet: WalletAdapterInterface = { disconnect: async () => { await trust.disconnect() }, - connect: async ( - _provider?: ProviderInterface, - _ops?: object - ): Promise => { + connect: async (): Promise => { await trust.connect() - return trust as BaseMessageSignerWalletAdapter + return trust as WalletAdapter } } diff --git a/packages/networks/solana/src/browser/adapters/WalletConnect.ts b/packages/networks/solana/src/browser/adapters/WalletConnect.ts index 3b945ff..9546d84 100644 --- a/packages/networks/solana/src/browser/adapters/WalletConnect.ts +++ b/packages/networks/solana/src/browser/adapters/WalletConnect.ts @@ -1,24 +1,21 @@ +import type { WalletAdapter } from '../Wallet.ts' +import type { Provider } from '../../services/Provider.ts' +import { WalletAdapterNetwork } from '@solana/wallet-adapter-base' import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' import WalletConnectWalletAdapter from '@multiplechain/solana-walletconnect' -import { - WalletAdapterNetwork, - type BaseMessageSignerWalletAdapter -} from '@solana/wallet-adapter-base' -import type { - ProviderInterface, - WalletAdapterInterface, - WalletConnectOps -} from '@multiplechain/types' +import type { ConnectConfig, WalletAdapterInterface } from '@multiplechain/types' const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJiYXNpYyIgaWQ9IkxheWVyXzEiCgkgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAzODcuNiAyMzcuNiIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9IldhbGxldENvbm5lY3RfMDAwMDAwNzM3MDMwNjM0MzgyMjA2NDI3MzAwMDAwMDI5MTc3MTc1NTIyMzY0NzI0OTZfIiBmaWxsPSIjM0I5OUZDIiBkPSJNNzkuNCw0Ni40CgljNjMuMi02MS45LDE2NS43LTYxLjksMjI4LjksMGw3LjYsNy40YzMuMiwzLjEsMy4yLDguMSwwLDExLjJsLTI2LDI1LjVjLTEuNiwxLjUtNC4xLDEuNS01LjcsMGwtMTAuNS0xMC4zCgljLTQ0LjEtNDMuMi0xMTUuNi00My4yLTE1OS43LDBsLTExLjIsMTFjLTEuNiwxLjUtNC4xLDEuNS01LjcsMEw3MSw2NS44Yy0zLjItMy4xLTMuMi04LjEsMC0xMS4yTDc5LjQsNDYuNHogTTM2Mi4xLDk5LjFsMjMuMiwyMi43CgljMy4yLDMuMSwzLjIsOC4xLDAsMTEuMkwyODAuOCwyMzUuM2MtMy4yLDMuMS04LjMsMy4xLTExLjQsMGMwLDAsMCwwLDAsMGwtNzQuMS03Mi42Yy0wLjgtMC44LTIuMS0wLjgtMi45LDBjMCwwLDAsMCwwLDAKCWwtNzQuMSw3Mi42Yy0zLjIsMy4xLTguMywzLjEtMTEuNCwwYzAsMCwwLDAsMCwwTDIuNCwxMzNjLTMuMi0zLjEtMy4yLTguMSwwLTExLjJsMjMuMi0yMi43YzMuMi0zLjEsOC4zLTMuMSwxMS40LDBsNzQuMSw3Mi42CgljMC44LDAuOCwyLjEsMC44LDIuOSwwYzAsMCwwLDAsMCwwbDc0LjEtNzIuNmMzLjItMy4xLDguMy0zLjEsMTEuNCwwYzAsMCwwLDAsMCwwbDc0LjEsNzIuNmMwLjgsMC44LDIuMSwwLjgsMi45LDBsNzQuMS03Mi42CglDMzUzLjgsOTYsMzU4LjksOTYsMzYyLjEsOTkuMXoiLz4KPC9zdmc+' let isConnected = false +let walletProvider: WalletConnectWalletAdapter | undefined -const WalletConnect: WalletAdapterInterface = { +const WalletConnect: WalletAdapterInterface = { icon, id: 'walletconnect', name: 'WalletConnect', + provider: walletProvider, platforms: [WalletPlatformEnum.UNIVERSAL], isDetected: () => true, isConnected: () => isConnected, @@ -33,21 +30,16 @@ const WalletConnect: WalletAdapterInterface = { localStorage.removeItem('WALLETCONNECT_DEEPLINK_CHOICE') indexedDB.deleteDatabase('WALLET_CONNECT_V2_INDEXED_DB') }, - connect: async ( - provider?: ProviderInterface, - _ops?: WalletConnectOps | object - ): Promise => { - const ops = _ops as WalletConnectOps - + connect: async (provider?: Provider, config?: ConnectConfig): Promise => { if (provider === undefined) { throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) } - if (ops === undefined) { - throw new Error(ErrorTypeEnum.OPS_IS_REQUIRED) + if (config === undefined) { + throw new Error(ErrorTypeEnum.CONFIG_IS_REQUIRED) } - if (ops.projectId === undefined) { + if (config.projectId === undefined) { throw new Error(ErrorTypeEnum.PROJECT_ID_IS_REQUIRED) } @@ -56,7 +48,7 @@ const WalletConnect: WalletAdapterInterface = { ? WalletAdapterNetwork.Devnet : WalletAdapterNetwork.Mainnet, options: { - projectId: ops.projectId, + projectId: config.projectId, relayUrl: 'wss://relay.walletconnect.com', qrcodeModalOptions: { mobileLinks: ['trust'], @@ -68,6 +60,8 @@ const WalletConnect: WalletAdapterInterface = { } }) + walletProvider = walletConnect + await walletConnect.connect() isConnected = true diff --git a/packages/networks/solana/src/browser/adapters/Web3Modal.ts b/packages/networks/solana/src/browser/adapters/Web3Modal.ts index c8dd207..dab5ccc 100644 --- a/packages/networks/solana/src/browser/adapters/Web3Modal.ts +++ b/packages/networks/solana/src/browser/adapters/Web3Modal.ts @@ -1,4 +1,6 @@ import type { Metadata } from '@web3modal/core' +import type { WalletAdapter } from '../Wallet.ts' +import type { Provider } from '../../services/Provider.ts' import { solana, solanaDevnet } from '@web3modal/solana/chains' import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' import type { BaseMessageSignerWalletAdapter } from '@solana/wallet-adapter-base' @@ -13,14 +15,15 @@ import { const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKIHdpZHRoPSI0MDAuMDAwMDAwcHQiIGhlaWdodD0iNDAwLjAwMDAwMHB0IiB2aWV3Qm94PSIwIDAgNDAwLjAwMDAwMCA0MDAuMDAwMDAwIgogcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCI+Cgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMDAwMCw0MDAuMDAwMDAwKSBzY2FsZSgwLjEwMDAwMCwtMC4xMDAwMDApIgpmaWxsPSIjMDAwMDAwIiBzdHJva2U9Im5vbmUiPgo8cGF0aCBkPSJNMCAyMDAwIGwwIC0yMDAwIDIwMDAgMCAyMDAwIDAgMCAyMDAwIDAgMjAwMCAtMjAwMCAwIC0yMDAwIDAgMAotMjAwMHogbTIyNjQgNzM2IGMxMjQgLTMyIDI2MSAtOTggMzYzIC0xNzUgOTEgLTY5IDE3MyAtMTUzIDE3MyAtMTc4IDAgLTE3Ci0xODcgLTIwMiAtMjA1IC0yMDMgLTYgMCAtMzEgMjEgLTU1IDQ2IC0yMjcgMjM0IC01NjkgMjk5IC04NTcgMTY0IC03OSAtMzYKLTEzMSAtNzMgLTIxMyAtMTQ5IC0zNiAtMzMgLTcyIC02MCAtODAgLTYxIC0yMCAwIC0yMDAgMTgxIC0yMDAgMjAyIDAgMjQgNDkKNzggMTM2IDE0OCAxMzYgMTA5IDI4MSAxNzkgNDQxIDIxNSAxMDYgMjMgMTE5IDI0IDI2MyAyMCAxMDQgLTMgMTU4IC0xMCAyMzQKLTI5eiBtLTEyODQgLTYwMyBjMTQgLTkgMTMwIC0xMjAgMjU4IC0yNDYgbDIzMyAtMjMxIDU3IDU1IGMzMSAyOSAxNDUgMTQxCjI1MyAyNDYgMTEwIDEwOSAyMDMgMTkzIDIxMyAxOTMgMjAgMCAzOSAtMTggNTIyIC00OTAgMyAtMiA3OCA2OCAxNjcgMTU2IDI3MAoyNjYgMzQ0IDMzNCAzNjIgMzM0IDEwIDAgNTcgLTM5IDEwNiAtODcgNjcgLTY1IDg5IC05MyA4OSAtMTEzIDAgLTIxIC02NiAtOTAKLTM0MSAtMzYwIC0xODggLTE4NCAtMzUwIC0zMzkgLTM2MCAtMzQ0IC0xMSAtNSAtMjcgLTUgLTM4IDAgLTExIDUgLTEyOCAxMTYKLTI2MSAyNDcgLTEzMyAxMzEgLTI0NSAyMzYgLTI0OCAyMzIgLTEzNiAtMTM4IC00OTAgLTQ3MyAtNTA4IC00NzkgLTI4IC0xMQotMTYgLTIyIC00NTEgNDA2IC0yMDMgMTk5IC0yODMgMjg0IC0yODMgMzAwIDAgMjggMTcxIDE5OCAyMDAgMTk4IDMgMCAxNyAtNwozMCAtMTd6Ii8+CjwvZz4KPC9zdmc+Cg==' -export interface Web3ModalOps extends Web3ModalOptions { +export interface Web3ModalConfig extends Web3ModalOptions { metadata: Metadata } -export interface Web3ModalAdapterInterface extends Omit { +export interface Web3ModalAdapterInterface + extends Omit, 'connect'> { connect: ( provider?: ProviderInterface, - ops?: Web3ModalOps | object + config?: Web3ModalConfig | object ) => Promise } @@ -35,12 +38,13 @@ interface Chain { let modal: Web3ModalType let currentNetwork: Chain let clickedAnyWallet = false +let walletProvider: BaseMessageSignerWalletAdapter | undefined let connectRejectMethod: (reason?: any) => void let connectResolveMethod: ( value: BaseMessageSignerWalletAdapter | PromiseLike ) => void -const web3Modal = (ops: Web3ModalOps): Web3ModalType => { +const web3Modal = (config: Web3ModalConfig): Web3ModalType => { if (modal !== undefined) { return modal } @@ -49,19 +53,19 @@ const web3Modal = (ops: Web3ModalOps): Web3ModalType => { const solanaConfig = defaultSolanaConfig({ chains, - projectId: ops.projectId, - metadata: ops.metadata + projectId: config.projectId, + metadata: config.metadata }) modal = createWeb3Modal({ chains, solanaConfig, - metadata: ops.metadata, - projectId: ops.projectId, - themeMode: ops.themeMode, + metadata: config.metadata, + projectId: config.projectId, + themeMode: config.themeMode, allowUnsupportedChain: true, defaultChain: currentNetwork, - customWallets: ops.customWallets, + customWallets: config.customWallets, themeVariables: { '--w3m-z-index': 999999999999 } @@ -103,7 +107,7 @@ const web3Modal = (ops: Web3ModalOps): Web3ModalType => { } // @ts-expect-error this provider methods enought for our needs - connectResolveMethod(ctx.provider as BaseMessageSignerWalletAdapter) + connectResolveMethod((walletProvider = ctx.provider as BaseMessageSignerWalletAdapter)) }) return modal @@ -113,6 +117,7 @@ const Web3Modal: Web3ModalAdapterInterface = { icon, id: 'web3modal', name: 'Web3Modal', + provider: walletProvider, platforms: [WalletPlatformEnum.UNIVERSAL], isDetected: () => true, isConnected: () => { @@ -144,23 +149,23 @@ const Web3Modal: Web3ModalAdapterInterface = { }, connect: async ( provider?: ProviderInterface, - _ops?: Web3ModalOps | object + _config?: Web3ModalConfig | object ): Promise => { - const ops = _ops as Web3ModalOps + const config = _config as Web3ModalConfig if (provider === undefined) { throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) } - if (ops === undefined) { - throw new Error(ErrorTypeEnum.OPS_IS_REQUIRED) + if (config === undefined) { + throw new Error(ErrorTypeEnum.CONFIG_IS_REQUIRED) } - if (ops.projectId === undefined) { + if (config.projectId === undefined) { throw new Error(ErrorTypeEnum.PROJECT_ID_IS_REQUIRED) } - if (ops.metadata === undefined) { + if (config.metadata === undefined) { throw new Error(ErrorTypeEnum.METADATA_IS_REQUIRED) } @@ -168,7 +173,7 @@ const Web3Modal: Web3ModalAdapterInterface = { return await new Promise((resolve, reject) => { try { - const modal = web3Modal(ops) + const modal = web3Modal(config) connectRejectMethod = async (reason) => { modal.disconnect() reject(reason) diff --git a/packages/networks/solana/src/browser/index.ts b/packages/networks/solana/src/browser/index.ts index 787e9bd..6439a63 100644 --- a/packages/networks/solana/src/browser/index.ts +++ b/packages/networks/solana/src/browser/index.ts @@ -1,4 +1,5 @@ -import { Wallet } from './Wallet.ts' +import type { Provider } from '../services/Provider.ts' +import { Wallet, type WalletAdapter } from './Wallet.ts' import * as adapterList from './adapters/index.ts' import type { WalletAdapterListType, @@ -6,9 +7,11 @@ import type { RegisterWalletAdapterType } from '@multiplechain/types' -const adapters: WalletAdapterListType = {} +const adapters: WalletAdapterListType = {} -const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => { +const registerAdapter: RegisterWalletAdapterType = ( + adapter: WalletAdapterInterface +): void => { if (Object.values(adapters).find((a) => a.id === adapter.id) !== undefined) { throw new Error(`Adapter with id ${adapter.id} already exists`) } diff --git a/packages/networks/solana/src/models/CoinTransaction.ts b/packages/networks/solana/src/models/CoinTransaction.ts index 1adc260..1e3ce68 100644 --- a/packages/networks/solana/src/models/CoinTransaction.ts +++ b/packages/networks/solana/src/models/CoinTransaction.ts @@ -1,14 +1,20 @@ import { fromLamports } from '../utils.ts' import { Transaction } from './Transaction.ts' -import type { ParsedInstruction } from '@solana/web3.js' -import { TransactionStatusEnum } from '@multiplechain/types' -import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types' +import type { ParsedInstruction, ParsedTransactionWithMeta } from '@solana/web3.js' +import { + AssetDirectionEnum, + TransactionStatusEnum, + type WalletAddress, + type CoinTransactionInterface, + type TransferAmount +} from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** + * @param {ParsedTransactionWithMeta} data Transaction data * @returns {Promise} Wallet address of the receiver of transaction */ - findTransferInstruction(data: any): ParsedInstruction | null { + findTransferInstruction(data: ParsedTransactionWithMeta): ParsedInstruction | null { return ( (data.transaction.message.instructions.find((instruction: any): boolean => { return ( @@ -21,9 +27,9 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter } /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() if (data === null) { return '' @@ -35,9 +41,9 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const data = await this.getData() if (data === null) { return '' @@ -47,9 +53,9 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter } /** - * @returns {Promise} Amount of coin that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const data = await this.getData() if (data === null) { return 0 @@ -62,14 +68,14 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/solana/src/models/ContractTransaction.ts b/packages/networks/solana/src/models/ContractTransaction.ts index 4fd88e2..dc0d07d 100644 --- a/packages/networks/solana/src/models/ContractTransaction.ts +++ b/packages/networks/solana/src/models/ContractTransaction.ts @@ -1,20 +1,21 @@ import { Transaction } from './Transaction.ts' -import type { ParsedInstruction } from '@solana/web3.js' -import type { ContractTransactionInterface } from '@multiplechain/types' +import type { ParsedInstruction, ParsedTransactionWithMeta } from '@solana/web3.js' +import type { ContractAddress, ContractTransactionInterface } from '@multiplechain/types' export class ContractTransaction extends Transaction implements ContractTransactionInterface { /** + * @param {ParsedTransactionWithMeta} data Transaction data * @returns {Promise} Wallet address of the receiver of transaction */ - findTransferInstruction(data: any): ParsedInstruction | null { + findTransferInstruction(data: ParsedTransactionWithMeta): ParsedInstruction | null { const length = data.transaction.message.instructions.length return data.transaction.message.instructions[length - 1] as ParsedInstruction } /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { const data = await this.getData() if (data === null) { return '' diff --git a/packages/networks/solana/src/models/NftTransaction.ts b/packages/networks/solana/src/models/NftTransaction.ts index 31de29f..804da91 100644 --- a/packages/networks/solana/src/models/NftTransaction.ts +++ b/packages/networks/solana/src/models/NftTransaction.ts @@ -1,13 +1,19 @@ -import type { ParsedInstruction } from '@solana/web3.js' -import { TransactionStatusEnum } from '@multiplechain/types' import { ContractTransaction } from './ContractTransaction.ts' -import { type NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { ParsedInstruction, ParsedTransactionWithMeta } from '@solana/web3.js' +import type { + NftId, + WalletAddress, + ContractAddress, + NftTransactionInterface +} from '@multiplechain/types' export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** + * @param {ParsedTransactionWithMeta} data Transaction data * @returns {Promise} Wallet address of the receiver of transaction */ - findTransferInstruction(data: any): ParsedInstruction | null { + findTransferInstruction(data: ParsedTransactionWithMeta): ParsedInstruction | null { return ( (data.transaction.message.instructions.find((instruction: any): boolean => { return ( @@ -20,9 +26,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { const data = await this.getData() if (data === null) { return '' @@ -46,9 +52,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} Receiver wallet address + * @returns {Promise} Receiver wallet address */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() if (data === null) { return '' @@ -62,9 +68,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const data = await this.getData() if (data === null) { @@ -75,9 +81,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} NFT ID + * @returns {Promise} NFT ID */ - async getNftId(): Promise { + async getNftId(): Promise { const data = await this.getData() if (data === null) { @@ -89,15 +95,15 @@ export class NftTransaction extends ContractTransaction implements NftTransactio /** * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {NftId} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - nftId: string | number + address: WalletAddress, + nftId: NftId ): Promise { const status = await this.getStatus() diff --git a/packages/networks/solana/src/models/TokenTransaction.ts b/packages/networks/solana/src/models/TokenTransaction.ts index d748020..078a0f3 100644 --- a/packages/networks/solana/src/models/TokenTransaction.ts +++ b/packages/networks/solana/src/models/TokenTransaction.ts @@ -1,15 +1,25 @@ import { PublicKey } from '@solana/web3.js' import { math } from '@multiplechain/utils' -import { TransactionStatusEnum } from '@multiplechain/types' import { ContractTransaction } from './ContractTransaction.ts' -import type { ParsedAccountData, ParsedInstruction } from '@solana/web3.js' -import { AssetDirectionEnum, type TokenTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { + ParsedAccountData, + ParsedInstruction, + ParsedTransactionWithMeta +} from '@solana/web3.js' +import type { + ContractAddress, + TokenTransactionInterface, + TransferAmount, + WalletAddress +} from '@multiplechain/types' export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** + * @param {ParsedTransactionWithMeta} data Transaction data * @returns {Promise} Wallet address of the receiver of transaction */ - findTransferInstruction(data: any): ParsedInstruction | null { + findTransferInstruction(data: ParsedTransactionWithMeta): ParsedInstruction | null { return ( (data.transaction.message.instructions.find((instruction: any): boolean => { return ( @@ -22,9 +32,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { const data = await this.getData() if (data === null) { return '' @@ -48,9 +58,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() if (data === null) { return '' @@ -65,9 +75,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const data = await this.getData() if (data === null) { return '' @@ -77,9 +87,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Amount of tokens that will be transferred + * @returns {Promise} Amount of tokens that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const data = await this.getData() if (data === null) { return 0 @@ -104,14 +114,14 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa /** * @param {AssetDirectionEnum} direction - Direction of the transaction (token) - * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param {number} amount Amount of tokens that will be approved + * @param {WalletAddress} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of tokens that will be approved * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/solana/src/models/Transaction.ts b/packages/networks/solana/src/models/Transaction.ts index 6daa7cd..b1c7ef1 100644 --- a/packages/networks/solana/src/models/Transaction.ts +++ b/packages/networks/solana/src/models/Transaction.ts @@ -1,33 +1,50 @@ import { fromLamports } from '../utils.ts' import { Provider } from '../services/Provider.ts' -import type { TransactionInterface } from '@multiplechain/types' -import type { ParsedTransactionWithMeta } from '@solana/web3.js' import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types' - -export class Transaction implements TransactionInterface { +import { + SystemProgram, + type ParsedInstruction, + type ParsedTransactionWithMeta +} from '@solana/web3.js' +import { + type BlockTimestamp, + type BlockNumber, + type TransactionFee, + type TransactionId, + type TransactionInterface, + type WalletAddress, + type BlockConfirmationCount, + TransactionTypeEnum +} from '@multiplechain/types' +import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token' + +export class Transaction implements TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId /** * Blockchain network provider */ provider: Provider + /** + * Transaction data + */ data: ParsedTransactionWithMeta | null = null /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider */ - constructor(id: string, provider?: Provider) { + constructor(id: TransactionId, provider?: Provider) { this.id = id this.provider = provider ?? Provider.instance } /** - * @returns {Promise} Transaction data + * @returns {Promise} Transaction data */ async getData(): Promise { if (this.data !== null) { @@ -86,12 +103,53 @@ export class Transaction implements TransactionInterface { } /** - * @returns {string} Transaction ID + * @returns {TransactionId} Transaction ID */ - getId(): string { + getId(): TransactionId { return this.id } + /** + * @returns {Promise} Type of the transaction + */ + async getType(): Promise { + const data = await this.getData() + + if (data === null) { + return TransactionTypeEnum.GENERAL + } + + const instructions = data.transaction.message.instructions as ParsedInstruction[] + + return await new Promise((resolve) => { + instructions.forEach((instruction) => { + if ( + instruction.programId.equals(SystemProgram.programId) && + (instruction.parsed.type === 'createAccount' || + instruction.parsed.type === 'transfer') + ) { + resolve(TransactionTypeEnum.COIN) + } else if (instruction.programId.equals(TOKEN_2022_PROGRAM_ID)) { + resolve(TransactionTypeEnum.TOKEN) + } else if (instruction.programId.equals(TOKEN_PROGRAM_ID)) { + const postBalance = data.meta?.postTokenBalances?.find( + (balance: any): boolean => { + return balance.mint !== undefined + } + ) + + if (postBalance?.uiTokenAmount.decimals === 0) { + resolve(TransactionTypeEnum.NFT) + } else { + resolve(TransactionTypeEnum.TOKEN) + } + } else { + resolve(TransactionTypeEnum.CONTRACT) + } + }) + }) + } + /** * @returns {string} Transaction URL */ @@ -103,9 +161,9 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSigner(): Promise { + async getSigner(): Promise { const data = await this.getData() return ( data?.transaction?.message?.accountKeys @@ -117,33 +175,33 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Transaction fee + * @returns {Promise} Transaction fee */ - async getFee(): Promise { + async getFee(): Promise { const data = await this.getData() return fromLamports(data?.meta?.fee ?? 0) } /** - * @returns {Promise} Block number that transaction + * @returns {Promise} Block number that transaction */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { const data = await this.getData() return data?.slot ?? 0 } /** - * @returns {Promise} Block timestamp that transaction + * @returns {Promise} Block timestamp that transaction */ - async getBlockTimestamp(): Promise { + async getBlockTimestamp(): Promise { const data = await this.getData() return data?.blockTime ?? 0 } /** - * @returns {Promise} Confirmation count of the block + * @returns {Promise} Confirmation count of the block */ - async getBlockConfirmationCount(): Promise { + async getBlockConfirmationCount(): Promise { const data = await this.getData() const currentSlot = await this.provider.web3.getSlot() return currentSlot - (data?.slot ?? 0) diff --git a/packages/networks/solana/src/services/Provider.ts b/packages/networks/solana/src/services/Provider.ts index 717de6c..7d2e92b 100644 --- a/packages/networks/solana/src/services/Provider.ts +++ b/packages/networks/solana/src/services/Provider.ts @@ -41,6 +41,11 @@ export class Provider implements ProviderInterface { } } + /** + * Web3 connection + */ + web3: Connection + /** * Node information */ @@ -51,8 +56,6 @@ export class Provider implements ProviderInterface { */ private static _instance: Provider - web3: Connection - /** * @param network - Network configuration of the provider */ @@ -127,7 +130,8 @@ export class Provider implements ProviderInterface { /** * Update network configuration of the provider - * @param network - Network configuration of the provider + * @param {NetworkConfigInterface} network - Network configuration of the provider + * @returns {void} */ update(network: NetworkConfigInterface): void { this.network = network @@ -142,7 +146,7 @@ export class Provider implements ProviderInterface { /** * Get the current network configuration is testnet or not - * @returns boolean + * @returns {boolean} Testnet status */ isTestnet(): boolean { return this.network?.testnet ?? false diff --git a/packages/networks/solana/src/services/TransactionListener.ts b/packages/networks/solana/src/services/TransactionListener.ts index b3c1a7c..7dac297 100644 --- a/packages/networks/solana/src/services/TransactionListener.ts +++ b/packages/networks/solana/src/services/TransactionListener.ts @@ -18,7 +18,12 @@ import { import type { DynamicTransactionType, TransactionListenerInterface, - DynamicTransactionListenerFilterType + DynamicTransactionListenerFilterType, + TransactionId, + WalletAddress, + ContractAddress, + TransferAmount, + NftId } from '@multiplechain/types' import { ErrorTypeEnum, @@ -51,16 +56,6 @@ export class TransactionListener< */ type: T - /** - * Transaction listener callback - */ - callbacks: CallBackType[] = [] - - /** - * Transaction listener filter - */ - filter?: DynamicTransactionListenerFilterType | Record - /** * Provider */ @@ -76,6 +71,11 @@ export class TransactionListener< */ connected: boolean = false + /** + * Transaction listener callback + */ + callbacks: CallBackType[] = [] + /** * Dynamic stop method */ @@ -84,7 +84,12 @@ export class TransactionListener< /** * Triggered transactions */ - triggeredTransactions: string[] = [] + triggeredTransactions: TransactionId[] = [] + + /** + * Transaction listener filter + */ + filter?: DynamicTransactionListenerFilterType | Record /** * @param {T} type - Transaction type @@ -246,8 +251,8 @@ export class TransactionListener< } interface ParamsType { - signer?: string - address?: string + signer?: WalletAddress + address?: ContractAddress } const expectedParams: ParamsType = {} @@ -307,10 +312,10 @@ export class TransactionListener< } interface ParamsType { - signer?: string - sender?: string - receiver?: string - amount?: number + signer?: WalletAddress + sender?: WalletAddress + receiver?: WalletAddress + amount?: TransferAmount } const expectedParams: ParamsType = {} @@ -367,12 +372,12 @@ export class TransactionListener< : (_transaction as NftTransaction) interface ParamsType { - signer?: string - sender?: string - receiver?: string - address?: string - amount?: number - nftId?: number | string + signer?: WalletAddress + sender?: WalletAddress + receiver?: WalletAddress + address?: ContractAddress + amount?: TransferAmount + nftId?: NftId } const expectedParams: ParamsType = {} @@ -475,7 +480,11 @@ export class TransactionListener< const instruction = transaction.findTransferInstruction(data) - if (instruction === null || instruction.parsed?.info?.tokenAmount?.decimals === 0) { + if ( + instruction === null || + (Number(instruction.parsed?.info?.amount) === 1 && + instruction.parsed?.info?.tokenAmount?.decimals === 0) + ) { return } @@ -517,7 +526,11 @@ export class TransactionListener< const instruction = transaction.findTransferInstruction(data) - if (instruction === null || instruction.parsed?.info?.tokenAmount?.decimals !== 0) { + if ( + instruction === null || + (Number(instruction.parsed?.info?.amount) !== 1 && + instruction.parsed?.info?.tokenAmount?.decimals !== 0) + ) { return } diff --git a/packages/networks/solana/src/services/TransactionSigner.ts b/packages/networks/solana/src/services/TransactionSigner.ts index 5e01680..ea9e85e 100644 --- a/packages/networks/solana/src/services/TransactionSigner.ts +++ b/packages/networks/solana/src/services/TransactionSigner.ts @@ -1,15 +1,13 @@ import { Provider } from '../services/Provider.ts' import { base58Decode } from '@multiplechain/utils' -import { Transaction } from '../models/Transaction.ts' -import { NftTransaction } from '../models/NftTransaction.ts' -import { CoinTransaction } from '../models/CoinTransaction.ts' -import { TokenTransaction } from '../models/TokenTransaction.ts' -import type { TransactionSignerInterface } from '@multiplechain/types' +import type { PrivateKey, TransactionId, TransactionSignerInterface } from '@multiplechain/types' import { Keypair, VersionedTransaction, Transaction as RawTransaction } from '@solana/web3.js' type SignedTransaction = Buffer | Uint8Array -export class TransactionSigner implements TransactionSignerInterface { +export class TransactionSigner + implements TransactionSignerInterface +{ /** * Transaction data from the blockchain network */ @@ -27,6 +25,7 @@ export class TransactionSigner implements TransactionSignerInterface { /** * @param {RawTransaction} rawData - Transaction data + * @param {Provider} provider - Blockchain network provider */ constructor(rawData: RawTransaction, provider?: Provider) { this.rawData = rawData @@ -35,10 +34,10 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Sign the transaction - * @param {string} privateKey - Transaction data - * @returns {Promise} Signed transaction data + * @param {PrivateKey} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - async sign(privateKey: string): Promise { + async sign(privateKey: PrivateKey): Promise { this.rawData.recentBlockhash = ( await this.provider.web3.getLatestBlockhash('finalized') ).blockhash @@ -63,8 +62,8 @@ export class TransactionSigner implements TransactionSignerInterface { } /** - * Get the raw transaction data - * @returns Transaction data + * @param {string} encodedTransaction - Encoded transaction + * @returns {RawTransaction | VersionedTransaction} Transaction data */ private getRawTransaction(encodedTransaction: string): RawTransaction | VersionedTransaction { let recoveredTransaction: RawTransaction | VersionedTransaction @@ -80,10 +79,10 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Send the transaction to the blockchain network - * @returns {Promise} + * @returns {Promise} */ - async send(): Promise { - return new Transaction(await this.provider.web3.sendRawTransaction(this.signedData)) + async send(): Promise { + return await this.provider.web3.sendRawTransaction(this.signedData) } /** @@ -102,33 +101,3 @@ export class TransactionSigner implements TransactionSignerInterface { return this.signedData } } - -export class CoinTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new CoinTransaction((await super.send()).getId()) - } -} - -export class TokenTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new TokenTransaction((await super.send()).getId()) - } -} - -export class NftTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new NftTransaction((await super.send()).getId()) - } -} diff --git a/packages/networks/solana/tests/assets.spec.ts b/packages/networks/solana/tests/assets.spec.ts index 680d8de..b48cf70 100644 --- a/packages/networks/solana/tests/assets.spec.ts +++ b/packages/networks/solana/tests/assets.spec.ts @@ -5,8 +5,8 @@ import { Coin } from '../src/assets/Coin.ts' import { Token } from '../src/assets/Token.ts' import { math } from '@multiplechain/utils' import { Transaction } from '../src/models/Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' import { TransactionSigner } from '../src/services/TransactionSigner.ts' +import { TransactionStatusEnum, type PrivateKey, type TransactionId } from '@multiplechain/types' const coinBalanceTestAmount = Number(process.env.SOL_COIN_BALANCE_TEST_AMOUNT) const tokenBalanceTestAmount = Number(process.env.SOL_TOKEN_BALANCE_TEST_AMOUNT) @@ -39,7 +39,7 @@ const waitSecondsBeforeThanNewTx = async (seconds: number): Promise => { return await new Promise((resolve) => setTimeout(resolve, seconds * 1000)) } -const checkSigner = async (signer: TransactionSigner, privateKey?: string): Promise => { +const checkSigner = async (signer: TransactionSigner, privateKey?: PrivateKey): Promise => { expect(signer).toBeInstanceOf(TransactionSigner) const rawData = signer.getRawData() @@ -51,8 +51,8 @@ const checkSigner = async (signer: TransactionSigner, privateKey?: string): Prom expect(signer.getSignedData()).toBeInstanceOf(Buffer) } -const checkTx = async (transaction: Transaction): Promise => { - expect(transaction).toBeInstanceOf(Transaction) +const checkTx = async (transactionId: TransactionId): Promise => { + const transaction = new Transaction(transactionId) const status = await transaction.wait(10 * 1000) expect(status).toBe(TransactionStatusEnum.CONFIRMED) } diff --git a/packages/networks/solana/tests/services.spec.ts b/packages/networks/solana/tests/services.spec.ts index e7aa9f2..113fd07 100644 --- a/packages/networks/solana/tests/services.spec.ts +++ b/packages/networks/solana/tests/services.spec.ts @@ -205,8 +205,8 @@ describe('Transaction Listener', () => { const transaction = await waitListenerEvent() expect(transaction).toBeInstanceOf(NftTransaction) - await transaction.wait() - await waitSecondsBeforeThanNewTx(10) + await (transaction as NftTransaction).wait() + await waitSecondsBeforeThanNewTx(20) const newSigner = await nft.transfer( receiverTestAddress, @@ -214,6 +214,6 @@ describe('Transaction Listener', () => { nftTestAddress2 ) - void (await newSigner.sign(receiverPrivateKey)).send() + await (await newSigner.sign(receiverPrivateKey)).send() }) }) diff --git a/packages/networks/tron/README.md b/packages/networks/tron/README.md index e69de29..d746267 100644 --- a/packages/networks/tron/README.md +++ b/packages/networks/tron/README.md @@ -0,0 +1,7 @@ +# MultipleChain standard for JavaScript + +## Introduction + +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. + +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/networks/tron/index.html b/packages/networks/tron/index.example.html similarity index 99% rename from packages/networks/tron/index.html rename to packages/networks/tron/index.example.html index 4902d75..b291d72 100644 --- a/packages/networks/tron/index.html +++ b/packages/networks/tron/index.example.html @@ -229,7 +229,7 @@ } const wallet = new Tron.browser.Wallet(adapter) - const adapterProvider = await wallet.connect(provider, { + const adapterProvider = await wallet.connect({ projectId: '113d9f5689edd84ff230c2a6d679c80c' }) diff --git a/packages/networks/tron/package.json b/packages/networks/tron/package.json index 6b4cce9..1a07a64 100644 --- a/packages/networks/tron/package.json +++ b/packages/networks/tron/package.json @@ -27,6 +27,16 @@ "types": "./dist/browser/index.d.ts" } }, + "typesVersions": { + "*": { + "node": [ + "./dist/index.d.ts" + ], + "browser": [ + "./dist/browser/index.d.ts" + ] + } + }, "files": [ "dist", "README.md", @@ -64,8 +74,8 @@ "dependencies": { "@beycandeveloper/tron-tx-decoder": "^2.0.5", "@multiplechain/tron-walletconnect": "^0.1.0", - "@multiplechain/types": "^0.1.56", - "@multiplechain/utils": "^0.1.20", + "@multiplechain/types": "^0.1.63", + "@multiplechain/utils": "^0.1.21", "@noble/secp256k1": "^1.7.1", "@tronweb3/tronwallet-adapter-bitkeep": "^1.1.1", "@tronweb3/tronwallet-adapter-okxwallet": "^1.0.3", diff --git a/packages/networks/tron/pnpm-lock.yaml b/packages/networks/tron/pnpm-lock.yaml index 9245368..0382b6f 100644 --- a/packages/networks/tron/pnpm-lock.yaml +++ b/packages/networks/tron/pnpm-lock.yaml @@ -12,11 +12,11 @@ dependencies: specifier: ^0.1.0 version: 0.1.0(typescript@5.4.5) '@multiplechain/types': - specifier: ^0.1.56 - version: 0.1.56 + specifier: ^0.1.63 + version: 0.1.63 '@multiplechain/utils': - specifier: ^0.1.20 - version: 0.1.20 + specifier: ^0.1.21 + version: 0.1.21 '@noble/secp256k1': specifier: ^1.7.1 version: 1.7.1 @@ -348,12 +348,12 @@ packages: - vite dev: false - /@multiplechain/types@0.1.56: - resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==} + /@multiplechain/types@0.1.63: + resolution: {integrity: sha512-4C201vUsN6F1S/M7vT+GZS0wTdKGZXHqn4YmC6ouC8n0uxMCEFdI2sSCadyHKFKG5OBPo16X1oFCX9MVwPqMoA==} dev: false - /@multiplechain/utils@0.1.20: - resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==} + /@multiplechain/utils@0.1.21: + resolution: {integrity: sha512-4mRlnhvXcS+7Hb1By5s2eoCH02kqOSQzV8qj0DHqZK20FtxtduaoGCtcClrFgEjN/HAXnHgdDc1pxTpveIkvRQ==} dependencies: '@types/ws': 8.5.10 bignumber.js: 9.1.2 diff --git a/packages/networks/tron/src/assets/Coin.ts b/packages/networks/tron/src/assets/Coin.ts index 5e791ce..2b4bb7f 100644 --- a/packages/networks/tron/src/assets/Coin.ts +++ b/packages/networks/tron/src/assets/Coin.ts @@ -1,8 +1,13 @@ import { Provider } from '../services/Provider.ts' -import { CoinTransactionSigner, type TransactionData } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type CoinInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' +import { TransactionSigner, type TransactionData } from '../services/TransactionSigner.ts' -export class Coin implements CoinInterface { +export class Coin implements CoinInterface { /** * Blockchain network provider */ @@ -37,25 +42,25 @@ export class Coin implements CoinInterface { } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of COIN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const balance = await this.provider.tronWeb.trx.getBalance(owner) return parseFloat(this.provider.tronWeb.fromSun(balance) as unknown as string) } /** - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred - * @returns {Promise} Transaction signer + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -70,7 +75,7 @@ export class Coin implements CoinInterface { const sunFormat = this.provider.tronWeb.toSun(amount) - return new CoinTransactionSigner( + return new TransactionSigner( (await this.provider.tronWeb.transactionBuilder.sendTrx( receiver, sunFormat, diff --git a/packages/networks/tron/src/assets/Contract.ts b/packages/networks/tron/src/assets/Contract.ts index 500dd3d..d4dd071 100644 --- a/packages/networks/tron/src/assets/Contract.ts +++ b/packages/networks/tron/src/assets/Contract.ts @@ -1,5 +1,5 @@ import { Provider } from '../services/Provider.ts' -import type { ContractInterface } from '@multiplechain/types' +import type { ContractAddress, ContractInterface, WalletAddress } from '@multiplechain/types' import type { TronWeb } from '../services/TronWeb.ts' interface InputOutputInterface { @@ -48,7 +48,7 @@ export class Contract implements ContractInterface { /** * Contract address */ - address: string + address: ContractAddress /** * Blockchain network provider @@ -71,11 +71,11 @@ export class Contract implements ContractInterface { tronContract: TronContract /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { this.ABI = ABI ?? [] this.address = address this.provider = provider ?? Provider.instance @@ -92,31 +92,52 @@ export class Contract implements ContractInterface { } /** - * @returns {string} Contract address + * @returns {ContractAddress} Contract address */ - getAddress(): string { + getAddress(): ContractAddress { return this.address } /** * @param {string} method Method name - * @param {any[]} args Method parameters - * @returns {Promise} Method result + * @param {unknown[]} args Method parameters + * @returns {Promise} Method result */ - async callMethod(method: string, ...args: any[]): Promise { + async callMethod(method: string, ...args: unknown[]): Promise { await this.setTronContract() return this.tronContract[method](...args).call() // eslint-disable-line } /** * @param {string} _method Method name - * @param {any[]} _args Sender wallet address - * @returns {Promise} Encoded method data + * @param {unknown[]} _args Sender wallet address + * @returns {Promise} Encoded method data */ - async getMethodData(_method: string, ..._args: any[]): Promise { + async getMethodData(_method: string, ..._args: unknown[]): Promise { throw new Error('Method not implemented.') } + /** + * @param {string} _function Method name + * @param {any} parameters Method parameters + * @param {WalletAddress} from Sender wallet address + */ + async getEstimateEnergy( + _function: string, + parameters: any, + from: WalletAddress + ): Promise { + const res = await this.provider.tronWeb.transactionBuilder.estimateEnergy( + this.address, + _function, + {}, + parameters, + from + ) + + return res.energy_required ?? 0 + } + /** * @param {string} method Method name * @returns {string} Method output @@ -139,11 +160,16 @@ export class Contract implements ContractInterface { } } - generateParameters(method: string, ...args: any[]): any { + /** + * @param {string} method Method name + * @param {unknown[]} args Method parameters + * @returns {any[]} Method parameters + */ + generateParameters(method: string, ...args: unknown[]): any { const matchedItem = this.ABI.find((func: FunctionInterface) => func.name === method) if (matchedItem !== undefined) { const inputs = matchedItem.inputs ?? [] - const parameters = [] as any[] + const parameters = [] as unknown[] inputs.forEach((input, index) => { parameters.push({ type: input.type, @@ -158,20 +184,22 @@ export class Contract implements ContractInterface { /** * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters + * @param {WalletAddress} from Sender wallet address + * @param {unknown[]} args Method parameters * @returns {Promise} Encoded method data */ async createTransactionData( method: string, - from: string, - ...args: any[] + from: WalletAddress, + ...args: unknown[] ): Promise { + const _function = this.generateFunction(method) + const parameters = this.generateParameters(method, ...args) return { address: this.address, - method: this.generateFunction(method), + method: _function, options: {}, - parameters: this.generateParameters(method, ...args), // eslint-disable-line + parameters, from } } diff --git a/packages/networks/tron/src/assets/NFT.ts b/packages/networks/tron/src/assets/NFT.ts index f2fb446..e334a80 100644 --- a/packages/networks/tron/src/assets/NFT.ts +++ b/packages/networks/tron/src/assets/NFT.ts @@ -1,16 +1,22 @@ import TRC721 from '../../resources/TRC721.json' import type { Provider } from '../services/Provider.ts' import { Contract, type InterfaceAbi } from './Contract.ts' -import { NftTransactionSigner } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type NftInterface } from '@multiplechain/types' - -export class NFT extends Contract implements NftInterface { +import { TransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type ContractAddress, + type NftId, + type NftInterface, + type WalletAddress +} from '@multiplechain/types' + +export class NFT extends Contract implements NftInterface { /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { super(address, provider, ABI ?? TRC721) } @@ -18,45 +24,45 @@ export class NFT extends Contract implements NftInterface { * @returns {Promise} NFT name */ async getName(): Promise { - return await this.callMethod('name') + return (await this.callMethod('name')) as string } /** * @returns {Promise} NFT symbol */ async getSymbol(): Promise { - return await this.callMethod('symbol') + return (await this.callMethod('symbol')) as string } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of NFT */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { return Number(await this.callMethod('balanceOf', owner)) } /** - * @param {number | string} nftId NFT ID - * @returns {Promise} Wallet address of the owner of the NFT + * @param {NftId} nftId NFT ID + * @returns {Promise} Wallet address of the owner of the NFT */ - async getOwner(nftId: number | string): Promise { + async getOwner(nftId: NftId): Promise { return this.provider.tronWeb.address.fromHex(await this.callMethod('ownerOf', nftId)) } /** - * @param {number | string} nftId NFT ID + * @param {NftId} nftId NFT ID * @returns {Promise} URI of the NFT */ - async getTokenURI(nftId: number | string): Promise { - return await this.callMethod('tokenURI', nftId) + async getTokenURI(nftId: NftId): Promise { + return (await this.callMethod('tokenURI', nftId)) as string } /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Wallet address of the approved spender + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Wallet address of the approved spender */ - async getApproved(nftId: number | string): Promise { + async getApproved(nftId: NftId): Promise { const address = await this.callMethod('getApproved', nftId) if (address !== '410000000000000000000000000000000000000000') { return this.provider.tronWeb.address.fromHex(address) @@ -65,32 +71,32 @@ export class NFT extends Contract implements NftInterface { } /** - * @param {string} sender Sender address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID + * @param {WalletAddress} sender Sender address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - nftId: number | string - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { return await this.transferFrom(sender, sender, receiver, nftId) } /** - * @param {string} spender Spender address - * @param {string} owner Owner address - * @param {string} receiver Receiver address - * @param {number | string} nftId NFT ID + * @param {WalletAddress} spender Spender address + * @param {WalletAddress} owner Owner address + * @param {WalletAddress} receiver Receiver address + * @param {NftId} nftId NFT ID * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ): Promise { const balance = await this.getBalance(owner) if (balance <= 0) { @@ -127,21 +133,21 @@ export class NFT extends Contract implements NftInterface { throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) } - return new NftTransactionSigner(result) + return new TransactionSigner(result) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {NftId} nftId ID of the NFT that will be transferred * @returns {Promise} Transaction signer */ async approve( - owner: string, - spender: string, - nftId: number | string - ): Promise { + owner: WalletAddress, + spender: WalletAddress, + nftId: NftId + ): Promise { // Check if tokens exist const balance = await this.getBalance(owner) @@ -164,6 +170,6 @@ export class NFT extends Contract implements NftInterface { throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) } - return new NftTransactionSigner(result) + return new TransactionSigner(result) } } diff --git a/packages/networks/tron/src/assets/Token.ts b/packages/networks/tron/src/assets/Token.ts index 975ae51..8823a4f 100644 --- a/packages/networks/tron/src/assets/Token.ts +++ b/packages/networks/tron/src/assets/Token.ts @@ -1,17 +1,23 @@ import { Contract, type InterfaceAbi } from './Contract.ts' -import { TokenTransactionSigner } from '../services/TransactionSigner.ts' -import { ErrorTypeEnum, type TokenInterface } from '@multiplechain/types' +import { TransactionSigner } from '../services/TransactionSigner.ts' +import { + ErrorTypeEnum, + type ContractAddress, + type TokenInterface, + type TransferAmount, + type WalletAddress +} from '@multiplechain/types' import { hexToNumber, numberToHex } from '@multiplechain/utils' import type { Provider } from '../services/Provider.ts' import TRC20 from '../../resources/TRC20.json' -export class Token extends Contract implements TokenInterface { +export class Token extends Contract implements TokenInterface { /** - * @param {string} address Contract address + * @param {ContractAddress} address Contract address * @param {Provider} provider Blockchain network provider * @param {InterfaceAbi} ABI Contract ABI */ - constructor(address: string, provider?: Provider, ABI?: InterfaceAbi) { + constructor(address: ContractAddress, provider?: Provider, ABI?: InterfaceAbi) { super(address, provider, ABI ?? TRC20) } @@ -19,28 +25,28 @@ export class Token extends Contract implements TokenInterface { * @returns {Promise} Token name */ async getName(): Promise { - return await this.callMethod('name') + return (await this.callMethod('name')) as string } /** * @returns {Promise} Token symbol */ async getSymbol(): Promise { - return await this.callMethod('symbol') + return (await this.callMethod('symbol')) as string } /** * @returns {Promise} Decimal value of the token */ async getDecimals(): Promise { - return await this.callMethod('decimals') + return (await this.callMethod('decimals')) as number } /** - * @param {string} owner Wallet address + * @param {WalletAddress} owner Wallet address * @returns {Promise} Wallet balance as currency of TOKEN */ - async getBalance(owner: string): Promise { + async getBalance(owner: WalletAddress): Promise { const [decimals, balance] = await Promise.all([ this.getDecimals(), this.callMethod('balanceOf', owner) @@ -60,11 +66,11 @@ export class Token extends Contract implements TokenInterface { } /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner + * @param {WalletAddress} owner Address of owner of the tokens that is being used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner * @returns {Promise} Amount of tokens that the spender is allowed to spend */ - async getAllowance(owner: string, spender: string): Promise { + async getAllowance(owner: WalletAddress, spender: WalletAddress): Promise { const [decimals, allowance] = await Promise.all([ this.getDecimals(), await this.callMethod('allowance', owner, spender) @@ -74,16 +80,16 @@ export class Token extends Contract implements TokenInterface { /** * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Transaction signer */ async transfer( - sender: string, - receiver: string, - amount: number - ): Promise { + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount <= 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -105,22 +111,22 @@ export class Token extends Contract implements TokenInterface { throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) } - return new TokenTransactionSigner(result) + return new TransactionSigner(result) } /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of tokens that will be transferred * @returns {Promise} Transaction signer */ async transferFrom( - spender: string, - owner: string, - receiver: string, - amount: number - ): Promise { + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -159,17 +165,21 @@ export class Token extends Contract implements TokenInterface { throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) } - return new TokenTransactionSigner(result) + return new TransactionSigner(result) } /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number} amount Amount of the tokens that will be used + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {TransferAmount} amount Amount of the tokens that will be used * @returns {Promise} Transaction signer */ - async approve(owner: string, spender: string, amount: number): Promise { + async approve( + owner: WalletAddress, + spender: WalletAddress, + amount: TransferAmount + ): Promise { if (amount < 0) { throw new Error(ErrorTypeEnum.INVALID_AMOUNT) } @@ -191,6 +201,6 @@ export class Token extends Contract implements TokenInterface { throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) } - return new TokenTransactionSigner(result) + return new TransactionSigner(result) } } diff --git a/packages/networks/tron/src/browser/Wallet.ts b/packages/networks/tron/src/browser/Wallet.ts index 5cb8c47..f642975 100644 --- a/packages/networks/tron/src/browser/Wallet.ts +++ b/packages/networks/tron/src/browser/Wallet.ts @@ -2,12 +2,16 @@ import { type WalletInterface, type WalletAdapterInterface, type WalletPlatformEnum, - type TransactionSignerInterface, ErrorTypeEnum, - type ProviderInterface + type UnknownConfig, + type ConnectConfig, + type WalletAddress, + type SignedMessage, + type TransactionId } from '@multiplechain/types' import { Provider } from '../services/Provider.ts' -import type { Adapter, AdapterEvents, Transaction } from '@tronweb3/tronwallet-abstract-adapter' +import type { TransactionSigner } from '../services/TransactionSigner.ts' +import type { Adapter, AdapterEvents } from '@tronweb3/tronwallet-abstract-adapter' export interface CustomAdapter extends Adapter { network?: () => Promise @@ -45,17 +49,24 @@ const rejectMap = (error: any, reject: (a: any) => any): any => { return reject(error) } -export class Wallet implements WalletInterface { - adapter: WalletAdapterInterface +type WalletAdapter = WalletAdapterInterface & { + provider?: + | CustomAdapter + | { on: (eventName: string, callback: (...args: any[]) => void) => void } +} + +export class Wallet implements WalletInterface { + adapter: WalletAdapter walletProvider: CustomAdapter networkProvider: Provider /** - * @param {WalletAdapterInterface} adapter + * @param {WalletAdapter} adapter + * @param {Provider} provider */ - constructor(adapter: WalletAdapterInterface, provider?: Provider) { + constructor(adapter: WalletAdapter, provider?: Provider) { this.adapter = adapter this.networkProvider = provider ?? Provider.instance } @@ -97,28 +108,27 @@ export class Wallet implements WalletInterface { /** * @param {string} url - * @param {object} ops + * @param {UnknownConfig} config * @returns {string} */ - createDeepLink(url: string, ops?: object): string | null { + createDeepLink(url: string, config?: UnknownConfig): string | null { if (this.adapter.createDeepLink === undefined) { return null } - return this.adapter.createDeepLink(url, ops) + return this.adapter.createDeepLink(url, config) } /** - * @param {ProviderInterface} provider - * @param {Object} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - async connect(provider?: ProviderInterface, ops?: object): Promise { + async connect(config?: ConnectConfig): Promise { return await new Promise((resolve, reject) => { this.adapter - .connect(provider, ops) + .connect(this.networkProvider, config) .then(async (provider) => { - this.walletProvider = provider as CustomAdapter + this.walletProvider = provider if ( this.walletProvider.network !== undefined && @@ -166,16 +176,17 @@ export class Wallet implements WalletInterface { } /** - * @returns {Promise} + * @returns {Promise} */ - async getAddress(): Promise { + async getAddress(): Promise { return this.walletProvider.address ?? '' } /** * @param {string} message + * @returns {Promise} */ - async signMessage(message: string): Promise { + async signMessage(message: string): Promise { return await new Promise((resolve, reject) => { this.walletProvider .signMessage(message) @@ -183,22 +194,38 @@ export class Wallet implements WalletInterface { resolve(signature) }) .catch((error: any) => { - reject(error) + rejectMap(error, reject) }) }) } /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - async sendTransaction(transactionSigner: TransactionSignerInterface): Promise { - const signedTx = await this.walletProvider.signTransaction( - transactionSigner.getRawData() as Transaction - ) - const { transaction } = await this.networkProvider.tronWeb.trx.sendRawTransaction(signedTx) - if (transaction === undefined) throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) - return transaction.txID as string + async sendTransaction(transactionSigner: TransactionSigner): Promise { + return await new Promise((resolve, reject) => { + try { + void (async () => { + const signedTx = await this.walletProvider + .signTransaction(transactionSigner.getRawData()) + .catch((error) => rejectMap(error, reject)) + + if (signedTx === undefined) return + + const { transaction } = + await this.networkProvider.tronWeb.trx.sendRawTransaction(signedTx) + + if (transaction === undefined) { + throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) + } + + resolve(transaction.txID as string) + })() + } catch (error) { + rejectMap(error, reject) + } + }) } /** @@ -208,7 +235,7 @@ export class Wallet implements WalletInterface { */ on(eventName: string, callback: (...args: any[]) => void): void { if (this.adapter?.provider?.on !== undefined) { - this.adapter.provider.on(eventName, callback) + this.adapter.provider.on(eventName as keyof AdapterEvents, callback) } else { this.walletProvider.on(eventName as keyof AdapterEvents, callback) } diff --git a/packages/networks/tron/src/browser/adapters/BitgetWallet.ts b/packages/networks/tron/src/browser/adapters/BitgetWallet.ts index 51254a9..e600591 100644 --- a/packages/networks/tron/src/browser/adapters/BitgetWallet.ts +++ b/packages/networks/tron/src/browser/adapters/BitgetWallet.ts @@ -1,7 +1,8 @@ import type { CustomAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { BitKeepAdapter } from '@tronweb3/tronwallet-adapter-bitkeep' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' const walletProvider = new BitKeepAdapter() @@ -13,7 +14,7 @@ declare global { } } -const BitgetWallet: WalletAdapterInterface = { +const BitgetWallet: WalletAdapterInterface = { id: 'bitgetwallet', name: 'BitgetWallet', icon: walletProvider.icon, @@ -25,7 +26,7 @@ const BitgetWallet: WalletAdapterInterface = { }, isDetected: () => Boolean(window.bitkeep?.tronLink), isConnected: () => Boolean(walletProvider.connected), - connect: async (_provider?: ProviderInterface): Promise => { + connect: async (): Promise => { return await new Promise((resolve, reject) => { try { walletProvider diff --git a/packages/networks/tron/src/browser/adapters/OkxWallet.ts b/packages/networks/tron/src/browser/adapters/OkxWallet.ts index cb4d1e8..4c2aaff 100644 --- a/packages/networks/tron/src/browser/adapters/OkxWallet.ts +++ b/packages/networks/tron/src/browser/adapters/OkxWallet.ts @@ -1,11 +1,12 @@ import type { CustomAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { OkxWalletAdapter } from '@tronweb3/tronwallet-adapter-okxwallet' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' const walletProvider = new OkxWalletAdapter() -const OkxWallet: WalletAdapterInterface = { +const OkxWallet: WalletAdapterInterface = { id: 'okxwallet', name: 'OkxWallet', icon: walletProvider.icon, @@ -17,7 +18,7 @@ const OkxWallet: WalletAdapterInterface = { }, isDetected: () => Boolean(window.okxwallet?.tronLink), isConnected: () => Boolean(walletProvider.connected), - connect: async (_provider?: ProviderInterface): Promise => { + connect: async (): Promise => { return await new Promise((resolve, reject) => { try { walletProvider diff --git a/packages/networks/tron/src/browser/adapters/TokenPocket.ts b/packages/networks/tron/src/browser/adapters/TokenPocket.ts index 5d16381..9e4ee19 100644 --- a/packages/networks/tron/src/browser/adapters/TokenPocket.ts +++ b/packages/networks/tron/src/browser/adapters/TokenPocket.ts @@ -1,7 +1,8 @@ import type { CustomAdapter } from '../Wallet.ts' import { WalletPlatformEnum } from '@multiplechain/types' +import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { TokenPocketAdapter } from '@tronweb3/tronwallet-adapter-tokenpocket' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' const walletProvider = new TokenPocketAdapter() @@ -13,7 +14,7 @@ declare global { } } -const TokenPocket: WalletAdapterInterface = { +const TokenPocket: WalletAdapterInterface = { id: 'tokenpocket', name: 'TokenPocket', icon: walletProvider.icon, @@ -32,7 +33,7 @@ const TokenPocket: WalletAdapterInterface = { }, isDetected: () => Boolean(window.tokenpocket?.tron), isConnected: () => Boolean(walletProvider.connected), - connect: async (_provider?: ProviderInterface): Promise => { + connect: async (): Promise => { return await new Promise((resolve, reject) => { try { walletProvider diff --git a/packages/networks/tron/src/browser/adapters/TronLink.ts b/packages/networks/tron/src/browser/adapters/TronLink.ts index 2ce0a65..e81a8f1 100644 --- a/packages/networks/tron/src/browser/adapters/TronLink.ts +++ b/packages/networks/tron/src/browser/adapters/TronLink.ts @@ -1,13 +1,13 @@ import { sleep } from '@multiplechain/utils' import type { CustomAdapter } from '../Wallet.ts' -import { WalletPlatformEnum } from '@multiplechain/types' import type { Provider } from '../../services/Provider.ts' +import type { WalletAdapterInterface } from '@multiplechain/types' import { TronLinkAdapter } from '@tronweb3/tronwallet-adapter-tronlink' -import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types' +import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' const walletProvider = new TronLinkAdapter() -const TronLink: WalletAdapterInterface = { +const TronLink: WalletAdapterInterface = { id: 'tronlink', name: 'TronLink', icon: walletProvider.icon, @@ -27,8 +27,11 @@ const TronLink: WalletAdapterInterface = { }, isDetected: () => Boolean(window.tronLink), isConnected: () => Boolean(walletProvider.connected), - connect: async (_provider?: ProviderInterface): Promise => { - const provider = _provider as Provider + connect: async (provider?: Provider): Promise => { + if (provider === undefined) { + throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) + } + return await new Promise((resolve, reject) => { try { walletProvider diff --git a/packages/networks/tron/src/browser/adapters/WalletConnect.ts b/packages/networks/tron/src/browser/adapters/WalletConnect.ts index dc32273..4d0c0c4 100644 --- a/packages/networks/tron/src/browser/adapters/WalletConnect.ts +++ b/packages/networks/tron/src/browser/adapters/WalletConnect.ts @@ -1,21 +1,20 @@ import type { CustomAdapter } from '../Wallet.ts' +import type { Provider } from '../../services/Provider.ts' import { ErrorTypeEnum, WalletPlatformEnum } from '@multiplechain/types' import { WalletConnectAdapter } from '@multiplechain/tron-walletconnect' -import type { - ProviderInterface, - WalletAdapterInterface, - WalletConnectOps -} from '@multiplechain/types' +import type { WalletAdapterInterface, WalletConnectConfig } from '@multiplechain/types' const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJiYXNpYyIgaWQ9IkxheWVyXzEiCgkgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAzODcuNiAyMzcuNiIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9IldhbGxldENvbm5lY3RfMDAwMDAwNzM3MDMwNjM0MzgyMjA2NDI3MzAwMDAwMDI5MTc3MTc1NTIyMzY0NzI0OTZfIiBmaWxsPSIjM0I5OUZDIiBkPSJNNzkuNCw0Ni40CgljNjMuMi02MS45LDE2NS43LTYxLjksMjI4LjksMGw3LjYsNy40YzMuMiwzLjEsMy4yLDguMSwwLDExLjJsLTI2LDI1LjVjLTEuNiwxLjUtNC4xLDEuNS01LjcsMGwtMTAuNS0xMC4zCgljLTQ0LjEtNDMuMi0xMTUuNi00My4yLTE1OS43LDBsLTExLjIsMTFjLTEuNiwxLjUtNC4xLDEuNS01LjcsMEw3MSw2NS44Yy0zLjItMy4xLTMuMi04LjEsMC0xMS4yTDc5LjQsNDYuNHogTTM2Mi4xLDk5LjFsMjMuMiwyMi43CgljMy4yLDMuMSwzLjIsOC4xLDAsMTEuMkwyODAuOCwyMzUuM2MtMy4yLDMuMS04LjMsMy4xLTExLjQsMGMwLDAsMCwwLDAsMGwtNzQuMS03Mi42Yy0wLjgtMC44LTIuMS0wLjgtMi45LDBjMCwwLDAsMCwwLDAKCWwtNzQuMSw3Mi42Yy0zLjIsMy4xLTguMywzLjEtMTEuNCwwYzAsMCwwLDAsMCwwTDIuNCwxMzNjLTMuMi0zLjEtMy4yLTguMSwwLTExLjJsMjMuMi0yMi43YzMuMi0zLjEsOC4zLTMuMSwxMS40LDBsNzQuMSw3Mi42CgljMC44LDAuOCwyLjEsMC44LDIuOSwwYzAsMCwwLDAsMCwwbDc0LjEtNzIuNmMzLjItMy4xLDguMy0zLjEsMTEuNCwwYzAsMCwwLDAsMCwwbDc0LjEsNzIuNmMwLjgsMC44LDIuMSwwLjgsMi45LDBsNzQuMS03Mi42CglDMzUzLjgsOTYsMzU4LjksOTYsMzYyLjEsOTkuMXoiLz4KPC9zdmc+' let isConnected = false +let walletProvider: WalletConnectAdapter | undefined -const WalletConnect: WalletAdapterInterface = { +const WalletConnect: WalletAdapterInterface = { icon, id: 'walletconnect', name: 'WalletConnect', + provider: walletProvider, platforms: [WalletPlatformEnum.UNIVERSAL], isDetected: () => true, isConnected: () => isConnected, @@ -30,30 +29,25 @@ const WalletConnect: WalletAdapterInterface = { localStorage.removeItem('WALLETCONNECT_DEEPLINK_CHOICE') indexedDB.deleteDatabase('WALLET_CONNECT_V2_INDEXED_DB') }, - connect: async ( - provider?: ProviderInterface, - _ops?: WalletConnectOps | object - ): Promise => { + connect: async (provider?: Provider, config?: WalletConnectConfig): Promise => { return await new Promise((resolve, reject) => { - const ops = _ops as WalletConnectOps - if (provider === undefined) { throw new Error(ErrorTypeEnum.PROVIDER_IS_REQUIRED) } - if (ops === undefined) { - throw new Error(ErrorTypeEnum.OPS_IS_REQUIRED) + if (config === undefined) { + throw new Error(ErrorTypeEnum.CONFIG_IS_REQUIRED) } - if (ops.projectId === undefined) { + if (config.projectId === undefined) { throw new Error(ErrorTypeEnum.PROJECT_ID_IS_REQUIRED) } - const walletProvider = new WalletConnectAdapter({ + const walletConnect = new WalletConnectAdapter({ network: provider.isTestnet() ? 'Nile' : 'Mainnet', options: { relayUrl: 'wss://relay.walletconnect.com', - projectId: ops.projectId + projectId: config.projectId }, qrcodeModalOptions: { mobileLinks: ['trust'], @@ -65,11 +59,12 @@ const WalletConnect: WalletAdapterInterface = { }) try { - walletProvider + walletConnect .connect() .then(async () => { isConnected = true - resolve(walletProvider as CustomAdapter) + walletProvider = walletConnect + resolve(walletConnect as CustomAdapter) }) .catch((error) => { reject(error) diff --git a/packages/networks/tron/src/browser/index.ts b/packages/networks/tron/src/browser/index.ts index 787e9bd..54b38fb 100644 --- a/packages/networks/tron/src/browser/index.ts +++ b/packages/networks/tron/src/browser/index.ts @@ -1,4 +1,5 @@ -import { Wallet } from './Wallet.ts' +import type { Provider } from '../services/Provider.ts' +import { Wallet, type CustomAdapter } from './Wallet.ts' import * as adapterList from './adapters/index.ts' import type { WalletAdapterListType, @@ -6,9 +7,11 @@ import type { RegisterWalletAdapterType } from '@multiplechain/types' -const adapters: WalletAdapterListType = {} +const adapters: WalletAdapterListType = {} -const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => { +const registerAdapter: RegisterWalletAdapterType = ( + adapter: WalletAdapterInterface +): void => { if (Object.values(adapters).find((a) => a.id === adapter.id) !== undefined) { throw new Error(`Adapter with id ${adapter.id} already exists`) } diff --git a/packages/networks/tron/src/models/CoinTransaction.ts b/packages/networks/tron/src/models/CoinTransaction.ts index e90ecb9..93fb32d 100644 --- a/packages/networks/tron/src/models/CoinTransaction.ts +++ b/packages/networks/tron/src/models/CoinTransaction.ts @@ -1,12 +1,12 @@ import { Transaction } from './Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { WalletAddress, CoinTransactionInterface, TransferAmount } from '@multiplechain/types' export class CoinTransaction extends Transaction implements CoinTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const data = await this.getData() return this.provider.tronWeb.address.fromHex( data?.raw_data.contract[0].parameter.value.to_address ?? '' @@ -14,16 +14,16 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { return await this.getSigner() } /** - * @returns {Promise} Amount of coin that will be transferred + * @returns {Promise} Amount of coin that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const data = await this.getData() return parseFloat( this.provider.tronWeb.fromSun( @@ -34,14 +34,14 @@ export class CoinTransaction extends Transaction implements CoinTransactionInter /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/tron/src/models/ContractTransaction.ts b/packages/networks/tron/src/models/ContractTransaction.ts index 07885d1..24c6095 100644 --- a/packages/networks/tron/src/models/ContractTransaction.ts +++ b/packages/networks/tron/src/models/ContractTransaction.ts @@ -1,7 +1,7 @@ // @ts-expect-error no need type import TxDecoder from '@beycandeveloper/tron-tx-decoder' import { Transaction, type TransactionData } from './Transaction.ts' -import type { ContractTransactionInterface } from '@multiplechain/types' +import type { ContractAddress, ContractTransactionInterface } from '@multiplechain/types' export interface DecodedInputData { methodName: string @@ -15,9 +15,9 @@ export interface DecodedInputData { export class ContractTransaction extends Transaction implements ContractTransactionInterface { /** - * @returns {Promise} Contract address of the transaction + * @returns {Promise} Contract address of the transaction */ - async getAddress(): Promise { + async getAddress(): Promise { const data = await this.getData() return this.provider.tronWeb.address.fromHex( data?.raw_data.contract[0].parameter.value.contract_address ?? '' diff --git a/packages/networks/tron/src/models/NftTransaction.ts b/packages/networks/tron/src/models/NftTransaction.ts index 26b12f1..ba349e2 100644 --- a/packages/networks/tron/src/models/NftTransaction.ts +++ b/packages/networks/tron/src/models/NftTransaction.ts @@ -1,12 +1,12 @@ import { ContractTransaction } from './ContractTransaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import { type NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { NftId, NftTransactionInterface, WalletAddress } from '@multiplechain/types' export class NftTransaction extends ContractTransaction implements NftTransactionInterface { /** - * @returns {Promise} Receiver wallet address + * @returns {Promise} Receiver wallet address */ - async getReceiver(): Promise { + async getReceiver(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -21,9 +21,9 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -38,23 +38,23 @@ export class NftTransaction extends ContractTransaction implements NftTransactio } /** - * @returns {Promise} NFT ID + * @returns {Promise} NFT ID */ - async getNftId(): Promise { + async getNftId(): Promise { return Number((await this.decodeData())?.decodedInput[2] ?? 0) } /** * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} nftId ID of the NFT that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {NftId} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - nftId: number | string + address: WalletAddress, + nftId: NftId ): Promise { const status = await this.getStatus() diff --git a/packages/networks/tron/src/models/TokenTransaction.ts b/packages/networks/tron/src/models/TokenTransaction.ts index 709eb6c..7aa8b92 100644 --- a/packages/networks/tron/src/models/TokenTransaction.ts +++ b/packages/networks/tron/src/models/TokenTransaction.ts @@ -1,14 +1,14 @@ import { hexToNumber } from '@multiplechain/utils' import { Token } from '../assets/Token.ts' import { ContractTransaction } from './ContractTransaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' -import { AssetDirectionEnum, type TokenTransactionInterface } from '@multiplechain/types' +import { TransactionStatusEnum, AssetDirectionEnum } from '@multiplechain/types' +import type { WalletAddress, TokenTransactionInterface, TransferAmount } from '@multiplechain/types' export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface { /** - * @returns {Promise} Wallet address of the receiver of transaction + * @returns {Promise} Wallet address of the receiver of transaction */ - async getReceiver(): Promise { + async getReceiver(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -23,9 +23,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSender(): Promise { + async getSender(): Promise { const decoded = await this.decodeData() if (decoded === null) { @@ -40,9 +40,9 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa } /** - * @returns {Promise} Amount of tokens that will be transferred + * @returns {Promise} Amount of tokens that will be transferred */ - async getAmount(): Promise { + async getAmount(): Promise { const token = new Token(await this.getAddress()) const decoded = await this.decodeData() if (decoded === null) { @@ -60,14 +60,14 @@ export class TokenTransaction extends ContractTransaction implements TokenTransa /** * @param {AssetDirectionEnum} direction - Direction of the transaction (token) - * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction - * @param {number} amount Amount of tokens that will be approved + * @param {WalletAddress} address - Wallet address of the owner or spender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of tokens that will be approved * @returns {Promise} Status of the transaction */ async verifyTransfer( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ): Promise { const status = await this.getStatus() diff --git a/packages/networks/tron/src/models/Transaction.ts b/packages/networks/tron/src/models/Transaction.ts index 23bc227..75254a8 100644 --- a/packages/networks/tron/src/models/Transaction.ts +++ b/packages/networks/tron/src/models/Transaction.ts @@ -1,6 +1,16 @@ import { Provider } from '../services/Provider.ts' -import type { TransactionInterface } from '@multiplechain/types' +import { + TransactionTypeEnum, + type BlockConfirmationCount, + type BlockNumber, + type BlockTimestamp, + type TransactionFee, + type TransactionId, + type TransactionInterface, + type WalletAddress +} from '@multiplechain/types' import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types' +import { NFT } from '../assets/NFT.ts' interface RetObject { contractRet: string @@ -62,11 +72,11 @@ export interface TransactionData { } } -export class Transaction implements TransactionInterface { +export class Transaction implements TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId /** * Blockchain network provider @@ -79,10 +89,10 @@ export class Transaction implements TransactionInterface { data: TransactionData /** - * @param {string} id Transaction id + * @param {TransactionId} id Transaction id * @param {Provider} provider Blockchain network provider */ - constructor(id: string, provider?: Provider) { + constructor(id: TransactionId, provider?: Provider) { this.id = id this.provider = provider ?? Provider.instance } @@ -133,12 +143,37 @@ export class Transaction implements TransactionInterface { } /** - * @returns {string} Transaction ID + * @returns {TransactionId} Transaction ID */ - getId(): string { + getId(): TransactionId { return this.id } + /** + * @returns {Promise} Type of the transaction + */ + async getType(): Promise { + const data = await this.getData() + + if (data === null) { + return TransactionTypeEnum.GENERAL + } + + if (data.raw_data.contract[0].type === 'TriggerSmartContract') { + const tryNft = new NFT(data.raw_data.contract[0].parameter.value.contract_address ?? '') + try { + await tryNft.getApproved(1) + return TransactionTypeEnum.NFT + } catch { + return TransactionTypeEnum.TOKEN + } + } else if (data.raw_data.contract[0].type === 'TransferContract') { + return TransactionTypeEnum.COIN + } + + return TransactionTypeEnum.GENERAL + } + /** * @returns {string} Transaction URL */ @@ -150,9 +185,9 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Wallet address of the sender of transaction + * @returns {Promise} Wallet address of the sender of transaction */ - async getSigner(): Promise { + async getSigner(): Promise { const data = await this.getData() return this.provider.tronWeb.address.fromHex( data?.raw_data.contract[0].parameter.value.owner_address ?? '' @@ -160,33 +195,33 @@ export class Transaction implements TransactionInterface { } /** - * @returns {Promise} Transaction fee + * @returns {Promise} Transaction fee */ - async getFee(): Promise { + async getFee(): Promise { const data = await this.getData() return parseFloat(this.provider.tronWeb.fromSun(data?.info?.fee ?? 0) as unknown as string) } /** - * @returns {Promise} Block number that transaction + * @returns {Promise} Block number that transaction */ - async getBlockNumber(): Promise { + async getBlockNumber(): Promise { const data = await this.getData() return data?.info?.blockNumber ?? 0 } /** - * @returns {Promise} Block timestamp that transaction + * @returns {Promise} Block timestamp that transaction */ - async getBlockTimestamp(): Promise { + async getBlockTimestamp(): Promise { const data = await this.getData() return parseInt((data?.info?.blockTimeStamp ?? 0).toString().replace(/0+$/, '')) } /** - * @returns {Promise} Confirmation count of the block + * @returns {Promise} Confirmation count of the block */ - async getBlockConfirmationCount(): Promise { + async getBlockConfirmationCount(): Promise { const data = await this.getData() const blockNumber = data?.info?.blockNumber ?? 0 const latestBlock = await this.provider.tronWeb.trx.getCurrentBlock() diff --git a/packages/networks/tron/src/services/Provider.ts b/packages/networks/tron/src/services/Provider.ts index 84e350f..3242588 100644 --- a/packages/networks/tron/src/services/Provider.ts +++ b/packages/networks/tron/src/services/Provider.ts @@ -63,7 +63,6 @@ export class Provider implements ProviderInterface { * @param network - Network configuration of the provider */ constructor(network: NetworkConfigInterface) { - this.network = network this.update(network) } @@ -110,7 +109,8 @@ export class Provider implements ProviderInterface { /** * Update network configuration of the provider - * @param network - Network configuration of the provider + * @param {NetworkConfigInterface} network - Network configuration of the provider + * @returns {void} */ update(network: NetworkConfigInterface): void { this.network = network @@ -127,7 +127,7 @@ export class Provider implements ProviderInterface { /** * Get the current network configuration is testnet or not - * @returns boolean + * @returns {boolean} Testnet or not */ isTestnet(): boolean { return this.network?.testnet ?? false diff --git a/packages/networks/tron/src/services/TransactionListener.ts b/packages/networks/tron/src/services/TransactionListener.ts index cb57c69..d4e7d4c 100644 --- a/packages/networks/tron/src/services/TransactionListener.ts +++ b/packages/networks/tron/src/services/TransactionListener.ts @@ -2,7 +2,8 @@ import type { TransactionTypeEnum, DynamicTransactionType, TransactionListenerInterface, - DynamicTransactionListenerFilterType + DynamicTransactionListenerFilterType, + TransactionId } from '@multiplechain/types' import { Provider } from './Provider.ts' @@ -41,29 +42,29 @@ export class TransactionListener< type: T /** - * Transaction listener callback + * Provider */ - callbacks: CallBackType[] = [] + provider: Provider /** - * Transaction listener filter + * Listener status */ - filter?: DynamicTransactionListenerFilterType + status: boolean = false /** - * Provider + * Transaction listener callback */ - provider: Provider + callbacks: CallBackType[] = [] /** - * Listener status + * Triggered transactions */ - status: boolean = false + triggeredTransactions: TransactionId[] = [] /** - * Triggered transactions + * Transaction listener filter */ - triggeredTransactions: string[] = [] + filter?: DynamicTransactionListenerFilterType /** * Dynamic stop method diff --git a/packages/networks/tron/src/services/TransactionSigner.ts b/packages/networks/tron/src/services/TransactionSigner.ts index 9f44ac1..d9d1cba 100644 --- a/packages/networks/tron/src/services/TransactionSigner.ts +++ b/packages/networks/tron/src/services/TransactionSigner.ts @@ -1,9 +1,10 @@ import { Provider } from '../services/Provider.ts' -import { Transaction } from '../models/Transaction.ts' -import { NftTransaction } from '../models/NftTransaction.ts' -import { CoinTransaction } from '../models/CoinTransaction.ts' -import { TokenTransaction } from '../models/TokenTransaction.ts' -import { ErrorTypeEnum, type TransactionSignerInterface } from '@multiplechain/types' +import { + ErrorTypeEnum, + type PrivateKey, + type TransactionId, + type TransactionSignerInterface +} from '@multiplechain/types' interface ParameterInterface { value: { @@ -33,13 +34,17 @@ export interface TransactionData { timestamp: number } raw_data_hex: string + signature?: string[] + [key: string]: unknown } export interface SignedTransactionData extends TransactionData { signature: string[] } -export class TransactionSigner implements TransactionSignerInterface { +export class TransactionSigner + implements TransactionSignerInterface +{ /** * Transaction data from the blockchain network */ @@ -57,6 +62,7 @@ export class TransactionSigner implements TransactionSignerInterface { /** * @param {TransactionData} rawData - Transaction data + * @param {Provider} provider - Blockchain network provider */ constructor(rawData: TransactionData, provider?: Provider) { this.rawData = rawData @@ -65,27 +71,27 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Sign the transaction - * @param {string} privateKey - Transaction data - * @returns {Promise} Signed transaction data + * @param {PrivateKey} privateKey - Transaction data + * @returns {Promise} Signed transaction data */ - async sign(privateKey: string): Promise { + async sign(privateKey: PrivateKey): Promise { this.signedData = await this.provider.tronWeb.trx.sign(this.rawData, privateKey) return this } /** * Send the transaction to the blockchain network - * @returns {Promise} + * @returns {Promise} */ - async send(): Promise { + async send(): Promise { const { transaction } = await this.provider.tronWeb.trx.sendRawTransaction(this.signedData) if (transaction === undefined) throw new Error(ErrorTypeEnum.TRANSACTION_CREATION_FAILED) - return new Transaction(transaction.txID as string) + return transaction.txID as string } /** * Get the raw transaction data - * @returns Transaction data + * @returns {TransactionData} */ getRawData(): TransactionData { return this.rawData @@ -93,39 +99,9 @@ export class TransactionSigner implements TransactionSignerInterface { /** * Get the signed transaction data - * @returns Signed transaction data + * @returns {SignedTransactionData} */ getSignedData(): SignedTransactionData { return this.signedData } } - -export class CoinTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new CoinTransaction((await super.send()).getId()) - } -} - -export class TokenTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new TokenTransaction((await super.send()).getId()) - } -} - -export class NftTransactionSigner extends TransactionSigner { - /** - * Send the transaction to the blockchain network - * @returns {Promise} Transaction data - */ - async send(): Promise { - return new NftTransaction((await super.send()).getId()) - } -} diff --git a/packages/networks/tron/src/services/TronWeb.ts b/packages/networks/tron/src/services/TronWeb.ts index 10ee335..fb07e92 100644 --- a/packages/networks/tron/src/services/TronWeb.ts +++ b/packages/networks/tron/src/services/TronWeb.ts @@ -3,6 +3,10 @@ import type { TransactionRawData } from '../assets/Contract.ts' import type { TransactionData } from './TransactionSigner.ts' export class TronWeb extends TronWebBase { + /** + * @param {TransactionRawData} data - Transaction data + * @returns {Promise} Transaction data + */ async triggerContract(data: TransactionRawData): Promise { const response = await this.transactionBuilder.triggerSmartContract( data.address, @@ -12,6 +16,7 @@ export class TronWeb extends TronWebBase { data.from ) + // eslint-disable-next-line if (response?.result?.result !== true) { return false } diff --git a/packages/networks/tron/src/services/tronweb.d.ts b/packages/networks/tron/src/services/tronweb.d.ts index 8ebd0db..daf6f57 100644 --- a/packages/networks/tron/src/services/tronweb.d.ts +++ b/packages/networks/tron/src/services/tronweb.d.ts @@ -1,6 +1,5 @@ /// declare module 'tronweb' { - export interface TronWebConfigInterface { fullNode: string solidityNode: string @@ -8,119 +7,213 @@ declare module 'tronweb' { } export class TronWeb { - constructor(config: TronWebConfigInterface); - contract(...args: any[]): any; - currentProvider(): any; - currentProviders(): any; - getEventByTransactionID(transactionID: string, callback?: any): any; - getEventResult(...args: any[]): any; - isConnected(callback?: any): any; - isValidProvider(provider: any): any; - setAddress(address: any): void; - setDefaultBlock(blockID: any): any; - setEventServer(eventServer: any): any; - setFullNode(fullNode: any): void; - setPrivateKey(privateKey: any): void; - setSolidityNode(solidityNode: any): void; - createAccount(callback?: any): Promise; - fromAscii(string: any, padding: any): any; - fromDecimal(value: any): any; - fromSun(sun: number): number; - fromUtf8(string: any): any; - isAddress(address: string): any; - sha3(string: any, prefix?: boolean): any; - toAscii(hex: any): any; - toBigNumber(amount: number): any; - toDecimal(value: any): any; - toHex(val: any): any; - toSun(trx: any): any; - toUtf8(hex: any): any; - trx: { - parseToken(token: any): any; - getCurrentBlock(callback?: any): Promise; - getBlock(block: any, callback?: any): Promise; - getBlockByHash(blockHash: any, callback?: any): Promise; - getBlockByNumber(blockID: any, callback?: any): Promise; - getBlockTransactionCount(block: any, callback?: any): Promise; - getTransactionFromBlock(block: any, index: number, callback?: any): Promise; - getTransaction(transactionID: any, callback?: any): Promise; - getConfirmedTransaction(transactionID: any, callback?: any): Promise; - getTransactionInfo(transactionID: any, callback?: any): Promise; - getTransactionsToAddress(address: any, limit: number, offset: number, callback?: any): Promise; - getTransactionsFromAddress(address: any, limit: number, offset: number, callback?: any): Promise; - getTransactionsRelated(address: any, direction: any, limit: number, offset: number, callback?: any): Promise; - getAccount(address: any, callback?: any): Promise; - getBalance(address: any, callback?: any): Promise; - getUnconfirmedAccount(address: any, callback?: any): Promise; - getUnconfirmedBalance(address: any, callback?: any): Promise; - getBandwidth(address: any, callback?: any): Promise; - getTokensIssuedByAddress(address: any, callback?: any): Promise; - getTokenFromID(tokenID: any, callback?: any): Promise; - listNodes(callback?: any): Promise; - getBlockRange(start: number, end: number, callback?: any): Promise; - listSuperRepresentatives(callback?: any): Promise; - listTokens(limit?: number, offset?: number, callback?: any): Promise; - timeUntilNextVoteCycle(callback?: any): Promise; - getContract(contractAddress: any, callback?: any): Promise; - verifyMessage(message: any, signature: any, address: any, useTronHeader: any, callback?: any): Promise; - sign(transaction: any, privateKey: any, useTronHeader?: boolean, callback?: any): Promise; - sendRawTransaction(signedTransaction: any, options?: any, callback?: any): Promise; - sendTransaction(to: any, amount: any, options: any, callback?: any): Promise; - sendToken(to: any, amount: any, tokenID: any, options: any, callback?: any): Promise; - freezeBalance(amount: any, duration: number, resource: string, options: any, callback?: any): Promise; - unfreezeBalance(resource: string, options: any, callback?: any): Promise; - updateAccount(accountName: string, options: any, callback?: any): Promise; - signMessage(...args: any[]): Promise; - sendAsset(...args: any[]): Promise; - send(...args: any[]): Promise; - sendTrx(...args: any[]): Promise; - broadcast(...args: any[]): Promise; - signTransaction(...args: any[]): Promise; - getProposal(proposalID: any, callback?: any): Promise; - listProposals(callback: any): Promise; - getChainParameters(callback: any): Promise; - getAccountResources(address: any, callback?: any): Promise; - getExchangeByID(exchangeID: any, callback?: any): Promise; - listExchanges(callback?: any): Promise; - listExchangesPaginated(limit: number, offset: number, callback?: any): Promise; + constructor(config: TronWebConfigInterface) + contract(...args: any[]): any + currentProvider(): any + currentProviders(): any + getEventByTransactionID(transactionID: string, callback?: any): any + getEventResult(...args: any[]): any + isConnected(callback?: any): any + isValidProvider(provider: any): any + setAddress(address: any): void + setDefaultBlock(blockID: any): any + setEventServer(eventServer: any): any + setFullNode(fullNode: any): void + setPrivateKey(privateKey: any): void + setSolidityNode(solidityNode: any): void + createAccount(callback?: any): Promise + fromAscii(string: any, padding: any): any + fromDecimal(value: any): any + fromSun(sun: number): number + fromUtf8(string: any): any + isAddress(address: string): any + sha3(string: any, prefix?: boolean): any + toAscii(hex: any): any + toBigNumber(amount: number): any + toDecimal(value: any): any + toHex(val: any): any + toSun(trx: any): any + toUtf8(hex: any): any + trx: { + parseToken(token: any): any + getCurrentBlock(callback?: any): Promise + getBlock(block: any, callback?: any): Promise + getBlockByHash(blockHash: any, callback?: any): Promise + getBlockByNumber(blockID: any, callback?: any): Promise + getBlockTransactionCount(block: any, callback?: any): Promise + getTransactionFromBlock(block: any, index: number, callback?: any): Promise + getTransaction(transactionID: any, callback?: any): Promise + getConfirmedTransaction(transactionID: any, callback?: any): Promise + getTransactionInfo(transactionID: any, callback?: any): Promise + getTransactionsToAddress( + address: any, + limit: number, + offset: number, + callback?: any + ): Promise + getTransactionsFromAddress( + address: any, + limit: number, + offset: number, + callback?: any + ): Promise + getTransactionsRelated( + address: any, + direction: any, + limit: number, + offset: number, + callback?: any + ): Promise + getAccount(address: any, callback?: any): Promise + getBalance(address: any, callback?: any): Promise + getUnconfirmedAccount(address: any, callback?: any): Promise + getUnconfirmedBalance(address: any, callback?: any): Promise + getBandwidth(address: any, callback?: any): Promise + getTokensIssuedByAddress(address: any, callback?: any): Promise + getTokenFromID(tokenID: any, callback?: any): Promise + listNodes(callback?: any): Promise + getBlockRange(start: number, end: number, callback?: any): Promise + listSuperRepresentatives(callback?: any): Promise + listTokens(limit?: number, offset?: number, callback?: any): Promise + timeUntilNextVoteCycle(callback?: any): Promise + getContract(contractAddress: any, callback?: any): Promise + verifyMessage( + message: any, + signature: any, + address: any, + useTronHeader: any, + callback?: any + ): Promise + sign( + transaction: any, + privateKey: any, + useTronHeader?: boolean, + callback?: any + ): Promise + sendRawTransaction(signedTransaction: any, options?: any, callback?: any): Promise + sendTransaction(to: any, amount: any, options: any, callback?: any): Promise + sendToken( + to: any, + amount: any, + tokenID: any, + options: any, + callback?: any + ): Promise + freezeBalance( + amount: any, + duration: number, + resource: string, + options: any, + callback?: any + ): Promise + unfreezeBalance(resource: string, options: any, callback?: any): Promise + updateAccount(accountName: string, options: any, callback?: any): Promise + signMessage(...args: any[]): Promise + sendAsset(...args: any[]): Promise + send(...args: any[]): Promise + sendTrx(...args: any[]): Promise + broadcast(...args: any[]): Promise + signTransaction(...args: any[]): Promise + getProposal(proposalID: any, callback?: any): Promise + listProposals(callback: any): Promise + getChainParameters(callback: any): Promise + getAccountResources(address: any, callback?: any): Promise + getExchangeByID(exchangeID: any, callback?: any): Promise + listExchanges(callback?: any): Promise + listExchangesPaginated(limit: number, offset: number, callback?: any): Promise } transactionBuilder: { - sendTrx(to: any, amount: any, from: any, callback?: any): Promise; - sendToken(to: any, amount: any, tokenID: any, from: any, callback?: any): Promise; - purchaseToken(issuerAddress: any, tokenID: any, amount: any, buyer: any, callback?: any): Promise; - freezeBalance(amount: any, duration: number, resource: string, address: any, callback?: any): Promise; - unfreezeBalance(resource: string, address: any, callback?: any): Promise; - withdrawBlockRewards(address: any, callback?: any): Promise; - applyForSR(address: any, url: any, callback?: any): Promise; - vote(votes: any, voterAddress: any, callback?: any): Promise; - createToken(options: any, issuerAddress: any, callback?: any): Promise; - updateAccount(accountName: any, address: any, callback?: any): Promise; - updateToken(options: any, issuerAddress: any, callback?: any): Promise; - sendAsset(...args: any[]): Promise; - purchaseAsset(...args: any[]): Promise; - createAsset(...args: any[]): Promise; - updateAsset(...args: any[]): Promise; - createProposal(parameters: any, issuerAddress: any, callback?: any): Promise; - deleteProposal(proposalID: any, issuerAddress: any, callback?: any): Promise; - voteProposal(proposalID: any, isApproval: any, voterAddress: any, callback?: any): Promise; - createTRXExchange(tokenName: any, tokenBalance: any, trxBalance: any, ownerAddress: any): Promise; - createTokenExchange(firstTokenName: any, firstTokenBalance: any, secondTokenName: any, secondTokenBalance: any, ownerAddress: any, callback?: any): Promise; - injectExchangeTokens(exchangeID: any, tokenName: any, tokenAmount: any, ownerAddress: any, callback?: any): Promise; - withdrawExchangeTokens(exchangeID: any, tokenName: any, tokenAmount: any, ownerAddress: any, callback?: any): Promise; - tradeExchangeTokens(exchangeID: any, tokenName: any, tokenAmountSold: any, tokenAmountExpected: any, ownerAddress: any, callback?: any): Promise; + estimateEnergy( + contractAddress: any, + functionSelector: any, + options: any, + parameter: any, + issuerAddress: any + ): Promise + sendTrx(to: any, amount: any, from: any, callback?: any): Promise + sendToken(to: any, amount: any, tokenID: any, from: any, callback?: any): Promise + purchaseToken( + issuerAddress: any, + tokenID: any, + amount: any, + buyer: any, + callback?: any + ): Promise + freezeBalance( + amount: any, + duration: number, + resource: string, + address: any, + callback?: any + ): Promise + unfreezeBalance(resource: string, address: any, callback?: any): Promise + withdrawBlockRewards(address: any, callback?: any): Promise + applyForSR(address: any, url: any, callback?: any): Promise + vote(votes: any, voterAddress: any, callback?: any): Promise + createToken(options: any, issuerAddress: any, callback?: any): Promise + updateAccount(accountName: any, address: any, callback?: any): Promise + updateToken(options: any, issuerAddress: any, callback?: any): Promise + sendAsset(...args: any[]): Promise + purchaseAsset(...args: any[]): Promise + createAsset(...args: any[]): Promise + updateAsset(...args: any[]): Promise + createProposal(parameters: any, issuerAddress: any, callback?: any): Promise + deleteProposal(proposalID: any, issuerAddress: any, callback?: any): Promise + voteProposal( + proposalID: any, + isApproval: any, + voterAddress: any, + callback?: any + ): Promise + createTRXExchange( + tokenName: any, + tokenBalance: any, + trxBalance: any, + ownerAddress: any + ): Promise + createTokenExchange( + firstTokenName: any, + firstTokenBalance: any, + secondTokenName: any, + secondTokenBalance: any, + ownerAddress: any, + callback?: any + ): Promise + injectExchangeTokens( + exchangeID: any, + tokenName: any, + tokenAmount: any, + ownerAddress: any, + callback?: any + ): Promise + withdrawExchangeTokens( + exchangeID: any, + tokenName: any, + tokenAmount: any, + ownerAddress: any, + callback?: any + ): Promise + tradeExchangeTokens( + exchangeID: any, + tokenName: any, + tokenAmountSold: any, + tokenAmountExpected: any, + ownerAddress: any, + callback?: any + ): Promise triggerSmartContract(...args: any[]): Promise<{ - transaction: any; + transaction: any result: { - result: boolean; - }; - }>; + result: boolean + } + }> } address: { - fromHex(e: any): any; - fromPrivateKey(e: any): any; - toHex(e: any): any; + fromHex(e: any): any + fromPrivateKey(e: any): any + toHex(e: any): any } } - export default TronWeb; -} \ No newline at end of file + export default TronWeb +} diff --git a/packages/networks/tron/tests/assets.spec.ts b/packages/networks/tron/tests/assets.spec.ts index c508375..787cae5 100644 --- a/packages/networks/tron/tests/assets.spec.ts +++ b/packages/networks/tron/tests/assets.spec.ts @@ -5,7 +5,7 @@ import { Coin } from '../src/assets/Coin.ts' import { math } from '@multiplechain/utils' import { Token } from '../src/assets/Token.ts' import { Transaction } from '../src/models/Transaction.ts' -import { TransactionStatusEnum } from '@multiplechain/types' +import { TransactionStatusEnum, type TransactionId } from '@multiplechain/types' import { TransactionSigner } from '../src/services/TransactionSigner.ts' const coinBalanceTestAmount = Number(process.env.TRON_COIN_BALANCE_TEST_AMOUNT) @@ -52,8 +52,8 @@ const checkSigner = async (signer: TransactionSigner, privateKey?: string): Prom assert.isObject(signer.getSignedData()) } -const checkTx = async (transaction: Transaction): Promise => { - expect(transaction).toBeInstanceOf(Transaction) +const checkTx = async (TransactionId: TransactionId): Promise => { + const transaction = new Transaction(TransactionId) const status = await transaction.wait(10 * 1000) expect(status).toBe(TransactionStatusEnum.CONFIRMED) } diff --git a/packages/types/README.md b/packages/types/README.md index c4887a7..d746267 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -1,209 +1,7 @@ -# MultipleChain Types +# MultipleChain standard for JavaScript -It is a suite of types defined to provide cross-network standardization in the MultipleChain project. +## Introduction -## Installation +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. -```bash -npm install @multiplechain/types -``` - -## Usage - -You can import all of the types at once: -```typescript -import type * as types from '@multiplechain/types'; -``` - -Or you import the types one by one: -```typescript -import type { - // Providers - ProviderInterface, - NetworkConfigInterface, - - // Models - TransactionInterface, - ContractTransactionInterface, - AssetTransactionInterface, - CoinTransactionInterface, - TokenTransactionInterface, - NftTransactionInterface, - - // Assets - AssetInterface, - ContractInterface, - CoinInterface, - TokenInterface, - NftInterface, - - // Enums - AssetDirectionEnum, - TransactionTypeEnum, - TransactionStatusEnum, - - // Transaction Listeners - TransactionListenerInterface, - DynamicTransactionType - - // Transaction Signer - TransactionSignerInterface -} from '@multiplechain/types'; -``` - -## Types -### Provider -#### [ProviderInterface](./packages/types/src/services/ProviderInterface.ts) -`Provider` is the main class that will be used for every network (EVM, Solana, Tron, etc.) - -`ProviderInterface` is the interface of `Provider` class. - -#### [NetworkConfigInterface](./packages/types/src/services/ProviderInterface.ts) -`update()` and `constructor()` methods of `Provider` class takes a config parameter. Interface of this config parameter is defined as `NetworkConfigInterface`. - -### Models -**There are 6 types of transaction models:** -#### [TransactionInterface](./packages/types/src/models.ts) -`TransactionInterface` is the most comprehensive interface compared to others. Every other interface extends `TransactionInterface`. - -This interface has ID management of transactions since each transaction and each transaction type has its own unique ID, and also has helper functions that is being used in every other transaction types such as `getBlockNumber` and `getStatus` - -#### [ContractTransactionInterface](./packages/types/src/models.ts) -Inherits `TransactionInterface`. Used for smart contracts transactions. Token and NFT transactions inherits `ContractTransactionInterface`. - -On top of `TransactionInterface`, lets developers to grab smart contract address used in transaction. - -```typescript -getAddress: () => string // Smart contract address of the transaction -``` - -#### [AssetTransactionInterface](./packages/types/src/models.ts) -Inherits `TransactionInterface`. Used for asset transactions. - -#### [CoinTransactionInterface](./packages/types/src/models.ts) -Used for transactions on blockchain done with native currency of the network. In other words, supports transaction data done on Layer-1 networks (Tron, Ethereum, Solana, etc.) - -#### [TokenTransactionInterface](./packages/types/src/models.ts) -Used for token transactions. Adds a support for verification of approvement on top of `AssetTransactionInterface`. - -#### [NftTransactionInterface](./packages/types/src/models.ts) -Used for NFT transactions. NFT transactions has a pointer property to NFT ID, `NftTransactionInterface` adds a helper method named `getNftId()` to grab that ID. - -Also just like `TokenTransactionInterface` there is an approvement verification method in `NftTransactionInterface` too. - -### Assets -**There are 5 types of asset interfaces** - -#### [AssetInterface](./packages/types/src/assets.ts) -`AssetInterface` is the most comprehensive interface compared to others. Every other interface except `ContractInterface` extends `TransactionInterface`. - -It has helper methods like starting a transfer. `transfer()` method is available for every asset type (COINs, TOKENs, NFTs). - -#### [ContractInterface](./packages/types/src/assets.ts) -`ContractInterface` is inherited by `TokenInterface` and `NftInterface`. It has helper methods like grabbing contract addresses. - -#### [CoinInterface](./packages/types/src/assets.ts) -Used for coin assets. Currently adds a helper method to get decimal value of the asset on top of `AssetInterface`. - -#### [TokenInterface](./packages/types/src/assets.ts) -Contains helper methods that can be used for grabbing token data like `getTotalSupply()`. - -#### [NftInterface](./packages/types/src/assets.ts) -Contains helper methods for NFT type of assets. Unlike the other asset interfaces, `NftInterface` overrides the `transfer()` method since it needs an `nftId` parameter instead of a `amount` parameter. - -### Enums - -#### [AssetDirectionEnum](./packages/types/src/enums.ts) -Asset transactions (COIN, TOKEN, NFT) has two directions - -```typescript -enum AssetDirectionEnum { - INCOMING, - OUTGOING -} -``` - -#### [TransactionTypeEnum](./packages/types/src/enums.ts) -There are six types of transactions at the moment. COIN, TOKEN, and NFT transactions are called ASSET transactions - -```typescript -enum TransactionTypeEnum { - GENERAL, - CONTRACT, - ASSET, - COIN, - TOKEN, - NFT -} -``` - -#### [TransactionStatusEnum](./packages/types/src/enums.ts) -There are 3 available transaction statuses: - * FAILED: When a transaction is failed - * PENDING: When a transaction has not been concluded - * CONFIRMED: When a transaction is confirmed - -```typescript -enum TransactionStatusEnum { - FAILED, - PENDING, - CONFIRMED -} - -``` - -### Transaction Listener - -#### [TransactionListenerInterface](./packages/types/src/services/TransactionListenerInterface.ts) -In order to listen to transactions whether they are `PENDING` or `FAILED` for instance, there needs to be a class providing methods for transaction listening. - -`TransactionListenerInterface` is the interface of the `TransactionListener` class which supports gathering transaction status, stopping the transaction, and callbacks after a transaction status change. - -#### [DynamicTransactionType](./packages/types/src/services/TransactionListenerInterface.ts) -There are different types of transactions, in order to listen correct transaction, correct transaction type needs to be provided. `DynamicTransactionType` is a helper interface that connects transaction types to their corresponding transaction interfaces - -```typescript -export type DynamicTransactionType = - T extends TransactionTypeEnum.GENERAL - ? TransactionInterface - : T extends TransactionTypeEnum.CONTRACT - ? ContractTransactionInterface - : T extends TransactionTypeEnum.COIN - ? CoinTransactionInterface - : T extends TransactionTypeEnum.TOKEN - ? TokenTransactionInterface - : T extends TransactionTypeEnum.NFT - ? NftTransactionInterface - : never -``` - -#### [DynamicTransactionListenerFilterType](./packages/types/src/services/TransactionListenerInterface.ts) -`filter` is an object that has values depending on transaction listener type. It has properties such as `sender`, `receiver`, etc. - -Just like `DynamicTransactionType`, `DynamicTransactionListenerFilterType` is a helper interface to get correct filter type. - -### Transaction Signer -#### [TransactionSignerInterface](./packages/types/src/services/TransactionSignerInterface.ts) - -Provides a class to sign and send transactions. `TransactionSignerInterface` has all of the methods to audit a signature. - -Methods of `TransactionSignerInterface` are as follows: - -Signs the transaction: -```typescript -sign: (privateKey: string) => TransactionSignerInterface -``` - -Sends the signed transaction: -```typescript -send: () => Promise -``` -Returns unsigned transaction data -```typescript -getRawData: () => any -``` - -Returns signed transaction data -```typescript -getSignedData: () => any -``` \ No newline at end of file +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/types/package.json b/packages/types/package.json index 6422f7f..ea1afe9 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@multiplechain/types", - "version": "0.1.56", + "version": "0.1.63", "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", diff --git a/packages/types/src/assets.ts b/packages/types/src/assets.ts index 71f5ed8..57a9e12 100644 --- a/packages/types/src/assets.ts +++ b/packages/types/src/assets.ts @@ -1,4 +1,4 @@ -import type { TransactionSignerInterface } from './services/TransactionSignerInterface.ts' +import type { ContractAddress, NftId, TransferAmount, WalletAddress } from './defines.ts' /** * There are 2 comprehensive interfaces: AssetInterface, ContractInterface @@ -10,38 +10,45 @@ export interface ContractInterface { /** * Given contract address */ - address: string + address: ContractAddress /** - * @returns {string} Given contract address + * @returns {ContractAddress} Given contract address */ - getAddress: () => string + getAddress: () => ContractAddress /** - * @param {string} method Method name - * @param {any[]} args Method parameters * Runs the contract methods dynamically + * @param {string} method Method name + * @param {unknown[]} args Method parameters + * @returns {Promise} Result of the method */ - callMethod: (method: string, ...args: any[]) => Promise + callMethod: (method: string, ...args: unknown[]) => Promise /** + * To get method data from called method * @param {string} method Method name - * @param {any[]} args Method parameters - * To get information from called method - * @returns Data used in transaction + * @param {unknown[]} args Method parameters + * @returns {Promise} Method data */ - getMethodData: (method: string, ...args: any[]) => Promise + getMethodData: (method: string, ...args: unknown[]) => Promise /** * @param {string} method Method name - * @param {string} from Sender wallet address - * @param {any[]} args Method parameters - * @returns {Promise} Transaction data - */ - createTransactionData: (method: string, from: string, ...args: any[]) => Promise + * @param {WalletAddress} from Sender wallet address + * @param {unknown[]} args Method parameters + * @returns {Promise} Transaction data + */ + createTransactionData: ( + method: string, + from: WalletAddress, + ...args: unknown[] + ) => Promise } -export interface AssetInterface { +// The Asset interface covers blockchain-specific standards that can vary between addresses. +// Example Coin (Native currency), Token, NFT etc. +export interface AssetInterface { /** * @returns {string} Name of the asset (long name) */ @@ -53,42 +60,45 @@ export interface AssetInterface { getSymbol: () => string /** - * @param {string} owner Address of the wallet - * @returns {Promise} Wallet balance as currency of TOKEN or COIN assets + * @param {WalletAddress} owner Address of the wallet + * @returns {Promise} Balance of assets */ - getBalance: (owner: string) => Promise + getBalance: (owner: WalletAddress) => Promise /** - * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN) - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of assets that will be transferred - * @returns {Promise} Transaction signer interface + * Asset transfer between wallets + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of assets that will be transferred + * @returns {Promise} Transaction signer interface */ transfer: ( - sender: string, - receiver: string, - amount: number - ) => Promise + sender: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ) => Promise } // Sub Interfaces -export interface CoinInterface extends AssetInterface { +export interface CoinInterface extends AssetInterface { /** + * Generally is static value you have to return * @returns {number} Decimal value of the coin */ getDecimals: () => number } -export interface TokenInterface - extends Omit, +export interface TokenInterface + extends Omit, 'getName' | 'getSymbol'>, ContractInterface { /** + * @override getName() in AssetInterface * @returns {Promise} Name of the asset (long name) */ getName: () => Promise /** + * @override getName() in AssetInterface * @returns {Promise} Symbol of the asset (short name) */ getSymbol: () => Promise @@ -104,104 +114,113 @@ export interface TokenInterface getTotalSupply: () => Promise /** - * @param {string} owner Address of owner of the tokens that is being used - * @param {string} spender Address of the spender that is using the tokens of owner + * If another wallet has been authorized to spend, it allows you to get this value. + * @param {WalletAddress} owner Address of owner of the tokens that is being used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner * @returns {Promise} Amount of the tokens that is being used by spender */ - getAllowance: (owner: string, spender: string) => Promise + getAllowance: (owner: WalletAddress, spender: WalletAddress) => Promise /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number} amount Amount of tokens that will be transferred - * @returns {Promise} Transaction signer interface + * Allowance spending with spender from owner to receiver + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {TransferAmount} amount Amount of tokens that will be transferred + * @returns {Promise} Transaction signer interface */ transferFrom: ( - spender: string, - owner: string, - receiver: string, - amount: number - ) => Promise + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + amount: TransferAmount + ) => Promise /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that is using the tokens of owner - * @param {number} amount Amount of the tokens that will be used - * @returns {Promise} Transaction signer interface + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that is using the tokens of owner + * @param {TransferAmount} amount Amount of the tokens that will be used + * @returns {Promise} Transaction signer interface */ - approve: (owner: string, spender: string, amount: number) => Promise + approve: ( + owner: WalletAddress, + spender: WalletAddress, + amount: TransferAmount + ) => Promise } -export interface NftInterface - extends Omit, +export interface NftInterface + extends Omit, 'transfer' | 'getName' | 'getSymbol'>, ContractInterface { /** + * @override getName() in AssetInterface * @returns {Promise} Name of the asset (long name) */ getName: () => Promise /** + * @override getName() in AssetInterface * @returns {Promise} Symbol of the asset (short name) */ getSymbol: () => Promise /** - * @param {number | string} nftId ID of the NFT - * @returns {Promise} Wallet address of owner of the NFT + * @param {NftId} nftId ID of the NFT + * @returns {Promise} Wallet address of owner of the NFT */ - getOwner: (nftId: number | string) => Promise + getOwner: (nftId: NftId) => Promise /** - * @param {number | string} nftId ID of the NFT + * @param {NftId} nftId ID of the NFT * @returns {Promise} URL of the metadata */ - getTokenURI: (nftId: number | string) => Promise + getTokenURI: (nftId: NftId) => Promise /** - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Amount of the tokens that is being used by spender + * If another wallet has been authorized to spend, it allows you to get this value. + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Amount of the tokens that is being used by spender */ - getApproved: (nftId: number | string) => Promise + getApproved: (nftId: NftId) => Promise /** * Transfers an NFT - * @param {string} sender Sender wallet address - * @param {string} receiver Receiver wallet address - * @param {number | string} nftId ID of the NFT that will be transferred * @override transfer() in AssetInterface - * @returns {Promise} Transaction signer interface + * @param {WalletAddress} sender Sender wallet address + * @param {WalletAddress} receiver Receiver wallet address + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Transaction signer interface */ transfer: ( - sender: string, - receiver: string, - nftId: number | string - ) => Promise + sender: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ) => Promise /** - * @param {string} spender Address of the spender of transaction - * @param {string} owner Address of owner of the nfts that will be used - * @param {string} receiver Address of the receiver that will receive the nfts - * @param {number | string} nftId ID of the NFT that will be transferred - * @returns {Promise} Transaction signer interface + * @param {WalletAddress} spender Address of the spender of transaction + * @param {WalletAddress} owner Address of owner of the nfts that will be used + * @param {WalletAddress} receiver Address of the receiver that will receive the nfts + * @param {NftId} nftId ID of the NFT that will be transferred + * @returns {Promise} Transaction signer interface */ transferFrom: ( - spender: string, - owner: string, - receiver: string, - nftId: number | string - ) => Promise + spender: WalletAddress, + owner: WalletAddress, + receiver: WalletAddress, + nftId: NftId + ) => Promise /** * Gives permission to the spender to spend owner's tokens - * @param {string} owner Address of owner of the tokens that will be used - * @param {string} spender Address of the spender that will use the tokens of owner - * @param {number | string} nftId ID of the NFT that will be transferred + * @param {WalletAddress} owner Address of owner of the tokens that will be used + * @param {WalletAddress} spender Address of the spender that will use the tokens of owner + * @param {NftId} nftId ID of the NFT that will be transferred */ approve: ( - owner: string, - spender: string, - nftId: number | string - ) => Promise + owner: WalletAddress, + spender: WalletAddress, + nftId: NftId + ) => Promise } diff --git a/packages/types/src/browser.ts b/packages/types/src/browser.ts index 778b3f8..f1c99e9 100644 --- a/packages/types/src/browser.ts +++ b/packages/types/src/browser.ts @@ -1,32 +1,56 @@ import type { WalletPlatformEnum } from './enums.ts' -import type { ProviderInterface } from './services/ProviderInterface.ts' -import type { TransactionSignerInterface } from './services/TransactionSignerInterface.ts' +import type { SignedMessage, TransactionId, WalletAddress } from './defines.ts' -export type RegisterWalletAdapterType = (walletAdapter: WalletAdapterInterface) => void +// WalletAdapter registration function for WalletInterface +export type RegisterWalletAdapterType = ( + walletAdapter: WalletAdapterInterface +) => void -export type WalletAdapterListType = Record +export type WalletAdapterListType = Record< + string, + WalletAdapterInterface +> -export interface WalletConnectOps { +export interface WalletConnectConfig { projectId: string themeMode?: 'dark' | 'light' } -export interface WalletAdapterInterface { +export type UnknownConfig = Record + +export type ConnectConfig = UnknownConfig & WalletConnectConfig + +// This is WalletAdapter definition for using in WalletInterface +export interface WalletAdapterInterface { id: string name: string icon: string - provider?: any + provider?: unknown downloadLink?: string platforms: WalletPlatformEnum[] disconnect?: () => void | Promise isDetected: () => boolean | Promise isConnected: () => boolean | Promise - createDeepLink?: (url: string, ops?: object) => string - connect: (provider?: ProviderInterface, ops?: object | WalletConnectOps) => Promise + createDeepLink?: (url: string, config?: UnknownConfig) => string + connect: (provider?: NetworkProvider, config?: ConnectConfig) => Promise } -export interface WalletInterface { - adapter: WalletAdapterInterface +// For signing generated transactions with wallets and for wallet connection processes. +export interface WalletInterface { + /** + * WalletAdapter instance + */ + adapter: WalletAdapterInterface + + /** + * Wallet provider is the instance of the wallet connection + */ + walletProvider: WalletProvider + + /** + * Network provider is the instance of the blockchain network connection + */ + networkProvider: NetworkProvider /** * @returns {String} @@ -55,49 +79,48 @@ export interface WalletInterface { /** * @param {String} url - * @param {Object} ops + * @param {UnknownConfig} config * @returns {String | null} */ - createDeepLink: (url: string, ops?: object) => string | null + createDeepLink: (url: string, config?: UnknownConfig) => string | null /** - * @param {ProviderInterface} provider - * @param {Object | WalletConnectOps} ops - * @returns {Promise} + * @param {ConnectConfig} config + * @returns {Promise} */ - connect: (provider?: ProviderInterface, ops?: object | WalletConnectOps) => Promise + connect: (config?: ConnectConfig) => Promise /** - * @returns {Boolean | Promise} + * @returns {boolean | Promise} */ isDetected: () => boolean | Promise /** - * @returns {Boolean | Promise} + * @returns {boolean | Promise} */ isConnected: () => boolean | Promise /** - * @returns {Promise} + * @returns {Promise} */ - getAddress: () => Promise + getAddress: () => Promise /** * @param {string} message - * @returns {Promise} + * @returns {Promise} */ - signMessage: (message: string) => Promise + signMessage: (message: string) => Promise /** - * @param {TransactionSignerInterface} transactionSigner - * @returns {Promise} + * @param {TransactionSigner} transactionSigner + * @returns {Promise} */ - sendTransaction: (transactionSigner: TransactionSignerInterface) => Promise + sendTransaction: (transactionSigner: TransactionSigner) => Promise /** * @param {string} eventName * @param {Function} callback * @returns {void} */ - on: (eventName: string, callback: (...args: any[]) => void) => void + on: (eventName: string, callback: (...args: unknown[]) => void) => void } diff --git a/packages/types/src/defines.ts b/packages/types/src/defines.ts new file mode 100644 index 0000000..4a2bcc9 --- /dev/null +++ b/packages/types/src/defines.ts @@ -0,0 +1,11 @@ +export type PrivateKey = T +export type TransactionId = T +export type SignedMessage = T +export type WalletAddress = T +export type ContractAddress = T +export type TransferAmount = T +export type NftId = T +export type BlockNumber = T +export type TransactionFee = T +export type BlockTimestamp = T +export type BlockConfirmationCount = T diff --git a/packages/types/src/enums.ts b/packages/types/src/enums.ts index 3992dd9..d1e7f75 100644 --- a/packages/types/src/enums.ts +++ b/packages/types/src/enums.ts @@ -57,7 +57,7 @@ export enum ErrorTypeEnum { CLOSED_WALLETCONNECT_MODAL = 'CLOSED_WALLETCONNECT_MODAL', PROJECT_ID_IS_REQUIRED = 'PROJECT_ID_IS_REQUIRED', METADATA_IS_REQUIRED = 'METADATA_IS_REQUIRED', - OPS_IS_REQUIRED = 'OPS_IS_REQUIRED' + CONFIG_IS_REQUIRED = 'CONFIG_IS_REQUIRED' } export enum WalletPlatformEnum { diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4ff7e39..961a8c5 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -3,6 +3,7 @@ export type * from './enums.ts' export type * from './assets.ts' export type * from './models.ts' export type * from './browser.ts' +export type * from './defines.ts' export type * from './services/ProviderInterface.ts' export type * from './services/TransactionListenerInterface.ts' export type * from './services/TransactionSignerInterface.ts' diff --git a/packages/types/src/models.ts b/packages/types/src/models.ts index 417114b..91fd922 100644 --- a/packages/types/src/models.ts +++ b/packages/types/src/models.ts @@ -1,10 +1,26 @@ -import type { AssetDirectionEnum, TransactionStatusEnum } from './enums.ts' - -export interface TransactionInterface { +import type { AssetDirectionEnum, TransactionStatusEnum, TransactionTypeEnum } from './enums.ts' +import type { + BlockConfirmationCount, + BlockNumber, + BlockTimestamp, + ContractAddress, + NftId, + TransactionFee, + TransactionId, + TransferAmount, + WalletAddress +} from './defines.ts' + +export interface TransactionInterface { /** * Each transaction has its own unique ID defined by the user */ - id: string + id: TransactionId + + /** + * Raw transaction data that is taken by blockchain network via RPC. + */ + data: TxData | null /** * @param {number} ms - Milliseconds to wait @@ -13,15 +29,20 @@ export interface TransactionInterface { wait: (ms?: number) => Promise /** - * @returns {Promise} Raw transaction data that is taken by blockchain network via RPC. + * @returns {Promise} Raw transaction data that is taken by blockchain network via RPC. */ - getData: () => Promise + getData: () => Promise /** - * @returns {string} ID of the transaction * this can be different names like txid, hash, signature etc. + * @returns {TransactionId} ID of the transaction */ - getId: () => string + getId: () => TransactionId + + /** + * @returns {Promise} Type of the transaction + */ + getType: () => Promise /** * @returns {string} Blockchain explorer URL of the transaction. Dependant on network. @@ -29,29 +50,29 @@ export interface TransactionInterface { getUrl: () => string /** - * @returns {Promise} Wallet address of the signer of transaction + * @returns {Promise} Wallet address of the signer of transaction */ - getSigner: () => Promise + getSigner: () => Promise /** - * @returns {Promise} Transaction fee as native coin amount + * @returns {Promise} Transaction fee as native coin amount */ - getFee: () => Promise + getFee: () => Promise /** - * @returns {Promise} Block ID of the transaction + * @returns {Promise} Block ID of the transaction */ - getBlockNumber: () => Promise + getBlockNumber: () => Promise /** - * @returns {Promise} UNIX timestamp of the date that block is added to blockchain + * @returns {Promise} UNIX timestamp of the date that block is added to blockchain */ - getBlockTimestamp: () => Promise + getBlockTimestamp: () => Promise /** - * @returns {Promise} Block confirmation amount + * @returns {Promise} Block confirmation amount */ - getBlockConfirmationCount: () => Promise + getBlockConfirmationCount: () => Promise /** * @returns {Promise} Status of the transaction. @@ -61,37 +82,37 @@ export interface TransactionInterface { export interface ContractTransactionInterface extends TransactionInterface { /** - * @returns {Promise} Smart contract address of the transaction + * @returns {Promise} Smart contract address of the transaction */ - getAddress: () => Promise + getAddress: () => Promise } export interface AssetTransactionInterface extends TransactionInterface { /** - * @returns {Promise} Receiver wallet address of the transaction (asset) + * @returns {Promise} Receiver wallet address of the transaction (asset) */ - getReceiver: () => Promise + getReceiver: () => Promise /** - * @returns {Promise} Wallet address of the sender of asset + * @returns {Promise} Wallet address of the sender of asset */ - getSender: () => Promise + getSender: () => Promise /** - * @returns {Promise} Transfer amount of the transaction (coin) + * @returns {Promise} Transfer amount of the transaction (coin) */ - getAmount: () => Promise + getAmount: () => Promise /** * @param {AssetDirectionEnum} direction - Direction of the transaction (asset) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number} amount Amount of assets that will be transferred + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {TransferAmount} amount Amount of assets that will be transferred * @returns {Promise} Status of the transaction */ verifyTransfer: ( direction: AssetDirectionEnum, - address: string, - amount: number + address: WalletAddress, + amount: TransferAmount ) => Promise } @@ -105,20 +126,21 @@ export interface NftTransactionInterface extends Omit, ContractTransactionInterface { /** - * @returns {Promise} ID of the NFT + * Replaces getAmount in the Asset interface. + * @returns {Promise} ID of the NFT */ - getNftId: () => Promise + getNftId: () => Promise /** - * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) - * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction - * @param {number | string} nftId ID of the NFT that will be transferred * @override verifyTransfer() in AssetTransactionInterface + * @param {AssetDirectionEnum} direction - Direction of the transaction (nft) + * @param {WalletAddress} address - Wallet address of the receiver or sender of the transaction, dependant on direction + * @param {NftId} nftId ID of the NFT that will be transferred * @returns {Promise} Status of the transaction */ verifyTransfer: ( direction: AssetDirectionEnum, - address: string, - nftId: number | string + address: WalletAddress, + nftId: NftId ) => Promise } diff --git a/packages/types/src/services/ProviderInterface.ts b/packages/types/src/services/ProviderInterface.ts index 75ff200..45897a2 100644 --- a/packages/types/src/services/ProviderInterface.ts +++ b/packages/types/src/services/ProviderInterface.ts @@ -1,7 +1,7 @@ /** * wsUrl: Websocket URL - * rpcUrl: RPC URL of the blockchain network - * testnet: @default true + * rpcUrl: RPC API URL + * testnet: @default false */ export interface NetworkConfigInterface { wsUrl?: string @@ -9,17 +9,17 @@ export interface NetworkConfigInterface { testnet?: boolean } -export interface ProviderInterface { +export interface ProviderInterface { /** * Network configuration of the provider */ - network: NetworkConfigInterface + network: NetworkConfig /** * Update network configuration of the provider - * @param {NetworkConfigInterface} network - Network configuration + * @param {NetworkConfig} network - Network configuration */ - update: (network: NetworkConfigInterface) => void + update: (network: NetworkConfig) => void /** * Get the current network configuration is testnet or not diff --git a/packages/types/src/services/TransactionListenerInterface.ts b/packages/types/src/services/TransactionListenerInterface.ts index f86e3a9..adce5d1 100644 --- a/packages/types/src/services/TransactionListenerInterface.ts +++ b/packages/types/src/services/TransactionListenerInterface.ts @@ -1,38 +1,45 @@ +import type { + NftId, + ContractAddress, + TransactionId, + TransferAmount, + WalletAddress +} from '../defines.ts' import { TransactionTypeEnum } from '../enums.ts' /** * Filter types for each transaction type in TransactionListenerInterface */ export interface TransactionListenerFilterInterface { - signer?: string + signer?: WalletAddress } export interface ContractTransactionListenerFilterInterface extends TransactionListenerFilterInterface { - address?: string + address?: ContractAddress } export interface AssetTransactionListenerFilterInterface extends TransactionListenerFilterInterface { - sender?: string - receiver?: string + sender?: WalletAddress + receiver?: WalletAddress } export interface CoinTransactionListenerFilterInterface extends AssetTransactionListenerFilterInterface { - amount?: number + amount?: TransferAmount } export interface TokenTransactionListenerFilterInterface extends AssetTransactionListenerFilterInterface, ContractTransactionListenerFilterInterface { - amount?: number + amount?: TransferAmount } export interface NftTransactionListenerFilterInterface extends AssetTransactionListenerFilterInterface, ContractTransactionListenerFilterInterface { - nftId?: number | string + nftId?: NftId } /** * Filter types for each transaction type in TransactionListenerInterface @@ -58,7 +65,7 @@ export type DynamicTransactionListenerFilterType /** * 'TransactionListenerProcessIndex' is an object that connects transaction types to their corresponding process methods. - * Example: this[TransactionListenerProcessIndex[type] as keyof TransactionListener]() + * Example: this[TransactionListenerProcessIndex[type]]() */ export const TransactionListenerProcessIndex = { [TransactionTypeEnum.GENERAL]: 'generalProcess', @@ -68,13 +75,9 @@ export const TransactionListenerProcessIndex = { [TransactionTypeEnum.NFT]: 'nftProcess' } -/** - * Dynamic types for transaction trigger and callback methods - */ /** * 'DynamicTransactionType' connects transaction types to their corresponding transaction interfaces * Every type of transaction has its own unique transaction interface. - * A sender's wallet address is a common value. */ export type DynamicTransactionType< T extends TransactionTypeEnum, @@ -118,44 +121,43 @@ export interface TransactionListenerInterface< callbacks: CallBackType[] /** - * Triggered transactions + * Triggered transactions are stored in the 'triggeredTransactions' array. */ - triggeredTransactions: string[] + triggeredTransactions: TransactionId[] /** * 'filter' is an object that has values depending on transaction listener type. - * E.g. no matter which type of transaction is listening, 'filter' has to have a 'sender' value + * E.g. no matter which type of transaction is listening, 'filter' has to have a 'signer' value */ filter?: DynamicTransactionListenerFilterType | Record /** - * stop() method closes the corresponding listener of the instance it's called from. + * If the listener is active, the 'stop' method deactivates the listener. * @returns {void} */ stop: () => void /** - * start() method starts the corresponding listener of the instance it's called from. + * If the listener is inactive, the 'start' method activates the listener. * @returns {void} */ start: () => void /** - * getStatus() method returns the status of the listener. + * The 'getStatus' method returns the status of the listener. * @returns {boolean} */ getStatus: () => boolean /** - * on() method is a listener that listens to the transaction events. - * When a transaction is detected, it triggers the event. + * The 'on' method adds a callback function to the 'callbacks' array and starts the listener. * @param {CallBackType} callback - a function that is triggered when a transaction is detected. * @return {Promise} */ on: (callback: CallBackType) => Promise /** - * trigger() method triggers the event when a transaction is detected. + * The 'trigger' method is triggered when a transaction is detected. * @param {Transaction} transaction - a transaction that is detected. * @return {void} */ diff --git a/packages/types/src/services/TransactionSignerInterface.ts b/packages/types/src/services/TransactionSignerInterface.ts index 4966879..bf4ac63 100644 --- a/packages/types/src/services/TransactionSignerInterface.ts +++ b/packages/types/src/services/TransactionSignerInterface.ts @@ -1,37 +1,33 @@ -import type { TransactionInterface } from '../models.ts' +import type { PrivateKey, TransactionId } from '../defines.ts' -/** - * "any" is dependent on the blockchain network, it can be a string, object or any other type - * so, you need define the type of the transaction data in your implementation - */ -export interface TransactionSignerInterface { +export interface TransactionSignerInterface { /** - * Transaction data from the blockchain network + * Transaction data type from the blockchain network */ - rawData: any + rawData: RawData /** - * Signed transaction data + * Signed transaction data type from the blockchain network */ - signedData?: any + signedData?: SignedData /** - * @param {string} privateKey - Private key of the wallet to sign the transaction + * @param {PrivateKey} privateKey - Private key of the wallet to sign the transaction */ - sign: (privateKey: string) => Promise + sign: (privateKey: PrivateKey) => Promise /** - * @returns {Promise} Send the transaction to the blockchain network, returns a promise of the transaction + * @returns {Promise} Send the transaction to the blockchain network, returns a promise of the transaction */ - send: () => Promise + send: () => Promise /** - * @returns {any} Unsigned transaction raw data + * @returns {RawData} Unsigned transaction raw data */ - getRawData: () => any + getRawData: () => RawData /** - * @returns {any} Signed transaction data + * @returns {SignedData} Signed transaction data */ - getSignedData: () => any + getSignedData: () => SignedData } diff --git a/packages/utils/README.md b/packages/utils/README.md index d872155..d746267 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -1,20 +1,7 @@ -# MultipleChain Utils +# MultipleChain standard for JavaScript -This package contains a set of utilities that are used across the MultipleChain packages. +## Introduction -## Installation +MultipleChain aims for easy access by simplifying the complex structure of many blockchains. For example, each blockchain network has a different structure for transfer initiation or transaction data. You may need to learn new things from scratch for each blockchain network. This is necessary if you want to go into detail. But if you just want to get to the basics. MultipleChain will make your work much easier. In many different programming languages. -```bash -npm install @multiplechain/utils -``` - -## Usage - -```javascript -import * as utils from '@multiplechain/utils'; -// or -import { toHex, numberToHex, hexToNumber } from '@multiplechain/utils'; - -const hex = utils.toHex(100); // 0x64 -const number = hexToNumber(hex); // 100 -``` +#### 📚 [Documentation](https://multiplechain.gitbook.io/multiplechain-docs) \ No newline at end of file diff --git a/packages/utils/package.json b/packages/utils/package.json index 7665b4c..6a892b3 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@multiplechain/utils", - "version": "0.1.20", + "version": "0.1.21", "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", diff --git a/vitest.config.ts b/vitest.config.ts index e153ac5..e9ed075 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -14,12 +14,6 @@ export default mergeConfig( '**/browser/**', '**/index.ts/**', '**/boilerplate/**', - '**/bitcoin/src/assets/NFT.ts', - '**/bitcoin/src/assets/Token.ts', - '**/bitcoin/src/assets/Contract.ts', - '**/bitcoin/src/models/NftTransaction.ts', - '**/bitcoin/src/models/TokenTransaction.ts', - '**/bitcoin/src/models/ContractTransaction.ts', '**/networks/**/src/services/TransactionListener.ts' ] },