@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.
- package/package.json +4 -8
- 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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
3
|
-
import fetchival from 'fetchival'
|
|
2
|
+
import { Connection, PublicKey } from '@exodus/solana-web3.js'
|
|
4
3
|
|
|
5
|
-
// Doc: https://
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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)
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
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 =
|
|
144
|
-
|
|
145
|
-
const
|
|
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.
|
|
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
|
-
|
|
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
|
-
}
|