@stellar/typescript-wallet-sdk 3.0.3 → 4.0.0-beta.1786560120405
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 +11 -0
- package/jest.e2e.config.js +11 -1
- package/jest.integration.config.js +11 -1
- package/lib/bundle.js +42692 -70966
- package/lib/bundle.js.map +1 -1
- package/lib/bundle_browser.js +33646 -437
- package/lib/bundle_browser.js.map +1 -1
- package/lib/walletSdk/Horizon/Transaction/TransactionBuilder.d.ts +3 -3
- package/package.json +5 -4
- package/src/shims/eventsource.js +45 -0
- package/src/walletSdk/Horizon/AccountService.ts +11 -4
- package/src/walletSdk/Horizon/Stellar.ts +2 -2
- package/src/walletSdk/Horizon/Transaction/TransactionBuilder.ts +16 -13
- package/test/accountService.test.ts +57 -1
- package/test/stellar.test.ts +65 -0
- package/test/toml.test.ts +102 -0
- package/tsconfig.json +1 -0
- package/webpack.config.js +11 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransactionSource, Transaction, Horizon, Memo, xdr } from "@stellar/stellar-sdk";
|
|
2
2
|
import { Config } from "../../";
|
|
3
3
|
import { AccountKeypair } from "../Account";
|
|
4
4
|
import { StellarAssetId } from "../../Asset";
|
|
@@ -17,12 +17,12 @@ export declare class TransactionBuilder extends CommonTransactionBuilder<Transac
|
|
|
17
17
|
* Creates a new instance of the TransactionBuilder class for constructing Stellar transactions.
|
|
18
18
|
* @constructor
|
|
19
19
|
* @param {Config} cfg - Configuration object for Stellar operations.
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {TransactionSource} sourceAccount - The source account for the transaction.
|
|
21
21
|
* @param {number} [baseFee] - The base fee for the transaction. If not given will use the config base fee.
|
|
22
22
|
* @param {Memo} [memo] - The memo for the transaction.
|
|
23
23
|
* @param {Horizon.Server.Timebounds} [timebounds] - The timebounds for the transaction. If not given will use the config timebounds.
|
|
24
24
|
*/
|
|
25
|
-
constructor(cfg: Config, sourceAccount:
|
|
25
|
+
constructor(cfg: Config, sourceAccount: TransactionSource, baseFee?: number, memo?: Memo, timebounds?: Horizon.Server.Timebounds);
|
|
26
26
|
/**
|
|
27
27
|
* Sponsoring a transaction.
|
|
28
28
|
* @param {AccountKeypair} sponsorAccount - The account doing the sponsoring.
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stellar/typescript-wallet-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-beta.1786560120405",
|
|
4
4
|
"engines": {
|
|
5
|
-
"node": ">=
|
|
5
|
+
"node": ">=22"
|
|
6
6
|
},
|
|
7
7
|
"browser": "./lib/bundle_browser.js",
|
|
8
8
|
"main": "./lib/bundle.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"pretty-quick": "^2.0.1",
|
|
36
36
|
"process": "^0.11.10",
|
|
37
37
|
"sinon": "^15.1.0",
|
|
38
|
+
"smol-toml": "^1.7.1",
|
|
38
39
|
"stream-browserify": "^3.0.0",
|
|
39
40
|
"ts-jest": "^29.0.5",
|
|
40
41
|
"ts-loader": "^9.4.2",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"dependencies": {
|
|
48
49
|
"@stablelib/base64": "^2.0.0",
|
|
49
50
|
"@stablelib/utf8": "^2.0.0",
|
|
50
|
-
"@stellar/stellar-sdk": "
|
|
51
|
+
"@stellar/stellar-sdk": "16.2.0",
|
|
51
52
|
"axios": "^1.4.0",
|
|
52
53
|
"base64url": "^3.0.1",
|
|
53
54
|
"https-browserify": "^1.0.0",
|
|
@@ -75,4 +76,4 @@
|
|
|
75
76
|
"example:sep24": "ts-node examples/sep24/sep24.ts",
|
|
76
77
|
"example:sep12": "ts-node examples/sep12/sep12.ts"
|
|
77
78
|
}
|
|
78
|
-
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy `eventsource` facade.
|
|
3
|
+
*
|
|
4
|
+
* eventsource v4 subclasses the DOM globals `Event`/`EventTarget` at module
|
|
5
|
+
* scope, which React Native's Hermes does not provide. Horizon's CallBuilder
|
|
6
|
+
* requires this module eagerly but only reads it inside `stream()`, so deferring
|
|
7
|
+
* evaluation to first property access keeps imports working everywhere while
|
|
8
|
+
* leaving streaming intact wherever those globals exist.
|
|
9
|
+
*
|
|
10
|
+
* Aliased in webpack.config.js: `eventsource$` resolves here, `eventsource-real$`
|
|
11
|
+
* to the real package.
|
|
12
|
+
*/
|
|
13
|
+
let realModule;
|
|
14
|
+
|
|
15
|
+
const load = () => {
|
|
16
|
+
if (!realModule) {
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line global-require
|
|
19
|
+
realModule = require("eventsource-real");
|
|
20
|
+
} catch (cause) {
|
|
21
|
+
// CallBuilder.stream() catches this and retries on a timer, so make the
|
|
22
|
+
// error it reports to onerror self-explanatory.
|
|
23
|
+
throw new Error(
|
|
24
|
+
"Horizon streaming needs the DOM Event/EventTarget globals and a " +
|
|
25
|
+
"streaming fetch body, which React Native does not provide. Alias " +
|
|
26
|
+
'"eventsource" to a native SSE client (e.g. react-native-sse) to use ' +
|
|
27
|
+
`CallBuilder.stream(). Original error: ${cause.message}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return realModule;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
Object.defineProperty(exports, "EventSource", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
get: () => load().EventSource,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
Object.defineProperty(exports, "ErrorEvent", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
get: () => load().ErrorEvent,
|
|
45
|
+
});
|
|
@@ -96,13 +96,20 @@ export class AccountService {
|
|
|
96
96
|
throw new OperationsLimitExceededError(HORIZON_LIMIT_MAX);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
let builder = this.server
|
|
100
100
|
.operations()
|
|
101
101
|
.forAccount(accountAddress)
|
|
102
102
|
.limit(limit)
|
|
103
103
|
.order(order)
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
.includeFailed(includeFailed);
|
|
105
|
+
|
|
106
|
+
// Only set the cursor when provided. In stellar-sdk v16 the call builder
|
|
107
|
+
// serializes query params via URLSearchParams, so passing an undefined
|
|
108
|
+
// cursor would send the literal "cursor=undefined" and Horizon returns 400.
|
|
109
|
+
if (cursor) {
|
|
110
|
+
builder = builder.cursor(cursor);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return builder.call();
|
|
107
114
|
}
|
|
108
115
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
TransactionSource,
|
|
3
3
|
Horizon,
|
|
4
4
|
Transaction,
|
|
5
5
|
TransactionBuilder as StellarTransactionBuilder,
|
|
@@ -75,7 +75,7 @@ export class Stellar {
|
|
|
75
75
|
memo,
|
|
76
76
|
timebounds,
|
|
77
77
|
}: TransactionParams): Promise<TransactionBuilder> {
|
|
78
|
-
let sourceAccount:
|
|
78
|
+
let sourceAccount: TransactionSource;
|
|
79
79
|
try {
|
|
80
80
|
sourceAccount = await this.cfg.stellar.server.loadAccount(
|
|
81
81
|
sourceAddress.publicKey,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Operation,
|
|
3
3
|
TransactionBuilder as StellarTransactionBuilder,
|
|
4
|
-
|
|
4
|
+
TransactionSource,
|
|
5
5
|
Transaction,
|
|
6
6
|
Horizon,
|
|
7
7
|
Memo,
|
|
@@ -40,14 +40,14 @@ export class TransactionBuilder extends CommonTransactionBuilder<TransactionBuil
|
|
|
40
40
|
* Creates a new instance of the TransactionBuilder class for constructing Stellar transactions.
|
|
41
41
|
* @constructor
|
|
42
42
|
* @param {Config} cfg - Configuration object for Stellar operations.
|
|
43
|
-
* @param {
|
|
43
|
+
* @param {TransactionSource} sourceAccount - The source account for the transaction.
|
|
44
44
|
* @param {number} [baseFee] - The base fee for the transaction. If not given will use the config base fee.
|
|
45
45
|
* @param {Memo} [memo] - The memo for the transaction.
|
|
46
46
|
* @param {Horizon.Server.Timebounds} [timebounds] - The timebounds for the transaction. If not given will use the config timebounds.
|
|
47
47
|
*/
|
|
48
48
|
constructor(
|
|
49
49
|
cfg: Config,
|
|
50
|
-
sourceAccount:
|
|
50
|
+
sourceAccount: TransactionSource,
|
|
51
51
|
baseFee?: number,
|
|
52
52
|
memo?: Memo,
|
|
53
53
|
timebounds?: Horizon.Server.Timebounds,
|
|
@@ -266,18 +266,21 @@ export class TransactionBuilder extends CommonTransactionBuilder<TransactionBuil
|
|
|
266
266
|
throw new WithdrawalTxMissingMemoError();
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
// The memo type/value come from the anchor (untrusted input). stellar-sdk
|
|
270
|
+
// validates them when the Memo is constructed and throws on anything
|
|
271
|
+
// malformed, so map any failure to a domain WithdrawalTxMemoError for both
|
|
272
|
+
// the hash and non-hash paths rather than leaking the raw SDK error.
|
|
273
|
+
try {
|
|
274
|
+
if (transaction.withdraw_memo_type === "hash") {
|
|
271
275
|
const buffer = Buffer.from(transaction.withdraw_memo, "base64");
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
this.setMemo(Memo.hash(buffer.toString("hex")));
|
|
277
|
+
} else {
|
|
278
|
+
this.setMemo(
|
|
279
|
+
new Memo(transaction.withdraw_memo_type, transaction.withdraw_memo),
|
|
280
|
+
);
|
|
276
281
|
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
new Memo(transaction.withdraw_memo_type, transaction.withdraw_memo),
|
|
280
|
-
);
|
|
282
|
+
} catch {
|
|
283
|
+
throw new WithdrawalTxMemoError();
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
return this.transfer(
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { Horizon } from "@stellar/stellar-sdk";
|
|
3
|
+
import sinon from "sinon";
|
|
3
4
|
|
|
4
5
|
import { AccountService, SigningKeypair, Wallet } from "../src";
|
|
5
|
-
import { HORIZON_ORDER } from "../src/walletSdk/Types";
|
|
6
|
+
import { HORIZON_ORDER, HORIZON_LIMIT_DEFAULT } from "../src/walletSdk/Types";
|
|
6
7
|
import { IssuedAssetId } from "../src/walletSdk/Asset";
|
|
7
8
|
|
|
8
9
|
let accountService: AccountService;
|
|
@@ -97,3 +98,58 @@ describe("Horizon", () => {
|
|
|
97
98
|
).toBeTruthy();
|
|
98
99
|
});
|
|
99
100
|
});
|
|
101
|
+
|
|
102
|
+
// Hermetic (no network) — kept out of the "Horizon" describe so its
|
|
103
|
+
// friendbot/loadAccount beforeAll does not run.
|
|
104
|
+
describe("AccountService.getHistory cursor handling", () => {
|
|
105
|
+
const accountAddress =
|
|
106
|
+
"GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB";
|
|
107
|
+
let service: AccountService;
|
|
108
|
+
let fakeBuilder: Record<string, sinon.SinonStub>;
|
|
109
|
+
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
service = Wallet.TestNet().stellar().account();
|
|
112
|
+
fakeBuilder = {
|
|
113
|
+
forAccount: sinon.stub(),
|
|
114
|
+
limit: sinon.stub(),
|
|
115
|
+
order: sinon.stub(),
|
|
116
|
+
includeFailed: sinon.stub(),
|
|
117
|
+
cursor: sinon.stub(),
|
|
118
|
+
call: sinon.stub().resolves({ records: [] }),
|
|
119
|
+
};
|
|
120
|
+
// Every chained method returns the builder itself.
|
|
121
|
+
["forAccount", "limit", "order", "includeFailed", "cursor"].forEach((m) =>
|
|
122
|
+
fakeBuilder[m].returns(fakeBuilder),
|
|
123
|
+
);
|
|
124
|
+
sinon
|
|
125
|
+
.stub(
|
|
126
|
+
service["server"] as unknown as { operations: () => unknown },
|
|
127
|
+
"operations",
|
|
128
|
+
)
|
|
129
|
+
.returns(fakeBuilder);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
sinon.restore();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Regression guard for the v16 URLSearchParams change: passing an undefined
|
|
137
|
+
// cursor into the call builder would serialize "cursor=undefined" and Horizon
|
|
138
|
+
// responds 400, so getHistory must omit .cursor() when none is provided.
|
|
139
|
+
it("omits .cursor() when no cursor is provided", async () => {
|
|
140
|
+
await service.getHistory({ accountAddress });
|
|
141
|
+
|
|
142
|
+
expect(fakeBuilder.cursor.called).toBe(false);
|
|
143
|
+
expect(fakeBuilder.call.calledOnce).toBe(true);
|
|
144
|
+
expect(fakeBuilder.limit.calledWith(HORIZON_LIMIT_DEFAULT)).toBe(true);
|
|
145
|
+
expect(fakeBuilder.order.calledWith(HORIZON_ORDER.DESC)).toBe(true);
|
|
146
|
+
expect(fakeBuilder.includeFailed.calledWith(false)).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("applies .cursor(value) when a cursor is provided", async () => {
|
|
150
|
+
await service.getHistory({ accountAddress, cursor: "abc123" });
|
|
151
|
+
|
|
152
|
+
expect(fakeBuilder.cursor.calledOnceWithExactly("abc123")).toBe(true);
|
|
153
|
+
expect(fakeBuilder.call.calledOnce).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
});
|
package/test/stellar.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Account,
|
|
2
3
|
Keypair,
|
|
3
4
|
Memo,
|
|
4
5
|
MemoText,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
import { TransactionStatus, WithdrawTransaction } from "../src/walletSdk/Types";
|
|
23
24
|
import {
|
|
24
25
|
TransactionSubmitWithFeeIncreaseFailedError,
|
|
26
|
+
WithdrawalTxMemoError,
|
|
25
27
|
WithdrawalTxMissingDestinationError,
|
|
26
28
|
WithdrawalTxMissingMemoError,
|
|
27
29
|
WithdrawalTxNotPendingUserTransferStartError,
|
|
@@ -753,3 +755,66 @@ describe("Account Modifying", () => {
|
|
|
753
755
|
expect(parseInt(accResp.balances[0].balance)).toBeGreaterThan(19998);
|
|
754
756
|
}, 30000);
|
|
755
757
|
});
|
|
758
|
+
|
|
759
|
+
// Hermetic (no network): stellar-sdk v16 tightened Memo validation, so a
|
|
760
|
+
// malformed anchor-supplied withdrawal memo now throws when the Memo is
|
|
761
|
+
// constructed. transferWithdrawalTransaction must map that to the domain
|
|
762
|
+
// WithdrawalTxMemoError for every memo type, not just "hash".
|
|
763
|
+
describe("transferWithdrawalTransaction memo validation", () => {
|
|
764
|
+
const localKp = SigningKeypair.fromSecret(
|
|
765
|
+
"SAS372GXRG6U7FW6W2PRVELKPOJG2FZZUADCIELWU2U3A45TNWXEQUV5",
|
|
766
|
+
);
|
|
767
|
+
let stellarLocal: Stellar;
|
|
768
|
+
|
|
769
|
+
beforeEach(() => {
|
|
770
|
+
stellarLocal = Wallet.TestNet().stellar();
|
|
771
|
+
sinon
|
|
772
|
+
.stub(stellarLocal.server, "loadAccount")
|
|
773
|
+
.resolves(new Account(localKp.publicKey, "1") as any);
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
afterEach(() => {
|
|
777
|
+
sinon.restore();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const buildWithdrawal = (memoType: string, memo: string) =>
|
|
781
|
+
({
|
|
782
|
+
status: TransactionStatus.pending_user_transfer_start,
|
|
783
|
+
withdraw_anchor_account: localKp.publicKey,
|
|
784
|
+
withdraw_memo_type: memoType,
|
|
785
|
+
withdraw_memo: memo,
|
|
786
|
+
amount_in: "10",
|
|
787
|
+
} as unknown as WithdrawTransaction);
|
|
788
|
+
|
|
789
|
+
const usdc = () => new IssuedAssetId("USDC", localKp.publicKey);
|
|
790
|
+
|
|
791
|
+
it("maps a malformed hash memo to WithdrawalTxMemoError", async () => {
|
|
792
|
+
const builder = await stellarLocal.transaction({ sourceAddress: localKp });
|
|
793
|
+
expect(() =>
|
|
794
|
+
builder.transferWithdrawalTransaction(
|
|
795
|
+
buildWithdrawal("hash", "not-a-valid-hash"),
|
|
796
|
+
usdc(),
|
|
797
|
+
),
|
|
798
|
+
).toThrow(WithdrawalTxMemoError);
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it("maps a malformed id memo to WithdrawalTxMemoError", async () => {
|
|
802
|
+
const builder = await stellarLocal.transaction({ sourceAddress: localKp });
|
|
803
|
+
expect(() =>
|
|
804
|
+
builder.transferWithdrawalTransaction(
|
|
805
|
+
buildWithdrawal("id", "not-a-number"),
|
|
806
|
+
usdc(),
|
|
807
|
+
),
|
|
808
|
+
).toThrow(WithdrawalTxMemoError);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it("maps an over-long text memo to WithdrawalTxMemoError", async () => {
|
|
812
|
+
const builder = await stellarLocal.transaction({ sourceAddress: localKp });
|
|
813
|
+
expect(() =>
|
|
814
|
+
builder.transferWithdrawalTransaction(
|
|
815
|
+
buildWithdrawal("text", "x".repeat(29)),
|
|
816
|
+
usdc(),
|
|
817
|
+
),
|
|
818
|
+
).toThrow(WithdrawalTxMemoError);
|
|
819
|
+
});
|
|
820
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { StellarToml } from "@stellar/stellar-sdk";
|
|
2
|
+
import { parse } from "smol-toml";
|
|
3
|
+
|
|
4
|
+
import { parseToml } from "../src/walletSdk/Utils/toml";
|
|
5
|
+
|
|
6
|
+
// Hermetic coverage for the SEP-1 TOML path. stellar-sdk v16's
|
|
7
|
+
// StellarToml.Resolver.resolve() fetches the raw text and returns
|
|
8
|
+
// `smol-toml`.parse(text) verbatim (no post-processing), so parsing a fixture
|
|
9
|
+
// string with the same parser here is a faithful, network-free stand-in for
|
|
10
|
+
// the whole pipeline: it exercises v16's strict TOML 1.0 parser and our
|
|
11
|
+
// TomlInfo mapping together.
|
|
12
|
+
const parseTomlString = (toml: string) =>
|
|
13
|
+
parseToml(parse(toml) as unknown as StellarToml.Api.StellarToml);
|
|
14
|
+
|
|
15
|
+
describe("parseToml", () => {
|
|
16
|
+
it("parses and maps a fully populated stellar.toml", () => {
|
|
17
|
+
const toml = `
|
|
18
|
+
VERSION = "2.0.0"
|
|
19
|
+
NETWORK_PASSPHRASE = "Test SDF Network ; September 2015"
|
|
20
|
+
SIGNING_KEY = "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB"
|
|
21
|
+
WEB_AUTH_ENDPOINT = "https://auth.example.com/auth"
|
|
22
|
+
TRANSFER_SERVER = "https://anchor.example.com/sep6"
|
|
23
|
+
TRANSFER_SERVER_SEP0024 = "https://anchor.example.com/sep24"
|
|
24
|
+
KYC_SERVER = "https://anchor.example.com/kyc"
|
|
25
|
+
HORIZON_URL = "https://horizon.example.com"
|
|
26
|
+
ACCOUNTS = ["GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB"]
|
|
27
|
+
|
|
28
|
+
[DOCUMENTATION]
|
|
29
|
+
ORG_NAME = "Example Anchor"
|
|
30
|
+
ORG_URL = "https://example.com"
|
|
31
|
+
|
|
32
|
+
[[CURRENCIES]]
|
|
33
|
+
code = "USDC"
|
|
34
|
+
issuer = "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
|
|
35
|
+
display_decimals = 2
|
|
36
|
+
is_asset_anchored = true
|
|
37
|
+
anchor_asset_type = "fiat"
|
|
38
|
+
collateral_addresses = ["GADDR1"]
|
|
39
|
+
|
|
40
|
+
[[PRINCIPALS]]
|
|
41
|
+
name = "Jane Doe"
|
|
42
|
+
email = "jane@example.com"
|
|
43
|
+
id_photo_hash = "abc123"
|
|
44
|
+
|
|
45
|
+
[[VALIDATORS]]
|
|
46
|
+
PUBLIC_KEY = "GDVALIDATORXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
|
47
|
+
HOST = "core.example.com:11625"
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const info = parseTomlString(toml);
|
|
51
|
+
|
|
52
|
+
expect(info.version).toBe("2.0.0");
|
|
53
|
+
expect(info.networkPassphrase).toBe("Test SDF Network ; September 2015");
|
|
54
|
+
expect(info.webAuthEndpoint).toBe("https://auth.example.com/auth");
|
|
55
|
+
expect(info.transferServer).toBe("https://anchor.example.com/sep6");
|
|
56
|
+
expect(info.transferServerSep24).toBe("https://anchor.example.com/sep24");
|
|
57
|
+
expect(info.signingKey).toBe(
|
|
58
|
+
"GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB",
|
|
59
|
+
);
|
|
60
|
+
expect(info.horizonUrl).toBe("https://horizon.example.com");
|
|
61
|
+
|
|
62
|
+
expect(info.documentation.orgName).toBe("Example Anchor");
|
|
63
|
+
expect(info.documentation.orgUrl).toBe("https://example.com");
|
|
64
|
+
|
|
65
|
+
expect(info.currencies).toHaveLength(1);
|
|
66
|
+
expect(info.currencies[0].code).toBe("USDC");
|
|
67
|
+
// display_decimals is a TOML integer; the mapping preserves the number.
|
|
68
|
+
expect(info.currencies[0].displayDecimals).toBe(2);
|
|
69
|
+
expect(info.currencies[0].isAssetAnchored).toBe(true);
|
|
70
|
+
expect(info.currencies[0].anchorAssetType).toBe("fiat");
|
|
71
|
+
expect(info.currencies[0].collateralAddresses).toEqual(["GADDR1"]);
|
|
72
|
+
|
|
73
|
+
expect(info.principals).toHaveLength(1);
|
|
74
|
+
expect(info.principals[0].name).toBe("Jane Doe");
|
|
75
|
+
expect(info.principals[0].idPhotoHash).toBe("abc123");
|
|
76
|
+
|
|
77
|
+
expect(info.validators).toHaveLength(1);
|
|
78
|
+
expect(info.validators[0].host).toBe("core.example.com:11625");
|
|
79
|
+
expect(info.validators[0].publicKey).toBe(
|
|
80
|
+
"GDVALIDATORXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("defaults optional array sections to [] when absent", () => {
|
|
85
|
+
const info = parseTomlString(`
|
|
86
|
+
[DOCUMENTATION]
|
|
87
|
+
ORG_NAME = "Example Anchor"
|
|
88
|
+
`);
|
|
89
|
+
|
|
90
|
+
expect(info.principals).toEqual([]);
|
|
91
|
+
expect(info.currencies).toEqual([]);
|
|
92
|
+
expect(info.validators).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("rejects malformed TOML (v16 smol-toml strict parsing)", () => {
|
|
96
|
+
// Duplicate keys are invalid under TOML 1.0; smol-toml throws where the
|
|
97
|
+
// previous lenient parser may not have.
|
|
98
|
+
expect(() =>
|
|
99
|
+
parseTomlString(`VERSION = "1"\nVERSION = "2"\n[DOCUMENTATION]\n`),
|
|
100
|
+
).toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
package/tsconfig.json
CHANGED
package/webpack.config.js
CHANGED
|
@@ -19,6 +19,17 @@ module.exports = (env = { NODE: false }) => {
|
|
|
19
19
|
},
|
|
20
20
|
resolve: {
|
|
21
21
|
extensions: [".js", ".json", ".ts"],
|
|
22
|
+
alias: {
|
|
23
|
+
// eventsource v4, reached via stellar-sdk's Horizon CallBuilder,
|
|
24
|
+
// subclasses the DOM globals Event and EventTarget at module scope, so
|
|
25
|
+
// importing this package threw "Property 'Event' doesn't exist" on React
|
|
26
|
+
// Native. Unlike the km and soroban packages, this one exposes
|
|
27
|
+
// Horizon.Server publicly, so stubbing eventsource out would break
|
|
28
|
+
// .stream() on web and Node. Defer its evaluation to first use instead.
|
|
29
|
+
eventsource$: path.resolve(__dirname, "src/shims/eventsource.js"),
|
|
30
|
+
// The facade's load target; "eventsource" would resolve back to itself.
|
|
31
|
+
"eventsource-real$": require.resolve("eventsource"),
|
|
32
|
+
},
|
|
22
33
|
fallback: isBrowser
|
|
23
34
|
? {
|
|
24
35
|
crypto: require.resolve("crypto-browserify"),
|