@stellar-broker/client 0.5.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 CHANGED
@@ -16,7 +16,7 @@ notifications from the server without delays. Trading, retries, and results conf
16
16
  background.
17
17
 
18
18
  ```js
19
- import StellarBrokerClient from '@stellar-broker/client'
19
+ import {StellarBrokerClient} from '@stellar-broker/client'
20
20
 
21
21
  //create client instance
22
22
  const client = new StellarBrokerClient({partnerKey: '<your_partner_key>'})
@@ -73,7 +73,7 @@ client.connect() //only once
73
73
  //request a price quote
74
74
  client.quote({
75
75
  sellingAsset: 'xlm',
76
- buyingAsset: 'USD-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
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: 'USD-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
111
+ buyingAsset: 'USDC-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
112
112
  sellingAmount: '1000',
113
113
  slippageTolerance: 0.02 //2%
114
114
  })
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("@stellar/stellar-sdk")):"function"==typeof define&&define.amd?define(["@stellar/stellar-sdk"],e):"object"==typeof exports?exports.stellarbroker=e(require("@stellar/stellar-sdk")):t.stellarbroker=e(t["@stellar/stellar-sdk"])}(this,(t=>(()=>{"use strict";var e={346:e=>{e.exports=t}},s={};function r(t){var o=s[t];if(void 0!==o)return o.exports;var i=s[t]={exports:{}};return e[t](i,i.exports,r),i.exports}r.d=(t,e)=>{for(var s in e)r.o(e,s)&&!r.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var o={};r.d(o,{default:()=>b});var i=r(346);class n extends Error{constructor(t,e){super(e),this.code=t}code=0}const a={invalidInitParam:t=>new n(1,`Invalid parameter value: "${t}"`),invalidAuthorizationParam:()=>new n(2,"Invalid authorization secret key or callback provided"),notConnected:()=>new n(3,"Client not connected to the server"),quoteNotSet:()=>new n(11,"Price quote not available"),quoteExpired:()=>new n(12,"Price quote expired"),quoteError:t=>new n(13,"Price quotation error: "+t),invalidQuoteParam:(t="asset",e)=>new n(14,`Invalid quote request parameter: "${t}". ${e}`),tradeInProgress:()=>new n(20,"Cannot change quote while trade is in progress"),invalidSwapTx:()=>new n(21,"Invalid swap transaction received from the server"),failedToSignTx:()=>new n(22,"Failed to sign received transaction"),unsupportedEventType:t=>new n(31,"Unknown event type: "+t),serverError:t=>new n(101,t)};function u(t,e,s){const r=new CustomEvent(t,{detail:e});return r[s||t]=e,r}function c(t,e){if("string"==typeof t){if("XLM"===t||"xlm"===t||"native"===t)return"XLM";if(t.includes(":")){const[s,r]=t.split(":");return d(s),l(r,e),s+"-"+r}if(t.includes("-")){const[s,r]=t.split("-");return d(s),l(r,e),t}}throw a.invalidQuoteParam(e,"Invalid asset")}function d(t,e){if(!/^[a-zA-Z0-9]{1,12}$/.test(t))throw a.invalidQuoteParam(e,"Invalid asset code: "+(t||"missing"));return t}function l(t,e){if(!t||!i.StrKey.isValidEd25519PublicKey(t))throw a.invalidQuoteParam(e,"Invalid account address: "+(t||"missing"));return t}function h(t){const{sellingAsset:e,selling_asset:s,buyingAsset:r,buying_asset:o,sellingAmount:i,selling_amount:n,buyingAmount:u,buying_amount:d,slippageTolerance:l,slippage_tolerance:h,...g}=t,m={sellingAsset:c(e||s,"sellingAsset"),buyingAsset:c(r||o,"buyingAsset"),sellingAmount:p(i||n,"sellingAmount"),slippageTolerance:f(l||h||.02,"slippageTolerance")};if(m.buyingAsset===m.sellingAsset)throw a.invalidQuoteParam("buyingAsset","Buying asset can't be the same as selling asset");if(void 0===m.sellingAmount)throw a.invalidQuoteParam("sellingAmount",'Parameter "sellingAmount" parameter is required');return Object.assign(m,g),Object.freeze(m),m}function p(t,e){if(void 0===t)return;const s=parseFloat(t);if(isNaN(s)||s<=0)throw a.invalidQuoteParam(e,"Invalid asset amount: "+t);return t}function f(t,e){const s=parseFloat(t);if(isNaN(s))throw a.invalidQuoteParam(e,"Invalid slippage tolerance, number expected");if(s<0)throw a.invalidQuoteParam(e,"Slippage tolerance is too small, expected value >= 0");if(s>.5)throw a.invalidQuoteParam(e,"Slippage tolerance is too large, expected value < 0.5 (50%)");return s}function g(t,e){return t.authorization.authorize(e).then((e=>{if("string"==typeof e&&(e=i.TransactionBuilder.fromXDR(e,t.network)),!e||e.signatures?.length<1)throw new Error("Transaction was not signed");return e})).catch((t=>{throw console.error(t),a.failedToSignTx()}))}class m{constructor(t){if("string"==typeof t)try{return void(this.authorization=i.Keypair.fromSecret(t))}catch(t){throw a.invalidAuthorizationParam()}else if("function"==typeof t)return void(this.authorization=t);throw a.invalidAuthorizationParam()}authorization;async authorize(t){return this.authorization instanceof i.Keypair?t.sign?(t.sign(this.authorization),t):this.authorization.sign(t):await function(t){if(!(t instanceof Promise)){if(!t)return Promise.reject();t=Promise.resolve(t)}return t}(this.authorization(t))}}class v{constructor(t){if(this.partnerKey=t.partnerKey,this.emitter=new EventTarget,this.network=i.Networks.PUBLIC,!t.account||!i.StrKey.isValidEd25519PublicKey(t.account))throw a.invalidQuoteParam("account","Invalid trader account address: "+(t.account?t.account:"missing"));this.trader=t.account,this.authorization=new m(t.authorization)}uid;status="disconnected";socket;emitter;origin="https://api.stellar.broker";network="";quoteRequest;lastQuote;tradeQuote;partnerKey;authorization;trader;connect(){return this.socket?.readyState===WebSocket.OPEN?Promise.resolve(this):(this.socket=new WebSocket(this.origin+"/ws?partner="+encodeURIComponent(this.partnerKey)),this.socket.onmessage=this.processMessage.bind(this),this.socket.onclose=()=>{"disconnected"!==this.status&&(this.status="disconnected",this.notifyError(a.notConnected())),console.log("Connection closed")},this.socket.onerror=t=>console.error(t),new Promise(((t,e)=>{const s=setTimeout((()=>e(this)),2e3);this.onSocketOpen=()=>{this.status="ready",this.heartbeat(),this.onSocketOpen=void 0,clearTimeout(s),t(this)}})))}processMessage(t){const e=JSON.parse(t.data);switch(e.type){case"connected":this.uid=e.uid,this.onSocketOpen&&this.onSocketOpen(),"disconnected"===this.status&&(this.status="ready");break;case"quote":this.lastQuote=e.quote,this.lastQuote.ts=new Date,this.emitter.dispatchEvent(u("quote",this.lastQuote));break;case"paused":this.emitter.dispatchEvent(u("paused",{}));break;case"tx":if("trade"!==this.status)return void console.log("Received tx in non-trading state",this.status,e);(async function(t,e){let s;try{s=i.TransactionBuilder.fromXDR(e.xdr,t.network)}catch(t){throw a.invalidSwapTx()}if(!function(t,e){for(let s of e.operations)if("invokeHostFunction"!==s.type){if("pathPaymentStrictSend"!==s.type&&"pathPaymentStrictReceive"!==s.type)return!1;if(s.destination!==t.trader){if("pathPaymentStrictSend"!==s.type)return!1;if(s.source&&s.source!==t.trader)return!1}else if(s.source&&s.source!==s.destination||s.destination!==t.trader)return!1}return!0}(t,s))throw a.invalidSwapTx();if(s.operations[0].auth?.length>0){if(!s.signatures.length)return await async function(t,e){const s=e.operations[0].auth[0],r=s.credentials().address();r.signatureExpirationLedger(e.ledgerBounds.maxLedger+1);const o=(0,i.hash)(Buffer.from(e.networkPassphrase)),n=i.xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(new i.xdr.HashIdPreimageSorobanAuthorization({networkId:o,nonce:r.nonce(),invocation:s.rootInvocation(),signatureExpirationLedger:r.signatureExpirationLedger()})),a=(0,i.hash)(n.toXDR()),u=await t.authorization.authorize(a),c=i.StrKey.encodeEd25519PublicKey(r.address().accountId().value()),d=(0,i.nativeToScVal)({public_key:i.StrKey.decodeEd25519PublicKey(c),signature:u},{type:{public_key:["symbol",null],signature:["symbol",null]}});r.signature(i.xdr.ScVal.scvVec([d]))}(t,s),s=await g(t,s),s.toXDR()}else s=await g(t,s);let r=i.TransactionBuilder.buildFeeBumpTransaction(t.trader,e.networkFee,s,t.network);return r=await g(t,r),r.toXDR()})(this,e).then((t=>{this.send({type:"tx",hash:e.hash,xdr:t})})).catch((t=>this.notifyError(t)));break;case"stop":this.emitter.dispatchEvent(u("finished",{status:e.status,sold:e.sold,bought:e.bought},"result")),this.status="ready",this.tradeQuote=void 0;break;case"progress":this.emitter.dispatchEvent(u("progress",{sold:e.sold,bought:e.bought},"status"));break;case"ping":e.uid===this.uid&&(this.heartbeat(),this.send({type:"pong",uid:this.uid}));break;case"error":this.stop(),this.notifyError(e.error);break;default:console.log("Unknown message type: "+e.type)}}quote(t){if("trade"===this.status)throw a.tradeInProgress();this.quoteRequest=h(t),this.status="quote",this.connect().then((()=>{this.status="quote",this.send({type:"quote",...this.quoteRequest})}))}confirmQuote(){if("disconnected"===this.status)throw a.notConnected();if("trade"===this.status)throw a.tradeInProgress();if("quote"!==this.status||!this.lastQuote)throw a.quoteNotSet();if(new Date-this.lastQuote.ts>1e4)throw a.quoteExpired();if("success"!==this.lastQuote.status)throw a.quoteError(this.lastQuote.error||"quote not available");this.tradeQuote=this.lastQuote,this.send({type:"trade",account:this.trader}),this.status="trade"}stop(){"trade"!==this.status&&"quote"!==this.status||(this.send({type:"stop"}),this.tradeQuote=void 0,this.status="ready")}send(t){this.socket.send(JSON.stringify(t))}heartbeat(){clearTimeout(this.pingHandler),this.pingHandler=setTimeout((()=>{console.warn("Lost connection with the server"),this.socket.close()}),7e3)}on(t,e){y(t),this.emitter.addEventListener(t,e)}once(t,e){y(t),this.emitter.addEventListener(t,e,{once:!0})}off(t,e){y(t),this.emitter.removeEventListener(t,e)}close(){try{this.status="disconnected",this.socket.close()}catch(t){}}notifyError(t){console.error(t);try{this.emitter.dispatchEvent(u("error",t instanceof Error?t.message:t))}catch(t){console.error(t)}}}function y(t){if(!w.includes(t))throw a.unsupportedEventType(t)}const w=["quote","paused","progress","finished","error"];v.estimateSwap=async function(t){const e=Object.entries(h(t)).filter((t=>void 0!==t[1])).map((([t,e])=>encodeURIComponent(t)+"="+encodeURIComponent(e))),s=(t.origin||"https://api.stellar.broker")+"/quote?"+e.join("&");try{const t=await fetch(s).then((t=>t.json()));if("success"!==t.status)throw a.quoteError(t.error||"Quote not available");return t}catch(t){if(t instanceof n)throw t;throw a.quoteError("Failed to fetch quote")}};const b=v;return o.default})()));
1
+ !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r(require("@stellar/stellar-sdk")):"function"==typeof define&&define.amd?define(["@stellar/stellar-sdk"],r):"object"==typeof exports?exports.stellarbroker=r(require("@stellar/stellar-sdk")):e.stellarbroker=r(e["@stellar/stellar-sdk"])}(this,(e=>(()=>{"use strict";var r={346:r=>{r.exports=e}},t={},s={};return function e(s){var o=t[s];if(void 0!==o)return o.exports;var l=t[s]={exports:{}};return r[s](l,l.exports,e),l.exports}(346),Error,new Set(["quote","paused","progress","finished","error"]),s.default})()));
2
2
  //# sourceMappingURL=stellarbroker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stellarbroker.js","mappings":"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","sources":["webpack://stellarbroker/webpack/universalModuleDefinition","webpack://stellarbroker/external umd \"@stellar/stellar-sdk\"","webpack://stellarbroker/webpack/bootstrap","webpack://stellarbroker/webpack/runtime/define property getters","webpack://stellarbroker/webpack/runtime/hasOwnProperty shorthand","webpack://stellarbroker/./src/errors.js","webpack://stellarbroker/./src/events.js","webpack://stellarbroker/./src/asset.js","webpack://stellarbroker/./src/quote-request.js","webpack://stellarbroker/./src/tx-processor.js","webpack://stellarbroker/./src/authorization.js","webpack://stellarbroker/./src/client.js","webpack://stellarbroker/./src/cjs.js","webpack://stellarbroker/./src/estimate.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","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","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","/**\r\n * @param {string} type\r\n * @param {{}} data\r\n * @param {string} [key]\r\n * @return {CustomEvent}\r\n */\r\nexport function buildEvent(type, data, key) {\r\n const evt = new CustomEvent(type, {detail: data})\r\n evt[key || type] = data\r\n return evt\r\n}","import {Asset, StrKey} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\n\n/**\n * @param {string|Asset|{}} asset\n * @return {Asset}\n */\nexport function convertToStellarAsset(asset) {\n if (asset instanceof Asset)\n return asset\n if (asset === 'xlm' || asset === 'XLM' || asset.asset_type === 'native')\n return Asset.native()\n if (asset.asset_type)\n return new Asset(asset.asset_code, asset.asset_issuer)\n const [code, issuer] = asset.includes('-') ?\n asset.split('-') :\n asset.split(':')\n return new Asset(code, issuer)\n}\n\nexport function parseAsset(asset, parameter) {\n if (typeof asset === 'string') {\n if (asset === 'XLM' || asset === 'xlm' || asset === 'native')\n return 'XLM'\n if (asset.includes(':')) {\n const [code, issuer] = asset.split(':')\n validateCode(code)\n validateAccount(issuer, parameter)\n return code + '-' + issuer\n }\n if (asset.includes('-')) {\n const [code, issuer] = asset.split('-')\n validateCode(code)\n validateAccount(issuer, parameter)\n return asset\n }\n }\n throw errors.invalidQuoteParam(parameter, 'Invalid asset')\n}\n\nfunction validateCode(code, parameter) {\n if (!/^[a-zA-Z0-9]{1,12}$/.test(code))\n throw errors.invalidQuoteParam(parameter, 'Invalid asset code: ' + (!code ? 'missing' : code))\n return code\n}\n\nfunction validateAccount(account, param) {\n if (!account || !StrKey.isValidEd25519PublicKey(account))\n throw errors.invalidQuoteParam(param, 'Invalid account address: ' + (!account ? 'missing' : account))\n return account\n}\n","import errors from './errors.js'\nimport {parseAsset} from './asset.js'\n\n/**\n * @typedef {object} SwapQuoteParams\n * @property {string} sellingAsset - Asset to sell\n * @property {string} buyingAsset - Asset to buy\n * @property {string} [sellingAmount] - Amount of selling asset\n * @property {number} [slippageTolerance] - Swap slippage tolerance (0.02 by default - 2%)\n */\n\n/**\n * @param {SwapQuoteParams} params\n * @return {SwapQuoteParams}\n */\nexport function validateQuoteRequest(params) {\n const {\n sellingAsset, selling_asset, buyingAsset, buying_asset, sellingAmount, selling_amount,\n buyingAmount, buying_amount, slippageTolerance, slippage_tolerance, ...other\n } = params\n const res = {\n sellingAsset: parseAsset(sellingAsset || selling_asset, 'sellingAsset'),\n buyingAsset: parseAsset(buyingAsset || buying_asset, 'buyingAsset'),\n sellingAmount: parseAmount(sellingAmount || selling_amount, 'sellingAmount'),\n //buyingAmount: parseAmount(buyingAmount || buying_amount, 'buyingAmount'),\n slippageTolerance: parseSlippageTolerance(slippageTolerance || slippage_tolerance || 0.02, 'slippageTolerance')\n }\n if (res.buyingAsset === res.sellingAsset)\n throw errors.invalidQuoteParam('buyingAsset', 'Buying asset can\\'t be the same as selling asset')\n if (res.sellingAmount === undefined)\n throw errors.invalidQuoteParam('sellingAmount', 'Parameter \"sellingAmount\" parameter is required')\n /*if (res.buyingAmount !== undefined && res.sellingAmount !== undefined)\n throw errors.invalidQuoteParam('sellingAmount', 'Parameters \"buyingAmount\" and \"sellingAmount\" are mutually exclusive')*/\n Object.assign(res, other) //add remaining optional params\n Object.freeze(res)\n return res\n}\n\nfunction parseAmount(amount, parameter) {\n if (amount === undefined)\n return undefined\n const parsed = parseFloat(amount)\n if (isNaN(parsed) || parsed <= 0)\n throw errors.invalidQuoteParam(parameter, 'Invalid asset amount: ' + amount)\n return amount\n}\n\nfunction parseSlippageTolerance(src, parameter) {\n const tolerance = parseFloat(src)\n if (isNaN(tolerance))\n throw errors.invalidQuoteParam(parameter, 'Invalid slippage tolerance, number expected')\n if (tolerance < 0)\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too small, expected value >= 0')\n if (tolerance > 0.5)\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too large, expected value < 0.5 (50%)')\n return tolerance\n}","import {hash, Keypair, nativeToScVal, StrKey, TransactionBuilder, xdr} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\n\n/**\n * @param {StellarBrokerClient} client\n * @param {{xdr: string, hash: string, networkFee: string, [confirmed]: boolean}} txRequest\n * @return {Promise<string>}\n */\nexport async function processTxRequest(client, txRequest) {\n //parse incoming transaction\n let tx\n try {\n tx = TransactionBuilder.fromXDR(txRequest.xdr, client.network)\n } catch (e) {\n throw errors.invalidSwapTx()\n }\n //check that transaction is correct\n if (!validateTransaction(client, tx))\n throw errors.invalidSwapTx()\n //if transaction has not been authorized yet by the channel account\n const isSorobanTx = tx.operations[0].auth?.length > 0\n if (isSorobanTx) {\n //send tx back to the server\n if (!tx.signatures.length) {\n //sign auth\n await authorizeInvocation(client, tx)\n tx = await signTx(client, tx)\n return tx.toXDR()//wait for the signed tx from the server to wrap it with fee bump tx\n }\n } else {\n //sign transaction\n tx = await signTx(client, tx)\n }\n //wrap with fee bump\n let wrapped = TransactionBuilder.buildFeeBumpTransaction(client.trader, txRequest.networkFee, tx, client.network)\n //sign fee bump wrapper tx\n wrapped = await signTx(client, wrapped)\n //respond with signed transaction\n return wrapped.toXDR()\n}\n\n/**\n * @param {StellarBrokerClient} client\n * @param {TransactionI} tx\n * @return {Promise<TransactionI>}\n * @private\n */\nasync function authorizeInvocation(client, tx) {\n //from https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97\n const auth = tx.operations[0].auth[0]\n /** @type {xdr.SorobanAddressCredentials} */\n const addrAuth = auth.credentials().address()\n addrAuth.signatureExpirationLedger(tx.ledgerBounds.maxLedger + 1)\n\n const networkId = hash(Buffer.from(tx.networkPassphrase))\n const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(\n new xdr.HashIdPreimageSorobanAuthorization({\n networkId,\n nonce: addrAuth.nonce(),\n invocation: auth.rootInvocation(),\n signatureExpirationLedger: addrAuth.signatureExpirationLedger()\n })\n )\n const payload = hash(preimage.toXDR())\n const signature = await client.authorization.authorize(payload)\n const publicKey = StrKey.encodeEd25519PublicKey(addrAuth.address().accountId().value())\n const sigScVal = nativeToScVal(\n {\n public_key: StrKey.decodeEd25519PublicKey(publicKey),\n signature\n },\n {\n type: {\n public_key: ['symbol', null],\n signature: ['symbol', null]\n }\n }\n )\n\n addrAuth.signature(xdr.ScVal.scvVec([sigScVal]))\n}\n\n/**\n * @param {StellarBrokerClient} client\n * @param {TransactionI} tx\n * @return {Promise<TransactionI>}\n * @private\n */\nfunction signTx(client, tx) {\n return client.authorization.authorize(tx)\n .then(tx => {\n if (typeof tx === 'string') { //normalize response\n tx = TransactionBuilder.fromXDR(tx, client.network)\n }\n if (!tx || tx.signatures?.length < 1)\n throw new Error(`Transaction was not signed`)\n return tx\n })\n .catch(e => {\n console.error(e)\n throw errors.failedToSignTx()\n })\n}\n\n/**\n * @param {StellarBrokerClient} client\n * @param {Transaction} tx\n * @throws {StellarBrokerError} Invalid swap transaction received\n */\nfunction validateTransaction(client, tx) {\n /*if (!(tx instanceof FeeBumpTransaction))\n return false\n const {innerTransaction} = tx\n if (tx.feeSource !== this.source)\n return false*/\n for (let swap of tx.operations) {\n if (swap.type === 'invokeHostFunction')\n continue //TODO: add additional validation\n if (swap.type !== 'pathPaymentStrictSend' && swap.type !== 'pathPaymentStrictReceive')\n return false\n const isFee = swap.destination !== client.trader\n if (isFee) {\n if (swap.type !== 'pathPaymentStrictSend')\n return false\n if ((swap.source && swap.source !== client.trader))\n return false\n } else {\n if ((swap.source && swap.source !== swap.destination) || swap.destination !== client.trader)\n return false\n }\n }\n //TODO: check assets and amounts\n return true\n}","import {Keypair} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\n\nexport class AuthorizationWrapper {\n /**\n * @param {ClientAuthorizationParams} authorization\n */\n constructor(authorization) {\n if (typeof authorization === 'string') {\n try {\n this.authorization = Keypair.fromSecret(authorization)\n return\n } catch (e) {\n throw errors.invalidAuthorizationParam()\n }\n } else if (typeof authorization === 'function') {\n this.authorization = authorization\n return\n }\n throw errors.invalidAuthorizationParam()\n }\n\n /**\n * @type {Keypair|ClientAuthorizationCallback}\n * @private\n */\n authorization\n\n /**\n * Authorize transaction or invocation footprint hash\n * @param {TransactionI|Buffer} payload\n * @return {Promise<TransactionI|Buffer>}\n */\n async authorize(payload) {\n if (this.authorization instanceof Keypair) {\n if (payload.sign){\n payload.sign(this.authorization)\n return payload\n }\n return this.authorization.sign(payload)\n }\n return await promisify(this.authorization(payload))\n }\n}\n\n/**\n * @param {*|Promise} callResult\n * @return {Promise}\n */\nfunction promisify(callResult) {\n if (!(callResult instanceof Promise)) {\n if (!callResult)\n return Promise.reject()\n callResult = Promise.resolve(callResult)\n }\n return callResult\n}\n\n/**\n * @typedef {function(TransactionI|Buffer):Promise<TransactionI|Buffer>} ClientAuthorizationCallback - Async authorization callback\n */\n\n/**\n * @typedef {string|ClientAuthorizationCallback} ClientAuthorizationParams - Authorization method, either account secret key or an authorization callback\n */","import {Networks, StrKey} from '@stellar/stellar-sdk'\nimport errors from './errors.js'\nimport {buildEvent} from './events.js'\nimport {validateQuoteRequest} from './quote-request.js'\nimport {processTxRequest} from './tx-processor.js'\nimport {AuthorizationWrapper} from './authorization.js'\n\nexport default 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 {ClientStatus}\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 {SwapQuoteParams}\n * @readonly\n */\n quoteRequest\n /**\n * Last quote received from the sever\n * @type {SwapQuoteResult}\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 = raw.quote\n this.lastQuote.ts = new Date()\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 {SwapQuoteParams} 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\nfunction validateEventType(type) {\n if (!StellarBrokerEvents.includes(type))\n throw errors.unsupportedEventType(type)\n}\n\nexport const StellarBrokerEvents = ['quote', 'paused', 'progress', 'finished', 'error']\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'} ClientStatus\n */\n\n/**\n * @typedef {'quote'|'paused'|'progress'|'finished'|'error'} StellarBrokerClientEvent\n */\n\n/**\n * @typedef {object} SwapQuoteResult\n * @property {string} ts - Quote timestamp formatted as ISO date\n * @property {string} sellingAsset - Asset to sell\n * @property {string} buyingAsset - Asset to buy\n * @property {number} slippageTolerance - Swap slippage tolerance\n * @property {string} sellingAmount - Amount of the selling asset\n * @property {string} [estimatedBuyingAmount] - Estimated amount of buyingAsset to receive\n * @property {{selling: string, buying: string, path: string[]}} [directTrade] - Equivalent direct path_payment trade estimate\n * @property {string} [error] - Error details from the server (for failed quotes)\n */\n","import StellarBrokerClient from './client.js'\r\nimport {estimateSwap} from './estimate.js'\r\n\r\nStellarBrokerClient.estimateSwap = estimateSwap\r\n\r\nexport default StellarBrokerClient","import errors, {StellarBrokerError} from './errors.js'\r\nimport {validateQuoteRequest} from './quote-request.js'\r\n\r\n/**\r\n * Request single swap quote estimate without trading\r\n * @param {SwapQuoteParams} params\r\n * @return {Promise<SwapQuoteResult>}\r\n */\r\nexport async function estimateSwap(params) {\r\n const query = Object.entries(validateQuoteRequest(params))\r\n .filter(kv => kv[1] !== undefined)\r\n .map(([param, value]) => encodeURIComponent(param) + '=' + encodeURIComponent(value))\r\n const url = (params.origin || 'https://api.stellar.broker') + '/quote?' + query.join('&')\r\n try {\r\n const res = await fetch(url).then(r => r.json())\r\n if (res.status !== 'success')\r\n throw errors.quoteError(res.error || 'Quote not available')\r\n return res\r\n } catch (e) {\r\n if (e instanceof StellarBrokerError)\r\n throw e\r\n throw errors.quoteError('Failed to fetch quote')\r\n return null\r\n }\r\n}"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__346__","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","StellarBrokerError","Error","constructor","code","message","super","invalidInitParam","param","invalidAuthorizationParam","notConnected","quoteNotSet","quoteExpired","quoteError","invalidQuoteParam","invalidParamName","details","tradeInProgress","invalidSwapTx","failedToSignTx","unsupportedEventType","type","serverError","buildEvent","data","evt","CustomEvent","detail","parseAsset","asset","parameter","includes","issuer","split","validateCode","validateAccount","errors","test","account","StrKey","isValidEd25519PublicKey","validateQuoteRequest","params","sellingAsset","selling_asset","buyingAsset","buying_asset","sellingAmount","selling_amount","buyingAmount","buying_amount","slippageTolerance","slippage_tolerance","other","res","parseAmount","parseSlippageTolerance","assign","freeze","amount","parsed","parseFloat","isNaN","src","tolerance","signTx","client","tx","authorization","authorize","then","TransactionBuilder","fromXDR","network","signatures","length","catch","e","console","error","AuthorizationWrapper","Keypair","fromSecret","payload","sign","callResult","Promise","reject","resolve","promisify","StellarBrokerClient","partnerKey","emitter","EventTarget","Networks","PUBLIC","trader","uid","status","socket","origin","quoteRequest","lastQuote","tradeQuote","connect","readyState","WebSocket","OPEN","encodeURIComponent","onmessage","processMessage","bind","onclose","notifyError","log","onerror","confirm","expirationTimeout","setTimeout","onSocketOpen","heartbeat","clearTimeout","raw","JSON","parse","quote","ts","Date","dispatchEvent","async","txRequest","xdr","swap","operations","destination","source","validateTransaction","auth","addrAuth","credentials","address","signatureExpirationLedger","ledgerBounds","maxLedger","networkId","hash","Buffer","from","networkPassphrase","preimage","HashIdPreimage","envelopeTypeSorobanAuthorization","HashIdPreimageSorobanAuthorization","nonce","invocation","rootInvocation","toXDR","signature","publicKey","encodeEd25519PublicKey","accountId","value","sigScVal","nativeToScVal","public_key","decodeEd25519PublicKey","ScVal","scvVec","authorizeInvocation","wrapped","buildFeeBumpTransaction","networkFee","processTxRequest","send","sold","bought","stop","confirmQuote","stringify","pingHandler","warn","close","on","callback","validateEventType","addEventListener","once","off","removeEventListener","StellarBrokerEvents","estimateSwap","query","entries","filter","kv","map","url","join","fetch","r","json"],"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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar-broker/client",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Client library for StellarBroker service",
5
5
  "author": "team@stellar.broker",
6
6
  "homepage": "https://stellar.broker",
package/src/client.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import {Networks, StrKey} from '@stellar/stellar-sdk'
2
2
  import errors from './errors.js'
3
3
  import {buildEvent} from './events.js'
4
- import {validateQuoteRequest} from './quote-request.js'
5
4
  import {processTxRequest} from './tx-processor.js'
5
+ import {validateQuoteRequest} from './quote-request.js'
6
+ import {QuoteResult} from './quote-result.js'
6
7
  import {AuthorizationWrapper} from './authorization.js'
7
8
 
8
- export default class StellarBrokerClient {
9
+ /**
10
+ * Client for StellarBroker service
11
+ */
12
+ export class StellarBrokerClient {
9
13
  /**
10
14
  * @param {ClientInitializationParams} params
11
15
  */
@@ -13,8 +17,6 @@ export default class StellarBrokerClient {
13
17
  this.partnerKey = params.partnerKey
14
18
  this.emitter = new EventTarget()
15
19
  this.network = Networks.PUBLIC
16
- if (!params.account || !StrKey.isValidEd25519PublicKey(params.account))
17
- throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!params.account ? 'missing' : params.account))
18
20
  this.trader = params.account
19
21
  this.authorization = new AuthorizationWrapper(params.authorization)
20
22
  }
@@ -25,7 +27,7 @@ export default class StellarBrokerClient {
25
27
  */
26
28
  uid
27
29
  /**
28
- * @type {ClientStatus}
30
+ * @type {ClientSessionStatus}
29
31
  * @readonly
30
32
  */
31
33
  status = 'disconnected'
@@ -52,13 +54,13 @@ export default class StellarBrokerClient {
52
54
  network = ''
53
55
  /**
54
56
  * Last quote request submitted by the client
55
- * @type {SwapQuoteParams}
57
+ * @type {QuoteParams}
56
58
  * @readonly
57
59
  */
58
60
  quoteRequest
59
61
  /**
60
62
  * Last quote received from the sever
61
- * @type {SwapQuoteResult}
63
+ * @type {QuoteResult}
62
64
  * @readonly
63
65
  */
64
66
  lastQuote
@@ -135,10 +137,9 @@ export default class StellarBrokerClient {
135
137
  }
136
138
  break
137
139
  case 'quote':
138
- this.lastQuote = raw.quote
139
- this.lastQuote.ts = new Date()
140
+ this.lastQuote = new QuoteResult(raw.quote)
140
141
  //send event to the client app
141
- this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))
142
+ this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))
142
143
  break
143
144
  case 'paused':
144
145
  //quotation paused due to inactivity
@@ -146,7 +147,7 @@ export default class StellarBrokerClient {
146
147
  break
147
148
  case 'tx':
148
149
  if (this.status !== 'trade') {
149
- console.log('Received tx in non-trading state', this.status, raw)
150
+ console.log('Received tx in a non-trading state', this.status, raw)
150
151
  return //skip unless trading is in progress
151
152
  }
152
153
  processTxRequest(this, raw)
@@ -192,12 +193,13 @@ export default class StellarBrokerClient {
192
193
 
193
194
  /**
194
195
  * Request swap quote
195
- * @param {SwapQuoteParams} params - Quote parameters
196
+ * @param {QuoteParams} params - Quote parameters
196
197
  */
197
198
  quote(params) {
198
199
  if (this.status === 'trade')
199
200
  throw errors.tradeInProgress()
200
201
  this.quoteRequest = validateQuoteRequest(params)
202
+
201
203
  this.status = 'quote'
202
204
  this.connect()
203
205
  .then(() => {
@@ -211,8 +213,9 @@ export default class StellarBrokerClient {
211
213
 
212
214
  /**
213
215
  * Confirm current quote and start trading
216
+ * @param {string} [account] - Trader account address (overrides value provided in the constructor)
214
217
  */
215
- confirmQuote() {
218
+ confirmQuote(account) {
216
219
  if (this.status === 'disconnected')
217
220
  throw errors.notConnected()
218
221
  if (this.status === 'trade')
@@ -223,11 +226,17 @@ export default class StellarBrokerClient {
223
226
  throw errors.quoteExpired()
224
227
  if (this.lastQuote.status !== 'success')
225
228
  throw errors.quoteError(this.lastQuote.error || 'quote not available')
226
-
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))
227
236
  this.tradeQuote = this.lastQuote
228
237
  this.send({
229
238
  type: 'trade',
230
- account: this.trader
239
+ account: trader
231
240
  })
232
241
  this.status = 'trade'
233
242
  }
@@ -318,36 +327,24 @@ export default class StellarBrokerClient {
318
327
  }
319
328
  }
320
329
 
330
+ const stellarBrokerEvents = new Set(['quote', 'paused', 'progress', 'finished', 'error'])
331
+
321
332
  function validateEventType(type) {
322
- if (!StellarBrokerEvents.includes(type))
333
+ if (!stellarBrokerEvents.has(type))
323
334
  throw errors.unsupportedEventType(type)
324
335
  }
325
336
 
326
- export const StellarBrokerEvents = ['quote', 'paused', 'progress', 'finished', 'error']
327
-
328
337
  /**
329
338
  * @typedef {object} ClientInitializationParams
330
- * @property {string} account - Trader account address
331
339
  * @property {ClientAuthorizationParams} authorization - Authorization method, either account secret key or an authorization callback
340
+ * @property {string} [account] - Trader account address
332
341
  * @property {string} [partnerKey] - Partner key
333
342
  */
334
343
 
335
344
  /**
336
- * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientStatus
337
- */
338
-
339
- /**
340
- * @typedef {'quote'|'paused'|'progress'|'finished'|'error'} StellarBrokerClientEvent
345
+ * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientSessionStatus - Current client session status
341
346
  */
342
347
 
343
348
  /**
344
- * @typedef {object} SwapQuoteResult
345
- * @property {string} ts - Quote timestamp formatted as ISO date
346
- * @property {string} sellingAsset - Asset to sell
347
- * @property {string} buyingAsset - Asset to buy
348
- * @property {number} slippageTolerance - Swap slippage tolerance
349
- * @property {string} sellingAmount - Amount of the selling asset
350
- * @property {string} [estimatedBuyingAmount] - Estimated amount of buyingAsset to receive
351
- * @property {{selling: string, buying: string, path: string[]}} [directTrade] - Equivalent direct path_payment trade estimate
352
- * @property {string} [error] - Error details from the server (for failed quotes)
349
+ * @typedef {'quote'|'paused'|'progress'|'finished'|'error'} StellarBrokerClientEvent - Event type generated by the client
353
350
  */
package/src/estimate.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import errors, {StellarBrokerError} from './errors.js'
2
2
  import {validateQuoteRequest} from './quote-request.js'
3
+ import {QuoteResult} from './quote-result.js'
3
4
 
4
5
  /**
5
6
  * Request single swap quote estimate without trading
6
- * @param {SwapQuoteParams} params
7
- * @return {Promise<SwapQuoteResult>}
7
+ * @param {QuoteParams} params
8
+ * @return {Promise<QuoteResult>}
8
9
  */
9
10
  export async function estimateSwap(params) {
10
11
  const query = Object.entries(validateQuoteRequest(params))
@@ -15,7 +16,7 @@ export async function estimateSwap(params) {
15
16
  const res = await fetch(url).then(r => r.json())
16
17
  if (res.status !== 'success')
17
18
  throw errors.quoteError(res.error || 'Quote not available')
18
- return res
19
+ return new QuoteResult(res)
19
20
  } catch (e) {
20
21
  if (e instanceof StellarBrokerError)
21
22
  throw e
package/src/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import {Asset, FeeBumpTransaction, TransactionI} from "@stellar/stellar-sdk";
1
+ import {Asset, TransactionI} from "@stellar/stellar-sdk";
2
2
 
3
3
  /**
4
4
  * Client for StellarBroker service
5
5
  */
6
- export default class StellarBrokerClient {
6
+ export class StellarBrokerClient {
7
7
  /**
8
8
  * @param {ClientInitializationParams} params
9
9
  */
@@ -20,9 +20,9 @@ export default class StellarBrokerClient {
20
20
  */
21
21
  readonly network: string;
22
22
  /**
23
- * @type {ClientStatus}
23
+ * @type {ClientSessionStatus}
24
24
  */
25
- readonly status: ClientStatus;
25
+ readonly status: ClientSessionStatus;
26
26
 
27
27
  /**
28
28
  * Connect to the StellarBroker server
@@ -32,9 +32,9 @@ export default class StellarBrokerClient {
32
32
 
33
33
  /**
34
34
  * Request swap quote
35
- * @param {SwapQuoteParams} params - Quote parameters
35
+ * @param {QuoteParams} params - Quote parameters
36
36
  */
37
- quote(params: SwapQuoteParams): void;
37
+ quote(params: QuoteParams): void;
38
38
 
39
39
  /**
40
40
  * Stop quotation/trading
@@ -43,8 +43,9 @@ export default 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
@@ -76,9 +77,12 @@ export default class StellarBrokerClient {
76
77
  /**
77
78
  * Request single swap quote estimate without trading
78
79
  */
79
- export function estimateSwap(params: SwapQuoteParams): Promise<SwapQuoteResult>;
80
+ export function estimateSwap(params: QuoteParams): Promise<QuoteResult>;
80
81
 
81
- export interface SwapQuoteParams {
82
+ /**
83
+ * Quote request parameters provided by the client
84
+ */
85
+ export interface QuoteParams {
82
86
  /**
83
87
  * Asset to sell
84
88
  */
@@ -97,64 +101,97 @@ export interface SwapQuoteParams {
97
101
  slippageTolerance?: number
98
102
  }
99
103
 
100
-
101
104
  export interface ClientInitializationParams {
105
+ /**
106
+ * Authorization method, either account secret key or an authorization callback
107
+ */
108
+ authorization: ClientAuthorizationParams;
102
109
  /**
103
110
  * Trader account address
104
111
  */
105
- account: string;
112
+ account?: string;
106
113
  /**
107
114
  * Partner key
108
115
  */
109
116
  partnerKey?: string;
110
- /**
111
- * Authorization method, either account secret key or an authorization callback
112
- */
113
- authorization: ClientAuthorizationParams;
114
117
  }
115
118
 
116
- export type ClientStatus = "disconnected" | "ready" | "quote" | "trade";
119
+ /**
120
+ * Current client session status
121
+ */
122
+ export type ClientSessionStatus = "disconnected" | "ready" | "quote" | "trade";
117
123
 
124
+ /**
125
+ * Event type generated by the client
126
+ */
118
127
  export type StellarBrokerClientEvent = "quote" | "progress" | "paused" | "finished" | "error";
119
128
 
120
- export interface SwapQuoteResult {
129
+ /**
130
+ * Result status code returned by the server
131
+ */
132
+ export type QuoteResultStatus = "unfeasible" | "rejected" | "success";
133
+
134
+ /**
135
+ * Quote received from the server
136
+ */
137
+ export interface QuoteResult {
121
138
  /**
122
- * Quote timestamp formatted as ISO date
139
+ * Quote timestamp
123
140
  */
124
- ts: string,
141
+ readonly ts: Date,
142
+ /**
143
+ * Quote status code
144
+ */
145
+ readonly status: QuoteResultStatus;
125
146
  /**
126
147
  * Asset to sell
127
148
  */
128
- sellingAsset: string;
149
+ readonly sellingAsset: string;
129
150
  /**
130
151
  * Asset to buy
131
152
  */
132
- buyingAsset: string;
153
+ readonly buyingAsset: string;
133
154
  /**
134
155
  * Swap slippage tolerance
135
156
  */
136
- slippageTolerance: number;
157
+ readonly slippageTolerance: number;
137
158
  /**
138
159
  * Amount of the selling asset
139
160
  */
140
- sellingAmount: string;
161
+ readonly sellingAmount: string;
141
162
  /**
142
163
  * Estimated amount of buyingAsset to receive
143
164
  */
144
- estimatedBuyingAmount?: string;
165
+ readonly estimatedBuyingAmount?: string;
145
166
  /**
146
167
  * Equivalent direct path_payment trade estimate
147
168
  */
148
- directTrade?: SwapQuoteDirectTradeResult;
169
+ readonly directTrade?: QuoteDirectTradeResult;
170
+ /**
171
+ * Difference between quoted price and estimated direct trade
172
+ */
173
+ readonly profit: string;
149
174
  /**
150
175
  * Error details from the server (for failed quotes)
151
176
  */
152
- error?: string;
177
+ readonly error?: string;
153
178
  }
154
179
 
155
- export interface SwapQuoteDirectTradeResult {
180
+ /**
181
+ * Emulated single `path_payment` trades result corresponding to the quote
182
+ */
183
+ export interface QuoteDirectTradeResult {
184
+ /**
185
+ * Estimated amount to sell
186
+ */
156
187
  selling: string;
188
+ /**
189
+ * Estimated amount to buy
190
+ */
157
191
  buying: string;
192
+ /**
193
+ * Conversion path
194
+ */
158
195
  path: string[];
159
196
  }
160
197
 
@@ -219,5 +256,5 @@ export class Mediator {
219
256
  /**
220
257
  * Dispose mediator account
221
258
  */
222
- dispose(address:string): Promise<void>;
259
+ dispose(address: string): Promise<void>;
223
260
  }
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import StellarBrokerClient from './client.js'
1
+ import {StellarBrokerClient} from './client.js'
2
2
  export * from './estimate.js'
3
3
  export * from './mediator.js'
4
4
 
5
- export default StellarBrokerClient
5
+ export {StellarBrokerClient}
@@ -2,7 +2,7 @@ import errors from './errors.js'
2
2
  import {parseAsset} from './asset.js'
3
3
 
4
4
  /**
5
- * @typedef {object} SwapQuoteParams
5
+ * @typedef {object} QuoteParams - Quote request parameters provided by the client
6
6
  * @property {string} sellingAsset - Asset to sell
7
7
  * @property {string} buyingAsset - Asset to buy
8
8
  * @property {string} [sellingAmount] - Amount of selling asset
@@ -10,8 +10,8 @@ import {parseAsset} from './asset.js'
10
10
  */
11
11
 
12
12
  /**
13
- * @param {SwapQuoteParams} params
14
- * @return {SwapQuoteParams}
13
+ * @param {QuoteParams} params
14
+ * @return {QuoteParams}
15
15
  */
16
16
  export function validateQuoteRequest(params) {
17
17
  const {
@@ -0,0 +1,93 @@
1
+ import {fromStroops, toStroops} from './stroops.js'
2
+
3
+ /**
4
+ * @typedef {'unfeasible'|'rejected'|'success'} QuoteResultStatus - Result status code returned by the server
5
+ */
6
+
7
+ /**
8
+ * @typedef {Object} QuoteDirectTradeResult - Emulated single `path_payment` trades result corresponding to the quote
9
+ * @property {string} selling - Estimated amount to sell
10
+ * @property {string} buying - Estimated amount to buy
11
+ * @property {string[]} path - Conversion path
12
+ */
13
+
14
+ /**
15
+ * Quote received from the server
16
+ */
17
+ export class QuoteResult {
18
+ /**
19
+ * @param {{}} result
20
+ * @package
21
+ */
22
+ constructor(result) {
23
+ Object.assign(this, result)
24
+ if (result.estimatedBuyingAmount && result.directTrade) {
25
+ const profit = toStroops(result.estimatedBuyingAmount) - toStroops(result.directTrade.buying)
26
+ if (profit > 0) {
27
+ this.profit = fromStroops(profit)
28
+ }
29
+ }
30
+ this.ts = new Date()
31
+ }
32
+
33
+ /**
34
+ * Quote timestamp
35
+ * @type {Date}
36
+ * @readonly
37
+ */
38
+ ts
39
+ /**
40
+ * Quote status code
41
+ * @type {QuoteResultStatus}
42
+ * @readonly
43
+ */
44
+ status
45
+ /**
46
+ * Asset to sell
47
+ * @type {string}
48
+ * @readonly
49
+ */
50
+ sellingAsset
51
+ /**
52
+ * Asset to buy
53
+ * @type {string}
54
+ * @readonly
55
+ */
56
+ buyingAsset
57
+ /**
58
+ * Swap slippage tolerance
59
+ * @type {number}
60
+ * @readonly
61
+ */
62
+ slippageTolerance
63
+ /**
64
+ * Amount of the selling asset
65
+ * @type {string}
66
+ * @readonly
67
+ */
68
+ sellingAmount
69
+ /**
70
+ * Estimated amount of buyingAsset to receive
71
+ * @type {string}
72
+ * @readonly
73
+ */
74
+ estimatedBuyingAmount
75
+ /**
76
+ * Equivalent direct path_payment trade estimate
77
+ * @type {QuoteDirectTradeResult}
78
+ * @readonly
79
+ */
80
+ directTrade
81
+ /**
82
+ * Difference between quoted price and estimated direct trade
83
+ * @type {string}
84
+ * @readonly
85
+ */
86
+ profit = '0'
87
+ /**
88
+ * Error details from the server (for rejected quote requests)
89
+ * @type {string}
90
+ * @readonly
91
+ */
92
+ error
93
+ }
@@ -1,6 +1,8 @@
1
- import {hash, Keypair, nativeToScVal, StrKey, TransactionBuilder, xdr} from '@stellar/stellar-sdk'
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
- const networkId = hash(Buffer.from(tx.networkPassphrase))
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,