@exodus/solana-api 1.0.1 → 1.1.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 (2) hide show
  1. package/package.json +4 -8
  2. package/src/index.js +32 -151
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/solana-api",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Exodus internal Solana asset API wrapper",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -13,12 +13,8 @@
13
13
  "access": "restricted"
14
14
  },
15
15
  "dependencies": {
16
- "fetchival": "~0.3.2",
17
- "lodash": "^4.17.11",
18
- "ms": "~0.7.1"
16
+ "@exodus/solana-web3.js": "0.87.1-exodus1",
17
+ "lodash": "^4.17.11"
19
18
  },
20
- "devDependencies": {
21
- "node-fetch": "~1.6.3"
22
- },
23
- "gitHead": "7afc2c44aff00085db4d14b7696a3fc88f165628"
19
+ "gitHead": "f18544ae45db87c7efec7d8139e1e04c51e0fc32"
24
20
  }
package/src/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // @flow
2
- import ms from 'ms'
3
- import fetchival from 'fetchival'
2
+ import { Connection, PublicKey } from '@exodus/solana-web3.js'
4
3
 
5
- // Doc: https://docs.solana.com/apps/jsonrpc-api
4
+ // Doc: https://solana-labs.github.io/solana-web3.js/
6
5
 
7
6
  const RPC_URL = 'https://api.mainnet-beta.solana.com' // https://solana-api.projectserum.com
8
7
 
@@ -13,111 +12,41 @@ class Api {
13
12
 
14
13
  setServer(rpcUrl) {
15
14
  this.rpcUrl = rpcUrl || RPC_URL
15
+ this.connection = new Connection(this.rpcUrl)
16
16
  }
17
17
 
18
- request(path) {
19
- return fetchival(this.rpcUrl, {
20
- timeout: ms('15s'),
21
- headers: { 'Content-Type': 'application/json' },
22
- })(path)
23
- }
24
-
25
- async getRecentBlockHash() {
26
- try {
27
- const { result, error } = await this.request('').post({
28
- jsonrpc: '2.0',
29
- id: getCurrentTime(),
30
- method: 'getRecentBlockhash',
31
- params: [],
32
- })
33
- if (error) throw new Error(error.message)
34
- return result.value.blockhash
35
- } catch (err) {
36
- console.log(err)
37
- throw new Error(JSON.stringify(await concatStream(err.response)))
38
- }
18
+ async getRecentBlockHash(): string {
19
+ const { blockhash } = await this.connection.getRecentBlockhash()
20
+ return blockhash
39
21
  }
40
22
 
41
23
  // Transaction structure: https://docs.solana.com/apps/jsonrpc-api#transaction-structure
42
24
  async getTransactionById(id: string) {
43
- try {
44
- const { result, error } = await this.request('').post({
45
- jsonrpc: '2.0',
46
- id: getCurrentTime(),
47
- method: 'getConfirmedTransaction',
48
- params: [id, 'json'],
49
- })
50
- if (error) throw new Error(error.message)
51
- return result
52
- } catch (err) {
53
- console.log(err)
54
- throw new Error(JSON.stringify(await concatStream(err.response)))
55
- }
25
+ return this.connection.getConfirmedTransaction(id)
56
26
  }
57
27
 
58
28
  async getFee(): number {
59
- try {
60
- const { result, error } = await this.request('').post({
61
- jsonrpc: '2.0',
62
- id: getCurrentTime(),
63
- method: 'getFees',
64
- params: [],
65
- })
66
- if (error) throw new Error(error.message)
67
- return result.value.feeCalculator.lamportsPerSignature
68
- } catch (err) {
69
- throw new Error(JSON.stringify(await concatStream(err.response)))
70
- }
29
+ const {
30
+ feeCalculator: { lamportsPerSignature },
31
+ } = await this.connection.getRecentBlockhash()
32
+ return lamportsPerSignature
71
33
  }
72
34
 
73
35
  async getBalance(address: string): number {
74
- try {
75
- const { result, error } = await this.request('').post({
76
- jsonrpc: '2.0',
77
- id: getCurrentTime(),
78
- method: 'getBalance',
79
- params: [address],
80
- })
81
- if (error) throw new Error(error.message)
82
- return result.value || 0
83
- } catch (err) {
84
- console.log(err)
85
- throw new Error(JSON.stringify(await concatStream(err.response)))
86
- }
36
+ return this.connection.getBalance(new PublicKey(address))
87
37
  }
88
38
 
89
- async getBlockTime(slot: number): any {
90
- try {
91
- const { result, error } = await this.request('').post({
92
- jsonrpc: '2.0',
93
- id: getCurrentTime(),
94
- method: 'getBlockTime',
95
- params: [slot],
96
- })
97
- // might result in error if executed on a validator with partial ledger (https://github.com/solana-labs/solana/issues/12413)
98
- if (error) console.log(error.message)
99
- return result
100
- } catch (err) {
101
- console.log(err)
102
- throw new Error(JSON.stringify(await concatStream(err.response)))
103
- }
39
+ async getBlockTime(slot: number) {
40
+ // might result in error if executed on a validator with partial ledger (https://github.com/solana-labs/solana/issues/12413)
41
+ return this.connection.getBlockTime(slot)
104
42
  }
105
43
 
106
- async getConfirmedSignaturesForAddress(address: string, { before, until, limit }): any {
107
- try {
108
- until = until || undefined
109
- const { result, error } = await this.request('').post({
110
- jsonrpc: '2.0',
111
- id: getCurrentTime(),
112
- method: 'getConfirmedSignaturesForAddress2',
113
- params: [address, { before, until, limit }],
114
- })
115
- if (error) throw new Error(error.message)
116
- return result
117
- } catch (err) {
118
- console.log(err)
119
- throw new Error(JSON.stringify(await concatStream(err.response)))
120
- }
44
+ async getConfirmedSignaturesForAddress(address: string, { before, until, limit } = {}): any {
45
+ return this.connection.getConfirmedSignaturesForAddress2(new PublicKey(address), {
46
+ before,
47
+ until,
48
+ limit,
49
+ })
121
50
  }
122
51
 
123
52
  /**
@@ -137,13 +66,15 @@ class Api {
137
66
 
138
67
  for (let tx of txsId) {
139
68
  // get tx details
140
- const txDetails = await this.getTransactionById(tx.signature)
69
+ const [txDetails, blockTime] = await Promise.all([
70
+ this.getTransactionById(tx.signature),
71
+ this.getBlockTime(tx.slot),
72
+ ])
141
73
  if (txDetails === null) continue
142
74
 
143
- const timestamp = (await this.getBlockTime(tx.slot)) * 1000
144
-
145
- const from = txDetails.transaction.message.accountKeys[0]
146
- const to = txDetails.transaction.message.accountKeys[1]
75
+ const timestamp = blockTime * 1000
76
+ const from = txDetails.transaction.keys[0].toString()
77
+ const to = txDetails.transaction.keys[1].toString()
147
78
  let { preBalances, postBalances, fee } = txDetails.meta
148
79
  const isSending = address === from
149
80
  fee = !isSending ? 0 : fee
@@ -161,17 +92,11 @@ class Api {
161
92
  from,
162
93
  to,
163
94
  amount, // lamports
164
- error: !(txDetails.meta.status.Ok === null),
95
+ error: !(txDetails.meta.err === null),
165
96
  })
166
97
  }
167
98
  } catch (err) {
168
99
  console.warn('Solana error:', err)
169
- if (err.response) {
170
- const error = new Error(JSON.stringify(await concatStream(err.response)))
171
- error.status = err.response.status
172
- error.statusText = err.response.statusText
173
- throw error
174
- }
175
100
  throw err
176
101
  }
177
102
 
@@ -184,20 +109,10 @@ class Api {
184
109
  * Broadcast a signed transaction
185
110
  */
186
111
  broadcastTransaction = async (signedTx: string): string => {
187
- console.log('Solana broadcasting TX:', signedTx)
188
-
189
- const { result, error } = await this.request('')
190
- .post({
191
- jsonrpc: '2.0',
192
- id: getCurrentTime(),
193
- method: 'sendTransaction',
194
- params: [signedTx],
195
- })
196
- .catch(async (err) => {
197
- throw new Error(JSON.stringify(await concatStream(err.response)))
198
- })
112
+ console.log('Solana broadcasting TX:', signedTx) // base64
199
113
 
200
- if (error) throw new Error(error.message)
114
+ const { message, ...result } = await this.connection.sendEncodedTransaction(signedTx)
115
+ if (message) throw new Error(message)
201
116
 
202
117
  console.log(`tx ${result} sent!`)
203
118
  return result || null
@@ -205,37 +120,3 @@ class Api {
205
120
  }
206
121
 
207
122
  export default new Api()
208
-
209
- function getCurrentTime() {
210
- const date = new Date()
211
- return date.getTime()
212
- }
213
-
214
- function concatStream(response) {
215
- const stream = response.body
216
- if (typeof stream !== 'object')
217
- throw new Error(`${response.status} - ${response.statusText || 'Server Error'}`)
218
- if ('locked' in stream) {
219
- // fetch browser
220
- try {
221
- return response.json()
222
- } catch (e) {
223
- return response.text()
224
- }
225
- }
226
- return new Promise((resolve, reject) => {
227
- let result = ''
228
- stream.on('data', (chunk) => {
229
- result += chunk.toString()
230
- })
231
- stream.on('end', () => {
232
- try {
233
- const parsed = JSON.parse(result)
234
- resolve(parsed)
235
- } catch (e) {
236
- resolve(result) // plain text
237
- }
238
- })
239
- stream.on('error', (err) => reject(err))
240
- })
241
- }