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