@defuse-protocol/intents-sdk 0.14.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/README.md ADDED
@@ -0,0 +1,732 @@
1
+ # @defuse-protocol/intents-sdk
2
+
3
+ The Intents SDK for Near Intents provides a set of tools for interacting with various bridge implementations. It simplifies the process of transferring assets from Near Intents to different blockchains.
4
+
5
+ ## Features
6
+
7
+ - Bridging from Near Intents:
8
+ - Support for multiple bridge implementations (Hot, PoA)
9
+ - Single and batch withdrawal operations
10
+ - Automatic fee estimation
11
+ - Built-in validation for withdrawal constraints:
12
+ - PoA Bridge minimum withdrawal amounts
13
+ - Hot Bridge Stellar trustline validation
14
+ - Transfers within Near Intents
15
+ - Transfers to NEAR blockchain
16
+ - Transfers to Virtual Chains (e.g. Aurora)
17
+
18
+
19
+
20
+ Note: Bridging to Near Intents is not supported yet.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @defuse-protocol/intents-sdk --save-exact
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```typescript
31
+ import { IntentsSDK, createIntentSignerNearKeyPair } from '@defuse-protocol/intents-sdk';
32
+ import { KeyPair } from 'near-api-js';
33
+
34
+ // Initialize the SDK with required configuration
35
+ const sdk = new IntentsSDK({
36
+ referral: 'your-referral-code', // Only referral is required
37
+ });
38
+
39
+ // Set up intent signer (for NEAR)
40
+ const keypair = KeyPair.fromString('your-private-key');
41
+ const signer = createIntentSignerNearKeyPair({
42
+ keypair: keypair,
43
+ accountId: 'your-account.near'
44
+ });
45
+ sdk.setIntentSigner(signer);
46
+
47
+ // Method 1: Complete end-to-end withdrawal (orchestrated)
48
+ const result = await sdk.processWithdrawal({
49
+ withdrawalParams: {
50
+ assetId: 'nep141:usdt.tether-token.near', // Asset identifier
51
+ amount: 1000000n, // Amount in smallest unit
52
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
53
+ feeInclusive: false, // Whether amount includes fees
54
+ // routeConfig is optional - will be auto-detected from assetId
55
+ }
56
+ });
57
+
58
+ console.log('Intent hash:', result.intentHash);
59
+ console.log('Destination tx:', result.destinationTx);
60
+
61
+ // Method 2: Granular control with individual methods
62
+ const feeEstimation = await sdk.estimateWithdrawalFee({
63
+ withdrawalParams: {
64
+ assetId: 'nep141:usdt.tether-token.near',
65
+ amount: 1000000n,
66
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
67
+ feeInclusive: false
68
+ }
69
+ });
70
+
71
+ const { intentHash } = await sdk.signAndSendWithdrawalIntent({
72
+ withdrawalParams: {
73
+ assetId: 'nep141:usdt.tether-token.near',
74
+ amount: 1000000n,
75
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
76
+ feeInclusive: false
77
+ },
78
+ feeEstimation
79
+ });
80
+
81
+ const intentTx = await sdk.waitForIntentSettlement({ intentHash });
82
+ const destinationTx = await sdk.waitForWithdrawalCompletion({
83
+ withdrawalParams: {
84
+ assetId: 'nep141:usdt.tether-token.near',
85
+ amount: 1000000n,
86
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
87
+ feeInclusive: false
88
+ },
89
+ intentTx
90
+ });
91
+ ```
92
+
93
+ ## Core Concepts
94
+
95
+ ### Route and Bridge Enums
96
+
97
+ The SDK uses two key concepts to organize withdrawal operations:
98
+
99
+ #### Routes
100
+ Routes define the **path** a withdrawal takes - the specific mechanism and destination for transferring assets. Each route represents a different withdrawal flow:
101
+
102
+ ```typescript
103
+ import { RouteEnum } from '@defuse-protocol/intents-sdk';
104
+
105
+ console.log(RouteEnum.HotBridge); // "hot_bridge" - Cross-chain via HOT protocol
106
+ console.log(RouteEnum.PoaBridge); // "poa_bridge" - Cross-chain via PoA bridge
107
+ console.log(RouteEnum.NearWithdrawal); // "near_withdrawal" - Direct to NEAR blockchain
108
+ console.log(RouteEnum.VirtualChain); // "virtual_chain" - To Aurora Engine chains
109
+ console.log(RouteEnum.InternalTransfer); // "internal_transfer" - Between protocol users
110
+ ```
111
+
112
+ #### Bridge Names
113
+ Bridge names identify the **underlying bridge infrastructure** that handles the cross-chain transfer. This determines which external protocol processes the withdrawal:
114
+
115
+ ```typescript
116
+ import { BridgeNameEnum } from '@defuse-protocol/intents-sdk';
117
+
118
+ console.log(BridgeNameEnum.Hot); // "hot" - HOT Labs bridge infrastructure
119
+ console.log(BridgeNameEnum.Poa); // "poa" - Proof-of-Authority bridge by Defuse Labs
120
+ console.log(BridgeNameEnum.None); // null - No external bridge (NEAR-native or internal)
121
+ ```
122
+
123
+ **Key Difference**:
124
+ - **Route** = "How and where" the withdrawal goes (the path)
125
+ - **Bridge Name** = "Who operates" the underlying infrastructure (the bridge provider)
126
+
127
+ For example, both `hot_bridge` and `poa_bridge` routes perform cross-chain transfers, but use different bridge infrastructures (`hot` vs `poa`) with different fee structures and supported networks.
128
+
129
+ ### Asset Identifiers
130
+
131
+ The SDK uses standardized asset identifiers in the format:
132
+ - `nep141:contract.near` - NEP-141 tokens
133
+ - `nep245:contract.near:tokenId` - NEP-245 multi-tokens
134
+
135
+ Asset Identifier uniquely determines the corresponding route and destination chain.
136
+
137
+ Examples:
138
+ - `nep141:usdt.tether-token.near` - USDT on NEAR
139
+ - `nep141:wrap.near` - Wrapped NEAR (native NEAR)
140
+ - `nep245:v2_1.omni.hot.tg:137_qiStmoQJDQPTebaPjgx5VBxZv6L` - Polygon USDC through Hot
141
+ - `nep141:base-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913.omft.near` - Base USDC through PoA
142
+
143
+ ### Intent Signers
144
+
145
+ Intent signers are required to authenticate and sign withdrawal operations. The SDK supports multiple signing methods:
146
+
147
+ - **NEAR KeyPair Signer** - Use NEAR account private keys for signing
148
+ - **NEP-413 Signer** - Custom message signing implementation
149
+ - **EVM/Viem Signer** - Use Ethereum-compatible wallets and accounts
150
+
151
+ You must set an intent signer before processing withdrawals:
152
+
153
+ ```typescript
154
+ // Example: Set up a NEAR KeyPair signer
155
+ const signer = createIntentSignerNearKeyPair({
156
+ keypair: KeyPair.fromString('your-private-key'),
157
+ accountId: 'your-account.near'
158
+ });
159
+ sdk.setIntentSigner(signer);
160
+ ```
161
+
162
+ See the [Intent Signers](#intent-signers-1) section below for detailed implementation examples.
163
+
164
+ ## Route Types
165
+
166
+ The SDK automatically detects and supports multiple route types based on asset identifiers:
167
+
168
+ ### Hot Bridge Route
169
+ - **Purpose**: Cross-chain transfers via HOT Labs infrastructure
170
+ - **Supported Assets**: Multi-tokens (NEP-245) from Hot protocol (contract `v2_1.omni.hot.tg`)
171
+ - **Use Case**: Cross-chain transfers for assets bridged through Hot protocol
172
+ - **Route Type**: `hot_bridge`
173
+
174
+ ### PoA Bridge Route
175
+ - **Purpose**: Proof-of-Authority bridge transfers operated by Defuse Labs
176
+ - **Supported Assets**: Fungible tokens (NEP-141) ending with `.omft.near`
177
+ - **Use Case**: Cross-chain transfers for assets bridged through PoA protocol
178
+ - **Route Type**: `poa_bridge`
179
+
180
+ ### Internal Transfer Route
181
+ - **Purpose**: Transfer between Near Intents users within the protocol
182
+ - **Supported Assets**: All NEP-141 and NEP-245 tokens
183
+ - **Use Case**: User A having funds in the protocol wants to transfer to User B
184
+ - **Route Type**: `internal_transfer`
185
+
186
+ ### Near Withdrawal Route
187
+ - **Purpose**: Transfers within the NEAR blockchain
188
+ - **Supported Assets**: NEP-141 tokens on NEAR, including native NEAR (wrap.near)
189
+ - **Use Case**: Same-chain transfers on NEAR
190
+ - **Route Type**: `near_withdrawal`
191
+
192
+ ### Virtual Chain Route
193
+ - **Purpose**: Transfers to Aurora Engine-powered chains (aka Virtual chains)
194
+ - **Supported Assets**: NEP-141 tokens with Aurora Engine integration
195
+ - **Use Case**: Near Intents to Aurora ecosystem transfers
196
+ - **Route Type**: `virtual_chain`
197
+ - **Note**: Requires explicit `routeConfig` with `auroraEngineContractId`
198
+
199
+ ### Fee Estimation
200
+
201
+ The SDK now supports both single and batch fee estimation:
202
+
203
+ ```typescript
204
+ // Single withdrawal fee estimation
205
+ const feeEstimation = await sdk.estimateWithdrawalFee({
206
+ withdrawalParams: {
207
+ assetId: 'nep141:usdt.tether-token.near',
208
+ amount: 1000000n,
209
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
210
+ feeInclusive: false
211
+ }
212
+ });
213
+
214
+ console.log('Fee amount:', feeEstimation.amount);
215
+ console.log('Quote info:', feeEstimation.quote); // null if fee paid with withdrawn token
216
+
217
+ // Batch fee estimation
218
+ const batchFees = await sdk.estimateWithdrawalFee({
219
+ withdrawalParams: [
220
+ {
221
+ assetId: 'nep141:usdt.tether-token.near',
222
+ amount: 1000000n,
223
+ destinationAddress: '0x742d35Cc...',
224
+ feeInclusive: false
225
+ },
226
+ {
227
+ assetId: 'nep245:v2_1.omni.hot.tg:137_qiStmoQJDQPTebaPjgx5VBxZv6L',
228
+ amount: 500000n,
229
+ destinationAddress: '0x742d35Cc...',
230
+ feeInclusive: false
231
+ }
232
+ ]
233
+ });
234
+
235
+ console.log('Batch fees:', batchFees); // Array of FeeEstimation objects
236
+ ```
237
+
238
+ ### Intent Signers
239
+
240
+ The SDK supports multiple intent signing methods using factory functions:
241
+
242
+ #### NEAR KeyPair Signer
243
+ ```typescript
244
+ import { createIntentSignerNearKeyPair, IntentsSDK } from '@defuse-protocol/intents-sdk';
245
+ import { KeyPair } from 'near-api-js';
246
+
247
+ const keyPair = KeyPair.fromString('your-private-key');
248
+ const signer = createIntentSignerNearKeyPair({
249
+ keypair: keyPair,
250
+ accountId: 'your-account.near'
251
+ });
252
+ ```
253
+
254
+ #### NEP-413 Signer
255
+ ```typescript
256
+ import { createIntentSignerNEP413 } from '@defuse-protocol/intents-sdk';
257
+
258
+ const signer = createIntentSignerNEP413({
259
+ signMessage: async (nep413Payload, nep413Hash) => {
260
+ // Implement your custom signing logic here
261
+ return {
262
+ publicKey: 'ed25519:YourPublicKey',
263
+ signature: 'base64-encoded-signature'
264
+ };
265
+ },
266
+ accountId: 'your-account.near'
267
+ });
268
+ ```
269
+
270
+ #### EVM/Viem Signer
271
+ ```typescript
272
+ import { createIntentSignerViem } from '@defuse-protocol/intents-sdk';
273
+ import { privateKeyToAccount } from 'viem/accounts';
274
+
275
+ const account = privateKeyToAccount('0x...');
276
+ const signer = createIntentSignerViem(account);
277
+
278
+ // Set the signer at runtime
279
+ sdk.setIntentSigner(signer);
280
+ ```
281
+
282
+ ## Advanced Usage
283
+
284
+ ### Custom RPC URLs
285
+
286
+ Set NEAR and EVM chains RPC URLs in the constructor:
287
+
288
+ ```typescript
289
+ import { Chains } from '@defuse-protocol/intents-sdk'
290
+
291
+ const sdk = new IntentsSDK({
292
+ ...,
293
+ rpc: {
294
+ [Chains.Near]: ['https://rpc.mainnet.near.org'],
295
+ [Chains.Polygon]: ['https://polygon-rpc.com'],
296
+ [Chains.BNB]: ['https://bsc-dataseed.binance.org'],
297
+ }
298
+ });
299
+ ```
300
+
301
+ ### Intent Publishing Hooks
302
+
303
+ Use the `onBeforePublishIntent` hook to intercept and process intent data before it's published to the relayer. This is useful for persistence, logging, analytics, or custom processing:
304
+
305
+ ```typescript
306
+ import { type OnBeforePublishIntentHook } from '@defuse-protocol/intents-sdk';
307
+
308
+ // Define your hook function
309
+ const onBeforePublishIntent: OnBeforePublishIntentHook = async (intentData) => {
310
+ // Save to database for tracking
311
+ await saveIntentToDatabase({
312
+ hash: intentData.intentHash,
313
+ payload: intentData.intentPayload,
314
+ timestamp: new Date(),
315
+ });
316
+
317
+ // Send analytics
318
+ analytics.track('intent_about_to_publish', {
319
+ intentHash: intentData.intentHash,
320
+ intentType: intentData.intentPayload.intents[0]?.intent,
321
+ });
322
+ };
323
+
324
+ // Use the hook with the functional API
325
+ const result = await sdk.processWithdrawal({
326
+ withdrawalParams: { /* ... */ },
327
+ intent: {
328
+ onBeforePublishIntent, // Add the hook here
329
+ }
330
+ });
331
+
332
+ // Or with granular control
333
+ const { intentHash } = await sdk.signAndSendWithdrawalIntent({
334
+ withdrawalParams: { /* ... */ },
335
+ feeEstimation: fee,
336
+ intent: {
337
+ onBeforePublishIntent, // Add the hook here
338
+ }
339
+ });
340
+
341
+ // Or with generic intent publishing
342
+ const { intentHash } = await sdk.signAndSendIntent({
343
+ intents: [/* ... */],
344
+ onBeforePublishIntent, // Add the hook here
345
+ });
346
+ ```
347
+
348
+ **Hook Parameters:**
349
+ - `intentHash` - The computed hash of the intent payload
350
+ - `intentPayload` - The unsigned intent payload
351
+ - `multiPayload` - The signed multi-payload containing signature and metadata
352
+ - `relayParams` - Additional parameters passed to the relayer (quote hashes)
353
+
354
+ **Important Notes:**
355
+ - The hook is called synchronously before publishing the intent
356
+ - If the hook throws an error, the withdrawal will fail
357
+ - The hook can be async and return a Promise
358
+
359
+ ### Batch Withdrawals
360
+
361
+ Process multiple withdrawals in a single intent:
362
+
363
+ ```typescript
364
+ const withdrawalParams = [
365
+ {
366
+ assetId: 'nep141:usdt.tether-token.near',
367
+ amount: 1000000n,
368
+ destinationAddress: '0x742d35Cc...',
369
+ feeInclusive: false
370
+ },
371
+ {
372
+ assetId: 'nep245:v2_1.omni.hot.tg:137_qiStmoQJDQPTebaPjgx5VBxZv6L',
373
+ amount: 100000n,
374
+ destinationAddress: '0x742d35Cc...',
375
+ feeInclusive: false
376
+ }
377
+ ]
378
+
379
+ // Method 1: Complete end-to-end batch processing
380
+ const batchResult = await sdk.processWithdrawal({
381
+ withdrawalParams,
382
+ // feeEstimation is optional - will be estimated automatically if not provided
383
+ });
384
+
385
+ console.log('Batch intent hash:', batchResult.intentHash);
386
+ console.log('Destination transactions:', batchResult.destinationTx); // Array of results
387
+
388
+ // Method 2: Step-by-step batch processing for granular control
389
+ const feeEstimation = await sdk.estimateWithdrawalFee({
390
+ withdrawalParams
391
+ });
392
+
393
+ const { intentHash } = await sdk.signAndSendWithdrawalIntent({
394
+ withdrawalParams,
395
+ feeEstimation
396
+ });
397
+
398
+ const intentTx = await sdk.waitForIntentSettlement({ intentHash });
399
+
400
+ const destinationTxs = await sdk.waitForWithdrawalCompletion({
401
+ withdrawalParams,
402
+ intentTx
403
+ });
404
+
405
+ console.log('All destination transactions:', destinationTxs);
406
+ ```
407
+
408
+ ### Intent Management
409
+
410
+ The SDK provides direct access to intent operations for advanced use cases:
411
+
412
+ ```typescript
413
+ // Generic intent signing and publishing
414
+ const { intentHash } = await sdk.signAndSendIntent({
415
+ intents: [/* array of intent primitives */],
416
+ signer: customIntentSigner, // optional - uses SDK default if not provided
417
+ onBeforePublishIntent: async (data) => {
418
+ // Custom logic before publishing
419
+ console.log('About to publish intent:', data.intentHash);
420
+ }
421
+ });
422
+
423
+ // Wait for intent settlement
424
+ const intentTx = await sdk.waitForIntentSettlement({
425
+ intentHash
426
+ });
427
+
428
+ // or manual status check
429
+
430
+ // Check intent status at any time
431
+ const status = await sdk.getIntentStatus({
432
+ intentHash: intentHash
433
+ });
434
+
435
+ console.log('Intent status:', status.status); // "PENDING" | "TX_BROADCASTED" | "SETTLED" | "NOT_FOUND_OR_NOT_VALID"
436
+
437
+ if (status.status === 'SETTLED') {
438
+ console.log('Settlement transaction:', status.txHash);
439
+ }
440
+ ```
441
+
442
+ **Intent Status Values:**
443
+ - `PENDING` - Intent published but not yet processed
444
+ - `TX_BROADCASTED` - Intent being processed, transaction broadcasted
445
+ - `SETTLED` - Intent successfully completed
446
+ - `NOT_FOUND_OR_NOT_VALID` - Intent not found or invalid, it isn't executed onchain
447
+
448
+ ### Route Configuration Factory Functions
449
+
450
+ **Recommended**: Use factory functions to create route configurations. The SDK provides factory functions for type-safe and convenient route configuration creation:
451
+
452
+ ```typescript
453
+ import {
454
+ createVirtualChainRoute,
455
+ createNearWithdrawalRoute,
456
+ createInternalTransferRoute
457
+ } from '@defuse-protocol/intents-sdk';
458
+
459
+ // Create virtual chain route configuration (recommended)
460
+ const virtualChainRoute = createVirtualChainRoute(
461
+ '0x4e45415f.c.aurora', // Aurora Engine contract ID
462
+ null // Proxy token contract ID (optional)
463
+ );
464
+
465
+ // Create near withdrawal route with custom message
466
+ const nearWithdrawalRoute = createNearWithdrawalRoute(
467
+ 'Custom withdrawal message' // Optional message
468
+ );
469
+
470
+ // Create internal transfer route
471
+ const internalTransferRoute = createInternalTransferRoute();
472
+
473
+ // Use the factory-created route configuration in withdrawal
474
+ const result = await sdk.processWithdrawal({
475
+ withdrawalParams: {
476
+ assetId: 'nep141:a35923162c49cf95e6bf26623385eb431ad920d3.factory.bridge.near',
477
+ amount: BigInt('1000000'),
478
+ destinationAddress: '0x742d35Cc6634C0532925a3b8D84B2021F90a51A3',
479
+ feeInclusive: false,
480
+ routeConfig: virtualChainRoute // Recommended: Use factory function
481
+ }
482
+ });
483
+ ```
484
+
485
+ ### Asset Information Parsing
486
+
487
+ Get detailed information about supported assets:
488
+
489
+ ```typescript
490
+ try {
491
+ const assetInfo = sdk.parseAssetId('nep141:usdt.tether-token.near');
492
+ console.log('Bridge name:', assetInfo.bridgeName);
493
+ console.log('Blockchain:', assetInfo.blockchain);
494
+ console.log('Contract ID:', assetInfo.contractId);
495
+ console.log('Standard:', assetInfo.standard);
496
+ } catch (error) {
497
+ console.log('Asset not supported');
498
+ }
499
+ ```
500
+
501
+ ### Waiting for Completion
502
+
503
+ Monitor withdrawal completion:
504
+
505
+ ```typescript
506
+ // Method 1: Using the orchestrated approach (automatic monitoring)
507
+ const result = await sdk.processWithdrawal({
508
+ withdrawalParams: {
509
+ assetId: 'nep141:usdt.tether-token.near',
510
+ amount: 1000000n,
511
+ destinationAddress: '0x742d35Cc...',
512
+ feeInclusive: false
513
+ }
514
+ });
515
+
516
+ console.log('Intent settled:', result.intentTx.hash);
517
+ console.log('Withdrawal completed:', result.destinationTx);
518
+
519
+ // Method 2: Step-by-step monitoring for granular control
520
+ const feeEstimation = await sdk.estimateWithdrawalFee({
521
+ withdrawalParams: {
522
+ assetId: 'nep141:usdt.tether-token.near',
523
+ amount: 1000000n,
524
+ destinationAddress: '0x742d35Cc...',
525
+ feeInclusive: false
526
+ }
527
+ });
528
+
529
+ const { intentHash } = await sdk.signAndSendWithdrawalIntent({
530
+ withdrawalParams: {
531
+ assetId: 'nep141:usdt.tether-token.near',
532
+ amount: 1000000n,
533
+ destinationAddress: '0x742d35Cc...',
534
+ feeInclusive: false
535
+ },
536
+ feeEstimation
537
+ });
538
+
539
+ // Monitor intent settlement
540
+ const intentTx = await sdk.waitForIntentSettlement({ intentHash });
541
+ console.log('Intent settled:', intentTx.hash);
542
+
543
+ // Wait for withdrawal completion on destination chain
544
+ const completionResult = await sdk.waitForWithdrawalCompletion({
545
+ withdrawalParams: {
546
+ assetId: 'nep141:usdt.tether-token.near',
547
+ amount: 1000000n,
548
+ destinationAddress: '0x742d35Cc...',
549
+ feeInclusive: false
550
+ },
551
+ intentTx
552
+ });
553
+
554
+ if ('hash' in completionResult) {
555
+ console.log('Withdrawal completed with hash:', completionResult.hash);
556
+ } else {
557
+ console.log('Withdrawal completion not trackable for this bridge');
558
+ }
559
+ ```
560
+
561
+ ### Error Handling
562
+
563
+ ```typescript
564
+ import { FeeExceedsAmountError, MinWithdrawalAmountError } from '@defuse-protocol/intents-sdk';
565
+
566
+ try {
567
+ const result = await sdk.processWithdrawal({
568
+ withdrawalParams: {
569
+ assetId: 'nep141:usdt.tether-token.near',
570
+ amount: BigInt('100'), // Very small amount
571
+ destinationAddress: '0x742d35Cc...',
572
+ feeInclusive: true // Fee must be less than amount
573
+ }
574
+ });
575
+ } catch (error) {
576
+ if (error instanceof FeeExceedsAmountError) {
577
+ console.log('Fee exceeds withdrawal amount');
578
+ console.log('Required fee:', error.feeEstimation.amount);
579
+ console.log('Withdrawal amount:', error.amount);
580
+ } else if (error instanceof MinWithdrawalAmountError) {
581
+ console.log('Amount below minimum withdrawal limit');
582
+ console.log('Minimum required:', error.minAmount);
583
+ console.log('Requested amount:', error.requestedAmount);
584
+ console.log('Asset:', error.assetId);
585
+ }
586
+ }
587
+
588
+ // Error handling with granular control
589
+ try {
590
+ const feeEstimation = await sdk.estimateWithdrawalFee({
591
+ withdrawalParams: {
592
+ assetId: 'nep141:usdt.tether-token.near',
593
+ amount: 100n,
594
+ destinationAddress: '0x742d35Cc...',
595
+ feeInclusive: true
596
+ }
597
+ });
598
+
599
+ // Continue with other operations...
600
+ } catch (error) {
601
+ // Handle specific errors at each step
602
+ console.error('Fee estimation failed:', error);
603
+ }
604
+ ```
605
+
606
+ #### PoA Bridge Minimum Withdrawal Amount Validation
607
+
608
+ PoA bridge has minimum withdrawal amount requirements that vary per token and blockchain. The SDK automatically validates this for all withdrawals.
609
+
610
+ ```typescript
611
+ // Validation happens automatically during withdrawal processing:
612
+ try {
613
+ const result = await sdk.processWithdrawal({
614
+ withdrawalParams: {
615
+ assetId: 'nep141:zec.omft.near', // Zcash token
616
+ amount: BigInt('50000000'), // 0.5 ZEC (in smallest units)
617
+ destinationAddress: 'your-zcash-address',
618
+ feeInclusive: false
619
+ }
620
+ });
621
+ } catch (error) {
622
+ if (error instanceof MinWithdrawalAmountError) {
623
+ console.log(`Minimum withdrawal for ${error.assetId}: ${error.minAmount}`);
624
+ console.log(`Requested amount: ${error.requestedAmount}`);
625
+ // For Zcash: minimum is typically 1.0 ZEC (100000000 in smallest units)
626
+ // Plus 0.2 ZEC fee, so user needs at least 1.2 ZEC to withdraw 1.0 ZEC
627
+ }
628
+ }
629
+ ```
630
+
631
+ Note: Other routes (Near Withdrawal, Virtual Chain, Internal Transfer) don't have minimum withdrawal restrictions, so validation passes through for those routes.
632
+
633
+ #### Hot Bridge Stellar Trustline Validation
634
+
635
+ Hot Bridge validates that destination addresses have the required trustlines when withdrawing to Stellar blockchain. This prevents failed transactions due to missing trustlines.
636
+
637
+ ```typescript
638
+ import { TrustlineNotFoundError } from '@defuse-protocol/intents-sdk';
639
+
640
+ // Validation happens automatically during withdrawal processing:
641
+ try {
642
+ const result = await sdk.processWithdrawal({
643
+ withdrawalParams: {
644
+ assetId: 'nep245:v2_1.omni.hot.tg:stellar_1_USD_GBDMM6LG7YX7YGF6JFAEWX3KFUSBXGAEPZ2IHDLWH:1100', // Stellar USD token
645
+ amount: BigInt('1000000'), // 1 USD (in smallest units)
646
+ destinationAddress: 'GCKFBEIYTKP6RYVDYGMVVMJ6J6XKCRZL74JPWTFGD2NQNMPBQC2LGTVZ', // Stellar address
647
+ feeInclusive: false
648
+ }
649
+ });
650
+ } catch (error) {
651
+ if (error instanceof TrustlineNotFoundError) {
652
+ console.log(`Trustline not found for token: ${error.assetId}`);
653
+ console.log(`Destination address: ${error.destinationAddress}`);
654
+ console.log('The destination address must have a trustline for this token before withdrawal');
655
+ // User needs to create a trustline for the token on Stellar before withdrawing
656
+ }
657
+ }
658
+ ```
659
+
660
+ **What is a trustline?**
661
+ On Stellar, accounts must explicitly create "trustlines" to hold non-native assets. Before receiving any token (except XLM), the destination address must:
662
+ 1. Create a trustline for that specific token
663
+ 2. Have sufficient XLM balance to maintain the trustline
664
+
665
+ **Why this validation matters:**
666
+ - Prevents failed withdrawals due to missing trustlines
667
+ - Saves gas fees and reduces user frustration
668
+ - Provides clear error messages for troubleshooting
669
+
670
+ Note: This validation only applies to Stellar destinations via Hot Bridge. Other blockchains and routes don't require trustline validation.
671
+
672
+ TBD
673
+
674
+ ## Supported Networks
675
+
676
+ For a list of supported chains, see the [Chain Support page](https://docs.near-intents.org/near-intents/chain-address-support) in the Near Intents documentation.
677
+
678
+ ## Development
679
+
680
+ ### Prerequisites
681
+
682
+ - [Node.js](https://nodejs.org/) (v20 or later)
683
+ - [pnpm](https://pnpm.io/) for package management
684
+
685
+ ### Setup
686
+
687
+ ```bash
688
+ # Install dependencies (from the monorepo root)
689
+ pnpm install
690
+ ```
691
+
692
+ ### Build
693
+
694
+ ```bash
695
+ # Build the package
696
+ pnpm run build
697
+
698
+ # Build in watch mode
699
+ pnpm run dev
700
+ ```
701
+
702
+ ### Test
703
+
704
+ ```bash
705
+ # Run tests
706
+ pnpm run test
707
+
708
+ # Run tests in watch mode
709
+ pnpm run test:watch
710
+ ```
711
+
712
+ ### Lint and Format
713
+
714
+ ```bash
715
+ # Check code style
716
+ pnpm run lint
717
+
718
+ # Format code
719
+ pnpm run format
720
+ ```
721
+
722
+ ## Version History
723
+
724
+ See [CHANGELOG.md](./CHANGELOG.md) for detailed version history and migration guides.
725
+
726
+ ## Contributing
727
+
728
+ This package is part of Near Intents SDK monorepo. Please refer to the main repository's contributing guidelines.
729
+
730
+ ## License
731
+
732
+ MIT