@stellar/typescript-wallet-sdk 3.0.3 → 4.0.0-beta.1786156822907
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 +42665 -70994
- package/lib/bundle.js.map +1 -1
- package/lib/bundle_browser.js +33595 -441
- 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/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
|
@@ -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.1786156822907",
|
|
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
|
+
}
|
|
@@ -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
|
+
});
|