From b54f5e6a3038f50076f33bdb0bb65007d614cac9 Mon Sep 17 00:00:00 2001 From: Raul Duarte Pereira Date: Wed, 10 Jul 2024 19:31:45 -0300 Subject: [PATCH] CU-86du15m7h - Plan, Structure and Implement BsLib support to multichain EVM --- .../CU-86dtz6h0n_2024-07-10-22-27.json | 10 + .../CU-86dtz6h0n_2024-07-10-22-27.json | 10 + .../CU-86dtz6h0n_2024-07-10-22-27.json | 10 + .../CU-86dtz6h0n_2024-07-10-22-27.json | 10 + packages/blockchain-service/package.json | 6 +- .../blockchain-service/src/BSAggregator.ts | 4 +- .../src/CryptoCompareEDS.ts | 7 +- packages/blockchain-service/src/interfaces.ts | 40 ++-- packages/bs-ethereum/package.json | 2 +- packages/bs-ethereum/src/BSEthereum.ts | 49 ++-- .../bs-ethereum/src/BitqueryBDSEthereum.ts | 53 +++-- .../bs-ethereum/src/BitqueryEDSEthereum.ts | 27 +-- .../bs-ethereum/src/GhostMarketNDSEthereum.ts | 23 +- packages/bs-ethereum/src/RpcBDSEthereum.ts | 34 ++- .../src/__tests__/BSEthereum.spec.ts | 26 +- .../src/__tests__/BitqueryBDSEthereum.spec.ts | 9 +- .../src/__tests__/BitqueryEDSEthereum.spec.ts | 2 +- .../__tests__/GhostMarketNDSEthereum.spec.ts | 7 +- .../__tests__/LedgerServiceEthereum.spec.ts | 2 +- .../src/__tests__/RpcBDSEthereum.spec.ts | 8 +- packages/bs-ethereum/src/assets/abis/ERC20.ts | 222 ++++++++++++++++++ .../bs-ethereum/src/assets/tokens/common.json | 8 - .../bs-ethereum/src/assets/tokens/index.ts | 57 +++++ packages/bs-ethereum/src/constants.ts | 197 +++++++++++++--- packages/bs-neo-legacy/src/BSNeoLegacy.ts | 28 +-- .../src/CryptoCompareEDSNeoLegacy.ts | 28 ++- .../bs-neo-legacy/src/DoraBDSNeoLegacy.ts | 24 +- packages/bs-neo-legacy/src/DoraESNeoLegacy.ts | 12 +- .../src/__tests__/BDSNeoLegacy.spec.ts | 2 +- .../src/__tests__/BSNeoLegacy.spec.ts | 10 +- packages/bs-neo-legacy/src/constants.ts | 13 +- packages/bs-neo3/package.json | 8 +- packages/bs-neo3/src/BSNeo3.ts | 25 +- packages/bs-neo3/src/DoraBDSNeo3.ts | 18 +- packages/bs-neo3/src/DoraESNeo3.ts | 17 +- packages/bs-neo3/src/FlamingoEDSNeo3.ts | 20 +- packages/bs-neo3/src/GhostMarketNDSNeo3.ts | 21 +- packages/bs-neo3/src/RpcBDSNeo3.ts | 10 +- .../bs-neo3/src/__tests__/BDSNeo3.spec.ts | 13 +- packages/bs-neo3/src/__tests__/BSNeo3.spec.ts | 29 +-- .../src/__tests__/GhostMarketNDSNeo3.spec.ts | 3 +- .../bs-neo3/src/assets/tokens/common.json | 4 +- packages/bs-neo3/src/constants.ts | 16 +- .../FlamingoSwapControllerService.ts | 16 +- .../src/flamingo-swap/FlamingoSwapHelper.ts | 16 +- ...lamingoSwapNeonDappKitInvocationBuilder.ts | 26 +- 46 files changed, 851 insertions(+), 331 deletions(-) create mode 100644 common/changes/@cityofzion/blockchain-service/CU-86dtz6h0n_2024-07-10-22-27.json create mode 100644 common/changes/@cityofzion/bs-ethereum/CU-86dtz6h0n_2024-07-10-22-27.json create mode 100644 common/changes/@cityofzion/bs-neo-legacy/CU-86dtz6h0n_2024-07-10-22-27.json create mode 100644 common/changes/@cityofzion/bs-neo3/CU-86dtz6h0n_2024-07-10-22-27.json create mode 100644 packages/bs-ethereum/src/assets/abis/ERC20.ts delete mode 100644 packages/bs-ethereum/src/assets/tokens/common.json create mode 100644 packages/bs-ethereum/src/assets/tokens/index.ts diff --git a/common/changes/@cityofzion/blockchain-service/CU-86dtz6h0n_2024-07-10-22-27.json b/common/changes/@cityofzion/blockchain-service/CU-86dtz6h0n_2024-07-10-22-27.json new file mode 100644 index 0000000..45b0b0b --- /dev/null +++ b/common/changes/@cityofzion/blockchain-service/CU-86dtz6h0n_2024-07-10-22-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/blockchain-service", + "comment": "Adapt network to support multi network", + "type": "major" + } + ], + "packageName": "@cityofzion/blockchain-service" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-ethereum/CU-86dtz6h0n_2024-07-10-22-27.json b/common/changes/@cityofzion/bs-ethereum/CU-86dtz6h0n_2024-07-10-22-27.json new file mode 100644 index 0000000..3d27eac --- /dev/null +++ b/common/changes/@cityofzion/bs-ethereum/CU-86dtz6h0n_2024-07-10-22-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-ethereum", + "comment": "Add support to EVM", + "type": "major" + } + ], + "packageName": "@cityofzion/bs-ethereum" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-neo-legacy/CU-86dtz6h0n_2024-07-10-22-27.json b/common/changes/@cityofzion/bs-neo-legacy/CU-86dtz6h0n_2024-07-10-22-27.json new file mode 100644 index 0000000..47edcf0 --- /dev/null +++ b/common/changes/@cityofzion/bs-neo-legacy/CU-86dtz6h0n_2024-07-10-22-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-neo-legacy", + "comment": "Adapt network to support multi network", + "type": "major" + } + ], + "packageName": "@cityofzion/bs-neo-legacy" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-neo3/CU-86dtz6h0n_2024-07-10-22-27.json b/common/changes/@cityofzion/bs-neo3/CU-86dtz6h0n_2024-07-10-22-27.json new file mode 100644 index 0000000..2151701 --- /dev/null +++ b/common/changes/@cityofzion/bs-neo3/CU-86dtz6h0n_2024-07-10-22-27.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-neo3", + "comment": "Adapt network to support multi network", + "type": "major" + } + ], + "packageName": "@cityofzion/bs-neo3" +} \ No newline at end of file diff --git a/packages/blockchain-service/package.json b/packages/blockchain-service/package.json index 7ff3612..bd8c05e 100644 --- a/packages/blockchain-service/package.json +++ b/packages/blockchain-service/package.json @@ -20,11 +20,11 @@ "@typescript-eslint/parser": "^6.5.0", "eslint": "^8.48.0", "ts-node": "10.9.1", - "typescript": "4.9.5" + "typescript": "4.9.5", + "typed-emitter": "~2.1.0" }, "dependencies": { "@ledgerhq/hw-transport": "~6.30.5", - "axios": "1.5.1", - "typed-emitter": "~2.1.0" + "axios": "1.5.1" } } diff --git a/packages/blockchain-service/src/BSAggregator.ts b/packages/blockchain-service/src/BSAggregator.ts index 4fb58bd..6117d62 100644 --- a/packages/blockchain-service/src/BSAggregator.ts +++ b/packages/blockchain-service/src/BSAggregator.ts @@ -1,4 +1,4 @@ -import { AccountWithDerivationPath, BlockchainService, Network, PartialBy } from './interfaces' +import { AccountWithDerivationPath, BlockchainService, PartialNetwork } from './interfaces' export class BSAggregator< BSCustomName extends string = string, @@ -12,7 +12,7 @@ export class BSAggregator< this.#blockchainServices = Object.values(blockchainServices) } - setNetwork(network: PartialBy) { + setNetwork(network: PartialNetwork) { this.#blockchainServices.forEach(bs => bs.setNetwork(network)) } diff --git a/packages/blockchain-service/src/CryptoCompareEDS.ts b/packages/blockchain-service/src/CryptoCompareEDS.ts index 8c91fca..a4bf258 100644 --- a/packages/blockchain-service/src/CryptoCompareEDS.ts +++ b/packages/blockchain-service/src/CryptoCompareEDS.ts @@ -2,7 +2,6 @@ import { Currency, ExchangeDataService, GetTokenPriceHistory, - NetworkType, Token, TokenPricesHistoryResponse, TokenPricesResponse, @@ -27,19 +26,15 @@ type CryptoCompareHistoryResponse = { } export class CryptoCompareEDS implements ExchangeDataService { - networkType: NetworkType readonly #axiosInstance: AxiosInstance readonly #tokens: Token[] - constructor(network: NetworkType, tokens: Token[] = []) { - this.networkType = network + constructor(tokens: Token[] = []) { this.#tokens = tokens this.#axiosInstance = axios.create({ baseURL: 'https://min-api.cryptocompare.com' }) } async getTokenPrices(currency: Currency): Promise { - if (this.networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet') - const tokenSymbols = this.#tokens.map(token => token.symbol) const { data: prices } = await this.#axiosInstance.get('/data/pricemultifull', { params: { diff --git a/packages/blockchain-service/src/interfaces.ts b/packages/blockchain-service/src/interfaces.ts index aa4c464..4f0ffdc 100644 --- a/packages/blockchain-service/src/interfaces.ts +++ b/packages/blockchain-service/src/interfaces.ts @@ -1,8 +1,6 @@ import Transport from '@ledgerhq/hw-transport' import TypedEmitter from 'typed-emitter' -export type PartialBy = Omit & Partial> - export type Account = { key: string type: 'wif' | 'privateKey' | 'publicKey' @@ -17,11 +15,19 @@ export interface Token { hash: string decimals: number } -export type NetworkType = 'mainnet' | 'testnet' | 'custom' -export type Network = { - type: NetworkType + +export type Network = { + id: T + name: string url: string } + +export type PartialNetwork = { + id: T + name?: string + url?: string +} + export type IntentTransferParam = { receiverAddress: string tokenHash: string @@ -37,15 +43,15 @@ export type TransferParam = { isLedger?: boolean } -export interface BlockchainService { +export interface BlockchainService { readonly blockchainName: BSCustomName readonly derivationPath: string readonly feeToken: Token exchangeDataService: ExchangeDataService blockchainDataService: BlockchainDataService tokens: Token[] - network: Network - setNetwork: (network: PartialBy) => void + network: Network + setNetwork: (partialNetwork: PartialNetwork) => void generateAccountFromMnemonic(mnemonic: string | string, index: number): AccountWithDerivationPath generateAccountFromKey(key: string): Account decrypt(keyOrJson: string, password: string): Promise @@ -267,27 +273,27 @@ export type SwapControllerServiceEvents = { lastAmountChanged: (lastAmountChanged: 'amountToUse' | 'amountToReceive' | null) => void | Promise } -export type SwapControllerServiceSwapArgs = { +export type SwapControllerServiceSwapArgs = { amountToUse: string amountToReceive: string tokenToUse: Token tokenToReceive: Token address: string deadline: string - network: Network + network: Network } -export type SwapControllerServiceSwapToUseArgs = { +export type SwapControllerServiceSwapToUseArgs = { minimumReceived: string type: 'swapTokenToUse' -} & SwapControllerServiceSwapArgs +} & SwapControllerServiceSwapArgs -export type SwapControllerServiceSwapToReceiveArgs = { +export type SwapControllerServiceSwapToReceiveArgs = { maximumSelling: string type: 'swapTokenToReceive' -} & SwapControllerServiceSwapArgs +} & SwapControllerServiceSwapArgs -export interface SwapControllerService { +export interface SwapControllerService { eventEmitter: TypedEmitter setAccountToUse(account: Account | null): void @@ -298,7 +304,9 @@ export interface SwapControllerService { setDeadline(deadline: string): void setSlippage(slippage: number): void swap(isLedger?: boolean): void - buildSwapArgs(): SwapControllerServiceSwapToUseArgs | SwapControllerServiceSwapToReceiveArgs + buildSwapArgs(): + | SwapControllerServiceSwapToUseArgs + | SwapControllerServiceSwapToReceiveArgs setReserves(): void startListeningBlockGeneration(): void stopListeningBlockGeneration(): void diff --git a/packages/bs-ethereum/package.json b/packages/bs-ethereum/package.json index 950feac..323a50b 100644 --- a/packages/bs-ethereum/package.json +++ b/packages/bs-ethereum/package.json @@ -25,13 +25,13 @@ "@ethersproject/json-wallets": "5.7.0", "@ethersproject/bytes": "5.7.0", "@ethersproject/bignumber": "5.7.0", - "@ledgerhq/hw-transport-node-hid": "~6.28.5", "@ledgerhq/hw-transport": "~6.30.5", "@ledgerhq/hw-app-eth": "~6.35.7", "@ethersproject/abstract-signer": "~5.7.0", "@ethersproject/properties": "~5.7.0" }, "devDependencies": { + "@ledgerhq/hw-transport-node-hid": "~6.28.5", "@types/jest": "29.5.3", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", diff --git a/packages/bs-ethereum/src/BSEthereum.ts b/packages/bs-ethereum/src/BSEthereum.ts index 0bcfb51..e212896 100644 --- a/packages/bs-ethereum/src/BSEthereum.ts +++ b/packages/bs-ethereum/src/BSEthereum.ts @@ -10,7 +10,7 @@ import { ExchangeDataService, Network, NftDataService, - PartialBy, + PartialNetwork, Token, TransferParam, } from '@cityofzion/blockchain-service' @@ -18,15 +18,28 @@ import { ethers } from 'ethers' import * as ethersJsonWallets from '@ethersproject/json-wallets' import * as ethersBytes from '@ethersproject/bytes' import * as ethersBigNumber from '@ethersproject/bignumber' -import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NATIVE_ASSETS, TOKENS } from './constants' import { BitqueryEDSEthereum } from './BitqueryEDSEthereum' import { GhostMarketNDSEthereum } from './GhostMarketNDSEthereum' import { RpcBDSEthereum } from './RpcBDSEthereum' import { BitqueryBDSEthereum } from './BitqueryBDSEthereum' import { LedgerServiceEthereum, LedgerSigner } from './LedgerServiceEthereum' import Transport from '@ledgerhq/hw-transport' +import { + AvailableNetworkIds, + BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID, + DEFAULT_URL_BY_NETWORK_ID, + DERIVATION_PATH, + NATIVE_ASSET_BY_NETWORK_ID, + NETWORK_NAME_BY_NETWORK_ID, +} from './constants' + export class BSEthereum - implements BlockchainService, BSWithNft, BSWithNameService, BSCalculableFee, BSWithLedger + implements + BlockchainService, + BSWithNft, + BSWithNameService, + BSCalculableFee, + BSWithLedger { readonly blockchainName: BSCustomName readonly feeToken: Token @@ -37,36 +50,34 @@ export class BSEthereum ledgerService: LedgerServiceEthereum tokens: Token[] nftDataService!: NftDataService - network!: Network + network!: Network constructor( blockchainName: BSCustomName, - network: PartialBy, + network: PartialNetwork, getLedgerTransport?: (account: Account) => Promise ) { this.blockchainName = blockchainName this.ledgerService = new LedgerServiceEthereum(getLedgerTransport) this.derivationPath = DERIVATION_PATH - this.tokens = TOKENS[network.type] - - this.feeToken = this.tokens.find(token => token.symbol === 'ETH')! + this.tokens = [NATIVE_ASSET_BY_NETWORK_ID[network.id]] + this.feeToken = NATIVE_ASSET_BY_NETWORK_ID[network.id] this.setNetwork(network) } - setNetwork(param: PartialBy) { + setNetwork(partialNetwork: PartialNetwork) { const network = { - type: param.type, - url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type], + id: partialNetwork.id, + name: partialNetwork.name ?? NETWORK_NAME_BY_NETWORK_ID[partialNetwork.id], + url: partialNetwork.url ?? DEFAULT_URL_BY_NETWORK_ID[partialNetwork.id], } this.network = network - if (network.type !== 'mainnet') { - this.blockchainDataService = new RpcBDSEthereum(network) - } else { - this.blockchainDataService = new BitqueryBDSEthereum(network) - } + const bitqueryNetwork = BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[partialNetwork.id] + + this.blockchainDataService = bitqueryNetwork ? new BitqueryBDSEthereum(network) : new RpcBDSEthereum(network) - this.exchangeDataService = new BitqueryEDSEthereum(network.type) + this.exchangeDataService = new BitqueryEDSEthereum(network.id) this.nftDataService = new GhostMarketNDSEthereum(network) } @@ -164,7 +175,7 @@ export class BSEthereum let transactionParams: ethers.utils.Deferrable - const isNative = NATIVE_ASSETS.some(asset => asset.hash === param.intent.tokenHash) + const isNative = NATIVE_ASSET_BY_NETWORK_ID[this.network.id].hash === param.intent.tokenHash if (isNative) { transactionParams = { to: param.intent.receiverAddress, @@ -204,7 +215,7 @@ export class BSEthereum let estimated: ethers.BigNumber - const isNative = NATIVE_ASSETS.some(asset => asset.hash === param.intent.tokenHash) + const isNative = NATIVE_ASSET_BY_NETWORK_ID[this.network.id].hash === param.intent.tokenHash const decimals = param.intent.tokenDecimals ?? 18 const amount = ethersBigNumber.parseFixed(param.intent.amount, decimals) diff --git a/packages/bs-ethereum/src/BitqueryBDSEthereum.ts b/packages/bs-ethereum/src/BitqueryBDSEthereum.ts index 54c505f..3733351 100644 --- a/packages/bs-ethereum/src/BitqueryBDSEthereum.ts +++ b/packages/bs-ethereum/src/BitqueryBDSEthereum.ts @@ -1,7 +1,6 @@ import { BalanceResponse, ContractResponse, - NetworkType, Token, TransactionsByAddressParams, TransactionsByAddressResponse, @@ -10,7 +9,12 @@ import { TransactionTransferNft, Network, } from '@cityofzion/blockchain-service' -import { BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE, BITQUERY_MIRROR_URL, NATIVE_ASSETS, TOKENS } from './constants' +import { + AvailableNetworkIds, + BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID, + BITQUERY_MIRROR_URL, + NATIVE_ASSET_BY_NETWORK_ID, +} from './constants' import { RpcBDSEthereum } from './RpcBDSEthereum' import axios, { AxiosInstance } from 'axios' @@ -100,15 +104,15 @@ type BitqueryGetBalanceResponse = { export class BitqueryBDSEthereum extends RpcBDSEthereum { readonly #client: AxiosInstance - readonly #networkType: Exclude + readonly #networkId: AvailableNetworkIds + readonly #tokenCache: Map = new Map() maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 8 - constructor(network: Network) { + constructor(network: Network) { super(network) - if (network.type === 'custom') throw new Error('Custom network not supported') - this.#networkType = network.type + this.#networkId = network.id this.#client = axios.create({ baseURL: BITQUERY_MIRROR_URL, @@ -117,7 +121,7 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { async getTransaction(hash: string): Promise { const result = await this.#client.get(`/get-transaction/${hash}`, { - params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE[this.#networkType] }, + params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[this.#networkId] }, }) if (!result.data || !result.data.ethereum.transfers.length) throw new Error('Transaction not found') @@ -150,7 +154,7 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { const offset = limit * (page - 1) const result = await this.#client.get(`/get-transactions/${address}`, { - params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE[this.#networkType], limit, offset }, + params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[this.#networkId], limit, offset }, }) if (!result.data) throw new Error('Address does not have transactions') @@ -192,11 +196,12 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { } async getTokenInfo(hash: string): Promise { - const localToken = TOKENS[this.#networkType].find(token => token.hash === hash) - if (localToken) return localToken + if (this.#tokenCache.has(hash)) { + return this.#tokenCache.get(hash)! + } const result = await this.#client.get(`/get-token-info/${hash}`, { - params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE[this.#networkType] }, + params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[this.#networkId] }, }) if (!result.data || result.data.ethereum.smartContractCalls.length <= 0) throw new Error('Token not found') @@ -208,32 +213,36 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { if (tokenType !== 'ERC20') throw new Error('Token is not ERC20') - return { + const token = { hash: address, - name, symbol, decimals, + name, } + + this.#tokenCache.set(hash, token) + + return token } async getBalance(address: string): Promise { const result = await this.#client.get(`/get-balance/${address}`, { - params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE[this.#networkType] }, + params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[this.#networkId] }, }) const data = result.data?.ethereum.address[0].balances ?? [] - const ethBalance = result.data?.ethereum.address[0].balance ?? 0 - const ethToken = NATIVE_ASSETS.find(asset => asset.symbol === 'ETH')! + const nativeBalance = result.data?.ethereum.address[0].balance ?? 0 + const nativeToken = NATIVE_ASSET_BY_NETWORK_ID[this.#networkId] const balances: BalanceResponse[] = [ { - amount: ethBalance.toString(), - token: ethToken, + amount: nativeBalance.toString(), + token: nativeToken, }, ] - data.forEach(({ value, currency: { address, decimals, name, symbol } }) => { - if (value < 0 || address === ethToken.hash) return + data.forEach(({ value, currency: { address, decimals, symbol, name } }) => { + if (value < 0 || address === nativeToken.hash) return balances.push({ amount: value.toString(), @@ -251,7 +260,7 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { private parseTransactionTransfer({ amount, - currency: { tokenType, address, decimals, name, symbol }, + currency: { tokenType, address, decimals, symbol, name }, entityId, sender, receiver, @@ -274,8 +283,8 @@ export class BitqueryBDSEthereum extends RpcBDSEthereum { token: { decimals: decimals, hash: address, - name: name, symbol: symbol, + name: name, }, type: 'token', } diff --git a/packages/bs-ethereum/src/BitqueryEDSEthereum.ts b/packages/bs-ethereum/src/BitqueryEDSEthereum.ts index 36e3863..c718eed 100644 --- a/packages/bs-ethereum/src/BitqueryEDSEthereum.ts +++ b/packages/bs-ethereum/src/BitqueryEDSEthereum.ts @@ -1,14 +1,13 @@ -import { - CryptoCompareEDS, - Currency, - ExchangeDataService, - NetworkType, - TokenPricesResponse, -} from '@cityofzion/blockchain-service' +import { CryptoCompareEDS, Currency, ExchangeDataService, TokenPricesResponse } from '@cityofzion/blockchain-service' import axios, { AxiosInstance } from 'axios' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' -import { BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE, BITQUERY_MIRROR_URL, TOKENS } from './constants' +import { + AvailableNetworkIds, + BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID, + BITQUERY_MIRROR_URL, + NATIVE_ASSET_BY_NETWORK_ID, +} from './constants' type BitQueryGetTokenPricesResponse = { ethereum: { @@ -32,24 +31,22 @@ type BitQueryGetTokenPricesResponse = { dayjs.extend(utc) export class BitqueryEDSEthereum extends CryptoCompareEDS implements ExchangeDataService { readonly #client: AxiosInstance - readonly #networkType: NetworkType + readonly #networkId: AvailableNetworkIds - constructor(networkType: NetworkType) { - super(networkType, TOKENS[networkType]) + constructor(networkId: AvailableNetworkIds) { + super([NATIVE_ASSET_BY_NETWORK_ID[networkId]]) - this.#networkType = networkType + this.#networkId = networkId this.#client = axios.create({ baseURL: BITQUERY_MIRROR_URL, }) } async getTokenPrices(currency: Currency): Promise { - if (this.#networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet') - const twoDaysAgo = dayjs.utc().subtract(2, 'day').startOf('date').toISOString() const result = await this.#client.get(`/get-price`, { - params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE[this.#networkType], after: twoDaysAgo }, + params: { network: BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID[this.#networkId], after: twoDaysAgo }, }) if (!result.data) { diff --git a/packages/bs-ethereum/src/GhostMarketNDSEthereum.ts b/packages/bs-ethereum/src/GhostMarketNDSEthereum.ts index 9a738df..e0cdc6e 100644 --- a/packages/bs-ethereum/src/GhostMarketNDSEthereum.ts +++ b/packages/bs-ethereum/src/GhostMarketNDSEthereum.ts @@ -1,18 +1,9 @@ -import { - NftResponse, - NftsResponse, - NetworkType, - NftDataService, - GetNftParam, - GetNftsByAddressParams, - HasTokenParam, - Network, -} from '@cityofzion/blockchain-service' +import { NftResponse, NftsResponse, GetNftParam, GetNftsByAddressParams, Network } from '@cityofzion/blockchain-service' import qs from 'query-string' import axios from 'axios' -import { GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants' import { RpcNDSEthereum } from './RpcNDSEthereum' +import { AvailableNetworkIds, GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants' type GhostMarketNFT = { tokenId: string @@ -50,11 +41,11 @@ type GhostMarketAssets = { next: string } export class GhostMarketNDSEthereum extends RpcNDSEthereum { - #networkType: NetworkType + #networkId: AvailableNetworkIds - constructor(network: Network) { + constructor(network: Network) { super(network) - this.#networkType = network.type + this.#networkId = network.id } async getNftsByAddress({ address, size = 18, cursor }: GetNftsByAddressParams): Promise { @@ -98,12 +89,12 @@ export class GhostMarketNDSEthereum extends RpcNDSEthereum { private getUrlWithParams(params: any) { const parameters = qs.stringify( { - chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.#networkType], + chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.#networkId], ...params, }, { arrayFormat: 'bracket' } ) - return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.#networkType]}/assets?${parameters}` + return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.#networkId]}/assets?${parameters}` } private parse(data: GhostMarketNFT) { diff --git a/packages/bs-ethereum/src/RpcBDSEthereum.ts b/packages/bs-ethereum/src/RpcBDSEthereum.ts index 568c1f5..5b06c44 100644 --- a/packages/bs-ethereum/src/RpcBDSEthereum.ts +++ b/packages/bs-ethereum/src/RpcBDSEthereum.ts @@ -10,14 +10,15 @@ import { TransactionsByAddressResponse, } from '@cityofzion/blockchain-service' import { ethers } from 'ethers' -import { RPC_LIST_BY_NETWORK_TYPE, TOKENS } from './constants' +import { AvailableNetworkIds, NATIVE_ASSET_BY_NETWORK_ID, RPC_LIST_BY_NETWORK_ID } from './constants' +import { ERC20_ABI } from './assets/abis/ERC20' export class RpcBDSEthereum implements BlockchainDataService { - readonly #network: Network + readonly #network: Network maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 5 - constructor(network: Network) { + constructor(network: Network) { this.#network = network } @@ -30,8 +31,7 @@ export class RpcBDSEthereum implements BlockchainDataService { const block = await provider.getBlock(transaction.blockHash) if (!block) throw new Error('Block not found') - const tokens = TOKENS[this.#network.type] - const token = tokens.find(token => token.symbol === 'ETH')! + const token = NATIVE_ASSET_BY_NETWORK_ID[this.#network.id] return { block: block.number, @@ -60,19 +60,27 @@ export class RpcBDSEthereum implements BlockchainDataService { } async getTokenInfo(hash: string): Promise { - const tokens = TOKENS[this.#network.type] - const token = tokens.find(token => token.hash === hash) - if (!token) throw new Error('Token not found') + if (NATIVE_ASSET_BY_NETWORK_ID[this.#network.id].hash === hash) return NATIVE_ASSET_BY_NETWORK_ID[this.#network.id] - return token + const provider = new ethers.providers.JsonRpcProvider(this.#network.url) + const contract = new ethers.Contract(hash, ERC20_ABI, provider) + + const decimals = await contract.decimals() + const symbol = await contract.symbol() + + return { + decimals, + symbol, + hash, + name: symbol, + } } async getBalance(address: string): Promise { const provider = new ethers.providers.JsonRpcProvider(this.#network.url) const balance = await provider.getBalance(address) - const tokens = TOKENS[this.#network.type] - const token = tokens.find(token => token.symbol === 'ETH')! + const token = NATIVE_ASSET_BY_NETWORK_ID[this.#network.id] return [ { @@ -90,7 +98,7 @@ export class RpcBDSEthereum implements BlockchainDataService { async getRpcList(): Promise { const list: RpcResponse[] = [] - const promises = RPC_LIST_BY_NETWORK_TYPE[this.#network.type].map(url => { + const promises = RPC_LIST_BY_NETWORK_ID[this.#network.id].map(url => { // eslint-disable-next-line no-async-promise-executor return new Promise(async resolve => { const timeout = setTimeout(() => { @@ -109,6 +117,8 @@ export class RpcBDSEthereum implements BlockchainDataService { height, latency, }) + } catch { + /* empty */ } finally { resolve() clearTimeout(timeout) diff --git a/packages/bs-ethereum/src/__tests__/BSEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/BSEthereum.spec.ts index a19f043..3e12d9b 100644 --- a/packages/bs-ethereum/src/__tests__/BSEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/BSEthereum.spec.ts @@ -8,15 +8,15 @@ let wallet: ethers.Wallet let account: Account describe('BSEthereum', () => { - beforeAll(async () => { - bsEthereum = new BSEthereum('neo3', { type: 'testnet' }) + beforeEach(async () => { + bsEthereum = new BSEthereum('neo3', { id: '11155111' }) wallet = ethers.Wallet.createRandom() account = { key: wallet.privateKey, type: 'privateKey', address: wallet.address, } - }, 60000) + }) it('Should be able to validate an address', () => { const validAddress = '0xD81a8F3c3f8b006Ef1ae4a2Fd28699AD7E3e21C5' @@ -127,7 +127,7 @@ describe('BSEthereum', () => { it.skip('Should be able to transfer a native token with ledger', async () => { const transport = await TransportNodeHid.create() - const service = new BSEthereum('neo3', { type: 'testnet' }, async () => transport) + const service = new BSEthereum('neo3', { id: '11155111' }, async () => transport) const publicKey = await service.ledgerService.getPublicKey(transport) const account = service.generateAccountFromPublicKey(publicKey) @@ -148,7 +148,7 @@ describe('BSEthereum', () => { it.skip('Should be able to transfer a ERC20 token with ledger', async () => { const transport = await TransportNodeHid.create() - const service = new BSEthereum('neo3', { type: 'testnet' }, async () => transport) + const service = new BSEthereum('neo3', { id: '11155111' }, async () => transport) const publicKey = await service.ledgerService.getPublicKey(transport) const account = service.generateAccountFromPublicKey(publicKey) @@ -171,4 +171,20 @@ describe('BSEthereum', () => { const address = await bsEthereum.resolveNameServiceDomain('alice.eth') expect(address).toEqual('0xa974890156A3649A23a6C0f2ebd77D6F7A7333d4') }, 10000) + + it.skip('Should be able to transfer a native token using a EVM', async () => { + bsEthereum.setNetwork({ id: '80002' }) + const account = bsEthereum.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string) + const transactionHash = await bsEthereum.transfer({ + senderAccount: account, + intent: { + amount: '0.00000001', + receiverAddress: '0x82B5Cd984880C8A821429cFFf89f36D35BaeBE89', + tokenDecimals: 18, + tokenHash: '-', + }, + }) + + expect(transactionHash).toEqual(expect.any(String)) + }) }) diff --git a/packages/bs-ethereum/src/__tests__/BitqueryBDSEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/BitqueryBDSEthereum.spec.ts index 84f4889..605a855 100644 --- a/packages/bs-ethereum/src/__tests__/BitqueryBDSEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/BitqueryBDSEthereum.spec.ts @@ -1,7 +1,11 @@ import { BitqueryBDSEthereum } from '../BitqueryBDSEthereum' -import { DEFAULT_URL_BY_NETWORK_TYPE } from '../constants' +import { DEFAULT_URL_BY_NETWORK_ID, NETWORK_NAME_BY_NETWORK_ID } from '../constants' -const bitqueryBDSEthereum = new BitqueryBDSEthereum({ type: 'mainnet', url: DEFAULT_URL_BY_NETWORK_TYPE.mainnet }) +const bitqueryBDSEthereum = new BitqueryBDSEthereum({ + id: '1', + url: DEFAULT_URL_BY_NETWORK_ID['1'], + name: NETWORK_NAME_BY_NETWORK_ID['1'], +}) describe('BitqueryBDSEthereum', () => { it('Should be able to get transaction - %s', async () => { @@ -103,7 +107,6 @@ describe('BitqueryBDSEthereum', () => { it('Should be able to get a list of rpc - %s', async () => { const list = await bitqueryBDSEthereum.getRpcList() - console.log(list) expect(list.length).toBeGreaterThan(0) list.forEach(rpc => { expect(rpc).toEqual({ diff --git a/packages/bs-ethereum/src/__tests__/BitqueryEDSEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/BitqueryEDSEthereum.spec.ts index 7fae843..7acefe8 100644 --- a/packages/bs-ethereum/src/__tests__/BitqueryEDSEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/BitqueryEDSEthereum.spec.ts @@ -4,7 +4,7 @@ let bitqueryEDSEthereum: BitqueryEDSEthereum describe('FlamingoEDSNeo3', () => { beforeAll(() => { - bitqueryEDSEthereum = new BitqueryEDSEthereum('mainnet') + bitqueryEDSEthereum = new BitqueryEDSEthereum('1') }) it('Should return a list with prices of tokens using USD', async () => { diff --git a/packages/bs-ethereum/src/__tests__/GhostMarketNDSEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/GhostMarketNDSEthereum.spec.ts index 01ade6e..d78fd9f 100644 --- a/packages/bs-ethereum/src/__tests__/GhostMarketNDSEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/GhostMarketNDSEthereum.spec.ts @@ -1,13 +1,14 @@ import { GhostMarketNDSEthereum } from '../GhostMarketNDSEthereum' -import { DEFAULT_URL_BY_NETWORK_TYPE } from '../constants' +import { DEFAULT_URL_BY_NETWORK_ID, NETWORK_NAME_BY_NETWORK_ID } from '../constants' let ghostMarketNDSEthereum: GhostMarketNDSEthereum describe('GhostMarketNDSEthereum', () => { beforeAll(() => { ghostMarketNDSEthereum = new GhostMarketNDSEthereum({ - type: 'mainnet', - url: DEFAULT_URL_BY_NETWORK_TYPE.mainnet, + id: '1', + url: DEFAULT_URL_BY_NETWORK_ID['1'], + name: NETWORK_NAME_BY_NETWORK_ID['1'], }) }) diff --git a/packages/bs-ethereum/src/__tests__/LedgerServiceEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/LedgerServiceEthereum.spec.ts index fe199a2..8f42c1a 100644 --- a/packages/bs-ethereum/src/__tests__/LedgerServiceEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/LedgerServiceEthereum.spec.ts @@ -4,7 +4,7 @@ import { ethers } from 'ethers' let ledgerSigner: LedgerSigner -describe('LedgerServiceEthereum', () => { +describe.skip('LedgerServiceEthereum', () => { beforeAll(async () => { const transport = await TransportNodeHid.create() ledgerSigner = new LedgerSigner(transport) diff --git a/packages/bs-ethereum/src/__tests__/RpcBDSEthereum.spec.ts b/packages/bs-ethereum/src/__tests__/RpcBDSEthereum.spec.ts index c76ab3a..3c130c3 100644 --- a/packages/bs-ethereum/src/__tests__/RpcBDSEthereum.spec.ts +++ b/packages/bs-ethereum/src/__tests__/RpcBDSEthereum.spec.ts @@ -1,7 +1,11 @@ import { RpcBDSEthereum } from '../RpcBDSEthereum' -import { DEFAULT_URL_BY_NETWORK_TYPE } from '../constants' +import { DEFAULT_URL_BY_NETWORK_ID, NETWORK_NAME_BY_NETWORK_ID } from '../constants' -const rpcBDSEthereum = new RpcBDSEthereum({ type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }) +const rpcBDSEthereum = new RpcBDSEthereum({ + id: '11155111', + url: DEFAULT_URL_BY_NETWORK_ID['11155111'], + name: NETWORK_NAME_BY_NETWORK_ID['11155111'], +}) describe('RpcBDSEthereum', () => { it('Should be able to get transaction', async () => { diff --git a/packages/bs-ethereum/src/assets/abis/ERC20.ts b/packages/bs-ethereum/src/assets/abis/ERC20.ts new file mode 100644 index 0000000..73aba49 --- /dev/null +++ b/packages/bs-ethereum/src/assets/abis/ERC20.ts @@ -0,0 +1,222 @@ +export const ERC20_ABI = [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [ + { + name: '', + type: 'string', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { + name: '_spender', + type: 'address', + }, + { + name: '_value', + type: 'uint256', + }, + ], + name: 'approve', + outputs: [ + { + name: '', + type: 'bool', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { + name: '_from', + type: 'address', + }, + { + name: '_to', + type: 'address', + }, + { + name: '_value', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + name: '', + type: 'bool', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [ + { + name: '', + type: 'uint8', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + name: 'balance', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [ + { + name: '', + type: 'string', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { + name: '_to', + type: 'address', + }, + { + name: '_value', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + name: '', + type: 'bool', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address', + }, + { + name: '_spender', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + payable: true, + stateMutability: 'payable', + type: 'fallback', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'owner', + type: 'address', + }, + { + indexed: true, + name: 'spender', + type: 'address', + }, + { + indexed: false, + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: 'from', + type: 'address', + }, + { + indexed: true, + name: 'to', + type: 'address', + }, + { + indexed: false, + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, +] diff --git a/packages/bs-ethereum/src/assets/tokens/common.json b/packages/bs-ethereum/src/assets/tokens/common.json deleted file mode 100644 index 96597dd..0000000 --- a/packages/bs-ethereum/src/assets/tokens/common.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "symbol": "ETH", - "name": "Ethereum", - "hash": "-", - "decimals": 18 - } -] diff --git a/packages/bs-ethereum/src/assets/tokens/index.ts b/packages/bs-ethereum/src/assets/tokens/index.ts new file mode 100644 index 0000000..c97914a --- /dev/null +++ b/packages/bs-ethereum/src/assets/tokens/index.ts @@ -0,0 +1,57 @@ +import { Token } from '@cityofzion/blockchain-service' + +export const ETH: Token = { + symbol: 'ETH', + hash: '-', + decimals: 18, + name: 'ETH', +} + +export const MATIC: Token = { + symbol: 'MATIC', + hash: '-', + decimals: 18, + name: 'MATIC', +} + +export const BNB: Token = { + symbol: 'BNB', + hash: '-', + decimals: 18, + name: 'BNB', +} + +export const FTM: Token = { + symbol: 'FTM', + hash: '-', + decimals: 18, + name: 'FTM', +} + +export const CELO: Token = { + symbol: 'CELO', + hash: '-', + decimals: 18, + name: 'CELO', +} + +export const AVAX: Token = { + symbol: 'AVAX', + hash: '-', + decimals: 18, + name: 'AVAX', +} + +export const CRO: Token = { + symbol: 'CRO', + hash: '-', + decimals: 18, + name: 'CRO', +} + +export const GAS: Token = { + symbol: 'GAS', + hash: '-', + decimals: 18, + name: 'GAS', +} diff --git a/packages/bs-ethereum/src/constants.ts b/packages/bs-ethereum/src/constants.ts index e878a3f..a69d6dc 100644 --- a/packages/bs-ethereum/src/constants.ts +++ b/packages/bs-ethereum/src/constants.ts @@ -1,44 +1,122 @@ -import { NetworkType, Token } from '@cityofzion/blockchain-service' -import commom from './assets/tokens/common.json' +import { Token } from '@cityofzion/blockchain-service' +import { AVAX, BNB, CELO, CRO, ETH, FTM, GAS, MATIC } from './assets/tokens' -export type BitqueryNetwork = 'ethereum' | 'goerli' +export type AvailableNetworkIds = + | '1' + | '10' + | '25' + | '56' + | '137' + | '250' + | '1101' + | '8453' + | '80002' + | '42161' + | '42220' + | '43114' + | '59144' + | '11155111' + | '12227331' -export const TOKENS: Record = { - mainnet: [...commom], - testnet: commom, - custom: commom, -} - -export const NATIVE_ASSETS = commom - -export const BITQUERY_MIRROR_URL = 'https://i4l7kcg43c.execute-api.us-east-1.amazonaws.com/production/' -export const BITQUERY_MIRROR_NETWORK_BY_NETWORK_TYPE: Record, BitqueryNetwork> = { - mainnet: 'ethereum', - testnet: 'goerli', -} - -export const GHOSTMARKET_URL_BY_NETWORK_TYPE: Partial> = { - mainnet: 'https://api.ghostmarket.io/api/v2', - testnet: 'https://api-testnet.ghostmarket.io/api/v2', -} +export const DERIVATION_PATH = "m/44'/60'/0'/0/?" +export const DEFAULT_PATH = "44'/60'/0'/0/0" -export const GHOSTMARKET_CHAIN_BY_NETWORK_TYPE: Partial> = { - mainnet: 'eth', - testnet: 'etht', +export const NATIVE_ASSET_BY_NETWORK_ID: Record = { + '1': ETH, + '10': ETH, + '25': CRO, + '56': BNB, + '137': MATIC, + '1101': ETH, + '250': FTM, + '8453': ETH, + '80002': MATIC, + '42161': ETH, + '42220': CELO, + '43114': AVAX, + '59144': ETH, + '11155111': ETH, + '12227331': GAS, } -export const DERIVATION_PATH = "m/44'/60'/0'/0/?" - -export const RPC_LIST_BY_NETWORK_TYPE: Record = { - mainnet: [ - 'https://ethereum-mainnet-rpc.allthatnode.com', +export const RPC_LIST_BY_NETWORK_ID: Record = { + '1': [ 'https://eth.llamarpc.com', + 'https://mainnet.infura.io/v3/', 'https://ethereum-rpc.publicnode.com', 'https://endpoints.omniatech.io/v1/eth/mainnet/public', 'https://rpc.flashbots.net', 'https://rpc.mevblocker.io', ], - testnet: [ + '10': [ + 'https://optimism.llamarpc.com', + 'https://endpoints.omniatech.io/v1/op/mainnet/public', + 'https://optimism-rpc.publicnode.com', + 'https://optimism.meowrpc.com', + 'https://optimism.rpc.subquery.network/public', + ], + '25': ['https://cronos-evm-rpc.publicnode.com', 'https://1rpc.io/cro', 'https://rpc.vvs.finance'], + '56': [ + 'https://bsc-dataseed.binance.org/', + 'https://binance.llamarpc.com', + 'https://bsc-dataseed.bnbchain.org', + 'https://endpoints.omniatech.io/v1/bsc/mainnet/public', + 'https://bsc-rpc.publicnode.com', + ], + '137': [ + 'https://polygon-mainnet.infura.io', + 'https://polygon.llamarpc.com', + 'https://endpoints.omniatech.io/v1/matic/mainnet/public', + 'https://polygon.drpc.org', + 'https://polygon.meowrpc.com', + ], + '250': [ + 'https://endpoints.omniatech.io/v1/fantom/mainnet/public', + 'https://rpcapi.fantom.network', + 'https://fantom-pokt.nodies.app', + 'https://fantom-rpc.publicnode.com', + 'https://fantom.drpc.org', + ], + '1101': [ + 'https://polygon-zkevm.drpc.org', + 'https://polygon-zkevm.blockpi.network/v1/rpc/public', + 'https://1rpc.io/polygon/zkevm', + ], + '80002': [ + 'https://polygon-amoy.drpc.org', + 'https://rpc.ankr.com/polygon_amoy', + 'https://polygon-amoy-bor-rpc.publicnode.com', + ], + '8453': [ + 'https://base.rpc.subquery.network/public', + 'https://base.llamarpc.com', + 'https://mainnet.base.org', + 'https://1rpc.io/base', + 'https://base.meowrpc.com', + 'https://base-rpc.publicnode.com', + 'https://endpoints.omniatech.io/v1/base/mainnet/public', + ], + '42161': [ + 'https://arbitrum.llamarpc.com', + 'https://arbitrum-one-rpc.publicnode.com', + 'https://arb-mainnet-public.unifra.io', + 'https://arbitrum-one.publicnode.com', + ], + '42220': [ + 'https://forno.celo.org', + 'https://api.tatum.io/v3/blockchain/node/celo-mainnet', + 'https://rpc.ankr.com/celo', + ], + '43114': [ + 'https://avalanche-mainnet.infura.io', + 'https://avalanche-c-chain-rpc.publicnode.com', + 'https://avalanche.public-rpc.com', + 'https://endpoints.omniatech.io/v1/avax/mainnet/public', + 'https://avalanche.drpc.org', + ], + '59144': ['https://linea.decubate.com', 'https://linea.blockpi.network/v1/rpc/public', 'https://linea.decubate.com'], + '11155111': [ + 'https://ethereum-sepolia.rpc.subquery.network/public', 'https://ethereum-sepolia-rpc.publicnode.com', 'https://endpoints.omniatech.io/v1/eth/sepolia/public', 'https://eth-sepolia.public.blastapi.io', @@ -46,13 +124,60 @@ export const RPC_LIST_BY_NETWORK_TYPE: Record = { 'https://1rpc.io/sepolia', 'https://eth-sepolia.api.onfinality.io/public', ], - custom: ['http://127.0.0.1:8545'], + '12227331': ['https://neoxseed1.ngd.network'], } -export const DEFAULT_URL_BY_NETWORK_TYPE: Record = { - mainnet: RPC_LIST_BY_NETWORK_TYPE.mainnet[0], - testnet: RPC_LIST_BY_NETWORK_TYPE.testnet[0], - custom: RPC_LIST_BY_NETWORK_TYPE.custom[0], +export const DEFAULT_URL_BY_NETWORK_ID: Record = { + '1': RPC_LIST_BY_NETWORK_ID['1'][0], + '10': RPC_LIST_BY_NETWORK_ID['10'][0], + '25': RPC_LIST_BY_NETWORK_ID['25'][0], + '56': RPC_LIST_BY_NETWORK_ID['56'][0], + '137': RPC_LIST_BY_NETWORK_ID['137'][0], + '250': RPC_LIST_BY_NETWORK_ID['250'][0], + '1101': RPC_LIST_BY_NETWORK_ID['1101'][0], + '8453': RPC_LIST_BY_NETWORK_ID['8453'][0], + '80002': RPC_LIST_BY_NETWORK_ID['80002'][0], + '42161': RPC_LIST_BY_NETWORK_ID['42161'][0], + '42220': RPC_LIST_BY_NETWORK_ID['42220'][0], + '43114': RPC_LIST_BY_NETWORK_ID['43114'][0], + '59144': RPC_LIST_BY_NETWORK_ID['59144'][0], + '11155111': RPC_LIST_BY_NETWORK_ID['11155111'][0], + '12227331': RPC_LIST_BY_NETWORK_ID['12227331'][0], } -export const DEFAULT_PATH = "44'/60'/0'/0/0" +export const NETWORK_NAME_BY_NETWORK_ID: Record = { + '1': 'Ethereum Mainnet', + '10': 'Optimism Mainnet', + '25': 'Cronos Mainnet', + '56': 'Binance Smart Chain Mainnet', + '137': 'Polygon Mainnet', + '250': 'Fantom Opera', + '1101': 'Polygon zkEVM', + '8453': 'Base', + '80002': 'Polygon Amoy', + '42161': 'Arbitrum One', + '42220': 'Celo Mainnet', + '43114': 'Avalanche C-Chain', + '59144': 'Linea', + '11155111': 'Ethereum Sepolia', + '12227331': 'NeoX Testnet', +} + +export const BITQUERY_MIRROR_URL = 'https://i4l7kcg43c.execute-api.us-east-1.amazonaws.com/production/' +export const BITQUERY_MIRROR_NETWORK_BY_NETWORK_ID: Partial> = { + '1': 'ethereum', + '25': 'cronos', + '56': 'bsc', + '137': 'matic', + '250': 'fantom', + '42220': 'celo_mainnet', + '43114': 'avalanche', +} + +export const GHOSTMARKET_URL_BY_NETWORK_TYPE: Partial> = { + 1: 'https://api.ghostmarket.io/api/v2', +} + +export const GHOSTMARKET_CHAIN_BY_NETWORK_TYPE: Partial> = { + 1: 'eth', +} diff --git a/packages/bs-neo-legacy/src/BSNeoLegacy.ts b/packages/bs-neo-legacy/src/BSNeoLegacy.ts index fddddba..4629f27 100644 --- a/packages/bs-neo-legacy/src/BSNeoLegacy.ts +++ b/packages/bs-neo-legacy/src/BSNeoLegacy.ts @@ -7,14 +7,15 @@ import { ExchangeDataService, Token, Network, - PartialBy, TransferParam, AccountWithDerivationPath, BSWithExplorerService, ExplorerService, + PartialNetwork, } from '@cityofzion/blockchain-service' import { api, sc, u, wallet } from '@cityofzion/neon-js' import { + AvailableNetworkIds, DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, LEGACY_NETWORK_BY_NETWORK_TYPE, @@ -27,7 +28,7 @@ import { keychain } from '@cityofzion/bs-asteroid-sdk' import { DoraESNeoLegacy } from './DoraESNeoLegacy' export class BSNeoLegacy - implements BlockchainService, BSClaimable, BSWithExplorerService + implements BlockchainService, BSClaimable, BSWithExplorerService { readonly blockchainName: BSCustomName readonly feeToken: Token @@ -39,33 +40,30 @@ export class BSNeoLegacy exchangeDataService!: ExchangeDataService explorerService!: ExplorerService tokens: Token[] - network!: Network + network!: Network legacyNetwork: string - constructor(blockchainName: BSCustomName, network: PartialBy) { - if (network.type === 'custom') throw new Error('Custom network is not supported for NEO Legacy') - + constructor(blockchainName: BSCustomName, network: PartialNetwork) { this.blockchainName = blockchainName - this.legacyNetwork = LEGACY_NETWORK_BY_NETWORK_TYPE[network.type] + this.legacyNetwork = LEGACY_NETWORK_BY_NETWORK_TYPE[network.id] this.derivationPath = DERIVATION_PATH - this.tokens = TOKENS[network.type] + this.tokens = TOKENS[network.id] this.claimToken = this.tokens.find(token => token.symbol === 'GAS')! this.burnToken = this.tokens.find(token => token.symbol === 'NEO')! this.feeToken = this.tokens.find(token => token.symbol === 'GAS')! this.setNetwork(network) } - setNetwork(param: PartialBy) { - if (param.type === 'custom') throw new Error('Custom network is not supported for NEO Legacy') - + setNetwork(param: PartialNetwork) { const network = { - type: param.type, - url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type], + id: param.id, + name: param.name ?? param.id, + url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.id], } this.network = network this.blockchainDataService = new DoraBDSNeoLegacy(network, this.feeToken, this.claimToken) - this.exchangeDataService = new CryptoCompareEDSNeoLegacy(network.type) - this.explorerService = new DoraESNeoLegacy(network.type) + this.exchangeDataService = new CryptoCompareEDSNeoLegacy(network.id) + this.explorerService = new DoraESNeoLegacy(network.id) } validateAddress(address: string): boolean { diff --git a/packages/bs-neo-legacy/src/CryptoCompareEDSNeoLegacy.ts b/packages/bs-neo-legacy/src/CryptoCompareEDSNeoLegacy.ts index f355369..b971e69 100644 --- a/packages/bs-neo-legacy/src/CryptoCompareEDSNeoLegacy.ts +++ b/packages/bs-neo-legacy/src/CryptoCompareEDSNeoLegacy.ts @@ -1,8 +1,28 @@ -import { CryptoCompareEDS, ExchangeDataService, NetworkType } from '@cityofzion/blockchain-service' -import { TOKENS } from './constants' +import { + CryptoCompareEDS, + Currency, + ExchangeDataService, + GetTokenPriceHistory, + TokenPricesHistoryResponse, + TokenPricesResponse, +} from '@cityofzion/blockchain-service' +import { AvailableNetworkIds, TOKENS } from './constants' export class CryptoCompareEDSNeoLegacy extends CryptoCompareEDS implements ExchangeDataService { - constructor(networkType: NetworkType) { - super(networkType, TOKENS[networkType]) + #networkId: AvailableNetworkIds + + constructor(networkId: AvailableNetworkIds) { + super(TOKENS[networkId]) + this.#networkId = networkId + } + + getTokenPriceHistory(params: GetTokenPriceHistory): Promise { + if (this.#networkId !== 'mainnet') throw new Error('Exchange is only available on mainnet') + return super.getTokenPriceHistory(params) + } + + getTokenPrices(currency: Currency): Promise { + if (this.#networkId !== 'mainnet') throw new Error('Exchange is only available on mainnet') + return super.getTokenPrices(currency) } } diff --git a/packages/bs-neo-legacy/src/DoraBDSNeoLegacy.ts b/packages/bs-neo-legacy/src/DoraBDSNeoLegacy.ts index 6bb0bbc..becb0fb 100644 --- a/packages/bs-neo-legacy/src/DoraBDSNeoLegacy.ts +++ b/packages/bs-neo-legacy/src/DoraBDSNeoLegacy.ts @@ -10,29 +10,27 @@ import { Token, Network, RpcResponse, - NetworkType, } from '@cityofzion/blockchain-service' import { api } from '@cityofzion/dora-ts' -import { RPC_LIST_BY_NETWORK_TYPE, TOKENS } from './constants' +import { AvailableNetworkIds, RPC_LIST_BY_NETWORK_TYPE, TOKENS } from './constants' import { rpc } from '@cityofzion/neon-js' export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { - readonly #network: Network + readonly #network: Network readonly #claimToken: Token readonly #feeToken: Token readonly #tokenCache: Map = new Map() maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 2 - constructor(network: Network, feeToken: Token, claimToken: Token) { - if (network.type === 'custom') throw new Error('Custom network is not supported for NEO Legacy') + constructor(network: Network, feeToken: Token, claimToken: Token) { this.#network = network this.#claimToken = claimToken this.#feeToken = feeToken } async getTransaction(hash: string): Promise { - const data = await api.NeoLegacyREST.transaction(hash, this.#network.type) + const data = await api.NeoLegacyREST.transaction(hash, this.#network.id) if (!data || 'error' in data) throw new Error(`Transaction ${hash} not found`) const vout: any[] = data.vout ?? [] @@ -64,7 +62,7 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { address, page = 1, }: TransactionsByAddressParams): Promise { - const data = await api.NeoLegacyREST.getAddressAbstracts(address, page, this.#network.type) + const data = await api.NeoLegacyREST.getAddressAbstracts(address, page, this.#network.id) const transactions = new Map() const promises = data.entries.map(async entry => { @@ -103,7 +101,7 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { } async getContract(contractHash: string): Promise { - const response = await api.NeoLegacyREST.contract(contractHash, this.#network.type) + const response = await api.NeoLegacyREST.contract(contractHash, this.#network.id) if (!response || 'error' in response) throw new Error(`Contract ${contractHash} not found`) return { @@ -114,14 +112,14 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { } async getTokenInfo(tokenHash: string): Promise { - const localToken = TOKENS[this.#network.type].find(token => token.hash === tokenHash) + const localToken = TOKENS[this.#network.id].find(token => token.hash === tokenHash) if (localToken) return localToken if (this.#tokenCache.has(tokenHash)) { return this.#tokenCache.get(tokenHash)! } - const data = await api.NeoLegacyREST.asset(tokenHash, this.#network.type) + const data = await api.NeoLegacyREST.asset(tokenHash, this.#network.id) if (!data || 'error' in data) throw new Error(`Token ${tokenHash} not found`) const token = { @@ -137,7 +135,7 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { } async getBalance(address: string): Promise { - const data = await api.NeoLegacyREST.balance(address, this.#network.type) + const data = await api.NeoLegacyREST.balance(address, this.#network.id) const promises = data.map>(async balance => { const hash = balance.asset.replace('0x', '') @@ -167,7 +165,7 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { } async getUnclaimed(address: string): Promise { - const { unclaimed } = await api.NeoLegacyREST.getUnclaimed(address, this.#network.type) + const { unclaimed } = await api.NeoLegacyREST.getUnclaimed(address, this.#network.id) return (unclaimed / 10 ** this.#claimToken.decimals).toFixed(this.#claimToken.decimals) } @@ -178,7 +176,7 @@ export class DoraBDSNeoLegacy implements BlockchainDataService, BDSClaimable { async getRpcList(): Promise { const list: RpcResponse[] = [] - const networkType = this.#network.type as Exclude + const networkType = this.#network.id const promises = RPC_LIST_BY_NETWORK_TYPE[networkType].map(url => { // eslint-disable-next-line no-async-promise-executor diff --git a/packages/bs-neo-legacy/src/DoraESNeoLegacy.ts b/packages/bs-neo-legacy/src/DoraESNeoLegacy.ts index f6f1f15..e298a7f 100644 --- a/packages/bs-neo-legacy/src/DoraESNeoLegacy.ts +++ b/packages/bs-neo-legacy/src/DoraESNeoLegacy.ts @@ -1,15 +1,15 @@ -import { BuildNftUrlParams, ExplorerService, NetworkType } from '@cityofzion/blockchain-service' +import { BuildNftUrlParams, ExplorerService } from '@cityofzion/blockchain-service' +import { AvailableNetworkIds } from './constants' export class DoraESNeoLegacy implements ExplorerService { - #networkType: NetworkType + #networkId: AvailableNetworkIds - constructor(networkType: NetworkType) { - this.#networkType = networkType + constructor(networkId: AvailableNetworkIds) { + this.#networkId = networkId } buildTransactionUrl(hash: string): string { - if (this.#networkType === 'custom') throw new Error('DoraESNeoLegacy does not support custom network') - return `https://dora.coz.io/transaction/neo2/${this.#networkType}/${hash}` + return `https://dora.coz.io/transaction/neo2/${this.#networkId}/${hash}` } buildNftUrl(_params: BuildNftUrlParams): string { diff --git a/packages/bs-neo-legacy/src/__tests__/BDSNeoLegacy.spec.ts b/packages/bs-neo-legacy/src/__tests__/BDSNeoLegacy.spec.ts index 19fba27..18e4412 100644 --- a/packages/bs-neo-legacy/src/__tests__/BDSNeoLegacy.spec.ts +++ b/packages/bs-neo-legacy/src/__tests__/BDSNeoLegacy.spec.ts @@ -4,7 +4,7 @@ import { DEFAULT_URL_BY_NETWORK_TYPE, TOKENS } from '../constants' const gasToken = TOKENS.testnet.find(t => t.symbol === 'GAS')! const doraBDSNeoLegacy = new DoraBDSNeoLegacy( - { type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }, + { id: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet, name: 'testnet' }, gasToken, gasToken ) diff --git a/packages/bs-neo-legacy/src/__tests__/BSNeoLegacy.spec.ts b/packages/bs-neo-legacy/src/__tests__/BSNeoLegacy.spec.ts index c2c59e8..6f6a3e6 100644 --- a/packages/bs-neo-legacy/src/__tests__/BSNeoLegacy.spec.ts +++ b/packages/bs-neo-legacy/src/__tests__/BSNeoLegacy.spec.ts @@ -5,7 +5,11 @@ let bsNeoLegacy: BSNeoLegacy describe('BSNeoLegacy', () => { beforeEach(() => { - bsNeoLegacy = new BSNeoLegacy('neoLegacy', { type: 'testnet', url: 'http://seed5.ngd.network:20332' }) + bsNeoLegacy = new BSNeoLegacy('neoLegacy', { + id: 'testnet', + url: 'http://seed5.ngd.network:20332', + name: 'testnet', + }) }) it('Should be able to validate an address', () => { @@ -85,7 +89,7 @@ describe('BSNeoLegacy', () => { }) it.skip('Should be able to transfer a nep5 asset', async () => { - bsNeoLegacy.setNetwork({ type: 'mainnet', url: 'http://seed9.ngd.network:10332' }) + bsNeoLegacy.setNetwork({ id: 'mainnet', url: 'http://seed9.ngd.network:10332' }) const account = bsNeoLegacy.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string) const balance = await bsNeoLegacy.blockchainDataService.getBalance(account.address) const LXBalance = balance.find(item => item.token.symbol === 'LX')! @@ -105,7 +109,7 @@ describe('BSNeoLegacy', () => { }) it.skip('Should be able to transfer a asset with tip', async () => { - bsNeoLegacy.setNetwork({ type: 'mainnet', url: 'http://seed9.ngd.network:10332' }) + bsNeoLegacy.setNetwork({ id: 'mainnet', url: 'http://seed9.ngd.network:10332' }) const account = bsNeoLegacy.generateAccountFromKey(process.env.TESTNET_PRIVATE_KEY as string) const balance = await bsNeoLegacy.blockchainDataService.getBalance(account.address) const LXBalance = balance.find(item => item.token.symbol === 'LX')! diff --git a/packages/bs-neo-legacy/src/constants.ts b/packages/bs-neo-legacy/src/constants.ts index 9093a7c..1d12b4d 100644 --- a/packages/bs-neo-legacy/src/constants.ts +++ b/packages/bs-neo-legacy/src/constants.ts @@ -1,14 +1,15 @@ -import { NetworkType, Token } from '@cityofzion/blockchain-service' +import { Token } from '@cityofzion/blockchain-service' import commom from './assets/tokens/common.json' import mainnet from './assets/tokens/mainnet.json' -export const TOKENS: Record = { +export type AvailableNetworkIds = 'mainnet' | 'testnet' + +export const TOKENS: Record = { mainnet: [...commom, ...mainnet], testnet: commom, - custom: commom, } -export const LEGACY_NETWORK_BY_NETWORK_TYPE: Record, string> = { +export const LEGACY_NETWORK_BY_NETWORK_TYPE: Record, string> = { mainnet: 'MainNet', testnet: 'TestNet', } @@ -17,7 +18,7 @@ export const NATIVE_ASSETS = commom export const DERIVATION_PATH = "m/44'/888'/0'/0/?" -export const RPC_LIST_BY_NETWORK_TYPE: Record, string[]> = { +export const RPC_LIST_BY_NETWORK_TYPE: Record, string[]> = { mainnet: [ 'http://seed9.ngd.network:10332', 'https://mainnet1.neo2.coz.io:443', @@ -37,7 +38,7 @@ export const RPC_LIST_BY_NETWORK_TYPE: Record, st ], } -export const DEFAULT_URL_BY_NETWORK_TYPE: Record, string> = { +export const DEFAULT_URL_BY_NETWORK_TYPE: Record, string> = { mainnet: RPC_LIST_BY_NETWORK_TYPE.mainnet[0], testnet: RPC_LIST_BY_NETWORK_TYPE.testnet[0], } diff --git a/packages/bs-neo3/package.json b/packages/bs-neo3/package.json index 824446f..18e78a0 100644 --- a/packages/bs-neo3/package.json +++ b/packages/bs-neo3/package.json @@ -25,21 +25,21 @@ "query-string": "7.1.3", "@ledgerhq/hw-transport": "~6.30.5", "@cityofzion/neon-dappkit": "0.4.1", - "@cityofzion/neon-dappkit-types": "~0.3.1", "bignumber.js": "~9.1.2", - "typed-emitter": "~2.1.0", "isomorphic-ws": "~5.0.0" }, "devDependencies": { + "@cityofzion/neon-dappkit-types": "~0.3.1", + "@ledgerhq/hw-transport-node-hid": "~6.28.5", "@types/jest": "29.5.3", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", - "@ledgerhq/hw-transport-node-hid": "~6.28.5", "dotenv": "16.3.1", "eslint": "^8.48.0", "jest": "29.6.2", "ts-jest": "29.1.1", "ts-node": "10.9.1", - "typescript": "4.9.5" + "typescript": "4.9.5", + "typed-emitter": "~2.1.0" } } diff --git a/packages/bs-neo3/src/BSNeo3.ts b/packages/bs-neo3/src/BSNeo3.ts index 5a3521a..4baf50f 100644 --- a/packages/bs-neo3/src/BSNeo3.ts +++ b/packages/bs-neo3/src/BSNeo3.ts @@ -8,7 +8,6 @@ import { Token, BSWithNameService, Network, - PartialBy, TransferParam, BSCalculableFee, NftDataService, @@ -17,13 +16,14 @@ import { BSWithExplorerService, ExplorerService, BSWithLedger, + PartialNetwork, } from '@cityofzion/blockchain-service' import { api, u, wallet } from '@cityofzion/neon-js' import Neon from '@cityofzion/neon-core' import { NeonInvoker, NeonParser } from '@cityofzion/neon-dappkit' import { RPCBDSNeo3 } from './RpcBDSNeo3' import { DoraBDSNeo3 } from './DoraBDSNeo3' -import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants' +import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, AvailableNetworkIds, TOKENS } from './constants' import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3' import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3' import { keychain } from '@cityofzion/bs-asteroid-sdk' @@ -34,7 +34,7 @@ import Transport from '@ledgerhq/hw-transport' export class BSNeo3 implements - BlockchainService, + BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, @@ -54,16 +54,16 @@ export class BSNeo3 exchangeDataService!: ExchangeDataService explorerService!: ExplorerService tokens: Token[] - network!: Network + network!: Network constructor( blockchainName: BSCustomName, - network: PartialBy, + network: PartialNetwork, getLedgerTransport?: (account: Account) => Promise ) { this.blockchainName = blockchainName this.ledgerService = new LedgerServiceNeo3(getLedgerTransport) - this.tokens = TOKENS[network.type] + this.tokens = TOKENS[network.id] this.derivationPath = DERIVATION_PATH this.feeToken = this.tokens.find(token => token.symbol === 'GAS')! this.burnToken = this.tokens.find(token => token.symbol === 'NEO')! @@ -71,22 +71,23 @@ export class BSNeo3 this.setNetwork(network) } - setNetwork(param: PartialBy) { + setNetwork(partialNetwork: PartialNetwork) { const network = { - type: param.type, - url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type], + id: partialNetwork.id, + name: partialNetwork.name ?? partialNetwork.id, + url: partialNetwork.url ?? DEFAULT_URL_BY_NETWORK_TYPE[partialNetwork.id], } this.network = network - if (network.type === 'custom') { + if (network.name === 'custom') { this.blockchainDataService = new RPCBDSNeo3(network, this.feeToken, this.claimToken) } else { this.blockchainDataService = new DoraBDSNeo3(network, this.feeToken, this.claimToken) } - this.exchangeDataService = new FlamingoEDSNeo3(network.type) + this.exchangeDataService = new FlamingoEDSNeo3(network.id) this.nftDataService = new GhostMarketNDSNeo3(network) - this.explorerService = new DoraESNeo3(network.type) + this.explorerService = new DoraESNeo3(network.id) } validateAddress(address: string): boolean { diff --git a/packages/bs-neo3/src/DoraBDSNeo3.ts b/packages/bs-neo3/src/DoraBDSNeo3.ts index 804877e..cf6d6f1 100644 --- a/packages/bs-neo3/src/DoraBDSNeo3.ts +++ b/packages/bs-neo3/src/DoraBDSNeo3.ts @@ -13,7 +13,7 @@ import { import { wallet, u } from '@cityofzion/neon-js' import { NeoRESTApi } from '@cityofzion/dora-ts/dist/api' import { RPCBDSNeo3 } from './RpcBDSNeo3' -import { TOKENS } from './constants' +import { AvailableNetworkIds, TOKENS } from './constants' const NeoRest = new NeoRESTApi({ doraUrl: 'https://dora.coz.io', @@ -21,8 +21,8 @@ const NeoRest = new NeoRESTApi({ }) export class DoraBDSNeo3 extends RPCBDSNeo3 { - constructor(network: Network, feeToken: Token, claimToken: Token) { - if (network.type === 'custom') { + constructor(network: Network, feeToken: Token, claimToken: Token) { + if (network.id === 'custom') { throw new Error('DoraBDSNeo3 does not support custom networks') } @@ -31,7 +31,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { async getTransaction(hash: string): Promise { try { - const data = await NeoRest.transaction(hash, this._network.type) + const data = await NeoRest.transaction(hash, this._network.id) return { block: data.block, time: Number(data.time), @@ -51,7 +51,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { address, page = 1, }: TransactionsByAddressParams): Promise { - const data = await NeoRest.addressTXFull(address, page, this._network.type) + const data = await NeoRest.addressTXFull(address, page, this._network.id) const promises = data.items.map(async (item): Promise => { const transferPromises: Promise[] = [] @@ -123,7 +123,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { async getContract(contractHash: string): Promise { try { - const data = await NeoRest.contract(contractHash, this._network.type) + const data = await NeoRest.contract(contractHash, this._network.id) return { hash: data.hash, methods: data.manifest.abi?.methods ?? [], @@ -135,7 +135,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { } async getTokenInfo(tokenHash: string): Promise { - const localToken = TOKENS[this._network.type].find(token => token.hash === tokenHash) + const localToken = TOKENS[this._network.id].find(token => token.hash === tokenHash) if (localToken) return localToken if (this._tokenCache.has(tokenHash)) { @@ -143,7 +143,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { } try { - const { decimals, symbol, name, scripthash } = await NeoRest.asset(tokenHash, this._network.type) + const { decimals, symbol, name, scripthash } = await NeoRest.asset(tokenHash, this._network.id) const token = { decimals: Number(decimals), symbol, @@ -159,7 +159,7 @@ export class DoraBDSNeo3 extends RPCBDSNeo3 { } async getBalance(address: string): Promise { - const response = await NeoRest.balance(address, this._network.type) + const response = await NeoRest.balance(address, this._network.id) const promises = response.map>(async balance => { try { diff --git a/packages/bs-neo3/src/DoraESNeo3.ts b/packages/bs-neo3/src/DoraESNeo3.ts index 8164f61..6a97e11 100644 --- a/packages/bs-neo3/src/DoraESNeo3.ts +++ b/packages/bs-neo3/src/DoraESNeo3.ts @@ -1,19 +1,20 @@ -import { ExplorerService, NetworkType, BuildNftUrlParams } from '@cityofzion/blockchain-service' +import { ExplorerService, BuildNftUrlParams } from '@cityofzion/blockchain-service' +import { AvailableNetworkIds } from './constants' export class DoraESNeo3 implements ExplorerService { - readonly #networkType: NetworkType + readonly #networkId: AvailableNetworkIds - constructor(networkType: NetworkType) { - this.#networkType = networkType + constructor(networkType: AvailableNetworkIds) { + this.#networkId = networkType } buildTransactionUrl(hash: string): string { - if (this.#networkType === 'custom') throw new Error('DoraESNeo3 does not support custom network') - return `https://dora.coz.io/transaction/neo3/${this.#networkType}/${hash}` + if (this.#networkId === 'custom') throw new Error('DoraESNeo3 does not support custom network') + return `https://dora.coz.io/transaction/neo3/${this.#networkId}/${hash}` } buildNftUrl({ contractHash, tokenId }: BuildNftUrlParams): string { - if (this.#networkType === 'custom') throw new Error('DoraESNeo3 does not support custom network') - return `https://dora.coz.io/nft/neo3/${this.#networkType}/${contractHash}/${tokenId}` + if (this.#networkId === 'custom') throw new Error('DoraESNeo3 does not support custom network') + return `https://dora.coz.io/nft/neo3/${this.#networkId}/${contractHash}/${tokenId}` } } diff --git a/packages/bs-neo3/src/FlamingoEDSNeo3.ts b/packages/bs-neo3/src/FlamingoEDSNeo3.ts index 75e25cf..b1f2500 100644 --- a/packages/bs-neo3/src/FlamingoEDSNeo3.ts +++ b/packages/bs-neo3/src/FlamingoEDSNeo3.ts @@ -2,11 +2,12 @@ import { CryptoCompareEDS, Currency, ExchangeDataService, - NetworkType, + GetTokenPriceHistory, + TokenPricesHistoryResponse, TokenPricesResponse, } from '@cityofzion/blockchain-service' import axios, { AxiosInstance } from 'axios' -import { TOKENS } from './constants' +import { AvailableNetworkIds, TOKENS } from './constants' type FlamingoTokenInfoPricesResponse = { symbol: string @@ -15,17 +16,17 @@ type FlamingoTokenInfoPricesResponse = { }[] export class FlamingoEDSNeo3 extends CryptoCompareEDS implements ExchangeDataService { - readonly #networkType: NetworkType + readonly #networkId: AvailableNetworkIds readonly #axiosInstance: AxiosInstance - constructor(networkType: NetworkType) { - super(networkType, TOKENS[networkType]) - this.#networkType = networkType + constructor(networkId: AvailableNetworkIds) { + super(TOKENS[networkId]) + this.#networkId = networkId this.#axiosInstance = axios.create({ baseURL: 'https://api.flamingo.finance' }) } async getTokenPrices(currency: Currency): Promise { - if (this.#networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet') + if (this.#networkId !== 'mainnet') throw new Error('Exchange is only available on mainnet') const { data: prices } = await this.#axiosInstance.get('/token-info/prices') @@ -42,6 +43,11 @@ export class FlamingoEDSNeo3 extends CryptoCompareEDS implements ExchangeDataSer })) } + getTokenPriceHistory(params: GetTokenPriceHistory): Promise { + if (this.#networkId !== 'mainnet') throw new Error('Exchange is only available on mainnet') + return super.getTokenPriceHistory(params) + } + private async getCurrencyRatio(currency: Currency): Promise { const { data } = await this.#axiosInstance.get(`/fiat/exchange-rate?pair=USD_${currency}`) return data diff --git a/packages/bs-neo3/src/GhostMarketNDSNeo3.ts b/packages/bs-neo3/src/GhostMarketNDSNeo3.ts index a04cd24..23210ef 100644 --- a/packages/bs-neo3/src/GhostMarketNDSNeo3.ts +++ b/packages/bs-neo3/src/GhostMarketNDSNeo3.ts @@ -1,15 +1,8 @@ -import { - NftResponse, - NftsResponse, - NetworkType, - GetNftParam, - GetNftsByAddressParams, - Network, -} from '@cityofzion/blockchain-service' +import { NftResponse, NftsResponse, GetNftParam, GetNftsByAddressParams, Network } from '@cityofzion/blockchain-service' import qs from 'query-string' import axios from 'axios' -import { GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants' +import { AvailableNetworkIds, GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants' import { RpcNDSNeo3 } from './RpcNDSNeo3' type GhostMarketNFT = { @@ -49,11 +42,11 @@ type GhostMarketAssets = { } export class GhostMarketNDSNeo3 extends RpcNDSNeo3 { - readonly #networkType: NetworkType + readonly #networkId: AvailableNetworkIds - constructor(network: Network) { + constructor(network: Network) { super(network) - this.#networkType = network.type + this.#networkId = network.id } async getNftsByAddress({ address, size = 18, cursor }: GetNftsByAddressParams): Promise { @@ -94,12 +87,12 @@ export class GhostMarketNDSNeo3 extends RpcNDSNeo3 { private getUrlWithParams(params: Record) { const parameters = qs.stringify( { - chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.#networkType], + chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.#networkId], ...params, }, { arrayFormat: 'bracket' } ) - return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.#networkType]}/assets?${parameters}` + return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.#networkId]}/assets?${parameters}` } private parse(data: GhostMarketNFT) { diff --git a/packages/bs-neo3/src/RpcBDSNeo3.ts b/packages/bs-neo3/src/RpcBDSNeo3.ts index 89873ef..873e810 100644 --- a/packages/bs-neo3/src/RpcBDSNeo3.ts +++ b/packages/bs-neo3/src/RpcBDSNeo3.ts @@ -14,17 +14,17 @@ import { } from '@cityofzion/blockchain-service' import { rpc, u } from '@cityofzion/neon-core' import { NeonInvoker, TypeChecker } from '@cityofzion/neon-dappkit' -import { RPC_LIST_BY_NETWORK_TYPE, TOKENS } from './constants' +import { AvailableNetworkIds, RPC_LIST_BY_NETWORK_TYPE, TOKENS } from './constants' export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable { readonly _tokenCache: Map = new Map() readonly _feeToken: Token readonly _claimToken: Token - readonly _network: Network + readonly _network: Network maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 2 - constructor(network: Network, feeToken: Token, claimToken: Token) { + constructor(network: Network, feeToken: Token, claimToken: Token) { this._network = network this._feeToken = feeToken this._claimToken = claimToken @@ -78,7 +78,7 @@ export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable { } async getTokenInfo(tokenHash: string): Promise { - const localToken = TOKENS[this._network.type].find(token => token.hash === tokenHash) + const localToken = TOKENS[this._network.id].find(token => token.hash === tokenHash) if (localToken) return localToken if (this._tokenCache.has(tokenHash)) { @@ -163,7 +163,7 @@ export class RPCBDSNeo3 implements BlockchainDataService, BDSClaimable { async getRpcList(): Promise { const list: RpcResponse[] = [] - const promises = RPC_LIST_BY_NETWORK_TYPE[this._network.type].map(url => { + const promises = RPC_LIST_BY_NETWORK_TYPE[this._network.id].map(url => { // eslint-disable-next-line no-async-promise-executor return new Promise(async resolve => { const timeout = setTimeout(() => { diff --git a/packages/bs-neo3/src/__tests__/BDSNeo3.spec.ts b/packages/bs-neo3/src/__tests__/BDSNeo3.spec.ts index bf5bd34..14424e6 100644 --- a/packages/bs-neo3/src/__tests__/BDSNeo3.spec.ts +++ b/packages/bs-neo3/src/__tests__/BDSNeo3.spec.ts @@ -4,8 +4,16 @@ import { RPCBDSNeo3 } from '../RpcBDSNeo3' import { DEFAULT_URL_BY_NETWORK_TYPE, TOKENS } from '../constants' const gasToken = TOKENS.testnet.find(t => t.symbol === 'GAS')! -const doraBDSNeo3 = new DoraBDSNeo3({ type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }, gasToken, gasToken) -const rpcBDSNeo3 = new RPCBDSNeo3({ type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet }, gasToken, gasToken) +const doraBDSNeo3 = new DoraBDSNeo3( + { id: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet, name: 'testnet' }, + gasToken, + gasToken +) +const rpcBDSNeo3 = new RPCBDSNeo3( + { id: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet, name: 'testnet' }, + gasToken, + gasToken +) describe('BDSNeo3', () => { it.each([doraBDSNeo3, rpcBDSNeo3])( @@ -32,7 +40,6 @@ describe('BDSNeo3', () => { async (bdsNeo3: BlockchainDataService) => { const address = 'NPB3Cze4wki9J36nnrT45qmi6P52Bhfqph' const response = await bdsNeo3.getTransactionsByAddress({ address, page: 1 }) - console.log({ response }) response.transactions.forEach(transaction => { expect(transaction).toEqual( expect.objectContaining({ diff --git a/packages/bs-neo3/src/__tests__/BSNeo3.spec.ts b/packages/bs-neo3/src/__tests__/BSNeo3.spec.ts index df8221f..0402303 100644 --- a/packages/bs-neo3/src/__tests__/BSNeo3.spec.ts +++ b/packages/bs-neo3/src/__tests__/BSNeo3.spec.ts @@ -2,19 +2,13 @@ import { sleep } from './utils/sleep' import { BSNeo3 } from '../BSNeo3' import { generateMnemonic } from '@cityofzion/bs-asteroid-sdk' import TransportNodeHid from '@ledgerhq/hw-transport-node-hid' -import Transport from '@ledgerhq/hw-transport' -import { Network } from '@cityofzion/blockchain-service' import { DEFAULT_URL_BY_NETWORK_TYPE } from '../constants' let bsNeo3: BSNeo3 -let transport: Transport - -const network: Network = { type: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet } describe('BSNeo3', () => { beforeAll(async () => { - transport = await TransportNodeHid.create() - bsNeo3 = new BSNeo3('neo3', network, async () => transport) + bsNeo3 = new BSNeo3('neo3', { id: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet, name: 'testnet' }) }, 60000) it('Should be able to validate an address', () => { @@ -129,21 +123,28 @@ describe('BSNeo3', () => { }) it.skip('Should be able to transfer with ledger', async () => { - const publicKey = await bsNeo3.ledgerService.getPublicKey(transport) + const transport = await TransportNodeHid.create() + const service = new BSNeo3( + 'neo3', + { id: 'testnet', url: DEFAULT_URL_BY_NETWORK_TYPE.testnet, name: 'testnet' }, + async () => transport + ) - const account = bsNeo3.generateAccountFromPublicKey(publicKey) + const publicKey = await service.ledgerService.getPublicKey(transport) - const balance = await bsNeo3.blockchainDataService.getBalance(account.address) - const gasBalance = balance.find(b => b.token.symbol === bsNeo3.feeToken.symbol) + const account = service.generateAccountFromPublicKey(publicKey) + + const balance = await service.blockchainDataService.getBalance(account.address) + const gasBalance = balance.find(b => b.token.symbol === service.feeToken.symbol) expect(Number(gasBalance?.amount)).toBeGreaterThan(0.00000001) - const transactionHash = await bsNeo3.transfer({ + const transactionHash = await service.transfer({ senderAccount: account, intent: { amount: '1', receiverAddress: 'NPRMF5bmYuW23DeDJqsDJenhXkAPSJyuYe', - tokenHash: bsNeo3.feeToken.hash, - tokenDecimals: bsNeo3.feeToken.decimals, + tokenHash: service.feeToken.hash, + tokenDecimals: service.feeToken.decimals, }, isLedger: true, }) diff --git a/packages/bs-neo3/src/__tests__/GhostMarketNDSNeo3.spec.ts b/packages/bs-neo3/src/__tests__/GhostMarketNDSNeo3.spec.ts index 32bd554..897cfac 100644 --- a/packages/bs-neo3/src/__tests__/GhostMarketNDSNeo3.spec.ts +++ b/packages/bs-neo3/src/__tests__/GhostMarketNDSNeo3.spec.ts @@ -6,8 +6,9 @@ let ghostMarketNDSNeo3: GhostMarketNDSNeo3 describe('GhostMarketNDSNeo3', () => { beforeAll(() => { ghostMarketNDSNeo3 = new GhostMarketNDSNeo3({ - type: 'mainnet', + id: 'mainnet', url: DEFAULT_URL_BY_NETWORK_TYPE.mainnet, + name: 'mainnet', }) }) diff --git a/packages/bs-neo3/src/assets/tokens/common.json b/packages/bs-neo3/src/assets/tokens/common.json index 5fad5e8..d94d4db 100644 --- a/packages/bs-neo3/src/assets/tokens/common.json +++ b/packages/bs-neo3/src/assets/tokens/common.json @@ -1,13 +1,13 @@ [ { "symbol": "GAS", - "name": "GASToken", + "name": "GAS", "hash": "d2a4cff31913016155e38e474a2c06d08be276cf", "decimals": 8 }, { "symbol": "NEO", - "name": "NeoToken", + "name": "NEO", "hash": "ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", "decimals": 0 } diff --git a/packages/bs-neo3/src/constants.ts b/packages/bs-neo3/src/constants.ts index 28a75eb..649befa 100644 --- a/packages/bs-neo3/src/constants.ts +++ b/packages/bs-neo3/src/constants.ts @@ -1,8 +1,10 @@ -import { NetworkType, Token } from '@cityofzion/blockchain-service' +import { Token } from '@cityofzion/blockchain-service' import commom from './assets/tokens/common.json' import mainnet from './assets/tokens/mainnet.json' -export const TOKENS: Record = { +export type AvailableNetworkIds = 'mainnet' | 'testnet' | 'custom' + +export const TOKENS: Record = { mainnet: [...commom, ...mainnet], testnet: commom, custom: commom, @@ -14,19 +16,19 @@ export const NEO_NS_HASH = '0x50ac1c37690cc2cfc594472833cf57505d5f46de' export const GAS_PER_NEO = 0.001 -export const GHOSTMARKET_URL_BY_NETWORK_TYPE: Partial> = { +export const GHOSTMARKET_URL_BY_NETWORK_TYPE: Partial> = { mainnet: 'https://api.ghostmarket.io/api/v2', testnet: 'https://api-testnet.ghostmarket.io/api/v2', } -export const GHOSTMARKET_CHAIN_BY_NETWORK_TYPE: Partial> = { +export const GHOSTMARKET_CHAIN_BY_NETWORK_TYPE: Partial> = { mainnet: 'n3', testnet: 'n3t', } export const DERIVATION_PATH = "m/44'/888'/0'/0/?" -export const RPC_LIST_BY_NETWORK_TYPE: Record = { +export const RPC_LIST_BY_NETWORK_TYPE: Record = { mainnet: [ 'https://mainnet1.neo.coz.io:443', 'https://mainnet4.neo.coz.io:443', @@ -51,7 +53,7 @@ export const RPC_LIST_BY_NETWORK_TYPE: Record = { custom: ['http://127.0.0.1:50012'], } -export const DEFAULT_URL_BY_NETWORK_TYPE: Record = { +export const DEFAULT_URL_BY_NETWORK_TYPE: Record = { mainnet: RPC_LIST_BY_NETWORK_TYPE.mainnet[0], testnet: RPC_LIST_BY_NETWORK_TYPE.testnet[0], custom: RPC_LIST_BY_NETWORK_TYPE.custom[0], @@ -67,7 +69,7 @@ export type SwapScriptHashes = { flpBneoGas: string } -export const SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE: Partial> = { +export const SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE: Partial> = { mainnet: { flamingoSwapRouter: '0xf970f4ccecd765b63732b821775dc38c25d74f23', flamingoPairWhiteList: '0xfb75a5314069b56e136713d38477f647a13991b4', diff --git a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapControllerService.ts b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapControllerService.ts index 10599bd..9f9784f 100644 --- a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapControllerService.ts +++ b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapControllerService.ts @@ -21,13 +21,13 @@ import { } from './FlamingoSwapError' import { FlamingoSwapHelper } from './FlamingoSwapHelper' import WebSocket from 'isomorphic-ws' -import { BLOCKCHAIN_WSS_URL } from '../constants' +import { AvailableNetworkIds, BLOCKCHAIN_WSS_URL } from '../constants' -export class FlamingoSwapControllerService implements SwapControllerService { +export class FlamingoSwapControllerService implements SwapControllerService { eventEmitter: TypedEmitter ws: WebSocket - readonly #network: Network + readonly #network: Network #accountToUse: Account | null = null @@ -55,21 +55,23 @@ export class FlamingoSwapControllerService implements SwapControllerService { #lastAmountChange: 'amountToReceive' | 'amountToUse' | null = null - constructor(network: Network) { - if (network.type === 'custom') throw new CustomNetworkNotSupportedError() + constructor(network: Network) { + if (network.id === 'custom') throw new CustomNetworkNotSupportedError() this.#network = network this.eventEmitter = new EventEmitter() as TypedEmitter } - buildSwapArgs(): SwapControllerServiceSwapToReceiveArgs | SwapControllerServiceSwapToUseArgs { + buildSwapArgs(): + | SwapControllerServiceSwapToReceiveArgs + | SwapControllerServiceSwapToUseArgs { if (!this.accountToUse) throw new FlamingoSwapMissingParametersError('accountToUse') if (!this.amountToReceive) throw new FlamingoSwapMissingParametersError('amountToReceive') if (!this.amountToUse) throw new FlamingoSwapMissingParametersError('amountToUse') if (!this.tokenToReceive) throw new FlamingoSwapMissingParametersError('tokenToReceive') if (!this.tokenToUse) throw new FlamingoSwapMissingParametersError('tokenToUse') - const baseSwapArgs: SwapControllerServiceSwapArgs = { + const baseSwapArgs: SwapControllerServiceSwapArgs = { address: this.accountToUse.address, amountToReceive: this.amountToReceive, amountToUse: this.amountToUse, diff --git a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapHelper.ts b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapHelper.ts index 7088486..75fdf24 100644 --- a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapHelper.ts +++ b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapHelper.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { Network, SwapRoute, Token } from '@cityofzion/blockchain-service' -import { SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE, TOKENS } from '../constants' +import { AvailableNetworkIds, SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE, TOKENS } from '../constants' type TGetSwapArgs = { amountToUse: string | null @@ -10,7 +10,7 @@ type TGetSwapArgs = { reservesToUse: string reservesToReceive: string slippage: number - network: Network + network: Network } type TCreateTradeDataArgs = { @@ -113,8 +113,8 @@ export class FlamingoSwapHelper { } } - static overrideToken(network: Network, token: Token): Token { - const neoScriptHash = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]!.neo + static overrideToken(network: Network, token: Token): Token { + const neoScriptHash = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]!.neo const isNeoToken = this.normalizeHash(token.hash) === this.normalizeHash(neoScriptHash) @@ -122,8 +122,8 @@ export class FlamingoSwapHelper { return token } - const bneoToken = TOKENS[network.type].find( - token => token.hash === SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]!.bneo + const bneoToken = TOKENS[network.id].find( + token => token.hash === SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]!.bneo ) if (!bneoToken) throw new Error('Bneo token not found') @@ -147,13 +147,13 @@ export class FlamingoSwapHelper { } } - private static overrideAmountInput(network: Network, amount: string, token: Token) { + private static overrideAmountInput(network: Network, amount: string, token: Token) { const tokenOverrode = this.overrideToken(network, token) return new BigNumber(amount).shiftedBy(tokenOverrode.decimals) } - private static overrideAmountToDisplay(network: Network, amount: string, token: Token) { + private static overrideAmountToDisplay(network: Network, amount: string, token: Token) { const tokenOverrode = this.overrideToken(network, token) return new BigNumber(amount).shiftedBy(-tokenOverrode.decimals).toFixed() diff --git a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapNeonDappKitInvocationBuilder.ts b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapNeonDappKitInvocationBuilder.ts index 59dce23..968a24f 100644 --- a/packages/bs-neo3/src/flamingo-swap/FlamingoSwapNeonDappKitInvocationBuilder.ts +++ b/packages/bs-neo3/src/flamingo-swap/FlamingoSwapNeonDappKitInvocationBuilder.ts @@ -1,5 +1,5 @@ import { ContractInvocation, ContractInvocationMulti } from '@cityofzion/neon-dappkit-types' -import { GAS_PER_NEO, SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE, SwapScriptHashes } from '../constants' +import { AvailableNetworkIds, GAS_PER_NEO, SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE, SwapScriptHashes } from '../constants' import { tx, u } from '@cityofzion/neon-core' import { Network, @@ -16,13 +16,17 @@ type TransferArgs = { } type GetReservesArgs = { - network: Network + network: Network tokenToReceiveScriptHash: string tokenToUseScriptHash: string } export class FlamingoSwapNeonDappKitInvocationBuilder { - static swapInvocation(data: SwapControllerServiceSwapToReceiveArgs | SwapControllerServiceSwapToUseArgs) { + static swapInvocation( + data: + | SwapControllerServiceSwapToReceiveArgs + | SwapControllerServiceSwapToUseArgs + ) { return data.type === 'swapTokenToReceive' ? this.swapTokenToReceiveForTokenToUseInvocation(data) : this.swapTokenToUseForTokenToReceiveInvocation(data) @@ -33,7 +37,7 @@ export class FlamingoSwapNeonDappKitInvocationBuilder { tokenToReceiveScriptHash, tokenToUseScriptHash, }: GetReservesArgs): ContractInvocationMulti { - const flamingoSwapRouter = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]!.flamingoSwapRouter + const flamingoSwapRouter = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]!.flamingoSwapRouter return { invocations: [ @@ -68,11 +72,11 @@ export class FlamingoSwapNeonDappKitInvocationBuilder { tokenToUse, deadline, network, - }: SwapControllerServiceSwapToReceiveArgs): ContractInvocationMulti { + }: SwapControllerServiceSwapToReceiveArgs): ContractInvocationMulti { const invocations: ContractInvocation[] = [] const allowedContracts: string[] = [] - const scriptHashes: SwapScriptHashes = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]! + const scriptHashes: SwapScriptHashes = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]! const tokenToReceiveOverrode = FlamingoSwapHelper.overrideToken(network, tokenToReceive) const amountToReceiveFormatted = u.BigInteger.fromDecimal( @@ -155,11 +159,11 @@ export class FlamingoSwapNeonDappKitInvocationBuilder { tokenToUse, minimumReceived, network, - }: SwapControllerServiceSwapToUseArgs): ContractInvocationMulti { + }: SwapControllerServiceSwapToUseArgs): ContractInvocationMulti { const invocations: ContractInvocation[] = [] const allowedContracts: string[] = [] - const scriptHashes = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]! + const scriptHashes = SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]! const isNeoSwapped = tokenToUse.hash === scriptHashes.neo if (isNeoSwapped) { @@ -280,9 +284,9 @@ export class FlamingoSwapNeonDappKitInvocationBuilder { return [scriptHashes.gas, scriptHashes.flpBneoGas, scriptHashes.bneo] } - private static overrideScriptHash(network: Network, scriptHash: string): string { - return scriptHash === SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]!.neo - ? SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.type]!.bneo + private static overrideScriptHash(network: Network, scriptHash: string): string { + return scriptHash === SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]!.neo + ? SWAP_SCRIPT_HASHES_BY_NETWORK_TYPE[network.id]!.bneo : scriptHash } }