feat: add account change notifications
This commit is contained in:
parent
9839c087d7
commit
e7097340f2
|
@ -23,4 +23,4 @@ lib
|
|||
doc
|
||||
|
||||
# VIM swap files
|
||||
*.sw.
|
||||
*.sw*
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
// flow-typed signature: 31b3dc13d06052ea505e8da9ca72c537
|
||||
// flow-typed version: <<STUB>>/rpc-websockets_v4.3.3/flow_v0.84.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'rpc-websockets'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'rpc-websockets' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'rpc-websockets/dist/index.browser-bundle' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/index.browser' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/index' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/client' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/client/websocket.browser' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/client/websocket' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/handler' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/server' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'rpc-websockets/dist/lib/utils' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'rpc-websockets/dist/index.browser-bundle.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/index.browser-bundle'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/index.browser.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/index.browser'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/index.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/index'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/client.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/client'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/client/websocket.browser.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/client/websocket.browser'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/client/websocket.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/client/websocket'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/handler.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/handler'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/server.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/server'>;
|
||||
}
|
||||
declare module 'rpc-websockets/dist/lib/utils.js' {
|
||||
declare module.exports: $Exports<'rpc-websockets/dist/lib/utils'>;
|
||||
}
|
|
@ -41,6 +41,8 @@ declare module '@solana/web3.js' {
|
|||
userdata: Buffer,
|
||||
}
|
||||
|
||||
declare type AccountChangeCallback = (accountInfo: AccountInfo) => void;
|
||||
|
||||
declare export type SignatureStatus = 'Confirmed'
|
||||
| 'AccountInUse'
|
||||
| 'SignatureNotFound'
|
||||
|
@ -58,6 +60,8 @@ declare module '@solana/web3.js' {
|
|||
getFinality(): Promise<number>;
|
||||
requestAirdrop(to: PublicKey, amount: number): Promise<TransactionSignature>;
|
||||
sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature>;
|
||||
onAccountChange(publickey: PublicKey, callback: AccountChangeCallback): Promise<number>;
|
||||
removeAccountListener(id: number): Promise<void>;
|
||||
}
|
||||
|
||||
// === src/system-program.js ===
|
||||
|
|
|
@ -4,6 +4,23 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"101": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/101/-/101-1.6.3.tgz",
|
||||
"integrity": "sha512-4dmQ45yY0Dx24Qxp+zAsNLlMF6tteCyfVzgbulvSyC7tCyd3V8sW76sS0tHq8NpcbXfWTKasfyfzU1Kd86oKzw==",
|
||||
"requires": {
|
||||
"clone": "1.0.4",
|
||||
"deep-eql": "0.1.3",
|
||||
"keypather": "1.10.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.0.0-beta.44",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz",
|
||||
|
@ -917,6 +934,19 @@
|
|||
"minimalistic-assert": "1.0.1"
|
||||
}
|
||||
},
|
||||
"assert-args": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/assert-args/-/assert-args-1.2.1.tgz",
|
||||
"integrity": "sha1-QEEDoUUqMv53iYgR5U5ZCoqTc70=",
|
||||
"requires": {
|
||||
"101": "1.6.3",
|
||||
"compound-subject": "0.0.1",
|
||||
"debug": "2.6.9",
|
||||
"get-prototype-of": "0.0.0",
|
||||
"is-capitalized": "1.0.0",
|
||||
"is-class": "0.0.4"
|
||||
}
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
|
@ -947,8 +977,7 @@
|
|||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
|
@ -2964,6 +2993,11 @@
|
|||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
|
||||
"dev": true
|
||||
},
|
||||
"compound-subject": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/compound-subject/-/compound-subject-0.0.1.tgz",
|
||||
"integrity": "sha1-JxVUaYoVrmCLHfyv0wt7oeqJLEs="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
@ -3277,7 +3311,6 @@
|
|||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
|
@ -3321,6 +3354,14 @@
|
|||
"mimic-response": "1.0.1"
|
||||
}
|
||||
},
|
||||
"deep-eql": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
|
||||
"integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
|
||||
"requires": {
|
||||
"type-detect": "0.1.1"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
|
@ -4631,6 +4672,11 @@
|
|||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
|
||||
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA=="
|
||||
},
|
||||
"evp_bytestokey": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
|
@ -6077,6 +6123,11 @@
|
|||
"integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
|
||||
"dev": true
|
||||
},
|
||||
"get-prototype-of": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-prototype-of/-/get-prototype-of-0.0.0.tgz",
|
||||
"integrity": "sha1-mHcr0QcW0W3rSzIlFsRp78oorEQ="
|
||||
},
|
||||
"get-stdin": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
|
||||
|
@ -6866,6 +6917,11 @@
|
|||
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
|
||||
"dev": true
|
||||
},
|
||||
"is-capitalized": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-capitalized/-/is-capitalized-1.0.0.tgz",
|
||||
"integrity": "sha1-TIRktNkdPk7rRIid0s2PGwrEwTY="
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz",
|
||||
|
@ -6875,6 +6931,11 @@
|
|||
"ci-info": "1.1.3"
|
||||
}
|
||||
},
|
||||
"is-class": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-class/-/is-class-0.0.4.tgz",
|
||||
"integrity": "sha1-4FdFFwW7NOOePjNZjJOpg3KWtzY="
|
||||
},
|
||||
"is-data-descriptor": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
|
||||
|
@ -8085,6 +8146,16 @@
|
|||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "http://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "1.0.0",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8155,6 +8226,14 @@
|
|||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"keypather": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/keypather/-/keypather-1.10.2.tgz",
|
||||
"integrity": "sha1-4ESWMtSz5RbyHMAUznxWRP3c5hQ=",
|
||||
"requires": {
|
||||
"101": "1.6.3"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
|
@ -9046,6 +9125,15 @@
|
|||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"mock-socket": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-8.0.4.tgz",
|
||||
"integrity": "sha512-xuO+Ep0xI60sXfBwIXezHuBnSj3Rafh6TSeTH81k2aFuNf64JW46PeDaViWMXQ/nMInknUzfMriPkoVhmh5Aag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"url-parse": "1.4.3"
|
||||
}
|
||||
},
|
||||
"modify-values": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz",
|
||||
|
@ -9055,8 +9143,7 @@
|
|||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.7",
|
||||
|
@ -13001,6 +13088,12 @@
|
|||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
|
||||
"dev": true
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
|
||||
"integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==",
|
||||
"dev": true
|
||||
},
|
||||
"quick-lru": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz",
|
||||
|
@ -13376,6 +13469,12 @@
|
|||
"resolve-from": "1.0.1"
|
||||
}
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"reselect": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz",
|
||||
|
@ -13690,6 +13789,39 @@
|
|||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"rpc-websockets": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-4.3.3.tgz",
|
||||
"integrity": "sha512-Pq+tubbPkY63e0b1jukKkEQNWa5twpSvdd5izvtAK8OAw4qGGbWzJytqOdtProzpZQhFzynaHLNwfocmx8tmbg==",
|
||||
"requires": {
|
||||
"assert-args": "1.2.1",
|
||||
"babel-runtime": "6.26.0",
|
||||
"circular-json": "0.5.9",
|
||||
"eventemitter3": "3.1.0",
|
||||
"uuid": "3.3.2",
|
||||
"ws": "5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"circular-json": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
|
||||
"integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"requires": {
|
||||
"async-limiter": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rst-selector-parser": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
||||
|
@ -15511,6 +15643,11 @@
|
|||
"prelude-ls": "1.1.2"
|
||||
}
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
|
||||
"integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI="
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
|
@ -15758,6 +15895,16 @@
|
|||
"integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=",
|
||||
"dev": true
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
|
||||
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "2.1.0",
|
||||
"requires-port": "1.0.0"
|
||||
}
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
|
||||
|
@ -16041,13 +16188,11 @@
|
|||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||
"dev": true,
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.0.tgz",
|
||||
"integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==",
|
||||
"requires": {
|
||||
"async-limiter": "1.0.0",
|
||||
"safe-buffer": "5.1.1"
|
||||
"async-limiter": "1.0.0"
|
||||
}
|
||||
},
|
||||
"xml-name-validator": {
|
||||
|
|
|
@ -59,8 +59,10 @@
|
|||
"jayson": "^2.0.6",
|
||||
"mz": "^2.7.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"rpc-websockets": "^4.3.3",
|
||||
"superstruct": "^0.6.0",
|
||||
"tweetnacl": "^1.0.0"
|
||||
"tweetnacl": "^1.0.0",
|
||||
"ws": "^6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "6.26.3",
|
||||
|
|
|
@ -70,18 +70,31 @@ function generateConfig(configType) {
|
|||
// maintained.
|
||||
config.external = [
|
||||
'assert',
|
||||
'babel-runtime/core-js/get-iterator',
|
||||
'babel-runtime/core-js/json/stringify',
|
||||
'babel-runtime/core-js/object/assign',
|
||||
'babel-runtime/core-js/object/get-prototype-of',
|
||||
'babel-runtime/core-js/object/keys',
|
||||
'babel-runtime/core-js/promise',
|
||||
'babel-runtime/helpers/asyncToGenerator',
|
||||
'babel-runtime/helpers/classCallCheck',
|
||||
'babel-runtime/helpers/createClass',
|
||||
'babel-runtime/helpers/get',
|
||||
'babel-runtime/helpers/inherits',
|
||||
'babel-runtime/helpers/possibleConstructorReturn',
|
||||
'babel-runtime/helpers/toConsumableArray',
|
||||
'babel-runtime/helpers/typeof',
|
||||
'babel-runtime/regenerator',
|
||||
'bn.js',
|
||||
'bs58',
|
||||
'buffer-layout',
|
||||
'elfy',
|
||||
'jayson/lib/client/browser',
|
||||
'node-fetch',
|
||||
'rpc-websockets',
|
||||
'superstruct',
|
||||
'tweetnacl',
|
||||
'url',
|
||||
];
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
// @flow
|
||||
|
||||
import assert from 'assert';
|
||||
import {
|
||||
parse as urlParse,
|
||||
format as urlFormat,
|
||||
} from 'url';
|
||||
import fetch from 'node-fetch';
|
||||
import jayson from 'jayson/lib/client/browser';
|
||||
import {struct} from 'superstruct';
|
||||
import {Client as RpcWebSocketClient} from 'rpc-websockets';
|
||||
|
||||
import {Transaction} from './transaction';
|
||||
import {PublicKey} from './publickey';
|
||||
|
@ -11,6 +16,7 @@ import {sleep} from './util/sleep';
|
|||
import type {Account} from './account';
|
||||
import type {TransactionSignature, TransactionId} from './transaction';
|
||||
|
||||
|
||||
type RpcRequest = (methodName: string, args: Array<any>) => any;
|
||||
|
||||
function createRpcRequest(url): RpcRequest {
|
||||
|
@ -79,11 +85,10 @@ function jsonRpcResult(resultDescription: any) {
|
|||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expected JSON RPC response for the "getAccountInfo" message
|
||||
* @private
|
||||
*/
|
||||
const GetAccountInfoRpcResult = jsonRpcResult({
|
||||
const AccountInfoResult = struct({
|
||||
executable: 'boolean',
|
||||
loader_program_id: 'array',
|
||||
program_id: 'array',
|
||||
|
@ -91,6 +96,18 @@ const GetAccountInfoRpcResult = jsonRpcResult({
|
|||
userdata: 'array',
|
||||
});
|
||||
|
||||
/**
|
||||
* Expected JSON RPC response for the "getAccountInfo" message
|
||||
*/
|
||||
const GetAccountInfoRpcResult = jsonRpcResult(AccountInfoResult);
|
||||
|
||||
/***
|
||||
* Expected JSON RPC response for the "accountNotification" message
|
||||
*/
|
||||
const AccountNotificationResult = struct({
|
||||
subscription: 'number',
|
||||
result: AccountInfoResult,
|
||||
});
|
||||
|
||||
/**
|
||||
* Expected JSON RPC response for the "confirmTransaction" message
|
||||
|
@ -148,6 +165,20 @@ type AccountInfo = {
|
|||
userdata: Buffer,
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for account change notifications
|
||||
*/
|
||||
export type AccountChangeCallback = (accountInfo: AccountInfo) => void;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
type AccountSubscriptionInfo = {
|
||||
publicKey: string; // PublicKey of the account as a base 58 string
|
||||
callback: AccountChangeCallback,
|
||||
subscriptionId: null | number; // null when there's no current server subscription id
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible signature status values
|
||||
*
|
||||
|
@ -164,6 +195,8 @@ export type SignatureStatus = 'Confirmed'
|
|||
*/
|
||||
export class Connection {
|
||||
_rpcRequest: RpcRequest;
|
||||
_rpcWebSocket: RpcWebSocketClient;
|
||||
_rpcWebSocketConnected: boolean = false;
|
||||
|
||||
_lastIdInfo: {
|
||||
lastId: TransactionId | null,
|
||||
|
@ -171,6 +204,8 @@ export class Connection {
|
|||
transactionSignatures: Array<string>,
|
||||
};
|
||||
_disableLastIdCaching: boolean = false
|
||||
_accountChangeSubscriptions: {[number]: AccountSubscriptionInfo} = {};
|
||||
_accountChangeSubscriptionCounter: number = 0;
|
||||
|
||||
/**
|
||||
* Establish a JSON RPC connection
|
||||
|
@ -178,15 +213,29 @@ export class Connection {
|
|||
* @param endpoint URL to the fullnode JSON RPC endpoint
|
||||
*/
|
||||
constructor(endpoint: string) {
|
||||
if (typeof endpoint !== 'string') {
|
||||
throw new Error('Connection endpoint not specified');
|
||||
}
|
||||
this._rpcRequest = createRpcRequest(endpoint);
|
||||
let url = urlParse(endpoint);
|
||||
|
||||
this._rpcRequest = createRpcRequest(url.href);
|
||||
this._lastIdInfo = {
|
||||
lastId: null,
|
||||
seconds: -1,
|
||||
transactionSignatures: [],
|
||||
};
|
||||
|
||||
url.protocol = 'ws';
|
||||
url.host = '';
|
||||
url.port = String(Number(url.port) + 1);
|
||||
this._rpcWebSocket = new RpcWebSocketClient(
|
||||
urlFormat(url),
|
||||
{
|
||||
autoconnect: false,
|
||||
max_reconnects: Infinity,
|
||||
}
|
||||
);
|
||||
this._rpcWebSocket.on('open', this._wsOnOpen.bind(this));
|
||||
this._rpcWebSocket.on('error', this._wsOnError.bind(this));
|
||||
this._rpcWebSocket.on('close', this._wsOnClose.bind(this));
|
||||
this._rpcWebSocket.on('accountNotification', this._wsOnAccountNotification.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,4 +420,133 @@ export class Connection {
|
|||
assert(res.result);
|
||||
return res.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_wsOnOpen() {
|
||||
this._rpcWebSocketConnected = true;
|
||||
this._updateSubscriptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_wsOnError(err: Error) {
|
||||
console.log('ws error:', err.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_wsOnClose(code: number, message: string) {
|
||||
// 1000 means _rpcWebSocket.close() was called explicitly
|
||||
if (code !== 1000) {
|
||||
console.log('ws close:', code, message);
|
||||
}
|
||||
this._rpcWebSocketConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_wsOnAccountNotification(notification: Object) {
|
||||
const res = AccountNotificationResult(notification);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
}
|
||||
|
||||
const keys = Object.keys(this._accountChangeSubscriptions).map(Number);
|
||||
for (let id of keys) {
|
||||
const sub = this._accountChangeSubscriptions[id];
|
||||
if (sub.subscriptionId === res.subscription) {
|
||||
const {result} = res;
|
||||
assert(typeof result !== 'undefined');
|
||||
|
||||
sub.callback({
|
||||
executable: result.executable,
|
||||
tokens: result.tokens,
|
||||
programId: new PublicKey(result.program_id),
|
||||
loaderProgramId: new PublicKey(result.loader_program_id),
|
||||
userdata: Buffer.from(result.userdata),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async _updateSubscriptions() {
|
||||
const keys = Object.keys(this._accountChangeSubscriptions).map(Number);
|
||||
if (keys.length === 0) {
|
||||
this._rpcWebSocket.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._rpcWebSocketConnected) {
|
||||
for (let id of keys) {
|
||||
this._accountChangeSubscriptions[id].subscriptionId = null;
|
||||
}
|
||||
this._rpcWebSocket.connect();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let id of keys) {
|
||||
const {subscriptionId, publicKey} = this._accountChangeSubscriptions[id];
|
||||
if (subscriptionId === null) {
|
||||
try {
|
||||
this._accountChangeSubscriptions[id].subscriptionId =
|
||||
await this._rpcWebSocket.call(
|
||||
'accountSubscribe',
|
||||
[publicKey]
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`accountSubscribe error for ${publicKey}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked whenever the specified account changes
|
||||
*
|
||||
* @param publickey Public key of the account to monitor
|
||||
* @param callback Function to invoke whenever the account is changed
|
||||
* @return subscription id
|
||||
*/
|
||||
onAccountChange(publicKey: PublicKey, callback: AccountChangeCallback): number {
|
||||
const id = ++this._accountChangeSubscriptionCounter;
|
||||
this._accountChangeSubscriptions[id] = {
|
||||
publicKey: publicKey.toBase58(),
|
||||
callback,
|
||||
subscriptionId: null
|
||||
};
|
||||
this._updateSubscriptions();
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister an account notification callback
|
||||
*
|
||||
* @param id subscription id to deregister
|
||||
*/
|
||||
async removeAccountChangeListener(id: number): Promise<void> {
|
||||
if (this._accountChangeSubscriptions[id]) {
|
||||
const {subscriptionId} = this._accountChangeSubscriptions[id];
|
||||
delete this._accountChangeSubscriptions[id];
|
||||
if (subscriptionId !== null) {
|
||||
try {
|
||||
await this._rpcWebSocket.call('accountUnsubscribe', [subscriptionId]);
|
||||
} catch (err) {
|
||||
console.log('accountUnsubscribe error:', err.message);
|
||||
}
|
||||
}
|
||||
this._updateSubscriptions();
|
||||
} else {
|
||||
throw new Error(`Unknown account change id: ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,12 +22,17 @@ export const mockRpc: Array<[string, RpcRequest, RpcResponse]> = [];
|
|||
// identified by `url` instead of using the mock
|
||||
export const mockRpcEnabled = !process.env.DOITLIVE;
|
||||
|
||||
let mockNotice = true;
|
||||
|
||||
// Suppress lint: 'JestMockFn' is not defined
|
||||
// eslint-disable-next-line no-undef
|
||||
const mock: JestMockFn<any, any> = jest.fn(
|
||||
(fetchUrl, fetchOptions) => {
|
||||
if (!mockRpcEnabled) {
|
||||
console.log(`Note: node-fetch mock is disabled, testing live against ${fetchUrl}`);
|
||||
if (mockNotice) {
|
||||
console.log(`Note: node-fetch mock is disabled, testing live against ${fetchUrl}`);
|
||||
mockNotice = false;
|
||||
}
|
||||
return fetch(fetchUrl, fetchOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import {Client as RpcWebSocketClient} from 'rpc-websockets';
|
||||
|
||||
// Define DOITLIVE in the environment to test against the real full node
|
||||
// identified by `url` instead of using the mock
|
||||
export const mockRpcEnabled = !process.env.DOITLIVE;
|
||||
|
||||
let mockNotice = true;
|
||||
|
||||
export class Client {
|
||||
client: RpcWebSocketClient;
|
||||
|
||||
constructor(url, options) {
|
||||
//console.log('MockClient', url, options);
|
||||
if (!mockRpcEnabled) {
|
||||
if (mockNotice) {
|
||||
console.log('Note: rpc-websockets mock is disabled, testing live against', url);
|
||||
mockNotice = false;
|
||||
}
|
||||
this.client = new RpcWebSocketClient(url, options);
|
||||
}
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (!mockRpcEnabled) {
|
||||
return this.client.connect();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!mockRpcEnabled) {
|
||||
return this.client.close();
|
||||
}
|
||||
}
|
||||
|
||||
on(event: string, callback: Function) {
|
||||
if (!mockRpcEnabled) {
|
||||
return this.client.on(event, callback);
|
||||
}
|
||||
//console.log('on', event);
|
||||
}
|
||||
|
||||
async call(method: string, params: Object): Promise<Object> {
|
||||
if (!mockRpcEnabled) {
|
||||
return await this.client.call(method, params);
|
||||
}
|
||||
throw new Error('call unsupported');
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
// @flow
|
||||
|
||||
import {
|
||||
Account,
|
||||
Connection,
|
||||
BpfLoader,
|
||||
Loader,
|
||||
SystemProgram,
|
||||
sendAndConfirmTransaction,
|
||||
} from '../src';
|
||||
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
||||
import {mockGetLastId} from './mockrpc/getlastid';
|
||||
|
@ -410,3 +412,46 @@ test('multi-instruction transaction', async () => {
|
|||
expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
|
||||
});
|
||||
|
||||
|
||||
test('account change notification', async () => {
|
||||
if (mockRpcEnabled) {
|
||||
console.log('non-live test skipped');
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = new Connection(url);
|
||||
const owner = new Account();
|
||||
const programAccount = new Account();
|
||||
|
||||
const mockCallback = jest.fn();
|
||||
|
||||
const subscriptionId = connection.onAccountChange(programAccount.publicKey, mockCallback);
|
||||
|
||||
await connection.requestAirdrop(owner.publicKey, 42);
|
||||
const transaction = SystemProgram.createAccount(
|
||||
owner.publicKey,
|
||||
programAccount.publicKey,
|
||||
42,
|
||||
3,
|
||||
BpfLoader.programId,
|
||||
);
|
||||
await sendAndConfirmTransaction(connection, owner, transaction);
|
||||
|
||||
const loader = new Loader(connection, BpfLoader.programId);
|
||||
await loader.load(programAccount, [1, 2, 3]);
|
||||
|
||||
await connection.removeAccountChangeListener(subscriptionId);
|
||||
|
||||
// mockCallback should be called twice
|
||||
expect(mockCallback.mock.calls).toHaveLength(2);
|
||||
|
||||
// First mockCallback call is due to SystemProgram.createAccount()
|
||||
expect(mockCallback.mock.calls[0][0].tokens).toBe(42);
|
||||
expect(mockCallback.mock.calls[0][0].executable).toBe(false);
|
||||
expect(mockCallback.mock.calls[0][0].userdata).toEqual(Buffer.from([0, 0, 0]));
|
||||
expect(mockCallback.mock.calls[0][0].programId).toEqual(BpfLoader.programId);
|
||||
|
||||
// Second mockCallback call is due to loader.load()
|
||||
expect(mockCallback.mock.calls[1][0].userdata).toEqual(Buffer.from([1, 2, 3]));
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
/**
|
||||
* The connection url to use when running unit tests against a live network
|
||||
*/
|
||||
export const url = 'http://localhost:8899';
|
||||
//export const url = 'http://testnet.solana.com:8899';
|
||||
//export const url = 'http://master.testnet.solana.com:8899';
|
||||
export const url = 'http://localhost:8899/';
|
||||
//export const url = 'http://testnet.solana.com:8899/';
|
||||
|
||||
|
|
Loading…
Reference in New Issue