@stellar-broker/client 0.6.0 → 0.6.1
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/README.md +2 -2
- package/lib/stellarbroker.js.map +1 -1
- package/package.json +1 -1
- package/src/client.js +14 -8
- package/src/index.d.ts +7 -6
- package/src/tx-processor.js +7 -3
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ client.connect() //only once
|
|
|
73
73
|
//request a price quote
|
|
74
74
|
client.quote({
|
|
75
75
|
sellingAsset: 'xlm',
|
|
76
|
-
buyingAsset: '
|
|
76
|
+
buyingAsset: 'USDC-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
|
|
77
77
|
sellingAmount: '1000', //1000.0000000 XLM
|
|
78
78
|
slippageTolerance: 0.02 //2%
|
|
79
79
|
})
|
|
@@ -108,7 +108,7 @@ import {estimateSwap} from '@stellar-broker/client'
|
|
|
108
108
|
|
|
109
109
|
estimateSwap({
|
|
110
110
|
sellingAsset: 'xlm',
|
|
111
|
-
buyingAsset: '
|
|
111
|
+
buyingAsset: 'USDC-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
|
|
112
112
|
sellingAmount: '1000',
|
|
113
113
|
slippageTolerance: 0.02 //2%
|
|
114
114
|
})
|
package/lib/stellarbroker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stellarbroker.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,EAAQG,QAAQ,yBACR,mBAAXC,QAAyBA,OAAOC,IAC9CD,OAAO,CAAC,wBAAyBJ,GACP,iBAAZC,QACdA,QAAuB,cAAID,EAAQG,QAAQ,yBAE3CJ,EAAoB,cAAIC,EAAQD,EAAK,wBACtC,CATD,CASGO,MAAOC,G,kCCTVL,EAAOD,QAAUM,C,GCCbC,EAA2B,CAAC,E,YAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaV,QAGrB,IAAIC,EAASM,EAAyBE,GAAY,CAGjDT,QAAS,CAAC,GAOX,OAHAY,EAAoBH,GAAUR,EAAQA,EAAOD,QAASQ,GAG/CP,EAAOD,OACf,C,MCtBwCa,MCmUZ,IAAIC,IAAI,CAAC,QAAS,SAAU,WAAY,WAAY,U","sources":["webpack://stellarbroker/webpack/universalModuleDefinition","webpack://stellarbroker/external umd \"@stellar/stellar-sdk\"","webpack://stellarbroker/webpack/bootstrap","webpack://stellarbroker/./src/errors.js","webpack://stellarbroker/./src/client.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"@stellar/stellar-sdk\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"@stellar/stellar-sdk\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"stellarbroker\"] = factory(require(\"@stellar/stellar-sdk\"));\n\telse\n\t\troot[\"stellarbroker\"] = factory(root[\"@stellar/stellar-sdk\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__346__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__346__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","export class StellarBrokerError extends Error {\n constructor(code, message) {\n super(message)\n this.code = code\n }\n\n /**\n * Numeric error code\n * @type {number}\n * @readonly\n */\n code = 0\n}\n\nconst errors = {\n invalidInitParam(param) {\n return new StellarBrokerError(1, `Invalid parameter value: \"${param}\"`)\n },\n invalidAuthorizationParam() {\n return new StellarBrokerError(2, 'Invalid authorization secret key or callback provided')\n },\n notConnected() {\n return new StellarBrokerError(3, 'Client not connected to the server')\n },\n quoteNotSet() {\n return new StellarBrokerError(11, 'Price quote not available')\n },\n quoteExpired() {\n return new StellarBrokerError(12, 'Price quote expired')\n },\n quoteError(message) {\n return new StellarBrokerError(13, 'Price quotation error: ' + message)\n },\n invalidQuoteParam(invalidParamName = 'asset', details) {\n return new StellarBrokerError(14, `Invalid quote request parameter: \"${invalidParamName}\". ${details}`)\n },\n tradeInProgress() {\n return new StellarBrokerError(20, 'Cannot change quote while trade is in progress')\n },\n invalidSwapTx() {\n return new StellarBrokerError(21, 'Invalid swap transaction received from the server')\n },\n failedToSignTx() {\n return new StellarBrokerError(22, 'Failed to sign received transaction')\n },\n unsupportedEventType(type) {\n return new StellarBrokerError(31, 'Unknown event type: ' + type)\n },\n\n\n serverError(message) {\n return new StellarBrokerError(101, message)\n }\n}\n\nexport default errors","import {Networks, StrKey} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\nimport {buildEvent} from './events.js'\nimport {processTxRequest} from './tx-processor.js'\nimport {validateQuoteRequest} from './quote-request.js'\nimport {QuoteResult} from './quote-result.js'\nimport {AuthorizationWrapper} from './authorization.js'\n\n/**\n * Client for StellarBroker service\n */\nexport class StellarBrokerClient {\n /**\n * @param {ClientInitializationParams} params\n */\n constructor(params) {\n this.partnerKey = params.partnerKey\n this.emitter = new EventTarget()\n this.network = Networks.PUBLIC\n if (!params.account || !StrKey.isValidEd25519PublicKey(params.account))\n throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!params.account ? 'missing' : params.account))\n this.trader = params.account\n this.authorization = new AuthorizationWrapper(params.authorization)\n }\n\n /**\n * @type {string}\n * @private\n */\n uid\n /**\n * @type {ClientSessionStatus}\n * @readonly\n */\n status = 'disconnected'\n /**\n * @type {WebSocket}\n * @private\n */\n socket\n /**\n * @type {EventTarget}\n * @private\n */\n emitter\n /**\n * @type {string}\n * @private\n */\n origin = 'https://api.stellar.broker'\n /**\n * Stellar network passphrase\n * @type {string}\n * @readonly\n */\n network = ''\n /**\n * Last quote request submitted by the client\n * @type {QuoteParams}\n * @readonly\n */\n quoteRequest\n /**\n * Last quote received from the sever\n * @type {QuoteResult}\n * @readonly\n */\n lastQuote\n /**\n * Current quote accepted for the trading\n * @private\n */\n tradeQuote\n /**\n * API key of the partner\n * @type {string}\n * @readonly\n */\n partnerKey\n /**\n * Account secret key or authorization callback\n * @type {AuthorizationWrapper}\n * @private\n */\n authorization\n /**\n * Trader account public key\n * @type {string}\n * @readonly\n */\n trader\n\n /**\n * Connect to the StellarBroker server\n * @return {Promise<StellarBrokerClient>}\n */\n connect() {\n if (this.socket?.readyState === WebSocket.OPEN)\n return Promise.resolve(this) //already opened\n this.socket = new WebSocket(this.origin + '/ws?partner=' + encodeURIComponent(this.partnerKey))\n this.socket.onmessage = this.processMessage.bind(this)\n this.socket.onclose = () => {\n if (this.status !== 'disconnected') {\n this.status = 'disconnected'\n this.notifyError(errors.notConnected())\n }\n console.log('Connection closed')\n }\n\n this.socket.onerror = e => console.error(e)\n\n return new Promise((confirm, reject) => { //TODO: use 'once'\n const expirationTimeout = setTimeout(() => reject(this), 2000) // 2s timeout\n this.onSocketOpen = () => {\n this.status = 'ready'\n this.heartbeat()\n this.onSocketOpen = undefined\n clearTimeout(expirationTimeout)\n confirm(this)\n }\n })\n }\n\n /**\n * Process incoming message\n * @param message\n * @private\n */\n processMessage(message) {\n const raw = JSON.parse(message.data)\n switch (raw.type) {\n case 'connected':\n this.uid = raw.uid\n if (this.onSocketOpen) {\n this.onSocketOpen()\n }\n if (this.status === 'disconnected') {\n this.status = 'ready'\n }\n break\n case 'quote':\n this.lastQuote = new QuoteResult(raw.quote)\n //send event to the client app\n this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))\n break\n case 'paused':\n //quotation paused due to inactivity\n this.emitter.dispatchEvent(buildEvent('paused', {}))\n break\n case 'tx':\n if (this.status !== 'trade') {\n console.log('Received tx in non-trading state', this.status, raw)\n return //skip unless trading is in progress\n }\n processTxRequest(this, raw)\n .then(xdr => {\n this.send({\n type: 'tx',\n hash: raw.hash,\n xdr\n })\n })\n .catch(e => this.notifyError(e))\n break\n case 'stop':\n this.emitter.dispatchEvent(buildEvent('finished', {\n status: raw.status,\n sold: raw.sold,\n bought: raw.bought\n }, 'result'))\n this.status = 'ready'\n this.tradeQuote = undefined\n break\n case 'progress':\n this.emitter.dispatchEvent(buildEvent('progress', {\n sold: raw.sold,\n bought: raw.bought\n }, 'status'))\n break\n case 'ping':\n if (raw.uid === this.uid) {\n this.heartbeat()\n this.send({type: 'pong', uid: this.uid})\n }\n break\n case 'error':\n this.stop()\n this.notifyError(raw.error)\n break\n default:\n console.log('Unknown message type: ' + raw.type)\n break\n }\n }\n\n /**\n * Request swap quote\n * @param {QuoteParams} params - Quote parameters\n */\n quote(params) {\n if (this.status === 'trade')\n throw errors.tradeInProgress()\n this.quoteRequest = validateQuoteRequest(params)\n this.status = 'quote'\n this.connect()\n .then(() => {\n this.status = 'quote'\n this.send({\n type: 'quote',\n ...this.quoteRequest\n })\n })\n }\n\n /**\n * Confirm current quote and start trading\n */\n confirmQuote() {\n if (this.status === 'disconnected')\n throw errors.notConnected()\n if (this.status === 'trade')\n throw errors.tradeInProgress()\n if (this.status !== 'quote' || !this.lastQuote)\n throw errors.quoteNotSet()\n if ((new Date() - this.lastQuote.ts) > 10_000) //do not allow stale quotes quoted more than 10s ago\n throw errors.quoteExpired()\n if (this.lastQuote.status !== 'success')\n throw errors.quoteError(this.lastQuote.error || 'quote not available')\n\n this.tradeQuote = this.lastQuote\n this.send({\n type: 'trade',\n account: this.trader\n })\n this.status = 'trade'\n }\n\n /**\n * Stop quotation/trading\n */\n stop() {\n if (this.status !== 'trade' && this.status !== 'quote')\n return\n this.send({type: 'stop'})\n this.tradeQuote = undefined\n this.status = 'ready'\n }\n\n /**\n * @param {{}} data\n * @private\n */\n send(data) {\n this.socket.send(JSON.stringify(data))\n }\n\n /**\n * @private\n */\n heartbeat() {\n clearTimeout(this.pingHandler)\n this.pingHandler = setTimeout(() => {\n console.warn('Lost connection with the server')\n this.socket.close()\n }, 7_000) // 7 seconds heartbeat timeout\n }\n\n /**\n * Add event listener\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n on(type, callback) {\n validateEventType(type)\n this.emitter.addEventListener(type, callback)\n }\n\n /**\n * Add event listener that will be executed once\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n once(type, callback) {\n validateEventType(type)\n this.emitter.addEventListener(type, callback, {once: true})\n }\n\n /**\n * Remove event listener\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n off(type, callback) {\n validateEventType(type)\n this.emitter.removeEventListener(type, callback)\n }\n\n /**\n * Close underlying connection and finalize the client\n */\n close() {\n try {\n //TODO: remove all attached event listeners\n this.status = 'disconnected'\n this.socket.close()\n } catch (e) {\n }\n }\n\n /**\n * @param {Error} e\n * @private\n */\n notifyError(e) {\n console.error(e)\n try {\n this.emitter.dispatchEvent(buildEvent('error', e instanceof Error ? e.message : e))\n } catch (e) {\n console.error(e)\n }\n }\n}\n\nconst stellarBrokerEvents = new Set(['quote', 'paused', 'progress', 'finished', 'error'])\n\nfunction validateEventType(type) {\n if (!stellarBrokerEvents.has(type))\n throw errors.unsupportedEventType(type)\n}\n\n/**\n * @typedef {object} ClientInitializationParams\n * @property {string} account - Trader account address\n * @property {ClientAuthorizationParams} authorization - Authorization method, either account secret key or an authorization callback\n * @property {string} [partnerKey] - Partner key\n */\n\n/**\n * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientSessionStatus - Current client session status\n */\n\n/**\n * @typedef {'quote'|'paused'|'progress'|'finished'|'error'} StellarBrokerClientEvent - Event type generated by the client\n */\n"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__346__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","Error","Set"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"stellarbroker.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,EAAQG,QAAQ,yBACR,mBAAXC,QAAyBA,OAAOC,IAC9CD,OAAO,CAAC,wBAAyBJ,GACP,iBAAZC,QACdA,QAAuB,cAAID,EAAQG,QAAQ,yBAE3CJ,EAAoB,cAAIC,EAAQD,EAAK,wBACtC,CATD,CASGO,MAAOC,G,kCCTVL,EAAOD,QAAUM,C,GCCbC,EAA2B,CAAC,E,YAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaV,QAGrB,IAAIC,EAASM,EAAyBE,GAAY,CAGjDT,QAAS,CAAC,GAOX,OAHAY,EAAoBH,GAAUR,EAAQA,EAAOD,QAASQ,GAG/CP,EAAOD,OACf,C,MCtBwCa,MCyUZ,IAAIC,IAAI,CAAC,QAAS,SAAU,WAAY,WAAY,U","sources":["webpack://stellarbroker/webpack/universalModuleDefinition","webpack://stellarbroker/external umd \"@stellar/stellar-sdk\"","webpack://stellarbroker/webpack/bootstrap","webpack://stellarbroker/./src/errors.js","webpack://stellarbroker/./src/client.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"@stellar/stellar-sdk\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"@stellar/stellar-sdk\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"stellarbroker\"] = factory(require(\"@stellar/stellar-sdk\"));\n\telse\n\t\troot[\"stellarbroker\"] = factory(root[\"@stellar/stellar-sdk\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__346__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__346__;","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","export class StellarBrokerError extends Error {\n constructor(code, message) {\n super(message)\n this.code = code\n }\n\n /**\n * Numeric error code\n * @type {number}\n * @readonly\n */\n code = 0\n}\n\nconst errors = {\n invalidInitParam(param) {\n return new StellarBrokerError(1, `Invalid parameter value: \"${param}\"`)\n },\n invalidAuthorizationParam() {\n return new StellarBrokerError(2, 'Invalid authorization secret key or callback provided')\n },\n notConnected() {\n return new StellarBrokerError(3, 'Client not connected to the server')\n },\n quoteNotSet() {\n return new StellarBrokerError(11, 'Price quote not available')\n },\n quoteExpired() {\n return new StellarBrokerError(12, 'Price quote expired')\n },\n quoteError(message) {\n return new StellarBrokerError(13, 'Price quotation error: ' + message)\n },\n invalidQuoteParam(invalidParamName = 'asset', details) {\n return new StellarBrokerError(14, `Invalid quote request parameter: \"${invalidParamName}\". ${details}`)\n },\n tradeInProgress() {\n return new StellarBrokerError(20, 'Cannot change quote while trade is in progress')\n },\n invalidSwapTx() {\n return new StellarBrokerError(21, 'Invalid swap transaction received from the server')\n },\n failedToSignTx() {\n return new StellarBrokerError(22, 'Failed to sign received transaction')\n },\n unsupportedEventType(type) {\n return new StellarBrokerError(31, 'Unknown event type: ' + type)\n },\n\n\n serverError(message) {\n return new StellarBrokerError(101, message)\n }\n}\n\nexport default errors","import {Networks, StrKey} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\nimport {buildEvent} from './events.js'\nimport {processTxRequest} from './tx-processor.js'\nimport {validateQuoteRequest} from './quote-request.js'\nimport {QuoteResult} from './quote-result.js'\nimport {AuthorizationWrapper} from './authorization.js'\n\n/**\n * Client for StellarBroker service\n */\nexport class StellarBrokerClient {\n /**\n * @param {ClientInitializationParams} params\n */\n constructor(params) {\n this.partnerKey = params.partnerKey\n this.emitter = new EventTarget()\n this.network = Networks.PUBLIC\n this.trader = params.account\n this.authorization = new AuthorizationWrapper(params.authorization)\n }\n\n /**\n * @type {string}\n * @private\n */\n uid\n /**\n * @type {ClientSessionStatus}\n * @readonly\n */\n status = 'disconnected'\n /**\n * @type {WebSocket}\n * @private\n */\n socket\n /**\n * @type {EventTarget}\n * @private\n */\n emitter\n /**\n * @type {string}\n * @private\n */\n origin = 'https://api.stellar.broker'\n /**\n * Stellar network passphrase\n * @type {string}\n * @readonly\n */\n network = ''\n /**\n * Last quote request submitted by the client\n * @type {QuoteParams}\n * @readonly\n */\n quoteRequest\n /**\n * Last quote received from the sever\n * @type {QuoteResult}\n * @readonly\n */\n lastQuote\n /**\n * Current quote accepted for the trading\n * @private\n */\n tradeQuote\n /**\n * API key of the partner\n * @type {string}\n * @readonly\n */\n partnerKey\n /**\n * Account secret key or authorization callback\n * @type {AuthorizationWrapper}\n * @private\n */\n authorization\n /**\n * Trader account public key\n * @type {string}\n * @readonly\n */\n trader\n\n /**\n * Connect to the StellarBroker server\n * @return {Promise<StellarBrokerClient>}\n */\n connect() {\n if (this.socket?.readyState === WebSocket.OPEN)\n return Promise.resolve(this) //already opened\n this.socket = new WebSocket(this.origin + '/ws?partner=' + encodeURIComponent(this.partnerKey))\n this.socket.onmessage = this.processMessage.bind(this)\n this.socket.onclose = () => {\n if (this.status !== 'disconnected') {\n this.status = 'disconnected'\n this.notifyError(errors.notConnected())\n }\n console.log('Connection closed')\n }\n\n this.socket.onerror = e => console.error(e)\n\n return new Promise((confirm, reject) => { //TODO: use 'once'\n const expirationTimeout = setTimeout(() => reject(this), 2000) // 2s timeout\n this.onSocketOpen = () => {\n this.status = 'ready'\n this.heartbeat()\n this.onSocketOpen = undefined\n clearTimeout(expirationTimeout)\n confirm(this)\n }\n })\n }\n\n /**\n * Process incoming message\n * @param message\n * @private\n */\n processMessage(message) {\n const raw = JSON.parse(message.data)\n switch (raw.type) {\n case 'connected':\n this.uid = raw.uid\n if (this.onSocketOpen) {\n this.onSocketOpen()\n }\n if (this.status === 'disconnected') {\n this.status = 'ready'\n }\n break\n case 'quote':\n this.lastQuote = new QuoteResult(raw.quote)\n //send event to the client app\n this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))\n break\n case 'paused':\n //quotation paused due to inactivity\n this.emitter.dispatchEvent(buildEvent('paused', {}))\n break\n case 'tx':\n if (this.status !== 'trade') {\n console.log('Received tx in a non-trading state', this.status, raw)\n return //skip unless trading is in progress\n }\n processTxRequest(this, raw)\n .then(xdr => {\n this.send({\n type: 'tx',\n hash: raw.hash,\n xdr\n })\n })\n .catch(e => this.notifyError(e))\n break\n case 'stop':\n this.emitter.dispatchEvent(buildEvent('finished', {\n status: raw.status,\n sold: raw.sold,\n bought: raw.bought\n }, 'result'))\n this.status = 'ready'\n this.tradeQuote = undefined\n break\n case 'progress':\n this.emitter.dispatchEvent(buildEvent('progress', {\n sold: raw.sold,\n bought: raw.bought\n }, 'status'))\n break\n case 'ping':\n if (raw.uid === this.uid) {\n this.heartbeat()\n this.send({type: 'pong', uid: this.uid})\n }\n break\n case 'error':\n this.stop()\n this.notifyError(raw.error)\n break\n default:\n console.log('Unknown message type: ' + raw.type)\n break\n }\n }\n\n /**\n * Request swap quote\n * @param {QuoteParams} params - Quote parameters\n */\n quote(params) {\n if (this.status === 'trade')\n throw errors.tradeInProgress()\n this.quoteRequest = validateQuoteRequest(params)\n\n this.status = 'quote'\n this.connect()\n .then(() => {\n this.status = 'quote'\n this.send({\n type: 'quote',\n ...this.quoteRequest\n })\n })\n }\n\n /**\n * Confirm current quote and start trading\n * @param {string} [account] - Trader account address (overrides value provided in the constructor)\n */\n confirmQuote(account) {\n if (this.status === 'disconnected')\n throw errors.notConnected()\n if (this.status === 'trade')\n throw errors.tradeInProgress()\n if (this.status !== 'quote' || !this.lastQuote)\n throw errors.quoteNotSet()\n if ((new Date() - this.lastQuote.ts) > 10_000) //do not allow stale quotes quoted more than 10s ago\n throw errors.quoteExpired()\n if (this.lastQuote.status !== 'success')\n throw errors.quoteError(this.lastQuote.error || 'quote not available')\n if (account) {\n this.trader = account\n }\n //ensure that a trader account address provided\n const {trader} = this\n if (!trader || !StrKey.isValidEd25519PublicKey(trader))\n throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!trader ? 'missing' : trader))\n this.tradeQuote = this.lastQuote\n this.send({\n type: 'trade',\n account: trader\n })\n this.status = 'trade'\n }\n\n /**\n * Stop quotation/trading\n */\n stop() {\n if (this.status !== 'trade' && this.status !== 'quote')\n return\n this.send({type: 'stop'})\n this.tradeQuote = undefined\n this.status = 'ready'\n }\n\n /**\n * @param {{}} data\n * @private\n */\n send(data) {\n this.socket.send(JSON.stringify(data))\n }\n\n /**\n * @private\n */\n heartbeat() {\n clearTimeout(this.pingHandler)\n this.pingHandler = setTimeout(() => {\n console.warn('Lost connection with the server')\n this.socket.close()\n }, 7_000) // 7 seconds heartbeat timeout\n }\n\n /**\n * Add event listener\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n on(type, callback) {\n validateEventType(type)\n this.emitter.addEventListener(type, callback)\n }\n\n /**\n * Add event listener that will be executed once\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n once(type, callback) {\n validateEventType(type)\n this.emitter.addEventListener(type, callback, {once: true})\n }\n\n /**\n * Remove event listener\n * @param {StellarBrokerClientEvent} type\n * @param {function} callback\n */\n off(type, callback) {\n validateEventType(type)\n this.emitter.removeEventListener(type, callback)\n }\n\n /**\n * Close underlying connection and finalize the client\n */\n close() {\n try {\n //TODO: remove all attached event listeners\n this.status = 'disconnected'\n this.socket.close()\n } catch (e) {\n }\n }\n\n /**\n * @param {Error} e\n * @private\n */\n notifyError(e) {\n console.error(e)\n try {\n this.emitter.dispatchEvent(buildEvent('error', e instanceof Error ? e.message : e))\n } catch (e) {\n console.error(e)\n }\n }\n}\n\nconst stellarBrokerEvents = new Set(['quote', 'paused', 'progress', 'finished', 'error'])\n\nfunction validateEventType(type) {\n if (!stellarBrokerEvents.has(type))\n throw errors.unsupportedEventType(type)\n}\n\n/**\n * @typedef {object} ClientInitializationParams\n * @property {ClientAuthorizationParams} authorization - Authorization method, either account secret key or an authorization callback\n * @property {string} [account] - Trader account address\n * @property {string} [partnerKey] - Partner key\n */\n\n/**\n * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientSessionStatus - Current client session status\n */\n\n/**\n * @typedef {'quote'|'paused'|'progress'|'finished'|'error'} StellarBrokerClientEvent - Event type generated by the client\n */\n"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__346__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","Error","Set"],"sourceRoot":""}
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -17,8 +17,6 @@ export class StellarBrokerClient {
|
|
|
17
17
|
this.partnerKey = params.partnerKey
|
|
18
18
|
this.emitter = new EventTarget()
|
|
19
19
|
this.network = Networks.PUBLIC
|
|
20
|
-
if (!params.account || !StrKey.isValidEd25519PublicKey(params.account))
|
|
21
|
-
throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!params.account ? 'missing' : params.account))
|
|
22
20
|
this.trader = params.account
|
|
23
21
|
this.authorization = new AuthorizationWrapper(params.authorization)
|
|
24
22
|
}
|
|
@@ -141,7 +139,7 @@ export class StellarBrokerClient {
|
|
|
141
139
|
case 'quote':
|
|
142
140
|
this.lastQuote = new QuoteResult(raw.quote)
|
|
143
141
|
//send event to the client app
|
|
144
|
-
|
|
142
|
+
this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))
|
|
145
143
|
break
|
|
146
144
|
case 'paused':
|
|
147
145
|
//quotation paused due to inactivity
|
|
@@ -149,7 +147,7 @@ export class StellarBrokerClient {
|
|
|
149
147
|
break
|
|
150
148
|
case 'tx':
|
|
151
149
|
if (this.status !== 'trade') {
|
|
152
|
-
console.log('Received tx in non-trading state', this.status, raw)
|
|
150
|
+
console.log('Received tx in a non-trading state', this.status, raw)
|
|
153
151
|
return //skip unless trading is in progress
|
|
154
152
|
}
|
|
155
153
|
processTxRequest(this, raw)
|
|
@@ -201,6 +199,7 @@ export class StellarBrokerClient {
|
|
|
201
199
|
if (this.status === 'trade')
|
|
202
200
|
throw errors.tradeInProgress()
|
|
203
201
|
this.quoteRequest = validateQuoteRequest(params)
|
|
202
|
+
|
|
204
203
|
this.status = 'quote'
|
|
205
204
|
this.connect()
|
|
206
205
|
.then(() => {
|
|
@@ -214,8 +213,9 @@ export class StellarBrokerClient {
|
|
|
214
213
|
|
|
215
214
|
/**
|
|
216
215
|
* Confirm current quote and start trading
|
|
216
|
+
* @param {string} [account] - Trader account address (overrides value provided in the constructor)
|
|
217
217
|
*/
|
|
218
|
-
confirmQuote() {
|
|
218
|
+
confirmQuote(account) {
|
|
219
219
|
if (this.status === 'disconnected')
|
|
220
220
|
throw errors.notConnected()
|
|
221
221
|
if (this.status === 'trade')
|
|
@@ -226,11 +226,17 @@ export class StellarBrokerClient {
|
|
|
226
226
|
throw errors.quoteExpired()
|
|
227
227
|
if (this.lastQuote.status !== 'success')
|
|
228
228
|
throw errors.quoteError(this.lastQuote.error || 'quote not available')
|
|
229
|
-
|
|
229
|
+
if (account) {
|
|
230
|
+
this.trader = account
|
|
231
|
+
}
|
|
232
|
+
//ensure that a trader account address provided
|
|
233
|
+
const {trader} = this
|
|
234
|
+
if (!trader || !StrKey.isValidEd25519PublicKey(trader))
|
|
235
|
+
throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!trader ? 'missing' : trader))
|
|
230
236
|
this.tradeQuote = this.lastQuote
|
|
231
237
|
this.send({
|
|
232
238
|
type: 'trade',
|
|
233
|
-
account:
|
|
239
|
+
account: trader
|
|
234
240
|
})
|
|
235
241
|
this.status = 'trade'
|
|
236
242
|
}
|
|
@@ -330,8 +336,8 @@ function validateEventType(type) {
|
|
|
330
336
|
|
|
331
337
|
/**
|
|
332
338
|
* @typedef {object} ClientInitializationParams
|
|
333
|
-
* @property {string} account - Trader account address
|
|
334
339
|
* @property {ClientAuthorizationParams} authorization - Authorization method, either account secret key or an authorization callback
|
|
340
|
+
* @property {string} [account] - Trader account address
|
|
335
341
|
* @property {string} [partnerKey] - Partner key
|
|
336
342
|
*/
|
|
337
343
|
|
package/src/index.d.ts
CHANGED
|
@@ -43,8 +43,9 @@ export class StellarBrokerClient {
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Confirm current quote and start trading
|
|
46
|
+
* @param {string} [account] - Trader account address (overrides value provided in the constructor)
|
|
46
47
|
*/
|
|
47
|
-
confirmQuote(): void;
|
|
48
|
+
confirmQuote(account?: string): void;
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Add event listener
|
|
@@ -101,18 +102,18 @@ export interface QuoteParams {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
export interface ClientInitializationParams {
|
|
105
|
+
/**
|
|
106
|
+
* Authorization method, either account secret key or an authorization callback
|
|
107
|
+
*/
|
|
108
|
+
authorization: ClientAuthorizationParams;
|
|
104
109
|
/**
|
|
105
110
|
* Trader account address
|
|
106
111
|
*/
|
|
107
|
-
account
|
|
112
|
+
account?: string;
|
|
108
113
|
/**
|
|
109
114
|
* Partner key
|
|
110
115
|
*/
|
|
111
116
|
partnerKey?: string;
|
|
112
|
-
/**
|
|
113
|
-
* Authorization method, either account secret key or an authorization callback
|
|
114
|
-
*/
|
|
115
|
-
authorization: ClientAuthorizationParams;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
/**
|
package/src/tx-processor.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {hash,
|
|
1
|
+
import {hash, nativeToScVal, StrKey, TransactionBuilder, xdr} from '@stellar/stellar-sdk'
|
|
2
2
|
import errors from './errors.js'
|
|
3
3
|
|
|
4
|
+
const networkIdCache = {}
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* @param {StellarBrokerClient} client
|
|
6
8
|
* @param {{xdr: string, hash: string, networkFee: string, [confirmed]: boolean}} txRequest
|
|
@@ -51,8 +53,10 @@ async function authorizeInvocation(client, tx) {
|
|
|
51
53
|
/** @type {xdr.SorobanAddressCredentials} */
|
|
52
54
|
const addrAuth = auth.credentials().address()
|
|
53
55
|
addrAuth.signatureExpirationLedger(tx.ledgerBounds.maxLedger + 1)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
let networkId = networkIdCache[tx.networkPassphrase]
|
|
57
|
+
if (!networkId) {
|
|
58
|
+
networkId = networkIdCache[tx.networkPassphrase] = hash(Buffer.from(tx.networkPassphrase))
|
|
59
|
+
}
|
|
56
60
|
const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
|
|
57
61
|
new xdr.HashIdPreimageSorobanAuthorization({
|
|
58
62
|
networkId,
|