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