@stellar/typescript-wallet-sdk 5.1.0 → 6.0.0-beta.1793996806758
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 +14 -0
- package/lib/bundle.js +1801 -435
- package/lib/bundle.js.map +1 -1
- package/lib/bundle_browser.js +1801 -435
- package/lib/bundle_browser.js.map +1 -1
- package/lib/walletSdk/Exceptions/index.d.ts +3 -0
- package/lib/walletSdk/Uri/Sep7Pay.d.ts +10 -0
- package/package.json +3 -3
- package/src/walletSdk/Exceptions/index.ts +9 -0
- package/src/walletSdk/Uri/Sep7Pay.ts +42 -1
- package/src/walletSdk/Uri/sep7Parser.ts +18 -2
- package/test/accountService.test.ts +3 -2
- package/test/auth.test.ts +51 -0
- package/test/e2e/reactNative.test.ts +112 -1
- package/test/fixtures/muxedContract.ts +47 -0
- package/test/integration/anchorplatform.test.ts +2 -1
- package/test/integration/recovery.test.ts +10 -10
- package/test/sep6.test.ts +2 -1
- package/test/sep7.test.ts +219 -2
- package/test/server.test.ts +39 -0
- package/test/stellar.test.ts +35 -15
- package/test/transaction.test.ts +43 -0
- package/test/wallet.test.ts +2 -1
|
@@ -172,6 +172,9 @@ export declare class Sep7InvalidUriError extends Error {
|
|
|
172
172
|
export declare class Sep7LongMsgError extends Error {
|
|
173
173
|
constructor(msgMaxLength: number);
|
|
174
174
|
}
|
|
175
|
+
export declare class Sep7ContractMemoError extends Error {
|
|
176
|
+
constructor();
|
|
177
|
+
}
|
|
175
178
|
export declare class Sep7UriTypeNotSupportedError extends Error {
|
|
176
179
|
constructor(type: string);
|
|
177
180
|
}
|
|
@@ -20,6 +20,8 @@ export declare class Sep7Pay extends Sep7Base {
|
|
|
20
20
|
*
|
|
21
21
|
* @constructor
|
|
22
22
|
* @param {URL | string} [uri] - uri to initialize the Sep7 instance.
|
|
23
|
+
* @throws {Sep7ContractMemoError} if 'uri' has a memo and a contract ("C...")
|
|
24
|
+
* or muxed contract ("W...") destination.
|
|
23
25
|
*/
|
|
24
26
|
constructor(uri?: URL | string);
|
|
25
27
|
/**
|
|
@@ -32,6 +34,10 @@ export declare class Sep7Pay extends Sep7Base {
|
|
|
32
34
|
* Gets the destination of the payment request, which should be a valid
|
|
33
35
|
* Stellar address.
|
|
34
36
|
*
|
|
37
|
+
* A contract ("C...") or muxed contract ("W...") destination can only be
|
|
38
|
+
* paid with a Stellar Asset Contract transfer, not a classic payment, and a
|
|
39
|
+
* uri with one can't have a memo.
|
|
40
|
+
*
|
|
35
41
|
* @returns {string | undefined} the 'destination' uri param if present.
|
|
36
42
|
*/
|
|
37
43
|
get destination(): string | undefined;
|
|
@@ -42,6 +48,8 @@ export declare class Sep7Pay extends Sep7Base {
|
|
|
42
48
|
* Deletes the uri 'destination' param if set as 'undefined'.
|
|
43
49
|
*
|
|
44
50
|
* @param {string | undefined} destination the uri 'destination' param to be set.
|
|
51
|
+
* @throws {Sep7ContractMemoError} if 'destination' is a contract ("C...") or
|
|
52
|
+
* muxed contract ("W...") address and the uri has a memo.
|
|
45
53
|
*/
|
|
46
54
|
set destination(destination: string | undefined);
|
|
47
55
|
/**
|
|
@@ -102,6 +110,8 @@ export declare class Sep7Pay extends Sep7Base {
|
|
|
102
110
|
* Deletes the uri 'memo' param if set as 'undefined'.
|
|
103
111
|
*
|
|
104
112
|
* @param {string | undefined} memo the uri 'memo' param to be set.
|
|
113
|
+
* @throws {Sep7ContractMemoError} if 'memo' is set and the uri's destination
|
|
114
|
+
* is a contract ("C...") or muxed contract ("W...") address.
|
|
105
115
|
*/
|
|
106
116
|
set memo(memo: string | undefined);
|
|
107
117
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stellar/typescript-wallet-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-beta.1793996806758",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22.12.0"
|
|
6
6
|
},
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@stablelib/base64": "^2.0.0",
|
|
51
51
|
"@stablelib/utf8": "^2.0.0",
|
|
52
|
-
"@stellar/stellar-sdk": "17.0
|
|
52
|
+
"@stellar/stellar-sdk": "17.4.0",
|
|
53
53
|
"axios": "^1.4.0",
|
|
54
54
|
"https-browserify": "^1.0.0",
|
|
55
55
|
"jws": "^4.0.0",
|
|
@@ -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
|
+
}
|
|
@@ -474,6 +474,15 @@ export class Sep7LongMsgError extends Error {
|
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
export class Sep7ContractMemoError extends Error {
|
|
478
|
+
constructor() {
|
|
479
|
+
super(
|
|
480
|
+
"the 'memo' parameter is not supported for a contract ('C') or muxed contract ('W') 'destination'",
|
|
481
|
+
);
|
|
482
|
+
Object.setPrototypeOf(this, Sep7ContractMemoError.prototype);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
477
486
|
export class Sep7UriTypeNotSupportedError extends Error {
|
|
478
487
|
constructor(type: string) {
|
|
479
488
|
super(
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
-
import { MemoType } from "@stellar/stellar-sdk";
|
|
1
|
+
import { MemoType, StrKey } from "@stellar/stellar-sdk";
|
|
2
2
|
import { Sep7Base } from "../Uri";
|
|
3
3
|
import { Sep7OperationType, WEB_STELLAR_SCHEME } from "../Types";
|
|
4
|
+
import { Sep7ContractMemoError } from "../Exceptions";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Throws if a uri would have a memo with a contract ("C...") or muxed
|
|
8
|
+
* contract ("W...") destination, which 'parseSep7Uri' rejects. Paying either
|
|
9
|
+
* takes a Soroban transaction, which can't have a memo.
|
|
10
|
+
*
|
|
11
|
+
* @param {string | undefined} destination the uri 'destination' param.
|
|
12
|
+
* @param {string | undefined} memo the uri 'memo' param.
|
|
13
|
+
* @throws {Sep7ContractMemoError} if both are set and 'destination' is a
|
|
14
|
+
* contract or muxed contract address.
|
|
15
|
+
*/
|
|
16
|
+
const assertNoContractMemo = (
|
|
17
|
+
destination: string | undefined,
|
|
18
|
+
memo: string | undefined,
|
|
19
|
+
) => {
|
|
20
|
+
if (
|
|
21
|
+
memo &&
|
|
22
|
+
destination &&
|
|
23
|
+
(StrKey.isValidContract(destination) ||
|
|
24
|
+
StrKey.isValidMuxedContract(destination))
|
|
25
|
+
) {
|
|
26
|
+
throw new Sep7ContractMemoError();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
4
29
|
|
|
5
30
|
/**
|
|
6
31
|
* The Sep-7 'pay' operation represents a request to pay a specific address
|
|
@@ -27,9 +52,13 @@ export class Sep7Pay extends Sep7Base {
|
|
|
27
52
|
*
|
|
28
53
|
* @constructor
|
|
29
54
|
* @param {URL | string} [uri] - uri to initialize the Sep7 instance.
|
|
55
|
+
* @throws {Sep7ContractMemoError} if 'uri' has a memo and a contract ("C...")
|
|
56
|
+
* or muxed contract ("W...") destination.
|
|
30
57
|
*/
|
|
31
58
|
constructor(uri?: URL | string) {
|
|
32
59
|
super(uri ?? new URL(`${WEB_STELLAR_SCHEME}${Sep7OperationType.pay}`));
|
|
60
|
+
|
|
61
|
+
assertNoContractMemo(this.destination, this.memo);
|
|
33
62
|
}
|
|
34
63
|
|
|
35
64
|
/**
|
|
@@ -45,6 +74,10 @@ export class Sep7Pay extends Sep7Base {
|
|
|
45
74
|
* Gets the destination of the payment request, which should be a valid
|
|
46
75
|
* Stellar address.
|
|
47
76
|
*
|
|
77
|
+
* A contract ("C...") or muxed contract ("W...") destination can only be
|
|
78
|
+
* paid with a Stellar Asset Contract transfer, not a classic payment, and a
|
|
79
|
+
* uri with one can't have a memo.
|
|
80
|
+
*
|
|
48
81
|
* @returns {string | undefined} the 'destination' uri param if present.
|
|
49
82
|
*/
|
|
50
83
|
get destination(): string | undefined {
|
|
@@ -58,8 +91,12 @@ export class Sep7Pay extends Sep7Base {
|
|
|
58
91
|
* Deletes the uri 'destination' param if set as 'undefined'.
|
|
59
92
|
*
|
|
60
93
|
* @param {string | undefined} destination the uri 'destination' param to be set.
|
|
94
|
+
* @throws {Sep7ContractMemoError} if 'destination' is a contract ("C...") or
|
|
95
|
+
* muxed contract ("W...") address and the uri has a memo.
|
|
61
96
|
*/
|
|
62
97
|
set destination(destination: string | undefined) {
|
|
98
|
+
assertNoContractMemo(destination, this.memo);
|
|
99
|
+
|
|
63
100
|
this.setParam("destination", destination);
|
|
64
101
|
}
|
|
65
102
|
|
|
@@ -142,8 +179,12 @@ export class Sep7Pay extends Sep7Base {
|
|
|
142
179
|
* Deletes the uri 'memo' param if set as 'undefined'.
|
|
143
180
|
*
|
|
144
181
|
* @param {string | undefined} memo the uri 'memo' param to be set.
|
|
182
|
+
* @throws {Sep7ContractMemoError} if 'memo' is set and the uri's destination
|
|
183
|
+
* is a contract ("C...") or muxed contract ("W...") address.
|
|
145
184
|
*/
|
|
146
185
|
set memo(memo: string | undefined) {
|
|
186
|
+
assertNoContractMemo(this.destination, memo);
|
|
187
|
+
|
|
147
188
|
this.setParam("memo", memo);
|
|
148
189
|
}
|
|
149
190
|
|
|
@@ -78,11 +78,15 @@ export const isValidSep7Uri = (uri: string): IsValidSep7UriResult => {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (type === Sep7OperationType.pay && destination) {
|
|
81
|
-
|
|
81
|
+
const isContract =
|
|
82
|
+
StrKey.isValidContract(destination) ||
|
|
83
|
+
StrKey.isValidMuxedContract(destination);
|
|
84
|
+
|
|
85
|
+
// Checks if it's a valid "G", "M", "C" or "W" Stellar address
|
|
82
86
|
const isValidStellarAddress =
|
|
83
87
|
StrKey.isValidEd25519PublicKey(destination) ||
|
|
84
88
|
StrKey.isValidMed25519PublicKey(destination) ||
|
|
85
|
-
|
|
89
|
+
isContract;
|
|
86
90
|
|
|
87
91
|
if (!isValidStellarAddress) {
|
|
88
92
|
return {
|
|
@@ -91,6 +95,18 @@ export const isValidSep7Uri = (uri: string): IsValidSep7UriResult => {
|
|
|
91
95
|
"the provided 'destination' parameter is not a valid Stellar address",
|
|
92
96
|
};
|
|
93
97
|
}
|
|
98
|
+
|
|
99
|
+
// A "C" (contract) or "W" (muxed contract) address can only be paid with
|
|
100
|
+
// a Stellar Asset Contract transfer. That's a Soroban transaction, which
|
|
101
|
+
// can't have a memo, so a wallet could only drop it. A "W" address's mux
|
|
102
|
+
// ID already identifies the payee.
|
|
103
|
+
if (isContract && url.searchParams.get("memo")) {
|
|
104
|
+
return {
|
|
105
|
+
result: false,
|
|
106
|
+
reason:
|
|
107
|
+
"the 'memo' parameter is not supported for a contract ('C') or muxed contract ('W') 'destination'",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
|
|
96
112
|
if (msg?.length > URI_MSG_MAX_LENGTH) {
|
|
@@ -11,7 +11,8 @@ let accountAddress: string;
|
|
|
11
11
|
|
|
12
12
|
describe("Horizon", () => {
|
|
13
13
|
// Creates testing stellar account with USDC trustline
|
|
14
|
-
// in case it doesn't exist just yet
|
|
14
|
+
// in case it doesn't exist just yet. One friendbot call can take 25 s or
|
|
15
|
+
// more on testnet, so this allows 90 s.
|
|
15
16
|
beforeAll(async () => {
|
|
16
17
|
const wallet = Wallet.TestNet();
|
|
17
18
|
const stellar = wallet.stellar();
|
|
@@ -46,7 +47,7 @@ describe("Horizon", () => {
|
|
|
46
47
|
await stellar.submitTransaction(addUsdcTx);
|
|
47
48
|
|
|
48
49
|
accountAddress = testingAccountKp.publicKey;
|
|
49
|
-
},
|
|
50
|
+
}, 90000);
|
|
50
51
|
|
|
51
52
|
it("should return stellar account details", async () => {
|
|
52
53
|
const response = await accountService.getInfo({ accountAddress });
|
package/test/auth.test.ts
CHANGED
|
@@ -726,6 +726,57 @@ describe("Sep10 challenge validation", () => {
|
|
|
726
726
|
});
|
|
727
727
|
});
|
|
728
728
|
|
|
729
|
+
// WebAuth.readChallengeTx checks these since stellar-sdk 17.3. SEP-10
|
|
730
|
+
// requires the client_domain operation's source to be the client domain's
|
|
731
|
+
// signing key.
|
|
732
|
+
describe("client_domain operation source", () => {
|
|
733
|
+
it.each([
|
|
734
|
+
["has no source account", "none"],
|
|
735
|
+
["uses the server account as its source", "server"],
|
|
736
|
+
])(
|
|
737
|
+
"should reject a challenge whose client_domain operation %s",
|
|
738
|
+
async (_, clientDomainSource) => {
|
|
739
|
+
const serverKeypair = Keypair.random();
|
|
740
|
+
const clientKeypair = Keypair.random();
|
|
741
|
+
const { xdr } = buildChallenge({
|
|
742
|
+
serverKeypair,
|
|
743
|
+
clientKeypair,
|
|
744
|
+
additionalOps: [
|
|
745
|
+
Operation.manageData({
|
|
746
|
+
name: "client_domain",
|
|
747
|
+
value: "wallet.example.com",
|
|
748
|
+
source:
|
|
749
|
+
clientDomainSource === "server"
|
|
750
|
+
? serverKeypair.publicKey()
|
|
751
|
+
: undefined,
|
|
752
|
+
}),
|
|
753
|
+
],
|
|
754
|
+
});
|
|
755
|
+
const { sep10, postStub } = setupSep10({
|
|
756
|
+
serverSigningKey: serverKeypair.publicKey(),
|
|
757
|
+
challengeXdr: xdr,
|
|
758
|
+
token: createJwt(clientKeypair),
|
|
759
|
+
});
|
|
760
|
+
const signWithDomainAccount = sinon
|
|
761
|
+
.stub()
|
|
762
|
+
.rejects(new Error("the challenge should be rejected first"));
|
|
763
|
+
|
|
764
|
+
const result = sep10.authenticate({
|
|
765
|
+
accountKp: SigningKeypair.fromSecret(clientKeypair.secret()),
|
|
766
|
+
clientDomain: "wallet.example.com",
|
|
767
|
+
walletSigner: {
|
|
768
|
+
signWithClientAccount: ({ transaction }) => transaction,
|
|
769
|
+
signWithDomainAccount,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
await expect(result).rejects.toThrow(ChallengeValidationFailedError);
|
|
773
|
+
await expect(result).rejects.toThrow(/client_domain/);
|
|
774
|
+
expect(signWithDomainAccount.notCalled).toBe(true);
|
|
775
|
+
expect(postStub.notCalled).toBe(true);
|
|
776
|
+
},
|
|
777
|
+
);
|
|
778
|
+
});
|
|
779
|
+
|
|
729
780
|
describe("network passphrase mismatch", () => {
|
|
730
781
|
it("should reject when server returns a different network passphrase", async () => {
|
|
731
782
|
const { xdr, serverKeypair, clientKeypair } = buildChallenge();
|
|
@@ -19,9 +19,22 @@ import * as vm from "vm";
|
|
|
19
19
|
*/
|
|
20
20
|
const BANNED = ["Buffer", "Event", "EventTarget", "process"];
|
|
21
21
|
|
|
22
|
+
// Behaves like fast-text-encoding 1.0.6, a TextDecoder that React Native apps
|
|
23
|
+
// commonly install on Hermes: its constructor throws for `{ fatal: true }`.
|
|
24
|
+
const NonFatalTextDecoder = function (
|
|
25
|
+
label?: string,
|
|
26
|
+
options?: TextDecoderOptions,
|
|
27
|
+
) {
|
|
28
|
+
if (options?.fatal) {
|
|
29
|
+
throw new RangeError("the 'fatal' option is unsupported");
|
|
30
|
+
}
|
|
31
|
+
return new TextDecoder(label, options);
|
|
32
|
+
} as unknown as typeof TextDecoder;
|
|
33
|
+
|
|
22
34
|
const makeHermesLikeContext = ({
|
|
23
35
|
includeBase64 = true,
|
|
24
36
|
includeTextCodec = true,
|
|
37
|
+
textDecoder = TextDecoder,
|
|
25
38
|
} = {}) => {
|
|
26
39
|
const sandbox: Record<string, unknown> = {
|
|
27
40
|
console,
|
|
@@ -56,7 +69,7 @@ const makeHermesLikeContext = ({
|
|
|
56
69
|
// "TypeError: globalThis.TextDecoder is not a constructor" at load —
|
|
57
70
|
// before any of our own code runs. See the test below that enforces this.
|
|
58
71
|
sandbox.TextEncoder = TextEncoder;
|
|
59
|
-
sandbox.TextDecoder =
|
|
72
|
+
sandbox.TextDecoder = textDecoder;
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
if (includeBase64) {
|
|
@@ -126,6 +139,25 @@ describe("React Native (Hermes-like) bundle compatibility", () => {
|
|
|
126
139
|
});
|
|
127
140
|
}
|
|
128
141
|
|
|
142
|
+
for (const [name, relativePath] of bundles) {
|
|
143
|
+
it(`loads the ${name} bundle with a TextDecoder that can't decode strictly`, () => {
|
|
144
|
+
// stellar-sdk 17.3+ builds a strict (`{ fatal: true }`) TextDecoder only
|
|
145
|
+
// when it decodes something: a stellar.toml or federation response, or
|
|
146
|
+
// an XdrString read with toStringStrict(). Loading a bundle must not
|
|
147
|
+
// need one, or an app whose TextDecoder can't decode strictly would
|
|
148
|
+
// crash on startup instead of only on those calls.
|
|
149
|
+
const bundlePath = path.resolve(__dirname, relativePath);
|
|
150
|
+
const context = makeHermesLikeContext({
|
|
151
|
+
textDecoder: NonFatalTextDecoder,
|
|
152
|
+
});
|
|
153
|
+
const code = fs.readFileSync(bundlePath, "utf8");
|
|
154
|
+
|
|
155
|
+
expect(() =>
|
|
156
|
+
vm.runInContext(code, context, { filename: `${name}.js` }),
|
|
157
|
+
).not.toThrow();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
it("exercises the core SDK public API in the sandbox", () => {
|
|
130
162
|
const context = makeHermesLikeContext();
|
|
131
163
|
const code = fs.readFileSync(
|
|
@@ -154,6 +186,85 @@ describe("React Native (Hermes-like) bundle compatibility", () => {
|
|
|
154
186
|
expect(result.signature).toHaveLength(88);
|
|
155
187
|
});
|
|
156
188
|
|
|
189
|
+
it("verifies a SEP-7 signature only with a TextDecoder that decodes strictly", async () => {
|
|
190
|
+
// Verifying reads the origin domain's stellar.toml, and stellar-sdk 17.3+
|
|
191
|
+
// decodes stellar.toml and federation responses with a strict
|
|
192
|
+
// (`{ fatal: true }`) TextDecoder. With a TextDecoder that can't build
|
|
193
|
+
// one, verifySignature() fails closed and returns false for a valid
|
|
194
|
+
// signature, and Anchor.sep1() rejects.
|
|
195
|
+
const verifyWith = (textDecoder: typeof TextDecoder) => {
|
|
196
|
+
const context = makeHermesLikeContext({ textDecoder });
|
|
197
|
+
// React Native provides these, and the SDK's stellar.toml fetch uses
|
|
198
|
+
// them. The fetch stub serves the toml that the code below writes.
|
|
199
|
+
Object.assign(context, {
|
|
200
|
+
Headers,
|
|
201
|
+
AbortController,
|
|
202
|
+
AbortSignal,
|
|
203
|
+
fetch: () => Promise.resolve(new Response(context.stellarToml)),
|
|
204
|
+
});
|
|
205
|
+
const code = fs.readFileSync(
|
|
206
|
+
path.resolve(__dirname, "../../lib/bundle_browser.js"),
|
|
207
|
+
"utf8",
|
|
208
|
+
);
|
|
209
|
+
vm.runInContext(code, context, { filename: "bundle_browser.js" });
|
|
210
|
+
|
|
211
|
+
return vm.runInContext(
|
|
212
|
+
`(() => {
|
|
213
|
+
const { Wallet, Sep7Pay } = module.exports;
|
|
214
|
+
const kp = Wallet.TestNet().stellar().account().createKeypair();
|
|
215
|
+
globalThis.stellarToml = 'URI_REQUEST_SIGNING_KEY="' + kp.publicKey + '"';
|
|
216
|
+
const uri = Sep7Pay.forDestination(kp.publicKey);
|
|
217
|
+
uri.originDomain = "example.com";
|
|
218
|
+
uri.addSignature(kp.keypair);
|
|
219
|
+
return uri.verifySignature();
|
|
220
|
+
})()`,
|
|
221
|
+
context,
|
|
222
|
+
) as Promise<boolean>;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
expect(await verifyWith(TextDecoder)).toBe(true);
|
|
226
|
+
expect(await verifyWith(NonFatalTextDecoder)).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("reads an anchor's stellar.toml only with a TextDecoder that decodes strictly", async () => {
|
|
230
|
+
// Every anchor flow starts from Anchor.sep1(), which reads the anchor's
|
|
231
|
+
// stellar.toml. With a TextDecoder that can't decode strictly, it rejects.
|
|
232
|
+
const sep1With = (textDecoder: typeof TextDecoder) => {
|
|
233
|
+
const context = makeHermesLikeContext({ textDecoder });
|
|
234
|
+
// React Native defines `window` as the global object, and
|
|
235
|
+
// Wallet.anchor() reads `window.URL` through it.
|
|
236
|
+
Object.assign(context, {
|
|
237
|
+
window: context,
|
|
238
|
+
Headers,
|
|
239
|
+
AbortController,
|
|
240
|
+
AbortSignal,
|
|
241
|
+
fetch: () =>
|
|
242
|
+
Promise.resolve(
|
|
243
|
+
new Response(
|
|
244
|
+
'WEB_AUTH_ENDPOINT="https://example.com/auth"\n[DOCUMENTATION]\nORG_NAME="Example"\n',
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
});
|
|
248
|
+
const code = fs.readFileSync(
|
|
249
|
+
path.resolve(__dirname, "../../lib/bundle_browser.js"),
|
|
250
|
+
"utf8",
|
|
251
|
+
);
|
|
252
|
+
vm.runInContext(code, context, { filename: "bundle_browser.js" });
|
|
253
|
+
|
|
254
|
+
return vm.runInContext(
|
|
255
|
+
`module.exports.Wallet.TestNet().anchor({ homeDomain: "example.com" }).sep1()`,
|
|
256
|
+
context,
|
|
257
|
+
) as Promise<{ webAuthEndpoint: string }>;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
expect((await sep1With(TextDecoder)).webAuthEndpoint).toBe(
|
|
261
|
+
"https://example.com/auth",
|
|
262
|
+
);
|
|
263
|
+
await expect(sep1With(NonFatalTextDecoder)).rejects.toThrow(
|
|
264
|
+
"the 'fatal' option is unsupported",
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
157
268
|
it("requires btoa/atob for base64 paths, while base32/StrKey keypair creation stays pure JS", () => {
|
|
158
269
|
const context = makeHermesLikeContext({ includeBase64: false });
|
|
159
270
|
const code = fs.readFileSync(
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Address, Operation, Transaction } from "@stellar/stellar-sdk";
|
|
2
|
+
|
|
3
|
+
// Protocol 30 (CAP-84) muxed contract addresses: a contract ID plus a uint64
|
|
4
|
+
// mux ID, encoded as a "W..." strkey.
|
|
5
|
+
|
|
6
|
+
// SEP-23's test vector: this contract with mux ID 0.
|
|
7
|
+
export const SEP23_CONTRACT =
|
|
8
|
+
"CA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA";
|
|
9
|
+
export const SEP23_MUXED_CONTRACT =
|
|
10
|
+
"WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWC";
|
|
11
|
+
|
|
12
|
+
// The same contract with the largest mux ID, 2^64 - 1, which a JS number
|
|
13
|
+
// can't hold exactly.
|
|
14
|
+
export const MUXED_CONTRACT_MAX_ID =
|
|
15
|
+
"WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJV7777777777777GMO";
|
|
16
|
+
|
|
17
|
+
// SEP23_MUXED_CONTRACT with its last character changed, so the checksum
|
|
18
|
+
// doesn't match.
|
|
19
|
+
export const MUXED_CONTRACT_BAD_CHECKSUM =
|
|
20
|
+
"WA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAAAWWA";
|
|
21
|
+
|
|
22
|
+
export const TRANSFER_SOURCE =
|
|
23
|
+
"GDVEU3DD4KOFECV66VIHWEZOYX4ZKR3WV27L464SIIPOU2IUI3JCZA57";
|
|
24
|
+
|
|
25
|
+
// The native XLM Stellar Asset Contract on testnet.
|
|
26
|
+
export const TESTNET_NATIVE_SAC =
|
|
27
|
+
"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
|
|
28
|
+
|
|
29
|
+
// An unsigned testnet transaction with one operation: TESTNET_NATIVE_SAC's
|
|
30
|
+
// transfer(TRANSFER_SOURCE, MUXED_CONTRACT_MAX_ID, 12345). Its only Protocol 30
|
|
31
|
+
// part is the `to` argument, an ScAddress of type SC_ADDRESS_TYPE_MUXED_CONTRACT.
|
|
32
|
+
export const TRANSFER_TO_MUXED_CONTRACT_XDR =
|
|
33
|
+
"AAAAAgAAAADqSmxj4pxSCr71UHsTLsX5lUd2rr6+e5JCHuppFEbSLAAAAGQAAAAAAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAAB15KLcsJwPM/q9+uf9O9NUEpVqLl5/JtFDqLIQrTRzmEAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAA6kpsY+KcUgq+9VB7Ey7F+ZVHdq6+vnuSQh7qaRRG0iwAAAASAAAABf//////////Pww0v5OtDZlx0EzMkPcFURyDiq2XNKSi+w16A/x/6JoAAAAKAAAAAAAAAAAAAAAAAAAwOQAAAAAAAAAAAAAAAA==";
|
|
34
|
+
|
|
35
|
+
// Reads the `to` argument of the transfer in a transaction like
|
|
36
|
+
// TRANSFER_TO_MUXED_CONTRACT_XDR, as a strkey.
|
|
37
|
+
export const transferRecipient = (tx: Transaction): string => {
|
|
38
|
+
const { func } = tx.operations[0] as Operation.InvokeHostFunction;
|
|
39
|
+
if (func.type !== "hostFunctionTypeInvokeContract") {
|
|
40
|
+
throw new Error(`expected a contract invocation, not ${func.type}`);
|
|
41
|
+
}
|
|
42
|
+
const to = func.invokeContract.args[1];
|
|
43
|
+
if (to.type !== "scvAddress") {
|
|
44
|
+
throw new Error(`expected an address, not ${to.type}`);
|
|
45
|
+
}
|
|
46
|
+
return Address.fromScAddress(to.address).toString();
|
|
47
|
+
};
|
|
@@ -9,6 +9,7 @@ let accountKp;
|
|
|
9
9
|
const anchorUrl = "anchor-sep-server-dev.stellar.org";
|
|
10
10
|
|
|
11
11
|
describe("Anchor Platform Integration Tests", () => {
|
|
12
|
+
// One friendbot call can take 25 s or more on testnet, so this allows 90 s.
|
|
12
13
|
beforeAll(async () => {
|
|
13
14
|
// Setup
|
|
14
15
|
wallet = Wallet.TestNet();
|
|
@@ -16,7 +17,7 @@ describe("Anchor Platform Integration Tests", () => {
|
|
|
16
17
|
anchor = wallet.anchor({ homeDomain: anchorUrl });
|
|
17
18
|
accountKp = stellar.account().createKeypair();
|
|
18
19
|
await stellar.fundTestnetAccount(accountKp.publicKey);
|
|
19
|
-
},
|
|
20
|
+
}, 90000);
|
|
20
21
|
|
|
21
22
|
it("SEP-10 auth should work", async () => {
|
|
22
23
|
const auth = await anchor.sep10();
|
|
@@ -51,15 +51,15 @@ describe("Recovery Integration Tests", () => {
|
|
|
51
51
|
await stellar.server.loadAccount(deviceKp.publicKey);
|
|
52
52
|
await stellar.server.loadAccount(recoveryKp.publicKey);
|
|
53
53
|
} catch (e) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"https://friendbot.stellar.org/?addr=" + deviceKp.publicKey,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
);
|
|
54
|
+
// One friendbot call can take 25 s or more on testnet, so these run in
|
|
55
|
+
// parallel and the test allows 120 s.
|
|
56
|
+
await Promise.all([
|
|
57
|
+
axios.get("https://friendbot.stellar.org/?addr=" + accountKp.publicKey),
|
|
58
|
+
axios.get("https://friendbot.stellar.org/?addr=" + deviceKp.publicKey),
|
|
59
|
+
axios.get(
|
|
60
|
+
"https://friendbot.stellar.org/?addr=" + recoveryKp.publicKey,
|
|
61
|
+
),
|
|
62
|
+
]);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Create SEP-30 identities
|
|
@@ -164,5 +164,5 @@ describe("Recovery Integration Tests", () => {
|
|
|
164
164
|
expect(resp.signers.find((obj) => obj.key === newKp.publicKey).weight).toBe(
|
|
165
165
|
10,
|
|
166
166
|
);
|
|
167
|
-
},
|
|
167
|
+
}, 120000);
|
|
168
168
|
});
|
package/test/sep6.test.ts
CHANGED
|
@@ -7,6 +7,7 @@ let sep6;
|
|
|
7
7
|
let accountKp;
|
|
8
8
|
|
|
9
9
|
describe("SEP-6", () => {
|
|
10
|
+
// One friendbot call can take 25 s or more on testnet, so this allows 90 s.
|
|
10
11
|
beforeAll(async () => {
|
|
11
12
|
wallet = Wallet.TestNet();
|
|
12
13
|
anchor = wallet.anchor({ homeDomain: "testanchor.stellar.org" });
|
|
@@ -16,7 +17,7 @@ describe("SEP-6", () => {
|
|
|
16
17
|
await axios.get(
|
|
17
18
|
"https://friendbot.stellar.org/?addr=" + accountKp.publicKey,
|
|
18
19
|
);
|
|
19
|
-
},
|
|
20
|
+
}, 90000);
|
|
20
21
|
|
|
21
22
|
it("should get anchor info", async () => {
|
|
22
23
|
const resp = await sep6.info();
|