Skip to content
This repository was archived by the owner on Dec 21, 2021. It is now read-only.

Commit 17c996c

Browse files
authored
DataUnions: encapsulate contract handling, remove obsolete wrappers, improve validation, memberStats format (#206)
Split DataUnionEndpoints.ts: move contract handling to Contracts.ts, ABI definitions to abi.ts and inline public methods to DataUnion.ts Smaller changes: - Remove obsolete caching from address fetching - Improve address validation (and add tests) - Split tests, improve test cleanup - Bug fix for payForSignatureTransport option in withdraws: method option overrides client option - Format of getMemberStats() data modified (enum values changed, number values string->BigNumber) - Type annotations for StreamrClientOptions: many of the fields are required, as we apply a default value -> users give options as Partial<StreamrClientOptions> - Id and debug are client properties, not options
1 parent e780937 commit 17c996c

20 files changed

+1450
-1586
lines changed

package-lock.json

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
"@types/uuid": "^8.3.0",
6666
"@typescript-eslint/eslint-plugin": "^4.15.1",
6767
"@typescript-eslint/parser": "^4.15.1",
68-
"async-mutex": "^0.3.0",
6968
"babel-loader": "^8.2.2",
7069
"babel-plugin-add-module-exports": "^1.0.4",
7170
"babel-plugin-transform-class-properties": "^6.24.1",
@@ -114,6 +113,7 @@
114113
"@ethersproject/sha2": "^5.0.8",
115114
"@ethersproject/transactions": "^5.0.10",
116115
"@ethersproject/wallet": "^5.0.11",
116+
"@ethersproject/web": "^5.0.13",
117117
"debug": "^4.3.2",
118118
"eventemitter3": "^4.0.7",
119119
"lodash.uniqueid": "^4.0.1",

src/Config.ts

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,55 @@
11
import qs from 'qs'
2-
import Debug from 'debug'
32
import { ControlLayer, MessageLayer } from 'streamr-client-protocol'
43
import { ExternalProvider, JsonRpcFetchFunc } from '@ethersproject/providers'
54
import { BigNumber } from '@ethersproject/bignumber'
6-
import { O } from 'ts-toolbelt'
7-
import { getVersionString, counterId } from './utils'
5+
import { getVersionString } from './utils'
6+
import { ConnectionInfo } from '@ethersproject/web'
87
import { Todo } from './types'
98

109
export type EthereumConfig = ExternalProvider|JsonRpcFetchFunc
1110

1211
export type StreamrClientOptions = {
13-
id?: string
14-
debug?: Debug.Debugger,
15-
auth?: {
12+
auth: {
1613
privateKey?: string
1714
ethereum?: EthereumConfig
1815
apiKey?: string
1916
username?: string
2017
password?: string
2118
}
22-
url?: string
23-
restUrl?: string
24-
streamrNodeAddress?: string
25-
autoConnect?: boolean
26-
autoDisconnect?: boolean
27-
orderMessages?: boolean,
28-
retryResendAfter?: number,
29-
gapFillTimeout?: number,
30-
maxGapRequests?: number,
31-
maxPublishQueueSize?: number,
32-
publishWithSignature?: Todo,
33-
verifySignatures?: Todo,
34-
publisherStoreKeyHistory?: boolean,
35-
groupKeys?: Todo
36-
keyExchange?: Todo
37-
mainnet?: Todo
38-
sidechain?: {
39-
url?: string
40-
},
19+
url: string
20+
restUrl: string
21+
streamrNodeAddress: string
22+
autoConnect: boolean
23+
autoDisconnect: boolean
24+
orderMessages: boolean
25+
retryResendAfter: number
26+
gapFillTimeout: number
27+
maxGapRequests: number
28+
maxPublishQueueSize: number
29+
publishWithSignature: Todo
30+
verifySignatures: Todo
31+
publisherStoreKeyHistory: boolean
32+
groupKeys: Todo
33+
keyExchange: Todo
34+
mainnet?: ConnectionInfo|string
35+
sidechain?: ConnectionInfo|string
4136
dataUnion?: string
42-
tokenAddress?: string,
43-
minimumWithdrawTokenWei?: BigNumber|number|string,
44-
sidechainTokenAddress?: string
45-
factoryMainnetAddress?: string
46-
factorySidechainAddress?: string
47-
payForSignatureTransport?: boolean
48-
cache?: {
49-
maxSize?: number,
50-
maxAge?: number
37+
tokenAddress: string,
38+
minimumWithdrawTokenWei?: BigNumber|number|string
39+
factoryMainnetAddress: string
40+
factorySidechainAddress: string
41+
payForSignatureTransport: boolean
42+
cache: {
43+
maxSize: number,
44+
maxAge: number
5145
}
5246
}
5347

54-
export type StreamrClientConfig = O.Compulsory<StreamrClientOptions, 'url' | 'restUrl'>
5548
const { ControlMessage } = ControlLayer
5649
const { StreamMessage } = MessageLayer
5750

5851
export default function ClientConfig(opts: Partial<StreamrClientOptions> = {}) {
59-
const { id = counterId('StreamrClient') } = opts
60-
61-
const defaults = {
62-
debug: Debug(id),
52+
const defaults: StreamrClientOptions = {
6353
// Authentication: identity used by this StreamrClient instance
6454
auth: {}, // can contain member privateKey or (window.)ethereum
6555

@@ -87,24 +77,19 @@ export default function ClientConfig(opts: Partial<StreamrClientOptions> = {}) {
8777
// Ethereum and Data Union related options
8878
// For ethers.js provider params, see https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#provider
8979
mainnet: undefined, // Default to ethers.js default provider settings
90-
sidechain: {
91-
url: undefined, // TODO: add our default public service sidechain node, also find good PoA params below
92-
// timeout:
93-
// pollingInterval:
94-
},
80+
sidechain: undefined, // TODO: add our default public service sidechain node, also find good PoA params below
9581
tokenAddress: '0x0Cf0Ee63788A0849fE5297F3407f701E122cC023',
9682
minimumWithdrawTokenWei: '1000000', // Threshold value set in AMB configs, smallest token amount to pass over the bridge
97-
sidechainTokenAddress: undefined, // TODO // sidechain token
98-
factoryMainnetAddress: undefined, // TODO // Data Union factory that creates a new Data Union
99-
factorySidechainAddress: undefined,
83+
factoryMainnetAddress: 'TODO', // TODO // Data Union factory that creates a new Data Union
84+
factorySidechainAddress: 'TODO',
10085
payForSignatureTransport: true, // someone must pay for transporting the withdraw tx to mainnet, either us or bridge operator
10186
cache: {
10287
maxSize: 10000,
10388
maxAge: 30 * 60 * 1000, // 30 minutes
10489
}
10590
}
10691

107-
const options: StreamrClientConfig = {
92+
const options: StreamrClientOptions = {
10893
...defaults,
10994
...opts,
11095
cache: {

src/Connection.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,8 @@ async function OpenWebSocket(url, opts, ...args) {
6363
})
6464

6565
// attach debug
66-
if (opts && opts.debug) {
67-
socket.debug = opts.debug.extend(socket.id)
68-
socket.debug.color = opts.debug.color // use existing colour
69-
} else {
70-
socket.debug = Debug('StreamrClient::ws').extend(socket.id)
71-
}
66+
socket.debug = opts.debug.extend(socket.id)
67+
socket.debug.color = opts.debug.color // use existing colour
7268
} catch (err) {
7369
reject(err)
7470
}
@@ -292,15 +288,9 @@ export default class Connection extends EventEmitter {
292288
}))
293289
}
294290

295-
constructor(options = {}) {
291+
constructor(options = {}, client) {
296292
super()
297-
const id = counterId(this.constructor.name)
298-
/* istanbul ignore next */
299-
if (options.debug) {
300-
this._debug = options.debug.extend(id)
301-
} else {
302-
this._debug = Debug(`StreamrClient::${id}`)
303-
}
293+
this._debug = client.debug.extend(counterId(this.constructor.name))
304294

305295
this.options = options
306296
this.options.autoConnect = !!this.options.autoConnect

src/StreamrClient.ts

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Debug from 'debug'
44

55
import { counterId, uuid, CacheAsyncFn } from './utils'
66
import { validateOptions } from './stream/utils'
7-
import Config, { StreamrClientOptions, StreamrClientConfig } from './Config'
7+
import Config, { StreamrClientOptions } from './Config'
88
import StreamrEthereum from './Ethereum'
99
import Session from './Session'
1010
import Connection, { ConnectionError } from './Connection'
@@ -14,8 +14,10 @@ import { getUserId } from './user'
1414
import { Todo, MaybeAsync } from './types'
1515
import { StreamEndpoints } from './rest/StreamEndpoints'
1616
import { LoginEndpoints } from './rest/LoginEndpoints'
17-
import { DataUnionEndpoints } from './rest/DataUnionEndpoints'
1817
import { DataUnion, DataUnionDeployOptions } from './dataunion/DataUnion'
18+
import { BigNumber } from '@ethersproject/bignumber'
19+
import { getAddress } from '@ethersproject/address'
20+
import { Contract } from '@ethersproject/contracts'
1921

2022
// TODO get metadata type from streamr-protocol-js project (it doesn't export the type definitions yet)
2123
export type OnMessageCallback = MaybeAsync<(message: any, metadata: any) => void>
@@ -32,8 +34,8 @@ export { StreamrClientOptions }
3234

3335
class StreamrConnection extends Connection {
3436
// TODO define args type when we convert Connection class to TypeScript
35-
constructor(...args: any) {
36-
super(...args)
37+
constructor(options: Todo, client: StreamrClient) {
38+
super(options, client)
3739
this.on('message', this.onConnectionMessage)
3840
}
3941

@@ -137,13 +139,13 @@ function Plugin(targetInstance: any, srcInstance: any) {
137139
}
138140

139141
// these are mixed in via Plugin function above
140-
interface StreamrClient extends StreamEndpoints, LoginEndpoints, DataUnionEndpoints {}
142+
interface StreamrClient extends StreamEndpoints, LoginEndpoints {}
141143

142144
// eslint-disable-next-line no-redeclare
143145
class StreamrClient extends EventEmitter {
144146
id: string
145147
debug: Debug.Debugger
146-
options: StreamrClientConfig
148+
options: StreamrClientOptions
147149
session: Session
148150
connection: StreamrConnection
149151
publisher: Todo
@@ -152,18 +154,13 @@ class StreamrClient extends EventEmitter {
152154
ethereum: StreamrEthereum
153155
streamEndpoints: StreamEndpoints
154156
loginEndpoints: LoginEndpoints
155-
dataUnionEndpoints: DataUnionEndpoints
156157

157158
constructor(options: Partial<StreamrClientOptions> = {}, connection?: StreamrConnection) {
158159
super()
159160
this.id = counterId(`${this.constructor.name}:${uid}`)
160161
this.debug = Debug(this.id)
161162

162-
this.options = Config({
163-
id: this.id,
164-
debug: this.debug,
165-
...options,
166-
})
163+
this.options = Config(options)
167164

168165
this.debug('new StreamrClient %s: %o', this.id, {
169166
version: process.env.version,
@@ -182,7 +179,7 @@ class StreamrClient extends EventEmitter {
182179
this.on('error', this._onError) // attach before creating sub-components incase they fire error events
183180

184181
this.session = new Session(this, this.options.auth)
185-
this.connection = connection || new StreamrConnection(this.options)
182+
this.connection = connection || new StreamrConnection(this.options, this)
186183

187184
this.connection
188185
.on('connected', this.onConnectionConnected)
@@ -195,7 +192,6 @@ class StreamrClient extends EventEmitter {
195192

196193
this.streamEndpoints = Plugin(this, new StreamEndpoints(this))
197194
this.loginEndpoints = Plugin(this, new LoginEndpoints(this))
198-
this.dataUnionEndpoints = Plugin(this, new DataUnionEndpoints(this))
199195
this.cached = new StreamrCached(this)
200196
}
201197

@@ -380,18 +376,42 @@ class StreamrClient extends EventEmitter {
380376
return this.getAddress()
381377
}
382378

379+
/**
380+
* Get token balance in "wei" (10^-18 parts) for given address
381+
*/
382+
async getTokenBalance(address: string): Promise<BigNumber> {
383+
const { tokenAddress } = this.options
384+
if (!tokenAddress) {
385+
throw new Error('StreamrClient has no tokenAddress configuration.')
386+
}
387+
const addr = getAddress(address)
388+
const provider = this.ethereum.getMainnetProvider()
389+
390+
const token = new Contract(tokenAddress, [{
391+
name: 'balanceOf',
392+
inputs: [{ type: 'address' }],
393+
outputs: [{ type: 'uint256' }],
394+
constant: true,
395+
payable: false,
396+
stateMutability: 'view',
397+
type: 'function'
398+
}], provider)
399+
return token.balanceOf(addr)
400+
}
401+
383402
getDataUnion(contractAddress: string) {
384-
return new DataUnion(contractAddress, undefined, this.dataUnionEndpoints)
403+
return DataUnion._fromContractAddress(contractAddress, this) // eslint-disable-line no-underscore-dangle
385404
}
386405

387406
async deployDataUnion(options?: DataUnionDeployOptions) {
388-
const contract = await this.dataUnionEndpoints.deployDataUnionContract(options)
389-
return new DataUnion(contract.address, contract.sidechain.address, this.dataUnionEndpoints)
407+
return DataUnion._deploy(options, this) // eslint-disable-line no-underscore-dangle
390408
}
391409

392410
_getDataUnionFromName({ dataUnionName, deployerAddress }: { dataUnionName: string, deployerAddress: string}) {
393-
const contractAddress = this.dataUnionEndpoints.calculateDataUnionMainnetAddress(dataUnionName, deployerAddress)
394-
return this.getDataUnion(contractAddress)
411+
return DataUnion._fromName({ // eslint-disable-line no-underscore-dangle
412+
dataUnionName,
413+
deployerAddress
414+
}, this)
395415
}
396416

397417
static generateEthereumAccount() {

0 commit comments

Comments
 (0)