@pollar/core 0.4.5 → 0.5.2
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 +1483 -290
- package/dist/index.d.ts +1483 -290
- package/dist/index.js +540 -361
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +531 -359
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -650,31 +650,98 @@ function createApiClient(baseUrl) {
|
|
|
650
650
|
return createClient({ baseUrl });
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
-
// src/
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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");
|
|
660
|
+
}
|
|
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
|
+
}
|
|
688
|
+
|
|
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
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/stellar/StellarClient.ts
|
|
721
|
+
var HORIZON_URLS = {
|
|
722
|
+
mainnet: "https://horizon.stellar.org",
|
|
723
|
+
testnet: "https://horizon-testnet.stellar.org"
|
|
663
724
|
};
|
|
664
|
-
var
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
725
|
+
var StellarClient = class {
|
|
726
|
+
constructor(config) {
|
|
727
|
+
this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
|
|
728
|
+
}
|
|
729
|
+
async submitTransaction(signedXdr) {
|
|
730
|
+
try {
|
|
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 };
|
|
742
|
+
} catch {
|
|
743
|
+
return { success: false, errorCode: "NETWORK_ERROR" };
|
|
744
|
+
}
|
|
678
745
|
}
|
|
679
746
|
};
|
|
680
747
|
|
|
@@ -698,8 +765,176 @@ var PollarFlowError = class extends Error {
|
|
|
698
765
|
}
|
|
699
766
|
};
|
|
700
767
|
|
|
768
|
+
// src/wallets/FreighterAdapter.ts
|
|
769
|
+
var import_freighter_api = __toESM(require_index_min());
|
|
770
|
+
|
|
771
|
+
// src/wallets/types.ts
|
|
772
|
+
var WalletType = /* @__PURE__ */ ((WalletType2) => {
|
|
773
|
+
WalletType2["FREIGHTER"] = "freighter";
|
|
774
|
+
WalletType2["ALBEDO"] = "albedo";
|
|
775
|
+
return WalletType2;
|
|
776
|
+
})(WalletType || {});
|
|
777
|
+
|
|
778
|
+
// src/wallets/FreighterAdapter.ts
|
|
779
|
+
var FreighterAdapter = class {
|
|
780
|
+
constructor() {
|
|
781
|
+
this.type = "freighter" /* FREIGHTER */;
|
|
782
|
+
}
|
|
783
|
+
async isAvailable() {
|
|
784
|
+
try {
|
|
785
|
+
return await (0, import_freighter_api.isConnected)();
|
|
786
|
+
} catch {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async connect() {
|
|
791
|
+
const connected = await (0, import_freighter_api.isConnected)();
|
|
792
|
+
if (!connected) {
|
|
793
|
+
throw new Error("Freighter wallet is not installed");
|
|
794
|
+
}
|
|
795
|
+
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
796
|
+
if (!allowed) {
|
|
797
|
+
await (0, import_freighter_api.setAllowed)();
|
|
798
|
+
}
|
|
799
|
+
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
800
|
+
if (!userInfo?.publicKey) {
|
|
801
|
+
throw new Error("Failed to get user information from Freighter");
|
|
802
|
+
}
|
|
803
|
+
return { address: userInfo.publicKey, publicKey: userInfo.publicKey };
|
|
804
|
+
}
|
|
805
|
+
async disconnect() {
|
|
806
|
+
}
|
|
807
|
+
async getPublicKey() {
|
|
808
|
+
try {
|
|
809
|
+
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
810
|
+
if (!allowed) return null;
|
|
811
|
+
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
812
|
+
return userInfo?.publicKey ?? null;
|
|
813
|
+
} catch {
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
async getNetwork() {
|
|
818
|
+
return (0, import_freighter_api.getNetwork)();
|
|
819
|
+
}
|
|
820
|
+
async signTransaction(xdr, options) {
|
|
821
|
+
const result = await (0, import_freighter_api.signTransaction)(xdr, {
|
|
822
|
+
network: options?.network,
|
|
823
|
+
networkPassphrase: options?.networkPassphrase,
|
|
824
|
+
accountToSign: options?.accountToSign
|
|
825
|
+
});
|
|
826
|
+
if (!result || typeof result !== "string") {
|
|
827
|
+
throw new Error("Invalid response from Freighter");
|
|
828
|
+
}
|
|
829
|
+
return { signedTxXdr: result };
|
|
830
|
+
}
|
|
831
|
+
async signAuthEntry(entryXdr, options) {
|
|
832
|
+
const result = await (0, import_freighter_api.signAuthEntry)(entryXdr, { accountToSign: options?.accountToSign });
|
|
833
|
+
if (!result || typeof result !== "string") {
|
|
834
|
+
throw new Error("Invalid response from Freighter");
|
|
835
|
+
}
|
|
836
|
+
return { signedAuthEntry: result };
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
// src/wallets/AlbedoAdapter.ts
|
|
841
|
+
function openAlbedoPopup(url) {
|
|
842
|
+
const popup = window.open(url, "albedo", "width=420,height=720,resizable=yes,scrollbars=yes");
|
|
843
|
+
if (!popup) {
|
|
844
|
+
throw new Error("Failed to open Albedo popup (blocked by browser)");
|
|
845
|
+
}
|
|
846
|
+
return popup;
|
|
847
|
+
}
|
|
848
|
+
function waitForAlbedoPopup() {
|
|
849
|
+
return new Promise((resolve, reject) => {
|
|
850
|
+
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
851
|
+
function handler(event) {
|
|
852
|
+
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
853
|
+
clearTimeout(timeout);
|
|
854
|
+
window.removeEventListener("message", handler);
|
|
855
|
+
resolve(event.data.payload);
|
|
856
|
+
}
|
|
857
|
+
window.addEventListener("message", handler);
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
function waitForAlbedoResult() {
|
|
861
|
+
return new Promise((resolve, reject) => {
|
|
862
|
+
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
863
|
+
const parseResult = () => {
|
|
864
|
+
const params = new URLSearchParams(window.location.search);
|
|
865
|
+
if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
|
|
866
|
+
clearTimeout(timeout);
|
|
867
|
+
const result = {};
|
|
868
|
+
params.forEach((value, key) => {
|
|
869
|
+
result[key] = value;
|
|
870
|
+
});
|
|
871
|
+
window.history.replaceState({}, document.title, window.location.pathname);
|
|
872
|
+
resolve(result);
|
|
873
|
+
};
|
|
874
|
+
parseResult();
|
|
875
|
+
window.addEventListener("popstate", parseResult);
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
var AlbedoAdapter = class {
|
|
879
|
+
constructor() {
|
|
880
|
+
this.type = "albedo" /* ALBEDO */;
|
|
881
|
+
}
|
|
882
|
+
async isAvailable() {
|
|
883
|
+
return typeof window !== "undefined";
|
|
884
|
+
}
|
|
885
|
+
async connect() {
|
|
886
|
+
const url = new URL("https://albedo.link");
|
|
887
|
+
url.searchParams.set("intent", "public-key");
|
|
888
|
+
url.searchParams.set("app_name", "Pollar");
|
|
889
|
+
url.searchParams.set("network", "testnet");
|
|
890
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
891
|
+
url.searchParams.set("origin", window.location.origin);
|
|
892
|
+
openAlbedoPopup(url.toString());
|
|
893
|
+
const result = await waitForAlbedoPopup();
|
|
894
|
+
if (!result.pubkey) {
|
|
895
|
+
throw new Error("Albedo connection rejected");
|
|
896
|
+
}
|
|
897
|
+
return { address: result.pubkey, publicKey: result.pubkey };
|
|
898
|
+
}
|
|
899
|
+
async disconnect() {
|
|
900
|
+
}
|
|
901
|
+
async getPublicKey() {
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
async getNetwork() {
|
|
905
|
+
throw new Error("Albedo does not expose network");
|
|
906
|
+
}
|
|
907
|
+
async signTransaction(xdr, _options) {
|
|
908
|
+
const url = new URL("https://albedo.link");
|
|
909
|
+
url.searchParams.set("intent", "tx");
|
|
910
|
+
url.searchParams.set("xdr", xdr);
|
|
911
|
+
url.searchParams.set("app_name", "Pollar");
|
|
912
|
+
url.searchParams.set("network", "testnet");
|
|
913
|
+
url.searchParams.set("callback", window.location.href);
|
|
914
|
+
url.searchParams.set("origin", window.location.origin);
|
|
915
|
+
window.location.href = url.toString();
|
|
916
|
+
const result = await waitForAlbedoResult();
|
|
917
|
+
if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
|
|
918
|
+
return { signedTxXdr: result.signed_envelope_xdr };
|
|
919
|
+
}
|
|
920
|
+
async signAuthEntry(entryXdr, _options) {
|
|
921
|
+
const url = new URL("https://albedo.link");
|
|
922
|
+
url.searchParams.set("intent", "sign-auth-entry");
|
|
923
|
+
url.searchParams.set("xdr", entryXdr);
|
|
924
|
+
url.searchParams.set("app_name", "Pollar");
|
|
925
|
+
url.searchParams.set("network", "testnet");
|
|
926
|
+
url.searchParams.set("callback", window.location.href);
|
|
927
|
+
url.searchParams.set("origin", window.location.origin);
|
|
928
|
+
window.location.href = url.toString();
|
|
929
|
+
const result = await waitForAlbedoResult();
|
|
930
|
+
if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
|
|
931
|
+
return { signedAuthEntry: result.signed_xdr };
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
|
|
701
935
|
// src/client/session.ts
|
|
702
936
|
var STORAGE_KEY = "pollar:session";
|
|
937
|
+
var WALLET_TYPE_KEY = "pollar:walletType";
|
|
703
938
|
function isValidSession(value) {
|
|
704
939
|
if (typeof value !== "object" || value === null) {
|
|
705
940
|
console.warn("[PollarClient:session] Invalid session \u2014 value is not an object");
|
|
@@ -835,8 +1070,15 @@ function writeStorage(session) {
|
|
|
835
1070
|
}
|
|
836
1071
|
function removeStorage() {
|
|
837
1072
|
localStorage.removeItem(STORAGE_KEY);
|
|
1073
|
+
localStorage.removeItem(WALLET_TYPE_KEY);
|
|
838
1074
|
console.info("[PollarClient:session] Session removed from storage");
|
|
839
1075
|
}
|
|
1076
|
+
function writeWalletType(type) {
|
|
1077
|
+
localStorage.setItem(WALLET_TYPE_KEY, type);
|
|
1078
|
+
}
|
|
1079
|
+
function readWalletType() {
|
|
1080
|
+
return localStorage.getItem(WALLET_TYPE_KEY);
|
|
1081
|
+
}
|
|
840
1082
|
|
|
841
1083
|
// src/client/stream.ts
|
|
842
1084
|
function abortableDelay(ms, signal) {
|
|
@@ -910,7 +1152,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
|
|
|
910
1152
|
}
|
|
911
1153
|
|
|
912
1154
|
// src/client/auth/authenticate.ts
|
|
913
|
-
async function authenticate(clientSessionId, deps) {
|
|
1155
|
+
async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
914
1156
|
const { api, signal, setAuthState, storeSession, clearSession } = deps;
|
|
915
1157
|
setAuthState({ step: "authenticating" });
|
|
916
1158
|
await streamUntilFound(api, clientSessionId, (data2) => data2?.status === "READY", 200, signal);
|
|
@@ -919,6 +1161,16 @@ async function authenticate(clientSessionId, deps) {
|
|
|
919
1161
|
signal
|
|
920
1162
|
});
|
|
921
1163
|
if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content)) {
|
|
1164
|
+
if (expectedWallet && data.content.data.providers.wallet?.address !== expectedWallet) {
|
|
1165
|
+
setAuthState({
|
|
1166
|
+
step: "error",
|
|
1167
|
+
previousStep: "authenticating",
|
|
1168
|
+
message: "Wallet mismatch: session wallet does not match connected wallet",
|
|
1169
|
+
errorCode: AUTH_ERROR_CODES.WALLET_AUTH_FAILED
|
|
1170
|
+
});
|
|
1171
|
+
clearSession();
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
922
1174
|
storeSession(data.content);
|
|
923
1175
|
} else {
|
|
924
1176
|
setAuthState({
|
|
@@ -931,8 +1183,8 @@ async function authenticate(clientSessionId, deps) {
|
|
|
931
1183
|
}
|
|
932
1184
|
}
|
|
933
1185
|
|
|
934
|
-
// src/client/auth/
|
|
935
|
-
async function
|
|
1186
|
+
// src/client/auth/deps.ts
|
|
1187
|
+
async function createAuthSession(deps) {
|
|
936
1188
|
const { api, signal, setAuthState } = deps;
|
|
937
1189
|
setAuthState({ step: "creating_session" });
|
|
938
1190
|
const { data, error } = await api.POST("/auth/session", { signal });
|
|
@@ -943,9 +1195,16 @@ async function initEmailSession(deps) {
|
|
|
943
1195
|
message: "Failed to create session",
|
|
944
1196
|
errorCode: AUTH_ERROR_CODES.SESSION_CREATE_FAILED
|
|
945
1197
|
});
|
|
946
|
-
return;
|
|
1198
|
+
return null;
|
|
947
1199
|
}
|
|
948
|
-
|
|
1200
|
+
return data.content.clientSessionId;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// src/client/auth/emailFlow.ts
|
|
1204
|
+
async function initEmailSession(deps) {
|
|
1205
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1206
|
+
if (!clientSessionId) return;
|
|
1207
|
+
deps.setAuthState({ step: "entering_email", clientSessionId });
|
|
949
1208
|
}
|
|
950
1209
|
async function sendEmailCode(email, clientSessionId, deps) {
|
|
951
1210
|
const { api, signal, setAuthState } = deps;
|
|
@@ -1008,25 +1267,10 @@ async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
|
1008
1267
|
}
|
|
1009
1268
|
|
|
1010
1269
|
// src/client/auth/oauthFlow.ts
|
|
1011
|
-
async function initOAuthSession(deps) {
|
|
1012
|
-
const { api, signal, setAuthState } = deps;
|
|
1013
|
-
setAuthState({ step: "creating_session" });
|
|
1014
|
-
const { data, error } = await api.POST("/auth/session", { signal });
|
|
1015
|
-
if (error || !data?.success) {
|
|
1016
|
-
setAuthState({
|
|
1017
|
-
step: "error",
|
|
1018
|
-
previousStep: "creating_session",
|
|
1019
|
-
message: "Failed to create session",
|
|
1020
|
-
errorCode: AUTH_ERROR_CODES.SESSION_CREATE_FAILED
|
|
1021
|
-
});
|
|
1022
|
-
return null;
|
|
1023
|
-
}
|
|
1024
|
-
return data.content.clientSessionId;
|
|
1025
|
-
}
|
|
1026
1270
|
async function loginOAuth(provider, deps) {
|
|
1027
1271
|
const { setAuthState, basePath, apiKey } = deps;
|
|
1028
1272
|
const popup = window.open("about:blank", "_blank");
|
|
1029
|
-
const clientSessionId = await
|
|
1273
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1030
1274
|
if (!clientSessionId) {
|
|
1031
1275
|
popup?.close();
|
|
1032
1276
|
return;
|
|
@@ -1044,173 +1288,6 @@ async function loginOAuth(provider, deps) {
|
|
|
1044
1288
|
await authenticate(clientSessionId, deps);
|
|
1045
1289
|
}
|
|
1046
1290
|
|
|
1047
|
-
// src/wallets/FreighterAdapter.ts
|
|
1048
|
-
var import_freighter_api = __toESM(require_index_min());
|
|
1049
|
-
|
|
1050
|
-
// src/wallets/types.ts
|
|
1051
|
-
var WalletType = /* @__PURE__ */ ((WalletType2) => {
|
|
1052
|
-
WalletType2["FREIGHTER"] = "freighter";
|
|
1053
|
-
WalletType2["ALBEDO"] = "albedo";
|
|
1054
|
-
return WalletType2;
|
|
1055
|
-
})(WalletType || {});
|
|
1056
|
-
|
|
1057
|
-
// src/wallets/FreighterAdapter.ts
|
|
1058
|
-
var FreighterAdapter = class {
|
|
1059
|
-
constructor() {
|
|
1060
|
-
this.type = "freighter" /* FREIGHTER */;
|
|
1061
|
-
}
|
|
1062
|
-
async isAvailable() {
|
|
1063
|
-
try {
|
|
1064
|
-
return await (0, import_freighter_api.isConnected)();
|
|
1065
|
-
} catch {
|
|
1066
|
-
return false;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
async connect() {
|
|
1070
|
-
const connected = await (0, import_freighter_api.isConnected)();
|
|
1071
|
-
if (!connected) {
|
|
1072
|
-
throw new Error("Freighter wallet is not installed");
|
|
1073
|
-
}
|
|
1074
|
-
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
1075
|
-
if (!allowed) {
|
|
1076
|
-
await (0, import_freighter_api.setAllowed)();
|
|
1077
|
-
}
|
|
1078
|
-
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
1079
|
-
if (!userInfo?.publicKey) {
|
|
1080
|
-
throw new Error("Failed to get user information from Freighter");
|
|
1081
|
-
}
|
|
1082
|
-
return { address: userInfo.publicKey, publicKey: userInfo.publicKey };
|
|
1083
|
-
}
|
|
1084
|
-
async disconnect() {
|
|
1085
|
-
}
|
|
1086
|
-
async getPublicKey() {
|
|
1087
|
-
try {
|
|
1088
|
-
const allowed = await (0, import_freighter_api.isAllowed)();
|
|
1089
|
-
if (!allowed) return null;
|
|
1090
|
-
const userInfo = await (0, import_freighter_api.getUserInfo)();
|
|
1091
|
-
return userInfo?.publicKey ?? null;
|
|
1092
|
-
} catch {
|
|
1093
|
-
return null;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
async getNetwork() {
|
|
1097
|
-
return (0, import_freighter_api.getNetwork)();
|
|
1098
|
-
}
|
|
1099
|
-
async signTransaction(xdr, options) {
|
|
1100
|
-
const result = await (0, import_freighter_api.signTransaction)(xdr, {
|
|
1101
|
-
network: options?.network,
|
|
1102
|
-
networkPassphrase: options?.networkPassphrase,
|
|
1103
|
-
accountToSign: options?.accountToSign
|
|
1104
|
-
});
|
|
1105
|
-
if (!result || typeof result !== "string") {
|
|
1106
|
-
throw new Error("Invalid response from Freighter");
|
|
1107
|
-
}
|
|
1108
|
-
return { signedTxXdr: result };
|
|
1109
|
-
}
|
|
1110
|
-
async signAuthEntry(entryXdr, options) {
|
|
1111
|
-
const result = await (0, import_freighter_api.signAuthEntry)(entryXdr, { accountToSign: options?.accountToSign });
|
|
1112
|
-
if (!result || typeof result !== "string") {
|
|
1113
|
-
throw new Error("Invalid response from Freighter");
|
|
1114
|
-
}
|
|
1115
|
-
return { signedAuthEntry: result };
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
|
|
1119
|
-
// src/wallets/AlbedoAdapter.ts
|
|
1120
|
-
function openAlbedoPopup(url) {
|
|
1121
|
-
const popup = window.open(url, "albedo", "width=420,height=720,resizable=yes,scrollbars=yes");
|
|
1122
|
-
if (!popup) {
|
|
1123
|
-
throw new Error("Failed to open Albedo popup (blocked by browser)");
|
|
1124
|
-
}
|
|
1125
|
-
return popup;
|
|
1126
|
-
}
|
|
1127
|
-
function waitForAlbedoPopup() {
|
|
1128
|
-
return new Promise((resolve, reject) => {
|
|
1129
|
-
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
1130
|
-
function handler(event) {
|
|
1131
|
-
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
1132
|
-
clearTimeout(timeout);
|
|
1133
|
-
window.removeEventListener("message", handler);
|
|
1134
|
-
resolve(event.data.payload);
|
|
1135
|
-
}
|
|
1136
|
-
window.addEventListener("message", handler);
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
function waitForAlbedoResult() {
|
|
1140
|
-
return new Promise((resolve, reject) => {
|
|
1141
|
-
const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
|
|
1142
|
-
const parseResult = () => {
|
|
1143
|
-
const params = new URLSearchParams(window.location.search);
|
|
1144
|
-
if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
|
|
1145
|
-
clearTimeout(timeout);
|
|
1146
|
-
const result = {};
|
|
1147
|
-
params.forEach((value, key) => {
|
|
1148
|
-
result[key] = value;
|
|
1149
|
-
});
|
|
1150
|
-
window.history.replaceState({}, document.title, window.location.pathname);
|
|
1151
|
-
resolve(result);
|
|
1152
|
-
};
|
|
1153
|
-
parseResult();
|
|
1154
|
-
window.addEventListener("popstate", parseResult);
|
|
1155
|
-
});
|
|
1156
|
-
}
|
|
1157
|
-
var AlbedoAdapter = class {
|
|
1158
|
-
constructor() {
|
|
1159
|
-
this.type = "albedo" /* ALBEDO */;
|
|
1160
|
-
}
|
|
1161
|
-
async isAvailable() {
|
|
1162
|
-
return typeof window !== "undefined";
|
|
1163
|
-
}
|
|
1164
|
-
async connect() {
|
|
1165
|
-
const url = new URL("https://albedo.link");
|
|
1166
|
-
url.searchParams.set("intent", "public-key");
|
|
1167
|
-
url.searchParams.set("app_name", "Pollar");
|
|
1168
|
-
url.searchParams.set("network", "testnet");
|
|
1169
|
-
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1170
|
-
url.searchParams.set("origin", window.location.origin);
|
|
1171
|
-
openAlbedoPopup(url.toString());
|
|
1172
|
-
const result = await waitForAlbedoPopup();
|
|
1173
|
-
if (!result.pubkey) {
|
|
1174
|
-
throw new Error("Albedo connection rejected");
|
|
1175
|
-
}
|
|
1176
|
-
return { address: result.pubkey, publicKey: result.pubkey };
|
|
1177
|
-
}
|
|
1178
|
-
async disconnect() {
|
|
1179
|
-
}
|
|
1180
|
-
async getPublicKey() {
|
|
1181
|
-
return null;
|
|
1182
|
-
}
|
|
1183
|
-
async getNetwork() {
|
|
1184
|
-
throw new Error("Albedo does not expose network");
|
|
1185
|
-
}
|
|
1186
|
-
async signTransaction(xdr, _options) {
|
|
1187
|
-
const url = new URL("https://albedo.link");
|
|
1188
|
-
url.searchParams.set("intent", "tx");
|
|
1189
|
-
url.searchParams.set("xdr", xdr);
|
|
1190
|
-
url.searchParams.set("app_name", "Pollar");
|
|
1191
|
-
url.searchParams.set("network", "testnet");
|
|
1192
|
-
url.searchParams.set("callback", window.location.href);
|
|
1193
|
-
url.searchParams.set("origin", window.location.origin);
|
|
1194
|
-
window.location.href = url.toString();
|
|
1195
|
-
const result = await waitForAlbedoResult();
|
|
1196
|
-
if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
|
|
1197
|
-
return { signedTxXdr: result.signed_envelope_xdr };
|
|
1198
|
-
}
|
|
1199
|
-
async signAuthEntry(entryXdr, _options) {
|
|
1200
|
-
const url = new URL("https://albedo.link");
|
|
1201
|
-
url.searchParams.set("intent", "sign-auth-entry");
|
|
1202
|
-
url.searchParams.set("xdr", entryXdr);
|
|
1203
|
-
url.searchParams.set("app_name", "Pollar");
|
|
1204
|
-
url.searchParams.set("network", "testnet");
|
|
1205
|
-
url.searchParams.set("callback", window.location.href);
|
|
1206
|
-
url.searchParams.set("origin", window.location.origin);
|
|
1207
|
-
window.location.href = url.toString();
|
|
1208
|
-
const result = await waitForAlbedoResult();
|
|
1209
|
-
if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
|
|
1210
|
-
return { signedAuthEntry: result.signed_xdr };
|
|
1211
|
-
}
|
|
1212
|
-
};
|
|
1213
|
-
|
|
1214
1291
|
// src/client/auth/walletFlow.ts
|
|
1215
1292
|
function withSignal(promise, signal) {
|
|
1216
1293
|
return Promise.race([
|
|
@@ -1226,18 +1303,9 @@ function withSignal(promise, signal) {
|
|
|
1226
1303
|
}
|
|
1227
1304
|
async function loginWallet(type, deps) {
|
|
1228
1305
|
const { api, signal, setAuthState } = deps;
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
setAuthState({
|
|
1233
|
-
step: "error",
|
|
1234
|
-
previousStep: "creating_session",
|
|
1235
|
-
message: "Failed to create session",
|
|
1236
|
-
errorCode: AUTH_ERROR_CODES.SESSION_CREATE_FAILED
|
|
1237
|
-
});
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
const clientSessionId = data.content.clientSessionId;
|
|
1306
|
+
const clientSessionId = await createAuthSession(deps);
|
|
1307
|
+
if (!clientSessionId) return;
|
|
1308
|
+
let connectedWallet;
|
|
1241
1309
|
try {
|
|
1242
1310
|
setAuthState({ step: "connecting_wallet", walletType: type });
|
|
1243
1311
|
const adapter = type === "freighter" /* FREIGHTER */ ? new FreighterAdapter() : new AlbedoAdapter();
|
|
@@ -1247,6 +1315,8 @@ async function loginWallet(type, deps) {
|
|
|
1247
1315
|
return;
|
|
1248
1316
|
}
|
|
1249
1317
|
const { publicKey } = await withSignal(adapter.connect(), signal);
|
|
1318
|
+
connectedWallet = publicKey;
|
|
1319
|
+
deps.storeWalletAdapter(adapter, type);
|
|
1250
1320
|
setAuthState({ step: "authenticating_wallet" });
|
|
1251
1321
|
const { data: walletData, error: walletError } = await api.POST("/auth/wallet", {
|
|
1252
1322
|
body: { clientSessionId, walletAddress: publicKey },
|
|
@@ -1270,22 +1340,9 @@ async function loginWallet(type, deps) {
|
|
|
1270
1340
|
});
|
|
1271
1341
|
return;
|
|
1272
1342
|
}
|
|
1273
|
-
await authenticate(clientSessionId, deps);
|
|
1343
|
+
await authenticate(clientSessionId, deps, connectedWallet);
|
|
1274
1344
|
}
|
|
1275
1345
|
|
|
1276
|
-
// src/client/helpers.ts
|
|
1277
|
-
var emitResponse = (state, response, success, errorCode, emitLog) => {
|
|
1278
|
-
const isSuccess = !response.error && !!response.data && !!response.data?.success;
|
|
1279
|
-
emitLog(
|
|
1280
|
-
state,
|
|
1281
|
-
isSuccess ? success.code : errorCode,
|
|
1282
|
-
isSuccess ? "info" : "error",
|
|
1283
|
-
isSuccess ? success.status || StateStatus.LOADING : StateStatus.ERROR,
|
|
1284
|
-
isSuccess ? response.data : response.error
|
|
1285
|
-
);
|
|
1286
|
-
return isSuccess;
|
|
1287
|
-
};
|
|
1288
|
-
|
|
1289
1346
|
// src/client/client.ts
|
|
1290
1347
|
var isBrowser = typeof window !== "undefined" && typeof localStorage !== "undefined";
|
|
1291
1348
|
function warnServerSide(method) {
|
|
@@ -1296,46 +1353,50 @@ function warnServerSide(method) {
|
|
|
1296
1353
|
var PollarClient = class {
|
|
1297
1354
|
constructor(config) {
|
|
1298
1355
|
this._session = null;
|
|
1299
|
-
this.
|
|
1300
|
-
this.
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
};
|
|
1356
|
+
this._transactionState = null;
|
|
1357
|
+
this._transactionStateListeners = /* @__PURE__ */ new Set();
|
|
1358
|
+
this._txHistoryState = { step: "idle" };
|
|
1359
|
+
this._txHistoryStateListeners = /* @__PURE__ */ new Set();
|
|
1360
|
+
this._walletBalanceState = { step: "idle" };
|
|
1361
|
+
this._walletBalanceStateListeners = /* @__PURE__ */ new Set();
|
|
1304
1362
|
this._authState = { step: "idle" };
|
|
1305
1363
|
this._authStateListeners = /* @__PURE__ */ new Set();
|
|
1364
|
+
this._networkState = { step: "idle" };
|
|
1365
|
+
this._networkStateListeners = /* @__PURE__ */ new Set();
|
|
1366
|
+
this._walletAdapter = null;
|
|
1306
1367
|
this._loginController = null;
|
|
1307
1368
|
this.apiKey = config.apiKey;
|
|
1308
1369
|
this.id = crypto.randomUUID();
|
|
1309
1370
|
this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
|
|
1310
1371
|
this._api = createApiClient(this.basePath);
|
|
1372
|
+
const self = this;
|
|
1311
1373
|
this._api.use({
|
|
1312
1374
|
onRequest({ request }) {
|
|
1313
1375
|
request.headers.set("x-pollar-api-key", config.apiKey);
|
|
1376
|
+
const accessToken = self._session?.token?.accessToken;
|
|
1377
|
+
if (accessToken) {
|
|
1378
|
+
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1379
|
+
}
|
|
1314
1380
|
return request;
|
|
1315
1381
|
}
|
|
1316
1382
|
});
|
|
1383
|
+
this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
|
|
1317
1384
|
if (!isBrowser) {
|
|
1318
1385
|
warnServerSide("constructor");
|
|
1319
1386
|
this._session = null;
|
|
1320
1387
|
return;
|
|
1321
1388
|
}
|
|
1322
|
-
console.info(`[PollarClient] Initialized \u2014 endpoint: ${this.basePath}`);
|
|
1323
|
-
this.
|
|
1389
|
+
console.info(`[PollarClient] Initialized \u2014 endpoint: ${this.basePath}, network: ${this._networkState.network}`);
|
|
1390
|
+
this._restoreSession();
|
|
1324
1391
|
window.addEventListener("storage", (e) => {
|
|
1325
1392
|
if (e.key === STORAGE_KEY) {
|
|
1326
1393
|
const prev = this._session;
|
|
1327
1394
|
console.info(`[PollarClient] Storage event \u2014 session ${this._session ? "updated" : prev ? "cleared" : "unchanged"}`);
|
|
1328
|
-
this.
|
|
1395
|
+
this._restoreSession();
|
|
1329
1396
|
}
|
|
1330
1397
|
});
|
|
1331
|
-
this._emitState("network", STATE_VAR_CODES.network.NETWORK_UPDATED, "info", StateStatus.SUCCESS, {
|
|
1332
|
-
network: "testnet"
|
|
1333
|
-
});
|
|
1334
1398
|
}
|
|
1335
1399
|
// ─── Auth state ──────────────────────────────────────────────────────────────
|
|
1336
|
-
isAuthenticated() {
|
|
1337
|
-
return !!this._session?.wallet?.publicKey;
|
|
1338
|
-
}
|
|
1339
1400
|
getAuthState() {
|
|
1340
1401
|
return this._authState;
|
|
1341
1402
|
}
|
|
@@ -1350,17 +1411,23 @@ var PollarClient = class {
|
|
|
1350
1411
|
warnServerSide("login");
|
|
1351
1412
|
return;
|
|
1352
1413
|
}
|
|
1353
|
-
if (options.provider === "google" || options.provider === "github") {
|
|
1354
|
-
this.loginOAuth(options.provider);
|
|
1355
|
-
} else if (options.provider === "email") {
|
|
1356
|
-
const { email } = options;
|
|
1414
|
+
if (options.provider === "google" || options.provider === "github" || options.provider === "email") {
|
|
1357
1415
|
const controller = this._newController();
|
|
1358
1416
|
const deps = this._flowDeps(controller.signal);
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1417
|
+
if (options.provider === "google" || options.provider === "github") {
|
|
1418
|
+
loginOAuth(options.provider, {
|
|
1419
|
+
...deps,
|
|
1420
|
+
basePath: this.basePath,
|
|
1421
|
+
apiKey: this.apiKey
|
|
1422
|
+
}).catch((err) => this._handleFlowError(err));
|
|
1423
|
+
} else if (options.provider === "email") {
|
|
1424
|
+
const { email } = options;
|
|
1425
|
+
initEmailSession(deps).then(() => {
|
|
1426
|
+
if (this._authState.step === "entering_email") {
|
|
1427
|
+
return sendEmailCode(email, this._authState.clientSessionId, deps);
|
|
1428
|
+
}
|
|
1429
|
+
}).catch((err) => this._handleFlowError(err));
|
|
1430
|
+
}
|
|
1364
1431
|
} else if (options.provider === "wallet") {
|
|
1365
1432
|
this.loginWallet(options.type);
|
|
1366
1433
|
}
|
|
@@ -1403,19 +1470,6 @@ var PollarClient = class {
|
|
|
1403
1470
|
(err) => this._handleFlowError(err)
|
|
1404
1471
|
);
|
|
1405
1472
|
}
|
|
1406
|
-
// ─── OAuth flow (single call) ─────────────────────────────────────────────
|
|
1407
|
-
loginOAuth(provider) {
|
|
1408
|
-
if (!isBrowser) {
|
|
1409
|
-
warnServerSide("loginOAuth");
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
const controller = this._newController();
|
|
1413
|
-
loginOAuth(provider, {
|
|
1414
|
-
...this._flowDeps(controller.signal),
|
|
1415
|
-
basePath: this.basePath,
|
|
1416
|
-
apiKey: this.apiKey
|
|
1417
|
-
}).catch((err) => this._handleFlowError(err));
|
|
1418
|
-
}
|
|
1419
1473
|
// ─── Wallet flow (single call) ────────────────────────────────────────────
|
|
1420
1474
|
loginWallet(type) {
|
|
1421
1475
|
if (!isBrowser) {
|
|
@@ -1440,24 +1494,85 @@ var PollarClient = class {
|
|
|
1440
1494
|
console.info("[PollarClient] Logout requested");
|
|
1441
1495
|
this._clearSession();
|
|
1442
1496
|
}
|
|
1443
|
-
// ───
|
|
1444
|
-
getApi() {
|
|
1445
|
-
return this._api;
|
|
1446
|
-
}
|
|
1497
|
+
// ─── Network ──────────────────────────────────────────────────────────────
|
|
1447
1498
|
getNetwork() {
|
|
1448
|
-
return this.
|
|
1499
|
+
return this._networkState.step === "connected" ? this._networkState.network : "testnet";
|
|
1500
|
+
}
|
|
1501
|
+
getNetworkState() {
|
|
1502
|
+
return this._networkState;
|
|
1503
|
+
}
|
|
1504
|
+
setNetwork(network) {
|
|
1505
|
+
this._setNetworkState({ step: "connected", network });
|
|
1506
|
+
}
|
|
1507
|
+
onNetworkStateChange(cb) {
|
|
1508
|
+
this._networkStateListeners.add(cb);
|
|
1509
|
+
cb(this._networkState);
|
|
1510
|
+
return () => this._networkStateListeners.delete(cb);
|
|
1511
|
+
}
|
|
1512
|
+
// ─── Transaction state ────────────────────────────────────────────────────
|
|
1513
|
+
getTransactionState() {
|
|
1514
|
+
return this._transactionState;
|
|
1515
|
+
}
|
|
1516
|
+
onTransactionStateChange(cb) {
|
|
1517
|
+
this._transactionStateListeners.add(cb);
|
|
1518
|
+
if (this._transactionState) cb(this._transactionState);
|
|
1519
|
+
return () => this._transactionStateListeners.delete(cb);
|
|
1520
|
+
}
|
|
1521
|
+
// ─── Tx history ──────────────────────────────────────────────────────────
|
|
1522
|
+
getTxHistoryState() {
|
|
1523
|
+
return this._txHistoryState;
|
|
1524
|
+
}
|
|
1525
|
+
onTxHistoryStateChange(cb) {
|
|
1526
|
+
this._txHistoryStateListeners.add(cb);
|
|
1527
|
+
cb(this._txHistoryState);
|
|
1528
|
+
return () => this._txHistoryStateListeners.delete(cb);
|
|
1529
|
+
}
|
|
1530
|
+
async fetchTxHistory(params = {}) {
|
|
1531
|
+
this._setTxHistoryState({ step: "loading", params });
|
|
1532
|
+
try {
|
|
1533
|
+
const { data, error } = await this._api.GET("/tx/history", { params: { query: params } });
|
|
1534
|
+
if (!error && data?.success && data.content) {
|
|
1535
|
+
this._setTxHistoryState({ step: "loaded", params, data: data.content });
|
|
1536
|
+
} else {
|
|
1537
|
+
const message = error?.message ?? "Failed to load history";
|
|
1538
|
+
this._setTxHistoryState({ step: "error", params, message });
|
|
1539
|
+
}
|
|
1540
|
+
} catch {
|
|
1541
|
+
this._setTxHistoryState({ step: "error", params, message: "Failed to load history" });
|
|
1542
|
+
}
|
|
1449
1543
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1544
|
+
// ─── Wallet balance ───────────────────────────────────────────────────────
|
|
1545
|
+
getWalletBalanceState() {
|
|
1546
|
+
return this._walletBalanceState;
|
|
1547
|
+
}
|
|
1548
|
+
onWalletBalanceStateChange(cb) {
|
|
1549
|
+
this._walletBalanceStateListeners.add(cb);
|
|
1550
|
+
cb(this._walletBalanceState);
|
|
1551
|
+
return () => this._walletBalanceStateListeners.delete(cb);
|
|
1552
|
+
}
|
|
1553
|
+
async refreshBalance(publicKey) {
|
|
1554
|
+
const pk = publicKey ?? this._session?.wallet?.publicKey;
|
|
1555
|
+
if (!pk) {
|
|
1556
|
+
this._setWalletBalanceState({ step: "error", message: "No wallet connected" });
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
this._setWalletBalanceState({ step: "loading" });
|
|
1560
|
+
try {
|
|
1561
|
+
const network = this.getNetwork();
|
|
1562
|
+
const { data, error } = await this._api.GET("/wallet/balance", { params: { query: { publicKey: pk, network } } });
|
|
1563
|
+
if (!error && data?.success && data.content) {
|
|
1564
|
+
this._setWalletBalanceState({ step: "loaded", data: data.content });
|
|
1565
|
+
} else {
|
|
1566
|
+
this._setWalletBalanceState({ step: "error", message: "Failed to load balance" });
|
|
1567
|
+
}
|
|
1568
|
+
} catch {
|
|
1569
|
+
this._setWalletBalanceState({ step: "error", message: "Failed to load balance" });
|
|
1454
1570
|
}
|
|
1455
|
-
return () => this._stateListeners.delete(cb);
|
|
1456
1571
|
}
|
|
1457
1572
|
// ─── Transactions ─────────────────────────────────────────────────────────
|
|
1458
1573
|
async buildTx(operation, params, options) {
|
|
1459
1574
|
if (!this._session?.wallet?.publicKey) {
|
|
1460
|
-
this.
|
|
1575
|
+
this._setTransactionState({ step: "error", details: "No wallet connected" });
|
|
1461
1576
|
return;
|
|
1462
1577
|
}
|
|
1463
1578
|
const body = {
|
|
@@ -1465,42 +1580,114 @@ var PollarClient = class {
|
|
|
1465
1580
|
publicKey: this._session.wallet.publicKey,
|
|
1466
1581
|
operation,
|
|
1467
1582
|
params,
|
|
1468
|
-
options: options
|
|
1583
|
+
options: options ?? {}
|
|
1469
1584
|
};
|
|
1470
1585
|
try {
|
|
1471
|
-
this.
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
this._emitState("transaction", STATE_VAR_CODES.transaction.BUILD_TRANSACTION_ERROR, "error", StateStatus.ERROR, {
|
|
1482
|
-
error
|
|
1483
|
-
});
|
|
1586
|
+
this._setTransactionState({ step: "building" });
|
|
1587
|
+
const { data, error } = await this._api.POST("/tx/build", { body });
|
|
1588
|
+
if (!error && data?.success && data.content) {
|
|
1589
|
+
this._setTransactionState({ step: "built", buildData: data.content });
|
|
1590
|
+
} else {
|
|
1591
|
+
const details = error?.details;
|
|
1592
|
+
this._setTransactionState({ step: "error", ...details && { details } });
|
|
1593
|
+
}
|
|
1594
|
+
} catch {
|
|
1595
|
+
this._setTransactionState({ step: "error" });
|
|
1484
1596
|
}
|
|
1485
1597
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1598
|
+
getWalletType() {
|
|
1599
|
+
return this._walletAdapter?.type ?? null;
|
|
1600
|
+
}
|
|
1601
|
+
async signAndSubmitTx(unsignedXdr) {
|
|
1602
|
+
const state = this._transactionState;
|
|
1603
|
+
const buildData = state?.step === "built" ? state.buildData : state?.step === "error" ? state.buildData : void 0;
|
|
1604
|
+
const isBuiltFlow = !!buildData;
|
|
1605
|
+
const stateExtra = buildData ? { buildData } : { external: true };
|
|
1606
|
+
this._setTransactionState({ step: "signing", ...stateExtra });
|
|
1607
|
+
const accountToSign = isBuiltFlow ? this._session?.wallet?.publicKey : this._session?.data?.providers?.wallet?.address ?? this._session?.wallet?.publicKey;
|
|
1608
|
+
if (this._walletAdapter) {
|
|
1609
|
+
try {
|
|
1610
|
+
const signOpts = accountToSign ? { networkPassphrase: this._networkPassphrase(), accountToSign } : { networkPassphrase: this._networkPassphrase() };
|
|
1611
|
+
const { signedTxXdr } = await this._walletAdapter.signTransaction(unsignedXdr, signOpts);
|
|
1612
|
+
const stellarClient = new StellarClient(this.getNetwork());
|
|
1613
|
+
const result = await stellarClient.submitTransaction(signedTxXdr);
|
|
1614
|
+
if (result.success) {
|
|
1615
|
+
this._setTransactionState({ step: "success", ...stateExtra, hash: result.hash });
|
|
1616
|
+
} else {
|
|
1617
|
+
this._setTransactionState({ step: "error", ...stateExtra, details: result.errorCode });
|
|
1618
|
+
}
|
|
1619
|
+
} catch {
|
|
1620
|
+
this._setTransactionState({ step: "error", ...stateExtra });
|
|
1621
|
+
}
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
const body = {
|
|
1625
|
+
network: this.getNetwork(),
|
|
1626
|
+
publicKey: this._session?.wallet?.publicKey ?? "",
|
|
1627
|
+
unsignedXdr
|
|
1628
|
+
};
|
|
1488
1629
|
try {
|
|
1489
|
-
this.
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
{
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
);
|
|
1498
|
-
} catch (error) {
|
|
1499
|
-
this._emitState("transaction", STATE_VAR_CODES.transaction.SIGN_SEND_TRANSACTION_ERROR, "error", StateStatus.ERROR, {
|
|
1500
|
-
error
|
|
1501
|
-
});
|
|
1630
|
+
const { data, error } = await this._api.POST("/tx/sign-and-send", { body });
|
|
1631
|
+
if (!error && data?.success && data.content?.hash) {
|
|
1632
|
+
this._setTransactionState({ step: "success", ...stateExtra, hash: data.content.hash });
|
|
1633
|
+
} else {
|
|
1634
|
+
const details = error?.details;
|
|
1635
|
+
this._setTransactionState({ step: "error", ...stateExtra, ...details && { details } });
|
|
1636
|
+
}
|
|
1637
|
+
} catch {
|
|
1638
|
+
this._setTransactionState({ step: "error", ...stateExtra });
|
|
1502
1639
|
}
|
|
1503
1640
|
}
|
|
1641
|
+
// ─── App config ───────────────────────────────────────────────────────────
|
|
1642
|
+
async getAppConfig() {
|
|
1643
|
+
try {
|
|
1644
|
+
const { data, error } = await this._api.GET("/applications/config");
|
|
1645
|
+
if (!data || error) return null;
|
|
1646
|
+
return data.content;
|
|
1647
|
+
} catch {
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
// ─── KYC ──────────────────────────────────────────────────────────────────
|
|
1652
|
+
getKycStatus(providerId) {
|
|
1653
|
+
return getKycStatus(this._api, providerId);
|
|
1654
|
+
}
|
|
1655
|
+
getKycProviders(country) {
|
|
1656
|
+
return getKycProviders(this._api, country);
|
|
1657
|
+
}
|
|
1658
|
+
startKyc(body) {
|
|
1659
|
+
return startKyc(this._api, body);
|
|
1660
|
+
}
|
|
1661
|
+
resolveKyc(providerId, level) {
|
|
1662
|
+
return resolveKyc(this._api, providerId, level);
|
|
1663
|
+
}
|
|
1664
|
+
pollKycStatus(providerId, opts) {
|
|
1665
|
+
return pollKycStatus(this._api, providerId, opts);
|
|
1666
|
+
}
|
|
1667
|
+
// ─── Ramps ────────────────────────────────────────────────────────────────
|
|
1668
|
+
getRampsQuote(query) {
|
|
1669
|
+
return getRampsQuote(this._api, query);
|
|
1670
|
+
}
|
|
1671
|
+
createOnRamp(body) {
|
|
1672
|
+
return createOnRamp(this._api, body);
|
|
1673
|
+
}
|
|
1674
|
+
createOffRamp(body) {
|
|
1675
|
+
return createOffRamp(this._api, body);
|
|
1676
|
+
}
|
|
1677
|
+
getRampTransaction(txId) {
|
|
1678
|
+
return getRampTransaction(this._api, txId);
|
|
1679
|
+
}
|
|
1680
|
+
pollRampTransaction(txId, opts) {
|
|
1681
|
+
return pollRampTransaction(this._api, txId, opts);
|
|
1682
|
+
}
|
|
1683
|
+
_setTxHistoryState(next) {
|
|
1684
|
+
this._txHistoryState = next;
|
|
1685
|
+
for (const cb of this._txHistoryStateListeners) cb(next);
|
|
1686
|
+
}
|
|
1687
|
+
_setWalletBalanceState(next) {
|
|
1688
|
+
this._walletBalanceState = next;
|
|
1689
|
+
for (const cb of this._walletBalanceStateListeners) cb(next);
|
|
1690
|
+
}
|
|
1504
1691
|
// ─── Private ──────────────────────────────────────────────────────────────
|
|
1505
1692
|
/** Creates a new AbortController, cancelling any existing flow first. */
|
|
1506
1693
|
_newController() {
|
|
@@ -1515,7 +1702,11 @@ var PollarClient = class {
|
|
|
1515
1702
|
signal,
|
|
1516
1703
|
setAuthState: this._setAuthState.bind(this),
|
|
1517
1704
|
storeSession: this._storeSession.bind(this),
|
|
1518
|
-
clearSession: this._clearSession.bind(this)
|
|
1705
|
+
clearSession: this._clearSession.bind(this),
|
|
1706
|
+
storeWalletAdapter: (adapter, type) => {
|
|
1707
|
+
this._walletAdapter = adapter;
|
|
1708
|
+
writeWalletType(type);
|
|
1709
|
+
}
|
|
1519
1710
|
};
|
|
1520
1711
|
}
|
|
1521
1712
|
_handleFlowError(error) {
|
|
@@ -1532,10 +1723,18 @@ var PollarClient = class {
|
|
|
1532
1723
|
errorCode: AUTH_ERROR_CODES.UNEXPECTED_ERROR
|
|
1533
1724
|
});
|
|
1534
1725
|
}
|
|
1535
|
-
|
|
1726
|
+
_restoreSession() {
|
|
1536
1727
|
this._session = readStorage();
|
|
1537
1728
|
if (this._session) {
|
|
1538
1729
|
this._authState = { step: "authenticated", session: this._session };
|
|
1730
|
+
if (this._session.data?.providers?.wallet?.address) {
|
|
1731
|
+
const storedType = readWalletType();
|
|
1732
|
+
if (storedType === "freighter" /* FREIGHTER */) {
|
|
1733
|
+
this._walletAdapter = new FreighterAdapter();
|
|
1734
|
+
} else if (storedType === "albedo" /* ALBEDO */) {
|
|
1735
|
+
this._walletAdapter = new AlbedoAdapter();
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1539
1738
|
console.info("[PollarClient] Session restored from storage");
|
|
1540
1739
|
} else {
|
|
1541
1740
|
console.info("[PollarClient] No session in storage");
|
|
@@ -1550,59 +1749,32 @@ var PollarClient = class {
|
|
|
1550
1749
|
_clearSession() {
|
|
1551
1750
|
console.info("[PollarClient] Session cleared");
|
|
1552
1751
|
this._session = null;
|
|
1752
|
+
this._walletAdapter = null;
|
|
1553
1753
|
removeStorage();
|
|
1554
|
-
this.
|
|
1754
|
+
this._transactionState = null;
|
|
1555
1755
|
this._setAuthState({ step: "idle" });
|
|
1556
1756
|
}
|
|
1757
|
+
_networkPassphrase() {
|
|
1758
|
+
return this.getNetwork() === "mainnet" ? "Public Global Stellar Network ; September 2015" : "Test SDF Network ; September 2015";
|
|
1759
|
+
}
|
|
1760
|
+
_setNetworkState(next) {
|
|
1761
|
+
this._networkState = next;
|
|
1762
|
+
const label = next.step === "connected" ? next.network : next.step;
|
|
1763
|
+
console.info(`[PollarClient] network:${label}`);
|
|
1764
|
+
for (const cb of this._networkStateListeners) cb(next);
|
|
1765
|
+
}
|
|
1557
1766
|
_setAuthState(next) {
|
|
1558
1767
|
this._authState = next;
|
|
1559
1768
|
console.info(`[PollarClient] auth:${next.step}`);
|
|
1560
1769
|
for (const cb of this._authStateListeners) cb(next);
|
|
1561
1770
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
for (const cb of this._stateListeners) cb(stateEntry);
|
|
1567
|
-
}
|
|
1568
|
-
};
|
|
1569
|
-
|
|
1570
|
-
// src/stellar/StellarClient.ts
|
|
1571
|
-
var HORIZON_URLS = {
|
|
1572
|
-
mainnet: "https://horizon.stellar.org",
|
|
1573
|
-
testnet: "https://horizon-testnet.stellar.org"
|
|
1574
|
-
};
|
|
1575
|
-
var StellarClient = class {
|
|
1576
|
-
constructor(config) {
|
|
1577
|
-
this.horizonUrl = typeof config === "string" ? HORIZON_URLS[config] : config.horizonUrl;
|
|
1578
|
-
}
|
|
1579
|
-
async getBalances(publicKey) {
|
|
1580
|
-
try {
|
|
1581
|
-
const response = await fetch(`${this.horizonUrl}/accounts/${publicKey}`);
|
|
1582
|
-
if (!response.ok) {
|
|
1583
|
-
if (response.status === 404) {
|
|
1584
|
-
console.warn(`[StellarClient] Account not found: ${publicKey}`);
|
|
1585
|
-
return { success: false, errorCode: "ACCOUNT_NOT_FOUND", balances: [] };
|
|
1586
|
-
}
|
|
1587
|
-
console.warn(`[StellarClient] Horizon API error: ${response.status}`);
|
|
1588
|
-
return { success: false, errorCode: "HORIZON_ERROR", balances: [] };
|
|
1589
|
-
}
|
|
1590
|
-
const data = await response.json();
|
|
1591
|
-
return {
|
|
1592
|
-
success: true,
|
|
1593
|
-
balances: data.balances.filter((b) => b.asset_type !== "liquidity_pool_shares").map((b) => ({
|
|
1594
|
-
asset: b.asset_type === "native" ? "XLM" : b.asset_code ?? "",
|
|
1595
|
-
balance: b.balance,
|
|
1596
|
-
...b.asset_type !== "native" && { assetIssuer: b.asset_issuer }
|
|
1597
|
-
}))
|
|
1598
|
-
};
|
|
1599
|
-
} catch (error) {
|
|
1600
|
-
console.warn("[StellarClient] Network error fetching balances", error);
|
|
1601
|
-
return { success: false, errorCode: "NETWORK_ERROR", balances: [] };
|
|
1602
|
-
}
|
|
1771
|
+
_setTransactionState(next) {
|
|
1772
|
+
this._transactionState = next;
|
|
1773
|
+
console.info(`[PollarClient] transaction:${next.step}`);
|
|
1774
|
+
for (const cb of this._transactionStateListeners) cb(next);
|
|
1603
1775
|
}
|
|
1604
1776
|
};
|
|
1605
1777
|
|
|
1606
|
-
export { AUTH_ERROR_CODES, AlbedoAdapter, FreighterAdapter, PollarClient,
|
|
1778
|
+
export { AUTH_ERROR_CODES, AlbedoAdapter, FreighterAdapter, PollarClient, StellarClient, WalletType, createOffRamp, createOnRamp, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, pollKycStatus, pollRampTransaction, resolveKyc, startKyc };
|
|
1607
1779
|
//# sourceMappingURL=index.mjs.map
|
|
1608
1780
|
//# sourceMappingURL=index.mjs.map
|