@cityofzion/bs-ethereum 1.0.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/BSEthereum.d.ts +10 -6
  2. package/dist/BSEthereum.js +60 -23
  3. package/dist/BitqueryBDSEthereum.d.ts +1 -2
  4. package/dist/BitqueryBDSEthereum.js +27 -12
  5. package/dist/BitqueryEDSEthereum.d.ts +1 -2
  6. package/dist/BitqueryEDSEthereum.js +20 -5
  7. package/dist/GhostMarketNDSEthereum.d.ts +1 -1
  8. package/dist/GhostMarketNDSEthereum.js +17 -3
  9. package/dist/LedgerServiceEthereum.d.ts +11 -0
  10. package/dist/LedgerServiceEthereum.js +81 -0
  11. package/dist/RpcBDSEthereum.d.ts +1 -1
  12. package/dist/RpcBDSEthereum.js +21 -7
  13. package/dist/constants.js +1 -1
  14. package/package.json +8 -2
  15. package/.eslintignore +0 -13
  16. package/.eslintrc.cjs +0 -22
  17. package/.rush/temp/operation/build/all.log +0 -1
  18. package/.rush/temp/operation/build/state.json +0 -3
  19. package/.rush/temp/package-deps_build.json +0 -28
  20. package/.rush/temp/shrinkwrap-deps.json +0 -449
  21. package/CHANGELOG.json +0 -104
  22. package/CHANGELOG.md +0 -57
  23. package/bs-ethereum.build.log +0 -1
  24. package/jest.config.ts +0 -13
  25. package/jest.setup.ts +0 -1
  26. package/src/BSEthereum.ts +0 -190
  27. package/src/BitqueryBDSEthereum.ts +0 -231
  28. package/src/BitqueryEDSEthereum.ts +0 -67
  29. package/src/GhostMarketNDSEthereum.ts +0 -125
  30. package/src/RpcBDSEthereum.ts +0 -88
  31. package/src/__tests__/BDSEthereum.spec.ts +0 -126
  32. package/src/__tests__/BSEthereum.spec.ts +0 -131
  33. package/src/__tests__/BitqueryEDSEthereum.spec.ts +0 -55
  34. package/src/__tests__/GhostMarketNDSEthereum.spec.ts +0 -49
  35. package/src/assets/tokens/common.json +0 -8
  36. package/src/constants.ts +0 -36
  37. package/src/graphql.ts +0 -288
  38. package/src/index.ts +0 -6
  39. package/tsconfig.build.json +0 -4
  40. package/tsconfig.json +0 -15
package/CHANGELOG.md DELETED
@@ -1,57 +0,0 @@
1
- # Change Log - @cityofzion/bs-ethereum
2
-
3
- This log was last generated on Mon, 04 Mar 2024 21:53:09 GMT and should not be manually modified.
4
-
5
- ## 1.0.4
6
- Mon, 04 Mar 2024 21:53:09 GMT
7
-
8
- ### Patches
9
-
10
- - fix wrong calculated fee
11
-
12
- ## 1.0.3
13
- Wed, 28 Feb 2024 17:43:01 GMT
14
-
15
- ### Patches
16
-
17
- - Add creator infomations in nft methods return
18
-
19
- ## 1.0.2
20
- Tue, 27 Feb 2024 20:29:09 GMT
21
-
22
- ### Patches
23
-
24
- - Remove the mining wait in the transfer method to avoid long promises that take time to resolve.
25
-
26
- ## 1.0.1
27
- Thu, 22 Feb 2024 16:49:56 GMT
28
-
29
- ### Patches
30
-
31
- - Fix getTokenPrices that is returning a wrong price for ETH token address
32
-
33
- ## 1.0.0
34
- Fri, 16 Feb 2024 16:13:22 GMT
35
-
36
- ### Breaking changes
37
-
38
- - Change bitquery providers to receive apiKey on class initialization
39
-
40
- ## 0.9.1
41
- Tue, 30 Jan 2024 18:26:00 GMT
42
-
43
- ### Patches
44
-
45
- - Fixed bug with repeated ETH token in getBalance method
46
-
47
- ## 0.9.0
48
- Tue, 05 Dec 2023 18:42:10 GMT
49
-
50
- ### Minor changes
51
-
52
- - Inserted within the TokenPricesResponse type the token hash
53
-
54
- ### Patches
55
-
56
- - Fixed issue with the getBalance method that returned negative numbers and the wrong ETH value
57
-
@@ -1 +0,0 @@
1
- Invoking: tsc --project tsconfig.build.json
package/jest.config.ts DELETED
@@ -1,13 +0,0 @@
1
- import { JestConfigWithTsJest } from 'ts-jest'
2
- const config: JestConfigWithTsJest = {
3
- preset: 'ts-jest',
4
- testEnvironment: 'node',
5
- clearMocks: true,
6
- verbose: true,
7
- bail: true,
8
- testMatch: ['<rootDir>/**/*.spec.ts'],
9
- setupFiles: ['<rootDir>/jest.setup.ts'],
10
- detectOpenHandles: true,
11
- }
12
-
13
- export default config
package/jest.setup.ts DELETED
@@ -1 +0,0 @@
1
- import 'dotenv/config'
package/src/BSEthereum.ts DELETED
@@ -1,190 +0,0 @@
1
- import {
2
- Account,
3
- AccountWithDerivationPath,
4
- BSCalculableFee,
5
- BSWithNameService,
6
- BSWithNft,
7
- BlockchainDataService,
8
- BlockchainService,
9
- ExchangeDataService,
10
- Network,
11
- NftDataService,
12
- PartialBy,
13
- Token,
14
- TransferParam,
15
- } from '@cityofzion/blockchain-service'
16
- import { ethers } from 'ethers'
17
- import * as ethersJsonWallets from '@ethersproject/json-wallets'
18
- import * as ethersBytes from '@ethersproject/bytes'
19
- import * as ethersBigNumber from '@ethersproject/bignumber'
20
- import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NATIVE_ASSETS, TOKENS } from './constants'
21
- import { BitqueryEDSEthereum } from './BitqueryEDSEthereum'
22
- import { GhostMarketNDSEthereum } from './GhostMarketNDSEthereum'
23
- import { RpcBDSEthereum } from './RpcBDSEthereum'
24
- import { BitqueryBDSEthereum } from './BitqueryBDSEthereum'
25
-
26
- export class BSEthereum<BSCustomName extends string = string>
27
- implements BlockchainService, BSWithNft, BSWithNameService, BSCalculableFee
28
- {
29
- readonly blockchainName: BSCustomName
30
- readonly feeToken: Token
31
- readonly derivationPath: string
32
- private readonly bitqueryApiKey: string
33
-
34
- blockchainDataService!: BlockchainDataService
35
- exchangeDataService!: ExchangeDataService
36
- tokens: Token[]
37
- nftDataService!: NftDataService
38
- network!: Network
39
-
40
- constructor(blockchainName: BSCustomName, network: PartialBy<Network, 'url'>, bitqueryApiKey: string) {
41
- this.blockchainName = blockchainName
42
- this.derivationPath = DERIVATION_PATH
43
- this.tokens = TOKENS[network.type]
44
- this.bitqueryApiKey = bitqueryApiKey
45
-
46
- this.feeToken = this.tokens.find(token => token.symbol === 'ETH')!
47
- this.setNetwork(network)
48
- }
49
-
50
- setNetwork(param: PartialBy<Network, 'url'>) {
51
- const network = {
52
- type: param.type,
53
- url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type],
54
- }
55
- this.network = network
56
-
57
- if (network.type === 'custom') {
58
- this.blockchainDataService = new RpcBDSEthereum(network)
59
- } else {
60
- this.blockchainDataService = new BitqueryBDSEthereum(network, this.bitqueryApiKey)
61
- }
62
-
63
- this.exchangeDataService = new BitqueryEDSEthereum(network.type, this.bitqueryApiKey)
64
- this.nftDataService = new GhostMarketNDSEthereum(network.type)
65
- }
66
-
67
- validateAddress(address: string): boolean {
68
- return ethers.utils.isAddress(address)
69
- }
70
-
71
- validateEncrypted(json: string): boolean {
72
- return ethersJsonWallets.isCrowdsaleWallet(json) || ethersJsonWallets.isKeystoreWallet(json)
73
- }
74
-
75
- validateKey(key: string): boolean {
76
- try {
77
- if (!key.startsWith('0x')) {
78
- key = '0x' + key
79
- }
80
- if (ethersBytes.hexDataLength(key) !== 32) return false
81
-
82
- return true
83
- } catch (error) {
84
- return false
85
- }
86
- }
87
-
88
- validateNameServiceDomainFormat(domainName: string): boolean {
89
- if (!domainName.endsWith('.eth')) return false
90
- return true
91
- }
92
-
93
- generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath {
94
- const path = this.derivationPath.replace('?', index.toString())
95
- const wallet = ethers.Wallet.fromMnemonic(Array.isArray(mnemonic) ? mnemonic.join(' ') : mnemonic, path)
96
-
97
- return {
98
- address: wallet.address,
99
- key: wallet.privateKey,
100
- type: 'privateKey',
101
- derivationPath: path,
102
- }
103
- }
104
-
105
- generateAccountFromKey(key: string): Account {
106
- const wallet = new ethers.Wallet(key)
107
- return {
108
- address: wallet.address,
109
- key,
110
- type: 'privateKey',
111
- }
112
- }
113
-
114
- async decrypt(json: string, password: string): Promise<Account> {
115
- const wallet = await ethers.Wallet.fromEncryptedJson(json, password)
116
- return {
117
- address: wallet.address,
118
- key: wallet.privateKey,
119
- type: 'privateKey',
120
- }
121
- }
122
-
123
- async encrypt(key: string, password: string): Promise<string> {
124
- const wallet = new ethers.Wallet(key)
125
- return wallet.encrypt(password)
126
- }
127
-
128
- async transfer({ senderAccount, intent }: TransferParam): Promise<string> {
129
- const provider = new ethers.providers.JsonRpcProvider(this.network.url)
130
- const wallet = new ethers.Wallet(senderAccount.key, provider)
131
-
132
- let transaction: ethers.providers.TransactionResponse
133
- const decimals = intent.tokenDecimals ?? 18
134
- const amount = ethersBigNumber.parseFixed(intent.amount, decimals)
135
-
136
- const isNative = NATIVE_ASSETS.some(asset => asset.hash === intent.tokenHash)
137
- if (!isNative) {
138
- const contract = new ethers.Contract(
139
- intent.tokenHash,
140
- ['function transfer(address to, uint amount) returns (bool)'],
141
- wallet
142
- )
143
- transaction = await contract.transfer(intent.receiverAddress, amount)
144
- } else {
145
- transaction = await wallet.sendTransaction({
146
- to: intent.receiverAddress,
147
- value: amount,
148
- })
149
- }
150
-
151
- return transaction.hash
152
- }
153
-
154
- async calculateTransferFee({ senderAccount, intent }: TransferParam): Promise<string> {
155
- const provider = new ethers.providers.JsonRpcProvider(this.network.url)
156
- const wallet = new ethers.Wallet(senderAccount.key, provider)
157
-
158
- const gasPrice = await provider.getGasPrice()
159
-
160
- let estimated: ethers.BigNumber
161
-
162
- const isNative = NATIVE_ASSETS.some(asset => asset.hash === intent.tokenHash)
163
- const decimals = intent.tokenDecimals ?? 18
164
- const amount = ethersBigNumber.parseFixed(intent.amount, decimals)
165
-
166
- if (!isNative) {
167
- const contract = new ethers.Contract(
168
- intent.tokenHash,
169
- ['function transfer(address to, uint amount) returns (bool)'],
170
- wallet
171
- )
172
-
173
- estimated = await contract.estimateGas.transfer(intent.receiverAddress, amount)
174
- } else {
175
- estimated = await wallet.estimateGas({
176
- to: intent.receiverAddress,
177
- value: amount,
178
- })
179
- }
180
-
181
- return ethers.utils.formatEther(gasPrice.mul(estimated))
182
- }
183
-
184
- async resolveNameServiceDomain(domainName: string): Promise<string> {
185
- const provider = new ethers.providers.JsonRpcProvider(this.network.url)
186
- const address = await provider.resolveName(domainName)
187
- if (!address) throw new Error('No address found for domain name')
188
- return address
189
- }
190
- }
@@ -1,231 +0,0 @@
1
- import {
2
- BalanceResponse,
3
- ContractResponse,
4
- NetworkType,
5
- Token,
6
- TransactionsByAddressParams,
7
- TransactionsByAddressResponse,
8
- TransactionResponse,
9
- TransactionTransferAsset,
10
- TransactionTransferNft,
11
- Network,
12
- } from '@cityofzion/blockchain-service'
13
- import { Client, fetchExchange } from '@urql/core'
14
- import fetch from 'node-fetch'
15
- import { BITQUERY_NETWORK_BY_NETWORK_TYPE, BITQUERY_URL, NATIVE_ASSETS, TOKENS } from './constants'
16
- import {
17
- BitqueryTransaction,
18
- bitqueryGetBalanceQuery,
19
- bitqueryGetTokenInfoQuery,
20
- bitqueryGetTransactionQuery,
21
- bitqueryGetTransactionsByAddressQuery,
22
- } from './graphql'
23
- import { RpcBDSEthereum } from './RpcBDSEthereum'
24
-
25
- export class BitqueryBDSEthereum extends RpcBDSEthereum {
26
- private readonly client: Client
27
- private readonly networkType: Exclude<NetworkType, 'custom'>
28
-
29
- maxTimeToConfirmTransactionInMs: number = 1000 * 60 * 8
30
-
31
- constructor(network: Network, apiKey: string) {
32
- super(network)
33
-
34
- if (network.type === 'custom') throw new Error('Custom network not supported')
35
- this.networkType = network.type
36
-
37
- this.client = new Client({
38
- url: BITQUERY_URL,
39
- exchanges: [fetchExchange],
40
- fetch,
41
- fetchOptions: {
42
- headers: {
43
- 'X-API-KEY': apiKey,
44
- },
45
- },
46
- })
47
- }
48
-
49
- async getTransaction(hash: string): Promise<TransactionResponse> {
50
- const result = await this.client
51
- .query(bitqueryGetTransactionQuery, {
52
- hash,
53
- network: BITQUERY_NETWORK_BY_NETWORK_TYPE[this.networkType],
54
- })
55
- .toPromise()
56
- if (result.error) throw new Error(result.error.message)
57
- if (!result.data || !result.data.ethereum.transfers.length) throw new Error('Transaction not found')
58
-
59
- const transfers = result.data.ethereum.transfers.map(this.parseTransactionTransfer)
60
-
61
- const {
62
- block: {
63
- height,
64
- timestamp: { unixtime },
65
- },
66
- transaction: { gasValue, hash: transactionHash },
67
- } = result.data.ethereum.transfers[0]
68
-
69
- return {
70
- block: height,
71
- time: unixtime,
72
- hash: transactionHash,
73
- fee: String(gasValue),
74
- transfers,
75
- notifications: [],
76
- }
77
- }
78
-
79
- async getTransactionsByAddress({
80
- address,
81
- page = 1,
82
- }: TransactionsByAddressParams): Promise<TransactionsByAddressResponse> {
83
- const limit = 10
84
- const offset = limit * (page - 1)
85
-
86
- const result = await this.client
87
- .query(bitqueryGetTransactionsByAddressQuery, {
88
- address,
89
- limit,
90
- offset,
91
- network: BITQUERY_NETWORK_BY_NETWORK_TYPE[this.networkType],
92
- })
93
- .toPromise()
94
-
95
- if (result.error) throw new Error(result.error.message)
96
- if (!result.data) throw new Error('Address does not have transactions')
97
-
98
- const totalCount =
99
- (result.data.ethereum.sentCount[0].count ?? 0) + (result.data.ethereum.receiverCount[0].count ?? 0)
100
- const mixedTransfers = [...(result?.data?.ethereum?.sent ?? []), ...(result?.data?.ethereum?.received ?? [])]
101
-
102
- const transactions = new Map<string, TransactionResponse>()
103
-
104
- mixedTransfers.forEach(transfer => {
105
- const transactionTransfer = this.parseTransactionTransfer(transfer)
106
-
107
- const existingTransaction = transactions.get(transfer.transaction.hash)
108
- if (existingTransaction) {
109
- existingTransaction.transfers.push(transactionTransfer)
110
- return
111
- }
112
-
113
- transactions.set(transfer.transaction.hash, {
114
- block: transfer.block.height,
115
- hash: transfer.transaction.hash,
116
- time: transfer.block.timestamp.unixtime,
117
- fee: String(transfer.transaction.gasValue),
118
- transfers: [transactionTransfer],
119
- notifications: [],
120
- })
121
- })
122
-
123
- return {
124
- totalCount,
125
- limit: limit * 2,
126
- transactions: Array.from(transactions.values()),
127
- }
128
- }
129
-
130
- async getContract(): Promise<ContractResponse> {
131
- throw new Error("Bitquery doesn't support contract info")
132
- }
133
-
134
- async getTokenInfo(hash: string): Promise<Token> {
135
- const localToken = TOKENS[this.networkType].find(token => token.hash === hash)
136
- if (localToken) return localToken
137
-
138
- const result = await this.client
139
- .query(bitqueryGetTokenInfoQuery, {
140
- hash,
141
- network: BITQUERY_NETWORK_BY_NETWORK_TYPE[this.networkType],
142
- })
143
- .toPromise()
144
-
145
- if (result.error) throw new Error(result.error.message)
146
- if (!result.data || result.data.ethereum.smartContractCalls.length <= 0) throw new Error('Token not found')
147
-
148
- const {
149
- address: { address },
150
- currency: { decimals, name, symbol, tokenType },
151
- } = result.data.ethereum.smartContractCalls[0].smartContract
152
-
153
- if (tokenType !== 'ERC20') throw new Error('Token is not ERC20')
154
-
155
- return {
156
- hash: address,
157
- name,
158
- symbol,
159
- decimals,
160
- }
161
- }
162
-
163
- async getBalance(address: string): Promise<BalanceResponse[]> {
164
- const result = await this.client
165
- .query(bitqueryGetBalanceQuery, {
166
- address,
167
- network: BITQUERY_NETWORK_BY_NETWORK_TYPE[this.networkType],
168
- })
169
- .toPromise()
170
-
171
- if (result.error) throw new Error(result.error.message)
172
- const data = result.data?.ethereum.address[0].balances ?? []
173
- const ethBalance = result.data?.ethereum.address[0].balance ?? 0
174
- const ethToken = NATIVE_ASSETS.find(asset => asset.symbol === 'ETH')!
175
-
176
- const balances: BalanceResponse[] = [
177
- {
178
- amount: ethBalance.toString(),
179
- token: ethToken,
180
- },
181
- ]
182
-
183
- data.forEach(({ value, currency: { address, decimals, name, symbol } }) => {
184
- if (value < 0 || address === ethToken.hash) return
185
-
186
- balances.push({
187
- amount: value.toString(),
188
- token: {
189
- hash: address,
190
- symbol,
191
- name,
192
- decimals,
193
- },
194
- })
195
- })
196
-
197
- return balances
198
- }
199
-
200
- private parseTransactionTransfer({
201
- amount,
202
- currency: { tokenType, address, decimals, name, symbol },
203
- entityId,
204
- sender,
205
- receiver,
206
- }: BitqueryTransaction): TransactionTransferAsset | TransactionTransferNft {
207
- if (tokenType === 'ERC721') {
208
- return {
209
- from: sender.address,
210
- to: receiver.address,
211
- tokenId: entityId,
212
- contractHash: address,
213
- type: 'nft',
214
- }
215
- }
216
-
217
- return {
218
- from: sender.address,
219
- to: receiver.address,
220
- contractHash: address,
221
- amount: amount.toString(),
222
- token: {
223
- decimals: decimals,
224
- hash: address,
225
- name: name,
226
- symbol: symbol,
227
- },
228
- type: 'token',
229
- }
230
- }
231
- }
@@ -1,67 +0,0 @@
1
- import { Currency, ExchangeDataService, NetworkType, TokenPricesResponse } from '@cityofzion/blockchain-service'
2
- import { Client, fetchExchange } from '@urql/core'
3
- import fetch from 'node-fetch'
4
- import { BITQUERY_URL } from './constants'
5
- import dayjs from 'dayjs'
6
- import utc from 'dayjs/plugin/utc'
7
- import { bitqueryGetPricesQuery } from './graphql'
8
-
9
- dayjs.extend(utc)
10
- export class BitqueryEDSEthereum implements ExchangeDataService {
11
- private readonly client: Client
12
- private readonly networkType: NetworkType
13
-
14
- constructor(networkType: NetworkType, apiKey: string) {
15
- this.networkType = networkType
16
-
17
- this.client = new Client({
18
- url: BITQUERY_URL,
19
- exchanges: [fetchExchange],
20
- fetch,
21
- fetchOptions: {
22
- headers: {
23
- 'X-API-KEY': apiKey,
24
- },
25
- },
26
- })
27
- }
28
-
29
- async getTokenPrices(currency: Currency): Promise<TokenPricesResponse[]> {
30
- if (this.networkType !== 'mainnet') throw new Error('Exchange is only available on mainnet')
31
-
32
- const twoDaysAgo = dayjs.utc().subtract(2, 'day').startOf('date').toISOString()
33
-
34
- const result = await this.client
35
- .query(bitqueryGetPricesQuery, { after: twoDaysAgo, network: 'ethereum' })
36
- .toPromise()
37
- if (result.error) {
38
- throw new Error(result.error.message)
39
- }
40
- if (!result.data) {
41
- throw new Error('There is no price data')
42
- }
43
-
44
- let currencyRatio: number = 1
45
- if (currency !== 'USD') {
46
- currencyRatio = await this.getCurrencyRatio(currency)
47
- }
48
-
49
- const prices = result.data.ethereum.dexTrades.map(
50
- (trade): TokenPricesResponse => ({
51
- symbol: trade.baseCurrency.symbol,
52
- price: trade.quotePrice * currencyRatio,
53
- hash: trade.baseCurrency.address,
54
- })
55
- )
56
-
57
- return prices
58
- }
59
-
60
- private async getCurrencyRatio(currency: Currency): Promise<number> {
61
- const request = await fetch(`https://api.flamingo.finance/fiat/exchange-rate?pair=USD_${currency}`, {
62
- method: 'GET',
63
- })
64
- const data = await request.json()
65
- return data
66
- }
67
- }
@@ -1,125 +0,0 @@
1
- import {
2
- NftResponse,
3
- NftsResponse,
4
- NetworkType,
5
- NftDataService,
6
- GetNftParam,
7
- GetNftsByAddressParams,
8
- } from '@cityofzion/blockchain-service'
9
- import qs from 'query-string'
10
-
11
- import { GHOSTMARKET_CHAIN_BY_NETWORK_TYPE, GHOSTMARKET_URL_BY_NETWORK_TYPE } from './constants'
12
- import fetch from 'node-fetch'
13
-
14
- type GhostMarketNFT = {
15
- tokenId: string
16
- contract: {
17
- chain?: string
18
- hash: string
19
- symbol: string
20
- }
21
- creator: {
22
- address: string
23
- offchainName?: string
24
- }
25
- apiUrl?: string
26
- ownerships: {
27
- owner: {
28
- address?: string
29
- }
30
- }[]
31
- collection: {
32
- name?: string
33
- logoUrl?: string
34
- }
35
- metadata: {
36
- description: string
37
- mediaType: string
38
- mediaUri: string
39
- mintDate: number
40
- mintNumber: number
41
- name: string
42
- }
43
- }
44
-
45
- type GhostMarketAssets = {
46
- assets: GhostMarketNFT[]
47
- next: string
48
- }
49
- export class GhostMarketNDSEthereum implements NftDataService {
50
- private networkType: NetworkType
51
-
52
- constructor(networkType: NetworkType) {
53
- this.networkType = networkType
54
- }
55
-
56
- async getNftsByAddress({ address, size = 18, cursor }: GetNftsByAddressParams): Promise<NftsResponse> {
57
- const url = this.getUrlWithParams({
58
- size,
59
- owners: [address],
60
- cursor: cursor,
61
- })
62
-
63
- const request = await fetch(url, { method: 'GET' })
64
- const data = (await request.json()) as GhostMarketAssets
65
- const nfts = data.assets ?? []
66
-
67
- return { nextCursor: data.next, items: nfts.map(this.parse.bind(this)) }
68
- }
69
-
70
- async getNft({ contractHash, tokenId }: GetNftParam): Promise<NftResponse> {
71
- const url = this.getUrlWithParams({
72
- contract: contractHash,
73
- tokenIds: [tokenId],
74
- })
75
-
76
- const request = await fetch(url, { method: 'GET' })
77
- const data = (await request.json()) as GhostMarketAssets
78
-
79
- return this.parse(data.assets[0])
80
- }
81
-
82
- private treatGhostMarketImage(srcImage?: string) {
83
- if (!srcImage) {
84
- return
85
- }
86
-
87
- if (srcImage.startsWith('ipfs://')) {
88
- const [, imageId] = srcImage.split('://')
89
-
90
- return `https://ghostmarket.mypinata.cloud/ipfs/${imageId}`
91
- }
92
-
93
- return srcImage
94
- }
95
-
96
- private getUrlWithParams(params: any) {
97
- const parameters = qs.stringify(
98
- {
99
- chain: GHOSTMARKET_CHAIN_BY_NETWORK_TYPE[this.networkType],
100
- ...params,
101
- },
102
- { arrayFormat: 'bracket' }
103
- )
104
- return `${GHOSTMARKET_URL_BY_NETWORK_TYPE[this.networkType]}/assets?${parameters}`
105
- }
106
-
107
- private parse(data: GhostMarketNFT) {
108
- const nftResponse: NftResponse = {
109
- collectionImage: this.treatGhostMarketImage(data.collection?.logoUrl),
110
- id: data.tokenId,
111
- contractHash: data.contract.hash,
112
- symbol: data.contract.symbol,
113
- collectionName: data.collection?.name,
114
- image: this.treatGhostMarketImage(data.metadata.mediaUri),
115
- isSVG: String(data.metadata.mediaType).includes('svg+xml'),
116
- name: data.metadata.name,
117
- creator: {
118
- address: data.creator.address,
119
- name: data.creator.offchainName,
120
- },
121
- }
122
-
123
- return nftResponse
124
- }
125
- }