@stellar/typescript-wallet-sdk 4.0.1 → 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.
- package/CHANGELOG.MD +26 -0
- package/examples/sep10/sep10Server.ts +1 -1
- package/examples/sep24/sep24.ts +1 -1
- package/examples/tsconfig.json +1 -0
- package/jest.e2e.config.js +2 -1
- package/jest.integration.config.js +2 -1
- package/lib/bundle.js +58575 -22165
- package/lib/bundle.js.map +1 -1
- package/lib/bundle_browser.js +49263 -12814
- package/lib/bundle_browser.js.map +1 -1
- package/lib/walletSdk/Customer/index.d.ts +4 -4
- package/lib/walletSdk/Exceptions/index.d.ts +3 -0
- package/lib/walletSdk/Horizon/AccountService.d.ts +2 -3
- package/lib/walletSdk/Horizon/Transaction/TransactionBuilder.d.ts +17 -14
- package/lib/walletSdk/Types/horizon.d.ts +11 -5
- package/lib/walletSdk/Types/sep12.d.ts +1 -2
- package/lib/walletSdk/Uri/Sep7Base.d.ts +1 -1
- package/package.json +7 -6
- package/src/walletSdk/Auth/AuthHeaderSigner.ts +11 -5
- package/src/walletSdk/Auth/WalletSigner.ts +1 -1
- package/src/walletSdk/Auth/index.ts +4 -3
- package/src/walletSdk/Customer/index.ts +6 -6
- package/src/walletSdk/Exceptions/index.ts +8 -0
- package/src/walletSdk/Horizon/AccountService.ts +2 -2
- package/src/walletSdk/Horizon/Stellar.ts +1 -1
- package/src/walletSdk/Horizon/Transaction/TransactionBuilder.ts +31 -19
- package/src/walletSdk/Recovery/AccountRecover.ts +1 -1
- package/src/walletSdk/Server/index.ts +2 -2
- package/src/walletSdk/Types/horizon.ts +16 -5
- package/src/walletSdk/Types/sep12.ts +1 -1
- package/src/walletSdk/Uri/Sep7Base.ts +10 -9
- package/src/walletSdk/Uri/Sep7Tx.ts +1 -1
- package/test/auth.test.ts +42 -0
- package/test/e2e/reactNative.test.ts +190 -0
- package/test/sep7.test.ts +72 -7
- package/test/transaction.test.ts +107 -4
- package/test/tsconfig.json +1 -0
- package/webpack.config.js +0 -4
package/test/auth.test.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
MuxedAccount,
|
|
8
8
|
Networks,
|
|
9
9
|
StellarToml,
|
|
10
|
+
StrKey,
|
|
10
11
|
Transaction,
|
|
11
12
|
TransactionBuilder as SdkTransactionBuilder,
|
|
12
13
|
Operation,
|
|
@@ -15,8 +16,12 @@ import {
|
|
|
15
16
|
import { randomBytes } from "crypto";
|
|
16
17
|
import axios from "axios";
|
|
17
18
|
import sinon from "sinon";
|
|
19
|
+
import { base64ToString, base64ToUint8Array } from "uint8array-extras";
|
|
20
|
+
import { encode as utf8Encode } from "@stablelib/utf8";
|
|
21
|
+
import nacl from "tweetnacl";
|
|
18
22
|
|
|
19
23
|
import { validateToken, Sep10, type WalletSigner } from "../src/walletSdk/Auth";
|
|
24
|
+
import { DefaultAuthHeaderSigner } from "../src/walletSdk/Auth/AuthHeaderSigner";
|
|
20
25
|
import {
|
|
21
26
|
Config,
|
|
22
27
|
StellarConfiguration,
|
|
@@ -837,3 +842,40 @@ describe("Anchor.sep10() signing key handling", () => {
|
|
|
837
842
|
expect(sep10).toBeDefined();
|
|
838
843
|
});
|
|
839
844
|
});
|
|
845
|
+
|
|
846
|
+
describe("auth header JWT encoding", () => {
|
|
847
|
+
it("signs with unpadded base64url and verifies against the public key", async () => {
|
|
848
|
+
const kp = SigningKeypair.fromSecret(Keypair.random().secret());
|
|
849
|
+
const signer = new DefaultAuthHeaderSigner();
|
|
850
|
+
|
|
851
|
+
const jwt = await signer.createToken({
|
|
852
|
+
claims: {
|
|
853
|
+
account: kp.publicKey,
|
|
854
|
+
home_domain: "test.com",
|
|
855
|
+
web_auth_endpoint: "https://test.com/auth",
|
|
856
|
+
},
|
|
857
|
+
issuer: kp,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const [header, payload, signature] = jwt.split(".");
|
|
861
|
+
|
|
862
|
+
// base64url: no +, / or = padding in any segment.
|
|
863
|
+
for (const segment of [header, payload, signature]) {
|
|
864
|
+
expect(segment).not.toMatch(/[+/=]/);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// The header must still decode to real JSON.
|
|
868
|
+
expect(JSON.parse(base64ToString(header))).toEqual({ alg: "EdDSA" });
|
|
869
|
+
|
|
870
|
+
// The signature must verify over "<header>.<payload>".
|
|
871
|
+
const naclKP = nacl.sign.keyPair.fromSeed(
|
|
872
|
+
StrKey.decodeEd25519SecretSeed(kp.secretKey),
|
|
873
|
+
);
|
|
874
|
+
const verified = nacl.sign.detached.verify(
|
|
875
|
+
utf8Encode(`${header}.${payload}`),
|
|
876
|
+
base64ToUint8Array(signature),
|
|
877
|
+
naclKP.publicKey,
|
|
878
|
+
);
|
|
879
|
+
expect(verified).toBe(true);
|
|
880
|
+
});
|
|
881
|
+
});
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as vm from "vm";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* React Native's Hermes runtime provides none of Buffer, Event, EventTarget or
|
|
7
|
+
* process. Loading each published bundle in a context without them catches the
|
|
8
|
+
* two failure modes this SDK has actually shipped: a reliance on the Buffer
|
|
9
|
+
* global, and dependencies that subclass the DOM Event globals at module scope.
|
|
10
|
+
*
|
|
11
|
+
* The sandbox also *grants* a few globals that a real RN app must supply
|
|
12
|
+
* itself, so this file is explicit about what environment it is modelling:
|
|
13
|
+
*
|
|
14
|
+
* - `crypto`: Hermes/RN does not provide this either. A real RN app needs
|
|
15
|
+
* `react-native-get-random-values` (or equivalent) for stellar-sdk's RNG
|
|
16
|
+
* path. This was already true under v16 and is not new to this migration.
|
|
17
|
+
* - `btoa`/`atob`: see the dedicated comment below.
|
|
18
|
+
* - `TextEncoder`/`TextDecoder`: see the dedicated comment below.
|
|
19
|
+
*/
|
|
20
|
+
const BANNED = ["Buffer", "Event", "EventTarget", "process"];
|
|
21
|
+
|
|
22
|
+
const makeHermesLikeContext = ({
|
|
23
|
+
includeBase64 = true,
|
|
24
|
+
includeTextCodec = true,
|
|
25
|
+
} = {}) => {
|
|
26
|
+
const sandbox: Record<string, unknown> = {
|
|
27
|
+
console,
|
|
28
|
+
URL,
|
|
29
|
+
URLSearchParams,
|
|
30
|
+
setTimeout,
|
|
31
|
+
clearTimeout,
|
|
32
|
+
setInterval,
|
|
33
|
+
clearInterval,
|
|
34
|
+
crypto: globalThis.crypto,
|
|
35
|
+
fetch: globalThis.fetch,
|
|
36
|
+
module: { exports: {} },
|
|
37
|
+
exports: {},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (includeTextCodec) {
|
|
41
|
+
// TextEncoder/TextDecoder are a real consumer requirement, not a test
|
|
42
|
+
// convenience, and the requirement is unavoidable: uint8array-extras@1.5.0
|
|
43
|
+
// — a direct dependency of ours and a transitive dependency of
|
|
44
|
+
// stellar-sdk — constructs both at module initialization, unguarded:
|
|
45
|
+
// `new globalThis.TextDecoder('utf8')` (index.js:113, inside a
|
|
46
|
+
// module-scope object literal) and `new globalThis.TextEncoder()`
|
|
47
|
+
// (index.js:128). By contrast, @exodus/bytes DOES guard this —
|
|
48
|
+
// fallback/platform.native.js only constructs them when
|
|
49
|
+
// `isNative(globalThis.TextDecoder)` holds, falling back to pure-JS
|
|
50
|
+
// decoding otherwise — so the requirement traces specifically to
|
|
51
|
+
// uint8array-extras, not to @exodus/bytes. Granting them here models an
|
|
52
|
+
// environment that satisfies this prerequisite. We cannot verify from
|
|
53
|
+
// here whether React Native itself provides them (this file makes no
|
|
54
|
+
// claim either way), but RN apps commonly polyfill TextEncoder/TextDecoder
|
|
55
|
+
// already for other libraries. Remove them and every bundle throws
|
|
56
|
+
// "TypeError: globalThis.TextDecoder is not a constructor" at load —
|
|
57
|
+
// before any of our own code runs. See the test below that enforces this.
|
|
58
|
+
sandbox.TextEncoder = TextEncoder;
|
|
59
|
+
sandbox.TextDecoder = TextDecoder;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (includeBase64) {
|
|
63
|
+
// btoa/atob are a real consumer requirement of this release, not a test
|
|
64
|
+
// convenience. stellar-sdk v17's xdr.encodeBytes(bytes, "base64") routes
|
|
65
|
+
// through its own lib/cjs/base/util/base64.js, which calls the bare `btoa`
|
|
66
|
+
// identifier with no fallback, and Task 2 removed the browser bundle's
|
|
67
|
+
// Buffer polyfill that used to cover it. Hermes provides btoa/atob
|
|
68
|
+
// natively as of React Native 0.74 (facebook/hermes#1255, #1256); React
|
|
69
|
+
// Native's own JS layer has never defined them, so RN < 0.74 and
|
|
70
|
+
// non-Hermes engines (JSC, V8) need a polyfill such as `base-64`.
|
|
71
|
+
// Granting them here models a supported RN >= 0.74 Hermes app. Remove
|
|
72
|
+
// them and every base64 path throws "ReferenceError: btoa is not
|
|
73
|
+
// defined"; hex and base32/StrKey still work, being pure JS.
|
|
74
|
+
sandbox.btoa = globalThis.btoa;
|
|
75
|
+
sandbox.atob = globalThis.atob;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
sandbox.global = sandbox;
|
|
79
|
+
sandbox.globalThis = sandbox;
|
|
80
|
+
|
|
81
|
+
const context = vm.createContext(sandbox);
|
|
82
|
+
for (const name of BANNED) {
|
|
83
|
+
vm.runInContext(
|
|
84
|
+
`if (typeof ${name} !== "undefined") { throw new Error("${name} leaked into the sandbox"); }`,
|
|
85
|
+
context,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return context;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const bundles = [
|
|
92
|
+
["core SDK", "../../lib/bundle_browser.js"],
|
|
93
|
+
["key manager", "../../../typescript-wallet-sdk-km/lib/bundle_browser.js"],
|
|
94
|
+
["soroban", "../../../typescript-wallet-sdk-soroban/lib/bundle_browser.js"],
|
|
95
|
+
] as const;
|
|
96
|
+
|
|
97
|
+
describe("React Native (Hermes-like) bundle compatibility", () => {
|
|
98
|
+
for (const [name, relativePath] of bundles) {
|
|
99
|
+
it(`loads the ${name} bundle without Buffer/Event/EventTarget/process`, () => {
|
|
100
|
+
const bundlePath = path.resolve(__dirname, relativePath);
|
|
101
|
+
expect(fs.existsSync(bundlePath)).toBe(true);
|
|
102
|
+
|
|
103
|
+
const context = makeHermesLikeContext();
|
|
104
|
+
const code = fs.readFileSync(bundlePath, "utf8");
|
|
105
|
+
|
|
106
|
+
expect(() =>
|
|
107
|
+
vm.runInContext(code, context, { filename: `${name}.js` }),
|
|
108
|
+
).not.toThrow();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const [name, relativePath] of bundles) {
|
|
113
|
+
it(`fails to load the ${name} bundle without TextEncoder/TextDecoder`, () => {
|
|
114
|
+
const bundlePath = path.resolve(__dirname, relativePath);
|
|
115
|
+
const context = makeHermesLikeContext({ includeTextCodec: false });
|
|
116
|
+
const code = fs.readFileSync(bundlePath, "utf8");
|
|
117
|
+
|
|
118
|
+
// uint8array-extras constructs its TextDecoder/TextEncoder at module
|
|
119
|
+
// initialization (see the comment in makeHermesLikeContext), so the
|
|
120
|
+
// failure happens at load time, before any of our own code runs. This
|
|
121
|
+
// turns the assumption granted above into an enforced, documented
|
|
122
|
+
// contract rather than a silent one.
|
|
123
|
+
expect(() =>
|
|
124
|
+
vm.runInContext(code, context, { filename: `${name}.js` }),
|
|
125
|
+
).toThrow(/TextDecoder is not a constructor/);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
it("exercises the core SDK public API in the sandbox", () => {
|
|
130
|
+
const context = makeHermesLikeContext();
|
|
131
|
+
const code = fs.readFileSync(
|
|
132
|
+
path.resolve(__dirname, "../../lib/bundle_browser.js"),
|
|
133
|
+
"utf8",
|
|
134
|
+
);
|
|
135
|
+
vm.runInContext(code, context, { filename: "bundle_browser.js" });
|
|
136
|
+
|
|
137
|
+
// Sep7Pay.addSignature is the densest no-Buffer path in our own code: it
|
|
138
|
+
// runs concatUint8Arrays + stringToUint8Array to build the payload, signs
|
|
139
|
+
// it, then base64-encodes via xdr.encodeBytes.
|
|
140
|
+
const result = vm.runInContext(
|
|
141
|
+
`(() => {
|
|
142
|
+
const { Wallet, Sep7Pay } = module.exports;
|
|
143
|
+
const kp = Wallet.TestNet().stellar().account().createKeypair();
|
|
144
|
+
const uri = Sep7Pay.forDestination(kp.publicKey);
|
|
145
|
+
return { publicKey: kp.publicKey, signature: uri.addSignature(kp.keypair) };
|
|
146
|
+
})()`,
|
|
147
|
+
context,
|
|
148
|
+
) as { publicKey: string; signature: string };
|
|
149
|
+
|
|
150
|
+
expect(result.publicKey).toMatch(/^G[A-Z2-7]{55}$/);
|
|
151
|
+
// The decisive assertion: base64 encoding worked without Buffer. A broken
|
|
152
|
+
// encoder yields comma-joined decimals.
|
|
153
|
+
expect(result.signature).toMatch(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
154
|
+
expect(result.signature).toHaveLength(88);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("requires btoa/atob for base64 paths, while base32/StrKey keypair creation stays pure JS", () => {
|
|
158
|
+
const context = makeHermesLikeContext({ includeBase64: false });
|
|
159
|
+
const code = fs.readFileSync(
|
|
160
|
+
path.resolve(__dirname, "../../lib/bundle_browser.js"),
|
|
161
|
+
"utf8",
|
|
162
|
+
);
|
|
163
|
+
vm.runInContext(code, context, { filename: "bundle_browser.js" });
|
|
164
|
+
|
|
165
|
+
// Keypair generation and the public key's StrKey encoding are pure JS
|
|
166
|
+
// (base32, not base64), so they must work even without btoa/atob.
|
|
167
|
+
const publicKey = vm.runInContext(
|
|
168
|
+
`(() => {
|
|
169
|
+
const { Wallet } = module.exports;
|
|
170
|
+
return Wallet.TestNet().stellar().account().createKeypair().publicKey;
|
|
171
|
+
})()`,
|
|
172
|
+
context,
|
|
173
|
+
) as string;
|
|
174
|
+
expect(publicKey).toMatch(/^G[A-Z2-7]{55}$/);
|
|
175
|
+
|
|
176
|
+
// Sep7Pay.addSignature's base64 encode step has no fallback: without
|
|
177
|
+
// btoa this must fail loudly rather than silently degrade.
|
|
178
|
+
expect(() =>
|
|
179
|
+
vm.runInContext(
|
|
180
|
+
`(() => {
|
|
181
|
+
const { Wallet, Sep7Pay } = module.exports;
|
|
182
|
+
const kp = Wallet.TestNet().stellar().account().createKeypair();
|
|
183
|
+
const uri = Sep7Pay.forDestination(kp.publicKey);
|
|
184
|
+
return uri.addSignature(kp.keypair);
|
|
185
|
+
})()`,
|
|
186
|
+
context,
|
|
187
|
+
),
|
|
188
|
+
).toThrow(/btoa is not defined/);
|
|
189
|
+
});
|
|
190
|
+
});
|
package/test/sep7.test.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Account,
|
|
3
|
+
Asset,
|
|
4
|
+
BASE_FEE,
|
|
5
|
+
Keypair,
|
|
6
|
+
Networks,
|
|
7
|
+
Operation,
|
|
8
|
+
StellarToml,
|
|
9
|
+
TransactionBuilder,
|
|
10
|
+
} from "@stellar/stellar-sdk";
|
|
2
11
|
import {
|
|
3
12
|
NativeAssetId,
|
|
4
13
|
Sep7Pay,
|
|
@@ -360,7 +369,7 @@ describe("Sep7Tx", () => {
|
|
|
360
369
|
txBuilder.transfer(testKp2.publicKey, new NativeAssetId(), "1");
|
|
361
370
|
const tx = txBuilder.build();
|
|
362
371
|
|
|
363
|
-
const xdr = tx.toEnvelope().
|
|
372
|
+
const xdr = tx.toEnvelope().toXdr("base64");
|
|
364
373
|
|
|
365
374
|
const uri = Sep7Tx.forTransaction(tx);
|
|
366
375
|
expect(uri.operationType).toBe("tx");
|
|
@@ -488,7 +497,7 @@ describe("Sep7Tx", () => {
|
|
|
488
497
|
txBuilder.transfer(testKp2.publicKey, new NativeAssetId(), "1");
|
|
489
498
|
const tx = txBuilder.build();
|
|
490
499
|
|
|
491
|
-
const xdr = tx.toEnvelope().
|
|
500
|
+
const xdr = tx.toEnvelope().toXdr("base64");
|
|
492
501
|
|
|
493
502
|
const uri = Sep7Tx.forTransaction(tx);
|
|
494
503
|
|
|
@@ -553,13 +562,11 @@ describe("Sep7Tx", () => {
|
|
|
553
562
|
txBuilder.transfer(testKp2.publicKey, new NativeAssetId(), "1");
|
|
554
563
|
const tx = txBuilder.build();
|
|
555
564
|
|
|
556
|
-
const xdr = tx.toEnvelope().
|
|
565
|
+
const xdr = tx.toEnvelope().toXdr("base64");
|
|
557
566
|
|
|
558
567
|
const uri = new Sep7Tx(`web+stellar:tx?xdr=${encodeURIComponent(xdr)}`);
|
|
559
568
|
|
|
560
|
-
expect(uri.getTransaction().toEnvelope().
|
|
561
|
-
xdr,
|
|
562
|
-
);
|
|
569
|
+
expect(uri.getTransaction().toEnvelope().toXdr("base64")).toBe(xdr);
|
|
563
570
|
});
|
|
564
571
|
});
|
|
565
572
|
|
|
@@ -1002,3 +1009,61 @@ describe("sep7Parser", () => {
|
|
|
1002
1009
|
expect(actual).toBe(expected);
|
|
1003
1010
|
});
|
|
1004
1011
|
});
|
|
1012
|
+
|
|
1013
|
+
describe("SEP-7 byte encoding regressions", () => {
|
|
1014
|
+
const BASE64_ONLY = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
1015
|
+
|
|
1016
|
+
it("produces a base64 signature, not comma-joined decimals", () => {
|
|
1017
|
+
const keypair = Keypair.random();
|
|
1018
|
+
const uri = Sep7Pay.forDestination(
|
|
1019
|
+
"GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO",
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
const signature = uri.addSignature(keypair);
|
|
1023
|
+
|
|
1024
|
+
expect(signature).toMatch(BASE64_ONLY);
|
|
1025
|
+
expect(signature).not.toContain(",");
|
|
1026
|
+
// An ed25519 signature is 64 bytes => 88 base64 characters with padding.
|
|
1027
|
+
expect(signature).toHaveLength(88);
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("round-trips a signature through verification", async () => {
|
|
1031
|
+
const keypair = Keypair.random();
|
|
1032
|
+
const uri = Sep7Pay.forDestination(
|
|
1033
|
+
"GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO",
|
|
1034
|
+
);
|
|
1035
|
+
uri.originDomain = "place.domain.com";
|
|
1036
|
+
uri.addSignature(keypair);
|
|
1037
|
+
|
|
1038
|
+
jest
|
|
1039
|
+
.spyOn(StellarToml.Resolver, "resolve")
|
|
1040
|
+
.mockResolvedValue({ URI_REQUEST_SIGNING_KEY: keypair.publicKey() });
|
|
1041
|
+
|
|
1042
|
+
await expect(uri.verifySignature()).resolves.toBe(true);
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
it("puts parseable XDR in the uri, not comma-joined decimals", () => {
|
|
1046
|
+
const keypair = Keypair.random();
|
|
1047
|
+
const account = new Account(keypair.publicKey(), "1");
|
|
1048
|
+
const tx = new TransactionBuilder(account, {
|
|
1049
|
+
fee: BASE_FEE,
|
|
1050
|
+
networkPassphrase: Networks.TESTNET,
|
|
1051
|
+
})
|
|
1052
|
+
.addOperation(
|
|
1053
|
+
Operation.payment({
|
|
1054
|
+
destination: keypair.publicKey(),
|
|
1055
|
+
asset: Asset.native(),
|
|
1056
|
+
amount: "1",
|
|
1057
|
+
}),
|
|
1058
|
+
)
|
|
1059
|
+
.setTimeout(30)
|
|
1060
|
+
.build();
|
|
1061
|
+
|
|
1062
|
+
const uri = Sep7Tx.forTransaction(tx);
|
|
1063
|
+
|
|
1064
|
+
expect(uri.xdr).toMatch(BASE64_ONLY);
|
|
1065
|
+
expect(uri.xdr).not.toContain(",");
|
|
1066
|
+
// The decisive check: it must parse back into the same transaction.
|
|
1067
|
+
expect(uri.getTransaction().toXdr()).toEqual(tx.toXdr());
|
|
1068
|
+
});
|
|
1069
|
+
});
|
package/test/transaction.test.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
});
|
package/test/tsconfig.json
CHANGED
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
|
};
|