@stellar/typescript-wallet-sdk 1.1.1 → 1.1.3

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.
@@ -54,3 +54,6 @@ export declare class WithdrawalTxNotPendingUserTransferStartError extends Error
54
54
  export declare class WithdrawalTxMissingMemoError extends Error {
55
55
  constructor();
56
56
  }
57
+ export declare class WithdrawalTxMemoError extends Error {
58
+ constructor();
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellar/typescript-wallet-sdk",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },
@@ -40,7 +40,7 @@
40
40
  "jws": "^4.0.0",
41
41
  "lodash": "^4.17.21",
42
42
  "query-string": "^7.1.3",
43
- "stellar-sdk": "^10.4.1",
43
+ "stellar-sdk": "^11.0.0-beta.3",
44
44
  "stream-http": "^3.2.0",
45
45
  "url": "^0.11.0",
46
46
  "util": "^0.12.5",
@@ -146,3 +146,10 @@ export class WithdrawalTxMissingMemoError extends Error {
146
146
  Object.setPrototypeOf(this, WithdrawalTxMissingMemoError.prototype);
147
147
  }
148
148
  }
149
+
150
+ export class WithdrawalTxMemoError extends Error {
151
+ constructor() {
152
+ super(`Error parsing withdrawal transaction memo`);
153
+ Object.setPrototypeOf(this, WithdrawalTxMemoError.prototype);
154
+ }
155
+ }
@@ -14,6 +14,7 @@ import {
14
14
  InsufficientStartingBalanceError,
15
15
  WithdrawalTxMissingMemoError,
16
16
  WithdrawalTxNotPendingUserTransferStartError,
17
+ WithdrawalTxMemoError,
17
18
  } from "../../Exceptions";
18
19
  import { IssuedAssetId, StellarAssetId } from "../../Asset";
19
20
  import { WithdrawTransaction, TransactionStatus } from "../../Types";
@@ -129,9 +130,21 @@ export class TransactionBuilder {
129
130
  throw new WithdrawalTxMissingMemoError();
130
131
  }
131
132
 
132
- return this.setMemo(
133
- new Memo(transaction.withdraw_memo_type, transaction.withdraw_memo),
134
- ).transfer(
133
+ if (transaction.withdraw_memo_type === "hash") {
134
+ try {
135
+ const buffer = Buffer.from(transaction.withdraw_memo, "base64");
136
+ const memo = Memo.hash(buffer.toString("hex"));
137
+ this.setMemo(memo);
138
+ } catch {
139
+ throw new WithdrawalTxMemoError();
140
+ }
141
+ } else {
142
+ this.setMemo(
143
+ new Memo(transaction.withdraw_memo_type, transaction.withdraw_memo),
144
+ );
145
+ }
146
+
147
+ return this.transfer(
135
148
  transaction.withdraw_anchor_account,
136
149
  assetId,
137
150
  transaction.amount_in,
@@ -1,5 +1,3 @@
1
- import isEqual from "lodash/isEqual";
2
-
3
1
  import { Anchor } from "../Anchor";
4
2
  import {
5
3
  AnchorTransaction,
@@ -151,9 +149,9 @@ export class Watcher {
151
149
  return isInProgress;
152
150
  }
153
151
 
154
- // if we've had the transaction before, only report updates
152
+ // if we've had the transaction before, only report updates if status changed
155
153
  if (registeredTransaction) {
156
- return !isEqual(registeredTransaction, transaction);
154
+ return registeredTransaction.status !== transaction.status;
157
155
  }
158
156
 
159
157
  // if it's NOT a registered transaction, and it's not the first
@@ -296,12 +294,13 @@ export class Watcher {
296
294
  const registeredTransaction =
297
295
  this._transactionsRegistry[assetCode][transaction.id];
298
296
 
299
- // if we've had the transaction before, only report if there is a change
297
+ // if we've had the transaction before, only report if there is a status change
298
+ let isChanged = true;
300
299
  if (
301
300
  registeredTransaction &&
302
- isEqual(registeredTransaction, transaction)
301
+ registeredTransaction.status === transaction.status
303
302
  ) {
304
- return;
303
+ isChanged = false;
305
304
  }
306
305
 
307
306
  this._transactionsRegistry[assetCode][transaction.id] = transaction;
@@ -320,7 +319,9 @@ export class Watcher {
320
319
  isRetry: true,
321
320
  });
322
321
  }, timeout);
323
- onMessage(transaction);
322
+ if (isChanged) {
323
+ onMessage(transaction);
324
+ }
324
325
  } else if (
325
326
  [
326
327
  TransactionStatus.completed,
@@ -36,7 +36,7 @@ describe("Stellar", () => {
36
36
  } catch (e) {
37
37
  await axios.get("https://friendbot.stellar.org/?addr=" + kp.publicKey);
38
38
  }
39
- });
39
+ }, 10000);
40
40
  it("should create and submit a transaction", async () => {
41
41
  const now = Math.floor(Date.now() / 1000) - 5;
42
42
 
@@ -198,32 +198,53 @@ describe("Stellar", () => {
198
198
  kp.sign(tx);
199
199
  });
200
200
  it("should transfer withdrawal transaction", async () => {
201
- const walletTransaction = {
202
- id: "db15d166-5a5e-4d5c-ba5d-271c32cd8cf0",
203
- kind: "withdrawal",
204
- status: TransactionStatus.pending_user_transfer_start,
205
- amount_in: "50.55",
206
- withdraw_memo_type: "text",
207
- withdraw_memo: "the withdraw memo",
208
- withdraw_anchor_account:
209
- "GCSGSR6KQQ5BP2FXVPWRL6SWPUSFWLVONLIBJZUKTVQB5FYJFVL6XOXE",
210
- } as WithdrawTransaction;
201
+ const memoExamples = [
202
+ {
203
+ type: "text",
204
+ value: "example text",
205
+ },
206
+ {
207
+ type: "id",
208
+ value: "12345",
209
+ },
210
+ {
211
+ type: "hash",
212
+ value: "AAAAAAAAAAAAAAAAAAAAAMAP+8deo0TViBD09TfOBY0=",
213
+ },
214
+ {
215
+ type: "hash",
216
+ value: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
217
+ },
218
+ ];
211
219
 
212
- const asset = new IssuedAssetId(
213
- "USDC",
214
- "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
215
- );
220
+ for (const memoExample of memoExamples) {
221
+ const walletTransaction = {
222
+ id: "db15d166-5a5e-4d5c-ba5d-271c32cd8cf0",
223
+ kind: "withdrawal",
224
+ status: TransactionStatus.pending_user_transfer_start,
225
+ amount_in: "50.55",
226
+ withdraw_memo_type: memoExample.type,
227
+ withdraw_memo: memoExample.value,
228
+ withdraw_anchor_account:
229
+ "GCSGSR6KQQ5BP2FXVPWRL6SWPUSFWLVONLIBJZUKTVQB5FYJFVL6XOXE",
230
+ } as WithdrawTransaction;
216
231
 
217
- const txBuilder = await stellar.transaction({
218
- sourceAddress: kp,
219
- baseFee: 100,
220
- });
232
+ const asset = new IssuedAssetId(
233
+ "USDC",
234
+ "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
235
+ );
221
236
 
222
- const txn = txBuilder
223
- .transferWithdrawalTransaction(walletTransaction, asset)
224
- .build();
225
- expect(txn).toBeTruthy();
226
- });
237
+ const txBuilder = await stellar.transaction({
238
+ sourceAddress: kp,
239
+ baseFee: 100,
240
+ });
241
+
242
+ const txn = txBuilder
243
+ .transferWithdrawalTransaction(walletTransaction, asset)
244
+ .build();
245
+ expect(txn).toBeTruthy();
246
+ }
247
+ }, 20000);
227
248
  });
228
249
 
229
250
  describe("Asset", () => {
@@ -64,6 +64,13 @@ describe("SEP-24 flow", () => {
64
64
  let anchor: Anchor;
65
65
  let accountKp: SigningKeypair;
66
66
  let authToken: string;
67
+ const makeTransaction = (eta: number, txStatus: TransactionStatus) => ({
68
+ kind: "deposit",
69
+ id: "TEST",
70
+ status: txStatus,
71
+ status_eta: eta,
72
+ message: "some message",
73
+ });
67
74
  describe("Anchor", () => {
68
75
  beforeAll(() => {
69
76
  const Wal = Wallet.TestNet();
@@ -809,6 +816,56 @@ describe("Anchor", () => {
809
816
  expect(onMessage.callCount).toBe(3);
810
817
  expect(onError.callCount).toBe(0);
811
818
 
819
+ // stops watching
820
+ stop();
821
+ });
822
+ it("should only report transactions with changed status", async () => {
823
+ const txn1 = makeTransaction(0, TransactionStatus.incomplete);
824
+ const txn2 = makeTransaction(0, TransactionStatus.incomplete);
825
+ txn2.message = "message changing";
826
+ const txn3 = makeTransaction(0, TransactionStatus.pending_anchor);
827
+
828
+ const onMessage = sinon.spy((m) => {
829
+ expect(m.message).toBe("some message");
830
+ expect(onMessage.callCount).toBeLessThan(3);
831
+ });
832
+
833
+ const onError = sinon.spy((e) => {
834
+ expect(e).toBeUndefined();
835
+ });
836
+
837
+ // mock default transactions response
838
+ jest
839
+ .spyOn(Sep24.prototype, "getTransactionsForAsset")
840
+ .mockResolvedValueOnce([txn1])
841
+ .mockResolvedValueOnce([txn2])
842
+ .mockResolvedValueOnce([txn3]);
843
+
844
+ // start watching
845
+ const watcher = anchor.sep24().watcher();
846
+ const { stop } = watcher.watchAllTransactions({
847
+ authToken,
848
+ assetCode: "SRT",
849
+ lang: "en-US",
850
+ onMessage,
851
+ onError,
852
+ timeout: 1,
853
+ });
854
+
855
+ // nothing should run at first (async api call in progress)
856
+ expect(onMessage.callCount).toBe(0);
857
+ expect(onError.callCount).toBe(0);
858
+
859
+ clock.next();
860
+ await sleep(1);
861
+ clock.next();
862
+ await sleep(1);
863
+ clock.next();
864
+ await sleep(1);
865
+
866
+ expect(onMessage.callCount).toBe(2);
867
+ expect(onError.callCount).toBe(0);
868
+
812
869
  // stops watching
813
870
  stop();
814
871
  });
@@ -818,13 +875,6 @@ describe("Anchor", () => {
818
875
  let clock: sinon.SinonFakeTimers;
819
876
  let watcher: Watcher;
820
877
 
821
- const makeTransaction = (eta: number, txStatus: TransactionStatus) => ({
822
- kind: "deposit",
823
- id: "TEST",
824
- status: txStatus,
825
- status_eta: eta,
826
- });
827
-
828
878
  beforeEach(async () => {
829
879
  clock = sinon.useFakeTimers(0);
830
880
  watcher = anchor.sep24().watcher();
@@ -1120,7 +1170,7 @@ describe("Anchor", () => {
1120
1170
  });
1121
1171
 
1122
1172
  test("Several pending transactions, one completed, no more after that", async () => {
1123
- const onMessage = sinon.spy(() => {
1173
+ const onMessage = sinon.spy((m) => {
1124
1174
  expect(onMessage.callCount).toBeLessThanOrEqual(8);
1125
1175
  });
1126
1176
 
@@ -1142,27 +1192,31 @@ describe("Anchor", () => {
1142
1192
  .mockResolvedValueOnce(
1143
1193
  makeTransaction(2, TransactionStatus.pending_anchor),
1144
1194
  )
1195
+ // should not be logged to onMessage
1145
1196
  .mockResolvedValueOnce(
1146
- makeTransaction(3, TransactionStatus.pending_external),
1197
+ makeTransaction(3, TransactionStatus.pending_anchor),
1147
1198
  )
1148
1199
  .mockResolvedValueOnce(
1149
- makeTransaction(4, TransactionStatus.pending_stellar),
1200
+ makeTransaction(4, TransactionStatus.pending_external),
1150
1201
  )
1151
1202
  .mockResolvedValueOnce(
1152
- makeTransaction(5, TransactionStatus.pending_trust),
1203
+ makeTransaction(5, TransactionStatus.pending_stellar),
1204
+ )
1205
+ .mockResolvedValueOnce(
1206
+ makeTransaction(6, TransactionStatus.pending_trust),
1153
1207
  )
1154
1208
  .mockResolvedValueOnce(
1155
- makeTransaction(6, TransactionStatus.pending_user_transfer_start),
1209
+ makeTransaction(7, TransactionStatus.pending_user_transfer_start),
1156
1210
  )
1157
1211
  .mockResolvedValueOnce(
1158
- makeTransaction(7, TransactionStatus.pending_user_transfer_complete),
1212
+ makeTransaction(8, TransactionStatus.pending_user_transfer_complete),
1159
1213
  )
1160
- .mockResolvedValueOnce(makeTransaction(8, TransactionStatus.completed))
1214
+ .mockResolvedValueOnce(makeTransaction(9, TransactionStatus.completed))
1161
1215
  .mockResolvedValueOnce(
1162
- makeTransaction(9, TransactionStatus.pending_anchor),
1216
+ makeTransaction(10, TransactionStatus.pending_anchor),
1163
1217
  )
1164
1218
  .mockResolvedValueOnce(
1165
- makeTransaction(10, TransactionStatus.pending_external),
1219
+ makeTransaction(11, TransactionStatus.pending_external),
1166
1220
  );
1167
1221
 
1168
1222
  // start watching
@@ -1206,6 +1260,9 @@ describe("Anchor", () => {
1206
1260
  clock.next();
1207
1261
  await sleep(1);
1208
1262
 
1263
+ clock.next();
1264
+ await sleep(1);
1265
+
1209
1266
  // 1 incomplete + 7 pending transactions
1210
1267
  expect(onMessage.callCount).toBe(8);
1211
1268
  expect(onSuccess.callCount).toBe(0);
@@ -1686,6 +1743,64 @@ describe("Anchor", () => {
1686
1743
  expect(onSuccess.callCount).toBe(0);
1687
1744
  expect(onError.callCount).toBe(1);
1688
1745
  });
1746
+
1747
+ it("should only report transactions with changed status", async () => {
1748
+ const txn1 = makeTransaction(0, TransactionStatus.incomplete);
1749
+ const txn2 = makeTransaction(0, TransactionStatus.incomplete);
1750
+ txn2.message = "message changing";
1751
+ const txn3 = makeTransaction(0, TransactionStatus.pending_anchor);
1752
+
1753
+ const onMessage = sinon.spy((m) => {
1754
+ expect(m.message).toBe("some message");
1755
+ expect(onMessage.callCount).toBeLessThan(3);
1756
+ });
1757
+
1758
+ const onSuccess = sinon.spy(() => {
1759
+ expect(onSuccess.callCount).toBe(0);
1760
+ });
1761
+
1762
+ const onError = sinon.spy((e) => {
1763
+ expect(e).toBeUndefined();
1764
+ });
1765
+
1766
+ // mock default transactions response
1767
+ jest
1768
+ .spyOn(Sep24.prototype, "getTransactionBy")
1769
+ .mockResolvedValueOnce(txn1)
1770
+ .mockResolvedValueOnce(txn2)
1771
+ .mockResolvedValueOnce(txn3);
1772
+
1773
+ // start watching
1774
+ const watcher = anchor.sep24().watcher();
1775
+ const { stop } = watcher.watchOneTransaction({
1776
+ authToken,
1777
+ assetCode: "SRT",
1778
+ id: "TEST",
1779
+ lang: "en-US",
1780
+ onMessage,
1781
+ onSuccess,
1782
+ onError,
1783
+ timeout: 1,
1784
+ });
1785
+
1786
+ // nothing should run at first (async api call in progress)
1787
+ expect(onMessage.callCount).toBe(0);
1788
+ expect(onError.callCount).toBe(0);
1789
+
1790
+ clock.next();
1791
+ await sleep(1);
1792
+ clock.next();
1793
+ await sleep(1);
1794
+ clock.next();
1795
+ await sleep(1);
1796
+
1797
+ expect(onMessage.callCount).toBe(2);
1798
+ expect(onError.callCount).toBe(0);
1799
+ expect(onSuccess.callCount).toBe(0);
1800
+
1801
+ // stops watching
1802
+ stop();
1803
+ });
1689
1804
  });
1690
1805
  });
1691
1806