UNPKG

@gala-chain/launchpad-sdk

Version:

TypeScript SDK for Gala Launchpad Backend API - Production-ready DeFi token launchpad integration with wallet-based authentication, GalaChain trading, and comprehensive user operations. 100% tested (22/22 endpoints working).

1 lines 114 kB
import{ethers as e,Wallet as t,Mnemonic as a,HDNodeWallet as n}from"ethers";import r from"axios";import{SigningType as s,SigningClient as o,ChainCallDTO as i,calculatePersonalSignPrefix as c}from"@gala-chain/connect";import{z as l}from"zod";import{TokenClassKey as d}from"@gala-chain/api";import u from"bignumber.js";import{v4 as h}from"uuid";import{io as g}from"socket.io-client";var p,m;!function(e){e.WALLET_NOT_CONNECTED="WALLET_NOT_CONNECTED",e.SIGNATURE_FAILED="SIGNATURE_FAILED",e.INVALID_ADDRESS="INVALID_ADDRESS",e.MESSAGE_GENERATION_FAILED="MESSAGE_GENERATION_FAILED"}(p||(p={}));class f extends Error{constructor(e,t,a){super(t),this.type=e,this.originalError=a,this.name="AuthError"}}class y{constructor(e){if(this.wallet=e.wallet,this.messagePrefix=e.messagePrefix||"Create a GalaChain Wallet",""===e.messagePrefix)throw new f(p.SIGNATURE_FAILED,"Message prefix cannot be empty");this.validateWallet()}async generateSignature(){try{const e=Date.now(),t=`${this.messagePrefix} ${e}`,a=await this.wallet.signMessage(t);return{message:t,signature:a,address:this.formatAddress(this.wallet.address),timestamp:e}}catch(e){throw new f(p.SIGNATURE_FAILED,"Failed to generate signature for authentication",e instanceof Error?e:new Error(String(e)))}}getAddress(){return this.formatAddress(this.wallet.address)}getEthereumAddress(){return this.wallet.address}formatAddress(e){const t=e.replace(/^0x/i,"");if(!/^[a-fA-F0-9]{40}$/.test(t))throw new f(p.INVALID_ADDRESS,`Invalid Ethereum address format: ${e}`);return`eth|${t}`}async signMessage(e){try{return{message:e,signature:await this.wallet.signMessage(e),address:this.wallet.address,timestamp:Date.now()}}catch(e){const t=e instanceof Error?e.message:String(e);throw new f(p.SIGNATURE_FAILED,t,e instanceof Error?e:new Error(String(e)))}}async generateAuthHeaders(e,t){try{const a=Date.now(),n=`${this.messagePrefix} ${t.toUpperCase()} ${e} ${a}`,r=await this.wallet.signMessage(n);return{"x-signature":r,"x-address":this.formatAddress(this.wallet.address),"x-message":n,"x-timestamp":a.toString()}}catch(e){throw new f(p.SIGNATURE_FAILED,"Failed to generate authentication headers",e instanceof Error?e:new Error(String(e)))}}async signTypedData(e,t,a){try{return await this.wallet.signTypedData(e,t,a)}catch(e){throw new f(p.SIGNATURE_FAILED,"Failed to sign typed data",e instanceof Error?e:new Error(String(e)))}}async generateCustomSignature(e){if(!e||"string"!=typeof e||0===e.trim().length)throw new f(p.SIGNATURE_FAILED,"Custom message must be a non-empty string");try{const t=await this.wallet.signMessage(e);return{message:e,signature:t,address:this.formatAddress(this.wallet.address),timestamp:Date.now()}}catch(e){throw new f(p.SIGNATURE_FAILED,"Failed to generate custom message signature",e instanceof Error?e:new Error(String(e)))}}validateWallet(){if(!this.wallet)throw new f(p.WALLET_NOT_CONNECTED,"Wallet is required for authentication");if(!this.wallet.address)throw new f(p.WALLET_NOT_CONNECTED,"Wallet address is not available");if(!this.wallet.privateKey&&!this.wallet.signMessage)throw new f(p.WALLET_NOT_CONNECTED,"Wallet must have a private key for signing messages")}}!function(e){e.DEBUG="DEBUG",e.INFO="INFO",e.WARN="WARN",e.ERROR="ERROR"}(m||(m={}));class A{constructor(e){this.levelPriority={[m.DEBUG]:0,[m.INFO]:1,[m.WARN]:2,[m.ERROR]:3},this.debugEnabled=e.debug,this.context=e.context||"SDK",this.minLevel=e.minLevel||(e.debug?m.DEBUG:m.INFO)}debug(e,t){this.log(m.DEBUG,e,t)}info(e,t){this.log(m.INFO,e,t)}warn(e,t){this.log(m.WARN,e,t)}error(e,t){this.log(m.ERROR,e,t)}log(e,t,a){if(this.levelPriority[e]<this.levelPriority[this.minLevel])return;if(e===m.DEBUG&&!this.debugEnabled)return;const n=`[${(new Date).toISOString()}] [${this.context}] [${e}]`,r=this.getConsoleMethod(e);void 0!==a?a instanceof Error?r(`${n} ${t}`,a.message,a.stack):r(`${n} ${t}`,a):r(`${n} ${t}`)}getConsoleMethod(e){switch(e){case m.DEBUG:return console.debug;case m.INFO:return console.info;case m.WARN:return console.warn;case m.ERROR:return console.error;default:return console.log}}child(e){return new A({debug:this.debugEnabled,context:`${this.context}:${e}`,minLevel:this.minLevel})}isDebugEnabled(){return this.debugEnabled&&this.levelPriority[m.DEBUG]>=this.levelPriority[this.minLevel]}}function w(e){const t={};for(const[a,n]of Object.entries(e))null!=n&&("string"==typeof n?t[a]=n:"number"==typeof n||"boolean"==typeof n?t[a]=n.toString():Array.isArray(n)?t[a]=n.join(","):t[a]="object"==typeof n?JSON.stringify(n):String(n));return t}class k{constructor(e,t={}){this.auth=e,this.debug=t.debug??!1,this.logger=new A({debug:this.debug,context:"HttpClient"}),this.axios=r.create({baseURL:t.baseUrl||"https://lpad-backend-dev1.defi.gala.com",timeout:t.timeout||3e4,headers:{Accept:"application/json",...t.headers}}),this.setupInterceptors()}async request(e){try{const t={method:e.method,url:e.url,data:e.data,...e.params&&{params:w(e.params)},...e.headers&&{headers:e.headers},...e.timeout&&{timeout:e.timeout}};e.data instanceof FormData&&(t.headers&&t.headers["Content-Type"]&&delete t.headers["Content-Type"],this.logger.debug("FormData detected - removing Content-Type header for multipart upload"));const a=e.data instanceof FormData?"[FormData object - multipart/form-data]":e.data;this.logger.debug("Request:",{method:e.method,url:e.url,fullUrl:`${this.axios.defaults.baseURL}${e.url}`,baseURL:this.axios.defaults.baseURL,params:t.params,data:a,isFormData:e.data instanceof FormData,contentType:t.headers?.["Content-Type"]||"not set"});const n=await this.axios.request(t);return this.logger.debug("Response:",{status:n.status,data:n.data}),n.data}catch(e){throw this.logger.error("Error:",e),e}}async get(e,t,a){return this.request({method:"GET",url:e,...t&&{params:t},...a&&{headers:a}})}async post(e,t,a){return this.request({method:"POST",url:e,data:t,...a&&{headers:a}})}async put(e,t,a){return this.request({method:"PUT",url:e,data:t,...a&&{headers:a}})}async delete(e,t,a){return this.request({method:"DELETE",url:e,...t&&{params:t},...a&&{headers:a}})}async patch(e,t,a){return this.request({method:"PATCH",url:e,data:t,...a&&{headers:a}})}getAddress(){return this.auth.getAddress()}getEthereumAddress(){return this.auth.getEthereumAddress()}async signMessage(e){return(await this.auth.signMessage(e)).signature}async signTypedData(e,t,a){return await this.auth.signTypedData(e,t,a)}async signCustomMessage(e){try{const t=await this.auth.generateCustomSignature(e);return this.logger.debug("Generated custom signature:",{message:e,address:t.address,ethereumAddress:this.auth.getEthereumAddress()}),{signature:t.signature,address:t.address,ethereumAddress:this.auth.getEthereumAddress()}}catch(e){throw this.logger.error("Custom signature generation failed:",e),new Error(`Failed to generate custom signature for message: ${e instanceof Error?e.message:"Unknown error"}`)}}async signWithGalaChain(e,t,a=s.SIGN_TYPED_DATA){const n=this.auth.wallet.privateKey;if(!n)throw new Error("Wallet private key not available for @gala-chain signing");const r=new o(n);return await r.sign(e,t,a)}setupInterceptors(){this.requestInterceptorId=this.axios.interceptors.request.use(async e=>{try{const t=await this.auth.generateSignature();return e.headers||(e.headers={}),e.headers.Sign=t.signature,e.data instanceof FormData||(e.headers["Content-Type"]="application/json"),this.logger.debug("Added signature header:",{address:t.address,message:t.message,timestamp:t.timestamp}),e}catch(e){throw this.logger.error("Failed to add signature:",e),e}},e=>Promise.reject(e)),this.responseInterceptorId=this.axios.interceptors.response.use(e=>e,e=>{if(e.response){const t={message:e.response.data?.message||e.message,error:e.response.data?.error,statusCode:e.response.status,details:e.response.data?.details,timestamp:e.response.data?.timestamp,path:e.response.data?.path};e.launchpadError=t,this.logger.error("Backend error:",t)}else e.request?this.logger.error("Network error:",e.message):this.logger.error("Request setup error:",e.message);return Promise.reject(e)})}cleanup(){void 0!==this.requestInterceptorId&&(this.axios.interceptors.request.eject(this.requestInterceptorId),this.requestInterceptorId=void 0),void 0!==this.responseInterceptorId&&(this.axios.interceptors.response.eject(this.responseInterceptorId),this.responseInterceptorId=void 0),this.logger.debug("Interceptors cleaned up")}}const v="Token name is required and must be a string",T=e=>`Could not find vault address for token: ${e}`;class b extends Error{constructor(e,t,a){super(e),this.field=t,this.code=a,this.name="ValidationError"}}class E extends Error{constructor(e,t,a){super(e),this.statusCode=t,this.originalError=a,this.name="NetworkError"}}class N extends Error{constructor(e,t){super(e),this.field=t,this.name="ConfigurationError"}}class I extends Error{constructor(e,t,a){super(e),this.transactionId=t,this.code=a,this.name="TransactionError"}}function S(e,t,a){const{MIN_PAGE:n,MAX_PAGE:r,MIN_LIMIT:s,MAX_LIMIT:o}=a.PAGINATION;if("number"!=typeof e||e<n||e>r)throw new b(`Page must be a number between ${n} and ${r}`,"page","INVALID_PAGE");if("number"!=typeof t||t<s||t>o)throw new b(`Limit must be a number between ${s} and ${o}`,"limit","INVALID_LIMIT")}const D={ETH_ADDRESS:/^0x[0-9a-fA-F]{40}$/,BACKEND_ADDRESS:/^eth\|[0-9a-fA-F]{40}$/};function F(e){return"string"==typeof e&&e.trim().length>0}function $(e){return!(!e||"string"!=typeof e)&&(D.ETH_ADDRESS.test(e)||D.BACKEND_ADDRESS.test(e))}function C(e){return e.startsWith("0x")?`eth|${e.slice(2)}`:e}const _=l.string().min(3,"Token name must be at least 3 characters").max(20,"Token name must be at most 20 characters").regex(/^[a-zA-Z0-9]{3,20}$/,"Token name can only contain letters and numbers"),U=l.string().min(1,"Token symbol must be at least 1 character").max(8,"Token symbol must be at most 8 characters").regex(/^[A-Z]{1,8}$/,"Token symbol must be uppercase letters only"),P=l.string().min(1,"Token description is required").max(500,"Token description must be at most 500 characters"),L=l.string().min(1,"Token name must be at least 1 character").max(50,"Token name must be at most 50 characters"),O=l.string().min(1,"Search query must be at least 1 character").max(100,"Search query must be at most 100 characters"),x=l.string().min(1,"Full name is required").max(100,"Full name must be at most 100 characters").regex(/^[a-zA-Z\s]+$/,"Full name can only contain letters and spaces"),R=l.string().regex(D.BACKEND_ADDRESS,"Address must be in format: eth|[40-hex-chars]"),B=l.string().regex(D.ETH_ADDRESS,"Invalid Ethereum address format"),M=l.string().refine(e=>D.BACKEND_ADDRESS.test(e)||D.ETH_ADDRESS.test(e),"Address must be either eth|[40-hex-chars] or 0x[40-hex-chars] format").transform(e=>e.startsWith("0x")?`eth|${e.slice(2)}`:e),K=l.string().refine(e=>D.BACKEND_ADDRESS.test(e)||/^service\|Token\$Unit\$[A-Z0-9]+\$eth:[0-9a-fA-F]{40}\$launchpad$/.test(e),"Invalid vault address format"),G=l.string().regex(/^\d+(\.\d+)?$/,"Must be a valid decimal number").refine(e=>parseFloat(e)>0,"Amount must be greater than zero"),V=l.string().regex(/^\d+(\.\d+)?$/,"Must be a valid decimal number").refine(e=>parseFloat(e)>=0,"Amount must be zero or greater"),j=l.string().regex(/^(?!0+(\.0+)?$)\d+(\.\d+)?$/,"Amount must be a positive, non-zero number"),q=l.string().url("Must be a valid URL").regex(/^https?:\/\//,"URL must start with http:// or https://"),W=l.string().optional().refine(e=>!e||/^https?:\/\/.+\..+/.test(e),"Must be a valid URL if provided"),H=l.number().int("Page must be an integer").min(1,"Page must be at least 1").max(1e3,"Page must be at most 1000").default(1);function Q(e=100){return l.number().int("Limit must be an integer").min(1,"Limit must be at least 1").max(e,`Limit must be at most ${e}`).default(10)}const X=Q(100),z=Q(20),Y=Q(20),J=l.number().int("File size must be an integer").min(1,"File must be at least 1 byte").max(10485760,"File must be at most 10MB"),Z=l.string().max(255,"Filename must be at most 255 characters"),ee=l.enum(["image/png","image/jpg","image/jpeg","image/gif","image/webp","image/svg+xml"]),te=l.string().min(1,"Comment message is required").max(500,"Comment must be at most 500 characters"),ae=l.string().datetime("Must be a valid ISO 8601 date string"),ne=l.number().int("Timestamp must be an integer").min(0,"Timestamp must be non-negative"),re=l.string().regex(/^0x[a-fA-F0-9]{64}$/,"Private key must be format: 0x + 64 hex characters"),se=l.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/,"Transaction ID must be in UUID format"),oe=l.string().regex(/^galaconnect-operation-[a-z0-9-]+$/,"Unique key must be format: galaconnect-operation-{unique-id}"),ie=l.object({websiteUrl:W,telegramUrl:W,twitterUrl:W}).refine(e=>e.websiteUrl||e.telegramUrl||e.twitterUrl,"At least one social URL (website, telegram, or twitter) is required"),ce=l.string().min(1,"Token category must not be empty").default("Unit"),le=l.string().min(1,"Token collection must not be empty").default("Token"),de=l.object({minFeePortion:G,maxFeePortion:G}),ue=l.object({tokenName:_,tokenSymbol:U,tokenDescription:P,tokenImage:l.union([l.instanceof(File),l.instanceof(Buffer),l.string().url("Token image must be a valid URL")]).optional(),preBuyQuantity:V.default("0"),websiteUrl:W,telegramUrl:W,twitterUrl:W,tokenCategory:ce,tokenCollection:le,reverseBondingCurveConfiguration:de.optional(),privateKey:re.optional()}),he=l.object({file:l.union([l.instanceof(File),l.instanceof(Buffer)]),tokenName:_}),ge=l.enum(["recent","popular"]),pe=l.object({tokenName:_.optional(),symbol:U.optional()}).refine(e=>e.tokenName||e.symbol,"At least one of tokenName or symbol is required"),me=l.enum(["NATIVE","MEME"]),fe=l.enum(["IN","OUT"]),ye=l.object({from:l.number().int("From timestamp must be an integer").min(173e6,"From timestamp must be at least 173000000"),to:l.number().int("To timestamp must be an integer").min(173e6,"To timestamp must be at least 173000000"),resolution:l.number().int("Resolution must be an integer").min(1,"Resolution must be at least 1"),tokenName:_}),Ae=l.object({tokenName:_,slippageToleranceFactor:l.number().min(0).max(1).optional(),maxAcceptableReverseBondingCurveFeeSlippageFactor:l.number().min(0).max(1).optional(),privateKey:re.optional()}),we=[".png",".jpg",".jpeg",".gif",".webp",".svg"],ke=l.object({file:l.union([l.instanceof(File),l.instanceof(Buffer)]),name:Z,size:J,type:ee}),ve=l.instanceof(File).refine(e=>e.size>=1&&e.size<=10485760,"File size must be between 1 byte and 10MB").refine(e=>["image/png","image/jpg","image/jpeg","image/gif","image/webp","image/svg+xml"].includes(e.type),"File must be a valid image type (PNG, JPG, JPEG, GIF, WebP, or SVG)").refine(e=>e.name.length<=255,"Filename must be at most 255 characters"),Te=l.instanceof(Buffer).refine(e=>e.length>=1&&e.length<=10485760,"Buffer size must be between 1 byte and 10MB"),be=l.union([ve,Te]),Ee=l.enum([".png",".jpg",".jpeg",".gif",".webp",".svg"]),Ne=Z.refine(e=>{const t=e.slice(e.lastIndexOf(".")).toLowerCase();return we.includes(t)},`Filename must end with one of: ${we.join(", ")}`),Ie=l.object({page:H,limit:X}),Se=l.object({page:H,limit:z}),De=l.object({page:H,limit:Y}),Fe=l.object({page:H,limit:Q(50)}),$e=Ie.extend({type:l.enum(["recent","popular"]).optional(),tokenName:l.string().min(1).max(50).optional(),search:l.string().min(1).max(100).optional()}),Ce=Se.extend({tokenName:l.string().min(1).max(50).optional(),search:l.string().min(1).max(100).optional()}),_e=De.extend({tradeType:l.enum(["BUY","SELL"]).optional(),tokenName:l.string().min(1).max(50).optional(),userAddress:l.string().regex(/^(0x[a-fA-F0-9]{40}|eth\|[a-fA-F0-9]{40})$/).optional(),startDate:l.string().datetime().optional(),endDate:l.string().datetime().optional(),sortOrder:l.enum(["ASC","DESC"]).default("DESC")}),Ue=l.object({page:l.number().int().min(1),limit:l.number().int().min(1),total:l.number().int().min(0),totalPages:l.number().int().min(0),hasNext:l.boolean(),hasPrevious:l.boolean()});function Pe(e){return l.object({data:l.array(e),page:l.number().int().min(1),limit:l.number().int().min(1),total:l.number().int().min(0),totalPages:l.number().int().min(0),hasNext:l.boolean(),hasPrevious:l.boolean()})}const Le=l.enum(["all","DEFI","ASSET"]),Oe=Se.extend({type:Le.optional(),address:M.optional(),search:O.optional(),tokenName:L.optional()}),xe=l.object({walletAddress:M,amount:j}),Re=l.object({address:M.optional(),refresh:l.boolean().optional()}),Be=l.object({profileImage:l.string(),fullName:x,address:M,privateKey:re.optional()}),Me=l.object({file:l.union([l.instanceof(File),l.instanceof(Buffer)]),address:M.optional(),privateKey:re.optional()}),Ke=l.object({address:M,tokenId:l.union([l.string(),l.object({collection:l.string(),category:l.string(),type:l.string(),additionalKey:l.string()}),l.object({collection:l.string(),category:l.string(),type:l.string(),additionalKey:l.string(),instance:l.string()})]).optional(),tokenClassKey:l.object({collection:l.string(),category:l.string(),type:l.string(),additionalKey:l.string()}).optional(),tokenName:L.optional()}).refine(e=>void 0!==e.tokenId||void 0!==e.tokenName,"At least one token identifier (tokenId or tokenName) is required"),Ge=l.enum(["buy","sell"]),Ve=l.enum(["BUY","SELL"]),je=l.object({tradeType:Ge,tokenAmount:G,vaultAddress:K,userAddress:M,slippageTolerance:G.optional(),deadline:l.number().int().positive().optional()}),qe=l.object({tokenSymbol:U,nativeTokenQuantity:G,expectedToken:G,maxAcceptableReverseBondingCurveFee:V.default("0").optional()}),We=l.object({tokenSymbol:U,tokenQuantity:G,expectedNativeToken:G,maxAcceptableReverseBondingCurveFee:V.default("0").optional()}),He=De.extend({tokenName:L.optional()}),Qe=l.object({page:l.number().int().min(1).max(1e3).default(1).optional(),limit:l.number().int().min(1).max(20).default(10).optional()}),Xe=l.enum(["NATIVE","MEME"]),ze=l.enum(["IN","OUT"]),Ye=l.object({type:Xe,method:ze,vaultAddress:K,amount:G}),Je=l.object({nativeTokenQuantity:G}),Ze=l.object({vaultAddress:K}),et=l.object({minFeePortion:G,maxFeePortion:G});function tt(e){return t=>{const a=e.safeParse(t);return{success:a.success,data:a.success?a.data:void 0,errors:a.success?void 0:a.error.errors.map(e=>e.message)}}}const at=tt(_),nt=tt(U),rt=tt(P),st=tt(M),ot=tt(K),it=tt(G),ct=tt(j),lt=tt(x),dt=tt(O),ut=tt(L),ht=tt(ue),gt=tt(ie),pt=tt(he),mt=tt(pe),ft=tt(Oe),yt=tt(xe),At=tt(Re),wt=tt(Be),kt=tt(Me),vt=tt(Ke),Tt=tt(je),bt=tt(qe),Et=tt(We),Nt=tt(He),It=tt(Qe),St=tt(Ye),Dt=tt(Je),Ft=tt(Ze);function $t(e,t){throw new b(e.join("; "),t,"VALIDATION_ERROR")}function Ct(e){const t=at(e);!t.success&&t.errors&&$t(t.errors,"tokenName")}function _t(e){const t=$e.safeParse(e);t.success||$t(t.error.errors.map(e=>e.message),"pagination")}function Ut(e){const t=mt(e);!t.success&&t.errors&&$t(t.errors,"options")}function Pt(e){const t=St(e);!t.success&&t.errors&&$t(t.errors,"options")}function Lt(e){const t=ye.safeParse(e);t.success||$t(t.error.errors.map(e=>e.message),"options")}function Ot(e){const t=M.safeParse(e);if(!t.success)throw new b("Ethereum address must be 40 hex characters (with or without 0x prefix)","ethereumAddress","INVALID_FORMAT");return t.data}var xt=Object.freeze({__proto__:null,normalizeAddressInput:function(e){if(!e)return;const t=M.safeParse(e);if(!t.success)throw new b(`Invalid address format: ${e}. Must be either "0x..." (Ethereum) or "eth|..." (GalaChain) format`,"address","INVALID_FORMAT");return t.data},toBackendAddressFormat:Ot,validateCheckPoolOptions:Ut,validateGetAmountOptions:Pt,validateGetGraphOptions:Lt,validatePagination:_t,validateTokenName:Ct});function Rt(e){if(!e)return[];return(Array.isArray(e)?e:e.token??[]).map(e=>({...e,createdAt:new Date(e.createdAt),updatedAt:new Date(e.updatedAt)}))}function Bt(e,t){const a=Number(e.page)||t.page,n=Number(e.limit)||t.limit,r=Number(e.total)||Number(e.data?.count)||0;return{page:a,limit:n,total:r,totalPages:Math.ceil(r/n)}}function Mt(e,t){return{hasNext:e<t,hasPrevious:e>1}}function Kt(e,t,a=!1){const n=!0===e.error||200!==e.status,r=a&&!e.data;if(n||r)throw new Error(e.message||t)}const Gt="/launchpad/upload-image",Vt="/launchpad/fetch-pool",jt="/launchpad/check-pool",qt="/launchpad/get-graph-data",Wt="/holders",Ht="/launchpad/get-badge/",Qt="/trade/",Xt="/token/commment",zt="/token/commment",Yt="/user/profile",Jt="/user/profile",Zt="/user/token-list",ea="/user/token-hold",ta="/user/transfer-faucets";function aa(e,t){return new b(`Token "${e}" not found. Please verify the token name is correct.`,"tokenName","TOKEN_NOT_FOUND")}function na(e,t){const a=t||e.charAt(0).toUpperCase()+e.slice(1);return new b(`${a} is required`,e,"REQUIRED_FIELD")}function ra(e,t,a){const n=a||e.charAt(0).toUpperCase()+e.slice(1);return new b(`${n} must be ${t}`,e,"INVALID_FORMAT")}function sa(e,t,a){return new E(e,t,a)}function oa(e,t){return new N(e,t)}function ia(e,t,a){return new I(e,t,a)}function ca(e){return e&&"object"==typeof e&&"string"==typeof e.tokenName&&(void 0===e.from||"number"==typeof e.from)&&(void 0===e.to||"number"==typeof e.to)&&(void 0===e.resolution||"number"==typeof e.resolution)}class la{constructor(e){this.http=e}async fetchPools(e={}){let t;_t({page:e.page??1,limit:e.limit??10}),e.tokenName&&Ct(e.tokenName),"recent"===e.type?t="RECENT":"popular"===e.type&&(t="POPULAR");const a={page:e.page||1,limit:e.limit||10};e.search&&(a.search=e.search),e.tokenName&&(a.tokenName=e.tokenName),t&&(a.type=t);const n={page:a.page.toString(),limit:a.limit.toString()};void 0!==a.type&&(n.type=a.type),void 0!==a.tokenName&&(n.tokenName=a.tokenName),void 0!==a.search&&(n.search=a.search);const r=w(n),s=await this.http.get(Vt,r);Kt(s,"Failed to fetch pools",!0);return{pools:function(e){if(!e)return[];let t=[];return e.tokens?t=Array.isArray(e.tokens)?e.tokens.map(e=>({...e,createdAt:new Date(e.created_at??e.createdAt)})):[{...e.tokens,createdAt:new Date(e.tokens.created_at??e.tokens.createdAt)}]:e.pools&&Array.isArray(e.pools)&&(t=e.pools.map(e=>({...e,createdAt:new Date(e.created_at??e.createdAt)}))),t}(s.data),page:s.data.page,limit:s.data.limit,total:s.data.total,totalPages:s.data.totalPages,hasNext:s.data.page<s.data.totalPages,hasPrevious:s.data.page>1}}async checkPool(e){Ut(e),e.tokenName&&Ct(e.tokenName);const t=w(e),a=await this.http.get(jt,t);Kt(a,"Failed to check pool");const n=a.data;return e.symbol?n?.isSymbolExist??!1:e.tokenName?n?.isNameExist??!1:n?.exists??!1}async isTokenNameAvailable(e){try{return!await this.checkPool({tokenName:e})}catch{return!1}}async isTokenSymbolAvailable(e){try{return!await this.checkPool({symbol:e})}catch{return!1}}async fetchVolumeData(e){if(!ca(e))throw new b("Invalid options provided. Expected { tokenName: string, from?: number, to?: number, resolution?: number }","options","INVALID_OPTIONS");const{tokenName:t,from:a,to:n,resolution:r}=e;if(Ct(t),!a||!n||!r)throw new b("Graph options (from, to, resolution) are required","options","MISSING_GRAPH_OPTIONS");const s={tokenName:t,from:a,to:n,resolution:r};Lt(s);const o=w(s),i=await this.http.get(qt,o);return Kt(i,"Failed to fetch graph data",!0),{dataPoints:i.data}}async fetchTokenDistribution(e){if(!e)throw na("tokenName","Token name");Ct(e);const t=await this.resolveTokenNameToVault(e);if(!t)throw new b(T(e),"tokenName","VAULT_NOT_FOUND");const a=encodeURIComponent(t),n=await this.http.get(`${Wt}/${a}`);return Kt(n,"Failed to fetch token distribution",!0),{holders:n.data.holders||[],totalSupply:n.data.totalSupply||"0",totalHolders:n.data.totalHolders||0,lastUpdated:new Date}}async fetchTokenBadges(e){if(!e)throw na("tokenName","Token name");Ct(e);const t=await this.http.get(Ht,{tokenName:e});return Kt(t,"Failed to fetch token badges",!0),{volumeBadges:t.data.volumeBadge||[],engagementBadges:t.data.engagementBadge||[]}}async hasTokenBadge(e){const{tokenName:t,badgeType:a,badgeName:n}=e;try{const e=await this.fetchTokenBadges(t);if(!e)return!1;const r=("volume"===a?e.volumeBadges:e.engagementBadges).find(e=>e.badgeName===n);return r?.isActive||!1}catch{return!1}}async resolveTokenNameToVault(e){try{const t=await this.fetchPools({tokenName:e});return t.pools&&Array.isArray(t.pools)&&t.pools.length>0?t.pools[0].vaultAddress||null:t.pools&&t.pools.tokens&&t.pools.tokens.vaultAddress||null}catch{return null}}}function da(e,t={}){const{stringifyFields:a=[],optionalFields:n=[],fieldMappings:r={}}=t,s={};for(const[t,o]of Object.entries(e)){const e=t;if(n.includes(e)&&void 0===o)continue;if(n.includes(e)&&"string"==typeof o&&0===o.trim().length)continue;const i=r[e],c=i?String(i):t;a.includes(e)?s[c]=String(o):s[c]=o}return w(s)}const ua={PAGINATION:{MIN_PAGE:1,MAX_PAGE:1e3,MIN_LIMIT:1,MAX_LIMIT:20}};class ha{constructor(e){this.http=e}async fetchTrades(e){if(!(t=e)||"object"!=typeof t||"string"!=typeof t.tokenName||void 0!==t.tradeType&&"buy"!==t.tradeType&&"sell"!==t.tradeType||void 0!==t.userAddress&&"string"!=typeof t.userAddress||void 0!==t.page&&"number"!=typeof t.page||void 0!==t.limit&&"number"!=typeof t.limit)throw new b("Invalid options provided. Expected { tokenName: string, tradeType?: string, userAddress?: string, page?: number, limit?: number, startDate?: Date, endDate?: Date, sortOrder?: string }","options","INVALID_OPTIONS");var t;const{tokenName:a,tradeType:n,userAddress:r,page:s=1,limit:o=10,startDate:i,endDate:c,sortOrder:l}=e;if(!F(a))throw new b("Token name is required and must be a non-empty string","tokenName","INVALID_TOKEN_NAME");S(s,o,ua);const d=function(e,t,a){return da({tokenName:e,page:t,limit:a},{stringifyFields:["page","limit"]})}(a,s,o),u=await this.http.get(Qt,d),h=(g=u.data)?(Array.isArray(g)?g:g.trades?g.trades:[]).map(e=>({...e,createdAt:new Date(e.createdAt),updatedAt:new Date(e.updatedAt)})):[];var g;const p=Bt(u,{page:s,limit:o}),m=Mt(p.page,p.totalPages);return{trades:h,...p,...m}}}const ga=new A({debug:!1,context:"DateUtils"});function pa(e,t){if(!e)return t||new Date;if(e instanceof Date)return isNaN(e.getTime())?t||new Date:e;try{const a=new Date(e);return isNaN(a.getTime())?(ga.warn(`Invalid date string received: "${e}". Using fallback.`),t||new Date):a}catch(a){return ga.warn(`Date parsing error for "${e}":`,a),t||new Date}}const ma={PAGINATION:{MIN_PAGE:1,MAX_PAGE:1e3,MIN_LIMIT:1,MAX_LIMIT:50},CONTENT:{MIN_LENGTH:1,MAX_LENGTH:500}};class fa{constructor(e,t){this.http=e,this.poolService=t}async fetchComments(e){if(!(t=e)||"object"!=typeof t||"string"!=typeof t.tokenName||void 0!==t.page&&"number"!=typeof t.page||void 0!==t.limit&&"number"!=typeof t.limit)throw new b("Invalid options provided. Expected { tokenName: string, page?: number, limit?: number }","options","INVALID_OPTIONS");var t;const{tokenName:a,page:n=1,limit:r=10}=e;if(!F(a))throw new b("Token name is required and must be a non-empty string","tokenName","INVALID_TOKEN_NAME");S(n,r,ma);const s=await this.poolService.resolveTokenNameToVault(a);if(!s)throw aa(a);const o=da({vaultAddress:s,page:n,limit:r},{stringifyFields:["page","limit"]}),i=await this.http.get(Xt,o);Kt(i,"Failed to fetch comments");return{comments:i.data.comments.map(e=>({...e,createdAt:pa(e.createdAt)})),total:i.data.count}}async postComment(e){if(!(t=e)||"object"!=typeof t||"string"!=typeof t.tokenName||"string"!=typeof t.content)throw new b("Invalid options provided. Expected { tokenName: string, content: string }","options","INVALID_OPTIONS");var t;const{tokenName:a,content:n}=e;if(!F(a))throw new b("Token name is required and must be a non-empty string","tokenName","INVALID_TOKEN_NAME");if(!function(e){if(!e||"string"!=typeof e)return!1;const t=e.trim();return t.length>=ma.CONTENT.MIN_LENGTH&&t.length<=ma.CONTENT.MAX_LENGTH}(n))throw new b(`Comment content must be between ${ma.CONTENT.MIN_LENGTH} and ${ma.CONTENT.MAX_LENGTH} characters`,"content","INVALID_CONTENT");const r=await this.poolService.resolveTokenNameToVault(a);if(!r)throw aa(a);const s={userAddress:this.http.getAddress(),vaultAddress:r,content:n};Kt(await this.http.post(zt,s),"Failed to create comment")}}const ya={PAGINATION:{MIN_PAGE:1,MAX_PAGE:1e3,MIN_LIMIT:1,MAX_LIMIT:20},USER_ADDRESS:{PATTERN:/^eth\|[0-9a-fA-F]{40}$/},TOKEN_NAME:{MIN_LENGTH:1,MAX_LENGTH:50},SEARCH:{MIN_LENGTH:1,MAX_LENGTH:100},FAUCET_AMOUNT:{POSITIVE_NON_ZERO_DECIMAL:/^(?!0+(\.0+)?$)\d+(\.\d+)?$/},PROFILE:{FULL_NAME:{MIN_LENGTH:1,MAX_LENGTH:100,ALPHABETS_ONLY_PATTERN:/^[a-zA-Z]+(?:\s[a-zA-Z]+)?$/}}};function Aa(e){return!(!e||"string"!=typeof e)&&ya.USER_ADDRESS.PATTERN.test(e)}const wa="Update profile";class ka{constructor(e){this.http=e}async fetchProfile(e){const t=e??this.http.getAddress();if(!Aa(t))throw new b("Address must be in format: eth|[40-hex-chars]","address","INVALID_ADDRESS");const a={userAddress:t};return await this.http.get(Yt,a)}async updateProfile(e){this.validateUpdateProfileData(e);let t=e.profileImage;if(!t||""===t.trim())try{const a=await this.fetchProfile(e.address);t=a.data?.profileImage||""}catch{t=""}const a={profileImage:t,fullName:e.fullName,userAddress:e.address},n=await this.http.signCustomMessage(wa),r={address:n.address,message:wa,publickey:n.ethereumAddress,sign:n.signature};Kt(await this.http.put(Jt,a,r),"Profile update failed")}async uploadProfileImage(e){this.validateUploadProfileImageOptions(e);const t=e.address??this.http.getAddress();try{const a=new FormData;if("undefined"!=typeof File&&e.file instanceof File)a.append("image",e.file);else{if(!Buffer.isBuffer(e.file))throw new b("Invalid file type","file","INVALID_FILE_TYPE");{const n=`profile-image-${t}.png`,r=new Blob([e.file],{type:"image/png"});a.append("image",r,n)}}const n=await this.http.request({method:"POST",url:`${Gt}?tokenName=${encodeURIComponent(t)}`,data:a,headers:{}});return Kt(n,"Image upload failed"),"string"==typeof n.data?n.data:""}catch(e){if(e instanceof b)throw e;throw new b(`Profile image upload failed: ${e instanceof Error?e.message:"Unknown error"}`,"file","UPLOAD_FAILED")}}async fetchTokenList(e){this.validateGetTokenListOptions(e);const t=da({page:e.page,limit:e.limit,type:"all"!==e.type&&e.type?e.type:"DEFI",address:e.address,search:e.search,tokenName:e.tokenName},{stringifyFields:["page","limit"],optionalFields:["address","search","tokenName"]}),a=await this.http.get(Zt,t);Kt(a,"Failed to fetch token list",!0);const n=Rt(a.data),r=Bt(a,{page:e.page||1,limit:e.limit||10}),s=Mt(r.page,r.totalPages);return{tokens:n,...r,...s}}async fetchTokensHeld(e){this.validateGetTokenListOptions(e);const t=da({page:e.page,limit:e.limit,address:e.address,search:e.search,tokenName:e.tokenName},{stringifyFields:["page","limit"],optionalFields:["address","search","tokenName"]}),a=await this.http.get(ea,t);Kt(a,"Failed to fetch tokens held",!0);const n=Rt(a.data),r=Bt(a,{page:e.page||1,limit:e.limit||10}),s=Mt(r.page,r.totalPages);return{tokens:n,...r,...s}}async fetchTokensCreated(e={}){const{page:t=1,limit:a=10,search:n,tokenName:r}=e,s={type:"DEFI",address:this.http.getAddress(),page:t,limit:a};return void 0!==n&&(s.search=n),void 0!==r&&(s.tokenName=r),this.fetchTokenList(s)}validateGetTokenListOptions(e){if(S(e.page,e.limit,ya),void 0!==e.address&&!Aa(e.address))throw new b("Address must be in format: eth|[40-hex-chars]","address","INVALID_ADDRESS");if(void 0!==e.search&&e.search.trim().length>0&&!((t=e.search)&&"string"==typeof t&&t.length>=ya.SEARCH.MIN_LENGTH&&t.length<=ya.SEARCH.MAX_LENGTH))throw new b(`Search query must be between ${ya.SEARCH.MIN_LENGTH} and ${ya.SEARCH.MAX_LENGTH} characters`,"search","INVALID_SEARCH");var t,a;if(void 0!==e.tokenName&&e.tokenName.trim().length>0&&!((a=e.tokenName)&&"string"==typeof a&&a.length>=ya.TOKEN_NAME.MIN_LENGTH&&a.length<=ya.TOKEN_NAME.MAX_LENGTH))throw new b(`Token name must be between ${ya.TOKEN_NAME.MIN_LENGTH} and ${ya.TOKEN_NAME.MAX_LENGTH} characters`,"tokenName","INVALID_TOKEN_NAME")}validateUpdateProfileData(e){if(!Aa(e.address))throw new b("Address must be in format: eth|[40-hex-chars]","address","INVALID_ADDRESS");if(!((t=e.fullName)&&"string"==typeof t&&t.length>=ya.PROFILE.FULL_NAME.MIN_LENGTH&&t.length<=ya.PROFILE.FULL_NAME.MAX_LENGTH&&ya.PROFILE.FULL_NAME.ALPHABETS_ONLY_PATTERN.test(t)))throw new b(`Full name must be between ${ya.PROFILE.FULL_NAME.MIN_LENGTH} and ${ya.PROFILE.FULL_NAME.MAX_LENGTH} characters`,"fullName","INVALID_FULL_NAME");var t}validateUploadProfileImageOptions(e){if(e.address&&!Aa(e.address))throw new b("Address must be in format: eth|[40-hex-chars]","address","INVALID_ADDRESS")}}class va extends Error{constructor(e,t,a){super(e),this.filename=t,this.mimeType=a,this.name="FileValidationError"}}function Ta(e,t,a){if(!e)throw new va("File is required",t,a);if("undefined"!=typeof File&&e instanceof File){const t=ve.safeParse(e);if(!t.success){const a=t.error.errors.map(e=>e.message).join("; ");throw new va(a,e.name,e.type)}return}if(Buffer.isBuffer(e)){if(!t)throw new va("Filename is required when uploading Buffer objects",t,a);const n=Te.safeParse(e);if(!n.success){const e=n.error.errors.map(e=>e.message).join("; ");throw new va(e,t,a)}if(t.length>255)throw new va(`Filename length ${t.length} exceeds maximum allowed length of 255 characters`,t,a);const r=["image/png","image/jpg","image/jpeg","image/gif","image/webp","image/svg+xml"];if(!r.includes(a))throw new va(`Invalid file type "${a}" is not allowed. Allowed types: ${r.join(", ")}`,t,a);const s=function(e){if(!e)return"";const t=e.lastIndexOf(".");if(-1===t)return"";return e.substring(t).toLowerCase()}(t),o=[".png",".jpg",".jpeg",".gif",".webp",".svg"];if(!o.includes(s))throw new va(`File extension "${s}" is not allowed. Allowed extensions: ${o.join(", ")}`,t,a);const i=function(e){switch(e.toLowerCase()){case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".svg":return"image/svg+xml";default:return"application/octet-stream"}}(s);if(i!==a&&"application/octet-stream"!==i)throw new va(`File extension "${s}" does not match MIME type "${a}"`,t,a);return}throw new va("File must be a File object (browser) or Buffer (Node.js)",t,a)}class ba{constructor(e){this.http=e}async uploadImageByTokenName(e){const{tokenName:t,options:a}=e;Ct(t);const n=`${t}.png`;Ta(a.file,n,"image/png");try{const e=new FormData;if("undefined"!=typeof File&&a.file instanceof File)e.append("image",a.file);else{if(!Buffer.isBuffer(a.file))throw ra("file","a File object (browser) or Buffer (Node.js)");{const n=`${a.tokenName??t}.png`,r=new Blob([a.file],{type:"image/png"});e.append("image",r,n)}}const n=await this.http.request({method:"POST",url:`${Gt}?tokenName=${encodeURIComponent(a.tokenName??t)}`,data:e,headers:{}});return Kt(n,"Image upload failed"),"string"==typeof n.data?n.data:""}catch(e){if(e instanceof Error&&e.message.includes("FormData"))throw oa("File upload failed: FormData not supported in this environment. Ensure you have proper polyfills for Node.js environments.","FormData");throw e}}}class Ea{constructor(e){this.http=e}async transferFaucets(e){this.validateTransferFaucetsData(e);const t={userAddress:e.walletAddress,amount:e.amount};Kt(await this.http.post(ta,t),"Faucet transfer failed")}validateTransferFaucetsData(e){if(!Aa(e.walletAddress))throw new b("Address must be in format: eth|[40-hex-chars]","address","INVALID_ADDRESS");if(!(t=e.amount)||"string"!=typeof t||!ya.FAUCET_AMOUNT.POSITIVE_NON_ZERO_DECIMAL.test(t))throw new b("Amount must be a positive decimal string greater than zero","amount","INVALID_AMOUNT");var t}}class Na{constructor(e){this.http=e,this.poolService=new la(e),this.tradeService=new ha(e),this.commentService=new fa(e,this.poolService),this.userService=new ka(e),this.imageService=new ba(e),this.faucetService=new Ea(e)}async uploadImageByTokenName(e){return this.imageService.uploadImageByTokenName(e)}async fetchPools(e={}){return this.poolService.fetchPools(e)}async checkPool(e){return this.poolService.checkPool(e)}async isTokenNameAvailable(e){return this.poolService.isTokenNameAvailable(e)}async isTokenSymbolAvailable(e){return this.poolService.isTokenSymbolAvailable(e)}async fetchVolumeData(e){return this.poolService.fetchVolumeData(e)}async fetchTokenDistribution(e){return this.poolService.fetchTokenDistribution(e)}async fetchTokenBadges(e){return this.poolService.fetchTokenBadges(e)}async hasTokenBadge(e){return this.poolService.hasTokenBadge(e)}async fetchTrades(e){return this.tradeService.fetchTrades(e)}async fetchComments(e){return this.commentService.fetchComments(e)}async postComment(e){return this.commentService.postComment(e)}async fetchProfile(e){return this.userService.fetchProfile(e)}async updateProfile(e){return this.userService.updateProfile(e)}async uploadProfileImage(e){return this.userService.uploadProfileImage(e)}async fetchTokenList(e){return this.userService.fetchTokenList(e)}async fetchTokensHeld(e){return this.userService.fetchTokensHeld(e)}async fetchTokensCreated(e={}){return this.userService.fetchTokensCreated(e)}async transferFaucets(e){return this.faucetService.transferFaucets(e)}getAddress(){return this.http.getAddress()}validateTokenName(e){return Ct(e)}}class Ia extends i{constructor(e){super(),this.from=e.from,this.to=e.to,this.quantity=e.quantity,this.tokenInstance=e.tokenInstance,this.uniqueKey=e.uniqueKey,e.signedPayload&&(this.signature=e.signedPayload.signature,this.domain=e.signedPayload.domain,this.types=e.signedPayload.types,e.signedPayload.prefix&&(this.prefix=e.signedPayload.prefix))}static fromTokenClassKey(e,t,a,n,r){let s;if("string"==typeof n){const e=n.split("$");if(4!==e.length)throw new Error(`Invalid token class key format: ${n}. Expected format: collection$category$type$additionalKey`);s={collection:e[0],category:e[1],type:e[2],additionalKey:e[3],instance:"0"}}else s={collection:n.collection,category:n.category,type:n.type,additionalKey:n.additionalKey,instance:"0"};return new Ia({from:e,to:t,quantity:a,tokenInstance:s,uniqueKey:r||`galaconnect-operation-${Date.now()}_${Math.random().toString(36).substring(2,8)}`})}static forGALA(e,t,a,n){return new Ia({from:e,to:t,quantity:a,tokenInstance:{collection:"GALA",category:"Unit",type:"none",additionalKey:"none",instance:"0"},uniqueKey:n||`galaconnect-operation-${Date.now()}_${Math.random().toString(36).substring(2,8)}`})}getTokenClassKey(){return`${this.tokenInstance.collection}$${this.tokenInstance.category}$${this.tokenInstance.type}$${this.tokenInstance.additionalKey}`}toSigningPayload(){return{from:this.from,to:this.to,quantity:this.quantity,tokenInstance:this.tokenInstance,uniqueKey:this.uniqueKey}}}class Sa{constructor(e){this.wallet=e}static generateUniqueKey(){return`${Date.now()}_${Math.random().toString(36).substring(2,8)}`}async signTransferToken(e){const t={name:"GalaChain",chainId:1},a={TransferToken:[{name:"from",type:"string"},{name:"to",type:"string"},{name:"quantity",type:"string"},{name:"tokenInstance",type:"TokenInstance"},{name:"uniqueKey",type:"string"}],TokenInstance:[{name:"collection",type:"string"},{name:"category",type:"string"},{name:"type",type:"string"},{name:"additionalKey",type:"string"},{name:"instance",type:"string"}]};return{signature:await this.wallet.signTypedData(t,a,e),domain:t,types:a,signerPublicKey:this.wallet.signingKey.publicKey}}static toGalaChainAddress(t){const a=t.replace("0x","");return`eth|${e.getAddress(`0x${a}`).replace("0x","")}`}static fromGalaChainAddress(e){return e.startsWith("eth|")?e.substring(4):e}static createGALATokenInstance(){return{collection:"GALA",category:"Unit",type:"none",additionalKey:"none",instance:"0"}}static createTokenInstanceFromClassKey(e){const t=e.split("$");if(4!==t.length)throw new Error(`Invalid token class key format: ${e}. Expected format: collection$category$type$additionalKey`);return{collection:t[0],category:t[1],type:t[2],additionalKey:t[3],instance:"0"}}}function Da(e){if("string"==typeof e){const t=e.split("|");if(t.length<4)throw new b(`Invalid tokenId string format: "${e}". Expected format: "collection|category|type|additionalKey" or "collection|category|type|additionalKey|instance"`,"tokenId","INVALID_TOKEN_ID_FORMAT");if(!(t[0]&&t[1]&&t[2]&&t[3]))throw new b(`Invalid tokenId string format: "${e}". All components (collection, category, type, additionalKey) must be non-empty`,"tokenId","INVALID_TOKEN_ID_FORMAT");return{collection:t[0],category:t[1],type:t[2],additionalKey:t[3],instance:t[4]||"0"}}if("object"==typeof e&&null!==e){if(!(e.collection&&e.category&&e.type&&e.additionalKey))throw new b("Invalid tokenId object format. All fields (collection, category, type, additionalKey) are required","tokenId","INVALID_TOKEN_ID_FORMAT");return"instance"in e&&void 0!==e.instance?e:{...e,instance:"0"}}throw new b(`Invalid tokenId type: ${typeof e}. Expected string, TokenClassKey, or TokenInstanceKey`,"tokenId","INVALID_TOKEN_ID_TYPE")}var Fa=Object.freeze({__proto__:null,normalizeToTokenInstanceKey:Da});const $a={MAX_UNIQUE_KEY_LENGTH:64,UNIQUE_KEY_PATTERN:/^(galaswap-operation-|galaconnect-operation-)/,TOKEN_NAME_PATTERN:/^[a-zA-Z0-9]+$/};var Ca;!function(e){e.INVALID_RECIPIENT="INVALID_RECIPIENT",e.INVALID_AMOUNT="INVALID_AMOUNT",e.INSUFFICIENT_BALANCE="INSUFFICIENT_BALANCE",e.TOKEN_NOT_FOUND="TOKEN_NOT_FOUND",e.SIGNATURE_FAILED="SIGNATURE_FAILED",e.NETWORK_ERROR="NETWORK_ERROR",e.DUPLICATE_TRANSFER="DUPLICATE_TRANSFER",e.TRANSFER_LIMIT_EXCEEDED="TRANSFER_LIMIT_EXCEEDED"}(Ca||(Ca={}));class _a extends Error{constructor(e,t,a){super(e),this.type=t,this.details=a,this.name="TransferError"}}class Ua{constructor(e,t,a,n=!1){this.http=e,this.wallet=t,this.tokenResolver=a,this.signatureHelper=new Sa(t),this.logger=new A({debug:n,context:"GalaChainService"})}async fetchPoolDetails(e){this.validateFetchPoolDetailsData(e);const t=await this.http.post("/api/asset/launchpad-contract/FetchSaleDetails",e);if(1!==t.Status)throw sa(`Failed to fetch pool details: Status ${t.Status}`,t.Status);return t}async fetchLaunchTokenFee(){const e=await this.http.post("/api/asset/launchpad-contract/FetchLaunchpadFeeAmount",{});if(1!==e.Status)throw sa(`Failed to fetch launch token fee: Status ${e.Status}`,e.Status);return e.Data.feeAmount}validateFetchPoolDetailsData(e){if(!(t=e)||"object"!=typeof t||"string"!=typeof t.vaultAddress)throw new b("Invalid fetch pool details data: missing required fields","data","INVALID_TYPE");var t;if(!e.vaultAddress||"string"!=typeof e.vaultAddress)throw new b("Vault address is required and must be a string","vaultAddress","INVALID_VAULT_ADDRESS");if(!e.vaultAddress.startsWith("service|Token$Unit$"))throw new b("Vault address must be in service format: service|Token$Unit$...","vaultAddress","INVALID_VAULT_ADDRESS")}async fetchGalaBalance(e){return this.fetchTokenBalance(e)}async fetchTokenBalance(e){try{const t=await this.http.post("/api/asset/token-contract/FetchBalances",e);if(1!==t.Status||!t.Data||0===t.Data.length)return null;const a=t.Data.find(t=>t.collection===e.collection&&t.category===e.category&&t.additionalKey===e.additionalKey&&t.type===e.type);if(!a||"0"===a.quantity)return null;const n=`${a.collection}|${a.category}|${a.additionalKey}|${a.type}`;return{quantity:a.quantity,collection:a.collection,category:a.category,tokenId:n}}catch(e){throw sa(`Failed to fetch token balance from GalaChain: ${e.message||"Unknown error"}`,void 0,e)}}async transferGala(e){this.validateTransferGalaData(e);try{const t=C(e.recipientAddress),a=C(this.wallet.address),n=Ia.forGALA(a,t,e.amount,e.uniqueKey),r=await this.signatureHelper.signTransferToken(n.toSigningPayload()),s=new Ia({...n.toSigningPayload(),signedPayload:r});this.logger.debug("[DEBUG] Full GALA Transfer Request Payload:",JSON.stringify(s,null,2));const o=await this.http.post("/api/asset/token-contract/TransferToken",s);if(this.logger.debug("[DEBUG] Transfer response:",JSON.stringify(o,null,2)),o&&"object"==typeof o){if("Status"in o&&1===o.Status&&"Data"in o){const e=o;return Array.isArray(e.Data)&&e.Data.length>0?"gala-transfer-successful":"transfer-successful-no-id"}if("transactionId"in o&&o.transactionId)return o.transactionId}throw new _a("Transfer succeeded but transaction ID could not be extracted",Ca.NETWORK_ERROR)}catch(t){throw this.handleTransferError(t,"GALA transfer failed",e)}}async transferToken(e){this.validateTransferTokenData(e);try{const t=C(e.to),a=C(this.wallet.address);let n;if(e.tokenId)n=Da(e.tokenId),this.logger.debug("[DEBUG] Using provided tokenId:",e.tokenId),this.logger.debug("[DEBUG] Normalized Token Instance:",JSON.stringify(n,null,2));else{if(!e.tokenName)throw new _a("Must provide either tokenId or tokenName for token identification",Ca.TOKEN_NOT_FOUND);n=await this.resolveTokenInstance(e.tokenName)}const r=new Ia({from:a,to:t,quantity:e.amount,tokenInstance:n,uniqueKey:e.uniqueKey||`galaconnect-operation-${Date.now()}_${Math.random().toString(36).substring(2,8)}`}),s=await this.signatureHelper.signTransferToken(r.toSigningPayload()),o=new Ia({...r.toSigningPayload(),signedPayload:s});this.logger.debug("[DEBUG] Full Transfer Request Payload:",JSON.stringify(o,null,2));const i=await this.http.post("/api/asset/token-contract/TransferToken",o);if(this.logger.debug("[DEBUG] Token transfer response:",JSON.stringify(i,null,2)),i&&"object"==typeof i){if("Status"in i&&1===i.Status&&"Data"in i){const e=i;return Array.isArray(e.Data)&&e.Data.length>0?"token-transfer-successful":"transfer-successful-no-id"}if("transactionId"in i&&i.transactionId)return i.transactionId}throw new _a("Transfer succeeded but transaction ID could not be extracted",Ca.NETWORK_ERROR)}catch(t){throw this.handleTransferError(t,"Token transfer failed",e)}}async resolveTokenClassKey(e){try{const t=await this.tokenResolver.resolveTokenClassKey(e);return this.logger.debug(`[DEBUG] Token class key resolution for '${e}':`,JSON.stringify(t,null,2)),t}catch(t){if(t instanceof _a)throw t;throw new _a(`Failed to resolve token class key for '${e}': ${t instanceof Error?t.message:String(t)}`,Ca.TOKEN_NOT_FOUND,{tokenName:e})}}validateTransferGalaData(e){if(!((t=e)&&"object"==typeof t&&"string"==typeof t.recipientAddress&&t.recipientAddress.trim().length>0&&"string"==typeof t.amount&&t.amount.trim().length>0)||void 0!==t.uniqueKey&&"string"!=typeof t.uniqueKey)throw new b("Invalid GALA transfer data: missing required fields");var t;if(!$(e.recipientAddress))throw new _a("Invalid recipient address format",Ca.INVALID_RECIPIENT,{recipientAddress:e.recipientAddress});if(parseFloat(e.amount)<=0)throw new _a("Transfer amount must be positive",Ca.INVALID_AMOUNT,{amount:e.amount});if(e.uniqueKey){if(e.uniqueKey.length>$a.MAX_UNIQUE_KEY_LENGTH)throw new b(`Unique key too long. Maximum length: ${$a.MAX_UNIQUE_KEY_LENGTH}`);if(!$a.UNIQUE_KEY_PATTERN.test(e.uniqueKey))throw new _a('Invalid unique key format. Must start with "galaswap-operation-" or "galaconnect-operation-"',Ca.INVALID_AMOUNT,{uniqueKey:e.uniqueKey})}}validateTransferTokenData(e){if(!((t=e)&&"object"==typeof t&&"string"==typeof t.to&&t.to.trim().length>0&&"string"==typeof t.amount&&t.amount.trim().length>0&&(void 0!==t.tokenId||"string"==typeof t.tokenName&&t.tokenName.trim().length>0))||void 0!==t.uniqueKey&&"string"!=typeof t.uniqueKey)throw new b("Invalid token transfer data: missing required fields");var t;if(!$(e.to))throw new _a("Invalid recipient address format",Ca.INVALID_RECIPIENT,{recipientAddress:e.to});if(!e.tokenId&&!e.tokenName)throw new _a("Must provide either tokenId or tokenName for token identification",Ca.TOKEN_NOT_FOUND);if(e.tokenName&&!$a.TOKEN_NAME_PATTERN.test(e.tokenName))throw new _a("Invalid token name format",Ca.TOKEN_NOT_FOUND,{tokenName:e.tokenName});if(parseFloat(e.amount)<=0)throw new _a("Transfer amount must be positive",Ca.INVALID_AMOUNT,{amount:e.amount});if(e.uniqueKey){if(e.uniqueKey.length>$a.MAX_UNIQUE_KEY_LENGTH)throw new b(`Unique key too long. Maximum length: ${$a.MAX_UNIQUE_KEY_LENGTH}`);if(!$a.UNIQUE_KEY_PATTERN.test(e.uniqueKey))throw new _a('Invalid unique key format. Must start with "galaswap-operation-" or "galaconnect-operation-"',Ca.INVALID_AMOUNT,{uniqueKey:e.uniqueKey})}}async resolveTokenInstance(e){try{const t=await this.tokenResolver.resolveTokenToVault(e);if(!t)throw new _a(`Token '${e}' not found or not available for transfer`,Ca.TOKEN_NOT_FOUND,{tokenName:e});const a=this.resolveTokenInstanceFromVaultAddress(t);return this.logger.debug(`[DEBUG] Token resolution for '${e}':\n Vault Address: ${t}\n Token Instance: ${JSON.stringify(a,null,2)}`),a}catch(t){if(t instanceof _a)throw t;throw new _a(`Failed to resolve token '${e}': ${t instanceof Error?t.message:String(t)}`,Ca.TOKEN_NOT_FOUND,{tokenName:e})}}resolveTokenInstanceFromVaultAddress(e){const[t,a]=e.split("|");if(!a)throw new _a(`Invalid vault address format: missing token components. Address: ${e}`,Ca.TOKEN_NOT_FOUND);const n=a.split("$");if(n.length<4)throw new _a(`Invalid vault address format: insufficient token components. Expected 4+, got ${n.length}. Address: ${e}`,Ca.TOKEN_NOT_FOUND);return{collection:n[0],category:n[1],type:n[2],additionalKey:n[3],instance:"0"}}handleTransferError(e,t,a){if(e instanceof _a)return e;if(e instanceof b)return new _a(e.message,Ca.INVALID_AMOUNT);if(400===e.response?.status)return new _a(e.response.data?.message||"Invalid transfer request",Ca.INVALID_AMOUNT);if(403===e.response?.status)return new _a("Insufficient balance for transfer",Ca.INSUFFICIENT_BALANCE);if(404===e.response?.status){const e={};return"tokenName"in a&&(e.tokenName=a.tokenName),new _a("Token not found",Ca.TOKEN_NOT_FOUND,e)}return"ECONNABORTED"===e.code||"ETIMEDOUT"===e.code?new _a("Transfer request timed out",Ca.NETWORK_ERROR):new _a(e.message||t,Ca.NETWORK_ERROR)}}class Pa{constructor(e){this.http=e}async fetchTokenSpotPrice(e){if(!e||Array.isArray(e)&&0===e.length)throw na("symbols","At least one symbol");const t=Array.isArray(e)?e.join(","):e;try{const e=await this.http.request({method:"GET",url:"/v1/tokens",params:{symbols:t}}),a=[];return e.tokens&&Array.isArray(e.tokens)&&e.tokens.forEach(e=>{e.currentPrices&&e.symbol&&a.push({symbol:e.symbol,price:e.currentPrices.usd})}),a}catch(e){throw sa(`Failed to fetch token prices: $