@circle-fin/app-kit 1.12.0 → 1.13.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.
@@ -664,6 +664,11 @@ class KitError extends Error {
664
664
  name: 'INPUT_UNSUPPORTED_TOKEN',
665
665
  type: 'INPUT'
666
666
  },
667
+ /** Action not supported by this adapter / ecosystem */ UNSUPPORTED_ACTION: {
668
+ code: 1008,
669
+ name: 'INPUT_UNSUPPORTED_ACTION',
670
+ type: 'INPUT'
671
+ },
667
672
  /** No route satisfies the slippage or minimum-output constraint */ SLIPPAGE_CONSTRAINT_NOT_MET: {
668
673
  code: 1009,
669
674
  name: 'INPUT_SLIPPAGE_CONSTRAINT_NOT_MET',
@@ -2298,6 +2303,8 @@ class KitError extends Error {
2298
2303
  Blockchain["Optimism_Sepolia"] = "Optimism_Sepolia";
2299
2304
  Blockchain["Pharos"] = "Pharos";
2300
2305
  Blockchain["Pharos_Testnet"] = "Pharos_Testnet";
2306
+ Blockchain["Plasma"] = "Plasma";
2307
+ Blockchain["Plasma_Testnet"] = "Plasma_Testnet";
2301
2308
  Blockchain["Polkadot_Asset_Hub"] = "Polkadot_Asset_Hub";
2302
2309
  Blockchain["Polkadot_Westmint"] = "Polkadot_Westmint";
2303
2310
  Blockchain["Plume"] = "Plume";
@@ -2367,6 +2374,7 @@ var BridgeChain;
2367
2374
  BridgeChain["Morph"] = "Morph";
2368
2375
  BridgeChain["Optimism"] = "Optimism";
2369
2376
  BridgeChain["Pharos"] = "Pharos";
2377
+ BridgeChain["Plasma"] = "Plasma";
2370
2378
  BridgeChain["Plume"] = "Plume";
2371
2379
  BridgeChain["Polygon"] = "Polygon";
2372
2380
  BridgeChain["Sei"] = "Sei";
@@ -2393,6 +2401,7 @@ var BridgeChain;
2393
2401
  BridgeChain["Morph_Testnet"] = "Morph_Testnet";
2394
2402
  BridgeChain["Optimism_Sepolia"] = "Optimism_Sepolia";
2395
2403
  BridgeChain["Pharos_Testnet"] = "Pharos_Testnet";
2404
+ BridgeChain["Plasma_Testnet"] = "Plasma_Testnet";
2396
2405
  BridgeChain["Plume_Testnet"] = "Plume_Testnet";
2397
2406
  BridgeChain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
2398
2407
  BridgeChain["Sei_Testnet"] = "Sei_Testnet";
@@ -2839,6 +2848,8 @@ var EarnChain;
2839
2848
  * This program handles minting operations for Gateway transactions
2840
2849
  * on Solana devnet.
2841
2850
  */ const GATEWAY_MINTER_SOLANA_DEVNET = 'GATEmKK2ECL1brEngQZWCgMWPbvrEYqsV6u29dAaHavr';
2851
+ /** TokenMessengerWithFees address shared by enabled EVM mainnet sources. */ const TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET = '0x71f54F818671cD0D7ea140Da213e5C8b5C92a408';
2852
+ /** TokenMessengerWithFees address shared by enabled EVM testnet sources. */ const TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET = '0x8745D906D67C346E5eb1aEEED38Eb87F34DF0C0A';
2842
2853
  /**
2843
2854
  * Circle's fee recipient address for EVM chains.
2844
2855
  *
@@ -2886,6 +2897,7 @@ var EarnChain;
2886
2897
  v2: {
2887
2898
  type: 'split',
2888
2899
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
2900
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
2889
2901
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
2890
2902
  confirmations: 1,
2891
2903
  fastConfirmations: 1
@@ -2953,6 +2965,7 @@ var EarnChain;
2953
2965
  v2: {
2954
2966
  type: 'split',
2955
2967
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
2968
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
2956
2969
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
2957
2970
  confirmations: 65,
2958
2971
  fastConfirmations: 1
@@ -3017,6 +3030,7 @@ var EarnChain;
3017
3030
  v2: {
3018
3031
  type: 'split',
3019
3032
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
3033
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3020
3034
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
3021
3035
  confirmations: 65,
3022
3036
  fastConfirmations: 1
@@ -3081,6 +3095,7 @@ var EarnChain;
3081
3095
  v2: {
3082
3096
  type: 'split',
3083
3097
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3098
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
3084
3099
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3085
3100
  confirmations: 1,
3086
3101
  fastConfirmations: 1
@@ -3142,6 +3157,7 @@ var EarnChain;
3142
3157
  v2: {
3143
3158
  type: 'split',
3144
3159
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
3160
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3145
3161
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
3146
3162
  confirmations: 1,
3147
3163
  fastConfirmations: 1
@@ -3209,6 +3225,7 @@ var EarnChain;
3209
3225
  v2: {
3210
3226
  type: 'split',
3211
3227
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3228
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
3212
3229
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3213
3230
  confirmations: 65,
3214
3231
  fastConfirmations: 1
@@ -3273,6 +3290,7 @@ var EarnChain;
3273
3290
  v2: {
3274
3291
  type: 'split',
3275
3292
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
3293
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3276
3294
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
3277
3295
  confirmations: 65,
3278
3296
  fastConfirmations: 1
@@ -3383,6 +3401,7 @@ var EarnChain;
3383
3401
  v2: {
3384
3402
  type: 'split',
3385
3403
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3404
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
3386
3405
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3387
3406
  confirmations: 65,
3388
3407
  fastConfirmations: 1
@@ -3427,6 +3446,7 @@ var EarnChain;
3427
3446
  v2: {
3428
3447
  type: 'split',
3429
3448
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
3449
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3430
3450
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
3431
3451
  confirmations: 65,
3432
3452
  fastConfirmations: 1
@@ -3658,6 +3678,7 @@ var EarnChain;
3658
3678
  v2: {
3659
3679
  type: 'split',
3660
3680
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3681
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
3661
3682
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3662
3683
  confirmations: 65,
3663
3684
  fastConfirmations: 2
@@ -3722,6 +3743,7 @@ var EarnChain;
3722
3743
  v2: {
3723
3744
  type: 'split',
3724
3745
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
3746
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3725
3747
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
3726
3748
  confirmations: 65,
3727
3749
  fastConfirmations: 2
@@ -3832,6 +3854,7 @@ var EarnChain;
3832
3854
  v2: {
3833
3855
  type: 'split',
3834
3856
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
3857
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
3835
3858
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
3836
3859
  confirmations: 1,
3837
3860
  fastConfirmations: 1
@@ -3891,6 +3914,7 @@ var EarnChain;
3891
3914
  v2: {
3892
3915
  type: 'split',
3893
3916
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
3917
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
3894
3918
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
3895
3919
  confirmations: 1,
3896
3920
  fastConfirmations: 1
@@ -4045,6 +4069,7 @@ var EarnChain;
4045
4069
  v2: {
4046
4070
  type: 'split',
4047
4071
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4072
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4048
4073
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4049
4074
  confirmations: 65,
4050
4075
  fastConfirmations: 1
@@ -4092,6 +4117,7 @@ var EarnChain;
4092
4117
  v2: {
4093
4118
  type: 'split',
4094
4119
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
4120
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
4095
4121
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
4096
4122
  confirmations: 65,
4097
4123
  fastConfirmations: 1
@@ -4136,6 +4162,7 @@ var EarnChain;
4136
4162
  v2: {
4137
4163
  type: 'split',
4138
4164
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4165
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4139
4166
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4140
4167
  confirmations: 1,
4141
4168
  fastConfirmations: 1
@@ -4181,6 +4208,7 @@ var EarnChain;
4181
4208
  v2: {
4182
4209
  type: 'split',
4183
4210
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
4211
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
4184
4212
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
4185
4213
  confirmations: 1,
4186
4214
  fastConfirmations: 1
@@ -4227,6 +4255,7 @@ var EarnChain;
4227
4255
  v2: {
4228
4256
  type: 'split',
4229
4257
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4258
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4230
4259
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4231
4260
  confirmations: 1,
4232
4261
  fastConfirmations: 1
@@ -4540,6 +4569,7 @@ var EarnChain;
4540
4569
  v2: {
4541
4570
  type: 'split',
4542
4571
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4572
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4543
4573
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4544
4574
  confirmations: 65,
4545
4575
  fastConfirmations: 1
@@ -4604,6 +4634,7 @@ var EarnChain;
4604
4634
  v2: {
4605
4635
  type: 'split',
4606
4636
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
4637
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
4607
4638
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
4608
4639
  confirmations: 65,
4609
4640
  fastConfirmations: 1
@@ -4724,6 +4755,98 @@ var EarnChain;
4724
4755
  }
4725
4756
  });
4726
4757
 
4758
+ /**
4759
+ * Plasma Mainnet chain definition
4760
+ * @remarks
4761
+ * This represents the official production network for the Plasma blockchain.
4762
+ * Plasma is an EVM-equivalent Layer 1 blockchain purpose-built for global
4763
+ * stablecoin payments, with deterministic BFT finality (PlasmaBFT/Fast-HotStuff).
4764
+ */ const Plasma = defineChain({
4765
+ type: 'evm',
4766
+ chain: Blockchain.Plasma,
4767
+ name: 'Plasma',
4768
+ title: 'Plasma Mainnet',
4769
+ nativeCurrency: {
4770
+ name: 'Plasma',
4771
+ symbol: 'XPL',
4772
+ decimals: 18
4773
+ },
4774
+ chainId: 9745,
4775
+ isTestnet: false,
4776
+ explorerUrl: 'https://plasmascan.to/tx/{hash}',
4777
+ rpcEndpoints: [
4778
+ 'https://rpc.plasma.to'
4779
+ ],
4780
+ eurcAddress: '0x3EE196E78d4d4248b849B8E1C7F44C5457FAFD2C',
4781
+ usdcAddress: '0x2d661C89D812261039AF9764eceaAee884f5F67F',
4782
+ usdtAddress: null,
4783
+ cctp: {
4784
+ domain: 33,
4785
+ contracts: {
4786
+ v2: {
4787
+ type: 'split',
4788
+ tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4789
+ messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4790
+ confirmations: 3,
4791
+ fastConfirmations: 1
4792
+ }
4793
+ },
4794
+ forwarderSupported: {
4795
+ source: false,
4796
+ destination: false
4797
+ }
4798
+ },
4799
+ kitContracts: {
4800
+ bridge: BRIDGE_CONTRACT_EVM_MAINNET
4801
+ }
4802
+ });
4803
+
4804
+ /**
4805
+ * Plasma Testnet chain definition
4806
+ * @remarks
4807
+ * This represents the official test network for the Plasma blockchain.
4808
+ * Plasma is an EVM-equivalent Layer 1 blockchain purpose-built for global
4809
+ * stablecoin payments, with deterministic BFT finality (PlasmaBFT/Fast-HotStuff).
4810
+ */ const PlasmaTestnet = defineChain({
4811
+ type: 'evm',
4812
+ chain: Blockchain.Plasma_Testnet,
4813
+ name: 'Plasma Testnet',
4814
+ title: 'Plasma Testnet',
4815
+ nativeCurrency: {
4816
+ name: 'Plasma',
4817
+ symbol: 'XPL',
4818
+ decimals: 18
4819
+ },
4820
+ chainId: 9746,
4821
+ isTestnet: true,
4822
+ explorerUrl: 'https://testnet.plasmascan.to/tx/{hash}',
4823
+ rpcEndpoints: [
4824
+ 'https://testnet-rpc.plasma.to'
4825
+ ],
4826
+ eurcAddress: '0x98AfA0F93Dd993B736399f9074eDcEBD1985A330',
4827
+ usdcAddress: '0xE67Fb267022cBA8064Dd388CC2FED724F3120D9D',
4828
+ usdtAddress: null,
4829
+ cctp: {
4830
+ domain: 33,
4831
+ contracts: {
4832
+ v2: {
4833
+ type: 'split',
4834
+ tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
4835
+ messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
4836
+ confirmations: 3,
4837
+ fastConfirmations: 1
4838
+ }
4839
+ },
4840
+ forwarderSupported: {
4841
+ source: false,
4842
+ destination: false
4843
+ }
4844
+ },
4845
+ kitContracts: {
4846
+ bridge: BRIDGE_CONTRACT_EVM_TESTNET
4847
+ }
4848
+ });
4849
+
4727
4850
  /**
4728
4851
  * Plume Mainnet chain definition
4729
4852
  * @remarks
@@ -4755,6 +4878,7 @@ var EarnChain;
4755
4878
  v2: {
4756
4879
  type: 'split',
4757
4880
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
4881
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4758
4882
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4759
4883
  confirmations: 65,
4760
4884
  fastConfirmations: 1
@@ -4801,6 +4925,7 @@ var EarnChain;
4801
4925
  v2: {
4802
4926
  type: 'split',
4803
4927
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
4928
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
4804
4929
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
4805
4930
  confirmations: 65,
4806
4931
  fastConfirmations: 1
@@ -4902,6 +5027,7 @@ var EarnChain;
4902
5027
  v2: {
4903
5028
  type: 'split',
4904
5029
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
5030
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
4905
5031
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
4906
5032
  confirmations: 33,
4907
5033
  fastConfirmations: 13
@@ -4967,6 +5093,7 @@ var EarnChain;
4967
5093
  v2: {
4968
5094
  type: 'split',
4969
5095
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
5096
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
4970
5097
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
4971
5098
  confirmations: 33,
4972
5099
  fastConfirmations: 13
@@ -5026,6 +5153,7 @@ var EarnChain;
5026
5153
  v2: {
5027
5154
  type: 'split',
5028
5155
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
5156
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
5029
5157
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
5030
5158
  confirmations: 1,
5031
5159
  fastConfirmations: 1
@@ -5085,6 +5213,7 @@ var EarnChain;
5085
5213
  v2: {
5086
5214
  type: 'split',
5087
5215
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
5216
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
5088
5217
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
5089
5218
  confirmations: 1,
5090
5219
  fastConfirmations: 1
@@ -5142,6 +5271,7 @@ var EarnChain;
5142
5271
  v2: {
5143
5272
  type: 'split',
5144
5273
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
5274
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
5145
5275
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
5146
5276
  confirmations: 1,
5147
5277
  fastConfirmations: 1
@@ -5200,6 +5330,7 @@ var EarnChain;
5200
5330
  v2: {
5201
5331
  type: 'split',
5202
5332
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
5333
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
5203
5334
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
5204
5335
  confirmations: 1,
5205
5336
  fastConfirmations: 1
@@ -5515,6 +5646,7 @@ var EarnChain;
5515
5646
  v2: {
5516
5647
  type: 'split',
5517
5648
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
5649
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
5518
5650
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
5519
5651
  confirmations: 65,
5520
5652
  fastConfirmations: 1
@@ -5579,6 +5711,7 @@ var EarnChain;
5579
5711
  v2: {
5580
5712
  type: 'split',
5581
5713
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
5714
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
5582
5715
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
5583
5716
  confirmations: 65,
5584
5717
  fastConfirmations: 1
@@ -5636,6 +5769,7 @@ var EarnChain;
5636
5769
  v2: {
5637
5770
  type: 'split',
5638
5771
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cF5d',
5772
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
5639
5773
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
5640
5774
  confirmations: 65,
5641
5775
  fastConfirmations: 1
@@ -5695,6 +5829,7 @@ var EarnChain;
5695
5829
  v2: {
5696
5830
  type: 'split',
5697
5831
  tokenMessenger: '0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa',
5832
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
5698
5833
  messageTransmitter: '0xe737e5cebeeba77efe34d4aa090756590b1ce275',
5699
5834
  confirmations: 65,
5700
5835
  fastConfirmations: 1
@@ -5755,6 +5890,7 @@ var EarnChain;
5755
5890
  v2: {
5756
5891
  type: 'split',
5757
5892
  tokenMessenger: '0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d',
5893
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
5758
5894
  messageTransmitter: '0x81D40F21F12A8F0E3252Bccb954D722d4c464B64',
5759
5895
  confirmations: 3,
5760
5896
  fastConfirmations: 3
@@ -5800,6 +5936,7 @@ var EarnChain;
5800
5936
  v2: {
5801
5937
  type: 'split',
5802
5938
  tokenMessenger: '0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA',
5939
+ tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
5803
5940
  messageTransmitter: '0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275',
5804
5941
  confirmations: 3,
5805
5942
  fastConfirmations: 1
@@ -6010,6 +6147,8 @@ var Chains = {
6010
6147
  OptimismSepolia: OptimismSepolia,
6011
6148
  Pharos: Pharos,
6012
6149
  PharosTestnet: PharosTestnet,
6150
+ Plasma: Plasma,
6151
+ PlasmaTestnet: PlasmaTestnet,
6013
6152
  Plume: Plume,
6014
6153
  PlumeTestnet: PlumeTestnet,
6015
6154
  PolkadotAssetHub: PolkadotAssetHub,
@@ -7975,6 +8114,7 @@ function parseOrThrow(value, schema, context) {
7975
8114
  [Blockchain.Noble]: 'uusdc',
7976
8115
  [Blockchain.Optimism]: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
7977
8116
  [Blockchain.Pharos]: '0xC879C018dB60520F4355C26eD1a6D572cdAC1815',
8117
+ [Blockchain.Plasma]: '0x2d661C89D812261039AF9764eceaAee884f5F67F',
7978
8118
  [Blockchain.Plume]: '0x222365EF19F7947e5484218551B56bb3965Aa7aF',
7979
8119
  [Blockchain.Polkadot_Asset_Hub]: '1337',
7980
8120
  [Blockchain.Polygon]: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
@@ -7995,6 +8135,7 @@ function parseOrThrow(value, schema, context) {
7995
8135
  [Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
7996
8136
  [Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
7997
8137
  [Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
8138
+ [Blockchain.Celo_Alfajores_Testnet]: '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B',
7998
8139
  [Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
7999
8140
  [Blockchain.Cronos_Testnet]: '0xEb33dc5fac03833e132593659e1dE7256aB59794',
8000
8141
  [Blockchain.Edge_Testnet]: '0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B',
@@ -8010,6 +8151,7 @@ function parseOrThrow(value, schema, context) {
8010
8151
  [Blockchain.Noble_Testnet]: 'uusdc',
8011
8152
  [Blockchain.Optimism_Sepolia]: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7',
8012
8153
  [Blockchain.Pharos_Testnet]: '0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B',
8154
+ [Blockchain.Plasma_Testnet]: '0xE67Fb267022cBA8064Dd388CC2FED724F3120D9D',
8013
8155
  [Blockchain.Plume_Testnet]: '0xcB5f30e335672893c7eb944B374c196392C19D18',
8014
8156
  [Blockchain.Polkadot_Westmint]: '31337',
8015
8157
  [Blockchain.Polygon_Amoy_Testnet]: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
@@ -8072,6 +8214,7 @@ function parseOrThrow(value, schema, context) {
8072
8214
  [Blockchain.Base]: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42',
8073
8215
  [Blockchain.Cronos]: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8',
8074
8216
  [Blockchain.Ethereum]: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c',
8217
+ [Blockchain.Plasma]: '0x3EE196E78d4d4248b849B8E1C7F44C5457FAFD2C',
8075
8218
  [Blockchain.Solana]: 'HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr',
8076
8219
  [Blockchain.World_Chain]: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B',
8077
8220
  // =========================================================================
@@ -8080,7 +8223,8 @@ function parseOrThrow(value, schema, context) {
8080
8223
  [Blockchain.Arc_Testnet]: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a',
8081
8224
  [Blockchain.Base_Sepolia]: '0x808456652fdb597867f38412077A9182bf77359F',
8082
8225
  [Blockchain.Cronos_Testnet]: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46',
8083
- [Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4'
8226
+ [Blockchain.Ethereum_Sepolia]: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4',
8227
+ [Blockchain.Plasma_Testnet]: '0x98AfA0F93Dd993B736399f9074eDcEBD1985A330'
8084
8228
  }
8085
8229
  };
8086
8230
 
@@ -8887,7 +9031,7 @@ function parseOrThrow(value, schema, context) {
8887
9031
  }
8888
9032
 
8889
9033
  var name = "@circle-fin/unified-balance-kit";
8890
- var version = "1.4.0";
9034
+ var version = "1.5.0";
8891
9035
  var pkg = {
8892
9036
  name: name,
8893
9037
  version: version};
@@ -9170,6 +9314,110 @@ var pkg = {
9170
9314
  * ```
9171
9315
  */ const USDC_DECIMALS$1 = 6;
9172
9316
 
9317
+ /**
9318
+ * Canonical list of actions that do not prepare or submit transactions.
9319
+ *
9320
+ * @internal
9321
+ */ const READ_ACTION_KEYS = [
9322
+ 'token.allowance',
9323
+ 'token.balanceOf',
9324
+ 'token.name',
9325
+ 'native.balanceOf',
9326
+ 'usdc.allowance',
9327
+ 'usdc.balanceOf',
9328
+ 'usdc.name',
9329
+ 'gateway.v1.isDelegate',
9330
+ 'gateway.v1.withdrawingBalance',
9331
+ 'gateway.v1.withdrawalBlock',
9332
+ 'gateway.v1.signBurnIntents'
9333
+ ];
9334
+ const READ_ACTION_KEY_SET = new Set(READ_ACTION_KEYS);
9335
+ /**
9336
+ * Check whether a runtime value identifies a read action.
9337
+ *
9338
+ * @param action - The value to classify.
9339
+ * @returns Whether the value is a registered read-action key.
9340
+ *
9341
+ * @example
9342
+ * ```typescript
9343
+ * import { isReadActionKey } from '@core/adapter'
9344
+ *
9345
+ * if (isReadActionKey(value)) {
9346
+ * await adapter.readAction(value, params, context)
9347
+ * }
9348
+ * ```
9349
+ *
9350
+ * @internal
9351
+ */ function isReadActionKey(action) {
9352
+ return typeof action === 'string' && READ_ACTION_KEY_SET.has(action);
9353
+ }
9354
+
9355
+ /**
9356
+ * Create the standard error for a missing or non-read action.
9357
+ *
9358
+ * @param action - The unsupported action value.
9359
+ * @returns A fatal unsupported-action error.
9360
+ *
9361
+ * @internal
9362
+ */ function createUnsupportedReadActionError(action) {
9363
+ return new KitError({
9364
+ ...InputError.UNSUPPORTED_ACTION,
9365
+ recoverability: 'FATAL',
9366
+ message: `Read action "${String(action)}" is not registered in this adapter.`
9367
+ });
9368
+ }
9369
+ /**
9370
+ * Execute a read through the adapter's dedicated read seam when available.
9371
+ *
9372
+ * @remarks
9373
+ * Fall back to the legacy `prepareAction().execute()` contract so providers
9374
+ * remain runtime-compatible with adapter versions released before `readAction`.
9375
+ * Consumers must upgrade their adapter package for reads to bypass custom
9376
+ * `prepareAction` wrappers.
9377
+ *
9378
+ * @typeParam TAdapterCapabilities - The adapter capabilities type.
9379
+ * @typeParam TActionKey - The read action key.
9380
+ * @param adapter - The adapter that owns the read action.
9381
+ * @param action - The read action to execute.
9382
+ * @param params - The parameters for the read action.
9383
+ * @param ctx - The operation context.
9384
+ * @returns The raw read-action result.
9385
+ * @throws {KitError} When `action` is not a supported read-action key.
9386
+ *
9387
+ * @example
9388
+ * ```typescript
9389
+ * import { executeAdapterReadAction } from '@core/adapter'
9390
+ * import { Ethereum } from '@core/chains'
9391
+ *
9392
+ * const allowance = await executeAdapterReadAction(
9393
+ * adapter,
9394
+ * 'token.allowance',
9395
+ * { tokenAddress, delegate },
9396
+ * { chain: Ethereum },
9397
+ * )
9398
+ * ```
9399
+ *
9400
+ * @internal
9401
+ */ async function executeAdapterReadAction(adapter, action, params, ctx) {
9402
+ if (!isReadActionKey(action)) {
9403
+ throw createUnsupportedReadActionError(action);
9404
+ }
9405
+ const runtimeAdapter = adapter;
9406
+ if (typeof runtimeAdapter.readAction === 'function') {
9407
+ return runtimeAdapter.readAction(action, params, ctx);
9408
+ }
9409
+ let request;
9410
+ try {
9411
+ request = await adapter.prepareAction(action, params, ctx);
9412
+ } catch (error) {
9413
+ if (error instanceof Error && error.message === `Action ${action} is not supported`) {
9414
+ throw createUnsupportedReadActionError(action);
9415
+ }
9416
+ throw error;
9417
+ }
9418
+ return request.execute();
9419
+ }
9420
+
9173
9421
  /**
9174
9422
  * Schema for validating hexadecimal strings with '0x' prefix.
9175
9423
  *
@@ -9379,16 +9627,15 @@ var pkg = {
9379
9627
  * ```
9380
9628
  */ const validateBalanceForTransaction = async (params)=>{
9381
9629
  const { amount, adapter, token, tokenAddress, operationContext } = params;
9382
- const balancePrepared = await adapter.prepareAction('usdc.balanceOf', {
9630
+ const balance = await executeAdapterReadAction(adapter, 'usdc.balanceOf', {
9383
9631
  walletAddress: operationContext.address
9384
9632
  }, operationContext);
9385
- const balance = await balancePrepared.execute();
9386
- if (BigInt(balance) < BigInt(amount)) {
9633
+ if (BigInt(String(balance)) < BigInt(amount)) {
9387
9634
  // Extract chain name from operationContext
9388
9635
  const chainName = extractChainInfo(operationContext.chain).name;
9389
9636
  // Create KitError with rich context in trace
9390
9637
  throw createInsufficientTokenBalanceError(chainName, token, {
9391
- balance: balance.toString(),
9638
+ balance: String(balance),
9392
9639
  amount,
9393
9640
  tokenAddress,
9394
9641
  walletAddress: operationContext.address
@@ -12360,6 +12607,23 @@ async function signUnits(group, signingUnits) {
12360
12607
  }
12361
12608
  return signedSets;
12362
12609
  }
12610
+ /**
12611
+ * Fail loudly if a signing group carries an intent it does not own.
12612
+ *
12613
+ * Every intent in a group is signed by `group.address`, so that signer must own
12614
+ * each intent's `spec.sourceSigner`. If distinct spend sources ever collapse
12615
+ * into one group again (DEVX-3638), this throws before signing instead of
12616
+ * silently producing a wrong-signer signature. specs are bytes32 and
12617
+ * `group.address` is a native address, so normalize both before comparing.
12618
+ */ function assertGroupSignerOwnsIntents(group) {
12619
+ if (group.address == null || group.address === '') return;
12620
+ const groupSigner = convertAddress(group.address, 'bytes32').toLowerCase();
12621
+ for (const intent of group.intents){
12622
+ if (intent.spec.sourceSigner.toLowerCase() !== groupSigner) {
12623
+ throw createValidationFailedError$1('group.address', group.address, `Signing group for ${group.address} carries an intent whose ` + `sourceSigner is ${intent.spec.sourceSigner}; a group must be signed ` + 'by the wallet that owns every intent it carries. This indicates ' + 'distinct spend sources collapsed into a single signing group.');
12624
+ }
12625
+ }
12626
+ }
12363
12627
  /**
12364
12628
  * Sign an EVM adapter group.
12365
12629
  *
@@ -12402,22 +12666,42 @@ async function signUnits(group, signingUnits) {
12402
12666
  // Validate the collection before doing bytecode reads or asking a wallet
12403
12667
  // to sign. evmSigningData owns the canonical BurnIntent validation.
12404
12668
  validateGroupIntents(group.intents);
12669
+ // Guard against a wrong-signer collapse: the group's signer must own every
12670
+ // intent it carries.
12671
+ assertGroupSignerOwnsIntents(group);
12405
12672
  const chainsByDomain = collectChainsByDomain(group);
12406
12673
  const signerTypeByDomain = await classifySignerTypes(group, chainsByDomain);
12407
12674
  const signingUnits = createSigningUnits(group, signerTypeByDomain);
12408
12675
  return await signUnits(group, signingUnits);
12409
12676
  }
12410
12677
 
12678
+ // One adapter instance can serve several signer addresses (e.g. a single
12679
+ // dev-controlled adapter used for multiple wallets), so the group map must be
12680
+ // keyed on `(adapter, sourceSigner)` rather than the adapter reference alone.
12681
+ // Keying on the adapter alone collapsed two same-chain sources into one group,
12682
+ // signing the second wallet's intent with the first wallet (DEVX-3638 /
12683
+ // DEVX-3194). A Map needs a primitive key, so give each adapter instance a
12684
+ // stable token and combine it with the (case-normalized) signer address.
12685
+ const adapterTokens = new WeakMap();
12686
+ let nextAdapterToken = 0;
12687
+ function groupKey(alloc) {
12688
+ let token = adapterTokens.get(alloc.adapter);
12689
+ if (token === undefined) {
12690
+ token = nextAdapterToken++;
12691
+ adapterTokens.set(alloc.adapter, token);
12692
+ }
12693
+ return `${String(token)}:${alloc.sourceSigner.toLowerCase()}`;
12694
+ }
12411
12695
  /**
12412
12696
  * Add an EVM intent into the batched EVM group map.
12413
12697
  *
12414
- * On EVM, all intents for the same adapter are batched into a single
12415
- * group. The signing step uses `chainsByDomain` to preserve EOA batching
12698
+ * On EVM, all intents for the same adapter **and signer** are batched into a
12699
+ * single group. The signing step uses `chainsByDomain` to preserve EOA batching
12416
12700
  * while signing ERC-1271 intents separately on their source chains.
12417
12701
  *
12418
12702
  * @param intent - The burn intent to group.
12419
12703
  * @param alloc - The allocation that resolved to this intent.
12420
- * @param evmGroups - Mutable map of adapter → EVM group.
12704
+ * @param evmGroups - Mutable map of `(adapter, sourceSigner)` key → EVM group.
12421
12705
  *
12422
12706
  * @example
12423
12707
  * ```typescript
@@ -12427,12 +12711,13 @@ async function signUnits(group, signingUnits) {
12427
12711
  * groupEvmIntents(intent, alloc, groups)
12428
12712
  * ```
12429
12713
  */ function groupEvmIntents(intent, alloc, evmGroups) {
12430
- const existing = evmGroups.get(alloc.adapter);
12714
+ const key = groupKey(alloc);
12715
+ const existing = evmGroups.get(key);
12431
12716
  if (existing) {
12432
12717
  existing.intents.push(intent);
12433
12718
  existing.chainsByDomain.set(alloc.chain.gateway.domain, alloc.chain);
12434
12719
  } else {
12435
- evmGroups.set(alloc.adapter, {
12720
+ evmGroups.set(key, {
12436
12721
  adapter: alloc.adapter,
12437
12722
  chain: alloc.chain,
12438
12723
  intents: [
@@ -13461,6 +13746,8 @@ const gatewayInfoSchema = zod.z.object({
13461
13746
  * Fetch Gateway info from the `/v1/info` endpoint.
13462
13747
  *
13463
13748
  * @param isTestnet - Whether to query the testnet or mainnet API.
13749
+ * @param config - Optional polling config overrides, including custom
13750
+ * `headers` forwarded with the request.
13464
13751
  * @returns Parsed Gateway info including per-domain processed heights.
13465
13752
  * @throws KitError on HTTP failure or invalid response.
13466
13753
  *
@@ -13469,11 +13756,11 @@ const gatewayInfoSchema = zod.z.object({
13469
13756
  * const info = await fetchGatewayInfo(true)
13470
13757
  * console.log(info.domains.map(d => `${d.chain}: ${d.processedHeight}`))
13471
13758
  * ```
13472
- */ async function fetchGatewayInfo(isTestnet) {
13759
+ */ async function fetchGatewayInfo(isTestnet, config) {
13473
13760
  const apiBaseUrl = getGatewayApiBaseUrl(isTestnet);
13474
13761
  const url = `${apiBaseUrl}/v1/info`;
13475
13762
  try {
13476
- return await pollApiGet(url, assertGatewayInfoResponse);
13763
+ return await pollApiGet(url, assertGatewayInfoResponse, config);
13477
13764
  } catch (error) {
13478
13765
  if (error instanceof KitError) {
13479
13766
  throw error;
@@ -13499,6 +13786,8 @@ const gatewayInfoSchema = zod.z.object({
13499
13786
  *
13500
13787
  * @param isTestnet - Whether to query the testnet or mainnet API.
13501
13788
  * @param domain - The Gateway domain number (e.g. 0 = Ethereum, 6 = Base).
13789
+ * @param config - Optional polling config overrides, including custom
13790
+ * `headers` forwarded with the `/v1/info` request.
13502
13791
  * @returns The processed height as a bigint.
13503
13792
  * @throws KitError when the domain is not found in the response.
13504
13793
  *
@@ -13507,10 +13796,10 @@ const gatewayInfoSchema = zod.z.object({
13507
13796
  * const height = await getProcessedHeight(true, 6) // Base Sepolia
13508
13797
  * console.log(`Gateway processed up to block ${height}`)
13509
13798
  * ```
13510
- */ async function getProcessedHeight(isTestnet, domain) {
13799
+ */ async function getProcessedHeight(isTestnet, domain, config) {
13511
13800
  // No caching in v1 — /v1/info is cheap and delegate status checks
13512
13801
  // are infrequent. Revisit if rate-limit or latency issues appear.
13513
- const info = await fetchGatewayInfo(isTestnet);
13802
+ const info = await fetchGatewayInfo(isTestnet, config);
13514
13803
  const entry = info.domains.find((d)=>d.domain === domain);
13515
13804
  if (!entry) {
13516
13805
  throw new KitError({
@@ -13976,12 +14265,22 @@ function throwNetworkMismatch(expected, actual) {
13976
14265
  * @param allocations - Normalized allocations used to map domain → adapter/chain.
13977
14266
  * @returns Array of groups, each with adapter, chain, and intents to sign.
13978
14267
  */ function groupIntentsByAdapter(intents, allocations) {
14268
+ // Keyed by `(adapter, sourceSigner)` (see groupEvmIntents), not by adapter
14269
+ // alone: two spend sources on the same chain — including two addresses behind
14270
+ // one shared adapter — must stay in separate signing groups.
13979
14271
  const evmGroups = new Map();
13980
14272
  const solanaGroups = [];
13981
14273
  for (const intent of intents){
13982
- const alloc = allocations.find((a)=>a.chain.gateway.domain === intent.spec.sourceDomain);
14274
+ // Match each intent back to its source allocation by BOTH Gateway domain
14275
+ // and signer. Two spend sources on the same chain share a domain, so
14276
+ // matching on domain alone always returned the FIRST allocation — folding
14277
+ // the second wallet's intent into the first wallet's signing group so it
14278
+ // was signed by the wrong wallet (DEVX-3638). specs carry sourceSigner as
14279
+ // bytes32 and allocations carry native addresses, so normalize both.
14280
+ const intentSigner = intent.spec.sourceSigner.toLowerCase();
14281
+ const alloc = allocations.find((a)=>a.chain.gateway.domain === intent.spec.sourceDomain && convertAddress(a.sourceSigner, 'bytes32').toLowerCase() === intentSigner);
13983
14282
  if (!alloc) {
13984
- throw createValidationFailedError$1('intent.spec.sourceDomain', intent.spec.sourceDomain, `No allocation found for sourceDomain ${String(intent.spec.sourceDomain)}; ` + 'estimate response contains an intent that does not match any allocation');
14283
+ throw createValidationFailedError$1('intent.spec.sourceDomain', intent.spec.sourceDomain, `No allocation found for sourceDomain ${String(intent.spec.sourceDomain)} and sourceSigner ${String(intent.spec.sourceSigner)}; ` + 'estimate response contains an intent that does not match any allocation');
13985
14284
  }
13986
14285
  if (alloc.chain.type === 'solana') {
13987
14286
  solanaGroups.push(groupSolanaIntents(intent, alloc));
@@ -17398,12 +17697,12 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17398
17697
  * exceeds the 5-minute timeout without reaching a terminal status.
17399
17698
  * @throws KitError Propagated from {@link pollApiGet} if the HTTP
17400
17699
  * request itself fails or returns a non-OK response.
17401
- */ async function pollForwarderCompletion(apiBaseUrl, transferId) {
17700
+ */ async function pollForwarderCompletion(apiBaseUrl, transferId, requestConfig) {
17402
17701
  const startTime = Date.now();
17403
17702
  while(Date.now() - startTime < FORWARDER_POLL_TIMEOUT_MS){
17404
17703
  let status;
17405
17704
  try {
17406
- status = await pollApiGet(`${apiBaseUrl}/v1/transfer/${transferId}`, assertGatewayTransferDetails);
17705
+ status = await pollApiGet(`${apiBaseUrl}/v1/transfer/${transferId}`, assertGatewayTransferDetails, requestConfig);
17407
17706
  } catch (error) {
17408
17707
  throwGatewayApiError(error);
17409
17708
  }
@@ -17521,7 +17820,7 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17521
17820
  * @param params - Spend parameters (source(s) and token).
17522
17821
  * @param destChain - Resolved destination chain (used for network type).
17523
17822
  * @returns Confirmed balances tagged with their originating source index.
17524
- */ async function fetchChainBalances(params, destChain) {
17823
+ */ async function fetchChainBalances(params, destChain, requestConfig) {
17525
17824
  const rawSources = Array.isArray(params.from) ? params.from : [
17526
17825
  params.from
17527
17826
  ];
@@ -17550,7 +17849,7 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17550
17849
  token: params.token,
17551
17850
  sources: querySource,
17552
17851
  networkType
17553
- });
17852
+ }, requestConfig);
17554
17853
  }));
17555
17854
  const chainBalances = [];
17556
17855
  for(let i = 0; i < balanceResults.length; i++){
@@ -17623,9 +17922,9 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17623
17922
  * @param recipientAddress - Resolved recipient address.
17624
17923
  * @param useForwarder - Whether the Forwarding Service path is active.
17625
17924
  * @returns Allocations, intents, and (auto-allocation only) confirmed balances.
17626
- */ async function resolveAllocationsAndIntents(params, destChain, recipientAddress, useForwarder) {
17925
+ */ async function resolveAllocationsAndIntents(params, destChain, recipientAddress, useForwarder, requestConfig) {
17627
17926
  if (params.amountIn) {
17628
- const chainBalances = await fetchChainBalances(params, destChain);
17927
+ const chainBalances = await fetchChainBalances(params, destChain, requestConfig);
17629
17928
  const { allocations, intents } = await buildAutoAllocatedFromBalances(params, destChain, recipientAddress, useForwarder, params.amountIn, chainBalances);
17630
17929
  return {
17631
17930
  allocations,
@@ -17648,11 +17947,11 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17648
17947
  * @param useForwarder - Whether the Forwarding Service path is active.
17649
17948
  * @param allocations - Normalised allocations (forwarded to error handler).
17650
17949
  * @returns Estimate entries and optional forwarding fee.
17651
- */ async function fetchEstimate(apiBaseUrl, estimateBody, useForwarder, allocations) {
17950
+ */ async function fetchEstimate(apiBaseUrl, estimateBody, useForwarder, allocations, requestConfig) {
17652
17951
  if (useForwarder) {
17653
17952
  let fwdResponse;
17654
17953
  try {
17655
- fwdResponse = await pollApiPost(`${apiBaseUrl}/v1/estimate?enableForwarder=true`, estimateBody, assertGatewayForwarderEstimateResponse);
17954
+ fwdResponse = await pollApiPost(`${apiBaseUrl}/v1/estimate?enableForwarder=true`, estimateBody, assertGatewayForwarderEstimateResponse, requestConfig);
17656
17955
  } catch (error) {
17657
17956
  throwGatewayApiError(error, allocations);
17658
17957
  }
@@ -17663,7 +17962,7 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17663
17962
  }
17664
17963
  let entries;
17665
17964
  try {
17666
- entries = await pollApiPost(`${apiBaseUrl}/v1/estimate`, estimateBody, assertGatewayEstimateResponse);
17965
+ entries = await pollApiPost(`${apiBaseUrl}/v1/estimate`, estimateBody, assertGatewayEstimateResponse, requestConfig);
17667
17966
  } catch (error) {
17668
17967
  throwGatewayApiError(error, allocations);
17669
17968
  }
@@ -17734,14 +18033,14 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17734
18033
  * @param destChain - Resolved destination chain with Gateway v1 config.
17735
18034
  * @param useForwarder - Whether the Forwarding Service path is active.
17736
18035
  * @returns Estimated intents (with real maxFee) and optional forwarding fee.
17737
- */ async function validateAndEstimate(allocations, intents, destChain, useForwarder) {
18036
+ */ async function validateAndEstimate(allocations, intents, destChain, useForwarder, requestConfig) {
17738
18037
  assertNetworkCompatibility(allocations, destChain);
17739
18038
  if (useForwarder) {
17740
18039
  assertForwarderRouteSupport(destChain, allocations);
17741
18040
  }
17742
18041
  const apiBaseUrl = getGatewayApiBaseUrl(destChain.isTestnet);
17743
18042
  const estimateBody = buildEstimateRequestBody(intents);
17744
- const { entries, forwardingFee } = await fetchEstimate(apiBaseUrl, estimateBody, useForwarder, allocations);
18043
+ const { entries, forwardingFee } = await fetchEstimate(apiBaseUrl, estimateBody, useForwarder, allocations, requestConfig);
17745
18044
  const estimatedIntents = parseEstimateResponse(entries, intents);
17746
18045
  return {
17747
18046
  estimatedIntents,
@@ -17800,7 +18099,7 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17800
18099
  }
17801
18100
  throw err;
17802
18101
  }
17803
- const { estimatedIntents, forwardingFee } = await validateAndEstimate(corrected.allocations, corrected.intents, opts.destChain, opts.useForwarder);
18102
+ const { estimatedIntents, forwardingFee } = await validateAndEstimate(corrected.allocations, corrected.intents, opts.destChain, opts.useForwarder, opts.requestConfig);
17804
18103
  const stillOverdrawn = findOverdrawnChains(estimatedIntents, corrected.allocations, opts.chainBalances);
17805
18104
  if (stillOverdrawn.length === 0) {
17806
18105
  return {
@@ -17834,12 +18133,12 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17834
18133
  * @param recipientAddress - Resolved recipient address.
17835
18134
  * @param useForwarder - Whether the Forwarding Service path is active.
17836
18135
  * @returns Allocations, estimated intents, and optional forwarding fee.
17837
- */ async function resolveValidatedIntents(params, destChain, recipientAddress, useForwarder) {
18136
+ */ async function resolveValidatedIntents(params, destChain, recipientAddress, useForwarder, requestConfig) {
17838
18137
  if (useForwarder) {
17839
18138
  assertForwarderRouteSupport(destChain);
17840
18139
  }
17841
- const { allocations, intents, chainBalances } = await resolveAllocationsAndIntents(params, destChain, recipientAddress, useForwarder);
17842
- const { estimatedIntents, forwardingFee } = await validateAndEstimate(allocations, intents, destChain, useForwarder);
18140
+ const { allocations, intents, chainBalances } = await resolveAllocationsAndIntents(params, destChain, recipientAddress, useForwarder, requestConfig);
18141
+ const { estimatedIntents, forwardingFee } = await validateAndEstimate(allocations, intents, destChain, useForwarder, requestConfig);
17843
18142
  if (params.amountIn && chainBalances) {
17844
18143
  const overdrawn = findOverdrawnChains(estimatedIntents, allocations, chainBalances);
17845
18144
  if (overdrawn.length > 0) {
@@ -17852,7 +18151,10 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17852
18151
  chainBalances,
17853
18152
  estimatedIntents,
17854
18153
  allocations,
17855
- overdrawn
18154
+ overdrawn,
18155
+ ...requestConfig ? {
18156
+ requestConfig
18157
+ } : {}
17856
18158
  });
17857
18159
  }
17858
18160
  }
@@ -17896,10 +18198,10 @@ const FORWARDER_POLL_TIMEOUT_MS = 300_000;
17896
18198
  attestationSignature: transferResponse.signature
17897
18199
  };
17898
18200
  }
17899
- async function runSpendNormalPath(params, destChain, useForwarder, dispatcher, steps) {
18201
+ async function runSpendNormalPath(params, destChain, useForwarder, dispatcher, steps, requestConfig) {
17900
18202
  const chainName = destChain.name;
17901
18203
  const recipientAddress = await resolveRecipientAddress(params.to);
17902
- const { allocations, estimatedIntents, forwardingFee: estimateFwdFee } = await resolveValidatedIntents(params, destChain, recipientAddress, useForwarder);
18204
+ const { allocations, estimatedIntents, forwardingFee: estimateFwdFee } = await resolveValidatedIntents(params, destChain, recipientAddress, useForwarder, requestConfig);
17903
18205
  pushStep(steps, dispatcher, 'gateway.spend.step.buildBurnIntents', chainName, {
17904
18206
  name: 'buildBurnIntents',
17905
18207
  state: 'success',
@@ -17936,7 +18238,7 @@ async function runSpendNormalPath(params, destChain, useForwarder, dispatcher, s
17936
18238
  const forwarderParam = useForwarder ? '?enableForwarder=true' : '';
17937
18239
  let transferResponse;
17938
18240
  try {
17939
- transferResponse = await pollApiPost(`${transferApiBaseUrl}/v1/transfer${forwarderParam}`, transferBody, assertGatewayTransferResponse);
18241
+ transferResponse = await pollApiPost(`${transferApiBaseUrl}/v1/transfer${forwarderParam}`, transferBody, assertGatewayTransferResponse, requestConfig);
17940
18242
  } catch (error) {
17941
18243
  throwGatewayApiError(error, allocations);
17942
18244
  }
@@ -18016,10 +18318,11 @@ function updateFeesWithActual(fees, completionStatus) {
18016
18318
  amount: actual
18017
18319
  } : f);
18018
18320
  }
18019
- async function resolveDestinationTxHash(params, destChain, forwarder, transferId, attestation, attestationSignature, fees) {
18321
+ async function resolveDestinationTxHash(opts) {
18322
+ const { params, destChain, forwarder, transferId, attestation, attestationSignature, fees, requestConfig } = opts;
18020
18323
  if (forwarder && transferId) {
18021
18324
  const apiBaseUrl = getGatewayApiBaseUrl(destChain.isTestnet);
18022
- const completionStatus = await pollForwarderCompletion(apiBaseUrl, transferId);
18325
+ const completionStatus = await pollForwarderCompletion(apiBaseUrl, transferId, requestConfig);
18023
18326
  if (!completionStatus.transactionHash) {
18024
18327
  throw new KitError({
18025
18328
  ...OnchainError.TRANSACTION_REVERTED,
@@ -18102,9 +18405,20 @@ function buildSpendResult(recipientAddress, destChain, txHash, allocationResults
18102
18405
  /**
18103
18406
  * Execute the mint step, recording success/error as a spend step.
18104
18407
  */ async function executeMintStep(opts) {
18105
- const { params, destChain, forwarder, transferId, attestation, attestationSignature, fees, steps, dispatcher } = opts;
18408
+ const { params, destChain, forwarder, transferId, attestation, attestationSignature, fees, steps, dispatcher, requestConfig } = opts;
18106
18409
  try {
18107
- const mintResult = await resolveDestinationTxHash(params, destChain, forwarder, transferId, attestation, attestationSignature, fees);
18410
+ const mintResult = await resolveDestinationTxHash({
18411
+ params,
18412
+ destChain,
18413
+ forwarder,
18414
+ transferId,
18415
+ attestation,
18416
+ attestationSignature,
18417
+ fees,
18418
+ ...requestConfig ? {
18419
+ requestConfig
18420
+ } : {}
18421
+ });
18108
18422
  pushStep(steps, dispatcher, 'gateway.spend.step.mint', destChain.name, {
18109
18423
  name: 'mint',
18110
18424
  state: 'success',
@@ -18159,7 +18473,8 @@ function buildSpendResult(recipientAddress, destChain, txHash, allocationResults
18159
18473
  assertSpendAddresses(params, destChain);
18160
18474
  const forwarder = isForwarderEnabled(params.to);
18161
18475
  const steps = [];
18162
- const context = params.config?.retry ? await resolveRetryContext(params) : await resolveNormalContext(params, destChain, forwarder, dispatcher, steps);
18476
+ const requestConfig = options?.requestConfig;
18477
+ const context = params.config?.retry ? await resolveRetryContext(params) : await resolveNormalContext(params, destChain, forwarder, dispatcher, steps, requestConfig);
18163
18478
  const fees = appendForwarderFee(context.fees, context.forwardingFee);
18164
18479
  const mintResult = await executeMintStep({
18165
18480
  params,
@@ -18170,7 +18485,10 @@ function buildSpendResult(recipientAddress, destChain, txHash, allocationResults
18170
18485
  attestationSignature: context.attestationSignature,
18171
18486
  fees,
18172
18487
  steps,
18173
- dispatcher
18488
+ dispatcher,
18489
+ ...requestConfig ? {
18490
+ requestConfig
18491
+ } : {}
18174
18492
  });
18175
18493
  // A throwing `onBroadcast` must never strand the caller after a
18176
18494
  // successful mint — the chain has moved. `safeInvokeCallback` swallows
@@ -18187,14 +18505,14 @@ function buildSpendResult(recipientAddress, destChain, txHash, allocationResults
18187
18505
  /**
18188
18506
  * Resolve the spend context via the normal (non-retry) path.
18189
18507
  * Validates `from`, then delegates to `runSpendNormalPath`.
18190
- */ async function resolveNormalContext(params, destChain, forwarder, dispatcher, steps) {
18508
+ */ async function resolveNormalContext(params, destChain, forwarder, dispatcher, steps, requestConfig) {
18191
18509
  if (!params.from) {
18192
18510
  throw createValidationFailedError$1('from', params.from, 'Source (from) is required when config.retry is not provided.');
18193
18511
  }
18194
18512
  return runSpendNormalPath({
18195
18513
  ...params,
18196
18514
  from: params.from
18197
- }, destChain, forwarder, dispatcher, steps);
18515
+ }, destChain, forwarder, dispatcher, steps, requestConfig);
18198
18516
  }
18199
18517
  /**
18200
18518
  * Estimate fees for a Gateway spend without executing it.
@@ -18217,13 +18535,13 @@ function buildSpendResult(recipientAddress, destChain, txHash, allocationResults
18217
18535
  * })
18218
18536
  * fees.forEach((f) => console.log(f.type, f.amount, f.allocations))
18219
18537
  * ```
18220
- */ async function estimateSpend$1(params) {
18538
+ */ async function estimateSpend$1(params, requestConfig) {
18221
18539
  const destChain = resolveChainIdentifier(params.to.chain);
18222
18540
  assertGatewayV1(destChain);
18223
18541
  assertSpendAddresses(params, destChain);
18224
18542
  const forwarder = isForwarderEnabled(params.to);
18225
18543
  const recipientAddress = await resolveRecipientAddress(params.to);
18226
- const { allocations, estimatedIntents, forwardingFee } = await resolveValidatedIntents(params, destChain, recipientAddress, forwarder);
18544
+ const { allocations, estimatedIntents, forwardingFee } = await resolveValidatedIntents(params, destChain, recipientAddress, forwarder, requestConfig);
18227
18545
  const fees = computeEstimateFees(estimatedIntents, allocations, params.config?.customFee);
18228
18546
  if (forwardingFee) {
18229
18547
  fees.push({
@@ -18347,6 +18665,9 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18347
18665
  * the delegate is `'ready'` (finalized); otherwise it is `'pending'`.
18348
18666
  *
18349
18667
  * @param params - The adapter context (`from`), delegate address, and token.
18668
+ * @param requestConfig - Optional polling config overrides, including custom
18669
+ * `headers` forwarded with the `/v1/info` request used on the EVM finality
18670
+ * check.
18350
18671
  * @returns Promise resolving to `'none'`, `'pending'`, or `'ready'`.
18351
18672
  * @throws KitError if the chain is unsupported or `/v1/info` fails.
18352
18673
  *
@@ -18366,7 +18687,7 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18366
18687
  * const status = await getDelegateStatus(params)
18367
18688
  * if (status === 'ready') { // safe to spend }
18368
18689
  * ```
18369
- */ async function getDelegateStatus$1(params) {
18690
+ */ async function getDelegateStatus$1(params, requestConfig) {
18370
18691
  const { from, delegateAddress } = params;
18371
18692
  const { adapter } = from;
18372
18693
  const chain = resolveChainIdentifier(from.chain);
@@ -18382,8 +18703,7 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18382
18703
  chain
18383
18704
  };
18384
18705
  // Step 1: Quick check at latest block (no HTTP call)
18385
- const latestRequest = await adapter.prepareAction('gateway.v1.isDelegate', baseActionParams, operationContext);
18386
- const latestResult = await latestRequest.execute();
18706
+ const latestResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', baseActionParams, operationContext);
18387
18707
  if (String(latestResult).toLowerCase() !== 'true') {
18388
18708
  return 'none';
18389
18709
  }
@@ -18392,13 +18712,12 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18392
18712
  // Solana uses confirmed vs finalized commitment as a proxy for
18393
18713
  // Gateway finality. This is conservative — can only over-report
18394
18714
  // 'pending', never falsely report 'ready'.
18395
- const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
18396
- ...baseActionParams,
18397
- commitment: 'finalized'
18398
- }, operationContext);
18399
18715
  let finalizedResult;
18400
18716
  try {
18401
- finalizedResult = await finalizedRequest.execute();
18717
+ finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
18718
+ ...baseActionParams,
18719
+ commitment: 'finalized'
18720
+ }, operationContext);
18402
18721
  } catch (error) {
18403
18722
  if (isBlockRangeError(error)) {
18404
18723
  return 'pending';
@@ -18408,11 +18727,7 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18408
18727
  return String(finalizedResult).toLowerCase() === 'true' ? 'ready' : 'pending';
18409
18728
  }
18410
18729
  // EVM: use processedHeight from /v1/info
18411
- const processedHeight = await getProcessedHeight(chain.isTestnet, chain.gateway.domain);
18412
- const finalizedRequest = await adapter.prepareAction('gateway.v1.isDelegate', {
18413
- ...baseActionParams,
18414
- blockNumber: processedHeight
18415
- }, operationContext);
18730
+ const processedHeight = await getProcessedHeight(chain.isTestnet, chain.gateway.domain, requestConfig);
18416
18731
  // If the RPC node lags Gateway's indexer view, the historical read at
18417
18732
  // processedHeight may throw a block-range error. This is safe to treat
18418
18733
  // as 'pending' because processedHeight comes from Gateway's /v1/info
@@ -18421,7 +18736,10 @@ function assertNotSelfDelegation(chain, signerAddress, delegateAddress, action)
18421
18736
  // Re-throw structural errors to avoid masking real bugs.
18422
18737
  let finalizedResult;
18423
18738
  try {
18424
- finalizedResult = await finalizedRequest.execute();
18739
+ finalizedResult = await executeAdapterReadAction(adapter, 'gateway.v1.isDelegate', {
18740
+ ...baseActionParams,
18741
+ blockNumber: processedHeight
18742
+ }, operationContext);
18425
18743
  } catch (error) {
18426
18744
  if (isBlockRangeError(error)) {
18427
18745
  return 'pending';
@@ -18482,8 +18800,8 @@ function parseAmountSafe(amount) {
18482
18800
  chain
18483
18801
  };
18484
18802
  const [withdrawingRaw, withdrawalBlockRaw] = await Promise.all([
18485
- adapter.prepareAction('gateway.v1.withdrawingBalance', readParams, operationContext).then(async (req)=>req.execute()),
18486
- adapter.prepareAction('gateway.v1.withdrawalBlock', readParams, operationContext).then(async (req)=>req.execute())
18803
+ executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', readParams, operationContext),
18804
+ executeAdapterReadAction(adapter, 'gateway.v1.withdrawalBlock', readParams, operationContext)
18487
18805
  ]);
18488
18806
  const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
18489
18807
  const withdrawalBlockValue = safeBigInt(String(withdrawalBlockRaw), 'withdrawalBlock');
@@ -18523,12 +18841,11 @@ function parseAmountSafe(amount) {
18523
18841
  const tokenAddress = getTokenAddress(chain, params.token);
18524
18842
  // Read the pending balance before withdrawing — the contract resets it to 0
18525
18843
  // after withdraw() executes, so this is the only way to capture the amount.
18526
- const withdrawingBalanceReq = await adapter.prepareAction('gateway.v1.withdrawingBalance', {
18844
+ const withdrawingRaw = await executeAdapterReadAction(adapter, 'gateway.v1.withdrawingBalance', {
18527
18845
  token: tokenAddress,
18528
18846
  depositor: signerAddress,
18529
18847
  chain
18530
18848
  }, operationContext);
18531
- const withdrawingRaw = await withdrawingBalanceReq.execute();
18532
18849
  const withdrawingValue = safeBigInt(String(withdrawingRaw), 'withdrawingBalance');
18533
18850
  if (withdrawingValue === 0n) {
18534
18851
  throw new KitError({
@@ -18628,6 +18945,35 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18628
18945
  dispatchEvent(dispatcher, opName, 'failed', chain, extractErrorInfo(error));
18629
18946
  }
18630
18947
 
18948
+ // ---------------------------------------------------------------------------
18949
+ // Config validation
18950
+ // ---------------------------------------------------------------------------
18951
+ function isPlainObject(value) {
18952
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
18953
+ return false;
18954
+ }
18955
+ const prototype = Object.getPrototypeOf(value);
18956
+ return prototype === Object.prototype || prototype === null;
18957
+ }
18958
+ function assertHeadersConfig(headers) {
18959
+ if (headers === undefined) {
18960
+ return;
18961
+ }
18962
+ if (!isPlainObject(headers)) {
18963
+ throw createValidationFailedError$1('config.headers', headers, 'config.headers must be a plain object with string header values when provided');
18964
+ }
18965
+ for (const [name, value] of Object.entries(headers)){
18966
+ if (typeof value !== 'string') {
18967
+ throw createValidationFailedError$1(`config.headers.${name}`, value, 'header values must be strings');
18968
+ }
18969
+ }
18970
+ }
18971
+ function assertGatewayV1Config(config) {
18972
+ if (!isPlainObject(config)) {
18973
+ throw createValidationFailedError$1('config', config, 'config must be a plain object when provided');
18974
+ }
18975
+ assertHeadersConfig(config['headers']);
18976
+ }
18631
18977
  // ---------------------------------------------------------------------------
18632
18978
  // Factory
18633
18979
  // ---------------------------------------------------------------------------
@@ -18637,7 +18983,12 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18637
18983
  * Returns a plain object that implements {@link IGatewayProvider} by
18638
18984
  * delegating to the standalone functions in `operations/`.
18639
18985
  *
18986
+ * @param config - Optional provider configuration. Supply `headers` to
18987
+ * forward custom HTTP headers with every Gateway API request. See
18988
+ * {@link GatewayV1Config}.
18640
18989
  * @returns An {@link IGatewayProvider} instance.
18990
+ * @throws Throws when `config` or `config.headers` is not a plain object, or a
18991
+ * header value is not a string.
18641
18992
  *
18642
18993
  * @example
18643
18994
  * ```typescript
@@ -18646,7 +18997,23 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18646
18997
  * const provider = createGatewayV1Provider()
18647
18998
  * const chains = provider.getSupportedChains()
18648
18999
  * ```
18649
- */ function createGatewayV1Provider() {
19000
+ *
19001
+ * @example
19002
+ * ```typescript
19003
+ * // Forward a custom header on every Gateway API request.
19004
+ * const provider = createGatewayV1Provider({
19005
+ * headers: { 'X-Access-Key': process.env.GATEWAY_ACCESS_KEY! },
19006
+ * })
19007
+ * ```
19008
+ */ function createGatewayV1Provider(config = {}) {
19009
+ assertGatewayV1Config(config);
19010
+ // Captured once and threaded to every Gateway API call the operations make,
19011
+ // so a configured header reaches balances, estimate, transfer, forwarder
19012
+ // status, and /v1/info alike. Left undefined when no headers are configured
19013
+ // so the operations keep their existing defaults untouched.
19014
+ const requestConfig = config.headers ? {
19015
+ headers: config.headers
19016
+ } : undefined;
18650
19017
  /**
18651
19018
  * Execute an operation with started/succeeded/failed event dispatch.
18652
19019
  */ async function withEvents(opName, chain, startedData, params, fn) {
@@ -18677,21 +19044,25 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18677
19044
  }, params, depositFor$1),
18678
19045
  spend: async (params, options)=>{
18679
19046
  const destChainName = extractChainInfo(params.to.chain).name;
19047
+ const mergedOptions = requestConfig ? {
19048
+ ...options,
19049
+ requestConfig
19050
+ } : options;
18680
19051
  return withEvents('spend', destChainName, {
18681
19052
  destinationChain: destChainName,
18682
19053
  token: params.token ?? 'USDC'
18683
- }, params, async (p)=>spend$1(p, provider.actionDispatcher, options));
19054
+ }, params, async (p)=>spend$1(p, provider.actionDispatcher, mergedOptions));
18684
19055
  },
18685
19056
  estimateSpend: async (params)=>{
18686
19057
  const destChainName = extractChainInfo(params.to.chain).name;
18687
19058
  return withEvents('estimateSpend', destChainName, {
18688
19059
  destinationChain: destChainName,
18689
19060
  token: params.token ?? 'USDC'
18690
- }, params, estimateSpend$1);
19061
+ }, params, async (p)=>estimateSpend$1(p, requestConfig));
18691
19062
  },
18692
19063
  getBalances: async (params)=>withEvents('getBalances', 'aggregate', {
18693
19064
  token: params.token ?? 'USDC'
18694
- }, params, getBalances$1),
19065
+ }, params, async (p)=>getBalances$1(p, requestConfig)),
18695
19066
  addDelegate: async (params)=>withEvents('addDelegate', extractChainInfo(params.from.chain).name, {
18696
19067
  delegateAddress: params.delegateAddress
18697
19068
  }, params, addDelegate$1),
@@ -18709,7 +19080,9 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18709
19080
  // in withEvents. getSupportedChains is synchronous. getDelegateStatus is a
18710
19081
  // lightweight read that consumers treat as a utility check, unlike
18711
19082
  // getBalances which involves API polling and benefits from lifecycle events.
18712
- getDelegateStatus: getDelegateStatus$1,
19083
+ // getDelegateStatus is wrapped to forward the provider's request config to
19084
+ // its /v1/info call; getSupportedChains makes no API call.
19085
+ getDelegateStatus: async (params)=>getDelegateStatus$1(params, requestConfig),
18713
19086
  getSupportedChains: getSupportedChains$1
18714
19087
  };
18715
19088
  return provider;
@@ -18719,6 +19092,7 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18719
19092
  * The default providers that will be used in addition to the providers
18720
19093
  * provided to the createUnifiedBalanceKitContext factory function.
18721
19094
  *
19095
+ * @param config - Optional configuration forwarded to the default provider.
18722
19096
  * @returns A readonly tuple with one element: the default Gateway v1 provider
18723
19097
  * instance created via {@link createGatewayV1Provider}.
18724
19098
  * @internal
@@ -18730,8 +19104,10 @@ function dispatchEvent(dispatcher, opName, stage, chain, data) {
18730
19104
  * const { providers } = createUnifiedBalanceKitContext()
18731
19105
  * // providers[0] is the default Gateway v1 instance from getDefaultProviders()
18732
19106
  * ```
18733
- */ const getDefaultProviders = ()=>[
18734
- createGatewayV1Provider()
19107
+ */ const getDefaultProviders = (config = {})=>[
19108
+ createGatewayV1Provider(config.headers ? {
19109
+ headers: config.headers
19110
+ } : {})
18735
19111
  ];
18736
19112
  function createUnifiedBalanceKitContext(config = {}) {
18737
19113
  // Runtime guard: `excludeDefaultProviders: true` with no `providers` is
@@ -18745,7 +19121,10 @@ function createUnifiedBalanceKitContext(config = {}) {
18745
19121
  // Initialize default providers — either the canonical singleton tuple or
18746
19122
  // an empty list when the caller has opted out. Typed loosely here; each
18747
19123
  // public overload narrows the return type for callers.
18748
- const defaultProviders = config.excludeDefaultProviders === true ? [] : getDefaultProviders();
19124
+ const defaultProvidersConfig = config.headers ? {
19125
+ headers: config.headers
19126
+ } : {};
19127
+ const defaultProviders = config.excludeDefaultProviders === true ? [] : getDefaultProviders(defaultProvidersConfig);
18749
19128
  const providers = [
18750
19129
  ...defaultProviders,
18751
19130
  ...config.providers ?? []
@@ -19436,29 +19815,65 @@ function sumAllocationsFromSources(sources) {
19436
19815
  }
19437
19816
  });
19438
19817
 
19818
+ function normalizeAddressIdentity(value) {
19819
+ if (typeof value !== 'string') return undefined;
19820
+ try {
19821
+ return convertAddress(value, 'bytes32').toLowerCase();
19822
+ } catch {
19823
+ // Address validation below owns the structured error for invalid input.
19824
+ return value;
19825
+ }
19826
+ }
19439
19827
  /**
19440
- * Reject `from` arrays containing the same adapter reference more than once.
19441
- * Must run on the raw input (before Zod parsing) because `z.object()` creates
19442
- * new wrapper objects and identity (`===`) is lost.
19828
+ * Reject `from` arrays that describe the same spend source more than once.
19829
+ *
19830
+ * One adapter instance may legitimately be reused across distinct Gateway
19831
+ * source accounts. When `sourceAccount` is omitted, the signer address
19832
+ * identifies the source. Must run on the raw input (before Zod parsing)
19833
+ * because `z.object()` creates new wrapper objects and adapter identity
19834
+ * (`===`) is lost.
19443
19835
  *
19444
19836
  * @internal
19445
- */ function assertNoDuplicateAdapters(params) {
19837
+ */ function assertNoDuplicateSpendSources(params) {
19446
19838
  if (params == null || typeof params !== 'object') return;
19447
19839
  const from = params['from'];
19448
19840
  if (!Array.isArray(from) || from.length < 2) return;
19449
- const seen = new Set();
19841
+ const seen = new Map();
19450
19842
  for (const source of from){
19451
19843
  if (source == null || typeof source !== 'object') continue;
19452
- const adapter = source['adapter'];
19844
+ const record = source;
19845
+ const adapter = record['adapter'];
19453
19846
  if (adapter == null) continue;
19454
- if (seen.has(adapter)) {
19847
+ const sourceAccount = normalizeAddressIdentity(record['sourceAccount']) ?? normalizeAddressIdentity(record['address']);
19848
+ let sourceAccounts = seen.get(adapter);
19849
+ if (sourceAccounts == null) {
19850
+ sourceAccounts = new Set();
19851
+ seen.set(adapter, sourceAccounts);
19852
+ }
19853
+ if (sourceAccounts.has(sourceAccount)) {
19455
19854
  throw new KitError({
19456
19855
  ...InputError.VALIDATION_FAILED,
19457
19856
  recoverability: 'FATAL',
19458
- message: 'Duplicate adapter instance detected in from array. ' + 'Each source must use a distinct adapter object.'
19857
+ message: 'Duplicate spend source detected in from array. The same adapter ' + 'and Gateway source account are reused more than once.'
19459
19858
  });
19460
19859
  }
19461
- seen.add(adapter);
19860
+ sourceAccounts.add(sourceAccount);
19861
+ }
19862
+ }
19863
+ function assertSourceAddressContexts(from) {
19864
+ const sources = Array.isArray(from) ? from : [
19865
+ from
19866
+ ];
19867
+ for (const raw of sources){
19868
+ if (raw == null || typeof raw !== 'object') continue;
19869
+ const source = raw;
19870
+ const addressContext = source.adapter?.capabilities?.addressContext;
19871
+ if (addressContext === 'developer-controlled' && (typeof source.address !== 'string' || source.address === '')) {
19872
+ throw createValidationFailedError$1('address', source.address, 'Address is required for developer-controlled adapters.');
19873
+ }
19874
+ if (addressContext === 'user-controlled' && source.address !== undefined) {
19875
+ throw createValidationFailedError$1('address', source.address, 'Address should not be provided for user-controlled adapters. ' + 'The address is automatically resolved from the connected wallet.');
19876
+ }
19462
19877
  }
19463
19878
  }
19464
19879
  /**
@@ -19472,14 +19887,17 @@ function sumAllocationsFromSources(sources) {
19472
19887
  * @throws \{KitError\} When validation fails; error code is
19473
19888
  * {@link InputError.VALIDATION_FAILED}.
19474
19889
  */ function assertSpendParams(params) {
19475
- assertNoDuplicateAdapters(params);
19890
+ assertNoDuplicateSpendSources(params);
19891
+ if (params != null && typeof params === 'object' && 'from' in params) {
19892
+ assertSourceAddressContexts(params['from']);
19893
+ }
19476
19894
  const result = spendParamsSchema.safeParse(params);
19477
19895
  if (!result.success) {
19478
19896
  throw convertZodErrorToStructured(result.error, params);
19479
19897
  }
19480
19898
  const data = result.data;
19481
19899
  // Validate recipientAddress against the destination chain
19482
- if (data.to.recipientAddress) {
19900
+ if (data.to.recipientAddress !== undefined && data.to.recipientAddress !== '') {
19483
19901
  const toChain = data.to.chain;
19484
19902
  if (!isValidAddressForChain(data.to.recipientAddress, toChain)) {
19485
19903
  const chainInfo = extractChainInfo(toChain);
@@ -19487,7 +19905,7 @@ function sumAllocationsFromSources(sources) {
19487
19905
  }
19488
19906
  }
19489
19907
  // Validate sourceAccount against each source's allocation chain
19490
- if (data.from) {
19908
+ if (data.from !== undefined) {
19491
19909
  assertSourceAccountAddresses(data.from);
19492
19910
  }
19493
19911
  }
@@ -19502,11 +19920,15 @@ function assertSourceAccountAddresses(from) {
19502
19920
  from
19503
19921
  ];
19504
19922
  for (const raw of sources){
19505
- if (!raw || typeof raw !== 'object' || !('sourceAccount' in raw)) continue;
19923
+ if (raw === null || typeof raw !== 'object' || !('sourceAccount' in raw)) {
19924
+ continue;
19925
+ }
19506
19926
  const source = raw;
19507
- if (!source.sourceAccount) continue;
19927
+ if (source.sourceAccount === undefined || source.sourceAccount === '') {
19928
+ continue;
19929
+ }
19508
19930
  const sourceChain = getSourceChainFromAllocations(source.allocations);
19509
- if (!sourceChain) continue;
19931
+ if (sourceChain === undefined) continue;
19510
19932
  if (!isValidAddressForChain(source.sourceAccount, sourceChain)) {
19511
19933
  const chainInfo = extractChainInfo(sourceChain);
19512
19934
  throw createInvalidAddressError(source.sourceAccount, String(chainInfo.name), chainInfo.expectedAddressFormat);