UNPKG

@xmr-core/xmr-mymonero-libs

Version:

A collection of utilities for building ontop of a MyMonero compatible API

142 lines (113 loc) 3.58 kB
import { config } from "@xmr-core/xmr-constants"; import { possibleOAAddress } from "../mymonero-send-tx/internal_libs/open_alias_lite"; import { decode_address, NetType } from "@xmr-core/xmr-crypto-utils"; import { Omit } from "../types"; export enum URITypes { addressAsFirstPathComponent = 1, addressAsAuthority = 2, } // we can stricten this typing using // a discriminate union later // since the TURITypes determines the nullity of the value type FundRequestPayload = { address: string; payment_id?: string | null; amount?: string | null; amountCcySymbol?: string | null; description?: string | null; message?: string | null; uriType: URITypes; }; export function encodeFundRequest(args: FundRequestPayload) { const address = args.address; if (!address) { throw Error("missing address"); } let mutable_uri = config.coinUriPrefix; const uriType = args.uriType; if (uriType === URITypes.addressAsAuthority) { mutable_uri += "//"; // use for inserting a // so data detectors pick it up… } else if (uriType === URITypes.addressAsFirstPathComponent) { // nothing to do } else { throw Error("Illegal args.uriType"); } mutable_uri += address; let queryParamStart = true; type ParamName = | "tx_amount" | "tx_amount_ccy" | "tx_description" | "tx_payment_id" | "tx_message"; function addParam(name: ParamName, value?: string | null) { if (!value) { return; } if (queryParamStart) { queryParamStart = false; } mutable_uri += queryParamStart ? "?" : "&"; mutable_uri += name + "=" + encodeURIComponent(value); } addParam("tx_amount", args.amount); const shouldAddCcySym = (args.amountCcySymbol || "").toLowerCase() !== config.coinSymbol.toLowerCase(); if (shouldAddCcySym) { addParam("tx_amount_ccy", args.amountCcySymbol); } addParam("tx_description", args.description); addParam("tx_payment_id", args.payment_id); addParam("tx_message", args.message); return mutable_uri; } type DecodeFundRequestPayload = Omit<FundRequestPayload, "uriType">; export function decodeFundRequest( str: string, nettype: NetType, ): DecodeFundRequestPayload { // detect no-scheme moneroAddr and possible OA addr - if has no monero: prefix if (!str.startsWith(config.coinUriPrefix)) { if (str.includes("?")) { // fairly sure this is correct.. (just an extra failsafe/filter) throw Error("Unrecognized URI format"); } if (possibleOAAddress(str)) { return { address: str, }; } try { decode_address(str, nettype); } catch (e) { throw Error("No Monero request info"); } // then it looks like a monero address return { address: str, }; } const url = new window.URL(str); const protocol = url.protocol; if (protocol !== config.coinUriPrefix) { throw Error("Request URI has non-Monero protocol"); } // it seems that if the URL has // in it, pathname will be empty, but host will contain the address instead let target_address = url.pathname || url.host || url.hostname; if (target_address.startsWith("//")) { target_address = target_address.slice("//".length); // strip prefixing "//" in case URL had protocol:// instead of protocol: } const searchParams = url.searchParams; const payload: DecodeFundRequestPayload = { address: target_address, }; const keyPrefixToTrim = "tx_"; (searchParams as any).forEach((value: string, key: string) => { const index = key.startsWith(keyPrefixToTrim) ? key.slice(keyPrefixToTrim.length, key.length) : key; payload[index as keyof DecodeFundRequestPayload] = value; }); return payload; }