@stellar/typescript-wallet-sdk 5.0.0-beta.1788566740000 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -49,7 +49,7 @@ export declare class Watcher {
49
49
  watchAllTransactions({ authToken, assetCode, onMessage, onError, watchlist, timeout, isRetry, lang, kind, noOlderThan, }: WatchTransactionsParams): WatcherResponse;
50
50
  /**
51
51
  * Watch a transaction until it stops pending. Takes three callbacks:
52
- * * onMessage - When the transaction comes back as pending_ or incomplete.
52
+ * * onMessage - When the transaction comes back as pending_, incomplete, or on_hold.
53
53
  * * onSuccess - When the transaction comes back as completed / refunded / expired.
54
54
  * * onError - When there's a runtime error, or the transaction comes back as
55
55
  * no_market / too_small / too_large / error.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar/typescript-wallet-sdk",
3
- "version": "5.0.0-beta.1788566740000",
3
+ "version": "5.0.0",
4
4
  "engines": {
5
5
  "node": ">=22.12.0"
6
6
  },
@@ -77,4 +77,4 @@
77
77
  "example:sep24": "ts-node examples/sep24/sep24.ts",
78
78
  "example:sep12": "ts-node examples/sep12/sep12.ts"
79
79
  }
80
- }
80
+ }
@@ -168,7 +168,8 @@ export class Watcher {
168
168
  const newTransactions = transactions.filter((transaction) => {
169
169
  const isInProgress =
170
170
  transaction.status.indexOf("pending") === 0 ||
171
- transaction.status === TransactionStatus.incomplete;
171
+ transaction.status === TransactionStatus.incomplete ||
172
+ transaction.status === TransactionStatus.on_hold;
172
173
  const registeredTransaction =
173
174
  this._transactionsRegistry[assetCode][transaction.id];
174
175
 
@@ -275,7 +276,7 @@ export class Watcher {
275
276
 
276
277
  /**
277
278
  * Watch a transaction until it stops pending. Takes three callbacks:
278
- * * onMessage - When the transaction comes back as pending_ or incomplete.
279
+ * * onMessage - When the transaction comes back as pending_, incomplete, or on_hold.
279
280
  * * onSuccess - When the transaction comes back as completed / refunded / expired.
280
281
  * * onError - When there's a runtime error, or the transaction comes back as
281
282
  * no_market / too_small / too_large / error.
@@ -368,7 +369,8 @@ export class Watcher {
368
369
 
369
370
  if (
370
371
  transaction.status.indexOf("pending") === 0 ||
371
- transaction.status === TransactionStatus.incomplete
372
+ transaction.status === TransactionStatus.incomplete ||
373
+ transaction.status === TransactionStatus.on_hold
372
374
  ) {
373
375
  if (this._oneTransactionWatcher[assetCode][id]) {
374
376
  clearTimeout(this._oneTransactionWatcher[assetCode][id]);
@@ -930,6 +930,83 @@ describe("Anchor", () => {
930
930
  // stops watching
931
931
  stop();
932
932
  });
933
+
934
+ test("on_hold is transient: not ignored on first poll", async () => {
935
+ const onMessage = sinon.spy();
936
+ const onError = sinon.spy();
937
+
938
+ const onHoldTransaction = makeTransaction(0, TransactionStatus.on_hold);
939
+ const completedTransaction = {
940
+ ...onHoldTransaction,
941
+ status: TransactionStatus.completed,
942
+ };
943
+
944
+ // first poll returns on_hold, second returns completed
945
+ jest
946
+ .spyOn(Sep24.prototype, "getTransactionsForAsset")
947
+ .mockResolvedValueOnce([onHoldTransaction])
948
+ .mockResolvedValueOnce([completedTransaction]);
949
+
950
+ const { stop } = watcher.watchAllTransactions({
951
+ authToken,
952
+ assetCode: "SRT",
953
+ lang: "en-US",
954
+ onMessage,
955
+ onError,
956
+ timeout: 1,
957
+ });
958
+
959
+ await sleep(1);
960
+
961
+ // on_hold must be treated as in-progress, not silently ignored
962
+ expect(onError.callCount).toBe(0);
963
+ expect(onMessage.callCount).toBe(1);
964
+
965
+ clock.next();
966
+ await sleep(1);
967
+
968
+ // now completed, reported as a status change
969
+ expect(onError.callCount).toBe(0);
970
+ expect(onMessage.callCount).toBe(2);
971
+
972
+ stop();
973
+ });
974
+
975
+ test("on_hold reported once when status unchanged across polls", async () => {
976
+ const onMessage = sinon.spy();
977
+ const onError = sinon.spy();
978
+
979
+ const onHoldTransaction = makeTransaction(0, TransactionStatus.on_hold);
980
+
981
+ // two consecutive polls return the same on_hold status
982
+ jest
983
+ .spyOn(Sep24.prototype, "getTransactionsForAsset")
984
+ .mockResolvedValueOnce([onHoldTransaction])
985
+ .mockResolvedValueOnce([onHoldTransaction]);
986
+
987
+ const { stop } = watcher.watchAllTransactions({
988
+ authToken,
989
+ assetCode: "SRT",
990
+ lang: "en-US",
991
+ onMessage,
992
+ onError,
993
+ timeout: 1,
994
+ });
995
+
996
+ await sleep(1);
997
+
998
+ // first poll: reported
999
+ expect(onMessage.callCount).toBe(1);
1000
+
1001
+ clock.next();
1002
+ await sleep(1);
1003
+
1004
+ // second poll: same status, not reported again
1005
+ expect(onMessage.callCount).toBe(1);
1006
+ expect(onError.callCount).toBe(0);
1007
+
1008
+ stop();
1009
+ });
933
1010
  });
934
1011
 
935
1012
  describe("watchOneTransaction", () => {
@@ -946,6 +1023,51 @@ describe("Anchor", () => {
946
1023
  clock.restore();
947
1024
  });
948
1025
 
1026
+ test("on_hold is transient: keeps watching until completed", async () => {
1027
+ const onMessage = sinon.spy();
1028
+ const onSuccess = sinon.spy();
1029
+ const onError = sinon.spy();
1030
+
1031
+ const onHoldTransaction = makeTransaction(0, TransactionStatus.on_hold);
1032
+ const completedTransaction = {
1033
+ ...onHoldTransaction,
1034
+ status: TransactionStatus.completed,
1035
+ };
1036
+
1037
+ // first poll returns on_hold, the next returns completed
1038
+ jest
1039
+ .spyOn(Sep24.prototype, "getTransactionBy")
1040
+ .mockResolvedValueOnce(onHoldTransaction)
1041
+ .mockResolvedValueOnce(completedTransaction);
1042
+
1043
+ const { stop } = watcher.watchOneTransaction({
1044
+ authToken,
1045
+ assetCode: "SRT",
1046
+ id: onHoldTransaction.id,
1047
+ onMessage,
1048
+ onSuccess,
1049
+ onError,
1050
+ timeout: 1,
1051
+ lang: "en-US",
1052
+ });
1053
+
1054
+ // first poll: on_hold is transient, so it must keep watching, not error out
1055
+ await sleep(1);
1056
+ expect(onError.callCount).toBe(0);
1057
+ expect(onSuccess.callCount).toBe(0);
1058
+ expect(onMessage.callCount).toBe(1);
1059
+
1060
+ clock.next();
1061
+ await sleep(1);
1062
+
1063
+ // second poll: now completed, so onSuccess fires and onError never does
1064
+ expect(onError.callCount).toBe(0);
1065
+ expect(onSuccess.callCount).toBe(1);
1066
+ expect(onMessage.callCount).toBe(1);
1067
+
1068
+ stop();
1069
+ });
1070
+
949
1071
  test("One completed / refunded / expired successes", async () => {
950
1072
  const onMessage = sinon.spy(() => {
951
1073
  expect(onMessage.callCount).toBe(0);