@cityofzion/bs-ethereum 0.8.1 → 0.9.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 (41) hide show
  1. package/.rush/temp/operation/build/all.log +1 -1
  2. package/.rush/temp/operation/build/state.json +1 -1
  3. package/.rush/temp/package-deps_build.json +6 -4
  4. package/CHANGELOG.json +27 -0
  5. package/CHANGELOG.md +15 -0
  6. package/bs-ethereum.build.log +1 -1
  7. package/dist/BSEthereum.d.ts +24 -23
  8. package/dist/BSEthereum.js +184 -178
  9. package/dist/BitqueryBDSEthereum.d.ts +14 -14
  10. package/dist/BitqueryBDSEthereum.js +197 -197
  11. package/dist/BitqueryEDSEthereum.d.ts +8 -8
  12. package/dist/BitqueryEDSEthereum.js +73 -73
  13. package/dist/GhostMarketNDSEthereum.d.ts +10 -10
  14. package/dist/GhostMarketNDSEthereum.js +77 -77
  15. package/dist/RpcBDSEthereum.d.ts +12 -12
  16. package/dist/RpcBDSEthereum.js +89 -89
  17. package/dist/assets/tokens/common.json +8 -8
  18. package/dist/constants.d.ts +16 -16
  19. package/dist/constants.js +33 -33
  20. package/dist/graphql.d.ts +124 -124
  21. package/dist/graphql.js +163 -163
  22. package/dist/index.d.ts +6 -6
  23. package/dist/index.js +22 -22
  24. package/jest.config.ts +13 -13
  25. package/jest.setup.ts +1 -1
  26. package/package.json +34 -34
  27. package/src/BSEthereum.ts +189 -184
  28. package/src/BitqueryBDSEthereum.ts +230 -230
  29. package/src/BitqueryEDSEthereum.ts +67 -67
  30. package/src/GhostMarketNDSEthereum.ts +122 -122
  31. package/src/RpcBDSEthereum.ts +88 -88
  32. package/src/__tests__/BDSEthereum.spec.ts +123 -123
  33. package/src/__tests__/BSEthereum.spec.ts +129 -123
  34. package/src/__tests__/BitqueryEDSEthereum.spec.ts +49 -49
  35. package/src/__tests__/GhostMarketNDSEthereum.spec.ts +45 -45
  36. package/src/assets/tokens/common.json +7 -7
  37. package/src/constants.ts +37 -37
  38. package/src/graphql.ts +291 -291
  39. package/src/index.ts +6 -6
  40. package/tsconfig.build.json +4 -4
  41. package/tsconfig.json +15 -15
@@ -1,230 +1,230 @@
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_API_KEY, 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) {
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': BITQUERY_API_KEY,
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
-
175
- const balances: BalanceResponse[] = [
176
- {
177
- amount: ethBalance.toString(),
178
- token: NATIVE_ASSETS.find(asset => asset.symbol === 'ETH')!,
179
- },
180
- ]
181
-
182
- data.forEach(({ value, currency: { address, decimals, name, symbol } }) => {
183
- if (value < 0) return
184
-
185
- balances.push({
186
- amount: value.toString(),
187
- token: {
188
- hash: address,
189
- symbol,
190
- name,
191
- decimals,
192
- },
193
- })
194
- })
195
-
196
- return balances
197
- }
198
-
199
- private parseTransactionTransfer({
200
- amount,
201
- currency: { tokenType, address, decimals, name, symbol },
202
- entityId,
203
- sender,
204
- receiver,
205
- }: BitqueryTransaction): TransactionTransferAsset | TransactionTransferNft {
206
- if (tokenType === 'ERC721') {
207
- return {
208
- from: sender.address,
209
- to: receiver.address,
210
- tokenId: entityId,
211
- contractHash: address,
212
- type: 'nft',
213
- }
214
- }
215
-
216
- return {
217
- from: sender.address,
218
- to: receiver.address,
219
- contractHash: address,
220
- amount: amount.toString(),
221
- token: {
222
- decimals: decimals,
223
- hash: address,
224
- name: name,
225
- symbol: symbol,
226
- },
227
- type: 'token',
228
- }
229
- }
230
- }
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_API_KEY, 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) {
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': BITQUERY_API_KEY,
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
+
175
+ const balances: BalanceResponse[] = [
176
+ {
177
+ amount: ethBalance.toString(),
178
+ token: NATIVE_ASSETS.find(asset => asset.symbol === 'ETH')!,
179
+ },
180
+ ]
181
+
182
+ data.forEach(({ value, currency: { address, decimals, name, symbol } }) => {
183
+ if (value < 0) return
184
+
185
+ balances.push({
186
+ amount: value.toString(),
187
+ token: {
188
+ hash: address,
189
+ symbol,
190
+ name,
191
+ decimals,
192
+ },
193
+ })
194
+ })
195
+
196
+ return balances
197
+ }
198
+
199
+ private parseTransactionTransfer({
200
+ amount,
201
+ currency: { tokenType, address, decimals, name, symbol },
202
+ entityId,
203
+ sender,
204
+ receiver,
205
+ }: BitqueryTransaction): TransactionTransferAsset | TransactionTransferNft {
206
+ if (tokenType === 'ERC721') {
207
+ return {
208
+ from: sender.address,
209
+ to: receiver.address,
210
+ tokenId: entityId,
211
+ contractHash: address,
212
+ type: 'nft',
213
+ }
214
+ }
215
+
216
+ return {
217
+ from: sender.address,
218
+ to: receiver.address,
219
+ contractHash: address,
220
+ amount: amount.toString(),
221
+ token: {
222
+ decimals: decimals,
223
+ hash: address,
224
+ name: name,
225
+ symbol: symbol,
226
+ },
227
+ type: 'token',
228
+ }
229
+ }
230
+ }
@@ -1,67 +1,67 @@
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_API_KEY, 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) {
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': BITQUERY_API_KEY,
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
+ 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_API_KEY, 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) {
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': BITQUERY_API_KEY,
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
+ }