@dynamic-labs/stellar 4.65.0 → 4.67.0

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 CHANGED
@@ -1,4 +1,36 @@
1
1
 
2
+ ## [4.67.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.66.0...v4.67.0) (2026-03-09)
3
+
4
+
5
+ ### Features
6
+
7
+ * add MFA methods to useStepUpAuthentication hook ([#10581](https://github.com/dynamic-labs/dynamic-auth/issues/10581)) ([7903290](https://github.com/dynamic-labs/dynamic-auth/commit/79032906d548ba2d4bcdf69cd9e6eb820064e408)), closes [dynamic-labs/dynamic-js-sdk#1061](https://github.com/dynamic-labs/dynamic-js-sdk/issues/1061) [dynamic-labs/dynamic-js-sdk#1062](https://github.com/dynamic-labs/dynamic-js-sdk/issues/1062)
8
+ * **demo:** add Passkey MFA and TOTP MFA step-up auth demos ([#10582](https://github.com/dynamic-labs/dynamic-auth/issues/10582)) ([92b9e28](https://github.com/dynamic-labs/dynamic-auth/commit/92b9e28e62084031a8bca11216aab39198948e82)), closes [#10581](https://github.com/dynamic-labs/dynamic-auth/issues/10581)
9
+ * **demo:** add Step-up Auth section ([#10483](https://github.com/dynamic-labs/dynamic-auth/issues/10483)) ([4bfff9a](https://github.com/dynamic-labs/dynamic-auth/commit/4bfff9a0fe475b4d8dbb6b8c4fccebb6550aa06f))
10
+ * **react-native:** add minifiedToken support to authModule ([#10595](https://github.com/dynamic-labs/dynamic-auth/issues/10595)) ([6b6e067](https://github.com/dynamic-labs/dynamic-auth/commit/6b6e067e62b97f573a31140967f3a07064e9f3fd))
11
+ * **stellar:** add account activation handling for unfunded recipients ([#10578](https://github.com/dynamic-labs/dynamic-auth/issues/10578)) ([ea5ef7b](https://github.com/dynamic-labs/dynamic-auth/commit/ea5ef7bfce7cf984ff091ea7d6321814dc3eea81))
12
+ * update demo CTA buttons - 'Book a call' and 'Get a free account' ([#10589](https://github.com/dynamic-labs/dynamic-auth/issues/10589)) ([e7d967c](https://github.com/dynamic-labs/dynamic-auth/commit/e7d967c59c5ad616348d9a51ea4bd314ec1ddd9f))
13
+ * **waas:** add password support to upgradeToDynamicWaas ([#10606](https://github.com/dynamic-labs/dynamic-auth/issues/10606)) ([d998a3e](https://github.com/dynamic-labs/dynamic-auth/commit/d998a3e955110f419dc40c4aeaddd93834957eb7))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * add overflow-y auto to backup codes view to prevent Complete button cutoff ([#10513](https://github.com/dynamic-labs/dynamic-auth/issues/10513)) ([5dfa94e](https://github.com/dynamic-labs/dynamic-auth/commit/5dfa94eaa232535f2e8c6905370dd8d8546485e3))
19
+ * prevent infinite re-render loop in UserPhoneField due to rapid t… ([#10475](https://github.com/dynamic-labs/dynamic-auth/issues/10475)) ([0d79b07](https://github.com/dynamic-labs/dynamic-auth/commit/0d79b0782606c9fa926ed4697a17381211ad2f4a))
20
+
21
+ ## [4.66.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.65.0...v4.66.0) (2026-03-02)
22
+
23
+
24
+ ### Features
25
+
26
+ * **stellar:** send serialized XDR for transaction signing ([#10548](https://github.com/dynamic-labs/dynamic-auth/issues/10548)) ([3ff6438](https://github.com/dynamic-labs/dynamic-auth/commit/3ff64384e16d5fdb32cd6a3fd144757bf05f4456))
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * prevent MFA backup codes screen from being dismissed before acknowledgement ([#10530](https://github.com/dynamic-labs/dynamic-auth/issues/10530)) ([67086f0](https://github.com/dynamic-labs/dynamic-auth/commit/67086f0b4a1302d39853eb2eedcb6f5b8bfca889))
32
+ * **react-native:** add retry when setting items to secure store ([#10576](https://github.com/dynamic-labs/dynamic-auth/issues/10576)) ([22ea162](https://github.com/dynamic-labs/dynamic-auth/commit/22ea162420806a4a67394c99f3691754982a7f1f))
33
+
2
34
  ## [4.65.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.64.0...v4.65.0) (2026-02-27)
3
35
 
4
36
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.65.0";
6
+ var version = "4.67.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.65.0";
2
+ var version = "4.67.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/stellar",
3
- "version": "4.65.0",
3
+ "version": "4.67.0",
4
4
  "description": "A React SDK for implementing Stellar wallet web3 authentication and authorization to your website.",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -18,17 +18,17 @@
18
18
  },
19
19
  "homepage": "https://www.dynamic.xyz/",
20
20
  "dependencies": {
21
- "@dynamic-labs/sdk-api-core": "0.0.875",
21
+ "@dynamic-labs/sdk-api-core": "0.0.881",
22
22
  "@stellar/stellar-sdk": "14.4.3",
23
- "@dynamic-labs/wallet-connector-core": "4.65.0",
24
- "@dynamic-labs/assert-package-version": "4.65.0",
23
+ "@dynamic-labs/wallet-connector-core": "4.67.0",
24
+ "@dynamic-labs/assert-package-version": "4.67.0",
25
25
  "@lobstrco/signer-extension-api": "2.0.0",
26
26
  "@stellar/freighter-api": "6.0.1",
27
- "@dynamic-labs/logger": "4.65.0",
28
- "@dynamic-labs/types": "4.65.0",
29
- "@dynamic-labs/utils": "4.65.0",
30
- "@dynamic-labs/waas": "4.65.0",
31
- "@dynamic-labs/wallet-book": "4.65.0",
27
+ "@dynamic-labs/logger": "4.67.0",
28
+ "@dynamic-labs/types": "4.67.0",
29
+ "@dynamic-labs/utils": "4.67.0",
30
+ "@dynamic-labs/waas": "4.67.0",
31
+ "@dynamic-labs/wallet-book": "4.67.0",
32
32
  "eventemitter3": "5.0.1"
33
33
  },
34
34
  "peerDependencies": {}
@@ -221,10 +221,24 @@ class DynamicWaasStellarConnector extends waas.withDynamicWaas(StellarWalletConn
221
221
  this.activeAccountAddress = undefined;
222
222
  });
223
223
  }
224
+ /**
225
+ * Converts network passphrase to numeric chainId for policy validation
226
+ * ChainIds: '1' = pubnet (mainnet), '2' = testnet, '3' = futurenet
227
+ */
228
+ getChainIdFromNetworkPassphrase(networkPassphrase) {
229
+ // Check futurenet first since its passphrase also contains 'Test'
230
+ if (networkPassphrase.includes('Future')) {
231
+ return '3';
232
+ }
233
+ if (networkPassphrase.includes('Test')) {
234
+ return '2';
235
+ }
236
+ return '1';
237
+ }
224
238
  /**
225
239
  * Signs a Stellar transaction XDR.
226
240
  * @param transactionXdr - The XDR-encoded transaction envelope to sign
227
- * @returns The signature as a base64 string
241
+ * @returns The signed transaction XDR
228
242
  */
229
243
  signTransaction(transactionXdr) {
230
244
  return _tslib.__awaiter(this, void 0, void 0, function* () {
@@ -240,19 +254,22 @@ class DynamicWaasStellarConnector extends waas.withDynamicWaas(StellarWalletConn
240
254
  const password = yield this.getPasswordIfNeeded({
241
255
  accountAddress: this.activeAccountAddress,
242
256
  });
243
- // Get the transaction hash that needs to be signed
257
+ // Get chainId from network passphrase for policy validation
244
258
  const networkPassphrase = yield this.getNetworkPassphrase();
245
- const transaction = stellarSdk.TransactionBuilder.fromXDR(transactionXdr, networkPassphrase);
246
- const transactionHash = transaction.hash().toString('hex');
247
- // Sign the transaction hash using MPC
259
+ const chainId = this.getChainIdFromNetworkPassphrase(networkPassphrase);
260
+ // Sign the transaction XDR using MPC
261
+ // The SDK will compute the hash internally and build the context for policy validation
248
262
  const signature = yield walletClient.signTransaction({
249
263
  authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
264
+ chainId,
250
265
  mfaToken,
251
266
  password,
252
267
  senderAddress: this.activeAccountAddress,
253
268
  signedSessionId,
254
- transaction: transactionHash,
269
+ transaction: transactionXdr,
255
270
  });
271
+ // Parse the XDR to add the signature
272
+ const transaction = stellarSdk.TransactionBuilder.fromXDR(transactionXdr, networkPassphrase);
256
273
  // The signature from WaaS is base64 encoded
257
274
  // addSignature expects both public key and signature as strings
258
275
  transaction.addSignature(this.activeAccountAddress, signature);
@@ -58,7 +58,7 @@ declare const DynamicWaasStellarConnector_base: (abstract new (...args: any[]) =
58
58
  createWalletAccount({ thresholdSignatureScheme, password, bitcoinConfig, }?: {
59
59
  thresholdSignatureScheme?: string | undefined;
60
60
  password?: string | undefined;
61
- bitcoinConfig?: import("@dynamic-labs-wallet/core").BitcoinConfig | undefined;
61
+ bitcoinConfig?: import("@dynamic-labs-wallet/browser-wallet-client").BitcoinConfig | undefined;
62
62
  } | undefined): Promise<{
63
63
  chainName: string;
64
64
  accountAddress: string;
@@ -132,10 +132,10 @@ declare const DynamicWaasStellarConnector_base: (abstract new (...args: any[]) =
132
132
  unlockWallet({ accountAddress, password, }: {
133
133
  accountAddress: string;
134
134
  password?: string | undefined;
135
- }): Promise<import("@dynamic-labs-wallet/core").GetWalletResponse>;
135
+ }): Promise<import("@dynamic-labs-wallet/browser-wallet-client").GetWalletResponse>;
136
136
  getWalletRecoveryState({ accountAddress, }: {
137
137
  accountAddress: string;
138
- }): Promise<import("@dynamic-labs-wallet/core").WalletRecoveryState>;
138
+ }): Promise<import("@dynamic-labs-wallet/browser-wallet-client").WalletRecoveryState>;
139
139
  endSession(): Promise<void>;
140
140
  getActiveAccountAddress(): Promise<string | undefined>;
141
141
  getConnectedAccounts(): Promise<string[]>;
@@ -256,10 +256,15 @@ export declare class DynamicWaasStellarConnector extends DynamicWaasStellarConne
256
256
  * Ends the current session and clears the active account address
257
257
  */
258
258
  endSession(): Promise<void>;
259
+ /**
260
+ * Converts network passphrase to numeric chainId for policy validation
261
+ * ChainIds: '1' = pubnet (mainnet), '2' = testnet, '3' = futurenet
262
+ */
263
+ private getChainIdFromNetworkPassphrase;
259
264
  /**
260
265
  * Signs a Stellar transaction XDR.
261
266
  * @param transactionXdr - The XDR-encoded transaction envelope to sign
262
- * @returns The signature as a base64 string
267
+ * @returns The signed transaction XDR
263
268
  */
264
269
  signTransaction(transactionXdr: string): Promise<string>;
265
270
  /**
@@ -217,10 +217,24 @@ class DynamicWaasStellarConnector extends withDynamicWaas(StellarWalletConnector
217
217
  this.activeAccountAddress = undefined;
218
218
  });
219
219
  }
220
+ /**
221
+ * Converts network passphrase to numeric chainId for policy validation
222
+ * ChainIds: '1' = pubnet (mainnet), '2' = testnet, '3' = futurenet
223
+ */
224
+ getChainIdFromNetworkPassphrase(networkPassphrase) {
225
+ // Check futurenet first since its passphrase also contains 'Test'
226
+ if (networkPassphrase.includes('Future')) {
227
+ return '3';
228
+ }
229
+ if (networkPassphrase.includes('Test')) {
230
+ return '2';
231
+ }
232
+ return '1';
233
+ }
220
234
  /**
221
235
  * Signs a Stellar transaction XDR.
222
236
  * @param transactionXdr - The XDR-encoded transaction envelope to sign
223
- * @returns The signature as a base64 string
237
+ * @returns The signed transaction XDR
224
238
  */
225
239
  signTransaction(transactionXdr) {
226
240
  return __awaiter(this, void 0, void 0, function* () {
@@ -236,19 +250,22 @@ class DynamicWaasStellarConnector extends withDynamicWaas(StellarWalletConnector
236
250
  const password = yield this.getPasswordIfNeeded({
237
251
  accountAddress: this.activeAccountAddress,
238
252
  });
239
- // Get the transaction hash that needs to be signed
253
+ // Get chainId from network passphrase for policy validation
240
254
  const networkPassphrase = yield this.getNetworkPassphrase();
241
- const transaction = TransactionBuilder.fromXDR(transactionXdr, networkPassphrase);
242
- const transactionHash = transaction.hash().toString('hex');
243
- // Sign the transaction hash using MPC
255
+ const chainId = this.getChainIdFromNetworkPassphrase(networkPassphrase);
256
+ // Sign the transaction XDR using MPC
257
+ // The SDK will compute the hash internally and build the context for policy validation
244
258
  const signature = yield walletClient.signTransaction({
245
259
  authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
260
+ chainId,
246
261
  mfaToken,
247
262
  password,
248
263
  senderAddress: this.activeAccountAddress,
249
264
  signedSessionId,
250
- transaction: transactionHash,
265
+ transaction: transactionXdr,
251
266
  });
267
+ // Parse the XDR to add the signature
268
+ const transaction = TransactionBuilder.fromXDR(transactionXdr, networkPassphrase);
252
269
  // The signature from WaaS is base64 encoded
253
270
  // addSignature expects both public key and signature as strings
254
271
  transaction.addSignature(this.activeAccountAddress, signature);
@@ -11,5 +11,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
11
11
  * Number of stroops per XLM (1 XLM = 10,000,000 stroops)
12
12
  */
13
13
  const STROOPS_PER_XLM = 1e7;
14
+ /**
15
+ * Minimum amount of XLM required to activate (create) a new Stellar account.
16
+ * This is the base reserve required by the network.
17
+ */
18
+ const STELLAR_MIN_ACTIVATION_AMOUNT = '1';
14
19
 
20
+ exports.STELLAR_MIN_ACTIVATION_AMOUNT = STELLAR_MIN_ACTIVATION_AMOUNT;
15
21
  exports.STROOPS_PER_XLM = STROOPS_PER_XLM;
@@ -19,3 +19,8 @@ export declare const STELLAR_FUTURENET_NETWORK_PASSPHRASE = "Test SDF Future Net
19
19
  * Number of stroops per XLM (1 XLM = 10,000,000 stroops)
20
20
  */
21
21
  export declare const STROOPS_PER_XLM = 10000000;
22
+ /**
23
+ * Minimum amount of XLM required to activate (create) a new Stellar account.
24
+ * This is the base reserve required by the network.
25
+ */
26
+ export declare const STELLAR_MIN_ACTIVATION_AMOUNT = "1";
@@ -7,5 +7,10 @@
7
7
  * Number of stroops per XLM (1 XLM = 10,000,000 stroops)
8
8
  */
9
9
  const STROOPS_PER_XLM = 1e7;
10
+ /**
11
+ * Minimum amount of XLM required to activate (create) a new Stellar account.
12
+ * This is the base reserve required by the network.
13
+ */
14
+ const STELLAR_MIN_ACTIVATION_AMOUNT = '1';
10
15
 
11
- export { STROOPS_PER_XLM };
16
+ export { STELLAR_MIN_ACTIVATION_AMOUNT, STROOPS_PER_XLM };
package/src/index.cjs CHANGED
@@ -12,8 +12,8 @@ var isStellarWallet = require('./wallet/isStellarWallet/isStellarWallet.cjs');
12
12
  var StellarWalletConnector = require('./connectors/StellarWalletConnector/StellarWalletConnector.cjs');
13
13
  var StellarLocalStorageCache = require('./StellarLocalStorageCache.cjs');
14
14
  require('@stellar/stellar-sdk');
15
- var getNetworkFromAddress = require('./utils/getNetworkFromAddress.cjs');
16
15
  require('../_virtual/_tslib.cjs');
16
+ var getNetworkFromAddress = require('./utils/getNetworkFromAddress.cjs');
17
17
  require('@dynamic-labs/utils');
18
18
  var DynamicWaasStellarConnector = require('./connectors/DynamicWaasStellarConnector/DynamicWaasStellarConnector.cjs');
19
19
 
package/src/index.js CHANGED
@@ -8,8 +8,8 @@ export { isStellarWallet } from './wallet/isStellarWallet/isStellarWallet.js';
8
8
  export { StellarWalletConnector } from './connectors/StellarWalletConnector/StellarWalletConnector.js';
9
9
  export { StellarLocalStorageCache } from './StellarLocalStorageCache.js';
10
10
  import '@stellar/stellar-sdk';
11
- export { getNetworkFromAddress } from './utils/getNetworkFromAddress.js';
12
11
  import '../_virtual/_tslib.js';
12
+ export { getNetworkFromAddress } from './utils/getNetworkFromAddress.js';
13
13
  import '@dynamic-labs/utils';
14
14
  import { DynamicWaasStellarConnector } from './connectors/DynamicWaasStellarConnector/DynamicWaasStellarConnector.js';
15
15
  export { DynamicWaasStellarConnector } from './connectors/DynamicWaasStellarConnector/DynamicWaasStellarConnector.js';
@@ -7,7 +7,9 @@ var _tslib = require('../../../_virtual/_tslib.cjs');
7
7
  var stellarSdk = require('@stellar/stellar-sdk');
8
8
  var utils = require('@dynamic-labs/utils');
9
9
  var index = require('../../consts/index.cjs');
10
+ var buildCreateAccountTransaction = require('../buildCreateAccountTransaction.cjs');
10
11
  var buildPaymentTransaction = require('../buildPaymentTransaction.cjs');
12
+ var checkAccountExists = require('../checkAccountExists.cjs');
11
13
  var createPaymentAsset = require('../createPaymentAsset.cjs');
12
14
  var getAccountBalance = require('../getAccountBalance.cjs');
13
15
 
@@ -22,6 +24,17 @@ class StellarUiTransaction {
22
24
  this.chain = 'STELLAR';
23
25
  this.data = undefined;
24
26
  this.fee = { gas: undefined };
27
+ /**
28
+ * Tracks whether the destination account exists (is funded).
29
+ * null = not checked yet, true = exists, false = needs activation
30
+ */
31
+ this.destinationAccountExists = null;
32
+ /**
33
+ * Pre-submit validation error message.
34
+ * Set when the transaction cannot proceed due to validation issues
35
+ * (e.g., trying to send < 1 XLM to an unfunded account).
36
+ */
37
+ this.preSubmitValidationError = undefined;
25
38
  /**
26
39
  * Formats a non-native token value back to a string
27
40
  *
@@ -143,6 +156,45 @@ class StellarUiTransaction {
143
156
  // Use Stellar SDK's built-in validation
144
157
  return stellarSdk.StrKey.isValidEd25519PublicKey(address);
145
158
  }
159
+ /**
160
+ * Checks if the destination account exists on the network.
161
+ * Also validates if the transaction can proceed based on:
162
+ * - If account doesn't exist and amount < 1 XLM, sets validation error
163
+ *
164
+ * @returns true if account exists, false if it needs activation
165
+ */
166
+ checkDestinationAccount() {
167
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
168
+ const { to, value } = this;
169
+ // Reset validation error
170
+ this.preSubmitValidationError = undefined;
171
+ if (!to) {
172
+ return true; // No destination yet, skip check
173
+ }
174
+ // Check if account exists
175
+ const exists = yield checkAccountExists.checkAccountExists(to, this.horizonServer);
176
+ this.destinationAccountExists = exists;
177
+ // If account doesn't exist, validate amount
178
+ if (!exists && value !== undefined) {
179
+ const xlmAmount = Number(value) / index.STROOPS_PER_XLM;
180
+ const minAmount = parseFloat(index.STELLAR_MIN_ACTIVATION_AMOUNT);
181
+ if (xlmAmount < minAmount) {
182
+ this.preSubmitValidationError =
183
+ 'The receiver address is not yet active. A minimum of 1 XLM is required.';
184
+ }
185
+ }
186
+ return exists;
187
+ });
188
+ }
189
+ /**
190
+ * Returns any pre-submit validation error.
191
+ * Used by the UI to display errors and disable the confirm button.
192
+ *
193
+ * @returns The validation error message, or undefined if valid
194
+ */
195
+ getPreSubmitValidationError() {
196
+ return this.preSubmitValidationError;
197
+ }
146
198
  /**
147
199
  * Creates a transaction for sending XLM or tokens
148
200
  *
@@ -169,23 +221,46 @@ class StellarUiTransaction {
169
221
  const [code, issuer] = nonNativeAddress.split(/[:-]/);
170
222
  asset = createPaymentAsset.createPaymentAsset({ code, issuer });
171
223
  amount = this.formatNonNativeToken(nonNativeValue, (_a = this.nonNativeDecimal) !== null && _a !== void 0 ? _a : 7);
224
+ // Non-native tokens always use Payment (account must exist to have trustline)
225
+ return buildPaymentTransaction.buildPaymentTransaction({
226
+ amount,
227
+ asset,
228
+ networkPassphrase: this.networkPassphrase,
229
+ sourceAccount,
230
+ toAddress: to,
231
+ });
172
232
  }
173
233
  else if (value) {
174
234
  // Transfer native XLM
175
235
  asset = stellarSdk.Asset.native();
176
236
  amount = (Number(value) / index.STROOPS_PER_XLM).toString();
237
+ // Check if we need to use CreateAccount instead of Payment
238
+ // This happens when sending to an unfunded account with >= 1 XLM
239
+ if (this.destinationAccountExists === false) {
240
+ const xlmAmount = Number(value) / index.STROOPS_PER_XLM;
241
+ const minAmount = parseFloat(index.STELLAR_MIN_ACTIVATION_AMOUNT);
242
+ if (xlmAmount >= minAmount) {
243
+ // Use CreateAccount operation for silent activation
244
+ return buildCreateAccountTransaction.buildCreateAccountTransaction({
245
+ destinationAddress: to,
246
+ networkPassphrase: this.networkPassphrase,
247
+ sourceAccount,
248
+ startingBalance: amount,
249
+ });
250
+ }
251
+ // If amount < 1 XLM, the validation error should have been set
252
+ // and the UI should prevent submission
253
+ }
254
+ // Standard payment to existing account
255
+ return buildPaymentTransaction.buildPaymentTransaction({
256
+ amount,
257
+ asset,
258
+ networkPassphrase: this.networkPassphrase,
259
+ sourceAccount,
260
+ toAddress: to,
261
+ });
177
262
  }
178
- else {
179
- return undefined;
180
- }
181
- // Build the transaction
182
- return buildPaymentTransaction.buildPaymentTransaction({
183
- amount,
184
- asset,
185
- networkPassphrase: this.networkPassphrase,
186
- sourceAccount,
187
- toAddress: to,
188
- });
263
+ return undefined;
189
264
  });
190
265
  }
191
266
  /**
@@ -33,6 +33,17 @@ export declare class StellarUiTransaction implements IUITransaction {
33
33
  private onSubmit;
34
34
  private horizonServer;
35
35
  private networkPassphrase;
36
+ /**
37
+ * Tracks whether the destination account exists (is funded).
38
+ * null = not checked yet, true = exists, false = needs activation
39
+ */
40
+ private destinationAccountExists;
41
+ /**
42
+ * Pre-submit validation error message.
43
+ * Set when the transaction cannot proceed due to validation issues
44
+ * (e.g., trying to send < 1 XLM to an unfunded account).
45
+ */
46
+ private preSubmitValidationError;
36
47
  constructor({ onSubmit, from, horizonServer, networkPassphrase, }: StellarUiTransactionProps);
37
48
  /**
38
49
  * Fetches the gas fee for the transaction.
@@ -96,6 +107,21 @@ export declare class StellarUiTransaction implements IUITransaction {
96
107
  * @returns true if the address format is valid
97
108
  */
98
109
  validateAddressFormat(address: string): boolean;
110
+ /**
111
+ * Checks if the destination account exists on the network.
112
+ * Also validates if the transaction can proceed based on:
113
+ * - If account doesn't exist and amount < 1 XLM, sets validation error
114
+ *
115
+ * @returns true if account exists, false if it needs activation
116
+ */
117
+ checkDestinationAccount(): Promise<boolean>;
118
+ /**
119
+ * Returns any pre-submit validation error.
120
+ * Used by the UI to display errors and disable the confirm button.
121
+ *
122
+ * @returns The validation error message, or undefined if valid
123
+ */
124
+ getPreSubmitValidationError(): string | undefined;
99
125
  /**
100
126
  * Creates a transaction for sending XLM or tokens
101
127
  *
@@ -2,8 +2,10 @@
2
2
  import { __awaiter } from '../../../_virtual/_tslib.js';
3
3
  import { StrKey, Asset } from '@stellar/stellar-sdk';
4
4
  import { formatNumberText } from '@dynamic-labs/utils';
5
- import { STROOPS_PER_XLM } from '../../consts/index.js';
5
+ import { STROOPS_PER_XLM, STELLAR_MIN_ACTIVATION_AMOUNT } from '../../consts/index.js';
6
+ import { buildCreateAccountTransaction } from '../buildCreateAccountTransaction.js';
6
7
  import { buildPaymentTransaction } from '../buildPaymentTransaction.js';
8
+ import { checkAccountExists } from '../checkAccountExists.js';
7
9
  import { createPaymentAsset } from '../createPaymentAsset.js';
8
10
  import { getAccountBalance } from '../getAccountBalance.js';
9
11
 
@@ -18,6 +20,17 @@ class StellarUiTransaction {
18
20
  this.chain = 'STELLAR';
19
21
  this.data = undefined;
20
22
  this.fee = { gas: undefined };
23
+ /**
24
+ * Tracks whether the destination account exists (is funded).
25
+ * null = not checked yet, true = exists, false = needs activation
26
+ */
27
+ this.destinationAccountExists = null;
28
+ /**
29
+ * Pre-submit validation error message.
30
+ * Set when the transaction cannot proceed due to validation issues
31
+ * (e.g., trying to send < 1 XLM to an unfunded account).
32
+ */
33
+ this.preSubmitValidationError = undefined;
21
34
  /**
22
35
  * Formats a non-native token value back to a string
23
36
  *
@@ -139,6 +152,45 @@ class StellarUiTransaction {
139
152
  // Use Stellar SDK's built-in validation
140
153
  return StrKey.isValidEd25519PublicKey(address);
141
154
  }
155
+ /**
156
+ * Checks if the destination account exists on the network.
157
+ * Also validates if the transaction can proceed based on:
158
+ * - If account doesn't exist and amount < 1 XLM, sets validation error
159
+ *
160
+ * @returns true if account exists, false if it needs activation
161
+ */
162
+ checkDestinationAccount() {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ const { to, value } = this;
165
+ // Reset validation error
166
+ this.preSubmitValidationError = undefined;
167
+ if (!to) {
168
+ return true; // No destination yet, skip check
169
+ }
170
+ // Check if account exists
171
+ const exists = yield checkAccountExists(to, this.horizonServer);
172
+ this.destinationAccountExists = exists;
173
+ // If account doesn't exist, validate amount
174
+ if (!exists && value !== undefined) {
175
+ const xlmAmount = Number(value) / STROOPS_PER_XLM;
176
+ const minAmount = parseFloat(STELLAR_MIN_ACTIVATION_AMOUNT);
177
+ if (xlmAmount < minAmount) {
178
+ this.preSubmitValidationError =
179
+ 'The receiver address is not yet active. A minimum of 1 XLM is required.';
180
+ }
181
+ }
182
+ return exists;
183
+ });
184
+ }
185
+ /**
186
+ * Returns any pre-submit validation error.
187
+ * Used by the UI to display errors and disable the confirm button.
188
+ *
189
+ * @returns The validation error message, or undefined if valid
190
+ */
191
+ getPreSubmitValidationError() {
192
+ return this.preSubmitValidationError;
193
+ }
142
194
  /**
143
195
  * Creates a transaction for sending XLM or tokens
144
196
  *
@@ -165,23 +217,46 @@ class StellarUiTransaction {
165
217
  const [code, issuer] = nonNativeAddress.split(/[:-]/);
166
218
  asset = createPaymentAsset({ code, issuer });
167
219
  amount = this.formatNonNativeToken(nonNativeValue, (_a = this.nonNativeDecimal) !== null && _a !== void 0 ? _a : 7);
220
+ // Non-native tokens always use Payment (account must exist to have trustline)
221
+ return buildPaymentTransaction({
222
+ amount,
223
+ asset,
224
+ networkPassphrase: this.networkPassphrase,
225
+ sourceAccount,
226
+ toAddress: to,
227
+ });
168
228
  }
169
229
  else if (value) {
170
230
  // Transfer native XLM
171
231
  asset = Asset.native();
172
232
  amount = (Number(value) / STROOPS_PER_XLM).toString();
233
+ // Check if we need to use CreateAccount instead of Payment
234
+ // This happens when sending to an unfunded account with >= 1 XLM
235
+ if (this.destinationAccountExists === false) {
236
+ const xlmAmount = Number(value) / STROOPS_PER_XLM;
237
+ const minAmount = parseFloat(STELLAR_MIN_ACTIVATION_AMOUNT);
238
+ if (xlmAmount >= minAmount) {
239
+ // Use CreateAccount operation for silent activation
240
+ return buildCreateAccountTransaction({
241
+ destinationAddress: to,
242
+ networkPassphrase: this.networkPassphrase,
243
+ sourceAccount,
244
+ startingBalance: amount,
245
+ });
246
+ }
247
+ // If amount < 1 XLM, the validation error should have been set
248
+ // and the UI should prevent submission
249
+ }
250
+ // Standard payment to existing account
251
+ return buildPaymentTransaction({
252
+ amount,
253
+ asset,
254
+ networkPassphrase: this.networkPassphrase,
255
+ sourceAccount,
256
+ toAddress: to,
257
+ });
173
258
  }
174
- else {
175
- return undefined;
176
- }
177
- // Build the transaction
178
- return buildPaymentTransaction({
179
- amount,
180
- asset,
181
- networkPassphrase: this.networkPassphrase,
182
- sourceAccount,
183
- toAddress: to,
184
- });
259
+ return undefined;
185
260
  });
186
261
  }
187
262
  /**
@@ -0,0 +1,39 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var stellarSdk = require('@stellar/stellar-sdk');
7
+
8
+ const DEFAULT_TIMEOUT = 180;
9
+ /**
10
+ * Builds a Stellar create account transaction.
11
+ *
12
+ * This is used when sending XLM to an account that doesn't exist yet.
13
+ * The CreateAccount operation creates and funds the destination account
14
+ * in a single atomic transaction.
15
+ *
16
+ * @param params - Transaction parameters
17
+ * @param params.sourceAccount - The source account
18
+ * @param params.networkPassphrase - The network passphrase
19
+ * @param params.destinationAddress - Address of the new account to create
20
+ * @param params.startingBalance - Amount of XLM to fund the new account with (min 1 XLM)
21
+ * @param params.memo - Optional transaction memo
22
+ * @returns A built Stellar transaction
23
+ */
24
+ const buildCreateAccountTransaction = (params) => {
25
+ const { sourceAccount, networkPassphrase, destinationAddress, startingBalance, memo, } = params;
26
+ const transactionBuilder = new stellarSdk.TransactionBuilder(sourceAccount, {
27
+ fee: stellarSdk.BASE_FEE,
28
+ networkPassphrase,
29
+ }).addOperation(stellarSdk.Operation.createAccount({
30
+ destination: destinationAddress,
31
+ startingBalance,
32
+ }));
33
+ if (memo) {
34
+ transactionBuilder.addMemo(memo);
35
+ }
36
+ return transactionBuilder.setTimeout(DEFAULT_TIMEOUT).build();
37
+ };
38
+
39
+ exports.buildCreateAccountTransaction = buildCreateAccountTransaction;
@@ -0,0 +1,23 @@
1
+ import { type Horizon, type Memo, Transaction } from '@stellar/stellar-sdk';
2
+ /**
3
+ * Builds a Stellar create account transaction.
4
+ *
5
+ * This is used when sending XLM to an account that doesn't exist yet.
6
+ * The CreateAccount operation creates and funds the destination account
7
+ * in a single atomic transaction.
8
+ *
9
+ * @param params - Transaction parameters
10
+ * @param params.sourceAccount - The source account
11
+ * @param params.networkPassphrase - The network passphrase
12
+ * @param params.destinationAddress - Address of the new account to create
13
+ * @param params.startingBalance - Amount of XLM to fund the new account with (min 1 XLM)
14
+ * @param params.memo - Optional transaction memo
15
+ * @returns A built Stellar transaction
16
+ */
17
+ export declare const buildCreateAccountTransaction: (params: {
18
+ destinationAddress: string;
19
+ memo?: Memo;
20
+ networkPassphrase: string;
21
+ sourceAccount: Horizon.AccountResponse;
22
+ startingBalance: string;
23
+ }) => Transaction;
@@ -0,0 +1,35 @@
1
+ 'use client'
2
+ import { TransactionBuilder, BASE_FEE, Operation } from '@stellar/stellar-sdk';
3
+
4
+ const DEFAULT_TIMEOUT = 180;
5
+ /**
6
+ * Builds a Stellar create account transaction.
7
+ *
8
+ * This is used when sending XLM to an account that doesn't exist yet.
9
+ * The CreateAccount operation creates and funds the destination account
10
+ * in a single atomic transaction.
11
+ *
12
+ * @param params - Transaction parameters
13
+ * @param params.sourceAccount - The source account
14
+ * @param params.networkPassphrase - The network passphrase
15
+ * @param params.destinationAddress - Address of the new account to create
16
+ * @param params.startingBalance - Amount of XLM to fund the new account with (min 1 XLM)
17
+ * @param params.memo - Optional transaction memo
18
+ * @returns A built Stellar transaction
19
+ */
20
+ const buildCreateAccountTransaction = (params) => {
21
+ const { sourceAccount, networkPassphrase, destinationAddress, startingBalance, memo, } = params;
22
+ const transactionBuilder = new TransactionBuilder(sourceAccount, {
23
+ fee: BASE_FEE,
24
+ networkPassphrase,
25
+ }).addOperation(Operation.createAccount({
26
+ destination: destinationAddress,
27
+ startingBalance,
28
+ }));
29
+ if (memo) {
30
+ transactionBuilder.addMemo(memo);
31
+ }
32
+ return transactionBuilder.setTimeout(DEFAULT_TIMEOUT).build();
33
+ };
34
+
35
+ export { buildCreateAccountTransaction };
@@ -0,0 +1,40 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../../_virtual/_tslib.cjs');
7
+
8
+ /**
9
+ * Checks if a Stellar account exists (is funded) on the network.
10
+ *
11
+ * On Stellar, accounts must be funded with at least 1 XLM to exist.
12
+ * This function attempts to load the account from Horizon.
13
+ * If the account doesn't exist, Horizon returns a 404 error.
14
+ *
15
+ * @param address - The Stellar public key (G...) to check
16
+ * @param horizonServer - The Horizon server instance
17
+ * @returns true if the account exists, false otherwise
18
+ */
19
+ const checkAccountExists = (address, horizonServer) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
20
+ var _a;
21
+ try {
22
+ yield horizonServer.loadAccount(address);
23
+ return true;
24
+ }
25
+ catch (error) {
26
+ // Horizon returns 404 for accounts that don't exist
27
+ // Any other error should be treated as network failure, assume exists to be safe
28
+ if (error &&
29
+ typeof error === 'object' &&
30
+ 'response' in error &&
31
+ ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
32
+ return false;
33
+ }
34
+ // For network errors or other issues, assume account exists
35
+ // to avoid blocking legitimate transactions
36
+ return true;
37
+ }
38
+ });
39
+
40
+ exports.checkAccountExists = checkAccountExists;
@@ -0,0 +1,13 @@
1
+ import type { Horizon } from '@stellar/stellar-sdk';
2
+ /**
3
+ * Checks if a Stellar account exists (is funded) on the network.
4
+ *
5
+ * On Stellar, accounts must be funded with at least 1 XLM to exist.
6
+ * This function attempts to load the account from Horizon.
7
+ * If the account doesn't exist, Horizon returns a 404 error.
8
+ *
9
+ * @param address - The Stellar public key (G...) to check
10
+ * @param horizonServer - The Horizon server instance
11
+ * @returns true if the account exists, false otherwise
12
+ */
13
+ export declare const checkAccountExists: (address: string, horizonServer: Horizon.Server) => Promise<boolean>;
@@ -0,0 +1,36 @@
1
+ 'use client'
2
+ import { __awaiter } from '../../_virtual/_tslib.js';
3
+
4
+ /**
5
+ * Checks if a Stellar account exists (is funded) on the network.
6
+ *
7
+ * On Stellar, accounts must be funded with at least 1 XLM to exist.
8
+ * This function attempts to load the account from Horizon.
9
+ * If the account doesn't exist, Horizon returns a 404 error.
10
+ *
11
+ * @param address - The Stellar public key (G...) to check
12
+ * @param horizonServer - The Horizon server instance
13
+ * @returns true if the account exists, false otherwise
14
+ */
15
+ const checkAccountExists = (address, horizonServer) => __awaiter(void 0, void 0, void 0, function* () {
16
+ var _a;
17
+ try {
18
+ yield horizonServer.loadAccount(address);
19
+ return true;
20
+ }
21
+ catch (error) {
22
+ // Horizon returns 404 for accounts that don't exist
23
+ // Any other error should be treated as network failure, assume exists to be safe
24
+ if (error &&
25
+ typeof error === 'object' &&
26
+ 'response' in error &&
27
+ ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
28
+ return false;
29
+ }
30
+ // For network errors or other issues, assume account exists
31
+ // to avoid blocking legitimate transactions
32
+ return true;
33
+ }
34
+ });
35
+
36
+ export { checkAccountExists };
@@ -1,5 +1,7 @@
1
+ export { buildCreateAccountTransaction } from './buildCreateAccountTransaction';
1
2
  export { buildPaymentTransaction } from './buildPaymentTransaction';
2
3
  export { buildTrustlineTransaction } from './buildTrustlineTransaction';
4
+ export { checkAccountExists } from './checkAccountExists';
3
5
  export { checkTrustline } from './checkTrustline';
4
6
  export { createPaymentAsset } from './createPaymentAsset';
5
7
  export { createTransactionMemo } from './createTransactionMemo';