/
scatterPlayer.js
299 lines (254 loc) · 7.96 KB
/
scatterPlayer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
import Eos from 'eosjs'
import DB from './db'
import { Player } from '../src/player'
import { forMs, forCondition } from '../src/utils/wait'
import { MykeyPlugins } from '../src/helpers/mykeyPlugins'
import Log_ from '../src/utils/log'
const log = Log_('scatterPlayer')
/**
* Event names supported in scatter player
* @type {{ERR_GET_SCATTER_FAILED: string, ERR_GET_IDENTITY_FAILED: string}}
*/
const EVENT_NAMES = {
ERR_GET_SCATTER_FAILED: 'ERR_GET_SCATTER_FAILED',
ERR_GET_IDENTITY_FAILED: 'ERR_GET_IDENTITY_FAILED',
ERR_LOGOUT_FAILED: 'ERR_LOGOUT_FAILED',
ERR_CONF_NOT_FOUND: 'ERR_CONF_NOT_FOUND'
}
/**
* Player on browser (need scatter)
* @author kinghand@foxmail.com
*/
export default class ScatterPlayer extends Player {
constructor (netConf) {
super()
this.events.enableEvents(EVENT_NAMES)
this._networks = netConf
this._db = new DB({
network_name: 'dev',
lang: 'ch'
})
log.info(`eosplayer created: \n${this.netName} \n${JSON.stringify(this.netConf)}`)
this.identityReceiver = []
}
/**
* storage of scatter player
* @return {DB}
*/
get storage () {
return this._db
}
/**
* switch to an network with name
* @param key
*/
switchNetwork (key) {
if (key in this._networks) {
this.storage.set('network_name', key)
this._eosClient = null
log.info(`network changed to ${this.netName}.`)
} else {
log.warning(`network ${key} cannot find.`)
}
}
/**
* add net config to table at runtime
* @param netName
* @param conf
*/
setNetConf (netName, conf) {
this._networks[netName] = conf
}
/**
* get network name in use
*/
get netName () {
return this.storage.get('network_name')
}
/**
* get network config of cur netName
*/
get netConf () {
let conf = this._networks[this.netName]
if (!conf) {
this.events.emitEvent(EVENT_NAMES.ERR_CONF_NOT_FOUND, new Error(`cannot find config of ${this.netName}`))
}
return conf
}
/**
* try get scatter
* @see https://get-scatter.com/docs/examples-interaction-flow
* @return {Scatter}
*/
get scatter () {
let scatter = window.scatter
if (!scatter) {
let err = new Error('scatter cannot found')
this.events.emitEvent(EVENT_NAMES.ERR_GET_SCATTER_FAILED, err)
// throw err;
}
return scatter
}
/**
* get Mykey plugins for sign and verify signature
*/
get mykeyPlugins () {
return new MykeyPlugins(this.chain)
}
/**
* try get scatter async - if not find
* @see https://get-scatter.com/docs/examples-interaction-flow
* @return {Scatter}
*/
async getScatterAsync (maxTry = 100) {
while (!window.scatter && maxTry--) {
log.verbose('get scatter failed, retry :', maxTry)
await forMs(100)
}
if (!window.scatter) {
let err = new Error('scatter cannot found')
this.events.emitEvent(EVENT_NAMES.ERR_GET_SCATTER_FAILED, err)
}
return window.scatter
}
/**
* login - require account identity from scatter
* @return {Promise<{Identity}>}
*/
async login () {
return await this.getIdentity()
}
/**
* logout
* @return {Promise<void>}
*/
async logout () {
try {
let ret = await (await this.getScatterAsync()).forgetIdentity()
log.info(`log out from ${this.storage.get('latest_chain_id')}`)
return ret
} catch (err) {
this.events.emitEvent(EVENT_NAMES.ERR_LOGOUT_FAILED, err)
}
}
/**
* get or create scatter
* @return {eosAPI}
*/
get eosClient () {
if (!this._eosClient) {
this._eosClient = this.scatter.eos(this.netConf, Eos, {}, this.netConf.protocol)
}
return this._eosClient
}
/**
* getIdentity of cur scatter user
* @attention When there are multiple concurrent getIdentity requests, scatter will only return the first one.
* @return {Promise<{Identity}>}
*/
async getIdentity () {
let scatter_ = await this.getScatterAsync()
let originChainID = this.storage.get('latest_chain_id')
let chainID = this.netConf.chainId
if ((!!originChainID) && chainID !== originChainID) {
log.info(`a changing of chain_id detected: ${originChainID} -> ${chainID} `)
await this.logout()
}
this.storage.set('latest_chain_id', chainID)
// using message queue to del
let identity
const receiveInstanceOrError = idty => identity = idty
this.identityReceiver.push(receiveInstanceOrError)
if (this.identityReceiver.length <= 1) {
scatter_.getIdentity({
accounts: [this.netConf] // need slot 'chainid' and 'blockchain'
}).then(() => {
this.identityReceiver.forEach(receiver => receiver(scatter_.identity.accounts.find(acc => acc.blockchain === 'eos')))
this.identityReceiver = []
}).catch(err => {
this.identityReceiver.forEach(receiver => receiver(err))
this.identityReceiver = []
})
}
await forCondition(() => !!identity) // using undefined to block operation, using null to handle error
if (identity instanceof Error || (identity.isError)) {
this.events.emitEvent(EVENT_NAMES.ERR_GET_IDENTITY_FAILED, identity)
throw identity
}
return identity
}
/**
* sign a message with current identity
* @param message
* @param {Object.<string,function>} plugins - plugin should be object
* @return {Promise<void>} - signed data
* @constructor
*/
async sign (message, signPlugins) {
let identity = await this.getIdentity()
let account = identity.name
const { permissions } = await this.getAccountInfo(account)
if (!permissions) {
log.warning(`permissions of account ${account} are not found.`)
return
}
const perm = permissions.find(p => p.perm_name === identity.authority)
log.info(`perm : ${JSON.stringify(perm)}`)
const { accounts, keys } = perm.required_auth
let pubkeys = keys
if(pubkeys.length == 0 && signPlugins) {
const accountsStrs = accounts.map(acc => `${acc.permission.actor}@${acc.permission.permission}`)
log.verbose('try sign', accounts, accountsStrs, signPlugins)
for (let i in accountsStrs) {
const plugin = signPlugins[accountsStrs[i]]
if (!plugin) {
continue
}
let signKey = await Promise.resolve(plugin(account))
pubkeys.push({key: signKey})
}
}
let ret = ''
for (let i = 0; i < pubkeys.length; i++) {
try {
log.info(`try sign (${JSON.stringify(pubkeys[i])}) : ${message}`)
ret = await this.scatter.getArbitrarySignature(pubkeys[i].key, message)
break
} catch (ex) {
log.warning(`try pub key failed ${pubkeys[i]}`)
}
}
return ret
}
get help () {
return super.help + `
## Usage of eosplayer (for broswer)
### Events
ERR_GET_SCATTER_FAILED
ERR_GET_IDENTITY_FAILED
ERR_LOGOUT_FAILED
### APIs
\`\`\`js
{void} eosplayer.switchNetwork(val) // switch network
{void} eosplayer.setNetConf(network_name, conf) // add a network config at runtime
get {Scatter} eosplayer.scatter // get scatter instance
get {Scatter} async getScatterAsync(maxTry = 100) // get scatter instance
get {string} eosplayer.netName // get current network name
get {string} eosplayer.netConf // get current network config
async {Identity} eosplayer.login() // let user allow you using identity
async {void} eosplayer.logout() // return back the identity
async {string} sign(message) // sign a message with current identity
\`\`\`
## Imported libs
\`\`\`js
window.eosjs = Eos; /** the eosjs lib @see {@url https://www.npmjs.com/package/eosjs} */
window.env = env; /** {isPc} */
window.idb = idb; /** idb lib for browser storage @see {@url https://www.npmjs.com/package/idb } */
window.BigNumber = BigNumber; /** big number @see {@url https://www.npmjs.com/package/bignumber.js} */
window.kh.eos.Player
window.kh.eos.ScatterPlayer
window.eosplayer = new ScatterPlayer(networks);
\`\`\`
`
}
}