@swype-org/react-sdk 0.1.45 → 0.1.46

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.cjs CHANGED
@@ -657,6 +657,59 @@ function normalizeSignature(sig) {
657
657
  );
658
658
  }
659
659
 
660
+ // src/passkey-delegation.ts
661
+ function isInCrossOriginIframe() {
662
+ if (typeof window === "undefined") return false;
663
+ if (window.parent === window) return false;
664
+ try {
665
+ void window.parent.location.origin;
666
+ return false;
667
+ } catch {
668
+ return true;
669
+ }
670
+ }
671
+ var delegationCounter = 0;
672
+ function delegatePasskeyCreate(options) {
673
+ return new Promise((resolve, reject) => {
674
+ const id = `pc-${++delegationCounter}-${Date.now()}`;
675
+ const handler = (event) => {
676
+ const data = event.data;
677
+ if (!data || typeof data !== "object") return;
678
+ if (data.type !== "swype:passkey-create-response" || data.id !== id) return;
679
+ window.removeEventListener("message", handler);
680
+ if (data.error) {
681
+ reject(new Error(data.error));
682
+ } else if (data.result) {
683
+ resolve(data.result);
684
+ } else {
685
+ reject(new Error("Invalid passkey create response."));
686
+ }
687
+ };
688
+ window.addEventListener("message", handler);
689
+ window.parent.postMessage({ type: "swype:passkey-create-request", id, options }, "*");
690
+ });
691
+ }
692
+ function delegatePasskeyGet(options) {
693
+ return new Promise((resolve, reject) => {
694
+ const id = `pg-${++delegationCounter}-${Date.now()}`;
695
+ const handler = (event) => {
696
+ const data = event.data;
697
+ if (!data || typeof data !== "object") return;
698
+ if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
699
+ window.removeEventListener("message", handler);
700
+ if (data.error) {
701
+ reject(new Error(data.error));
702
+ } else if (data.result) {
703
+ resolve(data.result);
704
+ } else {
705
+ reject(new Error("Invalid passkey get response."));
706
+ }
707
+ };
708
+ window.addEventListener("message", handler);
709
+ window.parent.postMessage({ type: "swype:passkey-get-request", id, options }, "*");
710
+ });
711
+ }
712
+
660
713
  // src/hooks.ts
661
714
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
662
715
  var WALLET_CLIENT_POLL_MS = 200;
@@ -778,6 +831,26 @@ async function createPasskeyCredential(params) {
778
831
  const challenge = new Uint8Array(32);
779
832
  crypto.getRandomValues(challenge);
780
833
  const rpId = resolvePasskeyRpId();
834
+ if (isInCrossOriginIframe()) {
835
+ return delegatePasskeyCreate({
836
+ challenge: toBase64(challenge),
837
+ rpId,
838
+ rpName: "Swype",
839
+ userId: toBase64(new TextEncoder().encode(params.userId)),
840
+ userName: params.displayName,
841
+ userDisplayName: params.displayName,
842
+ pubKeyCredParams: [
843
+ { alg: -7, type: "public-key" },
844
+ { alg: -257, type: "public-key" }
845
+ ],
846
+ authenticatorSelection: {
847
+ authenticatorAttachment: "platform",
848
+ residentKey: "preferred",
849
+ userVerification: "required"
850
+ },
851
+ timeout: 6e4
852
+ });
853
+ }
781
854
  await waitForDocumentFocus();
782
855
  const credential = await navigator.credentials.create({
783
856
  publicKey: {
@@ -821,6 +894,16 @@ async function findDevicePasskey(credentialIds) {
821
894
  try {
822
895
  const challenge = new Uint8Array(32);
823
896
  crypto.getRandomValues(challenge);
897
+ if (isInCrossOriginIframe()) {
898
+ const result = await delegatePasskeyGet({
899
+ challenge: toBase64(challenge),
900
+ rpId: resolvePasskeyRpId(),
901
+ allowCredentials: credentialIds.map((id) => ({ type: "public-key", id })),
902
+ userVerification: "discouraged",
903
+ timeout: 3e4
904
+ });
905
+ return result.credentialId;
906
+ }
824
907
  await waitForDocumentFocus();
825
908
  const assertion = await navigator.credentials.get({
826
909
  publicKey: {
@@ -1318,31 +1401,49 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1318
1401
  throw new Error("Timed out waiting for sign payload. Please try again.");
1319
1402
  }
1320
1403
  const hashBytes = hexToBytes(payload.userOpHash);
1321
- const allowCredentials = payload.passkeyCredentialId ? [{
1322
- type: "public-key",
1323
- id: base64ToBytes(payload.passkeyCredentialId)
1324
- }] : void 0;
1325
- await waitForDocumentFocus();
1326
- const assertion = await navigator.credentials.get({
1327
- publicKey: {
1328
- challenge: hashBytes,
1404
+ let signedUserOp;
1405
+ if (isInCrossOriginIframe()) {
1406
+ const delegatedResult = await delegatePasskeyGet({
1407
+ challenge: toBase64(hashBytes),
1329
1408
  rpId: resolvePasskeyRpId(),
1330
- allowCredentials,
1409
+ allowCredentials: payload.passkeyCredentialId ? [{ type: "public-key", id: payload.passkeyCredentialId }] : void 0,
1331
1410
  userVerification: "required",
1332
1411
  timeout: 6e4
1412
+ });
1413
+ signedUserOp = {
1414
+ ...payload.userOp,
1415
+ credentialId: delegatedResult.credentialId,
1416
+ signature: delegatedResult.signature,
1417
+ authenticatorData: delegatedResult.authenticatorData,
1418
+ clientDataJSON: delegatedResult.clientDataJSON
1419
+ };
1420
+ } else {
1421
+ const allowCredentials = payload.passkeyCredentialId ? [{
1422
+ type: "public-key",
1423
+ id: base64ToBytes(payload.passkeyCredentialId)
1424
+ }] : void 0;
1425
+ await waitForDocumentFocus();
1426
+ const assertion = await navigator.credentials.get({
1427
+ publicKey: {
1428
+ challenge: hashBytes,
1429
+ rpId: resolvePasskeyRpId(),
1430
+ allowCredentials,
1431
+ userVerification: "required",
1432
+ timeout: 6e4
1433
+ }
1434
+ });
1435
+ if (!assertion) {
1436
+ throw new Error("Passkey authentication was cancelled.");
1333
1437
  }
1334
- });
1335
- if (!assertion) {
1336
- throw new Error("Passkey authentication was cancelled.");
1438
+ const response = assertion.response;
1439
+ signedUserOp = {
1440
+ ...payload.userOp,
1441
+ credentialId: toBase64(assertion.rawId),
1442
+ signature: toBase64(response.signature),
1443
+ authenticatorData: toBase64(response.authenticatorData),
1444
+ clientDataJSON: toBase64(response.clientDataJSON)
1445
+ };
1337
1446
  }
1338
- const response = assertion.response;
1339
- const signedUserOp = {
1340
- ...payload.userOp,
1341
- credentialId: toBase64(assertion.rawId),
1342
- signature: toBase64(response.signature),
1343
- authenticatorData: toBase64(response.authenticatorData),
1344
- clientDataJSON: toBase64(response.clientDataJSON)
1345
- };
1346
1447
  return await signTransfer(
1347
1448
  apiBaseUrl,
1348
1449
  token ?? "",
@@ -3872,6 +3973,7 @@ function SwypePayment({
3872
3973
  const [mobileFlow, setMobileFlow] = react.useState(false);
3873
3974
  const pollingTransferIdRef = react.useRef(null);
3874
3975
  const mobileSigningTransferIdRef = react.useRef(null);
3976
+ const mobileSetupFlowRef = react.useRef(false);
3875
3977
  const processingStartedAtRef = react.useRef(null);
3876
3978
  const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
3877
3979
  const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
@@ -3882,7 +3984,7 @@ function SwypePayment({
3882
3984
  const transferSigning = useTransferSigning();
3883
3985
  const sourceType = connectingNewAccount ? "providerId" : selectedWalletId ? "walletId" : selectedAccountId ? "accountId" : "providerId";
3884
3986
  const sourceId = connectingNewAccount ? selectedProviderId ?? "" : selectedWalletId ? selectedWalletId : selectedAccountId ? selectedAccountId : selectedProviderId ?? "";
3885
- react.useCallback(async () => {
3987
+ const reloadAccounts = react.useCallback(async () => {
3886
3988
  const token = await getAccessToken();
3887
3989
  if (!token || !activeCredentialId) return;
3888
3990
  const [accts, prov] = await Promise.all([
@@ -4113,6 +4215,16 @@ function SwypePayment({
4113
4215
  if (!mobileFlow) return;
4114
4216
  const polledTransfer = polling.transfer;
4115
4217
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
4218
+ if (mobileSetupFlowRef.current) {
4219
+ mobileSetupFlowRef.current = false;
4220
+ setMobileFlow(false);
4221
+ polling.stopPolling();
4222
+ setTransfer(polledTransfer);
4223
+ reloadAccounts().catch(() => {
4224
+ });
4225
+ setStep("deposit");
4226
+ return;
4227
+ }
4116
4228
  if (transferSigning.signing) return;
4117
4229
  if (mobileSigningTransferIdRef.current === polledTransfer.id) return;
4118
4230
  mobileSigningTransferIdRef.current = polledTransfer.id;
@@ -4128,7 +4240,7 @@ function SwypePayment({
4128
4240
  }
4129
4241
  };
4130
4242
  void sign();
4131
- }, [mobileFlow, polling.transfer, transferSigning, onError]);
4243
+ }, [mobileFlow, polling.transfer, polling.stopPolling, transferSigning, onError, reloadAccounts]);
4132
4244
  react.useEffect(() => {
4133
4245
  if (!mobileFlow) return;
4134
4246
  const transferIdToResume = pollingTransferIdRef.current ?? transfer?.id;
@@ -4230,13 +4342,13 @@ function SwypePayment({
4230
4342
  }
4231
4343
  return count;
4232
4344
  }, [accounts]);
4233
- const handlePay = react.useCallback(async (depositAmount2) => {
4345
+ const handlePay = react.useCallback(async (depositAmount2, sourceOverrides) => {
4234
4346
  const parsedAmount = depositAmount2;
4235
4347
  if (isNaN(parsedAmount) || parsedAmount < MIN_SEND_AMOUNT_USD) {
4236
4348
  setError(`Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.`);
4237
4349
  return;
4238
4350
  }
4239
- if (!sourceId) {
4351
+ if (!sourceOverrides?.sourceId && !sourceId) {
4240
4352
  setError("No account or provider selected.");
4241
4353
  return;
4242
4354
  }
@@ -4251,10 +4363,16 @@ function SwypePayment({
4251
4363
  setCreatingTransfer(true);
4252
4364
  setMobileFlow(false);
4253
4365
  try {
4366
+ if (transfer?.status === "AUTHORIZED") {
4367
+ const signedTransfer2 = await transferSigning.signTransfer(transfer.id);
4368
+ setTransfer(signedTransfer2);
4369
+ polling.startPolling(transfer.id);
4370
+ return;
4371
+ }
4254
4372
  const token = await getAccessToken();
4255
4373
  if (!token) throw new Error("Not authenticated");
4256
- let effectiveSourceType = sourceType;
4257
- let effectiveSourceId = sourceId;
4374
+ let effectiveSourceType = sourceOverrides?.sourceType ?? sourceType;
4375
+ let effectiveSourceId = sourceOverrides?.sourceId ?? sourceId;
4258
4376
  if (effectiveSourceType === "accountId") {
4259
4377
  const acct = accounts.find((a) => a.id === effectiveSourceId);
4260
4378
  const activeWallet = acct?.wallets.find((w) => w.status === "ACTIVE");
@@ -4332,7 +4450,8 @@ function SwypePayment({
4332
4450
  onError,
4333
4451
  useWalletConnector,
4334
4452
  idempotencyKey,
4335
- merchantAuthorization
4453
+ merchantAuthorization,
4454
+ transfer
4336
4455
  ]);
4337
4456
  const handleRegisterPasskey = react.useCallback(async () => {
4338
4457
  setRegisteringPasskey(true);
@@ -4376,8 +4495,18 @@ function SwypePayment({
4376
4495
  setSelectedProviderId(providerId);
4377
4496
  setSelectedAccountId(null);
4378
4497
  setConnectingNewAccount(true);
4379
- setStep("deposit");
4380
- }, []);
4498
+ const isMobile = !shouldUseWalletConnector({
4499
+ useWalletConnector,
4500
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
4501
+ });
4502
+ if (isMobile) {
4503
+ mobileSetupFlowRef.current = true;
4504
+ const amount2 = depositAmount ?? 5;
4505
+ handlePay(amount2, { sourceType: "providerId", sourceId: providerId });
4506
+ } else {
4507
+ setStep("deposit");
4508
+ }
4509
+ }, [useWalletConnector, depositAmount, handlePay]);
4381
4510
  const handleContinueConnection = react.useCallback(
4382
4511
  (accountId) => {
4383
4512
  const acct = accounts.find((a) => a.id === accountId);