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

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.
Files changed (42) hide show
  1. package/CHANGELOG.MD +39 -0
  2. package/examples/sep10/sep10Server.ts +1 -1
  3. package/examples/sep24/sep24.ts +1 -1
  4. package/examples/tsconfig.json +1 -0
  5. package/jest.e2e.config.js +2 -1
  6. package/jest.integration.config.js +2 -1
  7. package/lib/bundle.js +58608 -22154
  8. package/lib/bundle.js.map +1 -1
  9. package/lib/bundle_browser.js +49296 -12803
  10. package/lib/bundle_browser.js.map +1 -1
  11. package/lib/walletSdk/Customer/index.d.ts +4 -4
  12. package/lib/walletSdk/Exceptions/index.d.ts +12 -0
  13. package/lib/walletSdk/Horizon/AccountService.d.ts +2 -3
  14. package/lib/walletSdk/Horizon/Transaction/TransactionBuilder.d.ts +17 -14
  15. package/lib/walletSdk/Server/index.d.ts +17 -1
  16. package/lib/walletSdk/Types/auth.d.ts +5 -0
  17. package/lib/walletSdk/Types/horizon.d.ts +11 -5
  18. package/lib/walletSdk/Types/sep12.d.ts +1 -2
  19. package/lib/walletSdk/Uri/Sep7Base.d.ts +1 -1
  20. package/package.json +7 -6
  21. package/src/walletSdk/Auth/AuthHeaderSigner.ts +11 -5
  22. package/src/walletSdk/Auth/WalletSigner.ts +1 -1
  23. package/src/walletSdk/Auth/index.ts +4 -3
  24. package/src/walletSdk/Customer/index.ts +6 -6
  25. package/src/walletSdk/Exceptions/index.ts +27 -0
  26. package/src/walletSdk/Horizon/AccountService.ts +2 -2
  27. package/src/walletSdk/Horizon/Stellar.ts +1 -1
  28. package/src/walletSdk/Horizon/Transaction/TransactionBuilder.ts +31 -19
  29. package/src/walletSdk/Recovery/AccountRecover.ts +1 -1
  30. package/src/walletSdk/Server/index.ts +55 -12
  31. package/src/walletSdk/Types/auth.ts +5 -0
  32. package/src/walletSdk/Types/horizon.ts +16 -5
  33. package/src/walletSdk/Types/sep12.ts +1 -1
  34. package/src/walletSdk/Uri/Sep7Base.ts +10 -9
  35. package/src/walletSdk/Uri/Sep7Tx.ts +1 -1
  36. package/test/auth.test.ts +42 -0
  37. package/test/e2e/reactNative.test.ts +190 -0
  38. package/test/sep7.test.ts +72 -7
  39. package/test/server.test.ts +219 -1
  40. package/test/transaction.test.ts +107 -4
  41. package/test/tsconfig.json +1 -0
  42. package/webpack.config.js +0 -4
@@ -1,6 +1,21 @@
1
1
  import axios from "axios";
2
- import { TransactionBuilder } from "@stellar/stellar-sdk";
2
+ import sinon from "sinon";
3
+ import {
4
+ Account,
5
+ Asset,
6
+ Keypair,
7
+ MuxedAccount,
8
+ Operation,
9
+ StellarToml,
10
+ Transaction,
11
+ TransactionBuilder,
12
+ WebAuth,
13
+ } from "@stellar/stellar-sdk";
3
14
  import { Wallet, Server } from "../src";
15
+ import {
16
+ ChallengeTxnClientAccountMismatchError,
17
+ ChallengeValidationFailedError,
18
+ } from "../src/walletSdk/Exceptions";
4
19
 
5
20
  let wallet;
6
21
  let account;
@@ -78,3 +93,206 @@ describe("Server helpers", () => {
78
93
  expect(parsed.kind).toBe("withdrawal");
79
94
  });
80
95
  });
96
+
97
+ describe("signChallengeTransaction validation", () => {
98
+ const anchorA = "anchor-a.example.com";
99
+ const anchorB = "anchor-b.example.com";
100
+
101
+ let anchorAKp;
102
+ let anchorBKp;
103
+ let accountKp;
104
+
105
+ beforeEach(() => {
106
+ anchorAKp = Keypair.random();
107
+ anchorBKp = Keypair.random();
108
+ accountKp = Wallet.TestNet().stellar().account().createKeypair();
109
+
110
+ // anchorDomain always resolves to anchor A's stellar.toml
111
+ sinon.stub(StellarToml.Resolver, "resolve").resolves({
112
+ SIGNING_KEY: anchorAKp.publicKey(),
113
+ WEB_AUTH_ENDPOINT: `https://${anchorA}/auth`,
114
+ DOCUMENTATION: {},
115
+ } as StellarToml.Api.StellarToml);
116
+ });
117
+
118
+ afterEach(() => {
119
+ sinon.restore();
120
+ });
121
+
122
+ // webAuthDomain defaults to anchor A, which is what the stubbed
123
+ // WEB_AUTH_ENDPOINT resolves to for anchorDomain.
124
+ const buildChallenge = (
125
+ serverKp,
126
+ homeDomain,
127
+ clientAccountID,
128
+ webAuthDomain = anchorA,
129
+ ) =>
130
+ WebAuth.buildChallengeTx(
131
+ serverKp,
132
+ clientAccountID,
133
+ homeDomain,
134
+ 300,
135
+ networkPassphrase,
136
+ webAuthDomain,
137
+ );
138
+
139
+ it("should sign a challenge issued by the expected anchor", async () => {
140
+ const challengeTx = buildChallenge(anchorAKp, anchorA, accountKp.publicKey);
141
+
142
+ const signedResp = await Server.signChallengeTransaction({
143
+ accountKp,
144
+ challengeTx,
145
+ networkPassphrase,
146
+ anchorDomain: anchorA,
147
+ });
148
+
149
+ const signedTxn = TransactionBuilder.fromXDR(
150
+ signedResp.transaction,
151
+ networkPassphrase,
152
+ );
153
+ expect(signedTxn.signatures.length).toBe(2);
154
+ });
155
+
156
+ it("should reject a challenge issued by a different anchor", async () => {
157
+ // A relays a challenge that anchor B issued for the same account, ordering
158
+ // its own signature first so that signatures[0] matches the SIGNING_KEY
159
+ // published at anchorDomain. Only the home domain, web auth domain and
160
+ // transaction source bind the challenge to the anchor that issued it.
161
+ const relayed = TransactionBuilder.fromXDR(
162
+ buildChallenge(anchorBKp, anchorB, accountKp.publicKey, anchorB),
163
+ networkPassphrase,
164
+ ) as Transaction;
165
+
166
+ const anchorBSignature = relayed.signatures[0];
167
+ relayed.signatures.length = 0;
168
+ relayed.sign(anchorAKp);
169
+ relayed.signatures.push(anchorBSignature);
170
+
171
+ await expect(
172
+ Server.signChallengeTransaction({
173
+ accountKp,
174
+ challengeTx: relayed.toXDR(),
175
+ networkPassphrase,
176
+ anchorDomain: anchorA,
177
+ }),
178
+ ).rejects.toThrow(ChallengeValidationFailedError);
179
+ });
180
+
181
+ it("should sign a challenge issued for a muxed account of the signing key", async () => {
182
+ // readChallengeTx returns the M... source verbatim; the underlying G... key
183
+ // is the correct signer, so this must be accepted.
184
+ const muxed = new MuxedAccount(
185
+ new Account(accountKp.publicKey, "0"),
186
+ "1234",
187
+ ).accountId();
188
+ expect(muxed.startsWith("M")).toBe(true);
189
+
190
+ const challengeTx = buildChallenge(anchorAKp, anchorA, muxed);
191
+
192
+ const signedResp = await Server.signChallengeTransaction({
193
+ accountKp,
194
+ challengeTx,
195
+ networkPassphrase,
196
+ anchorDomain: anchorA,
197
+ });
198
+
199
+ const signedTxn = TransactionBuilder.fromXDR(
200
+ signedResp.transaction,
201
+ networkPassphrase,
202
+ );
203
+ expect(signedTxn.signatures.length).toBe(2);
204
+ });
205
+
206
+ it("should reject a muxed challenge whose underlying key is not the signer", async () => {
207
+ const otherMuxed = new MuxedAccount(
208
+ new Account(Keypair.random().publicKey(), "0"),
209
+ "1234",
210
+ ).accountId();
211
+
212
+ await expect(
213
+ Server.signChallengeTransaction({
214
+ accountKp,
215
+ challengeTx: buildChallenge(anchorAKp, anchorA, otherMuxed),
216
+ networkPassphrase,
217
+ anchorDomain: anchorA,
218
+ }),
219
+ ).rejects.toThrow(ChallengeTxnClientAccountMismatchError);
220
+ });
221
+
222
+ it("should reject a challenge issued for a different account", async () => {
223
+ const otherAccount = Keypair.random().publicKey();
224
+ const challengeTx = buildChallenge(anchorAKp, anchorA, otherAccount);
225
+
226
+ await expect(
227
+ Server.signChallengeTransaction({
228
+ accountKp,
229
+ challengeTx,
230
+ networkPassphrase,
231
+ anchorDomain: anchorA,
232
+ }),
233
+ ).rejects.toThrow(ChallengeTxnClientAccountMismatchError);
234
+ });
235
+
236
+ it("should reject a transaction that is not a SEP-10 challenge", async () => {
237
+ const notAChallenge = new TransactionBuilder(
238
+ new Account(anchorAKp.publicKey(), "-1"),
239
+ { fee: "100", networkPassphrase },
240
+ )
241
+ .addOperation(
242
+ Operation.payment({
243
+ destination: anchorAKp.publicKey(),
244
+ asset: Asset.native(),
245
+ amount: "1000",
246
+ }),
247
+ )
248
+ .setTimeout(300)
249
+ .build();
250
+ notAChallenge.sign(anchorAKp);
251
+
252
+ await expect(
253
+ Server.signChallengeTransaction({
254
+ accountKp,
255
+ challengeTx: notAChallenge.toXDR(),
256
+ networkPassphrase,
257
+ anchorDomain: anchorA,
258
+ }),
259
+ ).rejects.toThrow(ChallengeValidationFailedError);
260
+ });
261
+
262
+ it("should wrap a malformed WEB_AUTH_ENDPOINT as a validation failure", async () => {
263
+ sinon.restore();
264
+ sinon.stub(StellarToml.Resolver, "resolve").resolves({
265
+ SIGNING_KEY: anchorAKp.publicKey(),
266
+ WEB_AUTH_ENDPOINT: "not a url",
267
+ DOCUMENTATION: {},
268
+ } as StellarToml.Api.StellarToml);
269
+
270
+ await expect(
271
+ Server.signChallengeTransaction({
272
+ accountKp,
273
+ challengeTx: buildChallenge(anchorAKp, anchorA, accountKp.publicKey),
274
+ networkPassphrase,
275
+ anchorDomain: anchorA,
276
+ }),
277
+ ).rejects.toThrow(ChallengeValidationFailedError);
278
+ });
279
+
280
+ it("should accept an explicit homeDomain that differs from anchorDomain", async () => {
281
+ const homeDomain = "wallet-home.example.com";
282
+ const challengeTx = buildChallenge(
283
+ anchorAKp,
284
+ homeDomain,
285
+ accountKp.publicKey,
286
+ );
287
+
288
+ const signedResp = await Server.signChallengeTransaction({
289
+ accountKp,
290
+ challengeTx,
291
+ networkPassphrase,
292
+ anchorDomain: anchorA,
293
+ homeDomain,
294
+ });
295
+
296
+ expect(signedResp.networkPassphrase).toBe(networkPassphrase);
297
+ });
298
+ });
@@ -1,8 +1,24 @@
1
1
  import axios from "axios";
2
- import { Horizon, MuxedAccount } from "@stellar/stellar-sdk";
3
-
4
- import { AccountService, SigningKeypair, Stellar, Wallet } from "../src";
2
+ import {
3
+ Account,
4
+ Horizon,
5
+ Keypair,
6
+ MuxedAccount,
7
+ Operation,
8
+ } from "@stellar/stellar-sdk";
9
+
10
+ import {
11
+ AccountService,
12
+ ApplicationConfiguration,
13
+ Config,
14
+ SigningKeypair,
15
+ Stellar,
16
+ StellarConfiguration,
17
+ TransactionBuilder,
18
+ Wallet,
19
+ } from "../src";
5
20
  import { IssuedAssetId, NativeAssetId } from "../src/walletSdk/Asset";
21
+ import { PathPayBoundRequiredError } from "../src/walletSdk/Exceptions";
6
22
 
7
23
  describe("Muxed Transactions", () => {
8
24
  let wallet: Wallet;
@@ -289,6 +305,7 @@ describe("Path Payment", () => {
289
305
  sendAsset: new NativeAssetId(),
290
306
  destAsset: usdcAsset,
291
307
  sendAmount: "5",
308
+ destMin: "4.9",
292
309
  })
293
310
  .build();
294
311
  expect(txn.operations[0].type).toBe("pathPaymentStrictSend");
@@ -304,6 +321,7 @@ describe("Path Payment", () => {
304
321
  sendAsset: new NativeAssetId(),
305
322
  destAsset: usdcAsset,
306
323
  destAmount: "5",
324
+ sendMax: "5.1",
307
325
  })
308
326
  .build();
309
327
  expect(txn.operations[0].type).toBe("pathPaymentStrictReceive");
@@ -314,10 +332,95 @@ describe("Path Payment", () => {
314
332
  sourceAddress: sourceKp,
315
333
  });
316
334
  const txn = txBuilder
317
- .swap(new NativeAssetId(), new NativeAssetId(), ".1")
335
+ .swap(new NativeAssetId(), new NativeAssetId(), ".1", ".1")
318
336
  .build();
319
337
  sourceKp.sign(txn);
320
338
  const success = await stellar.submitTransaction(txn);
321
339
  expect(success).toBe(true);
322
340
  }, 15000);
323
341
  });
342
+
343
+ describe("Path Payment bounds", () => {
344
+ const usdcAsset = new IssuedAssetId(
345
+ "USDC",
346
+ "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
347
+ );
348
+ const destination = Keypair.random().publicKey();
349
+
350
+ // Builds a TransactionBuilder offline so these tests never touch Horizon
351
+ const newBuilder = () =>
352
+ new TransactionBuilder(
353
+ new Config({
354
+ stellarConfiguration: StellarConfiguration.TestNet(),
355
+ applicationConfiguration: new ApplicationConfiguration(),
356
+ }),
357
+ new Account(Keypair.random().publicKey(), "0"),
358
+ );
359
+
360
+ it("throws when sendAmount is given without destMin", () => {
361
+ expect(() =>
362
+ newBuilder().pathPay({
363
+ destinationAddress: destination,
364
+ sendAsset: new NativeAssetId(),
365
+ destAsset: usdcAsset,
366
+ sendAmount: "5",
367
+ } as any),
368
+ ).toThrow(PathPayBoundRequiredError);
369
+ });
370
+
371
+ it("throws when destAmount is given without sendMax", () => {
372
+ expect(() =>
373
+ newBuilder().pathPay({
374
+ destinationAddress: destination,
375
+ sendAsset: new NativeAssetId(),
376
+ destAsset: usdcAsset,
377
+ destAmount: "5",
378
+ } as any),
379
+ ).toThrow(PathPayBoundRequiredError);
380
+ });
381
+
382
+ it("throws when swap is called without destMin", () => {
383
+ expect(() =>
384
+ (newBuilder() as any).swap(new NativeAssetId(), usdcAsset, "1"),
385
+ ).toThrow(PathPayBoundRequiredError);
386
+ });
387
+
388
+ it("uses the destMin given to pathPay", () => {
389
+ const txn = newBuilder()
390
+ .pathPay({
391
+ destinationAddress: destination,
392
+ sendAsset: new NativeAssetId(),
393
+ destAsset: usdcAsset,
394
+ sendAmount: "5",
395
+ destMin: "4.9",
396
+ })
397
+ .build();
398
+ const op = txn.operations[0] as Operation.PathPaymentStrictSend;
399
+ expect(op.type).toBe("pathPaymentStrictSend");
400
+ expect(op.destMin).toBe("4.9000000");
401
+ });
402
+
403
+ it("uses the sendMax given to pathPay", () => {
404
+ const txn = newBuilder()
405
+ .pathPay({
406
+ destinationAddress: destination,
407
+ sendAsset: new NativeAssetId(),
408
+ destAsset: usdcAsset,
409
+ destAmount: "5",
410
+ sendMax: "5.1",
411
+ })
412
+ .build();
413
+ const op = txn.operations[0] as Operation.PathPaymentStrictReceive;
414
+ expect(op.type).toBe("pathPaymentStrictReceive");
415
+ expect(op.sendMax).toBe("5.1000000");
416
+ });
417
+
418
+ it("uses the destMin given to swap", () => {
419
+ const txn = newBuilder()
420
+ .swap(new NativeAssetId(), usdcAsset, "1", "0.9")
421
+ .build();
422
+ const op = txn.operations[0] as Operation.PathPaymentStrictSend;
423
+ expect(op.type).toBe("pathPaymentStrictSend");
424
+ expect(op.destMin).toBe("0.9000000");
425
+ });
426
+ });
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "esModuleInterop": true,
4
+ "skipLibCheck": true,
4
5
  "baseUrl": "./",
5
6
  "outDir": "lib",
6
7
  "declaration": true,
package/webpack.config.js CHANGED
@@ -40,7 +40,6 @@ module.exports = (env = { NODE: false }) => {
40
40
  util: require.resolve("util"),
41
41
  vm: require.resolve("vm-browserify"),
42
42
  "process/browser": require.resolve("process/browser"),
43
- buffer: require.resolve("buffer"),
44
43
  }
45
44
  : {},
46
45
  },
@@ -60,9 +59,6 @@ module.exports = (env = { NODE: false }) => {
60
59
  new webpack.ProvidePlugin({
61
60
  process: "process/browser",
62
61
  }),
63
- new webpack.ProvidePlugin({
64
- Buffer: ["buffer", "Buffer"],
65
- }),
66
62
  ]
67
63
  : [],
68
64
  };