@stellar-broker/client 0.1.0 → 0.3.0

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
@@ -10,6 +10,10 @@ npm i @stellar-broker/client
10
10
 
11
11
  ### Example - Connect and trade in DirectFlow mode
12
12
 
13
+ This flow is suitable for the majority of scenarios when a user plans to swap assets.
14
+ It might consume more resources on the client side since the client keeps an open connection and constantly receives
15
+ price quote updates from the server.
16
+
13
17
  ```js
14
18
  import StellarBrokerClient from '@stellar-broker/client'
15
19
 
@@ -19,8 +23,7 @@ const client = new StellarBrokerClient({partnerKey: '<your_partner_key>'})
19
23
  //subscribe to the quote notifications
20
24
  client.on('quote', e => {
21
25
  console.log('Received quote from the server', e.quote)
22
- /*
23
- {
26
+ /*{
24
27
  "status": "success",
25
28
  "sellingAsset": "USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
26
29
  "buyingAsset": "XLM",
@@ -34,29 +37,24 @@ client.on('quote', e => {
34
37
  "sellingAmount": "10",
35
38
  "estimatedBuyingAmount": "100.688121",
36
39
  "ts": "2024-08-13T23:13:21.275Z"
37
- }
38
- */
40
+ }*/
39
41
  })
40
42
 
41
43
  client.on('finished', e => {
42
44
  console.log('Trade finished', e.result)
43
- /*
44
- {
45
+ /*{
45
46
  "status": "success",
46
47
  "sold": "10",
47
48
  "bought": "100.6704597"
48
- }
49
- */
49
+ }*/
50
50
  })
51
51
 
52
52
  client.on('progress', e => {
53
53
  console.log('Progress', e.status)
54
- /*
55
- {
54
+ /*{
56
55
  "sold": "10",
57
56
  "bought": "100.6704597"
58
- }
59
- */
57
+ }*/
60
58
  })
61
59
 
62
60
  client.on('error', e => {
@@ -70,23 +68,40 @@ client.connect() //only once
70
68
 
71
69
  //request a price quote
72
70
  client.quote({
73
- destination: '<account_pubkey>', //trader account pubkey
74
71
  sellingAsset: 'xlm',
75
72
  buyingAsset: 'USD-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
76
- sellingAmount: '1000', //1000.0000000
73
+ sellingAmount: '1000', //1000.0000000 XLM
77
74
  slippageTolerance: 0.02 //2%
78
75
  })
79
76
 
80
77
  //once the quote received from the server, we can confirm the quote
81
- client.confirmQuote(async (tx) => { //async signing callback - implement custom logic here
78
+ async function signTx(tx) { //async signing callback - implement custom logic here
82
79
  const kp = Keypair.fromSecret('<account_secret>')
83
80
  tx.sign(kp)
84
81
  return tx
85
- })
82
+ }
83
+ client.confirmQuote('<account_address>', signTx) //provide trader account address
86
84
 
87
85
  //trade can be interrupted from the client by calling
88
86
  client.stop()
89
87
 
90
88
  //cleanup resources and close connection once the trading has been finished
91
89
  client.close()
90
+ ```
91
+
92
+ ### Example - Get swap estimate without trading
93
+
94
+ Swap estimate may be handy in scenarios when the client has no intention to trade or receive price quote updates in
95
+ streaming mode, and just want to get a single price quote.
96
+
97
+ ```js
98
+ import {estimateSwap} from '@stellar-broker/client'
99
+
100
+ estimateSwap({
101
+ sellingAsset: 'xlm',
102
+ buyingAsset: 'USD-GDK2GNB4Q6FKNW2GNJIQFARI4RMSHV5DN5G4BBXX2F24RT5I4QT7TWZ7',
103
+ sellingAmount: '1000',
104
+ slippageTolerance: 0.02 //2%
105
+ })
106
+
92
107
  ```
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("@stellar/stellar-base")):"function"==typeof define&&define.amd?define(["@stellar/stellar-base"],e):"object"==typeof exports?exports.stellarbroker=e(require("@stellar/stellar-base")):t.stellarbroker=e(t["@stellar/stellar-base"])}(this,(t=>(()=>{"use strict";var e={755:e=>{e.exports=t}},s={};function i(t){var o=s[t];if(void 0!==o)return o.exports;var r=s[t]={exports:{}};return e[t](r,r.exports,i),r.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var o={};i.d(o,{default:()=>f});var r=i(755);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"),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,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"),failedToSignTx:()=>new n(22,"Failed to sign received transaction"),unsupportedEventType:t=>new n(31,"Unknown event type: "+t),serverError:t=>new n(101,"Server error: "+t)};function u(t,e,s){const i=new CustomEvent(t,{detail:e});return i[s||t]=e,i}function l(t,e){if("string"==typeof t){if("XLM"===t||"xlm"===t||"native"===t)return"XLM";if(t.includes(":")){const[s,i]=t.split(":");return c(s),h(i,e),s+"-"+i}if(t.includes("-")){const[s,i]=t.split("-");return c(s),h(i,e),t}}throw a.invalidQuoteParam(e,"Invalid asset")}function c(t,e){if(!/^[a-zA-Z0-9]{1,12}$/.test(t))throw a.invalidQuoteParam(e,"Invalid asset code: "+(void 0===t?"missing":t));return t}function h(t,e,s=!1){if(void 0!==t||!s){if(!r.StrKey.isValidEd25519PublicKey(t))throw a.invalidQuoteParam(e,"Invalid account address: "+(void 0===t?"missing":t));return t}}function d(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 p(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}const g=["quote","finished","progress","error"];function v(t){return!t instanceof Promise?t?Promise.resolve(t):Promise.reject():t}const f=class{constructor(t){if(!t.network)throw a.invalidInitParam("network");this.partnerKey=t.partnerKey,this.emitter=new EventTarget,this.network=r.Networks[t.network.toUpperCase()]||t.network,this.flow=t.flow||"direct"}status="disconnected";socket;emitter;origin="https://api.stellar.broker";network="";quoteRequest;lastQuote;tradeQuote;partnerKey;authorization;get source(){return this.quoteRequest?.destination}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=()=>{console.log("Connection closed"),this.status="disconnected"},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.onSocketOpen&&this.onSocketOpen();break;case"quote":this.lastQuote=e.quote,this.lastQuote.ts=new Date,this.emitter.dispatchEvent(u("quote",this.lastQuote));break;case"tx":if("trade"!==this.status)return void console.log("Received tx in non-trading state",this.status,e);this.processTxRequest(e);break;case"stop":this.emitter.dispatchEvent(u("finished",{status:e.status,sold:e.sold,bought:e.bought},"result")),this.status="ready";break;case"progress":this.emitter.dispatchEvent(u("progress",{sold:e.sold,bought:e.bought},"status"));break;case"ping":this.heartbeat(),this.send({type:"pong"});break;case"error":this.stop(),this.emitter.dispatchEvent(u("error","Server error: "+e.error));break;default:console.log("Unknown message type: "+e.type)}}quote(t){if("trade"===this.status)throw a.tradeInProgress();this.quoteRequest=function(t){const{destination:e,sellingAsset:s,selling_asset:i,buyingAsset:o,buying_asset:r,sellingAmount:n,selling_amount:u,buyingAmount:c,buying_amount:g,slippageTolerance:v,slippage_tolerance:f,...m}=t,w={sellingAsset:l(s||i,"sellingAsset"),buyingAsset:l(o||r,"buyingAsset"),sellingAmount:d(n||u,"sellingAmount"),buyingAmount:d(c||g,"buyingAmount"),slippageTolerance:p(v||f||.02,"slippageTolerance"),flow:"direct"};if(e&&(w.destination=h(e,"destination",!0)),w.buyingAsset===w.sellingAsset)throw a.invalidQuoteParam("buyingAsset","Buying asset can't be the same as selling asset");if(void 0===w.buyingAmount&&void 0===w.sellingAmount)throw a.invalidQuoteParam("sellingAmount",'Either "buyingAmount" or "sellingAmount" parameter is required');if(void 0!==w.buyingAmount&&void 0!==w.sellingAmount)throw a.invalidQuoteParam("sellingAmount",'Parameters "buyingAmount" and "sellingAmount" are mutually exclusive');return Object.assign(w,m),w}(t),this.status="quote",this.connect().then((()=>{this.send({type:"quote",...this.quoteRequest})}))}stop(){"trade"!==this.status&&"quote"!==this.status||(this.send({type:"stop"}),this.tradeQuote=void 0,this.status="ready")}confirmQuote(t){if("quote"!==this.status)throw a.tradeInProgress();if(!this.lastQuote)throw a.quoteNotSet();if(new Date-this.lastQuote.ts>7e3)throw a.quoteExpired();if("success"!==this.lastQuote.status)throw a.quoteError(this.lastQuote.error||"quote not available");if("string"==typeof t)try{this.authorization=r.Keypair.fromSecret(t)}catch(t){throw a.invalidAuthorizationParam()}else"function"==typeof t&&(this.authorization=t);this.tradeQuote=this.lastQuote,this.send({type:"trade"}),this.status="trade"}processTxRequest(t){let e;try{e=r.TransactionBuilder.fromXDR(t.xdr,this.network)}catch(t){throw a.invalidSwapTx()}if(!this.validateTransaction(e))throw a.invalidSwapTx();this.authorizeTx(e,t.networkFee).then((e=>{if(!e||e.signatures.length<1)return console.error(`Transaction ${t.hash} not signed`);this.send({type:"tx",hash:t.hash,xdr:e.toXDR()})})).catch((t=>{throw console.error(t),a.failedToSignTx()}))}async authorizeTx(t,e){this.authorization instanceof r.Keypair?t.sign(this.authorization):t=await v(this.authorization(t));let s=r.TransactionBuilder.buildFeeBumpTransaction(this.source,e,t,this.network);return this.authorization instanceof r.Keypair?s.sign(this.authorization):s=await v(this.authorization(s)),s}validateTransaction(t){for(let e of t.operations){if("pathPaymentStrictSend"!==e.type&&"pathPaymentStrictReceive"!==e.type)return!1;if(e.destination!==this.source){if("pathPaymentStrictSend"!==e.type)return!1;if(e.source&&e.source!==this.source)return!1}else if(e.source&&e.source!==e.destination||e.destination!==this.source)return!1}return!0}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()}),11e3)}on(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.addEventListener(t,e)}once(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.addEventListener(t,e,{once:!0})}off(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.removeEventListener(t,e)}close(){try{this.socket.close()}catch(t){}}};return o.default})()));
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("@stellar/stellar-base")):"function"==typeof define&&define.amd?define(["@stellar/stellar-base"],e):"object"==typeof exports?exports.stellarbroker=e(require("@stellar/stellar-base")):t.stellarbroker=e(t["@stellar/stellar-base"])}(this,(t=>(()=>{"use strict";var e={755:e=>{e.exports=t}},s={};function i(t){var o=s[t];if(void 0!==o)return o.exports;var r=s[t]={exports:{}};return e[t](r,r.exports,i),r.exports}i.d=(t,e)=>{for(var s in e)i.o(e,s)&&!i.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:e[s]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var o={};i.d(o,{default:()=>w});var r=i(755);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"),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,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"),failedToSignTx:()=>new n(22,"Failed to sign received transaction"),unsupportedEventType:t=>new n(31,"Unknown event type: "+t),serverError:t=>new n(101,"Server error: "+t)};function u(t,e,s){const i=new CustomEvent(t,{detail:e});return i[s||t]=e,i}function c(t){const{destination:e,sellingAsset:s,selling_asset:i,buyingAsset:o,buying_asset:r,sellingAmount:n,selling_amount:u,buyingAmount:c,buying_amount:h,slippageTolerance:d,slippage_tolerance:m,...g}=t,v={sellingAsset:l(s||i,"sellingAsset"),buyingAsset:l(o||r,"buyingAsset"),sellingAmount:p(n||u,"sellingAmount"),buyingAmount:p(c||h,"buyingAmount"),slippageTolerance:f(d||m||.02,"slippageTolerance"),flow:"direct"};if(v.buyingAsset===v.sellingAsset)throw a.invalidQuoteParam("buyingAsset","Buying asset can't be the same as selling asset");if(void 0===v.buyingAmount&&void 0===v.sellingAmount)throw a.invalidQuoteParam("sellingAmount",'Either "buyingAmount" or "sellingAmount" parameter is required');if(void 0!==v.buyingAmount&&void 0!==v.sellingAmount)throw a.invalidQuoteParam("sellingAmount",'Parameters "buyingAmount" and "sellingAmount" are mutually exclusive');return Object.assign(v,g),Object.freeze(v),v}function l(t,e){if("string"==typeof t){if("XLM"===t||"xlm"===t||"native"===t)return"XLM";if(t.includes(":")){const[s,i]=t.split(":");return h(s),d(i,e),s+"-"+i}if(t.includes("-")){const[s,i]=t.split("-");return h(s),d(i,e),t}}throw a.invalidQuoteParam(e,"Invalid asset")}function h(t,e){if(!/^[a-zA-Z0-9]{1,12}$/.test(t))throw a.invalidQuoteParam(e,"Invalid asset code: "+(t||"missing"));return t}function d(t,e){if(!t||!r.StrKey.isValidEd25519PublicKey(t))throw a.invalidQuoteParam(e,"Invalid account address: "+(t||"missing"));return t}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}class m{constructor(t){this.partnerKey=t.partnerKey,this.emitter=new EventTarget,this.network=r.Networks[(t.network||"PUBLIC").toUpperCase()]||t.network,this.flow=t.flow||"direct"}status="disconnected";socket;emitter;origin="https://api.stellar.broker";network="";quoteRequest;lastQuote;tradeQuote;partnerKey;authorization;source;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=()=>{console.log("Connection closed"),this.status="disconnected"},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.onSocketOpen&&this.onSocketOpen();break;case"quote":this.lastQuote=e.quote,this.lastQuote.ts=new Date,this.emitter.dispatchEvent(u("quote",this.lastQuote));break;case"tx":if("trade"!==this.status)return void console.log("Received tx in non-trading state",this.status,e);this.processTxRequest(e);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,this.source=void 0;break;case"progress":this.emitter.dispatchEvent(u("progress",{sold:e.sold,bought:e.bought},"status"));break;case"ping":this.heartbeat(),this.send({type:"pong"});break;case"error":this.stop(),this.emitter.dispatchEvent(u("error","Server error: "+e.error));break;default:console.log("Unknown message type: "+e.type)}}quote(t){if("trade"===this.status)throw a.tradeInProgress();this.quoteRequest=c(t),this.status="quote",this.connect().then((()=>{this.send({type:"quote",...this.quoteRequest})}))}stop(){"trade"!==this.status&&"quote"!==this.status||(this.send({type:"stop"}),this.tradeQuote=void 0,this.source=void 0,this.status="ready")}confirmQuote(t,e){if("quote"!==this.status)throw a.tradeInProgress();if(!this.lastQuote)throw a.quoteNotSet();if(new Date-this.lastQuote.ts>7e3)throw a.quoteExpired();if("success"!==this.lastQuote.status)throw a.quoteError(this.lastQuote.error||"quote not available");if(!t||!r.StrKey.isValidEd25519PublicKey(t))throw a.invalidQuoteParam("account","Invalid trader account address: "+(t||"missing"));if("string"==typeof e)try{this.authorization=r.Keypair.fromSecret(e)}catch(t){throw a.invalidAuthorizationParam()}else"function"==typeof e&&(this.authorization=e);this.tradeQuote=this.lastQuote,this.source=t,this.send({type:"trade",account:t}),this.status="trade"}processTxRequest(t){let e;try{e=r.TransactionBuilder.fromXDR(t.xdr,this.network)}catch(t){throw a.invalidSwapTx()}if(!this.validateTransaction(e))throw a.invalidSwapTx();this.authorizeTx(e,t.networkFee).then((e=>{if(!e||e.signatures.length<1)return console.error(`Transaction ${t.hash} not signed`);this.send({type:"tx",hash:t.hash,xdr:e.toXDR()})})).catch((t=>{throw console.error(t),a.failedToSignTx()}))}async authorizeTx(t,e){this.authorization instanceof r.Keypair?t.sign(this.authorization):t=await v(this.authorization(t));let s=r.TransactionBuilder.buildFeeBumpTransaction(this.source,e,t,this.network);return this.authorization instanceof r.Keypair?s.sign(this.authorization):s=await v(this.authorization(s)),s}validateTransaction(t){for(let e of t.operations){if("pathPaymentStrictSend"!==e.type&&"pathPaymentStrictReceive"!==e.type)return!1;if(e.destination!==this.source){if("pathPaymentStrictSend"!==e.type)return!1;if(e.source&&e.source!==this.source)return!1}else if(e.source&&e.source!==e.destination||e.destination!==this.source)return!1}return!0}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()}),11e3)}on(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.addEventListener(t,e)}once(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.addEventListener(t,e,{once:!0})}off(t,e){if(!g.includes(t))throw a.unsupportedEventType(t);this.emitter.removeEventListener(t,e)}close(){try{this.socket.close()}catch(t){}}}const g=["quote","finished","progress","error"];function v(t){return!t instanceof Promise?t?Promise.resolve(t):Promise.reject():t}m.estimateSwap=async function(t){const e=Object.entries(c(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 w=m;return o.default})()));
2
2
  //# sourceMappingURL=stellarbroker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stellarbroker.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,EAAQG,QAAQ,0BACR,mBAAXC,QAAyBA,OAAOC,IAC9CD,OAAO,CAAC,yBAA0BJ,GACR,iBAAZC,QACdA,QAAuB,cAAID,EAAQG,QAAQ,0BAE3CJ,EAAoB,cAAIC,EAAQD,EAAK,yBACtC,CATD,CASGO,MAAOC,G,kCCTVL,EAAOD,QAAUM,C,GCCbC,EAA2B,CAAC,EAGhC,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,CCrBAQ,EAAoBK,EAAI,CAACb,EAASc,KACjC,IAAI,IAAIC,KAAOD,EACXN,EAAoBQ,EAAEF,EAAYC,KAASP,EAAoBQ,EAAEhB,EAASe,IAC5EE,OAAOC,eAAelB,EAASe,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDP,EAAoBQ,EAAI,CAACK,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,G,6CCA3E,MAAMI,UAA2BC,MACpCC,WAAAA,CAAYC,EAAMC,GACdC,MAAMD,GACNzB,KAAKwB,KAAOA,CAChB,CAOAA,KAAO,EAGX,MAoCA,EApCe,CACXG,iBAAiBC,GACN,IAAIP,EAAmB,EAAG,6BAA6BO,MAElEC,0BAAyBA,IACd,IAAIR,EAAmB,EAAG,yDAErCS,YAAWA,IACA,IAAIT,EAAmB,GAAI,6BAEtCU,aAAYA,IACD,IAAIV,EAAmB,GAAI,uBAEtCW,WAAWP,GACA,IAAIJ,EAAmB,GAAI,0BAA4BI,GAElEQ,kBAAiBA,CAACC,EAAkBC,IACzB,IAAId,EAAmB,GAAI,qCAAqCa,OAAsBC,KAEjGC,gBAAeA,IACJ,IAAIf,EAAmB,GAAI,kDAEtCgB,cAAaA,IACF,IAAIhB,EAAmB,GAAI,qCAEtCiB,eAAcA,IACH,IAAIjB,EAAmB,GAAI,uCAEtCkB,qBAAqBC,GACV,IAAInB,EAAmB,GAAI,uBAAyBmB,GAE/DC,YAAYhB,GACD,IAAIJ,EAAmB,IAAK,iBAAmBI,ICxCvD,SAASiB,EAAWF,EAAMG,EAAMjC,GACnC,MAAMkC,EAAM,IAAIC,YAAYL,EAAM,CAACM,OAAQH,IAE3C,OADAC,EAAIlC,GAAO8B,GAAQG,EACZC,CACX,CCiCA,SAASG,EAAWC,EAAOC,GACvB,GAAqB,iBAAVD,EAAoB,CAC3B,GAAc,QAAVA,GAA6B,QAAVA,GAA6B,WAAVA,EACtC,MAAO,MACX,GAAIA,EAAME,SAAS,KAAM,CACrB,MAAO1B,EAAM2B,GAAUH,EAAMI,MAAM,KAGnC,OAFAC,EAAa7B,GACb8B,EAAgBH,EAAQF,GACjBzB,EAAO,IAAM2B,CACxB,CACA,GAAIH,EAAME,SAAS,KAAM,CACrB,MAAO1B,EAAM2B,GAAUH,EAAMI,MAAM,KAGnC,OAFAC,EAAa7B,GACb8B,EAAgBH,EAAQF,GACjBD,CACX,CACJ,CACA,MAAMO,EAAOtB,kBAAkBgB,EAAW,gBAC9C,CAEA,SAASI,EAAa7B,EAAMyB,GACxB,IAAK,sBAAsBO,KAAKhC,GAC5B,MAAM+B,EAAOtB,kBAAkBgB,EAAW,6BAAmC3C,IAATkB,EAAqB,UAAYA,IACzG,OAAOA,CACX,CAEA,SAAS8B,EAAgBG,EAAS7B,EAAO8B,GAAW,GAChD,QAAgBpD,IAAZmD,IAAyBC,EAA7B,CAEA,IAAKC,EAAAA,OAAOC,wBAAwBH,GAChC,MAAMF,EAAOtB,kBAAkBL,EAAO,kCAA2CtB,IAAZmD,EAAwB,UAAYA,IAC7G,OAAOA,CAHa,CAIxB,CAEA,SAASI,EAAYC,EAAQb,GACzB,QAAe3C,IAAXwD,EACA,OACJ,MAAMC,EAASC,WAAWF,GAC1B,GAAIG,MAAMF,IAAWA,GAAU,EAC3B,MAAMR,EAAOtB,kBAAkBgB,EAAW,yBAA2Ba,GACzE,OAAOA,CACX,CAEA,SAASI,EAAuBC,EAAKlB,GACjC,MAAMmB,EAAYJ,WAAWG,GAC7B,GAAIF,MAAMG,GACN,MAAMb,EAAOtB,kBAAkBgB,EAAW,+CAC9C,GAAImB,EAAY,EACZ,MAAMb,EAAOtB,kBAAkBgB,EAAW,wDAC9C,GAAImB,EAAY,GACZ,MAAMb,EAAOtB,kBAAkBgB,EAAW,+DAC9C,OAAOmB,CACX,CCmRO,MAAMC,EAAsB,CAAC,QAAS,WAAY,WAAY,SAErE,SAASC,EAAcC,GACnB,OAAKA,aAAsBC,QACnBD,EACOC,QAAQC,QAAQF,GACpBC,QAAQE,SAEZH,CACX,CCzXA,QDGe,MAIXhD,WAAAA,CAAYoD,GACR,IAAKA,EAAOC,QACR,MAAMrB,EAAO5B,iBAAiB,WAClC3B,KAAK6E,WAAaF,EAAOE,WACzB7E,KAAK8E,QAAU,IAAIC,YACnB/E,KAAK4E,QAAUI,EAAAA,SAASL,EAAOC,QAAQK,gBAAkBN,EAAOC,QAChE5E,KAAKkF,KAAOP,EAAOO,MAAQ,QAC/B,CAMAC,OAAS,eAKTC,OAKAN,QAKAO,OAAS,6BAMTT,QAAU,GAMVU,aAMAC,UAKAC,WAMAX,WAMAY,cAMA,UAAIC,GACA,OAAO1F,KAAKsF,cAAcK,WAC9B,CAMAC,OAAAA,GACI,OAAI5F,KAAKoF,QAAQS,aAAeC,UAAUC,KAC/BvB,QAAQC,QAAQzE,OAE3BA,KAAKoF,OAAS,IAAIU,UAAU9F,KAAKqF,OAAS,eAAiBW,mBAAmBhG,KAAK6E,aACnF7E,KAAKoF,OAAOa,UAAYjG,KAAKkG,eAAeC,KAAKnG,MACjDA,KAAKoF,OAAOgB,QAAU,KAClBC,QAAQC,IAAI,qBACZtG,KAAKmF,OAAS,cAAc,EAGhCnF,KAAKoF,OAAOmB,QAAUC,GAAKH,QAAQI,MAAMD,GAElC,IAAIhC,SAAQ,CAACkC,EAAShC,KACzB,MAAMiC,EAAoBC,YAAW,IAAMlC,EAAO1E,OAAO,KACzDA,KAAK6G,aAAe,KAChB7G,KAAKmF,OAAS,QACdnF,KAAK8G,YACL9G,KAAK6G,kBAAevG,EACpByG,aAAaJ,GACbD,EAAQ1G,KAAK,CAChB,IAET,CAOAkG,cAAAA,CAAezE,GACX,MAAMuF,EAAMC,KAAKC,MAAMzF,EAAQkB,MAC/B,OAAQqE,EAAIxE,MACR,IAAK,YACGxC,KAAK6G,cACL7G,KAAK6G,eAET,MACJ,IAAK,QACD7G,KAAKuF,UAAYyB,EAAIG,MACrBnH,KAAKuF,UAAU6B,GAAK,IAAIC,KAExBrH,KAAK8E,QAAQwC,cAAc5E,EAAW,QAAS1C,KAAKuF,YACpD,MACJ,IAAK,KACD,GAAoB,UAAhBvF,KAAKmF,OAEL,YADAkB,QAAQC,IAAI,mCAAoCtG,KAAKmF,OAAQ6B,GAGjEhH,KAAKuH,iBAAiBP,GACtB,MACJ,IAAK,OACDhH,KAAK8E,QAAQwC,cAAc5E,EAAW,WAAY,CAC9CyC,OAAQ6B,EAAI7B,OACZqC,KAAMR,EAAIQ,KACVC,OAAQT,EAAIS,QACb,WACHzH,KAAKmF,OAAS,QACd,MACJ,IAAK,WACDnF,KAAK8E,QAAQwC,cAAc5E,EAAW,WAAY,CAC9C8E,KAAMR,EAAIQ,KACVC,OAAQT,EAAIS,QACb,WACH,MACJ,IAAK,OACDzH,KAAK8G,YACL9G,KAAK0H,KAAK,CAAClF,KAAM,SACjB,MACJ,IAAK,QACDxC,KAAK2H,OACL3H,KAAK8E,QAAQwC,cAAc5E,EAAW,QAAS,iBAAmBsE,EAAIP,QACtE,MACJ,QACIJ,QAAQC,IAAI,yBAA2BU,EAAIxE,MAGvD,CAMA2E,KAAAA,CAAMxC,GACF,GAAoB,UAAhB3E,KAAKmF,OACL,MAAM5B,EAAOnB,kBACjBpC,KAAKsF,aD3JN,SAA8BX,GACjC,MAAM,YACFgB,EAAW,aAAEiC,EAAY,cAAEC,EAAa,YAAEC,EAAW,aAAEC,EAAY,cAAEC,EAAa,eAAEC,EAAc,aAClGC,EAAY,cAAEC,EAAa,kBAAEC,EAAiB,mBAAEC,KAAuBC,GACvE3D,EACE4D,EAAM,CACRX,aAAc7E,EAAW6E,GAAgBC,EAAe,gBACxDC,YAAa/E,EAAW+E,GAAeC,EAAc,eACrDC,cAAenE,EAAYmE,GAAiBC,EAAgB,iBAC5DC,aAAcrE,EAAYqE,GAAgBC,EAAe,gBACzDC,kBAAmBlE,EAAuBkE,GAAqBC,GAAsB,IAAM,qBAC3FnD,KAAM,UAKV,GAHIS,IACA4C,EAAI5C,YAAcrC,EAAgBqC,EAAa,eAAe,IAE9D4C,EAAIT,cAAgBS,EAAIX,aACxB,MAAMrE,EAAOtB,kBAAkB,cAAe,mDAClD,QAAyB3B,IAArBiI,EAAIL,mBAAoD5H,IAAtBiI,EAAIP,cACtC,MAAMzE,EAAOtB,kBAAkB,gBAAiB,kEACpD,QAAyB3B,IAArBiI,EAAIL,mBAAoD5H,IAAtBiI,EAAIP,cACtC,MAAMzE,EAAOtB,kBAAkB,gBAAiB,wEAEpD,OADArB,OAAO4H,OAAOD,EAAKD,GACZC,CACX,CCmI4BE,CAAqB9D,GACzC3E,KAAKmF,OAAS,QACdnF,KAAK4F,UACA8C,MAAK,KACF1I,KAAK0H,KAAK,CACNlF,KAAM,WACHxC,KAAKsF,cACV,GAEd,CAKAqC,IAAAA,GACwB,UAAhB3H,KAAKmF,QAAsC,UAAhBnF,KAAKmF,SAEpCnF,KAAK0H,KAAK,CACNlF,KAAM,SAEVxC,KAAKwF,gBAAalF,EAClBN,KAAKmF,OAAS,QAClB,CAMAwD,YAAAA,CAAalD,GACT,GAAoB,UAAhBzF,KAAKmF,OACL,MAAM5B,EAAOnB,kBACjB,IAAKpC,KAAKuF,UACN,MAAMhC,EAAOzB,cACjB,GAAK,IAAIuF,KAASrH,KAAKuF,UAAU6B,GAAM,IACnC,MAAM7D,EAAOxB,eACjB,GAA8B,YAA1B/B,KAAKuF,UAAUJ,OACf,MAAM5B,EAAOvB,WAAWhC,KAAKuF,UAAUkB,OAAS,uBACpD,GAA6B,iBAAlBhB,EACP,IACIzF,KAAKyF,cAAgBmD,EAAAA,QAAQC,WAAWpD,EAC5C,CAAE,MAAOe,GACL,MAAMjD,EAAO1B,2BACjB,KACgC,mBAAlB4D,IACdzF,KAAKyF,cAAgBA,GAEzBzF,KAAKwF,WAAaxF,KAAKuF,UACvBvF,KAAK0H,KAAK,CACNlF,KAAM,UAEVxC,KAAKmF,OAAS,OAClB,CAMAoC,gBAAAA,CAAiBP,GAEb,IAAI8B,EACJ,IACIA,EAAKC,EAAAA,mBAAmBC,QAAQhC,EAAIiC,IAAKjJ,KAAK4E,QAClD,CAAE,MAAO4B,GACL,MAAMjD,EAAOlB,eACjB,CAEA,IAAKrC,KAAKkJ,oBAAoBJ,GAC1B,MAAMvF,EAAOlB,gBAEjBrC,KAAKmJ,YAAYL,EAAI9B,EAAIoC,YACpBV,MAAKI,IACF,IAAKA,GAAMA,EAAGO,WAAWC,OAAS,EAC9B,OAAOjD,QAAQI,MAAM,eAAeO,EAAIuC,mBAE5CvJ,KAAK0H,KAAK,CACNlF,KAAM,KACN+G,KAAMvC,EAAIuC,KACVN,IAAKH,EAAGU,SACV,IAELC,OAAMjD,IAEH,MADAH,QAAQI,MAAMD,GACRjD,EAAOjB,gBAAgB,GAEzC,CAQA,iBAAM6G,CAAYL,EAAIM,GAEdpJ,KAAKyF,yBAAyBmD,EAAAA,QAC9BE,EAAGY,KAAK1J,KAAKyF,eAEbqD,QAAWxE,EAActE,KAAKyF,cAAcqD,IAGhD,IAAIa,EAAUZ,EAAAA,mBAAmBa,wBAAwB5J,KAAK0F,OAAQ0D,EAAYN,EAAI9I,KAAK4E,SAO3F,OALI5E,KAAKyF,yBAAyBmD,EAAAA,QAC9Be,EAAQD,KAAK1J,KAAKyF,eAElBkE,QAAgBrF,EAActE,KAAKyF,cAAckE,IAE9CA,CACX,CAMAT,mBAAAA,CAAoBJ,GAMhB,IAAK,IAAIe,KAAQf,EAAGgB,WAAY,CAC5B,GAAkB,0BAAdD,EAAKrH,MAAkD,6BAAdqH,EAAKrH,KAC9C,OAAO,EAEX,GADcqH,EAAKlE,cAAgB3F,KAAK0F,OAC7B,CACP,GAAkB,0BAAdmE,EAAKrH,KACL,OAAO,EACX,GAAKqH,EAAKnE,QAAUmE,EAAKnE,SAAW1F,KAAK0F,OACrC,OAAO,CACf,MACI,GAAKmE,EAAKnE,QAAUmE,EAAKnE,SAAWmE,EAAKlE,aAAgBkE,EAAKlE,cAAgB3F,KAAK0F,OAC/E,OAAO,CAEnB,CAEA,OAAO,CACX,CAMAgC,IAAAA,CAAK/E,GACD3C,KAAKoF,OAAOsC,KAAKT,KAAK8C,UAAUpH,GACpC,CAEAmE,SAAAA,GACIC,aAAa/G,KAAKgK,aAClBhK,KAAKgK,YAAcpD,YAAW,KAC1BP,QAAQ4D,KAAK,mCACbjK,KAAKoF,OAAO8E,OAAO,GACpB,KACP,CAOAC,EAAAA,CAAG3H,EAAM4H,GACL,IAAK/F,EAAoBnB,SAASV,GAC9B,MAAMe,EAAOhB,qBAAqBC,GACtCxC,KAAK8E,QAAQuF,iBAAiB7H,EAAM4H,EACxC,CAOAE,IAAAA,CAAK9H,EAAM4H,GACP,IAAK/F,EAAoBnB,SAASV,GAC9B,MAAMe,EAAOhB,qBAAqBC,GACtCxC,KAAK8E,QAAQuF,iBAAiB7H,EAAM4H,EAAU,CAACE,MAAM,GACzD,CAOAC,GAAAA,CAAI/H,EAAM4H,GACN,IAAK/F,EAAoBnB,SAASV,GAC9B,MAAMe,EAAOhB,qBAAqBC,GACtCxC,KAAK8E,QAAQ0F,oBAAoBhI,EAAM4H,EAC3C,CAKAF,KAAAA,GACI,IACIlK,KAAKoF,OAAO8E,OAChB,CAAE,MAAO1D,GACT,CACJ,G","sources":["webpack://stellarbroker/webpack/universalModuleDefinition","webpack://stellarbroker/external umd \"@stellar/stellar-base\"","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/quote-request.js","webpack://stellarbroker/./src/client.js","webpack://stellarbroker/./src/index.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"@stellar/stellar-base\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"@stellar/stellar-base\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"stellarbroker\"] = factory(require(\"@stellar/stellar-base\"));\n\telse\n\t\troot[\"stellarbroker\"] = factory(root[\"@stellar/stellar-base\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__755__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__755__;","// 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 {\r\n constructor(code, message) {\r\n super(message)\r\n this.code = code\r\n }\r\n\r\n /**\r\n * Numeric error code\r\n * @type {number}\r\n * @readonly\r\n */\r\n code = 0\r\n}\r\n\r\nconst errors = {\r\n invalidInitParam(param) {\r\n return new StellarBrokerError(1, `Invalid parameter value: \"${param}\"`)\r\n },\r\n invalidAuthorizationParam() {\r\n return new StellarBrokerError(2, 'Invalid authorization secret key or callback provided')\r\n },\r\n quoteNotSet() {\r\n return new StellarBrokerError(11, 'Price quote not available')\r\n },\r\n quoteExpired() {\r\n return new StellarBrokerError(12, 'Price quote expired')\r\n },\r\n quoteError(message) {\r\n return new StellarBrokerError(13, 'Price quotation error: ' + message)\r\n },\r\n invalidQuoteParam(invalidParamName, details) {\r\n return new StellarBrokerError(14, `Invalid quote request parameter: \"${invalidParamName}\". ${details}`)\r\n },\r\n tradeInProgress() {\r\n return new StellarBrokerError(20, 'Cannot change quote while trade is in progress')\r\n },\r\n invalidSwapTx() {\r\n return new StellarBrokerError(21, 'Invalid swap transaction received')\r\n },\r\n failedToSignTx() {\r\n return new StellarBrokerError(22, 'Failed to sign received transaction')\r\n },\r\n unsupportedEventType(type) {\r\n return new StellarBrokerError(31, 'Unknown event type: ' + type)\r\n },\r\n serverError(message) {\r\n return new StellarBrokerError(101, 'Server error: ' + message)\r\n }\r\n}\r\n\r\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 {StrKey} from '@stellar/stellar-base'\r\nimport errors from './errors.js'\r\n\r\n/**\r\n * @typedef {object} SwapQuoteParams\r\n * @property {string} sellingAsset - Asset to sell\r\n * @property {string} buyingAsset - Asset to buy\r\n * @property {string} [sellingAmount] - Amount of selling asset\r\n * @property {string} [buyingAmount] - Amount of buying asset\r\n * @property {number} [slippageTolerance] - Swap slippage tolerance (0.02 by default - 2%)\r\n * @property {string} [destination] - Trader account address\r\n */\r\n\r\n/**\r\n * @param {SwapQuoteParams} params\r\n * @return {SwapQuoteParams}\r\n */\r\nexport function validateQuoteRequest(params) {\r\n const {\r\n destination, sellingAsset, selling_asset, buyingAsset, buying_asset, sellingAmount, selling_amount,\r\n buyingAmount, buying_amount, slippageTolerance, slippage_tolerance, ...other\r\n } = params\r\n const res = {\r\n sellingAsset: parseAsset(sellingAsset || selling_asset, 'sellingAsset'),\r\n buyingAsset: parseAsset(buyingAsset || buying_asset, 'buyingAsset'),\r\n sellingAmount: parseAmount(sellingAmount || selling_amount, 'sellingAmount'),\r\n buyingAmount: parseAmount(buyingAmount || buying_amount, 'buyingAmount'),\r\n slippageTolerance: parseSlippageTolerance(slippageTolerance || slippage_tolerance || 0.02, 'slippageTolerance'),\r\n flow: 'direct'\r\n }\r\n if (destination) {\r\n res.destination = validateAccount(destination, 'destination', true)\r\n }\r\n if (res.buyingAsset === res.sellingAsset)\r\n throw errors.invalidQuoteParam('buyingAsset', 'Buying asset can\\'t be the same as selling asset')\r\n if (res.buyingAmount === undefined && res.sellingAmount === undefined)\r\n throw errors.invalidQuoteParam('sellingAmount', 'Either \"buyingAmount\" or \"sellingAmount\" parameter is required')\r\n if (res.buyingAmount !== undefined && res.sellingAmount !== undefined)\r\n throw errors.invalidQuoteParam('sellingAmount', 'Parameters \"buyingAmount\" and \"sellingAmount\" are mutually exclusive')\r\n Object.assign(res, other) //add remaining optional params\r\n return res\r\n}\r\n\r\nfunction parseAsset(asset, parameter) {\r\n if (typeof asset === 'string') {\r\n if (asset === 'XLM' || asset === 'xlm' || asset === 'native')\r\n return 'XLM'\r\n if (asset.includes(':')) {\r\n const [code, issuer] = asset.split(':')\r\n validateCode(code)\r\n validateAccount(issuer, parameter)\r\n return code + '-' + issuer\r\n }\r\n if (asset.includes('-')) {\r\n const [code, issuer] = asset.split('-')\r\n validateCode(code)\r\n validateAccount(issuer, parameter)\r\n return asset\r\n }\r\n }\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset')\r\n}\r\n\r\nfunction validateCode(code, parameter) {\r\n if (!/^[a-zA-Z0-9]{1,12}$/.test(code))\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset code: ' + (code === undefined ? 'missing' : code))\r\n return code\r\n}\r\n\r\nfunction validateAccount(account, param, optional = false) {\r\n if (account === undefined && optional)\r\n return undefined\r\n if (!StrKey.isValidEd25519PublicKey(account))\r\n throw errors.invalidQuoteParam(param, 'Invalid account address: ' + (account === undefined ? 'missing' : account))\r\n return account\r\n}\r\n\r\nfunction parseAmount(amount, parameter) {\r\n if (amount === undefined)\r\n return undefined\r\n const parsed = parseFloat(amount)\r\n if (isNaN(parsed) || parsed <= 0)\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset amount: ' + amount)\r\n return amount\r\n}\r\n\r\nfunction parseSlippageTolerance(src, parameter) {\r\n const tolerance = parseFloat(src)\r\n if (isNaN(tolerance))\r\n throw errors.invalidQuoteParam(parameter, 'Invalid slippage tolerance, number expected')\r\n if (tolerance < 0)\r\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too small, expected value >= 0')\r\n if (tolerance > 0.5)\r\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too large, expected value < 0.5 (50%)')\r\n return tolerance\r\n}","import {Networks, TransactionBuilder, Keypair, FeeBumpTransaction} from '@stellar/stellar-base'\r\nimport errors from './errors.js'\r\nimport {buildEvent} from './events.js'\r\nimport {validateQuoteRequest} from './quote-request.js'\r\n\r\nexport default class StellarBrokerClient {\r\n /**\r\n * @param {ClientInitializationParams} params\r\n */\r\n constructor(params) {\r\n if (!params.network)\r\n throw errors.invalidInitParam('network')\r\n this.partnerKey = params.partnerKey\r\n this.emitter = new EventTarget()\r\n this.network = Networks[params.network.toUpperCase()] || params.network\r\n this.flow = params.flow || 'direct'\r\n }\r\n\r\n /**\r\n * @type {ClientStatus}\r\n * @readonly\r\n */\r\n status = 'disconnected'\r\n /**\r\n * @type {WebSocket}\r\n * @private\r\n */\r\n socket\r\n /**\r\n * @type {EventTarget}\r\n * @private\r\n */\r\n emitter\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n origin = 'https://api.stellar.broker'\r\n /**\r\n * Stellar network passphrase\r\n * @type {string}\r\n * @readonly\r\n */\r\n network = ''\r\n /**\r\n * Last quote request submitted by the client\r\n * @type {SwapQuoteParams}\r\n * @private\r\n */\r\n quoteRequest\r\n /**\r\n * Last quote received from the sever\r\n * @type {SwapQuoteResult}\r\n * @readonly\r\n */\r\n lastQuote\r\n /**\r\n * Current quote accepted for the trading\r\n * @private\r\n */\r\n tradeQuote\r\n /**\r\n * API key of the partner\r\n * @type {string}\r\n * @private\r\n */\r\n partnerKey\r\n /**\r\n * Account secret key (for direct swap flow)\r\n * @type {Keypair|ClientAuthorizationCallback}\r\n * @private\r\n */\r\n authorization\r\n\r\n /**\r\n * Trader account public key\r\n * @type {string}\r\n */\r\n get source() {\r\n return this.quoteRequest?.destination\r\n }\r\n\r\n /**\r\n * Connect to the StellarBroker server\r\n * @return {Promise<StellarBrokerClient>}\r\n */\r\n connect() {\r\n if (this.socket?.readyState === WebSocket.OPEN)\r\n return Promise.resolve(this) //already opened\r\n\r\n this.socket = new WebSocket(this.origin + '/ws?partner=' + encodeURIComponent(this.partnerKey))\r\n this.socket.onmessage = this.processMessage.bind(this)\r\n this.socket.onclose = () => {\r\n console.log('Connection closed')\r\n this.status = 'disconnected'\r\n }\r\n\r\n this.socket.onerror = e => console.error(e)\r\n\r\n return new Promise((confirm, reject) => { //TODO: use 'once'\r\n const expirationTimeout = setTimeout(() => reject(this), 2000) // 2s timeout\r\n this.onSocketOpen = () => {\r\n this.status = 'ready'\r\n this.heartbeat()\r\n this.onSocketOpen = undefined\r\n clearTimeout(expirationTimeout)\r\n confirm(this)\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Process incoming message\r\n * @param message\r\n * @private\r\n */\r\n processMessage(message) {\r\n const raw = JSON.parse(message.data)\r\n switch (raw.type) {\r\n case 'connected':\r\n if (this.onSocketOpen) {\r\n this.onSocketOpen()\r\n }\r\n break\r\n case 'quote':\r\n this.lastQuote = raw.quote\r\n this.lastQuote.ts = new Date()\r\n //send event to the client app\r\n this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))\r\n break\r\n case 'tx':\r\n if (this.status !== 'trade') {\r\n console.log('Received tx in non-trading state', this.status, raw)\r\n return //skip unless trading is in progress\r\n }\r\n this.processTxRequest(raw)\r\n break\r\n case 'stop':\r\n this.emitter.dispatchEvent(buildEvent('finished', {\r\n status: raw.status,\r\n sold: raw.sold,\r\n bought: raw.bought\r\n }, 'result'))\r\n this.status = 'ready'\r\n break\r\n case 'progress':\r\n this.emitter.dispatchEvent(buildEvent('progress', {\r\n sold: raw.sold,\r\n bought: raw.bought\r\n }, 'status'))\r\n break\r\n case 'ping':\r\n this.heartbeat()\r\n this.send({type: 'pong'})\r\n break\r\n case 'error':\r\n this.stop()\r\n this.emitter.dispatchEvent(buildEvent('error', 'Server error: ' + raw.error))\r\n break\r\n default:\r\n console.log('Unknown message type: ' + raw.type)\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * Request swap quote\r\n * @param {SwapQuoteParams} params - Quote parameters\r\n */\r\n quote(params) {\r\n if (this.status === 'trade')\r\n throw errors.tradeInProgress()\r\n this.quoteRequest = validateQuoteRequest(params)\r\n this.status = 'quote'\r\n this.connect()\r\n .then(() => {\r\n this.send({\r\n type: 'quote',\r\n ...this.quoteRequest\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Stop quotation/trading\r\n */\r\n stop() {\r\n if (this.status !== 'trade' && this.status !== 'quote')\r\n return\r\n this.send({\r\n type: 'stop'\r\n })\r\n this.tradeQuote = undefined\r\n this.status = 'ready'\r\n }\r\n\r\n /**\r\n * Confirm current quote and start trading\r\n * @param {string|ClientAuthorizationCallback} authorization - Authorization method, either account secret key or an authorization callback\r\n */\r\n confirmQuote(authorization) {\r\n if (this.status !== 'quote')\r\n throw errors.tradeInProgress()\r\n if (!this.lastQuote)\r\n throw errors.quoteNotSet()\r\n if ((new Date() - this.lastQuote.ts) > 7000) //do not allow stale quotes quoted more than 7s ago\r\n throw errors.quoteExpired()\r\n if (this.lastQuote.status !== 'success')\r\n throw errors.quoteError(this.lastQuote.error || 'quote not available')\r\n if (typeof authorization === 'string') {\r\n try {\r\n this.authorization = Keypair.fromSecret(authorization)\r\n } catch (e) {\r\n throw errors.invalidAuthorizationParam()\r\n }\r\n } else if (typeof authorization === 'function') {\r\n this.authorization = authorization\r\n }\r\n this.tradeQuote = this.lastQuote\r\n this.send({\r\n type: 'trade'\r\n })\r\n this.status = 'trade'\r\n }\r\n\r\n /**\r\n * @param {{xdr: string, hash: string}} raw\r\n * @private\r\n */\r\n processTxRequest(raw) {\r\n //parse incoming transaction\r\n let tx\r\n try {\r\n tx = TransactionBuilder.fromXDR(raw.xdr, this.network)\r\n } catch (e) {\r\n throw errors.invalidSwapTx()\r\n }\r\n //check that transaction is correct\r\n if (!this.validateTransaction(tx))\r\n throw errors.invalidSwapTx()\r\n //sign transaction\r\n this.authorizeTx(tx, raw.networkFee)\r\n .then(tx => {\r\n if (!tx || tx.signatures.length < 1)\r\n return console.error(`Transaction ${raw.hash} not signed`)\r\n //respond with signed transaction\r\n this.send({\r\n type: 'tx',\r\n hash: raw.hash,\r\n xdr: tx.toXDR()\r\n })\r\n })\r\n .catch(e => {\r\n console.error(e)\r\n throw errors.failedToSignTx()\r\n })\r\n }\r\n\r\n /**\r\n * @param {Transaction} tx\r\n * @param {string} networkFee\r\n * @return {Promise<FeeBumpTransaction>}\r\n * @private\r\n */\r\n async authorizeTx(tx, networkFee) {\r\n //sign tx\r\n if (this.authorization instanceof Keypair) {\r\n tx.sign(this.authorization)\r\n } else {\r\n tx = await ensurePromise(this.authorization(tx))\r\n }\r\n //wrap with fee bump\r\n let wrapped = TransactionBuilder.buildFeeBumpTransaction(this.source, networkFee, tx, this.network)\r\n //sign fee bump wrapper tx\r\n if (this.authorization instanceof Keypair) {\r\n wrapped.sign(this.authorization)\r\n } else {\r\n wrapped = await ensurePromise(this.authorization(wrapped))\r\n }\r\n return wrapped\r\n }\r\n\r\n /**\r\n * @param {Transaction} tx\r\n * @throws {StellarBrokerError} Invalid swap transaction received\r\n */\r\n validateTransaction(tx) {\r\n /*if (!(tx instanceof FeeBumpTransaction))\r\n return false\r\n const {innerTransaction} = tx\r\n if (tx.feeSource !== this.source)\r\n return false*/\r\n for (let swap of tx.operations) {\r\n if (swap.type !== 'pathPaymentStrictSend' && swap.type !== 'pathPaymentStrictReceive')\r\n return false\r\n const isFee = swap.destination !== this.source\r\n if (isFee) {\r\n if (swap.type !== 'pathPaymentStrictSend')\r\n return false\r\n if ((swap.source && swap.source !== this.source))\r\n return false\r\n } else {\r\n if ((swap.source && swap.source !== swap.destination) || swap.destination !== this.source)\r\n return false\r\n }\r\n }\r\n //TODO: check assets and amounts\r\n return true\r\n }\r\n\r\n /**\r\n * @param {{}} data\r\n * @private\r\n */\r\n send(data) {\r\n this.socket.send(JSON.stringify(data))\r\n }\r\n\r\n heartbeat() {\r\n clearTimeout(this.pingHandler)\r\n this.pingHandler = setTimeout(() => {\r\n console.warn('Lost connection with the server')\r\n this.socket.close()\r\n }, 11_000) // 11 seconds heartbeat timeout\r\n }\r\n\r\n /**\r\n * Add event listener\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n on(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.addEventListener(type, callback)\r\n }\r\n\r\n /**\r\n * Add event listener that will be executed once\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n once(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.addEventListener(type, callback, {once: true})\r\n }\r\n\r\n /**\r\n * Remove event listener\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n off(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.removeEventListener(type, callback)\r\n }\r\n\r\n /**\r\n * Close underlying connection and finalize the client\r\n */\r\n close() {\r\n try {\r\n this.socket.close()\r\n } catch (e) {\r\n }\r\n }\r\n}\r\n\r\nexport const StellarBrokerEvents = ['quote', 'finished', 'progress', 'error']\r\n\r\nfunction ensurePromise(callResult) {\r\n if (!callResult instanceof Promise) {\r\n if (callResult)\r\n return Promise.resolve(callResult)\r\n return Promise.reject()\r\n }\r\n return callResult\r\n}\r\n\r\n/**\r\n * @typedef {object} ClientInitializationParams\r\n * @property {string} [network] - Stellar network identifier or passphrase\r\n * @property {string} [partnerKey] - Partner key\r\n * @property {SwapFlowMode} [flow] - Swap flow mode\r\n */\r\n\r\n/**\r\n * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientStatus\r\n */\r\n\r\n/**\r\n * @typedef {'quote'|'finished'|'progress'|'error'} StellarBrokerClientEvent\r\n */\r\n\r\n/**\r\n * @typedef {'direct'} SwapFlowMode\r\n */\r\n\r\n/**\r\n * @typedef {object} SwapQuoteResult\r\n * @property {string} sellingAsset\r\n * @property {string} buyingAsset\r\n * @property {number} slippageTolerance\r\n * @property {string} destination\r\n * @property {number} ledger\r\n * @property {string} [sellingAmount]\r\n * @property {string} [estimatedBuyingAmount]\r\n * @property {string} [buyingAmount]\r\n * @property {string} [estimatedSellingAmount]\r\n * @property {{selling: string, buying: string, path: string[]}} [directTrade]\r\n */\r\n\r\n/**\r\n * @typedef {function(FeeBumpTransaction):Promise<FeeBumpTransaction>} ClientAuthorizationCallback\r\n */","import StellarBrokerClient from './client.js'\r\n\r\nexport default StellarBrokerClient"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__755__","__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","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","optional","StrKey","isValidEd25519PublicKey","parseAmount","amount","parsed","parseFloat","isNaN","parseSlippageTolerance","src","tolerance","StellarBrokerEvents","ensurePromise","callResult","Promise","resolve","reject","params","network","partnerKey","emitter","EventTarget","Networks","toUpperCase","flow","status","socket","origin","quoteRequest","lastQuote","tradeQuote","authorization","source","destination","connect","readyState","WebSocket","OPEN","encodeURIComponent","onmessage","processMessage","bind","onclose","console","log","onerror","e","error","confirm","expirationTimeout","setTimeout","onSocketOpen","heartbeat","clearTimeout","raw","JSON","parse","quote","ts","Date","dispatchEvent","processTxRequest","sold","bought","send","stop","sellingAsset","selling_asset","buyingAsset","buying_asset","sellingAmount","selling_amount","buyingAmount","buying_amount","slippageTolerance","slippage_tolerance","other","res","assign","validateQuoteRequest","then","confirmQuote","Keypair","fromSecret","tx","TransactionBuilder","fromXDR","xdr","validateTransaction","authorizeTx","networkFee","signatures","length","hash","toXDR","catch","sign","wrapped","buildFeeBumpTransaction","swap","operations","stringify","pingHandler","warn","close","on","callback","addEventListener","once","off","removeEventListener"],"sourceRoot":""}
1
+ {"version":3,"file":"stellarbroker.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,EAAQG,QAAQ,0BACR,mBAAXC,QAAyBA,OAAOC,IAC9CD,OAAO,CAAC,yBAA0BJ,GACR,iBAAZC,QACdA,QAAuB,cAAID,EAAQG,QAAQ,0BAE3CJ,EAAoB,cAAIC,EAAQD,EAAK,yBACtC,CATD,CASGO,MAAOC,G,kCCTVL,EAAOD,QAAUM,C,GCCbC,EAA2B,CAAC,EAGhC,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,CCrBAQ,EAAoBK,EAAI,CAACb,EAASc,KACjC,IAAI,IAAIC,KAAOD,EACXN,EAAoBQ,EAAEF,EAAYC,KAASP,EAAoBQ,EAAEhB,EAASe,IAC5EE,OAAOC,eAAelB,EAASe,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,IAE1E,ECNDP,EAAoBQ,EAAI,CAACK,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,G,6CCA3E,MAAMI,UAA2BC,MACpCC,WAAAA,CAAYC,EAAMC,GACdC,MAAMD,GACNzB,KAAKwB,KAAOA,CAChB,CAOAA,KAAO,EAGX,MAoCA,EApCe,CACXG,iBAAiBC,GACN,IAAIP,EAAmB,EAAG,6BAA6BO,MAElEC,0BAAyBA,IACd,IAAIR,EAAmB,EAAG,yDAErCS,YAAWA,IACA,IAAIT,EAAmB,GAAI,6BAEtCU,aAAYA,IACD,IAAIV,EAAmB,GAAI,uBAEtCW,WAAWP,GACA,IAAIJ,EAAmB,GAAI,0BAA4BI,GAElEQ,kBAAiBA,CAACC,EAAkBC,IACzB,IAAId,EAAmB,GAAI,qCAAqCa,OAAsBC,KAEjGC,gBAAeA,IACJ,IAAIf,EAAmB,GAAI,kDAEtCgB,cAAaA,IACF,IAAIhB,EAAmB,GAAI,qCAEtCiB,eAAcA,IACH,IAAIjB,EAAmB,GAAI,uCAEtCkB,qBAAqBC,GACV,IAAInB,EAAmB,GAAI,uBAAyBmB,GAE/DC,YAAYhB,GACD,IAAIJ,EAAmB,IAAK,iBAAmBI,ICxCvD,SAASiB,EAAWF,EAAMG,EAAMjC,GACnC,MAAMkC,EAAM,IAAIC,YAAYL,EAAM,CAACM,OAAQH,IAE3C,OADAC,EAAIlC,GAAO8B,GAAQG,EACZC,CACX,CCOO,SAASG,EAAqBC,GACjC,MAAM,YACFC,EAAW,aAAEC,EAAY,cAAEC,EAAa,YAAEC,EAAW,aAAEC,EAAY,cAAEC,EAAa,eAAEC,EAAc,aAClGC,EAAY,cAAEC,EAAa,kBAAEC,EAAiB,mBAAEC,KAAuBC,GACvEZ,EACEa,EAAM,CACRX,aAAcY,EAAWZ,GAAgBC,EAAe,gBACxDC,YAAaU,EAAWV,GAAeC,EAAc,eACrDC,cAAeS,EAAYT,GAAiBC,EAAgB,iBAC5DC,aAAcO,EAAYP,GAAgBC,EAAe,gBACzDC,kBAAmBM,EAAuBN,GAAqBC,GAAsB,IAAM,qBAC3FM,KAAM,UAEV,GAAIJ,EAAIT,cAAgBS,EAAIX,aACxB,MAAMgB,EAAOjC,kBAAkB,cAAe,mDAClD,QAAyB3B,IAArBuD,EAAIL,mBAAoDlD,IAAtBuD,EAAIP,cACtC,MAAMY,EAAOjC,kBAAkB,gBAAiB,kEACpD,QAAyB3B,IAArBuD,EAAIL,mBAAoDlD,IAAtBuD,EAAIP,cACtC,MAAMY,EAAOjC,kBAAkB,gBAAiB,wEAGpD,OAFArB,OAAOuD,OAAON,EAAKD,GACnBhD,OAAOwD,OAAOP,GACPA,CACX,CAEA,SAASC,EAAWO,EAAOC,GACvB,GAAqB,iBAAVD,EAAoB,CAC3B,GAAc,QAAVA,GAA6B,QAAVA,GAA6B,WAAVA,EACtC,MAAO,MACX,GAAIA,EAAME,SAAS,KAAM,CACrB,MAAO/C,EAAMgD,GAAUH,EAAMI,MAAM,KAGnC,OAFAC,EAAalD,GACbmD,EAAgBH,EAAQF,GACjB9C,EAAO,IAAMgD,CACxB,CACA,GAAIH,EAAME,SAAS,KAAM,CACrB,MAAO/C,EAAMgD,GAAUH,EAAMI,MAAM,KAGnC,OAFAC,EAAalD,GACbmD,EAAgBH,EAAQF,GACjBD,CACX,CACJ,CACA,MAAMH,EAAOjC,kBAAkBqC,EAAW,gBAC9C,CAEA,SAASI,EAAalD,EAAM8C,GACxB,IAAK,sBAAsBM,KAAKpD,GAC5B,MAAM0C,EAAOjC,kBAAkBqC,EAAW,wBAA2B9C,GAAO,YAChF,OAAOA,CACX,CAEA,SAASmD,EAAgBE,EAASjD,GAC9B,IAAKiD,IAAYC,EAAAA,OAAOC,wBAAwBF,GAC5C,MAAMX,EAAOjC,kBAAkBL,EAAO,6BAAgCiD,GAAU,YACpF,OAAOA,CACX,CAEA,SAASd,EAAYiB,EAAQV,GACzB,QAAehE,IAAX0E,EACA,OACJ,MAAMC,EAASC,WAAWF,GAC1B,GAAIG,MAAMF,IAAWA,GAAU,EAC3B,MAAMf,EAAOjC,kBAAkBqC,EAAW,yBAA2BU,GACzE,OAAOA,CACX,CAEA,SAAShB,EAAuBoB,EAAKd,GACjC,MAAMe,EAAYH,WAAWE,GAC7B,GAAID,MAAME,GACN,MAAMnB,EAAOjC,kBAAkBqC,EAAW,+CAC9C,GAAIe,EAAY,EACZ,MAAMnB,EAAOjC,kBAAkBqC,EAAW,wDAC9C,GAAIe,EAAY,GACZ,MAAMnB,EAAOjC,kBAAkBqC,EAAW,+DAC9C,OAAOe,CACX,CCtFe,MAAMC,EAIjB/D,WAAAA,CAAYyB,GACRhD,KAAKuF,WAAavC,EAAOuC,WACzBvF,KAAKwF,QAAU,IAAIC,YACnBzF,KAAK0F,QAAUC,EAAAA,UAAU3C,EAAO0C,SAAW,UAAUE,gBAAkB5C,EAAO0C,QAC9E1F,KAAKiE,KAAOjB,EAAOiB,MAAQ,QAC/B,CAMA4B,OAAS,eAKTC,OAKAN,QAKAO,OAAS,6BAMTL,QAAU,GAMVM,aAMAC,UAKAC,WAMAX,WAMAY,cAOAC,OAMAC,OAAAA,GACI,OAAIrG,KAAK8F,QAAQQ,aAAeC,UAAUC,KAC/BC,QAAQC,QAAQ1G,OAE3BA,KAAK8F,OAAS,IAAIS,UAAUvG,KAAK+F,OAAS,eAAiBY,mBAAmB3G,KAAKuF,aACnFvF,KAAK8F,OAAOc,UAAY5G,KAAK6G,eAAeC,KAAK9G,MACjDA,KAAK8F,OAAOiB,QAAU,KAClBC,QAAQC,IAAI,qBACZjH,KAAK6F,OAAS,cAAc,EAGhC7F,KAAK8F,OAAOoB,QAAUC,GAAKH,QAAQI,MAAMD,GAElC,IAAIV,SAAQ,CAACY,EAASC,KACzB,MAAMC,EAAoBC,YAAW,IAAMF,EAAOtH,OAAO,KACzDA,KAAKyH,aAAe,KAChBzH,KAAK6F,OAAS,QACd7F,KAAK0H,YACL1H,KAAKyH,kBAAenH,EACpBqH,aAAaJ,GACbF,EAAQrH,KAAK,CAChB,IAET,CAOA6G,cAAAA,CAAepF,GACX,MAAMmG,EAAMC,KAAKC,MAAMrG,EAAQkB,MAC/B,OAAQiF,EAAIpF,MACR,IAAK,YACGxC,KAAKyH,cACLzH,KAAKyH,eAET,MACJ,IAAK,QACDzH,KAAKiG,UAAY2B,EAAIG,MACrB/H,KAAKiG,UAAU+B,GAAK,IAAIC,KAExBjI,KAAKwF,QAAQ0C,cAAcxF,EAAW,QAAS1C,KAAKiG,YACpD,MACJ,IAAK,KACD,GAAoB,UAAhBjG,KAAK6F,OAEL,YADAmB,QAAQC,IAAI,mCAAoCjH,KAAK6F,OAAQ+B,GAGjE5H,KAAKmI,iBAAiBP,GACtB,MACJ,IAAK,OACD5H,KAAKwF,QAAQ0C,cAAcxF,EAAW,WAAY,CAC9CmD,OAAQ+B,EAAI/B,OACZuC,KAAMR,EAAIQ,KACVC,OAAQT,EAAIS,QACb,WACHrI,KAAK6F,OAAS,QACd7F,KAAKkG,gBAAa5F,EAClBN,KAAKoG,YAAS9F,EACd,MACJ,IAAK,WACDN,KAAKwF,QAAQ0C,cAAcxF,EAAW,WAAY,CAC9C0F,KAAMR,EAAIQ,KACVC,OAAQT,EAAIS,QACb,WACH,MACJ,IAAK,OACDrI,KAAK0H,YACL1H,KAAKsI,KAAK,CAAC9F,KAAM,SACjB,MACJ,IAAK,QACDxC,KAAKuI,OACLvI,KAAKwF,QAAQ0C,cAAcxF,EAAW,QAAS,iBAAmBkF,EAAIR,QACtE,MACJ,QACIJ,QAAQC,IAAI,yBAA2BW,EAAIpF,MAGvD,CAMAuF,KAAAA,CAAM/E,GACF,GAAoB,UAAhBhD,KAAK6F,OACL,MAAM3B,EAAO9B,kBACjBpC,KAAKgG,aAAejD,EAAqBC,GACzChD,KAAK6F,OAAS,QACd7F,KAAKqG,UACAmC,MAAK,KACFxI,KAAKsI,KAAK,CACN9F,KAAM,WACHxC,KAAKgG,cACV,GAEd,CAKAuC,IAAAA,GACwB,UAAhBvI,KAAK6F,QAAsC,UAAhB7F,KAAK6F,SAEpC7F,KAAKsI,KAAK,CACN9F,KAAM,SAEVxC,KAAKkG,gBAAa5F,EAClBN,KAAKoG,YAAS9F,EACdN,KAAK6F,OAAS,QAClB,CAOA4C,YAAAA,CAAa5D,EAASsB,GAClB,GAAoB,UAAhBnG,KAAK6F,OACL,MAAM3B,EAAO9B,kBACjB,IAAKpC,KAAKiG,UACN,MAAM/B,EAAOpC,cACjB,GAAK,IAAImG,KAASjI,KAAKiG,UAAU+B,GAAM,IACnC,MAAM9D,EAAOnC,eACjB,GAA8B,YAA1B/B,KAAKiG,UAAUJ,OACf,MAAM3B,EAAOlC,WAAWhC,KAAKiG,UAAUmB,OAAS,uBACpD,IAAKvC,IAAYC,EAAAA,OAAOC,wBAAwBF,GAC5C,MAAMX,EAAOjC,kBAAkB,UAAW,oCAAuC4C,GAAU,YAC/F,GAA6B,iBAAlBsB,EACP,IACInG,KAAKmG,cAAgBuC,EAAAA,QAAQC,WAAWxC,EAC5C,CAAE,MAAOgB,GACL,MAAMjD,EAAOrC,2BACjB,KACgC,mBAAlBsE,IACdnG,KAAKmG,cAAgBA,GAEzBnG,KAAKkG,WAAalG,KAAKiG,UACvBjG,KAAKoG,OAASvB,EACd7E,KAAKsI,KAAK,CACN9F,KAAM,QACNqC,YAEJ7E,KAAK6F,OAAS,OAClB,CAMAsC,gBAAAA,CAAiBP,GAEb,IAAIgB,EACJ,IACIA,EAAKC,EAAAA,mBAAmBC,QAAQlB,EAAImB,IAAK/I,KAAK0F,QAClD,CAAE,MAAOyB,GACL,MAAMjD,EAAO7B,eACjB,CAEA,IAAKrC,KAAKgJ,oBAAoBJ,GAC1B,MAAM1E,EAAO7B,gBAEjBrC,KAAKiJ,YAAYL,EAAIhB,EAAIsB,YACpBV,MAAKI,IACF,IAAKA,GAAMA,EAAGO,WAAWC,OAAS,EAC9B,OAAOpC,QAAQI,MAAM,eAAeQ,EAAIyB,mBAE5CrJ,KAAKsI,KAAK,CACN9F,KAAM,KACN6G,KAAMzB,EAAIyB,KACVN,IAAKH,EAAGU,SACV,IAELC,OAAMpC,IAEH,MADAH,QAAQI,MAAMD,GACRjD,EAAO5B,gBAAgB,GAEzC,CAQA,iBAAM2G,CAAYL,EAAIM,GAEdlJ,KAAKmG,yBAAyBuC,EAAAA,QAC9BE,EAAGY,KAAKxJ,KAAKmG,eAEbyC,QAAWa,EAAczJ,KAAKmG,cAAcyC,IAGhD,IAAIc,EAAUb,EAAAA,mBAAmBc,wBAAwB3J,KAAKoG,OAAQ8C,EAAYN,EAAI5I,KAAK0F,SAO3F,OALI1F,KAAKmG,yBAAyBuC,EAAAA,QAC9BgB,EAAQF,KAAKxJ,KAAKmG,eAElBuD,QAAgBD,EAAczJ,KAAKmG,cAAcuD,IAE9CA,CACX,CAMAV,mBAAAA,CAAoBJ,GAMhB,IAAK,IAAIgB,KAAQhB,EAAGiB,WAAY,CAC5B,GAAkB,0BAAdD,EAAKpH,MAAkD,6BAAdoH,EAAKpH,KAC9C,OAAO,EAEX,GADcoH,EAAK3G,cAAgBjD,KAAKoG,OAC7B,CACP,GAAkB,0BAAdwD,EAAKpH,KACL,OAAO,EACX,GAAKoH,EAAKxD,QAAUwD,EAAKxD,SAAWpG,KAAKoG,OACrC,OAAO,CACf,MACI,GAAKwD,EAAKxD,QAAUwD,EAAKxD,SAAWwD,EAAK3G,aAAgB2G,EAAK3G,cAAgBjD,KAAKoG,OAC/E,OAAO,CAEnB,CAEA,OAAO,CACX,CAMAkC,IAAAA,CAAK3F,GACD3C,KAAK8F,OAAOwC,KAAKT,KAAKiC,UAAUnH,GACpC,CAEA+E,SAAAA,GACIC,aAAa3H,KAAK+J,aAClB/J,KAAK+J,YAAcvC,YAAW,KAC1BR,QAAQgD,KAAK,mCACbhK,KAAK8F,OAAOmE,OAAO,GACpB,KACP,CAOAC,EAAAA,CAAG1H,EAAM2H,GACL,IAAKC,EAAoB7F,SAAS/B,GAC9B,MAAM0B,EAAO3B,qBAAqBC,GACtCxC,KAAKwF,QAAQ6E,iBAAiB7H,EAAM2H,EACxC,CAOAG,IAAAA,CAAK9H,EAAM2H,GACP,IAAKC,EAAoB7F,SAAS/B,GAC9B,MAAM0B,EAAO3B,qBAAqBC,GACtCxC,KAAKwF,QAAQ6E,iBAAiB7H,EAAM2H,EAAU,CAACG,MAAM,GACzD,CAOAC,GAAAA,CAAI/H,EAAM2H,GACN,IAAKC,EAAoB7F,SAAS/B,GAC9B,MAAM0B,EAAO3B,qBAAqBC,GACtCxC,KAAKwF,QAAQgF,oBAAoBhI,EAAM2H,EAC3C,CAKAF,KAAAA,GACI,IACIjK,KAAK8F,OAAOmE,OAChB,CAAE,MAAO9C,GACT,CACJ,EAGG,MAAMiD,EAAsB,CAAC,QAAS,WAAY,WAAY,SAErE,SAASX,EAAcgB,GACnB,OAAKA,aAAsBhE,QACnBgE,EACOhE,QAAQC,QAAQ+D,GACpBhE,QAAQa,SAEZmD,CACX,CC7XAnF,EAAoBoF,aCKbC,eAA4B3H,GAC/B,MAAM4H,EAAQhK,OAAOiK,QAAQ9H,EAAqBC,IAC7C8H,QAAOC,QAAgBzK,IAAVyK,EAAG,KAChBC,KAAI,EAAEpJ,EAAOqJ,KAAWtE,mBAAmB/E,GAAS,IAAM+E,mBAAmBsE,KAC5EC,GAAOlI,EAAO+C,QAAU,8BAAgC,UAAY6E,EAAMO,KAAK,KACrF,IACI,MAAMtH,QAAYuH,MAAMF,GAAK1C,MAAK6C,GAAKA,EAAEC,SACzC,GAAmB,YAAfzH,EAAIgC,OACJ,MAAM3B,EAAOlC,WAAW6B,EAAIuD,OAAS,uBACzC,OAAOvD,CACX,CAAE,MAAOsD,GACL,GAAIA,aAAa9F,EACb,MAAM8F,EACV,MAAMjD,EAAOlC,WAAW,wBAE5B,CACJ,EDnBA,U","sources":["webpack://stellarbroker/webpack/universalModuleDefinition","webpack://stellarbroker/external umd \"@stellar/stellar-base\"","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/quote-request.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-base\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"@stellar/stellar-base\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"stellarbroker\"] = factory(require(\"@stellar/stellar-base\"));\n\telse\n\t\troot[\"stellarbroker\"] = factory(root[\"@stellar/stellar-base\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__755__) => {\nreturn ","module.exports = __WEBPACK_EXTERNAL_MODULE__755__;","// 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 {\r\n constructor(code, message) {\r\n super(message)\r\n this.code = code\r\n }\r\n\r\n /**\r\n * Numeric error code\r\n * @type {number}\r\n * @readonly\r\n */\r\n code = 0\r\n}\r\n\r\nconst errors = {\r\n invalidInitParam(param) {\r\n return new StellarBrokerError(1, `Invalid parameter value: \"${param}\"`)\r\n },\r\n invalidAuthorizationParam() {\r\n return new StellarBrokerError(2, 'Invalid authorization secret key or callback provided')\r\n },\r\n quoteNotSet() {\r\n return new StellarBrokerError(11, 'Price quote not available')\r\n },\r\n quoteExpired() {\r\n return new StellarBrokerError(12, 'Price quote expired')\r\n },\r\n quoteError(message) {\r\n return new StellarBrokerError(13, 'Price quotation error: ' + message)\r\n },\r\n invalidQuoteParam(invalidParamName, details) {\r\n return new StellarBrokerError(14, `Invalid quote request parameter: \"${invalidParamName}\". ${details}`)\r\n },\r\n tradeInProgress() {\r\n return new StellarBrokerError(20, 'Cannot change quote while trade is in progress')\r\n },\r\n invalidSwapTx() {\r\n return new StellarBrokerError(21, 'Invalid swap transaction received')\r\n },\r\n failedToSignTx() {\r\n return new StellarBrokerError(22, 'Failed to sign received transaction')\r\n },\r\n unsupportedEventType(type) {\r\n return new StellarBrokerError(31, 'Unknown event type: ' + type)\r\n },\r\n serverError(message) {\r\n return new StellarBrokerError(101, 'Server error: ' + message)\r\n }\r\n}\r\n\r\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 {StrKey} from '@stellar/stellar-base'\r\nimport errors from './errors.js'\r\n\r\n/**\r\n * @typedef {object} SwapQuoteParams\r\n * @property {string} sellingAsset - Asset to sell\r\n * @property {string} buyingAsset - Asset to buy\r\n * @property {string} [sellingAmount] - Amount of selling asset\r\n * @property {string} [buyingAmount] - Amount of buying asset\r\n * @property {number} [slippageTolerance] - Swap slippage tolerance (0.02 by default - 2%)\r\n * @property {string} [destination] - Trader account address\r\n */\r\n\r\n/**\r\n * @param {SwapQuoteParams} params\r\n * @return {SwapQuoteParams}\r\n */\r\nexport function validateQuoteRequest(params) {\r\n const {\r\n destination, sellingAsset, selling_asset, buyingAsset, buying_asset, sellingAmount, selling_amount,\r\n buyingAmount, buying_amount, slippageTolerance, slippage_tolerance, ...other\r\n } = params\r\n const res = {\r\n sellingAsset: parseAsset(sellingAsset || selling_asset, 'sellingAsset'),\r\n buyingAsset: parseAsset(buyingAsset || buying_asset, 'buyingAsset'),\r\n sellingAmount: parseAmount(sellingAmount || selling_amount, 'sellingAmount'),\r\n buyingAmount: parseAmount(buyingAmount || buying_amount, 'buyingAmount'),\r\n slippageTolerance: parseSlippageTolerance(slippageTolerance || slippage_tolerance || 0.02, 'slippageTolerance'),\r\n flow: 'direct'\r\n }\r\n if (res.buyingAsset === res.sellingAsset)\r\n throw errors.invalidQuoteParam('buyingAsset', 'Buying asset can\\'t be the same as selling asset')\r\n if (res.buyingAmount === undefined && res.sellingAmount === undefined)\r\n throw errors.invalidQuoteParam('sellingAmount', 'Either \"buyingAmount\" or \"sellingAmount\" parameter is required')\r\n if (res.buyingAmount !== undefined && res.sellingAmount !== undefined)\r\n throw errors.invalidQuoteParam('sellingAmount', 'Parameters \"buyingAmount\" and \"sellingAmount\" are mutually exclusive')\r\n Object.assign(res, other) //add remaining optional params\r\n Object.freeze(res)\r\n return res\r\n}\r\n\r\nfunction parseAsset(asset, parameter) {\r\n if (typeof asset === 'string') {\r\n if (asset === 'XLM' || asset === 'xlm' || asset === 'native')\r\n return 'XLM'\r\n if (asset.includes(':')) {\r\n const [code, issuer] = asset.split(':')\r\n validateCode(code)\r\n validateAccount(issuer, parameter)\r\n return code + '-' + issuer\r\n }\r\n if (asset.includes('-')) {\r\n const [code, issuer] = asset.split('-')\r\n validateCode(code)\r\n validateAccount(issuer, parameter)\r\n return asset\r\n }\r\n }\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset')\r\n}\r\n\r\nfunction validateCode(code, parameter) {\r\n if (!/^[a-zA-Z0-9]{1,12}$/.test(code))\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset code: ' + (!code ? 'missing' : code))\r\n return code\r\n}\r\n\r\nfunction validateAccount(account, param) {\r\n if (!account || !StrKey.isValidEd25519PublicKey(account))\r\n throw errors.invalidQuoteParam(param, 'Invalid account address: ' + (!account ? 'missing' : account))\r\n return account\r\n}\r\n\r\nfunction parseAmount(amount, parameter) {\r\n if (amount === undefined)\r\n return undefined\r\n const parsed = parseFloat(amount)\r\n if (isNaN(parsed) || parsed <= 0)\r\n throw errors.invalidQuoteParam(parameter, 'Invalid asset amount: ' + amount)\r\n return amount\r\n}\r\n\r\nfunction parseSlippageTolerance(src, parameter) {\r\n const tolerance = parseFloat(src)\r\n if (isNaN(tolerance))\r\n throw errors.invalidQuoteParam(parameter, 'Invalid slippage tolerance, number expected')\r\n if (tolerance < 0)\r\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too small, expected value >= 0')\r\n if (tolerance > 0.5)\r\n throw errors.invalidQuoteParam(parameter, 'Slippage tolerance is too large, expected value < 0.5 (50%)')\r\n return tolerance\r\n}","import {Networks, TransactionBuilder, Keypair, FeeBumpTransaction, StrKey} from '@stellar/stellar-base'\r\nimport errors from './errors.js'\r\nimport {buildEvent} from './events.js'\r\nimport {validateQuoteRequest} from './quote-request.js'\r\n\r\nexport default class StellarBrokerClient {\r\n /**\r\n * @param {ClientInitializationParams} params\r\n */\r\n constructor(params) {\r\n this.partnerKey = params.partnerKey\r\n this.emitter = new EventTarget()\r\n this.network = Networks[(params.network || 'PUBLIC').toUpperCase()] || params.network\r\n this.flow = params.flow || 'direct'\r\n }\r\n\r\n /**\r\n * @type {ClientStatus}\r\n * @readonly\r\n */\r\n status = 'disconnected'\r\n /**\r\n * @type {WebSocket}\r\n * @private\r\n */\r\n socket\r\n /**\r\n * @type {EventTarget}\r\n * @private\r\n */\r\n emitter\r\n /**\r\n * @type {string}\r\n * @private\r\n */\r\n origin = 'https://api.stellar.broker'\r\n /**\r\n * Stellar network passphrase\r\n * @type {string}\r\n * @readonly\r\n */\r\n network = ''\r\n /**\r\n * Last quote request submitted by the client\r\n * @type {SwapQuoteParams}\r\n * @readonly\r\n */\r\n quoteRequest\r\n /**\r\n * Last quote received from the sever\r\n * @type {SwapQuoteResult}\r\n * @readonly\r\n */\r\n lastQuote\r\n /**\r\n * Current quote accepted for the trading\r\n * @private\r\n */\r\n tradeQuote\r\n /**\r\n * API key of the partner\r\n * @type {string}\r\n * @readonly\r\n */\r\n partnerKey\r\n /**\r\n * Account secret key (for direct swap flow)\r\n * @type {Keypair|ClientAuthorizationCallback}\r\n * @private\r\n */\r\n authorization\r\n\r\n /**\r\n * Trader account public key\r\n * @type {string}\r\n * @readonly\r\n */\r\n source\r\n\r\n /**\r\n * Connect to the StellarBroker server\r\n * @return {Promise<StellarBrokerClient>}\r\n */\r\n connect() {\r\n if (this.socket?.readyState === WebSocket.OPEN)\r\n return Promise.resolve(this) //already opened\r\n\r\n this.socket = new WebSocket(this.origin + '/ws?partner=' + encodeURIComponent(this.partnerKey))\r\n this.socket.onmessage = this.processMessage.bind(this)\r\n this.socket.onclose = () => {\r\n console.log('Connection closed')\r\n this.status = 'disconnected'\r\n }\r\n\r\n this.socket.onerror = e => console.error(e)\r\n\r\n return new Promise((confirm, reject) => { //TODO: use 'once'\r\n const expirationTimeout = setTimeout(() => reject(this), 2000) // 2s timeout\r\n this.onSocketOpen = () => {\r\n this.status = 'ready'\r\n this.heartbeat()\r\n this.onSocketOpen = undefined\r\n clearTimeout(expirationTimeout)\r\n confirm(this)\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Process incoming message\r\n * @param message\r\n * @private\r\n */\r\n processMessage(message) {\r\n const raw = JSON.parse(message.data)\r\n switch (raw.type) {\r\n case 'connected':\r\n if (this.onSocketOpen) {\r\n this.onSocketOpen()\r\n }\r\n break\r\n case 'quote':\r\n this.lastQuote = raw.quote\r\n this.lastQuote.ts = new Date()\r\n //send event to the client app\r\n this.emitter.dispatchEvent(buildEvent('quote', this.lastQuote))\r\n break\r\n case 'tx':\r\n if (this.status !== 'trade') {\r\n console.log('Received tx in non-trading state', this.status, raw)\r\n return //skip unless trading is in progress\r\n }\r\n this.processTxRequest(raw)\r\n break\r\n case 'stop':\r\n this.emitter.dispatchEvent(buildEvent('finished', {\r\n status: raw.status,\r\n sold: raw.sold,\r\n bought: raw.bought\r\n }, 'result'))\r\n this.status = 'ready'\r\n this.tradeQuote = undefined\r\n this.source = undefined\r\n break\r\n case 'progress':\r\n this.emitter.dispatchEvent(buildEvent('progress', {\r\n sold: raw.sold,\r\n bought: raw.bought\r\n }, 'status'))\r\n break\r\n case 'ping':\r\n this.heartbeat()\r\n this.send({type: 'pong'})\r\n break\r\n case 'error':\r\n this.stop()\r\n this.emitter.dispatchEvent(buildEvent('error', 'Server error: ' + raw.error))\r\n break\r\n default:\r\n console.log('Unknown message type: ' + raw.type)\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * Request swap quote\r\n * @param {SwapQuoteParams} params - Quote parameters\r\n */\r\n quote(params) {\r\n if (this.status === 'trade')\r\n throw errors.tradeInProgress()\r\n this.quoteRequest = validateQuoteRequest(params)\r\n this.status = 'quote'\r\n this.connect()\r\n .then(() => {\r\n this.send({\r\n type: 'quote',\r\n ...this.quoteRequest\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Stop quotation/trading\r\n */\r\n stop() {\r\n if (this.status !== 'trade' && this.status !== 'quote')\r\n return\r\n this.send({\r\n type: 'stop'\r\n })\r\n this.tradeQuote = undefined\r\n this.source = undefined\r\n this.status = 'ready'\r\n }\r\n\r\n /**\r\n * Confirm current quote and start trading\r\n * @param {string} account - Trader account address\r\n * @param {string|ClientAuthorizationCallback} authorization - Authorization method, either account secret key or an authorization callback\r\n */\r\n confirmQuote(account, authorization) {\r\n if (this.status !== 'quote')\r\n throw errors.tradeInProgress()\r\n if (!this.lastQuote)\r\n throw errors.quoteNotSet()\r\n if ((new Date() - this.lastQuote.ts) > 7000) //do not allow stale quotes quoted more than 7s ago\r\n throw errors.quoteExpired()\r\n if (this.lastQuote.status !== 'success')\r\n throw errors.quoteError(this.lastQuote.error || 'quote not available')\r\n if (!account || !StrKey.isValidEd25519PublicKey(account))\r\n throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!account ? 'missing' : account))\r\n if (typeof authorization === 'string') {\r\n try {\r\n this.authorization = Keypair.fromSecret(authorization)\r\n } catch (e) {\r\n throw errors.invalidAuthorizationParam()\r\n }\r\n } else if (typeof authorization === 'function') {\r\n this.authorization = authorization\r\n }\r\n this.tradeQuote = this.lastQuote\r\n this.source = account\r\n this.send({\r\n type: 'trade',\r\n account\r\n })\r\n this.status = 'trade'\r\n }\r\n\r\n /**\r\n * @param {{xdr: string, hash: string}} raw\r\n * @private\r\n */\r\n processTxRequest(raw) {\r\n //parse incoming transaction\r\n let tx\r\n try {\r\n tx = TransactionBuilder.fromXDR(raw.xdr, this.network)\r\n } catch (e) {\r\n throw errors.invalidSwapTx()\r\n }\r\n //check that transaction is correct\r\n if (!this.validateTransaction(tx))\r\n throw errors.invalidSwapTx()\r\n //sign transaction\r\n this.authorizeTx(tx, raw.networkFee)\r\n .then(tx => {\r\n if (!tx || tx.signatures.length < 1)\r\n return console.error(`Transaction ${raw.hash} not signed`)\r\n //respond with signed transaction\r\n this.send({\r\n type: 'tx',\r\n hash: raw.hash,\r\n xdr: tx.toXDR()\r\n })\r\n })\r\n .catch(e => {\r\n console.error(e)\r\n throw errors.failedToSignTx()\r\n })\r\n }\r\n\r\n /**\r\n * @param {Transaction} tx\r\n * @param {string} networkFee\r\n * @return {Promise<FeeBumpTransaction>}\r\n * @private\r\n */\r\n async authorizeTx(tx, networkFee) {\r\n //sign tx\r\n if (this.authorization instanceof Keypair) {\r\n tx.sign(this.authorization)\r\n } else {\r\n tx = await ensurePromise(this.authorization(tx))\r\n }\r\n //wrap with fee bump\r\n let wrapped = TransactionBuilder.buildFeeBumpTransaction(this.source, networkFee, tx, this.network)\r\n //sign fee bump wrapper tx\r\n if (this.authorization instanceof Keypair) {\r\n wrapped.sign(this.authorization)\r\n } else {\r\n wrapped = await ensurePromise(this.authorization(wrapped))\r\n }\r\n return wrapped\r\n }\r\n\r\n /**\r\n * @param {Transaction} tx\r\n * @throws {StellarBrokerError} Invalid swap transaction received\r\n */\r\n validateTransaction(tx) {\r\n /*if (!(tx instanceof FeeBumpTransaction))\r\n return false\r\n const {innerTransaction} = tx\r\n if (tx.feeSource !== this.source)\r\n return false*/\r\n for (let swap of tx.operations) {\r\n if (swap.type !== 'pathPaymentStrictSend' && swap.type !== 'pathPaymentStrictReceive')\r\n return false\r\n const isFee = swap.destination !== this.source\r\n if (isFee) {\r\n if (swap.type !== 'pathPaymentStrictSend')\r\n return false\r\n if ((swap.source && swap.source !== this.source))\r\n return false\r\n } else {\r\n if ((swap.source && swap.source !== swap.destination) || swap.destination !== this.source)\r\n return false\r\n }\r\n }\r\n //TODO: check assets and amounts\r\n return true\r\n }\r\n\r\n /**\r\n * @param {{}} data\r\n * @private\r\n */\r\n send(data) {\r\n this.socket.send(JSON.stringify(data))\r\n }\r\n\r\n heartbeat() {\r\n clearTimeout(this.pingHandler)\r\n this.pingHandler = setTimeout(() => {\r\n console.warn('Lost connection with the server')\r\n this.socket.close()\r\n }, 11_000) // 11 seconds heartbeat timeout\r\n }\r\n\r\n /**\r\n * Add event listener\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n on(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.addEventListener(type, callback)\r\n }\r\n\r\n /**\r\n * Add event listener that will be executed once\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n once(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.addEventListener(type, callback, {once: true})\r\n }\r\n\r\n /**\r\n * Remove event listener\r\n * @param {StellarBrokerClientEvent} type\r\n * @param {function} callback\r\n */\r\n off(type, callback) {\r\n if (!StellarBrokerEvents.includes(type))\r\n throw errors.unsupportedEventType(type)\r\n this.emitter.removeEventListener(type, callback)\r\n }\r\n\r\n /**\r\n * Close underlying connection and finalize the client\r\n */\r\n close() {\r\n try {\r\n this.socket.close()\r\n } catch (e) {\r\n }\r\n }\r\n}\r\n\r\nexport const StellarBrokerEvents = ['quote', 'finished', 'progress', 'error']\r\n\r\nfunction ensurePromise(callResult) {\r\n if (!callResult instanceof Promise) {\r\n if (callResult)\r\n return Promise.resolve(callResult)\r\n return Promise.reject()\r\n }\r\n return callResult\r\n}\r\n\r\n/**\r\n * @typedef {object} ClientInitializationParams\r\n * @property {string} [network] - Stellar network identifier or passphrase\r\n * @property {string} [partnerKey] - Partner key\r\n * @property {SwapFlowMode} [flow] - Swap flow mode\r\n */\r\n\r\n/**\r\n * @typedef {'disconnected'|'ready'|'quote'|'trade'} ClientStatus\r\n */\r\n\r\n/**\r\n * @typedef {'quote'|'finished'|'progress'|'error'} StellarBrokerClientEvent\r\n */\r\n\r\n/**\r\n * @typedef {'direct'} SwapFlowMode\r\n */\r\n\r\n/**\r\n * @typedef {object} SwapQuoteResult\r\n * @property {string} sellingAsset\r\n * @property {string} buyingAsset\r\n * @property {number} slippageTolerance\r\n * @property {string} destination\r\n * @property {number} ledger\r\n * @property {string} [sellingAmount]\r\n * @property {string} [estimatedBuyingAmount]\r\n * @property {string} [buyingAmount]\r\n * @property {string} [estimatedSellingAmount]\r\n * @property {{selling: string, buying: string, path: string[]}} [directTrade]\r\n */\r\n\r\n/**\r\n * @typedef {function(FeeBumpTransaction):Promise<FeeBumpTransaction>} ClientAuthorizationCallback\r\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__755__","__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","quoteNotSet","quoteExpired","quoteError","invalidQuoteParam","invalidParamName","details","tradeInProgress","invalidSwapTx","failedToSignTx","unsupportedEventType","type","serverError","buildEvent","data","evt","CustomEvent","detail","validateQuoteRequest","params","destination","sellingAsset","selling_asset","buyingAsset","buying_asset","sellingAmount","selling_amount","buyingAmount","buying_amount","slippageTolerance","slippage_tolerance","other","res","parseAsset","parseAmount","parseSlippageTolerance","flow","errors","assign","freeze","asset","parameter","includes","issuer","split","validateCode","validateAccount","test","account","StrKey","isValidEd25519PublicKey","amount","parsed","parseFloat","isNaN","src","tolerance","StellarBrokerClient","partnerKey","emitter","EventTarget","network","Networks","toUpperCase","status","socket","origin","quoteRequest","lastQuote","tradeQuote","authorization","source","connect","readyState","WebSocket","OPEN","Promise","resolve","encodeURIComponent","onmessage","processMessage","bind","onclose","console","log","onerror","e","error","confirm","reject","expirationTimeout","setTimeout","onSocketOpen","heartbeat","clearTimeout","raw","JSON","parse","quote","ts","Date","dispatchEvent","processTxRequest","sold","bought","send","stop","then","confirmQuote","Keypair","fromSecret","tx","TransactionBuilder","fromXDR","xdr","validateTransaction","authorizeTx","networkFee","signatures","length","hash","toXDR","catch","sign","ensurePromise","wrapped","buildFeeBumpTransaction","swap","operations","stringify","pingHandler","warn","close","on","callback","StellarBrokerEvents","addEventListener","once","off","removeEventListener","callResult","estimateSwap","async","query","entries","filter","kv","map","value","url","join","fetch","r","json"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stellar-broker/client",
3
- "version": "0.1.0",
4
- "description": "Browser client library for StellarBroker",
3
+ "version": "0.3.0",
4
+ "description": "Browser client library for StellarBroker service",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "module": "src/index.js",
@@ -14,7 +14,7 @@
14
14
  "type": "git",
15
15
  "url": "https://github.com/stellar-broker/client"
16
16
  },
17
- "author": "",
17
+ "author": "StellarBroker",
18
18
  "license": "MIT",
19
19
  "peerDependencies": {
20
20
  "@stellar/stellar-base": ">=11"
package/src/client.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Networks, TransactionBuilder, Keypair, FeeBumpTransaction} from '@stellar/stellar-base'
1
+ import {Networks, TransactionBuilder, Keypair, FeeBumpTransaction, StrKey} from '@stellar/stellar-base'
2
2
  import errors from './errors.js'
3
3
  import {buildEvent} from './events.js'
4
4
  import {validateQuoteRequest} from './quote-request.js'
@@ -8,11 +8,9 @@ export default class StellarBrokerClient {
8
8
  * @param {ClientInitializationParams} params
9
9
  */
10
10
  constructor(params) {
11
- if (!params.network)
12
- throw errors.invalidInitParam('network')
13
11
  this.partnerKey = params.partnerKey
14
12
  this.emitter = new EventTarget()
15
- this.network = Networks[params.network.toUpperCase()] || params.network
13
+ this.network = Networks[(params.network || 'PUBLIC').toUpperCase()] || params.network
16
14
  this.flow = params.flow || 'direct'
17
15
  }
18
16
 
@@ -45,7 +43,7 @@ export default class StellarBrokerClient {
45
43
  /**
46
44
  * Last quote request submitted by the client
47
45
  * @type {SwapQuoteParams}
48
- * @private
46
+ * @readonly
49
47
  */
50
48
  quoteRequest
51
49
  /**
@@ -62,7 +60,7 @@ export default class StellarBrokerClient {
62
60
  /**
63
61
  * API key of the partner
64
62
  * @type {string}
65
- * @private
63
+ * @readonly
66
64
  */
67
65
  partnerKey
68
66
  /**
@@ -75,10 +73,9 @@ export default class StellarBrokerClient {
75
73
  /**
76
74
  * Trader account public key
77
75
  * @type {string}
76
+ * @readonly
78
77
  */
79
- get source() {
80
- return this.quoteRequest?.destination
81
- }
78
+ source
82
79
 
83
80
  /**
84
81
  * Connect to the StellarBroker server
@@ -142,6 +139,8 @@ export default class StellarBrokerClient {
142
139
  bought: raw.bought
143
140
  }, 'result'))
144
141
  this.status = 'ready'
142
+ this.tradeQuote = undefined
143
+ this.source = undefined
145
144
  break
146
145
  case 'progress':
147
146
  this.emitter.dispatchEvent(buildEvent('progress', {
@@ -191,14 +190,16 @@ export default class StellarBrokerClient {
191
190
  type: 'stop'
192
191
  })
193
192
  this.tradeQuote = undefined
193
+ this.source = undefined
194
194
  this.status = 'ready'
195
195
  }
196
196
 
197
197
  /**
198
198
  * Confirm current quote and start trading
199
+ * @param {string} account - Trader account address
199
200
  * @param {string|ClientAuthorizationCallback} authorization - Authorization method, either account secret key or an authorization callback
200
201
  */
201
- confirmQuote(authorization) {
202
+ confirmQuote(account, authorization) {
202
203
  if (this.status !== 'quote')
203
204
  throw errors.tradeInProgress()
204
205
  if (!this.lastQuote)
@@ -207,6 +208,8 @@ export default class StellarBrokerClient {
207
208
  throw errors.quoteExpired()
208
209
  if (this.lastQuote.status !== 'success')
209
210
  throw errors.quoteError(this.lastQuote.error || 'quote not available')
211
+ if (!account || !StrKey.isValidEd25519PublicKey(account))
212
+ throw errors.invalidQuoteParam('account', 'Invalid trader account address: ' + (!account ? 'missing' : account))
210
213
  if (typeof authorization === 'string') {
211
214
  try {
212
215
  this.authorization = Keypair.fromSecret(authorization)
@@ -217,8 +220,10 @@ export default class StellarBrokerClient {
217
220
  this.authorization = authorization
218
221
  }
219
222
  this.tradeQuote = this.lastQuote
223
+ this.source = account
220
224
  this.send({
221
- type: 'trade'
225
+ type: 'trade',
226
+ account
222
227
  })
223
228
  this.status = 'trade'
224
229
  }
package/src/estimate.js CHANGED
@@ -2,19 +2,24 @@ import errors, {StellarBrokerError} from './errors.js'
2
2
  import {validateQuoteRequest} from './quote-request.js'
3
3
 
4
4
  /**
5
- * Estimate price swap without trading.
5
+ * Request single swap quote estimate without trading
6
6
  * @param {SwapQuoteParams} params
7
+ * @return {Promise<SwapQuoteResult>}
7
8
  */
8
9
  export async function estimateSwap(params) {
9
- const query = Object.entries(validateQuoteRequest(params)).map(([param, value]) => encodeURIComponent(param) + '=' + encodeURIComponent(value))
10
+ const query = Object.entries(validateQuoteRequest(params))
11
+ .filter(kv => kv[1] !== undefined)
12
+ .map(([param, value]) => encodeURIComponent(param) + '=' + encodeURIComponent(value))
10
13
  const url = (params.origin || 'https://api.stellar.broker') + '/quote?' + query.join('&')
11
14
  try {
12
15
  const res = await fetch(url).then(r => r.json())
13
16
  if (res.status !== 'success')
14
17
  throw errors.quoteError(res.error || 'Quote not available')
18
+ return res
15
19
  } catch (e) {
16
20
  if (e instanceof StellarBrokerError)
17
21
  throw e
18
22
  throw errors.quoteError('Failed to fetch quote')
23
+ return null
19
24
  }
20
25
  }
package/src/index.d.ts CHANGED
@@ -38,9 +38,10 @@ export default class StellarBrokerClient {
38
38
 
39
39
  /**
40
40
  * Confirm current quote and start trading
41
+ * @param {string} account - Trader account address
41
42
  * @param {string|ClientAuthorizationCallback} authorization - Authorization method, either account secret key or an authorization callback
42
43
  */
43
- confirmQuote(authorization: string | ClientAuthorizationCallback): void;
44
+ confirmQuote(account: string, authorization: string | ClientAuthorizationCallback): void;
44
45
 
45
46
  /**
46
47
  * Add event listener
@@ -62,12 +63,18 @@ export default class StellarBrokerClient {
62
63
  * @param {function} callback
63
64
  */
64
65
  off(type: StellarBrokerClientEvent, callback: Function): void;
66
+
65
67
  /**
66
68
  * Close underlying connection and finalize the client
67
69
  */
68
70
  close(): void;
69
71
  }
70
72
 
73
+ /**
74
+ * Request single swap quote estimate without trading
75
+ */
76
+ export function estimateSwap(params: SwapQuoteParams): Promise<SwapQuoteResult>;
77
+
71
78
  export interface SwapQuoteParams {
72
79
  /**
73
80
  * Asset to sell
@@ -93,14 +100,6 @@ export interface SwapQuoteParams {
93
100
 
94
101
 
95
102
  export interface ClientInitializationParams {
96
- /**
97
- * Trader account public key
98
- */
99
- account: string;
100
- /**
101
- * Stellar network identifier or passphrase (pubnet by default)
102
- */
103
- network?: string;
104
103
  /**
105
104
  * Partner key
106
105
  */
@@ -109,11 +108,15 @@ export interface ClientInitializationParams {
109
108
  * Swap flow mode
110
109
  */
111
110
  flow?: SwapFlowMode;
111
+ /**
112
+ * Stellar network identifier or passphrase (pubnet by default)
113
+ */
114
+ network?: string;
112
115
  }
113
116
 
114
117
  export type ClientStatus = "disconnected" | "ready" | "quote" | "trade";
115
118
 
116
- export type StellarBrokerClientEvent = "quote" | "finished" | "progress";
119
+ export type StellarBrokerClientEvent = "quote" | "finished" | "progress" | "error";
117
120
 
118
121
  export type SwapFlowMode = "direct";
119
122
 
@@ -127,6 +130,13 @@ export interface SwapQuoteResult {
127
130
  estimatedBuyingAmount?: string;
128
131
  buyingAmount?: string;
129
132
  estimatedSellingAmount?: string;
133
+ directTrade?: SwapQuoteDirectTradeResult;
134
+ }
135
+
136
+ export interface SwapQuoteDirectTradeResult {
137
+ selling: string;
138
+ buying: string;
139
+ path: string[];
130
140
  }
131
141
 
132
142
  export class StellarBrokerError extends Error {
package/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import StellarBrokerClient from './client.js'
2
+ export * from './estimate.js'
2
3
 
3
4
  export default StellarBrokerClient
@@ -28,9 +28,6 @@ export function validateQuoteRequest(params) {
28
28
  slippageTolerance: parseSlippageTolerance(slippageTolerance || slippage_tolerance || 0.02, 'slippageTolerance'),
29
29
  flow: 'direct'
30
30
  }
31
- if (destination) {
32
- res.destination = validateAccount(destination, 'destination', true)
33
- }
34
31
  if (res.buyingAsset === res.sellingAsset)
35
32
  throw errors.invalidQuoteParam('buyingAsset', 'Buying asset can\'t be the same as selling asset')
36
33
  if (res.buyingAmount === undefined && res.sellingAmount === undefined)
@@ -38,6 +35,7 @@ export function validateQuoteRequest(params) {
38
35
  if (res.buyingAmount !== undefined && res.sellingAmount !== undefined)
39
36
  throw errors.invalidQuoteParam('sellingAmount', 'Parameters "buyingAmount" and "sellingAmount" are mutually exclusive')
40
37
  Object.assign(res, other) //add remaining optional params
38
+ Object.freeze(res)
41
39
  return res
42
40
  }
43
41
 
@@ -63,15 +61,13 @@ function parseAsset(asset, parameter) {
63
61
 
64
62
  function validateCode(code, parameter) {
65
63
  if (!/^[a-zA-Z0-9]{1,12}$/.test(code))
66
- throw errors.invalidQuoteParam(parameter, 'Invalid asset code: ' + (code === undefined ? 'missing' : code))
64
+ throw errors.invalidQuoteParam(parameter, 'Invalid asset code: ' + (!code ? 'missing' : code))
67
65
  return code
68
66
  }
69
67
 
70
- function validateAccount(account, param, optional = false) {
71
- if (account === undefined && optional)
72
- return undefined
73
- if (!StrKey.isValidEd25519PublicKey(account))
74
- throw errors.invalidQuoteParam(param, 'Invalid account address: ' + (account === undefined ? 'missing' : account))
68
+ function validateAccount(account, param) {
69
+ if (!account || !StrKey.isValidEd25519PublicKey(account))
70
+ throw errors.invalidQuoteParam(param, 'Invalid account address: ' + (!account ? 'missing' : account))
75
71
  return account
76
72
  }
77
73
 
@@ -1,43 +0,0 @@
1
- const path = require('path')
2
- const webpack = require('webpack')
3
-
4
- module.exports = function () {
5
- return {
6
- mode: 'production',
7
- devtool: 'source-map',
8
- entry: {
9
- stellarbroker: [path.join(__dirname, '/src/index.js')]
10
- },
11
- output: {
12
- path: path.join(__dirname, './lib'),
13
- filename: '[name].js',
14
- library: {
15
- name: 'stellarbroker',
16
- type: 'umd',
17
- export: 'default'
18
- },
19
- libraryTarget: 'umd',
20
- globalObject: 'this'
21
- },
22
- module: {
23
- rules: [
24
- {
25
- test: /\.js?$/,
26
- loader: 'babel-loader',
27
- exclude: /node_modules/
28
- }
29
- ]
30
- },
31
- externals: {
32
- '@stellar/stellar-base': '@stellar/stellar-base'
33
- },
34
- plugins: [
35
- new webpack.DefinePlugin({
36
- 'process.env.NODE_ENV': JSON.stringify('production')
37
- })
38
- ],
39
- optimization: {
40
- minimize: true
41
- }
42
- }
43
- }