@nowramp/sdk 0.1.9 → 0.1.11

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/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.9
2
+ * @nowramp/sdk v0.1.11
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],r=["localhost","127.0.0.1"],n={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(n.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(n.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&r.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function a(i,r){if(!i)return{valid:!0};const n=i.trim();if(r){const i=t[r.toUpperCase()];if(i){const{pattern:t,description:r}=e[i];return t.test(n)?{valid:!0}:{valid:!1,error:r}}}for(const{pattern:t}of Object.values(e))if(t.test(n))return{valid:!0};return n.length>=10&&n.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!c(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),r=i.data?.attributes||i.data||i;return{rate:r.rate,sourceAmount:r.sourceAmount,sourceCurrency:r.sourceCurrency,destinationAmount:r.destinationAmount,destinationCurrency:r.destinationCurrency,fees:{processingFee:r.fees?.processingFee||0,processingFeePercent:r.fees?.processingFeePercent||0,networkFee:r.fees?.networkFee||0,totalFee:r.fees?.totalFee||0},provider:r.provider||"unknown",expiresAt:r.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=i,exports.API_KEY_PREFIXES=n,exports.CURRENCY_CHAIN_MAP=t,exports.DEV_ALLOWED_HOSTNAMES=r,exports.RampApi=l,exports.RampWidget=d,exports.VERSION="0.1.9",exports.default=d,exports.getCurrencies=async function(e){return new l(e).getCurrencies()},exports.getRate=async function(e,t){return new l(e).getRate(t)},exports.validateApiKey=o,exports.validateMessageOrigin=c,exports.validateRedirectUrl=s,exports.validateWalletAddress=a;
6
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],r=["localhost","127.0.0.1"],i={public:"pk_",secret:"sk_"};function s(e){return e?e.includes(i.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(i.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function n(e){if(!e)return{valid:!0};try{const i=new URL(e);return t.includes(i.protocol)||"http:"===i.protocol&&r.includes(i.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${i.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function o(t,r){if(!t)return{valid:!0};const i=t.trim();if(r){const t=r.toLowerCase(),s=e[t];if(s){const{pattern:e,description:t}=s;return e.test(i)?{valid:!0}:{valid:!1,error:t}}return i.length>=10&&i.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return i.length>=10&&i.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const r=new URL(e);return"localhost"===r.hostname||"127.0.0.1"===r.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=s(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=n(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=o(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!a(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const r=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},r)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class c{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${r}`)}const r=await t.json(),i=r.data?.attributes||r.data||r;return{rate:i.rate,sourceAmount:i.sourceAmount,sourceCurrency:i.sourceCurrency,destinationAmount:i.destinationAmount,destinationCurrency:i.destinationCurrency,fees:{processingFee:i.fees?.processingFee||0,processingFeePercent:i.fees?.processingFeePercent||0,networkFee:i.fees?.networkFee||0,totalFee:i.fees?.totalFee||0},provider:i.provider||"unknown",expiresAt:i.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${r}`)}const t=await e.json(),r=t.data?.attributes||t.data||t;return{fiat:r.fiat||[],crypto:r.crypto||[],provider:r.provider||"unknown",environment:r.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),r=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${r}`)}const r=await t.json();return r.data?.attributes||r.data||r}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),r=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${r}`)}const t=await e.json();return t.data?.attributes||t.data||t}}exports.ADDRESS_PATTERNS=e,exports.ALLOWED_PROTOCOLS=t,exports.API_KEY_PREFIXES=i,exports.DEV_ALLOWED_HOSTNAMES=r,exports.RampApi=c,exports.RampWidget=d,exports.VERSION="0.1.11",exports.default=d,exports.getCurrencies=async function(e){return new c(e).getCurrencies()},exports.getRate=async function(e,t){return new c(e).getRate(t)},exports.getSupportedNetworks=async function(e){return new c({apiKey:"",projectId:"",...e}).getSupportedNetworks()},exports.validateAddress=async function(e,t){return new c({apiKey:"",projectId:"",...e}).validateAddress(t)},exports.validateAddressBatch=async function(e,t){return new c({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},exports.validateApiKey=s,exports.validateMessageOrigin=a,exports.validateRedirectUrl=n,exports.validateWalletAddress=o;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,11 @@
4
4
  type FlowType = 'buy' | 'sell';
5
5
  type Theme = 'light' | 'dark';
6
6
  type Locale = 'en' | 'es' | 'fr' | 'de';
7
- type ChainType = 'evm' | 'solana' | 'bitcoin';
7
+ type ChainType = 'evm' | 'solana' | 'bitcoin' | 'tezos' | 'other';
8
+ /**
9
+ * Extended chain type for validation API (includes all supported chains)
10
+ */
11
+ type ValidationChainType = 'evm' | 'solana' | 'bitcoin' | 'tezos' | 'other';
8
12
  interface QuoteData {
9
13
  id: string;
10
14
  sourceAmount: number;
@@ -180,6 +184,94 @@ interface AvailableCurrencies {
180
184
  crypto: CryptoCurrency[];
181
185
  /** Provider that returned these currencies */
182
186
  provider: string;
187
+ /** Environment mode (sandbox or production) */
188
+ environment: 'sandbox' | 'production';
189
+ }
190
+ /**
191
+ * Parameters for validating a wallet address
192
+ */
193
+ interface ValidateAddressParams {
194
+ /** The wallet address to validate */
195
+ address: string;
196
+ /** Network identifier (e.g., 'ethereum', 'polygon', 'bitcoin') */
197
+ network?: string;
198
+ /** Chain type override (e.g., 'evm', 'solana', 'bitcoin', 'tezos', 'other') */
199
+ chainType?: ValidationChainType;
200
+ }
201
+ /**
202
+ * Detailed validation information
203
+ */
204
+ interface ValidationDetails {
205
+ /** Whether the address format is valid */
206
+ formatValid: boolean;
207
+ /** Human-readable message about the format validation */
208
+ formatMessage: string;
209
+ /** Whether the checksum is valid (EVM only) */
210
+ checksumValid?: boolean;
211
+ /** Bitcoin address type: 'legacy' | 'segwit' | 'native-segwit' | 'taproot' */
212
+ addressType?: string;
213
+ /** Tezos account type: 'implicit' | 'contract' */
214
+ accountType?: string;
215
+ }
216
+ /**
217
+ * Result of validating a wallet address
218
+ */
219
+ interface AddressValidationResult {
220
+ /** Whether the address is valid for the specified network/chain */
221
+ isValid: boolean;
222
+ /** The original address submitted */
223
+ address: string;
224
+ /** The network ID if specified or detected */
225
+ network: string | null;
226
+ /** The chain type if specified or detected */
227
+ chainType: ValidationChainType | null;
228
+ /** Checksummed/normalized address if applicable (EVM addresses) */
229
+ normalizedAddress: string | null;
230
+ /** Detailed validation information */
231
+ details: ValidationDetails;
232
+ /** Warning messages (non-blocking issues) */
233
+ warnings: string[];
234
+ /** Error messages (blocking issues) */
235
+ errors: string[];
236
+ }
237
+ /**
238
+ * Result of batch address validation
239
+ */
240
+ interface AddressValidationBatchResult {
241
+ /** Individual validation results */
242
+ results: AddressValidationResult[];
243
+ /** Number of valid addresses */
244
+ validCount: number;
245
+ /** Number of invalid addresses */
246
+ invalidCount: number;
247
+ }
248
+ /**
249
+ * Supported network information
250
+ */
251
+ interface SupportedNetwork {
252
+ /** Network identifier */
253
+ id: string;
254
+ /** Display name */
255
+ name: string;
256
+ /** Chain type */
257
+ chainType: ValidationChainType;
258
+ }
259
+ /**
260
+ * Result of getting supported networks
261
+ */
262
+ interface SupportedNetworksResult {
263
+ /** List of all supported networks */
264
+ networks: SupportedNetwork[];
265
+ /** Networks grouped by chain type */
266
+ grouped: {
267
+ evm: SupportedNetwork[];
268
+ solana: SupportedNetwork[];
269
+ bitcoin: SupportedNetwork[];
270
+ tezos: SupportedNetwork[];
271
+ other: SupportedNetwork[];
272
+ };
273
+ /** Total count of networks */
274
+ total: number;
183
275
  }
184
276
 
185
277
  /**
@@ -195,10 +287,6 @@ declare const ADDRESS_PATTERNS: Record<ChainType, {
195
287
  pattern: RegExp;
196
288
  description: string;
197
289
  }>;
198
- /**
199
- * Currency to chain type mapping
200
- */
201
- declare const CURRENCY_CHAIN_MAP: Record<string, ChainType>;
202
290
  /**
203
291
  * Allowed protocols for redirect URLs
204
292
  */
@@ -230,12 +318,12 @@ declare function validateApiKey(apiKey: string): ValidationResult;
230
318
  */
231
319
  declare function validateRedirectUrl(url: string): ValidationResult;
232
320
  /**
233
- * Validate wallet address format
321
+ * Validate wallet address format based on chain type
234
322
  *
235
- * Validates against chain-specific patterns if currency is provided,
236
- * otherwise attempts auto-detection.
323
+ * @param address - The wallet address to validate
324
+ * @param chainType - Chain type from API (evm, solana, bitcoin, tezos, other)
237
325
  */
238
- declare function validateWalletAddress(address: string, destinationCurrency?: string): ValidationResult;
326
+ declare function validateWalletAddress(address: string, chainType?: ChainType): ValidationResult;
239
327
  /**
240
328
  * Validate message origin against expected widget URL
241
329
  *
@@ -357,6 +445,64 @@ declare class RampApi {
357
445
  * ```
358
446
  */
359
447
  getCurrencies(): Promise<AvailableCurrencies>;
448
+ /**
449
+ * Validate a wallet address
450
+ *
451
+ * @param params - Address validation parameters
452
+ * @returns Validation result with details
453
+ *
454
+ * @example
455
+ * ```typescript
456
+ * // Validate EVM address
457
+ * const result = await api.validateAddress({
458
+ * address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21',
459
+ * network: 'ethereum',
460
+ * });
461
+ *
462
+ * if (result.isValid) {
463
+ * console.log('Normalized address:', result.normalizedAddress);
464
+ * }
465
+ *
466
+ * // Auto-detect chain type
467
+ * const autoResult = await api.validateAddress({
468
+ * address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq',
469
+ * });
470
+ * console.log('Chain type:', autoResult.chainType); // 'bitcoin'
471
+ * ```
472
+ */
473
+ validateAddress(params: ValidateAddressParams): Promise<AddressValidationResult>;
474
+ /**
475
+ * Validate multiple wallet addresses in batch
476
+ *
477
+ * @param addresses - Array of address validation parameters (max 20)
478
+ * @returns Batch validation results
479
+ *
480
+ * @example
481
+ * ```typescript
482
+ * const results = await api.validateAddressBatch([
483
+ * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' },
484
+ * { address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', chainType: 'bitcoin' },
485
+ * { address: 'tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb' },
486
+ * ]);
487
+ *
488
+ * console.log(`Valid: ${results.validCount}, Invalid: ${results.invalidCount}`);
489
+ * ```
490
+ */
491
+ validateAddressBatch(addresses: ValidateAddressParams[]): Promise<AddressValidationBatchResult>;
492
+ /**
493
+ * Get list of supported networks for address validation
494
+ *
495
+ * @returns Supported networks with chain type information
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * const networks = await api.getSupportedNetworks();
500
+ *
501
+ * console.log('EVM networks:', networks.grouped.evm.map(n => n.name));
502
+ * console.log('Total networks:', networks.total);
503
+ * ```
504
+ */
505
+ getSupportedNetworks(): Promise<SupportedNetworksResult>;
360
506
  }
361
507
  /**
362
508
  * Standalone function to get conversion rate
@@ -388,6 +534,64 @@ declare function getRate(config: RampApiConfig, params: GetRateParams): Promise<
388
534
  * ```
389
535
  */
390
536
  declare function getCurrencies(config: RampApiConfig): Promise<AvailableCurrencies>;
537
+ /**
538
+ * Standalone function to validate a wallet address
539
+ * Convenience wrapper for one-off address validation
540
+ *
541
+ * Note: This is a public endpoint that doesn't require authentication
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * import { validateAddress } from '@nowramp/sdk';
546
+ *
547
+ * const result = await validateAddress(
548
+ * { apiUrl: 'https://api.nowramp.com' },
549
+ * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' }
550
+ * );
551
+ *
552
+ * if (result.isValid) {
553
+ * console.log('Address is valid!', result.normalizedAddress);
554
+ * }
555
+ * ```
556
+ */
557
+ declare function validateAddress(config: Pick<RampApiConfig, 'apiUrl'>, params: ValidateAddressParams): Promise<AddressValidationResult>;
558
+ /**
559
+ * Standalone function to validate multiple wallet addresses
560
+ * Convenience wrapper for one-off batch validation
561
+ *
562
+ * Note: This is a public endpoint that doesn't require authentication
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * import { validateAddressBatch } from '@nowramp/sdk';
567
+ *
568
+ * const results = await validateAddressBatch(
569
+ * { apiUrl: 'https://api.nowramp.com' },
570
+ * [
571
+ * { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f1bD21', network: 'ethereum' },
572
+ * { address: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', chainType: 'bitcoin' },
573
+ * ]
574
+ * );
575
+ *
576
+ * console.log(`Valid: ${results.validCount}, Invalid: ${results.invalidCount}`);
577
+ * ```
578
+ */
579
+ declare function validateAddressBatch(config: Pick<RampApiConfig, 'apiUrl'>, addresses: ValidateAddressParams[]): Promise<AddressValidationBatchResult>;
580
+ /**
581
+ * Standalone function to get supported networks for validation
582
+ * Convenience wrapper for one-off network listing
583
+ *
584
+ * Note: This is a public endpoint that doesn't require authentication
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * import { getSupportedNetworks } from '@nowramp/sdk';
589
+ *
590
+ * const networks = await getSupportedNetworks({ apiUrl: 'https://api.nowramp.com' });
591
+ * console.log('EVM networks:', networks.grouped.evm.map(n => n.name));
592
+ * ```
593
+ */
594
+ declare function getSupportedNetworks(config: Pick<RampApiConfig, 'apiUrl'>): Promise<SupportedNetworksResult>;
391
595
 
392
596
  /**
393
597
  * NowRamp Widget SDK
@@ -414,5 +618,5 @@ declare function getCurrencies(config: RampApiConfig): Promise<AvailableCurrenci
414
618
  */
415
619
  declare const VERSION: string;
416
620
 
417
- export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, CURRENCY_CHAIN_MAP, DEV_ALLOWED_HOSTNAMES, RampApi, RampWidget, VERSION, RampWidget as default, getCurrencies, getRate, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
418
- export type { AvailableCurrencies, ChainType, CryptoCurrency, ErrorData, FiatCurrency, FlowType, GetRateParams, Locale, OrderData, QuoteData, RampApiConfig, RampWidgetConfig, RateResult, Theme, ValidationResult, WidgetEventType, WidgetMessage };
621
+ export { ADDRESS_PATTERNS, ALLOWED_PROTOCOLS, API_KEY_PREFIXES, DEV_ALLOWED_HOSTNAMES, RampApi, RampWidget, VERSION, RampWidget as default, getCurrencies, getRate, getSupportedNetworks, validateAddress, validateAddressBatch, validateApiKey, validateMessageOrigin, validateRedirectUrl, validateWalletAddress };
622
+ export type { AddressValidationBatchResult, AddressValidationResult, AvailableCurrencies, ChainType, CryptoCurrency, ErrorData, FiatCurrency, FlowType, GetRateParams, Locale, OrderData, QuoteData, RampApiConfig, RampWidgetConfig, RateResult, SupportedNetwork, SupportedNetworksResult, Theme, ValidateAddressParams, ValidationChainType, ValidationDetails, ValidationResult, WidgetEventType, WidgetMessage };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.9
2
+ * @nowramp/sdk v0.1.11
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"}},t={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},i=["https:"],n=["localhost","127.0.0.1"],r={public:"pk_",secret:"sk_"};function o(e){return e?e.includes(r.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(r.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function s(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&n.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function a(i,n){if(!i)return{valid:!0};const r=i.trim();if(n){const i=t[n.toUpperCase()];if(i){const{pattern:t,description:n}=e[i];return t.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:t}of Object.values(e))if(t.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function c(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=o(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!c(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),n=i.data?.attributes||i.data||i;return{rate:n.rate,sourceAmount:n.sourceAmount,sourceCurrency:n.sourceCurrency,destinationAmount:n.destinationAmount,destinationCurrency:n.destinationCurrency,fees:{processingFee:n.fees?.processingFee||0,processingFeePercent:n.fees?.processingFeePercent||0,networkFee:n.fees?.networkFee||0,totalFee:n.fees?.totalFee||0},provider:n.provider||"unknown",expiresAt:n.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}async function h(e,t){return new l(e).getRate(t)}async function p(e){return new l(e).getCurrencies()}const f="0.1.9";export{e as ADDRESS_PATTERNS,i as ALLOWED_PROTOCOLS,r as API_KEY_PREFIXES,t as CURRENCY_CHAIN_MAP,n as DEV_ALLOWED_HOSTNAMES,l as RampApi,d as RampWidget,f as VERSION,d as default,p as getCurrencies,h as getRate,o as validateApiKey,c as validateMessageOrigin,s as validateRedirectUrl,a as validateWalletAddress};
6
+ const e={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},t=["https:"],i=["localhost","127.0.0.1"],r={public:"pk_",secret:"sk_"};function n(e){return e?e.includes(r.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(r.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function s(e){if(!e)return{valid:!0};try{const r=new URL(e);return t.includes(r.protocol)||"http:"===r.protocol&&i.includes(r.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${r.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function o(t,i){if(!t)return{valid:!0};const r=t.trim();if(i){const t=i.toLowerCase(),n=e[t];if(n){const{pattern:e,description:t}=n;return e.test(r)?{valid:!0}:{valid:!1,error:t}}return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function a(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class d{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=n(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=s(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=o(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!a(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class c{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),r=i.data?.attributes||i.data||i;return{rate:r.rate,sourceAmount:r.sourceAmount,sourceCurrency:r.sourceCurrency,destinationAmount:r.destinationAmount,destinationCurrency:r.destinationCurrency,fees:{processingFee:r.fees?.processingFee||0,processingFeePercent:r.fees?.processingFeePercent||0,networkFee:r.fees?.networkFee||0,totalFee:r.fees?.totalFee||0},provider:r.provider||"unknown",expiresAt:r.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown",environment:i.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${i}`)}const i=await t.json();return i.data?.attributes||i.data||i}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${i}`)}const i=await t.json();return i.data?.attributes||i.data||i}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${i}`)}const t=await e.json();return t.data?.attributes||t.data||t}}async function l(e,t){return new c(e).getRate(t)}async function h(e){return new c(e).getCurrencies()}async function p(e,t){return new c({apiKey:"",projectId:"",...e}).validateAddress(t)}async function u(e,t){return new c({apiKey:"",projectId:"",...e}).validateAddressBatch(t)}async function g(e){return new c({apiKey:"",projectId:"",...e}).getSupportedNetworks()}const f="0.1.11";export{e as ADDRESS_PATTERNS,t as ALLOWED_PROTOCOLS,r as API_KEY_PREFIXES,i as DEV_ALLOWED_HOSTNAMES,c as RampApi,d as RampWidget,f as VERSION,d as default,h as getCurrencies,l as getRate,g as getSupportedNetworks,p as validateAddress,u as validateAddressBatch,n as validateApiKey,a as validateMessageOrigin,s as validateRedirectUrl,o as validateWalletAddress};
package/dist/sdk.umd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * @nowramp/sdk v0.1.9
2
+ * @nowramp/sdk v0.1.11
3
3
  * (c) 2026 NowRamp
4
4
  * @license MIT
5
5
  */
6
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NowRamp={})}(this,function(e){"use strict";const t={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"}},i={ETH:"evm",USDC:"evm",USDT:"evm",DAI:"evm",WETH:"evm",WBTC:"evm",MATIC:"evm",SOL:"solana",BTC:"bitcoin"},n=["https:"],r=["localhost","127.0.0.1"],o={public:"pk_",secret:"sk_"};function s(e){return e?e.includes(o.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(o.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function a(e){if(!e)return{valid:!0};try{const t=new URL(e);return n.includes(t.protocol)||"http:"===t.protocol&&r.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function c(e,n){if(!e)return{valid:!0};const r=e.trim();if(n){const e=i[n.toUpperCase()];if(e){const{pattern:i,description:n}=t[e];return i.test(r)?{valid:!0}:{valid:!1,error:n}}}for(const{pattern:e}of Object.values(t))if(e.test(r))return{valid:!0};return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"walletAddress format is not recognized"}}function d(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class l{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=s(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=a(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=c(this.config.walletAddress,this.config.destinationCurrency);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!d(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class h{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),n=i.data?.attributes||i.data||i;return{rate:n.rate,sourceAmount:n.sourceAmount,sourceCurrency:n.sourceCurrency,destinationAmount:n.destinationAmount,destinationCurrency:n.destinationCurrency,fees:{processingFee:n.fees?.processingFee||0,processingFeePercent:n.fees?.processingFeePercent||0,networkFee:n.fees?.networkFee||0,totalFee:n.fees?.totalFee||0},provider:n.provider||"unknown",expiresAt:n.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown"}}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=n,e.API_KEY_PREFIXES=o,e.CURRENCY_CHAIN_MAP=i,e.DEV_ALLOWED_HOSTNAMES=r,e.RampApi=h,e.RampWidget=l,e.VERSION="0.1.9",e.default=l,e.getCurrencies=async function(e){return new h(e).getCurrencies()},e.getRate=async function(e,t){return new h(e).getRate(t)},e.validateApiKey=s,e.validateMessageOrigin=d,e.validateRedirectUrl=a,e.validateWalletAddress=c,Object.defineProperty(e,"__esModule",{value:!0})});
6
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).NowRamp={})}(this,function(e){"use strict";const t={evm:{pattern:/^0x[a-fA-F0-9]{40}$/,description:"EVM address must start with 0x followed by 40 hexadecimal characters"},solana:{pattern:/^[1-9A-HJ-NP-Za-km-z]{32,44}$/,description:"Solana address must be 32-44 base58 characters"},bitcoin:{pattern:/^(1[a-km-zA-HJ-NP-Z1-9]{25,34}|3[a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/,description:"Bitcoin address must be a valid legacy, SegWit, or native SegWit format"},tezos:{pattern:/^(tz[123]|KT1)[a-zA-Z0-9]{33}$/,description:"Tezos address must start with tz1/tz2/tz3 (implicit) or KT1 (contract)"},other:{pattern:/^.{10,100}$/,description:"Address format varies by network (10-100 characters)"}},i=["https:"],r=["localhost","127.0.0.1"],n={public:"pk_",secret:"sk_"};function s(e){return e?e.includes(n.secret)?{valid:!1,error:"Secret API keys (sk_) cannot be used in the SDK. Use a public key (pk_) instead. Secret keys should only be used in backend server code."}:(e.includes(n.public)||console.warn("RampWidget: API key does not have a public key prefix (pk_). For security, please generate a new public key for frontend use."),{valid:!0}):{valid:!1,error:"apiKey is required"}}function o(e){if(!e)return{valid:!0};try{const t=new URL(e);return i.includes(t.protocol)||"http:"===t.protocol&&r.includes(t.hostname)?{valid:!0}:{valid:!1,error:`redirectUrl must use HTTPS (got ${t.protocol})`}}catch{return{valid:!1,error:"redirectUrl is not a valid URL"}}}function a(e,i){if(!e)return{valid:!0};const r=e.trim();if(i){const e=i.toLowerCase(),n=t[e];if(n){const{pattern:e,description:t}=n;return e.test(r)?{valid:!0}:{valid:!1,error:t}}return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}return r.length>=10&&r.length<=100?{valid:!0}:{valid:!1,error:"Address format not recognized"}}function d(e,t){if(!t)return!0;try{if(e===new URL(t).origin)return!0;const i=new URL(e);return"localhost"===i.hostname||"127.0.0.1"===i.hostname}catch{return!1}}class c{constructor(e){this.iframe=null,this.modalContainer=null,this.boundMessageHandler=null,this.isOpen=!1,this.config={widgetUrl:e.widgetUrl||"https://widget.nowramp.com/widget/latest/index.html",apiUrl:e.apiUrl||"https://api.nowramp.com",flowType:"buy",theme:"light",...e},this.validateConfig()}open(){this.isOpen?console.warn("RampWidget: Widget is already open"):(this.isOpen=!0,this.setupMessageListener(),this.createIframe())}close(){this.isOpen&&(this.isOpen=!1,this.cleanup(),this.config.onClose?.())}setAmount(e){this.sendCommand("SET_AMOUNT",e)}setTheme(e){this.sendCommand("SET_THEME",e)}refreshQuote(){this.sendCommand("REFRESH_QUOTE")}validateConfig(){if(!this.config.apiKey)throw new Error("RampWidget: apiKey is required");if(!this.config.projectId)throw new Error("RampWidget: projectId is required");if(!this.config.externalUserId)throw new Error("RampWidget: externalUserId is required");const e=s(this.config.apiKey);if(!e.valid)throw new Error(`RampWidget: ${e.error}`);if(this.config.redirectUrl){const e=o(this.config.redirectUrl);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}if(this.config.walletAddress){const e=a(this.config.walletAddress,void 0);if(!e.valid)throw new Error(`RampWidget: ${e.error}`)}}buildWidgetUrl(){const e=new URLSearchParams({apiKey:this.config.apiKey,projectId:this.config.projectId,externalUserId:this.config.externalUserId,parentOrigin:window.location.origin,flowType:this.config.flowType||"buy",theme:this.config.theme||"light"});return this.config.apiUrl&&e.set("apiUrl",this.config.apiUrl),this.config.locale&&e.set("locale",this.config.locale),void 0!==this.config.amount&&e.set("amount",String(this.config.amount)),this.config.sourceCurrency&&e.set("sourceCurrency",this.config.sourceCurrency),this.config.destinationCurrency&&e.set("destinationCurrency",this.config.destinationCurrency),this.config.walletAddress&&e.set("walletAddress",this.config.walletAddress),this.config.network&&e.set("network",this.config.network),this.config.provider&&e.set("provider",this.config.provider),this.config.redirectUrl&&e.set("redirectUrl",this.config.redirectUrl),!1===this.config.autoRedirect&&e.set("autoRedirect","false"),`${this.config.widgetUrl}?${e.toString()}`}createIframe(){this.iframe=document.createElement("iframe"),this.iframe.src=this.buildWidgetUrl(),this.iframe.style.border="none",this.iframe.style.width="100%",this.iframe.style.height="100%",this.iframe.allow="camera *; microphone *; geolocation *; encrypted-media *; clipboard-read; clipboard-write",this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-modals");const e=this.getContainer();e?e.appendChild(this.iframe):this.createModal()}getContainer(){return this.config.container?"string"==typeof this.config.container?document.querySelector(this.config.container):this.config.container:null}createModal(){this.modalContainer=document.createElement("div"),this.modalContainer.style.cssText="\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n ";const e=document.createElement("div");e.style.cssText="\n position: relative;\n width: 100%;\n max-width: 440px;\n height: 90vh;\n max-height: 700px;\n background: white;\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n ",this.iframe&&e.appendChild(this.iframe),this.modalContainer.appendChild(e),this.modalContainer.onclick=e=>{e.target===this.modalContainer&&this.close()};document.addEventListener("keydown",e=>{"Escape"===e.key&&this.close()}),document.body.appendChild(this.modalContainer),document.body.style.overflow="hidden"}setupMessageListener(){this.boundMessageHandler=this.handleMessage.bind(this),window.addEventListener("message",this.boundMessageHandler)}handleMessage(e){if(!d(e.origin,this.config.widgetUrl))return;const t=e.data;if(t&&"ramp-widget"===t.source)switch(t.type){case"WIDGET_READY":this.config.onReady?.();break;case"WIDGET_CLOSE":this.close();break;case"STEP_CHANGE":this.config.onStepChange?.(t.payload.step);break;case"QUOTE_CREATED":this.config.onQuoteCreated?.(t.payload);break;case"ORDER_CREATED":this.config.onOrderCreated?.(t.payload);break;case"ORDER_COMPLETED":this.config.onSuccess?.(t.payload);break;case"ERROR":this.config.onError?.(t.payload)}}sendCommand(e,t){if(!this.iframe?.contentWindow)return void console.warn("RampWidget: Cannot send command, widget not open");const i=this.config.widgetUrl?new URL(this.config.widgetUrl).origin:"*";this.iframe.contentWindow.postMessage({source:"ramp-sdk",type:e,payload:t},i)}cleanup(){this.boundMessageHandler&&(window.removeEventListener("message",this.boundMessageHandler),this.boundMessageHandler=null),this.iframe?.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.modalContainer?.parentNode&&(this.modalContainer.parentNode.removeChild(this.modalContainer),document.body.style.overflow=""),this.modalContainer=null}}class l{constructor(e){this.config=e,this.baseUrl=e.apiUrl||"https://api.nowramp.com"}async getRate(e){const t=await fetch(`${this.baseUrl}/v1/rates/convert`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({from:e.from,to:e.to,amount:e.amount,flowType:e.flowType||"buy"})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to get rate: ${i}`)}const i=await t.json(),r=i.data?.attributes||i.data||i;return{rate:r.rate,sourceAmount:r.sourceAmount,sourceCurrency:r.sourceCurrency,destinationAmount:r.destinationAmount,destinationCurrency:r.destinationCurrency,fees:{processingFee:r.fees?.processingFee||0,processingFeePercent:r.fees?.processingFeePercent||0,networkFee:r.fees?.networkFee||0,totalFee:r.fees?.totalFee||0},provider:r.provider||"unknown",expiresAt:r.expiresAt}}async getCurrencies(){const e=await fetch(`${this.baseUrl}/v1/rates/currencies`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get currencies: ${i}`)}const t=await e.json(),i=t.data?.attributes||t.data||t;return{fiat:i.fiat||[],crypto:i.crypto||[],provider:i.provider||"unknown",environment:i.environment||"sandbox"}}async validateAddress(e){const t=await fetch(`${this.baseUrl}/public/validate/address`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate address: ${i}`)}const i=await t.json();return i.data?.attributes||i.data||i}async validateAddressBatch(e){if(e.length>20)throw new Error("Maximum 20 addresses per batch");const t=await fetch(`${this.baseUrl}/public/validate/address/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({addresses:e})});if(!t.ok){const e=await t.json().catch(()=>({})),i=e.error?.message||e.message||`HTTP ${t.status}`;throw new Error(`Failed to validate addresses: ${i}`)}const i=await t.json();return i.data?.attributes||i.data||i}async getSupportedNetworks(){const e=await fetch(`${this.baseUrl}/public/validate/networks`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok){const t=await e.json().catch(()=>({})),i=t.error?.message||t.message||`HTTP ${e.status}`;throw new Error(`Failed to get supported networks: ${i}`)}const t=await e.json();return t.data?.attributes||t.data||t}}e.ADDRESS_PATTERNS=t,e.ALLOWED_PROTOCOLS=i,e.API_KEY_PREFIXES=n,e.DEV_ALLOWED_HOSTNAMES=r,e.RampApi=l,e.RampWidget=c,e.VERSION="0.1.11",e.default=c,e.getCurrencies=async function(e){return new l(e).getCurrencies()},e.getRate=async function(e,t){return new l(e).getRate(t)},e.getSupportedNetworks=async function(e){return new l({apiKey:"",projectId:"",...e}).getSupportedNetworks()},e.validateAddress=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddress(t)},e.validateAddressBatch=async function(e,t){return new l({apiKey:"",projectId:"",...e}).validateAddressBatch(t)},e.validateApiKey=s,e.validateMessageOrigin=d,e.validateRedirectUrl=o,e.validateWalletAddress=a,Object.defineProperty(e,"__esModule",{value:!0})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowramp/sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Official SDK for embedding the NowRamp fiat-to-crypto widget",
5
5
  "author": "NowRamp <support@nowramp.com>",
6
6
  "license": "MIT",