@pollar/core 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1543 -381
- package/dist/index.d.ts +1543 -381
- package/dist/index.js +775 -581
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +765 -579
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -650,249 +650,118 @@ function createApiClient(baseUrl) {
|
|
|
650
650
|
return createClient({ baseUrl });
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
-
// src/
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
var PollarStateVar = {
|
|
661
|
-
NETWORK: "network",
|
|
662
|
-
AUTHENTICATION: "authentication",
|
|
663
|
-
TRANSACTION: "transaction"
|
|
664
|
-
};
|
|
665
|
-
var STATE_VAR_CODES = {
|
|
666
|
-
authentication: {
|
|
667
|
-
NONE: "NONE",
|
|
668
|
-
LOGOUT: "LOGOUT",
|
|
669
|
-
CREATE_SESSION_START: "CREATE_SESSION_START",
|
|
670
|
-
CREATE_SESSION_ERROR: "CREATE_SESSION_ERROR",
|
|
671
|
-
CREATE_SESSION_SUCCESS: "CREATE_SESSION_SUCCESS",
|
|
672
|
-
EMAIL_AUTH_START: "EMAIL_AUTH_START",
|
|
673
|
-
EMAIL_AUTH_START_ERROR: "EMAIL_AUTH_START_ERROR",
|
|
674
|
-
EMAIL_AUTH_START_SUCCESS: "EMAIL_AUTH_START_SUCCESS",
|
|
675
|
-
EMAIL_AUTH_CODE_ERROR: "EMAIL_AUTH_CODE_ERROR",
|
|
676
|
-
EMAIL_AUTH_CODE_SUCCESS: "EMAIL_AUTH_CODE_SUCCESS",
|
|
677
|
-
WALLET_AUTH_START: "WALLET_AUTH_START",
|
|
678
|
-
WALLET_AUTH_FREIGHTER_NOT_INSTALLED: "WALLET_AUTH_FREIGHTER_NOT_INSTALLED",
|
|
679
|
-
WALLET_AUTH_ALBEDO_NOT_INSTALLED: "WALLET_AUTH_ALBEDO_NOT_INSTALLED",
|
|
680
|
-
WALLET_AUTH_CONNECTED: "WALLET_AUTH_CONNECTED",
|
|
681
|
-
WALLET_AUTH_LOGIN_START: "WALLET_AUTH_LOGIN_START",
|
|
682
|
-
WALLET_AUTH_LOGIN_START_SUCCESS: "WALLET_AUTH_LOGIN_START_SUCCESS",
|
|
683
|
-
WALLET_AUTH_LOGIN_START_ERROR: "WALLET_AUTH_LOGIN_START_ERROR",
|
|
684
|
-
WALLET_AUTH_ERROR: "WALLET_AUTH_ERROR",
|
|
685
|
-
STREAM_POLL_START: "STREAM_POLL_START",
|
|
686
|
-
STREAM_POLL_EVENT: "STREAM_POLL_EVENT",
|
|
687
|
-
STREAM_POLL_READY: "STREAM_POLL_READY",
|
|
688
|
-
FETCH_SESSION_START: "FETCH_SESSION_START",
|
|
689
|
-
FETCH_SESSION_SUCCESS: "FETCH_SESSION_SUCCESS",
|
|
690
|
-
FETCH_SESSION_ERROR: "FETCH_SESSION_ERROR",
|
|
691
|
-
NO_RESTORED_SESSION: "NO_RESTORED_SESSION",
|
|
692
|
-
RESTORED_SESSION_SUCCESS: "RESTORED_SESSION_SUCCESS",
|
|
693
|
-
RESTORED_SESSION_ERROR: "RESTORED_SESSION_ERROR",
|
|
694
|
-
SESSION_STORED: "SESSION_STORED",
|
|
695
|
-
ERROR_ABORTED: "ABORTED",
|
|
696
|
-
ERROR_UNKNOWN: "ERROR_UNKNOWN"
|
|
697
|
-
},
|
|
698
|
-
walletAddress: {
|
|
699
|
-
NONE: "NONE",
|
|
700
|
-
REMOVED_ADDRESS: "REMOVED_ADDRESS",
|
|
701
|
-
UPDATED_ADDRESS: "UPDATED_ADDRESS"
|
|
702
|
-
},
|
|
703
|
-
transaction: {
|
|
704
|
-
NONE: "NONE",
|
|
705
|
-
BUILD_TRANSACTION_ERROR_NO_WALLET: "BUILD_TRANSACTION_ERROR_NO_WALLET",
|
|
706
|
-
BUILD_TRANSACTION_START: "BUILD_TRANSACTION_START",
|
|
707
|
-
BUILD_TRANSACTION_SUCCESS: "BUILD_TRANSACTION_SUCCESS",
|
|
708
|
-
BUILD_TRANSACTION_ERROR: "BUILD_TRANSACTION_ERROR",
|
|
709
|
-
SIGN_SEND_TRANSACTION_START: "SIGN_SEND_TRANSACTION_START",
|
|
710
|
-
SIGN_SEND_TRANSACTION_SUCCESS: "SIGN_SEND_TRANSACTION_SUCCESS",
|
|
711
|
-
SIGN_SEND_TRANSACTION_ERROR: "SIGN_SEND_TRANSACTION_ERROR"
|
|
712
|
-
},
|
|
713
|
-
network: {
|
|
714
|
-
NONE: "NONE",
|
|
715
|
-
NETWORK_UPDATED: "NETWORK_UPDATED"
|
|
653
|
+
// src/api/endpoints/kyc.ts
|
|
654
|
+
async function getKycStatus(api, providerId) {
|
|
655
|
+
const { data, error } = await api.GET("/kyc/status", {
|
|
656
|
+
params: { query: providerId ? { providerId } : {} }
|
|
657
|
+
});
|
|
658
|
+
if (!data?.content || error) {
|
|
659
|
+
throw new Error(error?.error ?? "Failed to get KYC status");
|
|
716
660
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
};
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
661
|
+
return data.content;
|
|
662
|
+
}
|
|
663
|
+
async function getKycProviders(api, country) {
|
|
664
|
+
const { data, error } = await api.GET("/kyc/providers", { params: { query: { country } } });
|
|
665
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to get KYC providers");
|
|
666
|
+
return data.content;
|
|
667
|
+
}
|
|
668
|
+
async function startKyc(api, body) {
|
|
669
|
+
const { data, error } = await api.POST("/kyc/start", { body });
|
|
670
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to start KYC");
|
|
671
|
+
return data.content;
|
|
672
|
+
}
|
|
673
|
+
async function resolveKyc(api, providerId, level = "basic") {
|
|
674
|
+
const { status } = await getKycStatus(api, providerId);
|
|
675
|
+
if (status === "approved") return { alreadyApproved: true };
|
|
676
|
+
const started = await startKyc(api, { providerId, level });
|
|
677
|
+
return { alreadyApproved: false, ...started };
|
|
678
|
+
}
|
|
679
|
+
async function pollKycStatus(api, providerId, { intervalMs = 3e3, timeoutMs = 3e5 } = {}) {
|
|
680
|
+
const deadline = Date.now() + timeoutMs;
|
|
681
|
+
while (Date.now() < deadline) {
|
|
682
|
+
const { status } = await getKycStatus(api, providerId);
|
|
683
|
+
if (status === "approved" || status === "rejected") return status;
|
|
684
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
685
|
+
}
|
|
686
|
+
throw new Error("KYC polling timed out");
|
|
687
|
+
}
|
|
734
688
|
|
|
735
|
-
// src/
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
return
|
|
740
|
-
}
|
|
689
|
+
// src/api/endpoints/ramps.ts
|
|
690
|
+
async function getRampsQuote(api, query) {
|
|
691
|
+
const { data, error } = await api.GET("/ramps/quote", { params: { query } });
|
|
692
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to get ramp quotes");
|
|
693
|
+
return data.content;
|
|
694
|
+
}
|
|
695
|
+
async function createOnRamp(api, body) {
|
|
696
|
+
const { data, error } = await api.POST("/ramps/onramp", { body });
|
|
697
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to create onramp");
|
|
698
|
+
return data.content;
|
|
699
|
+
}
|
|
700
|
+
async function createOffRamp(api, body) {
|
|
701
|
+
const { data, error } = await api.POST("/ramps/offramp", { body });
|
|
702
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to create offramp");
|
|
703
|
+
return data.content;
|
|
704
|
+
}
|
|
705
|
+
async function getRampTransaction(api, txId) {
|
|
706
|
+
const { data, error } = await api.GET("/ramps/transaction/{txId}", { params: { path: { txId } } });
|
|
707
|
+
if (!data?.content || error) throw new Error(error?.error ?? "Failed to get transaction");
|
|
708
|
+
return data.content;
|
|
709
|
+
}
|
|
710
|
+
async function pollRampTransaction(api, txId, { intervalMs = 5e3, timeoutMs = 6e5 } = {}) {
|
|
711
|
+
const deadline = Date.now() + timeoutMs;
|
|
712
|
+
while (Date.now() < deadline) {
|
|
713
|
+
const { status } = await getRampTransaction(api, txId);
|
|
714
|
+
if (status === "completed" || status === "failed") return status;
|
|
715
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
716
|
+
}
|
|
717
|
+
throw new Error("Ramp transaction polling timed out");
|
|
718
|
+
}
|
|
741
719
|
|
|
742
|
-
// src/
|
|
743
|
-
var
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
} catch {
|
|
751
|
-
return false;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
async connect() {
|
|
755
|
-
const connected = await (0, import_freighter_api.isConnected)();
|
|
756
|
-
if (!connected) {
|
|
757
|
-
throw new Error("Freighter wallet is not installed");
|
|
758
|
-
}
|
|
759
|
-
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
760
|
-
if (!allowed) {
|
|
761
|
-
await (0, import_freighter_api.setAllowed)();
|
|
762
|
-
}
|
|
763
|
-
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
764
|
-
if (!userInfo?.publicKey) {
|
|
765
|
-
throw new Error("Failed to get user information from Freighter");
|
|
766
|
-
}
|
|
767
|
-
return { address: userInfo.publicKey, publicKey: userInfo.publicKey };
|
|
768
|
-
}
|
|
769
|
-
async disconnect() {
|
|
720
|
+
// src/stellar/StellarClient.ts
|
|
721
|
+
var HORIZON_URLS = {
|
|
722
|
+
mainnet: "https://horizon.stellar.org",
|
|
723
|
+
testnet: "https://horizon-testnet.stellar.org"
|
|
724
|
+
};
|
|
725
|
+
var StellarClient = class {
|
|
726
|
+
constructor(config) {
|
|
727
|
+
this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
|
|
770
728
|
}
|
|
771
|
-
async
|
|
729
|
+
async submitTransaction(signedXdr) {
|
|
772
730
|
try {
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
731
|
+
const response = await fetch(`${this.horizonUrl}/transactions`, {
|
|
732
|
+
method: "POST",
|
|
733
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
734
|
+
body: new URLSearchParams({ tx: signedXdr })
|
|
735
|
+
});
|
|
736
|
+
if (!response.ok) {
|
|
737
|
+
const body = await response.json().catch(() => ({}));
|
|
738
|
+
return { success: false, errorCode: body.extras?.result_codes?.transaction ?? "HORIZON_ERROR" };
|
|
739
|
+
}
|
|
740
|
+
const data = await response.json();
|
|
741
|
+
return { success: true, hash: data.hash };
|
|
777
742
|
} catch {
|
|
778
|
-
return
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
async getNetwork() {
|
|
782
|
-
return (0, import_freighter_api.getNetwork)();
|
|
783
|
-
}
|
|
784
|
-
async signTransaction(xdr, options) {
|
|
785
|
-
const result = await (0, import_freighter_api.signTransaction)(xdr, {
|
|
786
|
-
network: options?.network,
|
|
787
|
-
networkPassphrase: options?.networkPassphrase,
|
|
788
|
-
accountToSign: options?.accountToSign
|
|
789
|
-
});
|
|
790
|
-
if (!result || typeof result !== "string") {
|
|
791
|
-
throw new Error("Invalid response from Freighter");
|
|
792
|
-
}
|
|
793
|
-
return { signedTxXdr: result };
|
|
794
|
-
}
|
|
795
|
-
async signAuthEntry(entryXdr, options) {
|
|
796
|
-
const result = await (0, import_freighter_api.signAuthEntry)(entryXdr, { accountToSign: options?.accountToSign });
|
|
797
|
-
if (!result || typeof result !== "string") {
|
|
798
|
-
throw new Error("Invalid response from Freighter");
|
|
743
|
+
return { success: false, errorCode: "NETWORK_ERROR" };
|
|
799
744
|
}
|
|
800
|
-
return { signedAuthEntry: result };
|
|
801
745
|
}
|
|
802
746
|
};
|
|
803
747
|
|
|
804
|
-
// src/
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
window.addEventListener("message", handler);
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
function waitForAlbedoResult() {
|
|
825
|
-
return new Promise((resolve, reject) => {
|
|
826
|
-
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
827
|
-
const parseResult = () => {
|
|
828
|
-
const params = new URLSearchParams(window.location.search);
|
|
829
|
-
if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
|
|
830
|
-
clearTimeout(timeout);
|
|
831
|
-
const result = {};
|
|
832
|
-
params.forEach((value, key) => {
|
|
833
|
-
result[key] = value;
|
|
834
|
-
});
|
|
835
|
-
window.history.replaceState({}, document.title, window.location.pathname);
|
|
836
|
-
resolve(result);
|
|
837
|
-
};
|
|
838
|
-
parseResult();
|
|
839
|
-
window.addEventListener("popstate", parseResult);
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
var AlbedoAdapter = class {
|
|
843
|
-
constructor() {
|
|
844
|
-
this.type = "albedo" /* ALBEDO */;
|
|
845
|
-
}
|
|
846
|
-
async isAvailable() {
|
|
847
|
-
return typeof window !== "undefined";
|
|
848
|
-
}
|
|
849
|
-
async connect() {
|
|
850
|
-
const url = new URL("https://albedo.link");
|
|
851
|
-
url.searchParams.set("intent", "public-key");
|
|
852
|
-
url.searchParams.set("app_name", "Pollar");
|
|
853
|
-
url.searchParams.set("network", "testnet");
|
|
854
|
-
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
855
|
-
url.searchParams.set("origin", window.location.origin);
|
|
856
|
-
openAlbedoPopup(url.toString());
|
|
857
|
-
const result = await waitForAlbedoPopup();
|
|
858
|
-
if (!result.pubkey) {
|
|
859
|
-
throw new Error("Albedo connection rejected");
|
|
860
|
-
}
|
|
861
|
-
return { address: result.pubkey, publicKey: result.pubkey };
|
|
862
|
-
}
|
|
863
|
-
async disconnect() {
|
|
864
|
-
}
|
|
865
|
-
async getPublicKey() {
|
|
866
|
-
return null;
|
|
867
|
-
}
|
|
868
|
-
async getNetwork() {
|
|
869
|
-
throw new Error("Albedo does not expose network");
|
|
870
|
-
}
|
|
871
|
-
async signTransaction(xdr, _options) {
|
|
872
|
-
const url = new URL("https://albedo.link");
|
|
873
|
-
url.searchParams.set("intent", "tx");
|
|
874
|
-
url.searchParams.set("xdr", xdr);
|
|
875
|
-
url.searchParams.set("app_name", "Pollar");
|
|
876
|
-
url.searchParams.set("network", "testnet");
|
|
877
|
-
url.searchParams.set("callback", window.location.href);
|
|
878
|
-
url.searchParams.set("origin", window.location.origin);
|
|
879
|
-
window.location.href = url.toString();
|
|
880
|
-
const result = await waitForAlbedoResult();
|
|
881
|
-
if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
|
|
882
|
-
return { signedTxXdr: result.signed_envelope_xdr };
|
|
883
|
-
}
|
|
884
|
-
async signAuthEntry(entryXdr, _options) {
|
|
885
|
-
const url = new URL("https://albedo.link");
|
|
886
|
-
url.searchParams.set("intent", "sign-auth-entry");
|
|
887
|
-
url.searchParams.set("xdr", entryXdr);
|
|
888
|
-
url.searchParams.set("app_name", "Pollar");
|
|
889
|
-
url.searchParams.set("network", "testnet");
|
|
890
|
-
url.searchParams.set("callback", window.location.href);
|
|
891
|
-
url.searchParams.set("origin", window.location.origin);
|
|
892
|
-
window.location.href = url.toString();
|
|
893
|
-
const result = await waitForAlbedoResult();
|
|
894
|
-
if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
|
|
895
|
-
return { signedAuthEntry: result.signed_xdr };
|
|
748
|
+
// src/types.ts
|
|
749
|
+
var AUTH_ERROR_CODES = {
|
|
750
|
+
SESSION_CREATE_FAILED: "SESSION_CREATE_FAILED",
|
|
751
|
+
EMAIL_SEND_FAILED: "EMAIL_SEND_FAILED",
|
|
752
|
+
EMAIL_VERIFY_FAILED: "EMAIL_VERIFY_FAILED",
|
|
753
|
+
EMAIL_CODE_EXPIRED: "EMAIL_CODE_EXPIRED",
|
|
754
|
+
EMAIL_CODE_INVALID: "EMAIL_CODE_INVALID",
|
|
755
|
+
AUTH_FAILED: "AUTH_FAILED",
|
|
756
|
+
WALLET_CONNECT_FAILED: "WALLET_CONNECT_FAILED",
|
|
757
|
+
WALLET_AUTH_FAILED: "WALLET_AUTH_FAILED",
|
|
758
|
+
UNEXPECTED_ERROR: "UNEXPECTED_ERROR"
|
|
759
|
+
};
|
|
760
|
+
var PollarFlowError = class extends Error {
|
|
761
|
+
constructor(message) {
|
|
762
|
+
super(message);
|
|
763
|
+
this.code = "INVALID_FLOW";
|
|
764
|
+
this.name = "PollarFlowError";
|
|
896
765
|
}
|
|
897
766
|
};
|
|
898
767
|
|
|
@@ -1107,432 +976,749 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
|
|
|
1107
976
|
}
|
|
1108
977
|
}
|
|
1109
978
|
|
|
1110
|
-
// src/client/
|
|
1111
|
-
function
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
signal.addEventListener("abort", () => reject(new DOMException("Aborted", "AbortError")), { once: true });
|
|
1120
|
-
})
|
|
1121
|
-
]);
|
|
1122
|
-
}
|
|
1123
|
-
async function login(options, deps) {
|
|
1124
|
-
const { api, basePath, apiKey, signal, emitState, storeSession, clearSession } = deps;
|
|
1125
|
-
const oauthPopup = options.provider === "google" || options.provider === "github" ? window.open("about:blank", "_blank") : null;
|
|
1126
|
-
emitState("authentication", STATE_VAR_CODES.authentication.CREATE_SESSION_START, "info", StateStatus.LOADING);
|
|
1127
|
-
const createSessionResponse = await api.POST("/auth/session", { signal });
|
|
1128
|
-
if (!emitResponse(
|
|
1129
|
-
PollarStateVar.AUTHENTICATION,
|
|
1130
|
-
createSessionResponse,
|
|
1131
|
-
{ code: STATE_VAR_CODES.authentication.CREATE_SESSION_SUCCESS },
|
|
1132
|
-
STATE_VAR_CODES.authentication.CREATE_SESSION_ERROR,
|
|
1133
|
-
emitState
|
|
1134
|
-
)) {
|
|
1135
|
-
oauthPopup?.close();
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
const clientSessionId = createSessionResponse.data.content.clientSessionId;
|
|
1139
|
-
switch (options.provider) {
|
|
1140
|
-
case "email": {
|
|
1141
|
-
emitState("authentication", STATE_VAR_CODES.authentication.EMAIL_AUTH_START, "info", StateStatus.LOADING, {
|
|
1142
|
-
email: options.email
|
|
1143
|
-
});
|
|
1144
|
-
const emailRes = await api.POST(`/auth/email`, {
|
|
1145
|
-
body: { clientSessionId, email: options.email },
|
|
1146
|
-
signal
|
|
1147
|
-
});
|
|
1148
|
-
if (!emitResponse(
|
|
1149
|
-
PollarStateVar.AUTHENTICATION,
|
|
1150
|
-
emailRes,
|
|
1151
|
-
{ code: STATE_VAR_CODES.authentication.EMAIL_AUTH_START_SUCCESS },
|
|
1152
|
-
STATE_VAR_CODES.authentication.EMAIL_AUTH_START_ERROR,
|
|
1153
|
-
emitState
|
|
1154
|
-
)) {
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
break;
|
|
1158
|
-
}
|
|
1159
|
-
case "google":
|
|
1160
|
-
case "github": {
|
|
1161
|
-
const url = new URL(`${basePath}/auth/${options.provider}`);
|
|
1162
|
-
url.searchParams.set("api_key", apiKey);
|
|
1163
|
-
url.searchParams.set("client_session_id", clientSessionId);
|
|
1164
|
-
url.searchParams.set("redirect_uri", window.location.origin);
|
|
1165
|
-
if (oauthPopup) {
|
|
1166
|
-
oauthPopup.location.href = url.toString();
|
|
1167
|
-
} else {
|
|
1168
|
-
window.open(url.toString(), "_blank");
|
|
1169
|
-
}
|
|
1170
|
-
break;
|
|
1171
|
-
}
|
|
1172
|
-
case "wallet": {
|
|
1173
|
-
try {
|
|
1174
|
-
emitState("authentication", STATE_VAR_CODES.authentication.WALLET_AUTH_START, "info", StateStatus.LOADING, {
|
|
1175
|
-
adapter: options.type
|
|
1176
|
-
});
|
|
1177
|
-
const adapter = options.type === "freighter" /* FREIGHTER */ ? new FreighterAdapter() : new AlbedoAdapter();
|
|
1178
|
-
const available = await withSignal(adapter.isAvailable(), signal);
|
|
1179
|
-
if (!available) {
|
|
1180
|
-
emitState(
|
|
1181
|
-
"authentication",
|
|
1182
|
-
options.type === "freighter" /* FREIGHTER */ ? STATE_VAR_CODES.authentication.WALLET_AUTH_FREIGHTER_NOT_INSTALLED : STATE_VAR_CODES.authentication.WALLET_AUTH_ALBEDO_NOT_INSTALLED,
|
|
1183
|
-
"info",
|
|
1184
|
-
StateStatus.LOADING,
|
|
1185
|
-
{
|
|
1186
|
-
type: options.type
|
|
1187
|
-
}
|
|
1188
|
-
);
|
|
1189
|
-
}
|
|
1190
|
-
const { publicKey } = await withSignal(adapter.connect(), signal);
|
|
1191
|
-
emitState("authentication", STATE_VAR_CODES.authentication.WALLET_AUTH_CONNECTED, "info", StateStatus.LOADING, {
|
|
1192
|
-
adapter: options.type,
|
|
1193
|
-
publicKey
|
|
1194
|
-
});
|
|
1195
|
-
emitState("authentication", STATE_VAR_CODES.authentication.WALLET_AUTH_LOGIN_START, "info", StateStatus.LOADING, {
|
|
1196
|
-
adapter: options.type,
|
|
1197
|
-
publicKey
|
|
1198
|
-
});
|
|
1199
|
-
const emailRes = await api.POST(`/auth/wallet`, {
|
|
1200
|
-
body: { clientSessionId, walletAddress: publicKey },
|
|
1201
|
-
signal
|
|
1202
|
-
});
|
|
1203
|
-
if (!emitResponse(
|
|
1204
|
-
PollarStateVar.AUTHENTICATION,
|
|
1205
|
-
emailRes,
|
|
1206
|
-
{ code: STATE_VAR_CODES.authentication.WALLET_AUTH_LOGIN_START_SUCCESS },
|
|
1207
|
-
STATE_VAR_CODES.authentication.WALLET_AUTH_LOGIN_START_ERROR,
|
|
1208
|
-
emitState
|
|
1209
|
-
)) {
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
} catch (error2) {
|
|
1213
|
-
emitState("authentication", STATE_VAR_CODES.authentication.WALLET_AUTH_ERROR, "error", StateStatus.ERROR, {
|
|
1214
|
-
clientSessionId
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
break;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
emitState("authentication", STATE_VAR_CODES.authentication.STREAM_POLL_START, "info", StateStatus.LOADING, {
|
|
1221
|
-
clientSessionId
|
|
1222
|
-
});
|
|
1223
|
-
await streamUntilFound(
|
|
1224
|
-
api,
|
|
1225
|
-
clientSessionId,
|
|
1226
|
-
(data2) => {
|
|
1227
|
-
const status = data2?.status;
|
|
1228
|
-
if (status === "READY") {
|
|
1229
|
-
emitState("authentication", STATE_VAR_CODES.authentication.STREAM_POLL_READY, "info", StateStatus.LOADING);
|
|
1230
|
-
return true;
|
|
1231
|
-
}
|
|
1232
|
-
emitState(
|
|
1233
|
-
"authentication",
|
|
1234
|
-
STATE_VAR_CODES.authentication.STREAM_POLL_EVENT + (status ? `/${status}` : ""),
|
|
1235
|
-
"info",
|
|
1236
|
-
StateStatus.LOADING,
|
|
1237
|
-
data2
|
|
1238
|
-
);
|
|
1239
|
-
return false;
|
|
1240
|
-
},
|
|
1241
|
-
200,
|
|
1242
|
-
signal
|
|
1243
|
-
);
|
|
1244
|
-
emitState("authentication", STATE_VAR_CODES.authentication.FETCH_SESSION_START, "info", StateStatus.LOADING);
|
|
1245
|
-
const { data, error } = await api.POST(`/auth/login`, {
|
|
979
|
+
// src/client/auth/authenticate.ts
|
|
980
|
+
async function authenticate(clientSessionId, deps) {
|
|
981
|
+
const { api, signal, setAuthState, storeSession, clearSession } = deps;
|
|
982
|
+
setAuthState({ step: "authenticating" });
|
|
983
|
+
await streamUntilFound(api, clientSessionId, (data2) => data2?.status === "READY", 200, signal);
|
|
984
|
+
const { data, error } = await api.POST("/auth/login", {
|
|
1246
985
|
body: { clientSessionId },
|
|
1247
986
|
signal
|
|
1248
987
|
});
|
|
1249
988
|
if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content)) {
|
|
1250
|
-
emitState("authentication", STATE_VAR_CODES.authentication.FETCH_SESSION_SUCCESS, "info", StateStatus.SUCCESS);
|
|
1251
989
|
storeSession(data.content);
|
|
1252
990
|
} else {
|
|
1253
|
-
|
|
1254
|
-
error,
|
|
1255
|
-
|
|
991
|
+
setAuthState({
|
|
992
|
+
step: "error",
|
|
993
|
+
previousStep: "authenticating",
|
|
994
|
+
message: "Failed to load session",
|
|
995
|
+
errorCode: AUTH_ERROR_CODES.AUTH_FAILED
|
|
1256
996
|
});
|
|
1257
997
|
clearSession();
|
|
1258
998
|
}
|
|
1259
999
|
}
|
|
1260
1000
|
|
|
1261
|
-
// src/client/
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
)
|
|
1001
|
+
// src/client/auth/deps.ts
|
|
1002
|
+
async function createAuthSession(deps) {
|
|
1003
|
+
const { api, signal, setAuthState } = deps;
|
|
1004
|
+
setAuthState({ step: "creating_session" });
|
|
1005
|
+
const { data, error } = await api.POST("/auth/session", { signal });
|
|
1006
|
+
if (error || !data?.success) {
|
|
1007
|
+
setAuthState({
|
|
1008
|
+
step: "error",
|
|
1009
|
+
previousStep: "creating_session",
|
|
1010
|
+
message: "Failed to create session",
|
|
1011
|
+
errorCode: AUTH_ERROR_CODES.SESSION_CREATE_FAILED
|
|
1012
|
+
});
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
return data.content.clientSessionId;
|
|
1267
1016
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1017
|
+
|
|
1018
|
+
// src/client/auth/emailFlow.ts
|
|
1019
|
+
async function initEmailSession(deps) {
|
|
1020
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1021
|
+
if (!clientSessionId) return;
|
|
1022
|
+
deps.setAuthState({ step: "entering_email", clientSessionId });
|
|
1023
|
+
}
|
|
1024
|
+
async function sendEmailCode(email, clientSessionId, deps) {
|
|
1025
|
+
const { api, signal, setAuthState } = deps;
|
|
1026
|
+
setAuthState({ step: "sending_email", email });
|
|
1027
|
+
const { data, error } = await api.POST("/auth/email", {
|
|
1028
|
+
body: { clientSessionId, email },
|
|
1029
|
+
signal
|
|
1030
|
+
});
|
|
1031
|
+
if (error || !data?.success) {
|
|
1032
|
+
setAuthState({
|
|
1033
|
+
step: "error",
|
|
1034
|
+
previousStep: "sending_email",
|
|
1035
|
+
message: "Failed to send code",
|
|
1036
|
+
errorCode: AUTH_ERROR_CODES.EMAIL_SEND_FAILED
|
|
1286
1037
|
});
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
setAuthState({ step: "entering_code", clientSessionId, email });
|
|
1041
|
+
}
|
|
1042
|
+
async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
1043
|
+
const { api, signal, setAuthState } = deps;
|
|
1044
|
+
setAuthState({ step: "verifying_email_code", clientSessionId, email });
|
|
1045
|
+
const { data, error } = await api.POST("/auth/email/verify-code", {
|
|
1046
|
+
body: { clientSessionId, code },
|
|
1047
|
+
signal
|
|
1048
|
+
});
|
|
1049
|
+
if (data?.code === "SDK_EMAIL_CODE_VERIFIED") {
|
|
1050
|
+
await authenticate(clientSessionId, deps);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const errCode = error?.error ?? data?.code;
|
|
1054
|
+
if (errCode === "SDK_EMAIL_CODE_EXPIRED") {
|
|
1055
|
+
setAuthState({
|
|
1056
|
+
step: "error",
|
|
1057
|
+
previousStep: "verifying_email_code",
|
|
1058
|
+
message: "Code expired \u2014 request a new one",
|
|
1059
|
+
errorCode: AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED,
|
|
1060
|
+
clientSessionId,
|
|
1061
|
+
email
|
|
1062
|
+
});
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (errCode === "INVALID_EMAIL_CODE" || errCode === "SDK_EMAIL_CODE_INVALID") {
|
|
1066
|
+
setAuthState({
|
|
1067
|
+
step: "error",
|
|
1068
|
+
previousStep: "verifying_email_code",
|
|
1069
|
+
message: "Invalid code \u2014 try again",
|
|
1070
|
+
errorCode: AUTH_ERROR_CODES.EMAIL_CODE_INVALID,
|
|
1071
|
+
clientSessionId,
|
|
1072
|
+
email
|
|
1073
|
+
});
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
setAuthState({
|
|
1077
|
+
step: "error",
|
|
1078
|
+
previousStep: "verifying_email_code",
|
|
1079
|
+
message: "Failed to verify code \u2014 try again",
|
|
1080
|
+
errorCode: AUTH_ERROR_CODES.EMAIL_VERIFY_FAILED
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/client/auth/oauthFlow.ts
|
|
1085
|
+
async function loginOAuth(provider, deps) {
|
|
1086
|
+
const { setAuthState, basePath, apiKey } = deps;
|
|
1087
|
+
const popup = window.open("about:blank", "_blank");
|
|
1088
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1089
|
+
if (!clientSessionId) {
|
|
1090
|
+
popup?.close();
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
setAuthState({ step: "opening_oauth", provider });
|
|
1094
|
+
const url = new URL(`${basePath}/auth/${provider}`);
|
|
1095
|
+
url.searchParams.set("api_key", apiKey);
|
|
1096
|
+
url.searchParams.set("client_session_id", clientSessionId);
|
|
1097
|
+
url.searchParams.set("redirect_uri", window.location.origin);
|
|
1098
|
+
if (popup) {
|
|
1099
|
+
popup.location.href = url.toString();
|
|
1100
|
+
} else {
|
|
1101
|
+
window.open(url.toString(), "_blank");
|
|
1102
|
+
}
|
|
1103
|
+
await authenticate(clientSessionId, deps);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// src/wallets/FreighterAdapter.ts
|
|
1107
|
+
var import_freighter_api = __toESM(require_index_min());
|
|
1108
|
+
|
|
1109
|
+
// src/wallets/types.ts
|
|
1110
|
+
var WalletType = /* @__PURE__ */ ((WalletType2) => {
|
|
1111
|
+
WalletType2["FREIGHTER"] = "freighter";
|
|
1112
|
+
WalletType2["ALBEDO"] = "albedo";
|
|
1113
|
+
return WalletType2;
|
|
1114
|
+
})(WalletType || {});
|
|
1115
|
+
|
|
1116
|
+
// src/wallets/FreighterAdapter.ts
|
|
1117
|
+
var FreighterAdapter = class {
|
|
1118
|
+
constructor() {
|
|
1119
|
+
this.type = "freighter" /* FREIGHTER */;
|
|
1120
|
+
}
|
|
1121
|
+
async isAvailable() {
|
|
1122
|
+
try {
|
|
1123
|
+
return await (0, import_freighter_api.isConnected)();
|
|
1124
|
+
} catch {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
async connect() {
|
|
1129
|
+
const connected = await (0, import_freighter_api.isConnected)();
|
|
1130
|
+
if (!connected) {
|
|
1131
|
+
throw new Error("Freighter wallet is not installed");
|
|
1132
|
+
}
|
|
1133
|
+
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
1134
|
+
if (!allowed) {
|
|
1135
|
+
await (0, import_freighter_api.setAllowed)();
|
|
1136
|
+
}
|
|
1137
|
+
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
1138
|
+
if (!userInfo?.publicKey) {
|
|
1139
|
+
throw new Error("Failed to get user information from Freighter");
|
|
1140
|
+
}
|
|
1141
|
+
return { address: userInfo.publicKey, publicKey: userInfo.publicKey };
|
|
1142
|
+
}
|
|
1143
|
+
async disconnect() {
|
|
1144
|
+
}
|
|
1145
|
+
async getPublicKey() {
|
|
1146
|
+
try {
|
|
1147
|
+
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
1148
|
+
if (!allowed) return null;
|
|
1149
|
+
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
1150
|
+
return userInfo?.publicKey ?? null;
|
|
1151
|
+
} catch {
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
async getNetwork() {
|
|
1156
|
+
return (0, import_freighter_api.getNetwork)();
|
|
1157
|
+
}
|
|
1158
|
+
async signTransaction(xdr, options) {
|
|
1159
|
+
const result = await (0, import_freighter_api.signTransaction)(xdr, {
|
|
1160
|
+
network: options?.network,
|
|
1161
|
+
networkPassphrase: options?.networkPassphrase,
|
|
1162
|
+
accountToSign: options?.accountToSign
|
|
1163
|
+
});
|
|
1164
|
+
if (!result || typeof result !== "string") {
|
|
1165
|
+
throw new Error("Invalid response from Freighter");
|
|
1166
|
+
}
|
|
1167
|
+
return { signedTxXdr: result };
|
|
1168
|
+
}
|
|
1169
|
+
async signAuthEntry(entryXdr, options) {
|
|
1170
|
+
const result = await (0, import_freighter_api.signAuthEntry)(entryXdr, { accountToSign: options?.accountToSign });
|
|
1171
|
+
if (!result || typeof result !== "string") {
|
|
1172
|
+
throw new Error("Invalid response from Freighter");
|
|
1173
|
+
}
|
|
1174
|
+
return { signedAuthEntry: result };
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// src/wallets/AlbedoAdapter.ts
|
|
1179
|
+
function openAlbedoPopup(url) {
|
|
1180
|
+
const popup = window.open(url, "albedo", "width=420,height=720,resizable=yes,scrollbars=yes");
|
|
1181
|
+
if (!popup) {
|
|
1182
|
+
throw new Error("Failed to open Albedo popup (blocked by browser)");
|
|
1183
|
+
}
|
|
1184
|
+
return popup;
|
|
1185
|
+
}
|
|
1186
|
+
function waitForAlbedoPopup() {
|
|
1187
|
+
return new Promise((resolve, reject) => {
|
|
1188
|
+
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
1189
|
+
function handler(event) {
|
|
1190
|
+
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
1191
|
+
clearTimeout(timeout);
|
|
1192
|
+
window.removeEventListener("message", handler);
|
|
1193
|
+
resolve(event.data.payload);
|
|
1194
|
+
}
|
|
1195
|
+
window.addEventListener("message", handler);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
function waitForAlbedoResult() {
|
|
1199
|
+
return new Promise((resolve, reject) => {
|
|
1200
|
+
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
1201
|
+
const parseResult = () => {
|
|
1202
|
+
const params = new URLSearchParams(window.location.search);
|
|
1203
|
+
if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
|
|
1204
|
+
clearTimeout(timeout);
|
|
1205
|
+
const result = {};
|
|
1206
|
+
params.forEach((value, key) => {
|
|
1207
|
+
result[key] = value;
|
|
1208
|
+
});
|
|
1209
|
+
window.history.replaceState({}, document.title, window.location.pathname);
|
|
1210
|
+
resolve(result);
|
|
1211
|
+
};
|
|
1212
|
+
parseResult();
|
|
1213
|
+
window.addEventListener("popstate", parseResult);
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
var AlbedoAdapter = class {
|
|
1217
|
+
constructor() {
|
|
1218
|
+
this.type = "albedo" /* ALBEDO */;
|
|
1219
|
+
}
|
|
1220
|
+
async isAvailable() {
|
|
1221
|
+
return typeof window !== "undefined";
|
|
1222
|
+
}
|
|
1223
|
+
async connect() {
|
|
1224
|
+
const url = new URL("https://albedo.link");
|
|
1225
|
+
url.searchParams.set("intent", "public-key");
|
|
1226
|
+
url.searchParams.set("app_name", "Pollar");
|
|
1227
|
+
url.searchParams.set("network", "testnet");
|
|
1228
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1229
|
+
url.searchParams.set("origin", window.location.origin);
|
|
1230
|
+
openAlbedoPopup(url.toString());
|
|
1231
|
+
const result = await waitForAlbedoPopup();
|
|
1232
|
+
if (!result.pubkey) {
|
|
1233
|
+
throw new Error("Albedo connection rejected");
|
|
1234
|
+
}
|
|
1235
|
+
return { address: result.pubkey, publicKey: result.pubkey };
|
|
1236
|
+
}
|
|
1237
|
+
async disconnect() {
|
|
1238
|
+
}
|
|
1239
|
+
async getPublicKey() {
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
async getNetwork() {
|
|
1243
|
+
throw new Error("Albedo does not expose network");
|
|
1244
|
+
}
|
|
1245
|
+
async signTransaction(xdr, _options) {
|
|
1246
|
+
const url = new URL("https://albedo.link");
|
|
1247
|
+
url.searchParams.set("intent", "tx");
|
|
1248
|
+
url.searchParams.set("xdr", xdr);
|
|
1249
|
+
url.searchParams.set("app_name", "Pollar");
|
|
1250
|
+
url.searchParams.set("network", "testnet");
|
|
1251
|
+
url.searchParams.set("callback", window.location.href);
|
|
1252
|
+
url.searchParams.set("origin", window.location.origin);
|
|
1253
|
+
window.location.href = url.toString();
|
|
1254
|
+
const result = await waitForAlbedoResult();
|
|
1255
|
+
if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
|
|
1256
|
+
return { signedTxXdr: result.signed_envelope_xdr };
|
|
1257
|
+
}
|
|
1258
|
+
async signAuthEntry(entryXdr, _options) {
|
|
1259
|
+
const url = new URL("https://albedo.link");
|
|
1260
|
+
url.searchParams.set("intent", "sign-auth-entry");
|
|
1261
|
+
url.searchParams.set("xdr", entryXdr);
|
|
1262
|
+
url.searchParams.set("app_name", "Pollar");
|
|
1263
|
+
url.searchParams.set("network", "testnet");
|
|
1264
|
+
url.searchParams.set("callback", window.location.href);
|
|
1265
|
+
url.searchParams.set("origin", window.location.origin);
|
|
1266
|
+
window.location.href = url.toString();
|
|
1267
|
+
const result = await waitForAlbedoResult();
|
|
1268
|
+
if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
|
|
1269
|
+
return { signedAuthEntry: result.signed_xdr };
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
// src/client/auth/walletFlow.ts
|
|
1274
|
+
function withSignal(promise, signal) {
|
|
1275
|
+
return Promise.race([
|
|
1276
|
+
promise,
|
|
1277
|
+
new Promise((_, reject) => {
|
|
1278
|
+
if (signal.aborted) {
|
|
1279
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
signal.addEventListener("abort", () => reject(new DOMException("Aborted", "AbortError")), { once: true });
|
|
1283
|
+
})
|
|
1284
|
+
]);
|
|
1285
|
+
}
|
|
1286
|
+
async function loginWallet(type, deps) {
|
|
1287
|
+
const { api, signal, setAuthState } = deps;
|
|
1288
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1289
|
+
if (!clientSessionId) return;
|
|
1290
|
+
try {
|
|
1291
|
+
setAuthState({ step: "connecting_wallet", walletType: type });
|
|
1292
|
+
const adapter = type === "freighter" /* FREIGHTER */ ? new FreighterAdapter() : new AlbedoAdapter();
|
|
1293
|
+
const available = await withSignal(adapter.isAvailable(), signal);
|
|
1294
|
+
if (!available) {
|
|
1295
|
+
setAuthState({ step: "wallet_not_installed", walletType: type });
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
const { publicKey } = await withSignal(adapter.connect(), signal);
|
|
1299
|
+
deps.storeWalletAdapter(adapter);
|
|
1300
|
+
setAuthState({ step: "authenticating_wallet" });
|
|
1301
|
+
const { data: walletData, error: walletError } = await api.POST("/auth/wallet", {
|
|
1302
|
+
body: { clientSessionId, walletAddress: publicKey },
|
|
1303
|
+
signal
|
|
1304
|
+
});
|
|
1305
|
+
if (walletError || !walletData?.success) {
|
|
1306
|
+
setAuthState({
|
|
1307
|
+
step: "error",
|
|
1308
|
+
previousStep: "authenticating_wallet",
|
|
1309
|
+
message: "Wallet authentication failed",
|
|
1310
|
+
errorCode: AUTH_ERROR_CODES.WALLET_AUTH_FAILED
|
|
1311
|
+
});
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
} catch {
|
|
1315
|
+
setAuthState({
|
|
1316
|
+
step: "error",
|
|
1317
|
+
previousStep: "connecting_wallet",
|
|
1318
|
+
message: "Wallet connection failed",
|
|
1319
|
+
errorCode: AUTH_ERROR_CODES.WALLET_CONNECT_FAILED
|
|
1320
|
+
});
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
await authenticate(clientSessionId, deps);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// src/client/client.ts
|
|
1327
|
+
var isBrowser = typeof window !== "undefined" && typeof localStorage !== "undefined";
|
|
1328
|
+
function warnServerSide(method) {
|
|
1329
|
+
console.warn(
|
|
1330
|
+
`[PollarClient] ${method}() called server-side \u2014 browser APIs unavailable. Use PollarClient only in Client Components.`
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
var PollarClient = class {
|
|
1334
|
+
constructor(config) {
|
|
1335
|
+
this._session = null;
|
|
1336
|
+
this._transactionState = null;
|
|
1337
|
+
this._transactionStateListeners = /* @__PURE__ */ new Set();
|
|
1338
|
+
this._txHistoryState = { step: "idle" };
|
|
1339
|
+
this._txHistoryStateListeners = /* @__PURE__ */ new Set();
|
|
1340
|
+
this._authState = { step: "idle" };
|
|
1341
|
+
this._authStateListeners = /* @__PURE__ */ new Set();
|
|
1342
|
+
this._networkState = { step: "idle" };
|
|
1343
|
+
this._networkStateListeners = /* @__PURE__ */ new Set();
|
|
1344
|
+
this._walletAdapter = null;
|
|
1345
|
+
this._loginController = null;
|
|
1346
|
+
this.apiKey = config.apiKey;
|
|
1347
|
+
this.id = crypto.randomUUID();
|
|
1348
|
+
this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
|
|
1349
|
+
this._api = createApiClient(this.basePath);
|
|
1350
|
+
const self = this;
|
|
1351
|
+
this._api.use({
|
|
1352
|
+
onRequest({ request }) {
|
|
1353
|
+
request.headers.set("x-pollar-api-key", config.apiKey);
|
|
1354
|
+
const accessToken = self._session?.token?.accessToken;
|
|
1355
|
+
if (accessToken) {
|
|
1356
|
+
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1357
|
+
}
|
|
1358
|
+
return request;
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
|
|
1362
|
+
if (!isBrowser) {
|
|
1363
|
+
warnServerSide("constructor");
|
|
1364
|
+
this._session = null;
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
console.info(`[PollarClient] Initialized \u2014 endpoint: ${this.basePath}, network: ${this._networkState.network}`);
|
|
1368
|
+
this._restoreSession();
|
|
1369
|
+
window.addEventListener("storage", (e) => {
|
|
1295
1370
|
if (e.key === STORAGE_KEY) {
|
|
1296
1371
|
const prev = this._session;
|
|
1297
1372
|
console.info(`[PollarClient] Storage event \u2014 session ${this._session ? "updated" : prev ? "cleared" : "unchanged"}`);
|
|
1298
|
-
this.
|
|
1373
|
+
this._restoreSession();
|
|
1299
1374
|
}
|
|
1300
1375
|
});
|
|
1301
|
-
this._emitState("network", STATE_VAR_CODES.network.NETWORK_UPDATED, "info", StateStatus.SUCCESS, {
|
|
1302
|
-
network: "testnet"
|
|
1303
|
-
});
|
|
1304
1376
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1377
|
+
// ─── Auth state ──────────────────────────────────────────────────────────────
|
|
1378
|
+
getAuthState() {
|
|
1379
|
+
return this._authState;
|
|
1307
1380
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
return this._api;
|
|
1381
|
+
onAuthStateChange(cb) {
|
|
1382
|
+
this._authStateListeners.add(cb);
|
|
1383
|
+
cb(this._authState);
|
|
1384
|
+
return () => this._authStateListeners.delete(cb);
|
|
1313
1385
|
}
|
|
1386
|
+
// ─── Login (unified entry point) ─────────────────────────────────────────
|
|
1314
1387
|
login(options) {
|
|
1315
1388
|
if (!isBrowser) {
|
|
1316
1389
|
warnServerSide("login");
|
|
1317
|
-
return
|
|
1318
|
-
cancelLogin: () => {
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1390
|
+
return;
|
|
1321
1391
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1392
|
+
if (options.provider === "google" || options.provider === "github" || options.provider === "email") {
|
|
1393
|
+
const controller = this._newController();
|
|
1394
|
+
const deps = this._flowDeps(controller.signal);
|
|
1395
|
+
if (options.provider === "google" || options.provider === "github") {
|
|
1396
|
+
loginOAuth(options.provider, {
|
|
1397
|
+
...deps,
|
|
1398
|
+
basePath: this.basePath,
|
|
1399
|
+
apiKey: this.apiKey
|
|
1400
|
+
}).catch((err) => this._handleFlowError(err));
|
|
1401
|
+
} else if (options.provider === "email") {
|
|
1402
|
+
const { email } = options;
|
|
1403
|
+
initEmailSession(deps).then(() => {
|
|
1404
|
+
if (this._authState.step === "entering_email") {
|
|
1405
|
+
return sendEmailCode(email, this._authState.clientSessionId, deps);
|
|
1406
|
+
}
|
|
1407
|
+
}).catch((err) => this._handleFlowError(err));
|
|
1337
1408
|
}
|
|
1338
|
-
|
|
1339
|
-
this.
|
|
1340
|
-
|
|
1341
|
-
});
|
|
1342
|
-
});
|
|
1343
|
-
return { cancelLogin: () => controller.abort() };
|
|
1409
|
+
} else if (options.provider === "wallet") {
|
|
1410
|
+
this.loginWallet(options.type);
|
|
1411
|
+
}
|
|
1344
1412
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1413
|
+
// ─── Email OTP flow (3 steps) ─────────────────────────────────────────────
|
|
1414
|
+
beginEmailLogin() {
|
|
1415
|
+
if (!isBrowser) {
|
|
1416
|
+
warnServerSide("beginEmailLogin");
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
const controller = this._newController();
|
|
1420
|
+
initEmailSession(this._flowDeps(controller.signal)).catch((err) => this._handleFlowError(err));
|
|
1421
|
+
}
|
|
1422
|
+
sendEmailCode(email) {
|
|
1423
|
+
if (!isBrowser) {
|
|
1424
|
+
warnServerSide("sendEmailCode");
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
if (this._authState.step !== "entering_email") {
|
|
1428
|
+
throw new PollarFlowError(`sendEmailCode() requires step 'entering_email', current step is '${this._authState.step}'`);
|
|
1351
1429
|
}
|
|
1352
|
-
|
|
1430
|
+
const { clientSessionId } = this._authState;
|
|
1431
|
+
const signal = this._loginController.signal;
|
|
1432
|
+
sendEmailCode(email, clientSessionId, this._flowDeps(signal)).catch((err) => this._handleFlowError(err));
|
|
1353
1433
|
}
|
|
1354
|
-
|
|
1434
|
+
verifyEmailCode(code) {
|
|
1355
1435
|
if (!isBrowser) {
|
|
1356
1436
|
warnServerSide("verifyEmailCode");
|
|
1357
1437
|
return;
|
|
1358
1438
|
}
|
|
1439
|
+
const isRetryableError = this._authState.step === "error" && this._authState.clientSessionId != null && (this._authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID || this._authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED);
|
|
1440
|
+
if (this._authState.step !== "entering_code" && !isRetryableError) {
|
|
1441
|
+
throw new PollarFlowError(`verifyEmailCode() requires step 'entering_code', current step is '${this._authState.step}'`);
|
|
1442
|
+
}
|
|
1443
|
+
const state = this._authState;
|
|
1444
|
+
const clientSessionId = state.step === "entering_code" ? state.clientSessionId : state.clientSessionId;
|
|
1445
|
+
const email = state.step === "entering_code" ? state.email : state.email ?? "";
|
|
1446
|
+
const controller = this._newController();
|
|
1447
|
+
verifyAndAuthenticate(code, clientSessionId, email, this._flowDeps(controller.signal)).catch(
|
|
1448
|
+
(err) => this._handleFlowError(err)
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
// ─── Wallet flow (single call) ────────────────────────────────────────────
|
|
1452
|
+
loginWallet(type) {
|
|
1453
|
+
if (!isBrowser) {
|
|
1454
|
+
warnServerSide("loginWallet");
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
const controller = this._newController();
|
|
1458
|
+
loginWallet(type, this._flowDeps(controller.signal)).catch((err) => this._handleFlowError(err));
|
|
1459
|
+
}
|
|
1460
|
+
// ─── Cancel ───────────────────────────────────────────────────────────────
|
|
1461
|
+
cancelLogin() {
|
|
1462
|
+
this._loginController?.abort();
|
|
1463
|
+
this._loginController = null;
|
|
1464
|
+
this._setAuthState({ step: "idle" });
|
|
1465
|
+
}
|
|
1466
|
+
// ─── Logout ───────────────────────────────────────────────────────────────
|
|
1467
|
+
logout() {
|
|
1468
|
+
if (!isBrowser) {
|
|
1469
|
+
warnServerSide("logout");
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
console.info("[PollarClient] Logout requested");
|
|
1473
|
+
this._clearSession();
|
|
1474
|
+
}
|
|
1475
|
+
// ─── Network ──────────────────────────────────────────────────────────────
|
|
1476
|
+
getNetwork() {
|
|
1477
|
+
return this._networkState.step === "connected" ? this._networkState.network : "testnet";
|
|
1478
|
+
}
|
|
1479
|
+
getNetworkState() {
|
|
1480
|
+
return this._networkState;
|
|
1481
|
+
}
|
|
1482
|
+
setNetwork(network) {
|
|
1483
|
+
this._setNetworkState({ step: "connected", network });
|
|
1484
|
+
}
|
|
1485
|
+
onNetworkStateChange(cb) {
|
|
1486
|
+
this._networkStateListeners.add(cb);
|
|
1487
|
+
cb(this._networkState);
|
|
1488
|
+
return () => this._networkStateListeners.delete(cb);
|
|
1489
|
+
}
|
|
1490
|
+
// ─── Transaction state ────────────────────────────────────────────────────
|
|
1491
|
+
getTransactionState() {
|
|
1492
|
+
return this._transactionState;
|
|
1493
|
+
}
|
|
1494
|
+
onTransactionStateChange(cb) {
|
|
1495
|
+
this._transactionStateListeners.add(cb);
|
|
1496
|
+
if (this._transactionState) cb(this._transactionState);
|
|
1497
|
+
return () => this._transactionStateListeners.delete(cb);
|
|
1498
|
+
}
|
|
1499
|
+
// ─── Tx history ──────────────────────────────────────────────────────────
|
|
1500
|
+
_setTxHistoryState(next) {
|
|
1501
|
+
this._txHistoryState = next;
|
|
1502
|
+
for (const cb of this._txHistoryStateListeners) cb(next);
|
|
1503
|
+
}
|
|
1504
|
+
getTxHistoryState() {
|
|
1505
|
+
return this._txHistoryState;
|
|
1506
|
+
}
|
|
1507
|
+
onTxHistoryStateChange(cb) {
|
|
1508
|
+
this._txHistoryStateListeners.add(cb);
|
|
1509
|
+
cb(this._txHistoryState);
|
|
1510
|
+
return () => this._txHistoryStateListeners.delete(cb);
|
|
1511
|
+
}
|
|
1512
|
+
async fetchTxHistory(params = {}) {
|
|
1513
|
+
this._setTxHistoryState({ step: "loading", params });
|
|
1359
1514
|
try {
|
|
1360
|
-
const {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
error
|
|
1367
|
-
});
|
|
1368
|
-
return;
|
|
1515
|
+
const { data, error } = await this._api.GET("/tx/history", { params: { query: params } });
|
|
1516
|
+
if (!error && data?.success && data.content) {
|
|
1517
|
+
this._setTxHistoryState({ step: "loaded", params, data: data.content });
|
|
1518
|
+
} else {
|
|
1519
|
+
const message = error?.message ?? "Failed to load history";
|
|
1520
|
+
this._setTxHistoryState({ step: "error", params, message });
|
|
1369
1521
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
error
|
|
1373
|
-
});
|
|
1374
|
-
} catch (error) {
|
|
1375
|
-
this._emitState(
|
|
1376
|
-
"authentication",
|
|
1377
|
-
STATE_VAR_CODES.authentication.WALLET_AUTH_ALBEDO_NOT_INSTALLED,
|
|
1378
|
-
"error",
|
|
1379
|
-
StateStatus.ERROR
|
|
1380
|
-
);
|
|
1522
|
+
} catch {
|
|
1523
|
+
this._setTxHistoryState({ step: "error", params, message: "Failed to load history" });
|
|
1381
1524
|
}
|
|
1382
1525
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1526
|
+
// ─── Wallet balance ───────────────────────────────────────────────────────
|
|
1527
|
+
async getWalletBalance(publicKey) {
|
|
1528
|
+
const pk = publicKey ?? this._session?.wallet?.publicKey;
|
|
1529
|
+
if (!pk) return null;
|
|
1530
|
+
const network = this.getNetwork();
|
|
1531
|
+
const { data, error } = await this._api.GET("/wallet/balance", { params: { query: { publicKey: pk, network } } });
|
|
1532
|
+
if (!error && data?.success && data.content) return data.content;
|
|
1533
|
+
return null;
|
|
1385
1534
|
}
|
|
1535
|
+
// ─── Transactions ─────────────────────────────────────────────────────────
|
|
1386
1536
|
async buildTx(operation, params, options) {
|
|
1387
1537
|
if (!this._session?.wallet?.publicKey) {
|
|
1388
|
-
this.
|
|
1538
|
+
this._setTransactionState({ step: "error", details: "No wallet connected" });
|
|
1389
1539
|
return;
|
|
1390
1540
|
}
|
|
1391
1541
|
const body = {
|
|
1392
1542
|
network: this.getNetwork(),
|
|
1393
|
-
publicKey: this._session
|
|
1543
|
+
publicKey: this._session.wallet.publicKey,
|
|
1394
1544
|
operation,
|
|
1395
1545
|
params,
|
|
1396
|
-
options: options
|
|
1546
|
+
options: options ?? {}
|
|
1397
1547
|
};
|
|
1398
1548
|
try {
|
|
1399
|
-
this.
|
|
1400
|
-
const
|
|
1401
|
-
if (!
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
this._emitState.bind(this)
|
|
1407
|
-
)) {
|
|
1408
|
-
return;
|
|
1549
|
+
this._setTransactionState({ step: "building" });
|
|
1550
|
+
const { data, error } = await this._api.POST("/tx/build", { body });
|
|
1551
|
+
if (!error && data?.success && data.content) {
|
|
1552
|
+
this._setTransactionState({ step: "built", buildData: data.content });
|
|
1553
|
+
} else {
|
|
1554
|
+
const details = error?.details;
|
|
1555
|
+
this._setTransactionState({ step: "error", ...details && { details } });
|
|
1409
1556
|
}
|
|
1410
|
-
} catch
|
|
1411
|
-
this.
|
|
1412
|
-
body,
|
|
1413
|
-
error
|
|
1414
|
-
});
|
|
1415
|
-
return;
|
|
1557
|
+
} catch {
|
|
1558
|
+
this._setTransactionState({ step: "error" });
|
|
1416
1559
|
}
|
|
1417
1560
|
}
|
|
1418
|
-
async
|
|
1561
|
+
async signAndSubmitTx(unsignedXdr) {
|
|
1562
|
+
if (this._transactionState?.step !== "built") {
|
|
1563
|
+
throw new PollarFlowError(
|
|
1564
|
+
`signAndSubmitTx() requires step 'built', current step is '${this._transactionState?.step ?? "none"}'`
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
const buildData = this._transactionState.buildData;
|
|
1568
|
+
this._setTransactionState({ step: "signing", buildData });
|
|
1569
|
+
if (this._walletAdapter) {
|
|
1570
|
+
try {
|
|
1571
|
+
const signOpts = this._session?.wallet?.publicKey ? { networkPassphrase: this._networkPassphrase(), accountToSign: this._session.wallet.publicKey } : { networkPassphrase: this._networkPassphrase() };
|
|
1572
|
+
const { signedTxXdr } = await this._walletAdapter.signTransaction(unsignedXdr, signOpts);
|
|
1573
|
+
const stellarClient = new StellarClient(this.getNetwork());
|
|
1574
|
+
const result = await stellarClient.submitTransaction(signedTxXdr);
|
|
1575
|
+
if (result.success) {
|
|
1576
|
+
this._setTransactionState({ step: "success", buildData, hash: result.hash });
|
|
1577
|
+
} else {
|
|
1578
|
+
this._setTransactionState({ step: "error", ...buildData && { buildData }, details: result.errorCode });
|
|
1579
|
+
}
|
|
1580
|
+
} catch {
|
|
1581
|
+
this._setTransactionState({ step: "error", ...buildData && { buildData } });
|
|
1582
|
+
}
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1419
1585
|
const body = {
|
|
1420
1586
|
network: this.getNetwork(),
|
|
1421
|
-
|
|
1587
|
+
publicKey: this._session?.wallet?.publicKey ?? "",
|
|
1588
|
+
unsignedXdr
|
|
1422
1589
|
};
|
|
1423
1590
|
try {
|
|
1424
|
-
this.
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
{
|
|
1430
|
-
STATE_VAR_CODES.transaction.SIGN_SEND_TRANSACTION_ERROR,
|
|
1431
|
-
this._emitState.bind(this)
|
|
1432
|
-
)) {
|
|
1433
|
-
return;
|
|
1591
|
+
const { data, error } = await this._api.POST("/tx/sign-and-send", { body });
|
|
1592
|
+
if (!error && data?.success && data.content?.hash) {
|
|
1593
|
+
this._setTransactionState({ step: "success", buildData, hash: data.content.hash });
|
|
1594
|
+
} else {
|
|
1595
|
+
const details = error?.details;
|
|
1596
|
+
this._setTransactionState({ step: "error", ...buildData && { buildData }, ...details && { details } });
|
|
1434
1597
|
}
|
|
1435
|
-
} catch
|
|
1436
|
-
this.
|
|
1437
|
-
body,
|
|
1438
|
-
error
|
|
1439
|
-
});
|
|
1440
|
-
return;
|
|
1598
|
+
} catch {
|
|
1599
|
+
this._setTransactionState({ step: "error", ...buildData && { buildData } });
|
|
1441
1600
|
}
|
|
1442
1601
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1602
|
+
// ─── App config ───────────────────────────────────────────────────────────
|
|
1603
|
+
async getAppConfig() {
|
|
1604
|
+
try {
|
|
1605
|
+
const { data, error } = await this._api.GET("/applications/config");
|
|
1606
|
+
if (!data || error) return null;
|
|
1607
|
+
return data.content;
|
|
1608
|
+
} catch {
|
|
1609
|
+
return null;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
// ─── KYC ──────────────────────────────────────────────────────────────────
|
|
1613
|
+
getKycStatus(providerId) {
|
|
1614
|
+
return getKycStatus(this._api, providerId);
|
|
1615
|
+
}
|
|
1616
|
+
getKycProviders(country) {
|
|
1617
|
+
return getKycProviders(this._api, country);
|
|
1618
|
+
}
|
|
1619
|
+
startKyc(body) {
|
|
1620
|
+
return startKyc(this._api, body);
|
|
1621
|
+
}
|
|
1622
|
+
resolveKyc(providerId, level) {
|
|
1623
|
+
return resolveKyc(this._api, providerId, level);
|
|
1624
|
+
}
|
|
1625
|
+
pollKycStatus(providerId, opts) {
|
|
1626
|
+
return pollKycStatus(this._api, providerId, opts);
|
|
1627
|
+
}
|
|
1628
|
+
// ─── Ramps ────────────────────────────────────────────────────────────────
|
|
1629
|
+
getRampsQuote(query) {
|
|
1630
|
+
return getRampsQuote(this._api, query);
|
|
1631
|
+
}
|
|
1632
|
+
createOnRamp(body) {
|
|
1633
|
+
return createOnRamp(this._api, body);
|
|
1634
|
+
}
|
|
1635
|
+
createOffRamp(body) {
|
|
1636
|
+
return createOffRamp(this._api, body);
|
|
1637
|
+
}
|
|
1638
|
+
getRampTransaction(txId) {
|
|
1639
|
+
return getRampTransaction(this._api, txId);
|
|
1640
|
+
}
|
|
1641
|
+
pollRampTransaction(txId, opts) {
|
|
1642
|
+
return pollRampTransaction(this._api, txId, opts);
|
|
1643
|
+
}
|
|
1644
|
+
// ─── Private ──────────────────────────────────────────────────────────────
|
|
1645
|
+
/** Creates a new AbortController, cancelling any existing flow first. */
|
|
1646
|
+
_newController() {
|
|
1647
|
+
this._loginController?.abort();
|
|
1648
|
+
this._loginController = new AbortController();
|
|
1649
|
+
return this._loginController;
|
|
1650
|
+
}
|
|
1651
|
+
/** Builds the deps object passed to flow functions via bind pattern. */
|
|
1652
|
+
_flowDeps(signal) {
|
|
1653
|
+
return {
|
|
1654
|
+
api: this._api,
|
|
1655
|
+
signal,
|
|
1656
|
+
setAuthState: this._setAuthState.bind(this),
|
|
1657
|
+
storeSession: this._storeSession.bind(this),
|
|
1658
|
+
clearSession: this._clearSession.bind(this),
|
|
1659
|
+
storeWalletAdapter: (adapter) => {
|
|
1660
|
+
this._walletAdapter = adapter;
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
_handleFlowError(error) {
|
|
1665
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1666
|
+
console.info("[PollarClient] Login cancelled");
|
|
1667
|
+
this._setAuthState({ step: "idle" });
|
|
1446
1668
|
return;
|
|
1447
1669
|
}
|
|
1448
|
-
console.
|
|
1449
|
-
this.
|
|
1670
|
+
console.error("[PollarClient] Unexpected error in auth flow", error);
|
|
1671
|
+
this._setAuthState({
|
|
1672
|
+
step: "error",
|
|
1673
|
+
previousStep: this._authState.step,
|
|
1674
|
+
message: "An unexpected error occurred",
|
|
1675
|
+
errorCode: AUTH_ERROR_CODES.UNEXPECTED_ERROR
|
|
1676
|
+
});
|
|
1450
1677
|
}
|
|
1451
|
-
|
|
1678
|
+
_restoreSession() {
|
|
1452
1679
|
this._session = readStorage();
|
|
1453
1680
|
if (this._session) {
|
|
1454
|
-
this.
|
|
1455
|
-
"authentication",
|
|
1456
|
-
STATE_VAR_CODES.authentication.RESTORED_SESSION_SUCCESS,
|
|
1457
|
-
"info",
|
|
1458
|
-
StateStatus.SUCCESS,
|
|
1459
|
-
this._session
|
|
1460
|
-
);
|
|
1681
|
+
this._authState = { step: "authenticated", session: this._session };
|
|
1461
1682
|
console.info("[PollarClient] Session restored from storage");
|
|
1462
1683
|
} else {
|
|
1463
|
-
|
|
1464
|
-
console.info("[PollarClient] Session NO restored from storage");
|
|
1684
|
+
console.info("[PollarClient] No session in storage");
|
|
1465
1685
|
}
|
|
1466
1686
|
}
|
|
1467
1687
|
_storeSession(session) {
|
|
1468
1688
|
console.info(`[PollarClient] Session stored \u2014 user: ${session.userId ?? "anonymous"}`);
|
|
1469
1689
|
this._session = session;
|
|
1470
1690
|
writeStorage(session);
|
|
1471
|
-
this.
|
|
1472
|
-
"authentication",
|
|
1473
|
-
STATE_VAR_CODES.authentication.SESSION_STORED,
|
|
1474
|
-
"info",
|
|
1475
|
-
StateStatus.SUCCESS,
|
|
1476
|
-
this._session
|
|
1477
|
-
);
|
|
1691
|
+
this._setAuthState({ step: "authenticated", session });
|
|
1478
1692
|
}
|
|
1479
1693
|
_clearSession() {
|
|
1480
1694
|
console.info("[PollarClient] Session cleared");
|
|
1481
1695
|
this._session = null;
|
|
1696
|
+
this._walletAdapter = null;
|
|
1482
1697
|
removeStorage();
|
|
1483
|
-
this.
|
|
1484
|
-
|
|
1485
|
-
authentication: [],
|
|
1486
|
-
transaction: []
|
|
1487
|
-
};
|
|
1488
|
-
this._emitState("authentication", STATE_VAR_CODES.authentication.LOGOUT, "info", StateStatus.NONE);
|
|
1489
|
-
}
|
|
1490
|
-
_emitState(fn, code, level, status, data) {
|
|
1491
|
-
const stateEntry = { var: fn, code, level, data, status, ts: Date.now() };
|
|
1492
|
-
this._state[fn].push(stateEntry);
|
|
1493
|
-
console[level](`[PollarClient] ${fn}:${code} \u2014 ${status}`);
|
|
1494
|
-
for (const cb of this._stateListeners) {
|
|
1495
|
-
cb(stateEntry);
|
|
1496
|
-
}
|
|
1698
|
+
this._transactionState = null;
|
|
1699
|
+
this._setAuthState({ step: "idle" });
|
|
1497
1700
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
// src/stellar/StellarClient.ts
|
|
1501
|
-
var HORIZON_URLS = {
|
|
1502
|
-
mainnet: "https://horizon.stellar.org",
|
|
1503
|
-
testnet: "https://horizon-testnet.stellar.org"
|
|
1504
|
-
};
|
|
1505
|
-
var StellarClient = class {
|
|
1506
|
-
constructor(config) {
|
|
1507
|
-
this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
|
|
1701
|
+
_networkPassphrase() {
|
|
1702
|
+
return this.getNetwork() === "mainnet" ? "Public Global Stellar Network ; September 2015" : "Test SDF Network ; September 2015";
|
|
1508
1703
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
asset: b.asset_type === "native" ? "XLM" : b.asset_code ?? "",
|
|
1525
|
-
balance: b.balance,
|
|
1526
|
-
...b.asset_type !== "native" && { assetIssuer: b.asset_issuer }
|
|
1527
|
-
}))
|
|
1528
|
-
};
|
|
1529
|
-
} catch (error) {
|
|
1530
|
-
console.warn("[StellarClient] Network error fetching balances", error);
|
|
1531
|
-
return { success: false, errorCode: "NETWORK_ERROR", balances: [] };
|
|
1532
|
-
}
|
|
1704
|
+
_setNetworkState(next) {
|
|
1705
|
+
this._networkState = next;
|
|
1706
|
+
const label = next.step === "connected" ? next.network : next.step;
|
|
1707
|
+
console.info(`[PollarClient] network:${label}`);
|
|
1708
|
+
for (const cb of this._networkStateListeners) cb(next);
|
|
1709
|
+
}
|
|
1710
|
+
_setAuthState(next) {
|
|
1711
|
+
this._authState = next;
|
|
1712
|
+
console.info(`[PollarClient] auth:${next.step}`);
|
|
1713
|
+
for (const cb of this._authStateListeners) cb(next);
|
|
1714
|
+
}
|
|
1715
|
+
_setTransactionState(next) {
|
|
1716
|
+
this._transactionState = next;
|
|
1717
|
+
console.info(`[PollarClient] transaction:${next.step}`);
|
|
1718
|
+
for (const cb of this._transactionStateListeners) cb(next);
|
|
1533
1719
|
}
|
|
1534
1720
|
};
|
|
1535
1721
|
|
|
1536
|
-
export { AlbedoAdapter, FreighterAdapter, PollarClient,
|
|
1722
|
+
export { AUTH_ERROR_CODES, AlbedoAdapter, FreighterAdapter, PollarClient, StellarClient, WalletType, createOffRamp, createOnRamp, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, pollKycStatus, pollRampTransaction, resolveKyc, startKyc };
|
|
1537
1723
|
//# sourceMappingURL=index.mjs.map
|
|
1538
1724
|
//# sourceMappingURL=index.mjs.map
|