@getomnichain/omnichain 0.5.1 → 0.6.1

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.
Files changed (300) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +5 -3
  3. package/dist/cjs/address.factory.js +7 -4
  4. package/dist/cjs/address.factory.js.map +1 -1
  5. package/dist/cjs/asset_balance.js +7 -0
  6. package/dist/cjs/asset_balance.js.map +1 -0
  7. package/dist/cjs/asset_map.js +45 -0
  8. package/dist/cjs/asset_map.js.map +1 -0
  9. package/dist/cjs/asset_registry.js +21 -0
  10. package/dist/cjs/asset_registry.js.map +1 -0
  11. package/dist/cjs/bip39.js +88 -0
  12. package/dist/cjs/bip39.js.map +1 -0
  13. package/dist/cjs/bytes_from_hex.js +34 -0
  14. package/dist/cjs/bytes_from_hex.js.map +1 -0
  15. package/dist/cjs/chain_type.js +35 -0
  16. package/dist/cjs/chain_type.js.map +1 -0
  17. package/dist/cjs/evm/evm_chain.js +16 -0
  18. package/dist/cjs/evm/evm_chain.js.map +1 -1
  19. package/dist/cjs/index.js +12 -0
  20. package/dist/cjs/index.js.map +1 -1
  21. package/dist/cjs/network_type.js +5 -2
  22. package/dist/cjs/network_type.js.map +1 -1
  23. package/dist/cjs/python_builtins.js +225 -0
  24. package/dist/cjs/python_builtins.js.map +1 -0
  25. package/dist/cjs/python_json.js +120 -0
  26. package/dist/cjs/python_json.js.map +1 -0
  27. package/dist/cjs/python_pydantic.js +50 -0
  28. package/dist/cjs/python_pydantic.js.map +1 -0
  29. package/dist/cjs/python_repr.js +172 -0
  30. package/dist/cjs/python_repr.js.map +1 -0
  31. package/dist/cjs/signed_transaction.js +24 -0
  32. package/dist/cjs/signed_transaction.js.map +1 -0
  33. package/dist/cjs/stellar/index.js +26 -0
  34. package/dist/cjs/stellar/index.js.map +1 -0
  35. package/dist/cjs/stellar/stellar_address.js +25 -0
  36. package/dist/cjs/stellar/stellar_address.js.map +1 -0
  37. package/dist/cjs/stellar/stellar_asset.js +155 -0
  38. package/dist/cjs/stellar/stellar_asset.js.map +1 -0
  39. package/dist/cjs/stellar/stellar_assets.js +26 -0
  40. package/dist/cjs/stellar/stellar_assets.js.map +1 -0
  41. package/dist/cjs/stellar/stellar_chain.js +1334 -0
  42. package/dist/cjs/stellar/stellar_chain.js.map +1 -0
  43. package/dist/cjs/stellar/stellar_chains.js +28 -0
  44. package/dist/cjs/stellar/stellar_chains.js.map +1 -0
  45. package/dist/cjs/stellar/stellar_gas_pricing.js +22 -0
  46. package/dist/cjs/stellar/stellar_gas_pricing.js.map +1 -0
  47. package/dist/cjs/stellar/stellar_strkey.js +71 -0
  48. package/dist/cjs/stellar/stellar_strkey.js.map +1 -0
  49. package/dist/cjs/stellar/stellar_transaction_status.js +72 -0
  50. package/dist/cjs/stellar/stellar_transaction_status.js.map +1 -0
  51. package/dist/cjs/stellar/stellar_transactions.js +475 -0
  52. package/dist/cjs/stellar/stellar_transactions.js.map +1 -0
  53. package/dist/cjs/stellar/stellar_wallet.js +195 -0
  54. package/dist/cjs/stellar/stellar_wallet.js.map +1 -0
  55. package/dist/cjs/token.js +3 -1
  56. package/dist/cjs/token.js.map +1 -1
  57. package/dist/cjs/transaction_json.js +85 -0
  58. package/dist/cjs/transaction_json.js.map +1 -0
  59. package/dist/cjs/transaction_prerequisite.js +19 -0
  60. package/dist/cjs/transaction_prerequisite.js.map +1 -0
  61. package/dist/cjs/transaction_simulation.js +21 -0
  62. package/dist/cjs/transaction_simulation.js.map +1 -0
  63. package/dist/cjs/tron/index.js +32 -0
  64. package/dist/cjs/tron/index.js.map +1 -0
  65. package/dist/cjs/tron/tron_abi.js +287 -0
  66. package/dist/cjs/tron/tron_abi.js.map +1 -0
  67. package/dist/cjs/tron/tron_address.js +38 -0
  68. package/dist/cjs/tron/tron_address.js.map +1 -0
  69. package/dist/cjs/tron/tron_asset.js +56 -0
  70. package/dist/cjs/tron/tron_asset.js.map +1 -0
  71. package/dist/cjs/tron/tron_assets.js +17 -0
  72. package/dist/cjs/tron/tron_assets.js.map +1 -0
  73. package/dist/cjs/tron/tron_base58.js +95 -0
  74. package/dist/cjs/tron/tron_base58.js.map +1 -0
  75. package/dist/cjs/tron/tron_canonical_transaction.js +268 -0
  76. package/dist/cjs/tron/tron_canonical_transaction.js.map +1 -0
  77. package/dist/cjs/tron/tron_chain.js +809 -0
  78. package/dist/cjs/tron/tron_chain.js.map +1 -0
  79. package/dist/cjs/tron/tron_chains.js +19 -0
  80. package/dist/cjs/tron/tron_chains.js.map +1 -0
  81. package/dist/cjs/tron/tron_client.js +327 -0
  82. package/dist/cjs/tron/tron_client.js.map +1 -0
  83. package/dist/cjs/tron/tron_contract.js +195 -0
  84. package/dist/cjs/tron/tron_contract.js.map +1 -0
  85. package/dist/cjs/tron/tron_gas_pricing.js +21 -0
  86. package/dist/cjs/tron/tron_gas_pricing.js.map +1 -0
  87. package/dist/cjs/tron/tron_keys.js +251 -0
  88. package/dist/cjs/tron/tron_keys.js.map +1 -0
  89. package/dist/cjs/tron/tron_transaction_builder.js +196 -0
  90. package/dist/cjs/tron/tron_transaction_builder.js.map +1 -0
  91. package/dist/cjs/tron/tron_transaction_status.js +118 -0
  92. package/dist/cjs/tron/tron_transaction_status.js.map +1 -0
  93. package/dist/cjs/tron/tron_transactions.js +192 -0
  94. package/dist/cjs/tron/tron_transactions.js.map +1 -0
  95. package/dist/cjs/tron/tron_wallet.js +152 -0
  96. package/dist/cjs/tron/tron_wallet.js.map +1 -0
  97. package/dist/cjs/unsigned_transaction.js +12 -0
  98. package/dist/cjs/unsigned_transaction.js.map +1 -1
  99. package/dist/cjs/wallet.base.js +29 -0
  100. package/dist/cjs/wallet.base.js.map +1 -0
  101. package/dist/esm/address.factory.d.ts.map +1 -1
  102. package/dist/esm/address.factory.js +7 -4
  103. package/dist/esm/address.factory.js.map +1 -1
  104. package/dist/esm/asset_balance.d.ts +5 -0
  105. package/dist/esm/asset_balance.d.ts.map +1 -0
  106. package/dist/esm/asset_balance.js +3 -0
  107. package/dist/esm/asset_balance.js.map +1 -0
  108. package/dist/esm/asset_map.d.ts +12 -0
  109. package/dist/esm/asset_map.d.ts.map +1 -0
  110. package/dist/esm/asset_map.js +40 -0
  111. package/dist/esm/asset_map.js.map +1 -0
  112. package/dist/esm/asset_registry.d.ts +6 -0
  113. package/dist/esm/asset_registry.d.ts.map +1 -0
  114. package/dist/esm/asset_registry.js +15 -0
  115. package/dist/esm/asset_registry.js.map +1 -0
  116. package/dist/esm/bip39.d.ts +4 -0
  117. package/dist/esm/bip39.d.ts.map +1 -0
  118. package/dist/esm/bip39.js +83 -0
  119. package/dist/esm/bip39.js.map +1 -0
  120. package/dist/esm/bytes_from_hex.d.ts +2 -0
  121. package/dist/esm/bytes_from_hex.d.ts.map +1 -0
  122. package/dist/esm/bytes_from_hex.js +31 -0
  123. package/dist/esm/bytes_from_hex.js.map +1 -0
  124. package/dist/esm/chain_type.d.ts +29 -0
  125. package/dist/esm/chain_type.d.ts.map +1 -0
  126. package/dist/esm/chain_type.js +32 -0
  127. package/dist/esm/chain_type.js.map +1 -0
  128. package/dist/esm/evm/evm_chain.d.ts +1 -0
  129. package/dist/esm/evm/evm_chain.d.ts.map +1 -1
  130. package/dist/esm/evm/evm_chain.js +17 -1
  131. package/dist/esm/evm/evm_chain.js.map +1 -1
  132. package/dist/esm/index.d.ts +12 -0
  133. package/dist/esm/index.d.ts.map +1 -1
  134. package/dist/esm/index.js +12 -0
  135. package/dist/esm/index.js.map +1 -1
  136. package/dist/esm/network_type.d.ts +3 -2
  137. package/dist/esm/network_type.d.ts.map +1 -1
  138. package/dist/esm/network_type.js +6 -3
  139. package/dist/esm/network_type.js.map +1 -1
  140. package/dist/esm/python_builtins.d.ts +13 -0
  141. package/dist/esm/python_builtins.d.ts.map +1 -0
  142. package/dist/esm/python_builtins.js +212 -0
  143. package/dist/esm/python_builtins.js.map +1 -0
  144. package/dist/esm/python_json.d.ts +9 -0
  145. package/dist/esm/python_json.d.ts.map +1 -0
  146. package/dist/esm/python_json.js +117 -0
  147. package/dist/esm/python_json.js.map +1 -0
  148. package/dist/esm/python_pydantic.d.ts +11 -0
  149. package/dist/esm/python_pydantic.d.ts.map +1 -0
  150. package/dist/esm/python_pydantic.js +39 -0
  151. package/dist/esm/python_pydantic.js.map +1 -0
  152. package/dist/esm/python_repr.d.ts +17 -0
  153. package/dist/esm/python_repr.d.ts.map +1 -0
  154. package/dist/esm/python_repr.js +160 -0
  155. package/dist/esm/python_repr.js.map +1 -0
  156. package/dist/esm/signed_transaction.d.ts +17 -0
  157. package/dist/esm/signed_transaction.d.ts.map +1 -0
  158. package/dist/esm/signed_transaction.js +19 -0
  159. package/dist/esm/signed_transaction.js.map +1 -0
  160. package/dist/esm/stellar/index.d.ts +10 -0
  161. package/dist/esm/stellar/index.d.ts.map +1 -0
  162. package/dist/esm/stellar/index.js +10 -0
  163. package/dist/esm/stellar/index.js.map +1 -0
  164. package/dist/esm/stellar/stellar_address.d.ts +8 -0
  165. package/dist/esm/stellar/stellar_address.d.ts.map +1 -0
  166. package/dist/esm/stellar/stellar_address.js +21 -0
  167. package/dist/esm/stellar/stellar_address.js.map +1 -0
  168. package/dist/esm/stellar/stellar_asset.d.ts +52 -0
  169. package/dist/esm/stellar/stellar_asset.d.ts.map +1 -0
  170. package/dist/esm/stellar/stellar_asset.js +147 -0
  171. package/dist/esm/stellar/stellar_asset.js.map +1 -0
  172. package/dist/esm/stellar/stellar_assets.d.ts +11 -0
  173. package/dist/esm/stellar/stellar_assets.d.ts.map +1 -0
  174. package/dist/esm/stellar/stellar_assets.js +23 -0
  175. package/dist/esm/stellar/stellar_assets.js.map +1 -0
  176. package/dist/esm/stellar/stellar_chain.d.ts +173 -0
  177. package/dist/esm/stellar/stellar_chain.d.ts.map +1 -0
  178. package/dist/esm/stellar/stellar_chain.js +1325 -0
  179. package/dist/esm/stellar/stellar_chain.js.map +1 -0
  180. package/dist/esm/stellar/stellar_chains.d.ts +5 -0
  181. package/dist/esm/stellar/stellar_chains.d.ts.map +1 -0
  182. package/dist/esm/stellar/stellar_chains.js +25 -0
  183. package/dist/esm/stellar/stellar_chains.js.map +1 -0
  184. package/dist/esm/stellar/stellar_gas_pricing.d.ts +13 -0
  185. package/dist/esm/stellar/stellar_gas_pricing.d.ts.map +1 -0
  186. package/dist/esm/stellar/stellar_gas_pricing.js +18 -0
  187. package/dist/esm/stellar/stellar_gas_pricing.js.map +1 -0
  188. package/dist/esm/stellar/stellar_strkey.d.ts +3 -0
  189. package/dist/esm/stellar/stellar_strkey.d.ts.map +1 -0
  190. package/dist/esm/stellar/stellar_strkey.js +67 -0
  191. package/dist/esm/stellar/stellar_strkey.js.map +1 -0
  192. package/dist/esm/stellar/stellar_transaction_status.d.ts +39 -0
  193. package/dist/esm/stellar/stellar_transaction_status.d.ts.map +1 -0
  194. package/dist/esm/stellar/stellar_transaction_status.js +68 -0
  195. package/dist/esm/stellar/stellar_transaction_status.js.map +1 -0
  196. package/dist/esm/stellar/stellar_transactions.d.ts +166 -0
  197. package/dist/esm/stellar/stellar_transactions.d.ts.map +1 -0
  198. package/dist/esm/stellar/stellar_transactions.js +451 -0
  199. package/dist/esm/stellar/stellar_transactions.js.map +1 -0
  200. package/dist/esm/stellar/stellar_wallet.d.ts +43 -0
  201. package/dist/esm/stellar/stellar_wallet.d.ts.map +1 -0
  202. package/dist/esm/stellar/stellar_wallet.js +190 -0
  203. package/dist/esm/stellar/stellar_wallet.js.map +1 -0
  204. package/dist/esm/token.d.ts +1 -0
  205. package/dist/esm/token.d.ts.map +1 -1
  206. package/dist/esm/token.js +3 -1
  207. package/dist/esm/token.js.map +1 -1
  208. package/dist/esm/transaction_json.d.ts +21 -0
  209. package/dist/esm/transaction_json.d.ts.map +1 -0
  210. package/dist/esm/transaction_json.js +75 -0
  211. package/dist/esm/transaction_json.js.map +1 -0
  212. package/dist/esm/transaction_prerequisite.d.ts +17 -0
  213. package/dist/esm/transaction_prerequisite.d.ts.map +1 -0
  214. package/dist/esm/transaction_prerequisite.js +13 -0
  215. package/dist/esm/transaction_prerequisite.js.map +1 -0
  216. package/dist/esm/transaction_simulation.d.ts +20 -0
  217. package/dist/esm/transaction_simulation.d.ts.map +1 -0
  218. package/dist/esm/transaction_simulation.js +17 -0
  219. package/dist/esm/transaction_simulation.js.map +1 -0
  220. package/dist/esm/tron/index.d.ts +16 -0
  221. package/dist/esm/tron/index.d.ts.map +1 -0
  222. package/dist/esm/tron/index.js +16 -0
  223. package/dist/esm/tron/index.js.map +1 -0
  224. package/dist/esm/tron/tron_abi.d.ts +3 -0
  225. package/dist/esm/tron/tron_abi.d.ts.map +1 -0
  226. package/dist/esm/tron/tron_abi.js +283 -0
  227. package/dist/esm/tron/tron_abi.js.map +1 -0
  228. package/dist/esm/tron/tron_address.d.ts +10 -0
  229. package/dist/esm/tron/tron_address.d.ts.map +1 -0
  230. package/dist/esm/tron/tron_address.js +34 -0
  231. package/dist/esm/tron/tron_address.js.map +1 -0
  232. package/dist/esm/tron/tron_asset.d.ts +19 -0
  233. package/dist/esm/tron/tron_asset.d.ts.map +1 -0
  234. package/dist/esm/tron/tron_asset.js +51 -0
  235. package/dist/esm/tron/tron_asset.js.map +1 -0
  236. package/dist/esm/tron/tron_assets.d.ts +10 -0
  237. package/dist/esm/tron/tron_assets.d.ts.map +1 -0
  238. package/dist/esm/tron/tron_assets.js +14 -0
  239. package/dist/esm/tron/tron_assets.js.map +1 -0
  240. package/dist/esm/tron/tron_base58.d.ts +7 -0
  241. package/dist/esm/tron/tron_base58.d.ts.map +1 -0
  242. package/dist/esm/tron/tron_base58.js +84 -0
  243. package/dist/esm/tron/tron_base58.js.map +1 -0
  244. package/dist/esm/tron/tron_canonical_transaction.d.ts +164 -0
  245. package/dist/esm/tron/tron_canonical_transaction.d.ts.map +1 -0
  246. package/dist/esm/tron/tron_canonical_transaction.js +262 -0
  247. package/dist/esm/tron/tron_canonical_transaction.js.map +1 -0
  248. package/dist/esm/tron/tron_chain.d.ts +136 -0
  249. package/dist/esm/tron/tron_chain.d.ts.map +1 -0
  250. package/dist/esm/tron/tron_chain.js +801 -0
  251. package/dist/esm/tron/tron_chain.js.map +1 -0
  252. package/dist/esm/tron/tron_chains.d.ts +5 -0
  253. package/dist/esm/tron/tron_chains.d.ts.map +1 -0
  254. package/dist/esm/tron/tron_chains.js +16 -0
  255. package/dist/esm/tron/tron_chains.js.map +1 -0
  256. package/dist/esm/tron/tron_client.d.ts +56 -0
  257. package/dist/esm/tron/tron_client.d.ts.map +1 -0
  258. package/dist/esm/tron/tron_client.js +319 -0
  259. package/dist/esm/tron/tron_client.js.map +1 -0
  260. package/dist/esm/tron/tron_contract.d.ts +69 -0
  261. package/dist/esm/tron/tron_contract.d.ts.map +1 -0
  262. package/dist/esm/tron/tron_contract.js +190 -0
  263. package/dist/esm/tron/tron_contract.js.map +1 -0
  264. package/dist/esm/tron/tron_gas_pricing.d.ts +12 -0
  265. package/dist/esm/tron/tron_gas_pricing.d.ts.map +1 -0
  266. package/dist/esm/tron/tron_gas_pricing.js +17 -0
  267. package/dist/esm/tron/tron_gas_pricing.js.map +1 -0
  268. package/dist/esm/tron/tron_keys.d.ts +61 -0
  269. package/dist/esm/tron/tron_keys.d.ts.map +1 -0
  270. package/dist/esm/tron/tron_keys.js +228 -0
  271. package/dist/esm/tron/tron_keys.js.map +1 -0
  272. package/dist/esm/tron/tron_transaction_builder.d.ts +56 -0
  273. package/dist/esm/tron/tron_transaction_builder.d.ts.map +1 -0
  274. package/dist/esm/tron/tron_transaction_builder.js +188 -0
  275. package/dist/esm/tron/tron_transaction_builder.js.map +1 -0
  276. package/dist/esm/tron/tron_transaction_status.d.ts +54 -0
  277. package/dist/esm/tron/tron_transaction_status.d.ts.map +1 -0
  278. package/dist/esm/tron/tron_transaction_status.js +113 -0
  279. package/dist/esm/tron/tron_transaction_status.js.map +1 -0
  280. package/dist/esm/tron/tron_transactions.d.ts +96 -0
  281. package/dist/esm/tron/tron_transactions.d.ts.map +1 -0
  282. package/dist/esm/tron/tron_transactions.js +181 -0
  283. package/dist/esm/tron/tron_transactions.js.map +1 -0
  284. package/dist/esm/tron/tron_wallet.d.ts +43 -0
  285. package/dist/esm/tron/tron_wallet.d.ts.map +1 -0
  286. package/dist/esm/tron/tron_wallet.js +148 -0
  287. package/dist/esm/tron/tron_wallet.js.map +1 -0
  288. package/dist/esm/unsigned_transaction.d.ts +5 -0
  289. package/dist/esm/unsigned_transaction.d.ts.map +1 -1
  290. package/dist/esm/unsigned_transaction.js +12 -0
  291. package/dist/esm/unsigned_transaction.js.map +1 -1
  292. package/dist/esm/wallet.base.d.ts +32 -0
  293. package/dist/esm/wallet.base.d.ts.map +1 -0
  294. package/dist/esm/wallet.base.js +23 -0
  295. package/dist/esm/wallet.base.js.map +1 -0
  296. package/docs/CONNECTIONS.md +1 -0
  297. package/docs/README.md +16 -7
  298. package/docs/stellar.md +113 -0
  299. package/docs/tron.md +103 -0
  300. package/package.json +14 -2
@@ -0,0 +1,1325 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { Account, AccountRequiresMemoError, Address, BadResponseError, Horizon, Keypair, Memo, MemoNone, MuxedAccount, NotFoundError, Operation, StrKey, TransactionBuilder, nativeToScVal, rpc, scValToBigInt, xdr, } from '@stellar/stellar-sdk';
3
+ import { Decimal } from 'decimal.js';
4
+ import { isAbstractGasPricing } from "../abstract_gas_pricing.js";
5
+ import { AssetMap } from "../asset_map.js";
6
+ import { bytesFromHex } from "../bytes_from_hex.js";
7
+ import { Chain, resolveTransferAmount, } from "../chain.base.js";
8
+ import { ChainType } from "../chain_type.js";
9
+ import { ChainError, ChainErrorKinds, sanitizeCause, sanitizeMessage } from "../errors.js";
10
+ import { pyDecodeUtf8, pyEncodeUtf8, pyContains, pyStringContainer, pyItem, pyIntOf, pyTruthy, pyDecimalStr, pyDecimalString } from "../python_builtins.js";
11
+ import { pyRepr, pyTypeRepr, pyTypeName } from "../python_repr.js";
12
+ import { isPydanticAbsent, isPydanticDict, isPydanticLaxBool, isPydanticLaxInt, isPydanticOptionalStr, isPydanticStrList, pydanticLaxIntValue, } from "../python_pydantic.js";
13
+ import { NetworkType } from "../network_type.js";
14
+ import { FeePriority } from "../priority.js";
15
+ import { JSON_TRANSACTION_TYPE_KEY, coerceJsonDict } from "../transaction_json.js";
16
+ import { UnsignedTransactionWithPrerequisites } from "../transaction_prerequisite.js";
17
+ import { TransactionSimulationStatusTypes } from "../transaction_simulation.js";
18
+ import { AssetBalanceChange, hrDecimalToMinorUnits, minorUnitsToHrString, } from "../transaction_status.js";
19
+ import { AbstractSignedMessage } from "../wallet.base.js";
20
+ import { StellarAsset, stellarSdkAsset, stellarSdkAssetFromXdr } from "./stellar_asset.js";
21
+ import { STELLAR_MIN_BASE_FEE_STROOPS, StellarGasPricing } from "./stellar_gas_pricing.js";
22
+ import { StellarTransactionStatus } from "./stellar_transaction_status.js";
23
+ import { StellarBroadcastTransactionResponse, StellarChangeTrustLineTransactionPrerequisite, StellarSignedTransaction, StellarTransactionFees, StellarTransactionSimulationResult, StellarUnsignedTransaction, parseSignedStellarEnvelope, parseStellarExpertTransactionInfo, stellarOperationAmount, SorobanRpcErrorResponse, loadStellarAccount, simulateSorobanTransaction, sorobanRpcError, stellarTextMemo, toClassicStellarAccountId, } from "./stellar_transactions.js";
24
+ const STELLAR_MESSAGE_PREFIX = 'Stellar Signed Message:\n';
25
+ const NON_SAC_TOKEN_CACHE_SIZE = 2000;
26
+ const HORIZON_DEFAULT_BASE_FEE_STROOPS = 100;
27
+ const PythonDecimal = Decimal.clone({ precision: 28, rounding: Decimal.ROUND_HALF_EVEN });
28
+ export function stellarMessageHash(message) {
29
+ const messageBytes = typeof message === 'string' ? Buffer.from(pyEncodeUtf8(message)) : Buffer.from(message);
30
+ return createHash('sha256').update(Buffer.concat([Buffer.from(STELLAR_MESSAGE_PREFIX, 'utf8'), messageBytes])).digest();
31
+ }
32
+ export class StellarSignedMessage extends AbstractSignedMessage {
33
+ constructor(signature) {
34
+ super(signature);
35
+ }
36
+ }
37
+ export class StellarChain extends Chain {
38
+ chainAgnosticNamespace;
39
+ stellarExpertApiUrl;
40
+ networkPassphrase;
41
+ defaultHorizonUrl;
42
+ defaultSorobanRpcUrl;
43
+ horizonUrl;
44
+ sorobanRpcUrl;
45
+ _randomCallAccountId;
46
+ _nativeAsset;
47
+ _horizonServer = null;
48
+ _sorobanServer = null;
49
+ nonSacTokenCache = new Map();
50
+ assetDecimalsByIdentifier = new Map();
51
+ static _FEE_PRIORITY_MULTIPLIERS = {
52
+ [FeePriority.SLOW]: 1.0,
53
+ [FeePriority.NORMAL]: 1.0,
54
+ [FeePriority.FAST]: 2.0,
55
+ };
56
+ constructor(init) {
57
+ super(init.chainId, init.name, NetworkType.STELLAR, 5.0, StellarAsset.NATIVE_CODE, init.explorerUrl);
58
+ this.chainAgnosticNamespace = `stellar:${init.chainAgnosticStellarIdentifier}`;
59
+ this.stellarExpertApiUrl = init.stellarExpertApiUrl;
60
+ this.networkPassphrase = init.networkPassphrase;
61
+ this._randomCallAccountId = Keypair.random().publicKey();
62
+ this._nativeAsset = new StellarAsset({
63
+ chainId: init.chainId,
64
+ networkPassphrase: init.networkPassphrase,
65
+ code: StellarAsset.NATIVE_CODE,
66
+ issuer: null,
67
+ });
68
+ this.horizonUrl = init.horizonUrl ?? null;
69
+ this.sorobanRpcUrl = init.sorobanRpcUrl ?? null;
70
+ this.defaultHorizonUrl = init.defaultHorizonUrl;
71
+ this.defaultSorobanRpcUrl = init.defaultSorobanRpcUrl;
72
+ }
73
+ get asyncHorizonServer() {
74
+ if (this._horizonServer === null) {
75
+ this.horizonUrl = this.horizonUrl || this._loadHorizonUrl();
76
+ this._horizonServer = new Horizon.Server(this.horizonUrl, { allowHttp: this.horizonUrl.startsWith('http://') });
77
+ applyAiohttpClientLimits(this._horizonServer.httpClient);
78
+ }
79
+ return this._horizonServer;
80
+ }
81
+ get asyncSorobanServer() {
82
+ if (this._sorobanServer === null) {
83
+ this.sorobanRpcUrl = this.sorobanRpcUrl || this._loadSorobanRpcUrl();
84
+ this._sorobanServer = new rpc.Server(this.sorobanRpcUrl, { allowHttp: this.sorobanRpcUrl.startsWith('http://') });
85
+ applyAiohttpClientLimits(this._sorobanServer.httpClient);
86
+ }
87
+ return this._sorobanServer;
88
+ }
89
+ static horizonUrlEnvForChainId(chainId) {
90
+ return `STELLAR_${chainId}_HORIZON_URL`;
91
+ }
92
+ static sorobanRpcUrlEnvForChainId(chainId) {
93
+ return `STELLAR_${chainId}_SOROBAN_RPC_URL`;
94
+ }
95
+ _loadHorizonUrl() {
96
+ return this.loadUrlFromEnv([`${this.envName()}_HORIZON_URL`, StellarChain.horizonUrlEnvForChainId(this.chainId)], this.defaultHorizonUrl);
97
+ }
98
+ _loadSorobanRpcUrl() {
99
+ return this.loadUrlFromEnv([`${this.envName()}_SOROBAN_RPC_URL`, StellarChain.sorobanRpcUrlEnvForChainId(this.chainId)], this.defaultSorobanRpcUrl);
100
+ }
101
+ get chainType() {
102
+ return ChainType.STELLAR;
103
+ }
104
+ get nativeAsset() {
105
+ return this._nativeAsset;
106
+ }
107
+ get nativeToken() {
108
+ return this._nativeAsset;
109
+ }
110
+ toString() {
111
+ return `StellarChain[chain_id:${this.chainId}]`;
112
+ }
113
+ getAssetExplorerUrl(asset) {
114
+ this.assertStellarAsset(asset);
115
+ if (asset.isNative())
116
+ return null;
117
+ if (asset.issuer === null)
118
+ return `${this.explorerBaseUrl}/contract/${asset.identifier}`;
119
+ return `${this.explorerBaseUrl}/asset/${asset.code}-${asset.issuer}`;
120
+ }
121
+ getWalletAddressExplorerUrl(walletAddress) {
122
+ return `${this.explorerBaseUrl}/account/${walletAddress}`;
123
+ }
124
+ getTransactionExplorerUrl(txHash) {
125
+ return `${this.explorerBaseUrl}/tx/${txHash}`;
126
+ }
127
+ getWalletExplorerUrl(address) {
128
+ return this.getWalletAddressExplorerUrl(address);
129
+ }
130
+ getTokenExplorerUrl(tokenIdentifier) {
131
+ if (!tokenIdentifier)
132
+ return this.explorerBaseUrl;
133
+ return `${this.explorerBaseUrl}/contract/${tokenIdentifier}`;
134
+ }
135
+ static formatAssetIdentifier(identifier) {
136
+ return identifier ?? null;
137
+ }
138
+ static validateAssetIdentifier(identifier) {
139
+ if (identifier === null || identifier === undefined)
140
+ return;
141
+ if (!StrKey.isValidContract(identifier)) {
142
+ throw new ChainError(ChainErrorKinds.InvalidTokenIdentifier, `Invalid stellar asset identifier ${identifier}`, { identifier });
143
+ }
144
+ }
145
+ static formatWalletAddress(walletAddress) {
146
+ return walletAddress;
147
+ }
148
+ static validateWalletAddress(walletAddress) {
149
+ if (StrKey.isValidEd25519PublicKey(walletAddress))
150
+ return;
151
+ if (StrKey.isValidMed25519PublicKey(walletAddress))
152
+ return;
153
+ throw new ChainError(ChainErrorKinds.InvalidAddress, `Invalid Stellar wallet address ${walletAddress}`, { address: walletAddress });
154
+ }
155
+ static toClassicAccountId(address) {
156
+ return toClassicStellarAccountId(address);
157
+ }
158
+ validateAddress(raw) {
159
+ try {
160
+ StellarChain.validateWalletAddress(raw);
161
+ return true;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ validateTokenIdentifier(raw) {
168
+ try {
169
+ StellarChain.validateAssetIdentifier(raw);
170
+ return true;
171
+ }
172
+ catch {
173
+ return false;
174
+ }
175
+ }
176
+ async getBaseFee() {
177
+ let latestLedger;
178
+ try {
179
+ latestLedger = (await this.asyncHorizonServer.ledgers().order('desc').limit(1).call());
180
+ }
181
+ catch (err) {
182
+ throw new ChainError(ChainErrorKinds.RpcError, sanitizeMessage(err instanceof Error ? err.message : String(err), this.horizonUrl), { chainId: this.chainId }, sanitizeCause(err, this.horizonUrl));
183
+ }
184
+ const embedded = Array.isArray(latestLedger.records) ? { records: latestLedger.records } : pyItem(latestLedger, '_embedded');
185
+ if (!pyTruthy(embedded))
186
+ return HORIZON_DEFAULT_BASE_FEE_STROOPS;
187
+ const records = pyItem(embedded, 'records');
188
+ if (!pyTruthy(records))
189
+ return HORIZON_DEFAULT_BASE_FEE_STROOPS;
190
+ const first = Array.isArray(records) ? records[0] : pyItem(records, '0');
191
+ if (!pyTruthy(first))
192
+ return HORIZON_DEFAULT_BASE_FEE_STROOPS;
193
+ return Number(pyIntOf(pyItem(first, 'base_fee_in_stroops')));
194
+ }
195
+ async _resolveGasPricing(gasPricing) {
196
+ if (isAbstractGasPricing(gasPricing)) {
197
+ if (!(gasPricing instanceof StellarGasPricing)) {
198
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Expected StellarGasPricing for a Stellar chain, got ${gasPricing.constructor.name}`, { chainId: this.chainId });
199
+ }
200
+ return Math.max(STELLAR_MIN_BASE_FEE_STROOPS, gasPricing.baseFeeStroops);
201
+ }
202
+ if (!Object.values(FeePriority).includes(gasPricing)) {
203
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Unsupported gas_pricing ${pyRepr(gasPricing)}`, { chainId: this.chainId });
204
+ }
205
+ const baseFee = await this.getBaseFee();
206
+ const scaled = Math.trunc(baseFee * StellarChain._FEE_PRIORITY_MULTIPLIERS[gasPricing]);
207
+ return Math.max(STELLAR_MIN_BASE_FEE_STROOPS, scaled);
208
+ }
209
+ createNonSacToken(contractId) {
210
+ const cached = this.nonSacTokenCache.get(contractId);
211
+ if (cached !== undefined) {
212
+ this.nonSacTokenCache.delete(contractId);
213
+ this.nonSacTokenCache.set(contractId, cached);
214
+ return cached;
215
+ }
216
+ const pending = (async () => {
217
+ const decimalsResp = await this._callHostFunction({ contractId, functionName: 'decimals', parameters: [] });
218
+ const decimals = scvalToUint32(firstHostFunctionResult(decimalsResp));
219
+ const symbolResp = await this._callHostFunction({ contractId, functionName: 'symbol', parameters: [] });
220
+ const symbol = scvalToUtf8String(firstHostFunctionResult(symbolResp));
221
+ return new StellarAsset({
222
+ chainId: this.chainId,
223
+ networkPassphrase: this.networkPassphrase,
224
+ code: symbol,
225
+ issuer: null,
226
+ contractId,
227
+ decimals,
228
+ });
229
+ })();
230
+ this.nonSacTokenCache.set(contractId, pending);
231
+ if (this.nonSacTokenCache.size > NON_SAC_TOKEN_CACHE_SIZE) {
232
+ const oldest = this.nonSacTokenCache.keys().next().value;
233
+ this.nonSacTokenCache.delete(oldest);
234
+ }
235
+ pending.catch(() => {
236
+ if (this.nonSacTokenCache.get(contractId) === pending)
237
+ this.nonSacTokenCache.delete(contractId);
238
+ });
239
+ return pending;
240
+ }
241
+ createSacToken(code, issuer) {
242
+ return new StellarAsset({
243
+ chainId: this.chainId,
244
+ networkPassphrase: this.networkPassphrase,
245
+ code,
246
+ issuer,
247
+ });
248
+ }
249
+ async resolveAsset(identifier) {
250
+ if (identifier === null ||
251
+ identifier === undefined ||
252
+ identifier === StellarAsset.PUBLIC_NATIVE_CONTRACT_ID ||
253
+ identifier === StellarAsset.TESTNET_NATIVE_CONTRACT_ID) {
254
+ return this._nativeAsset;
255
+ }
256
+ const decimalsResults = await this._callHostFunction({ contractId: identifier, functionName: 'decimals', parameters: [] });
257
+ const decimals = scvalToUint32(firstHostFunctionResult(decimalsResults));
258
+ const nameResults = await this._callHostFunction({ contractId: identifier, functionName: 'name', parameters: [] });
259
+ const name = scvalToUtf8String(firstHostFunctionResult(nameResults));
260
+ let code;
261
+ let issuer;
262
+ if (name === 'native') {
263
+ return this._nativeAsset;
264
+ }
265
+ else if (!name.includes(':')) {
266
+ const symbolResults = await this._callHostFunction({ contractId: identifier, functionName: 'symbol', parameters: [] });
267
+ code = scvalToUtf8String(firstHostFunctionResult(symbolResults));
268
+ issuer = null;
269
+ }
270
+ else {
271
+ const separator = name.indexOf(':');
272
+ code = name.slice(0, separator);
273
+ issuer = name.slice(separator + 1);
274
+ }
275
+ return new StellarAsset({
276
+ chainId: this.chainId,
277
+ networkPassphrase: this.networkPassphrase,
278
+ code,
279
+ issuer,
280
+ contractId: identifier,
281
+ decimals,
282
+ });
283
+ }
284
+ createAsset(_symbol, _identifier, _decimals) {
285
+ throw new ChainError(ChainErrorKinds.FeatureNotSupported, 'Cannot create asset for stellar via create_asset method', {
286
+ chainId: this.chainId,
287
+ });
288
+ }
289
+ async _callHostFunction(req) {
290
+ if (!StrKey.isValidContract(req.contractId)) {
291
+ throw new ChainError(ChainErrorKinds.InvalidTokenIdentifier, '`contract_id` is invalid.', {
292
+ chainId: this.chainId,
293
+ identifier: req.contractId,
294
+ });
295
+ }
296
+ const account = req.accountId === undefined || req.accountId === null
297
+ ? new Account(this._randomCallAccountId, '0')
298
+ : await this.soroban(this.asyncSorobanServer.getAccount(req.accountId));
299
+ const tx = new TransactionBuilder(account, { fee: '300', networkPassphrase: this.networkPassphrase })
300
+ .addOperation(Operation.invokeContractFunction({
301
+ contract: req.contractId,
302
+ function: req.functionName,
303
+ args: req.parameters,
304
+ }))
305
+ .setTimeout(300)
306
+ .build();
307
+ return (await simulateSorobanTransaction(tx, this)).results;
308
+ }
309
+ async _sorobanRpc(method, params) {
310
+ const soroban = this.asyncSorobanServer;
311
+ const body = { jsonrpc: '2.0', id: randomUUID().replace(/-/g, ''), method, params };
312
+ const config = { validateStatus: () => true, responseType: 'text', transformResponse: [(data) => data] };
313
+ const response = await soroban.httpClient.post(soroban.serverURL.toString(), body, config);
314
+ let reply;
315
+ try {
316
+ reply = JSON.parse(String(response.data));
317
+ }
318
+ catch {
319
+ throw new ChainError(ChainErrorKinds.RpcError, `Soroban ${method} returned a body that is not valid JSON (HTTP ${response.status})`, {
320
+ chainId: this.chainId,
321
+ });
322
+ }
323
+ const error = isPydanticDict(reply) ? reply.error : undefined;
324
+ const validEnvelope = isPydanticDict(reply) &&
325
+ typeof reply.jsonrpc === 'string' &&
326
+ (typeof reply.id === 'string' || isPydanticLaxInt(reply.id)) &&
327
+ (isPydanticAbsent(error) ||
328
+ (isPydanticDict(error) && isPydanticLaxInt(error.code) && isPydanticOptionalStr(error.message) && isPydanticOptionalStr(error.data)));
329
+ if (!validEnvelope) {
330
+ throw new ChainError(ChainErrorKinds.RpcError, `Soroban ${method} reply does not match the JSON-RPC Response model`, { chainId: this.chainId });
331
+ }
332
+ const envelope = reply;
333
+ if (isPydanticDict(error)) {
334
+ throw new SorobanRpcErrorResponse(Number(pydanticLaxIntValue(error.code)), error.message, error.data, this.chainId);
335
+ }
336
+ if (isPydanticAbsent(envelope.result)) {
337
+ throw new ChainError(ChainErrorKinds.RpcError, '', { chainId: this.chainId });
338
+ }
339
+ return envelope.result;
340
+ }
341
+ async _getAssetDecimalsInternal(_symbol, identifier) {
342
+ try {
343
+ const decimalsResult = await this._callHostFunction({
344
+ contractId: identifier,
345
+ functionName: 'decimals',
346
+ parameters: [],
347
+ });
348
+ return scvalToUint32(firstHostFunctionResult(decimalsResult));
349
+ }
350
+ catch {
351
+ return StellarAsset.DECIMALS;
352
+ }
353
+ }
354
+ async getCachedAssetDecimals(symbol, identifier) {
355
+ if (identifier === null || identifier === undefined) {
356
+ if (symbol !== this._nativeAsset.symbol) {
357
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid native asset symbol ${symbol}`, { chainId: this.chainId });
358
+ }
359
+ return this._nativeAsset.decimals;
360
+ }
361
+ let decimals = this.assetDecimalsByIdentifier.get(identifier);
362
+ if (decimals === undefined) {
363
+ decimals = await this._getAssetDecimalsInternal(symbol, identifier);
364
+ this.assetDecimalsByIdentifier.set(identifier, decimals);
365
+ }
366
+ return decimals;
367
+ }
368
+ async _checkMemoRequired(transaction) {
369
+ if (transaction.memo.type !== MemoNone)
370
+ return;
371
+ const checked = new Set();
372
+ for (const [index, operation] of transaction.operations.entries()) {
373
+ if (!MEMO_REQUIRED_OPERATION_TYPES.has(operation.type))
374
+ continue;
375
+ const destination = StellarChain._muxedToClassic(operation.destination);
376
+ if (checked.has(destination))
377
+ continue;
378
+ checked.add(destination);
379
+ if (destination.startsWith('M'))
380
+ continue;
381
+ let account;
382
+ try {
383
+ account = (await this.asyncHorizonServer.accounts().accountId(destination).call());
384
+ }
385
+ catch (err) {
386
+ if (err instanceof NotFoundError)
387
+ continue;
388
+ throw err;
389
+ }
390
+ const dataRenamedBySdk = isPydanticDict(account._links) && 'data' in account._links;
391
+ if (dataRenamedBySdk && !('data_attr' in account))
392
+ throw new ChainError(ChainErrorKinds.InvalidArgument, pyRepr('data'));
393
+ const data = dataRenamedBySdk ? account.data_attr : pyItem(account, 'data');
394
+ if (!isPydanticDict(data)) {
395
+ throw new ChainError(ChainErrorKinds.RpcError, `'${pyTypeName(data)}' object has no attribute 'get'`, { chainId: this.chainId });
396
+ }
397
+ if (data[MEMO_REQUIRED_CONFIG_KEY] === MEMO_REQUIRED_CONFIG_VALUE) {
398
+ throw new AccountRequiresMemoError('Destination account requires a memo in the transaction.', destination, index);
399
+ }
400
+ }
401
+ }
402
+ async _submitTransaction(envelope) {
403
+ const horizon = this.asyncHorizonServer;
404
+ await this._checkMemoRequired(envelope);
405
+ const response = await horizon.httpClient.post(horizon.serverURL.clone().segment('transactions').toString(), `tx=${encodeURIComponent(envelope.toEnvelope().toXDR().toString('base64'))}`, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
406
+ return response.data;
407
+ }
408
+ async _loadAccountData(accountId) {
409
+ return this.asyncHorizonServer.accounts().accountId(accountId).call();
410
+ }
411
+ async getAssetBalance(asset, ownerAddress) {
412
+ this.assertStellarAsset(asset);
413
+ StellarChain.validateWalletAddress(ownerAddress);
414
+ const ownerClassicAddress = StellarChain.toClassicAccountId(ownerAddress);
415
+ if (asset.isNative() || asset.isSac()) {
416
+ const accountData = await this._loadAccountData(ownerClassicAddress);
417
+ const balances = (accountData.balances ?? []);
418
+ if (asset.isNative()) {
419
+ const record = accountData;
420
+ const subentryCount = Math.trunc(Number(record.subentry_count ?? 0));
421
+ const numSponsoring = Math.trunc(Number(record.num_sponsoring ?? 0));
422
+ const numSponsored = Math.trunc(Number(record.num_sponsored ?? 0));
423
+ const reserveUnits = 2 + subentryCount + numSponsoring - numSponsored;
424
+ const reservedStroops = BigInt(reserveUnits) * 5000000n;
425
+ for (const b of balances) {
426
+ if (b.asset_type === 'native') {
427
+ const totalStroops = hrDecimalToMinorUnits(new Decimal(pyDecimalString(pyItem(b, 'balance'))), StellarAsset.DECIMALS);
428
+ const availableStroops = totalStroops - reservedStroops;
429
+ return new Decimal(minorUnitsToHrString(availableStroops < 0n ? 0n : availableStroops, StellarAsset.DECIMALS));
430
+ }
431
+ }
432
+ return new Decimal(0);
433
+ }
434
+ for (const b of balances) {
435
+ if (b.asset_type === 'native')
436
+ continue;
437
+ if (b.asset_code === asset.code && b.asset_issuer === asset.issuer) {
438
+ return new Decimal(pyDecimalString(pyItem(b, 'balance')));
439
+ }
440
+ }
441
+ return new Decimal(0);
442
+ }
443
+ const result = await this._callHostFunction({
444
+ contractId: asset.contractId,
445
+ functionName: 'balance',
446
+ parameters: [new Address(ownerClassicAddress).toScVal()],
447
+ });
448
+ const balance = scvalToInt128(firstHostFunctionResult(result));
449
+ return new Decimal(minorUnitsToHrString(balance, asset.decimals));
450
+ }
451
+ async getWalletBalance(ownerAddress) {
452
+ const ownerClassicAddress = StellarChain.toClassicAccountId(ownerAddress);
453
+ const accountData = await this._loadAccountData(ownerClassicAddress);
454
+ const balances = (accountData.balances ?? []);
455
+ const result = new AssetMap();
456
+ for (const b of balances) {
457
+ if (b.asset_type === 'native') {
458
+ result.set(this._nativeAsset, new Decimal(pyDecimalString(pyItem(b, 'balance'))));
459
+ }
460
+ else {
461
+ const code = b.asset_code;
462
+ const issuer = b.asset_issuer;
463
+ if (code === undefined || code === null || issuer === undefined || issuer === null)
464
+ continue;
465
+ result.set(this.createSacToken(code, issuer), new Decimal(pyDecimalString(pyItem(b, 'balance'))));
466
+ }
467
+ }
468
+ return result;
469
+ }
470
+ async getTrustLineLimit(walletAddress, asset) {
471
+ this.assertStellarAsset(asset);
472
+ if (asset.isNative()) {
473
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Cannot get trustline for native XLM asset ${String(asset)}`, { chainId: this.chainId });
474
+ }
475
+ const classicAddress = StellarChain.toClassicAccountId(walletAddress);
476
+ const accountData = await this._loadAccountData(classicAddress);
477
+ const trustLine = { balance: new Decimal(0), limit: new Decimal(0) };
478
+ for (const b of (accountData.balances ?? [])) {
479
+ if (b.asset_type === 'native')
480
+ continue;
481
+ if (b.asset_code === asset.code && b.asset_issuer === asset.issuer) {
482
+ trustLine.limit = new Decimal(pyDecimalString('limit' in b ? b.limit : '0'));
483
+ trustLine.balance = new Decimal(pyDecimalString('balance' in b ? b.balance : '0'));
484
+ }
485
+ }
486
+ return trustLine;
487
+ }
488
+ createPrerequisiteForReceivableAsset(receiveAsset, receiverAddress) {
489
+ this.assertStellarAsset(receiveAsset);
490
+ if (receiveAsset.chainId !== this.chainId) {
491
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid chain id ${receiveAsset.chainId} for ${String(receiveAsset)}, expected ${this.chainId}.`, { chainId: this.chainId });
492
+ }
493
+ StellarChain.validateWalletAddress(receiverAddress);
494
+ const receiverClassicAddress = StellarChain.toClassicAccountId(receiverAddress);
495
+ if (receiveAsset.isNative())
496
+ return null;
497
+ if (!receiveAsset.isSac())
498
+ return null;
499
+ return new StellarChangeTrustLineTransactionPrerequisite({
500
+ chainId: this.chainId,
501
+ code: receiveAsset.code,
502
+ issuer: receiveAsset.issuer,
503
+ limit: new Decimal(StellarAsset.TRUST_LINE_MAX_LIMIT),
504
+ walletAddress: receiverClassicAddress,
505
+ });
506
+ }
507
+ buildUnsignedTransaction(req) {
508
+ return new StellarUnsignedTransaction({
509
+ chainId: this.chainId,
510
+ sourceAccountId: req.sourceAddress,
511
+ operations: req.operations,
512
+ baseFee: req.baseFee ?? null,
513
+ memo: req.memoText ? stellarTextMemo(req.memoText) : null,
514
+ });
515
+ }
516
+ buildTokenTransferOperation(req) {
517
+ return Operation.invokeContractFunction({
518
+ contract: req.asset.contractId,
519
+ function: 'transfer',
520
+ args: [
521
+ new Address(req.senderAddress).toScVal(),
522
+ new Address(req.receiverAddress).toScVal(),
523
+ nativeToScVal(hrDecimalToMinorUnits(new Decimal(req.amountHr.toString()), req.asset.decimals), { type: 'i128' }),
524
+ ],
525
+ });
526
+ }
527
+ async createTransferTransaction(req) {
528
+ const asset = req.asset;
529
+ this.assertStellarAsset(asset);
530
+ if (asset.chainId !== this.chainId) {
531
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid chain id ${asset.chainId} for asset ${String(asset)}, expected ${this.chainId}.`, { chainId: this.chainId });
532
+ }
533
+ StellarChain.validateWalletAddress(req.senderAddress);
534
+ const receiverIsContract = StrKey.isValidContract(req.receiverAddress);
535
+ if (!receiverIsContract) {
536
+ StellarChain.validateWalletAddress(req.receiverAddress);
537
+ }
538
+ const senderClassicAddress = StellarChain.toClassicAccountId(req.senderAddress);
539
+ const receiverTargetAddress = receiverIsContract ? req.receiverAddress : StellarChain.toClassicAccountId(req.receiverAddress);
540
+ const useClassicPayment = (asset.isNative() || asset.isSac()) && !receiverIsContract;
541
+ const memoText = req.memoText ?? null;
542
+ if (!useClassicPayment && memoText) {
543
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Cannot transfer ${asset.symbol} to ${req.receiverAddress} with a memo: it requires a Soroban \`transfer\` invocation, and Soroban transactions do not support memos.`, { chainId: this.chainId });
544
+ }
545
+ const transferOp = useClassicPayment
546
+ ? Operation.payment({
547
+ source: senderClassicAddress,
548
+ destination: receiverTargetAddress,
549
+ asset: asset.toSdkAsset(),
550
+ amount: stellarOperationAmount(req.amountHr, 'amount'),
551
+ })
552
+ : this.buildTokenTransferOperation({
553
+ asset,
554
+ senderAddress: senderClassicAddress,
555
+ receiverAddress: receiverTargetAddress,
556
+ amountHr: req.amountHr,
557
+ });
558
+ const baseFee = await this._resolveGasPricing(req.gasPricing === undefined ? FeePriority.NORMAL : req.gasPricing);
559
+ const unsignedTx = new StellarUnsignedTransaction({
560
+ chainId: this.chainId,
561
+ sourceAccountId: senderClassicAddress,
562
+ operations: [transferOp],
563
+ baseFee,
564
+ memo: memoText ? stellarTextMemo(memoText) : null,
565
+ });
566
+ const prerequisites = [];
567
+ if (!receiverIsContract) {
568
+ const prerequisite = this.createPrerequisiteForReceivableAsset(asset, receiverTargetAddress);
569
+ if (prerequisite !== null)
570
+ prerequisites.push(prerequisite);
571
+ }
572
+ return new UnsignedTransactionWithPrerequisites({ prerequisites, transaction: unsignedTx });
573
+ }
574
+ async createExactInSwapTransaction(req) {
575
+ const senderClassicAddress = StellarChain.toClassicAccountId(req.senderAddress);
576
+ const receiverClassicAddress = StellarChain.toClassicAccountId(req.receiverAddress);
577
+ const sendPath = await this.asyncHorizonServer
578
+ .strictSendPaths(req.sendAsset.toSdkAsset(), pyDecimalStr(req.sendAmount.toString()), [req.receiveAsset.toSdkAsset()])
579
+ .call();
580
+ const records = sendPath.records;
581
+ if (records.length === 0) {
582
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'Cannot find a swap route', { chainId: this.chainId });
583
+ }
584
+ const bestRecord = records[0];
585
+ const path = bestRecord.path.map((p) => p.asset_type === 'native' ? this._nativeAsset.toSdkAsset() : stellarSdkAsset(p.asset_code, p.asset_issuer));
586
+ const destinationAmount = new PythonDecimal(pyDecimalString(pyItem(bestRecord, 'destination_amount')));
587
+ const minimumReceiveValue = destinationAmount
588
+ .mul(new PythonDecimal(1).minus(new PythonDecimal(req.slippageTolerancePercent.toString()).div(new PythonDecimal(100))))
589
+ .toDecimalPlaces(req.receiveAsset.decimals, Decimal.ROUND_HALF_UP);
590
+ const prerequisite = this.createPrerequisiteForReceivableAsset(req.receiveAsset, receiverClassicAddress);
591
+ const prerequisites = [];
592
+ if (prerequisite !== null)
593
+ prerequisites.push(prerequisite);
594
+ const unsignedTx = new StellarUnsignedTransaction({
595
+ chainId: this.chainId,
596
+ sourceAccountId: senderClassicAddress,
597
+ operations: [
598
+ Operation.pathPaymentStrictSend({
599
+ sendAsset: req.sendAsset.toSdkAsset(),
600
+ sendAmount: stellarOperationAmount(req.sendAmount, 'send_amount'),
601
+ destAsset: req.receiveAsset.toSdkAsset(),
602
+ destMin: stellarOperationAmount(minimumReceiveValue, 'dest_min'),
603
+ path,
604
+ source: senderClassicAddress,
605
+ destination: receiverClassicAddress,
606
+ }),
607
+ ],
608
+ });
609
+ return new UnsignedTransactionWithPrerequisites({ prerequisites, transaction: unsignedTx });
610
+ }
611
+ static _muxedToClassic(account) {
612
+ if (account instanceof MuxedAccount)
613
+ return account.baseAccount().accountId();
614
+ return toClassicStellarAccountId(account);
615
+ }
616
+ _balanceChangesFromOperations(tx, opts = {}) {
617
+ const { filteredWallets = null, filteredAssets = null } = opts;
618
+ const includeNative = filteredAssets === null || filteredAssets.some((a) => a.isNative());
619
+ const filteredAssetsByIdentifier = filteredAssets === null ? null : this.identifierIndex(filteredAssets);
620
+ const txSourceClassic = StellarChain._muxedToClassic(tx.source);
621
+ const balanceChanges = new Map();
622
+ const record = (account, sdkAsset, deltaHr) => {
623
+ if (filteredWallets !== null && !pyContains(filteredWallets, account))
624
+ return;
625
+ let stellarAsset;
626
+ if (sdkAsset.isNative()) {
627
+ if (!includeNative)
628
+ return;
629
+ stellarAsset = this._nativeAsset;
630
+ }
631
+ else {
632
+ stellarAsset = this.createSacToken(sdkAsset.getCode(), sdkAsset.getIssuer());
633
+ if (filteredAssetsByIdentifier !== null && !filteredAssetsByIdentifier.has(stellarAsset.identifier))
634
+ return;
635
+ }
636
+ AssetBalanceChange.upsert(balanceChanges, account, stellarAsset, AssetBalanceChange.fromHr(deltaHr, stellarAsset.decimals));
637
+ };
638
+ const rawOperations = transactionXdrOperations(tx);
639
+ for (const [index, op] of tx.operations.entries()) {
640
+ const opSource = op.source ? StellarChain._muxedToClassic(op.source) : txSourceClassic;
641
+ const body = rawOperations[index].body();
642
+ if (op.type === 'payment') {
643
+ const asset = stellarSdkAssetFromXdr(body.paymentOp().asset());
644
+ const amountHr = new Decimal(op.amount);
645
+ const destination = StellarChain._muxedToClassic(op.destination);
646
+ record(opSource, asset, amountHr.neg());
647
+ record(destination, asset, amountHr);
648
+ }
649
+ else if (op.type === 'pathPaymentStrictSend') {
650
+ const raw = body.pathPaymentStrictSendOp();
651
+ const sendAmountHr = new Decimal(op.sendAmount);
652
+ const destMin = new Decimal(op.destMin);
653
+ const destination = StellarChain._muxedToClassic(op.destination);
654
+ record(opSource, stellarSdkAssetFromXdr(raw.sendAsset()), sendAmountHr.neg());
655
+ record(destination, stellarSdkAssetFromXdr(raw.destAsset()), destMin);
656
+ }
657
+ else if (op.type === 'pathPaymentStrictReceive') {
658
+ const raw = body.pathPaymentStrictReceiveOp();
659
+ const sendMax = new Decimal(op.sendMax);
660
+ const destAmount = new Decimal(op.destAmount);
661
+ const destination = StellarChain._muxedToClassic(op.destination);
662
+ record(opSource, stellarSdkAssetFromXdr(raw.sendAsset()), sendMax.neg());
663
+ record(destination, stellarSdkAssetFromXdr(raw.destAsset()), destAmount);
664
+ }
665
+ }
666
+ return balanceChanges;
667
+ }
668
+ async _getTransactionDataFromStellarExpert(pagingToken) {
669
+ const url = `${this.stellarExpertApiUrl}/tx/${pagingToken}`;
670
+ const response = await fetch(url, { signal: AbortSignal.timeout(AIOHTTP_DEFAULT_TOTAL_TIMEOUT_MS) });
671
+ return parseStellarExpertTransactionInfo(await response.json());
672
+ }
673
+ async getTransactionStatus(txHash, opts) {
674
+ if (opts?.wait || (opts?.confirmations !== undefined && opts.confirmations > 1)) {
675
+ throw new ChainError(ChainErrorKinds.FeatureNotSupported, 'StellarChain.getTransactionStatus does not honor wait/confirmations opts (would silently return immediately). Poll consumer-side or omit the opts.', { chainId: this.chainId });
676
+ }
677
+ const filteredWallets = opts?.filteredWallets === undefined || opts.filteredWallets === null ? null : pyStringContainer(opts.filteredWallets);
678
+ const filteredAssets = opts?.filteredAssets === undefined || opts.filteredAssets === null ? null : [...opts.filteredAssets];
679
+ if (Array.isArray(txHash)) {
680
+ return runBatch(txHash, (hash) => this.getTransactionStatusOnce(hash, filteredWallets, filteredAssets));
681
+ }
682
+ return this.getTransactionStatusOnce(txHash, filteredWallets, filteredAssets);
683
+ }
684
+ async getTransactionStatusOnce(txHash, filteredWallets, filteredAssets) {
685
+ let txResp;
686
+ try {
687
+ txResp = await this.asyncHorizonServer.transactions().transaction(txHash).call();
688
+ }
689
+ catch (err) {
690
+ if (err instanceof NotFoundError) {
691
+ return StellarTransactionStatus.notFound(this.chainId, { code: 'NOT_FOUND', reason: err.message });
692
+ }
693
+ throw this.rpcError(`Horizon transaction lookup failed for ${txHash}`, err, txHash, this.horizonUrl);
694
+ }
695
+ const ledger = await this.asyncHorizonServer.ledgers().ledger(txResp.ledger_attr).call();
696
+ const inclusionAt = new Date(ledger.closed_at);
697
+ const raw = txResp;
698
+ const pagingToken = String(raw.paging_token);
699
+ const successful = Boolean(raw.successful);
700
+ const envelopeXdr = String(raw.envelope_xdr);
701
+ let fees = null;
702
+ if ('fee_charged' in raw) {
703
+ const feePayer = String(raw.fee_account);
704
+ const feeCharged = Math.trunc(Number(raw.fee_charged ?? raw.max_fee ?? '0'));
705
+ fees = new StellarTransactionFees({ feeStroops: feeCharged, feePayer });
706
+ }
707
+ const txEnvelope = xdr.TransactionEnvelope.fromXDR(envelopeXdr, 'base64');
708
+ let operationsXdr;
709
+ let memoXdr;
710
+ switch (txEnvelope.switch().name) {
711
+ case 'envelopeTypeTxFeeBump': {
712
+ const inner = txEnvelope.feeBump().tx().innerTx().v1().tx();
713
+ operationsXdr = inner.operations();
714
+ memoXdr = inner.memo();
715
+ break;
716
+ }
717
+ case 'envelopeTypeTxV0': {
718
+ const inner = txEnvelope.v0().tx();
719
+ operationsXdr = inner.operations();
720
+ memoXdr = inner.memo();
721
+ break;
722
+ }
723
+ case 'envelopeTypeTx': {
724
+ const inner = txEnvelope.v1().tx();
725
+ operationsXdr = inner.operations();
726
+ memoXdr = inner.memo();
727
+ break;
728
+ }
729
+ default:
730
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Unknown envelope type: ${txEnvelope.switch().name}`, {
731
+ chainId: this.chainId,
732
+ txHash,
733
+ });
734
+ }
735
+ const memo = Memo.fromXDRObject(memoXdr);
736
+ if (!successful) {
737
+ const extras = raw.extras;
738
+ const resultCodes = extras?.result_codes || raw.result_codes;
739
+ const errorMessage = resultCodes ? `Stellar transaction failed: ${pyRepr(resultCodes)}` : 'Stellar transaction failed';
740
+ return StellarTransactionStatus.failed({
741
+ chainId: this.chainId,
742
+ inclusionAt,
743
+ error: { code: 'FAILED', reason: errorMessage },
744
+ fees,
745
+ memo,
746
+ });
747
+ }
748
+ const operations = operationsXdr.map((op) => Operation.fromXDRObject(op));
749
+ const isContractCall = operations.length === 1 && operations[0].type === 'invokeHostFunction';
750
+ if (isContractCall) {
751
+ if (operations.length > 1) {
752
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Soroban contract call cannot have more than 1 operation ${txHash}`, { chainId: this.chainId, txHash });
753
+ }
754
+ let sorobanTxResp;
755
+ try {
756
+ const reply = await this._sorobanRpc('getTransaction', { hash: txHash });
757
+ sorobanTxResp = isGetTransactionResponse(reply) ? reply : null;
758
+ }
759
+ catch {
760
+ sorobanTxResp = null;
761
+ }
762
+ const sorobanDiagnosticEvents = sorobanTxResp?.events?.diagnosticEventsXdr;
763
+ const hasSorobanTxResp = sorobanTxResp !== null &&
764
+ sorobanTxResp.status !== rpc.Api.GetTransactionStatus.NOT_FOUND &&
765
+ sorobanDiagnosticEvents !== undefined &&
766
+ sorobanDiagnosticEvents !== null;
767
+ let stellarExpertResult = null;
768
+ if (!hasSorobanTxResp) {
769
+ try {
770
+ stellarExpertResult = await this._getTransactionDataFromStellarExpert(pagingToken);
771
+ }
772
+ catch {
773
+ stellarExpertResult = null;
774
+ }
775
+ }
776
+ if (hasSorobanTxResp && sorobanTxResp !== null) {
777
+ const balanceChanges = await this.getBalanceChangesFromDiagnosisEvents(sorobanDiagnosticEvents.map((d) => xdr.DiagnosticEvent.fromXDR(d, 'base64')), { filteredWallets, filteredAssets });
778
+ this.upsertFee(balanceChanges, fees);
779
+ if (sorobanTxResp.status === rpc.Api.GetTransactionStatus.SUCCESS) {
780
+ return StellarTransactionStatus.successful({ chainId: this.chainId, inclusionAt, balanceChanges, fees, memo });
781
+ }
782
+ return StellarTransactionStatus.failed({
783
+ chainId: this.chainId,
784
+ inclusionAt,
785
+ error: {
786
+ code: String(sorobanTxResp.status),
787
+ reason: `Stellar soroban transaction failed: GetTransactionStatus.${sorobanTxResp.status}`,
788
+ },
789
+ fees,
790
+ memo,
791
+ });
792
+ }
793
+ else if (stellarExpertResult !== null) {
794
+ const txEnv = parseSignedStellarEnvelope(stellarExpertResult.body, this.networkPassphrase);
795
+ const txMeta = xdr.TransactionMeta.fromXDR(stellarExpertResult.meta, 'base64');
796
+ const ops = txEnv.operations;
797
+ if (!(ops.length === 1 && ops[0].type === 'invokeHostFunction')) {
798
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Stellar expert returned envelope is not contract call, operations: ${ops.map((o) => o.type).join(',')}`, { chainId: this.chainId, txHash });
799
+ }
800
+ if (txMeta.switch() !== 4) {
801
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Unsupported transaction metadata version ${txMeta.switch()}`, {
802
+ chainId: this.chainId,
803
+ txHash,
804
+ });
805
+ }
806
+ const txResult = xdr.TransactionResult.fromXDR(stellarExpertResult.result, 'base64');
807
+ const balanceChanges = await this.getBalanceChangesFromDiagnosisEvents(txMeta.v4().diagnosticEvents(), { filteredWallets, filteredAssets });
808
+ this.upsertFee(balanceChanges, fees);
809
+ const resultCode = txResult.result().switch();
810
+ if (resultCode.name === 'txSuccess') {
811
+ return StellarTransactionStatus.successful({ chainId: this.chainId, inclusionAt, balanceChanges, fees, memo });
812
+ }
813
+ return StellarTransactionStatus.failed({
814
+ chainId: this.chainId,
815
+ inclusionAt,
816
+ error: { code: resultCode.name, reason: `Stellar transaction failed: ${resultCode.value}` },
817
+ fees,
818
+ memo,
819
+ });
820
+ }
821
+ else {
822
+ throw new ChainError(ChainErrorKinds.RpcError, `Cannot get status of transaction from soroban rpc and stellar expert for tx hash ${txHash}`, { chainId: this.chainId, txHash });
823
+ }
824
+ }
825
+ const balanceChanges = await this._effectsToBalanceChanges(txHash, { filteredWallets, filteredAssets });
826
+ this.upsertFee(balanceChanges, fees);
827
+ return StellarTransactionStatus.successful({ chainId: this.chainId, inclusionAt, balanceChanges, fees, memo });
828
+ }
829
+ async _effectsToBalanceChanges(txHash, opts = {}) {
830
+ const { filteredWallets = null, filteredAssets = null } = opts;
831
+ const includeNative = filteredAssets === null || filteredAssets.some((a) => a.isNative());
832
+ const filteredAssetsByIdentifier = filteredAssets === null ? null : this.identifierIndex(filteredAssets);
833
+ const balanceChanges = new Map();
834
+ const page = await this.asyncHorizonServer.effects().forTransaction(txHash).limit(200).call();
835
+ const records = (page.records ?? []);
836
+ const buildAsset = (rec) => {
837
+ if (rec.asset_type === 'native') {
838
+ return includeNative ? this._nativeAsset : null;
839
+ }
840
+ const code = rec.asset_code;
841
+ const issuer = rec.asset_issuer;
842
+ if (code === undefined || code === null || issuer === undefined || issuer === null)
843
+ return null;
844
+ const stellarAsset = this.createSacToken(code, issuer);
845
+ if (filteredAssetsByIdentifier !== null && !filteredAssetsByIdentifier.has(stellarAsset.identifier))
846
+ return null;
847
+ return stellarAsset;
848
+ };
849
+ const operationId = (rec) => String(rec.id ?? '').split('-')[0];
850
+ const nativelyCreditedByOperation = new Set();
851
+ for (const rec of records) {
852
+ if (rec.type === 'account_credited' && rec.asset_type === 'native') {
853
+ if (rec.account)
854
+ nativelyCreditedByOperation.add(`${operationId(rec)}|${rec.account}`);
855
+ }
856
+ }
857
+ for (const rec of records) {
858
+ const eType = rec.type;
859
+ const wallet = eType === 'contract_credited' || eType === 'contract_debited' ? rec.contract : rec.account;
860
+ if (!wallet)
861
+ continue;
862
+ if (filteredWallets !== null && !pyContains(filteredWallets, wallet))
863
+ continue;
864
+ if (eType === 'account_credited' || eType === 'contract_credited') {
865
+ const stellarAsset = buildAsset(rec);
866
+ if (stellarAsset === null)
867
+ continue;
868
+ AssetBalanceChange.upsert(balanceChanges, wallet, stellarAsset, AssetBalanceChange.fromHr(new Decimal(pyDecimalString(pyItem(rec, 'amount'))), stellarAsset.decimals));
869
+ }
870
+ else if (eType === 'account_debited' || eType === 'contract_debited') {
871
+ const stellarAsset = buildAsset(rec);
872
+ if (stellarAsset === null)
873
+ continue;
874
+ AssetBalanceChange.upsert(balanceChanges, wallet, stellarAsset, AssetBalanceChange.fromHr(new Decimal(pyDecimalString(pyItem(rec, 'amount'))).neg(), stellarAsset.decimals));
875
+ }
876
+ else if (eType === 'account_created') {
877
+ if (!includeNative)
878
+ continue;
879
+ const stellarAsset = this._nativeAsset;
880
+ const startingBalance = new Decimal(pyDecimalString(pyItem(rec, 'starting_balance')));
881
+ if (nativelyCreditedByOperation.has(`${operationId(rec)}|${wallet}`))
882
+ continue;
883
+ AssetBalanceChange.upsert(balanceChanges, wallet, stellarAsset, AssetBalanceChange.fromHr(startingBalance, stellarAsset.decimals));
884
+ }
885
+ }
886
+ return balanceChanges;
887
+ }
888
+ static getTransfersFromDiagnosisEvents(diagnosisEvents) {
889
+ const transfers = [];
890
+ for (const d of diagnosisEvents) {
891
+ const event = d.event();
892
+ const contractId = event.contractId();
893
+ const topics = event.body().v0().topics();
894
+ if (contractId && topics.length > 0) {
895
+ const tokenContractId = StrKey.encodeContract(contractId);
896
+ if (topics[0].switch().name === 'scvSymbol') {
897
+ const topic0 = topics[0].sym().toString();
898
+ if (topic0 === 'transfer') {
899
+ if (topics.length < 3)
900
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, 'list index out of range');
901
+ const transferFrom = Address.fromScVal(topics[1]).toString();
902
+ const transferTo = Address.fromScVal(topics[2]).toString();
903
+ let contractSacCode;
904
+ let contractSacIssuer;
905
+ if (topics.length > 3) {
906
+ const tokenIdentifier = scvalToUtf8String(topics[3]);
907
+ if (tokenIdentifier === 'native') {
908
+ contractSacCode = 'XLM';
909
+ contractSacIssuer = null;
910
+ }
911
+ else if (tokenIdentifier.includes(':')) {
912
+ const splitted = tokenIdentifier.split(':');
913
+ contractSacCode = splitted[0];
914
+ contractSacIssuer = splitted[1];
915
+ }
916
+ else {
917
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Invalid token identifier ${tokenIdentifier}`);
918
+ }
919
+ }
920
+ else {
921
+ contractSacCode = null;
922
+ contractSacIssuer = null;
923
+ }
924
+ const transferAmountMr = scvalToInt128(event.body().v0().data());
925
+ transfers.push({
926
+ fromAccount: transferFrom,
927
+ toAccount: transferTo,
928
+ tokenContractId,
929
+ tokenContractSacCode: contractSacCode,
930
+ tokenContractSacIssuer: contractSacIssuer,
931
+ amountMr: transferAmountMr,
932
+ });
933
+ }
934
+ }
935
+ }
936
+ }
937
+ return transfers;
938
+ }
939
+ async getBalanceChangesFromDiagnosisEvents(diagnosisEvents, opts = {}) {
940
+ const { filteredWallets = null, filteredAssets = null } = opts;
941
+ const tokenTransfers = StellarChain.getTransfersFromDiagnosisEvents(diagnosisEvents);
942
+ const balanceChanges = new Map();
943
+ for (const t of tokenTransfers) {
944
+ const asset = t.tokenContractSacCode !== null
945
+ ? this.createSacToken(t.tokenContractSacCode, t.tokenContractSacIssuer)
946
+ : await this.createNonSacToken(t.tokenContractId);
947
+ if (filteredAssets !== null && !filteredAssets.some((a) => a.strictEquals(asset)))
948
+ continue;
949
+ if (filteredWallets === null || pyContains(filteredWallets, t.fromAccount)) {
950
+ AssetBalanceChange.upsert(balanceChanges, t.fromAccount, asset, AssetBalanceChange.fromMr(-t.amountMr, asset.decimals));
951
+ }
952
+ if (filteredWallets === null || pyContains(filteredWallets, t.toAccount)) {
953
+ AssetBalanceChange.upsert(balanceChanges, t.toAccount, asset, AssetBalanceChange.fromMr(t.amountMr, asset.decimals));
954
+ }
955
+ }
956
+ return balanceChanges;
957
+ }
958
+ async supportsFullTransactionSimulation(unsignedTransaction) {
959
+ if (!(unsignedTransaction instanceof StellarUnsignedTransaction)) {
960
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'Expected StellarUnsignedTransaction', { chainId: this.chainId });
961
+ }
962
+ return true;
963
+ }
964
+ async simulateTransaction(req) {
965
+ const transaction = req.transaction;
966
+ if (!(transaction instanceof StellarUnsignedTransaction)) {
967
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid transaction: ${String(transaction)}, expected StellarUnsignedTransaction.`, { chainId: this.chainId });
968
+ }
969
+ const filteredWallets = req.filteredWallets === undefined || req.filteredWallets === null ? null : pyStringContainer(req.filteredWallets);
970
+ const filteredAssets = req.filteredAssets === undefined || req.filteredAssets === null ? null : [...req.filteredAssets];
971
+ const envelope = await transaction.buildTransactionEnvelope(this);
972
+ if (transaction.isSorobanInvokeContractTransaction) {
973
+ const sim = await simulateSorobanTransaction(envelope, this);
974
+ if (sim.error) {
975
+ return new StellarTransactionSimulationResult({
976
+ chainId: this.chainId,
977
+ statusType: TransactionSimulationStatusTypes.Failed,
978
+ balanceChanges: new Map(),
979
+ error: new Error(sim.error),
980
+ });
981
+ }
982
+ if (sim.events === null) {
983
+ throw new ChainError(ChainErrorKinds.RpcError, "'NoneType' object is not iterable", { chainId: this.chainId });
984
+ }
985
+ const balanceChanges = await this.getBalanceChangesFromDiagnosisEvents(sim.events.map((event) => xdr.DiagnosticEvent.fromXDR(event, 'base64')), { filteredWallets, filteredAssets });
986
+ return new StellarTransactionSimulationResult({
987
+ chainId: this.chainId,
988
+ statusType: TransactionSimulationStatusTypes.Success,
989
+ balanceChanges,
990
+ error: null,
991
+ fees: sim.minResourceFee !== null
992
+ ? new StellarTransactionFees({
993
+ feeStroops: Number(sim.minResourceFee),
994
+ feePayer: StellarChain._muxedToClassic(envelope.source),
995
+ })
996
+ : null,
997
+ });
998
+ }
999
+ try {
1000
+ const balanceChanges = this._balanceChangesFromOperations(envelope, { filteredWallets, filteredAssets });
1001
+ const memo = envelope.memo;
1002
+ const feePayer = StellarChain._muxedToClassic(envelope.source);
1003
+ const feeStroops = Math.trunc(Number(envelope.fee));
1004
+ const fees = new StellarTransactionFees({ feeStroops, feePayer });
1005
+ AssetBalanceChange.upsert(balanceChanges, feePayer, this._nativeAsset, AssetBalanceChange.fromMr(-BigInt(feeStroops), StellarAsset.DECIMALS));
1006
+ return new StellarTransactionSimulationResult({
1007
+ chainId: this.chainId,
1008
+ statusType: TransactionSimulationStatusTypes.Success,
1009
+ balanceChanges,
1010
+ error: null,
1011
+ fees,
1012
+ memo,
1013
+ });
1014
+ }
1015
+ catch (err) {
1016
+ return new StellarTransactionSimulationResult({
1017
+ chainId: this.chainId,
1018
+ statusType: TransactionSimulationStatusTypes.Failed,
1019
+ balanceChanges: new Map(),
1020
+ error: err instanceof Error ? err : new Error(String(err)),
1021
+ });
1022
+ }
1023
+ }
1024
+ async broadcastSignedTransaction(signedTransaction) {
1025
+ if (!(signedTransaction instanceof StellarSignedTransaction)) {
1026
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid transaction ${String(signedTransaction)}, expected StellarSignedTransaction.`, { chainId: this.chainId });
1027
+ }
1028
+ if (this.chainId !== signedTransaction.chainId) {
1029
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid chain for signed transaction with chain id ${signedTransaction.chainId}, expected ${this.chainId}.`, { chainId: this.chainId });
1030
+ }
1031
+ const envelope = parseSignedStellarEnvelope(signedTransaction.signedXdr, this.networkPassphrase);
1032
+ try {
1033
+ const response = await this._submitTransaction(envelope);
1034
+ if (response === null || typeof response !== 'object' || !('hash' in response)) {
1035
+ throw new ChainError(ChainErrorKinds.RpcError, pyRepr('hash'), { chainId: this.chainId, txHash: signedTransaction.txHash });
1036
+ }
1037
+ return new StellarBroadcastTransactionResponse({ chain: this, txHash: response.hash });
1038
+ }
1039
+ catch (err) {
1040
+ return new StellarBroadcastTransactionResponse({
1041
+ chain: this,
1042
+ txHash: signedTransaction.txHash,
1043
+ broadcastError: err instanceof Error ? err : new Error(String(err)),
1044
+ });
1045
+ }
1046
+ }
1047
+ static verifySignature(publicKey, message, signedMessage) {
1048
+ if (!(signedMessage instanceof StellarSignedMessage)) {
1049
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `StellarChain expected a StellarSignedMessage, got ${pyTypeRepr(signedMessage)}`);
1050
+ }
1051
+ try {
1052
+ return Keypair.fromPublicKey(String(publicKey)).verify(stellarMessageHash(message), Buffer.from(bytesFromHex(signedMessage.signature)));
1053
+ }
1054
+ catch {
1055
+ return false;
1056
+ }
1057
+ }
1058
+ verifySignature(publicKey, message, signedMessage) {
1059
+ return StellarChain.verifySignature(publicKey, message, signedMessage);
1060
+ }
1061
+ async getAccountNextSequence(walletAddress) {
1062
+ const classicAddress = StellarChain.toClassicAccountId(walletAddress);
1063
+ const account = await loadStellarAccount(this, classicAddress);
1064
+ return BigInt(account.sequenceNumber()) + 1n;
1065
+ }
1066
+ async getBalance(owner, tokenIdentifier) {
1067
+ const asset = await this.resolveAsset(tokenIdentifier);
1068
+ const balance = await this.getAssetBalance(asset, owner);
1069
+ return hrDecimalToMinorUnits(balance, asset.decimals);
1070
+ }
1071
+ async createTransferUnsignedTransaction(req) {
1072
+ if (!req.from) {
1073
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'Stellar transfers require an explicit `from` (no implicit signer)', {
1074
+ chainId: this.chainId,
1075
+ });
1076
+ }
1077
+ if (req.isFullBalance === true) {
1078
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'CreateTransferRequest.isFullBalance is not supported on StellarChain — pass `amount` or `amountHr` explicitly.', { chainId: this.chainId });
1079
+ }
1080
+ const asset = await this.resolveAsset(req.tokenIdentifier);
1081
+ const resolved = resolveTransferAmount(req, asset.decimals);
1082
+ if (resolved.kind !== 'exact') {
1083
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'Stellar transfers require an explicit amount', { chainId: this.chainId });
1084
+ }
1085
+ const bundle = await this.createTransferTransaction({
1086
+ asset,
1087
+ amountHr: new Decimal(minorUnitsToHrString(resolved.amountMr, asset.decimals)),
1088
+ senderAddress: req.from,
1089
+ receiverAddress: req.to,
1090
+ memoText: req.memo ?? null,
1091
+ gasPricing: req.gasPricing,
1092
+ });
1093
+ for (const prerequisite of bundle.prerequisites) {
1094
+ if (await this.isPrerequisiteAlreadyMet(prerequisite))
1095
+ continue;
1096
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Stellar transfer to ${req.to} needs a prerequisite the receiver must handle first (${prerequisite.constructor.name}); createTransferUnsignedTransaction cannot return it. Use createTransferTransaction and handle the prerequisite with the receiver's wallet.`, { chainId: this.chainId, address: req.to });
1097
+ }
1098
+ return bundle.transaction;
1099
+ }
1100
+ async isPrerequisiteAlreadyMet(prerequisite) {
1101
+ if (!(prerequisite instanceof StellarChangeTrustLineTransactionPrerequisite))
1102
+ return false;
1103
+ try {
1104
+ const current = await this.getTrustLineLimit(prerequisite.walletAddress, this.createSacToken(prerequisite.code, prerequisite.issuer));
1105
+ return current.limit.gte(prerequisite.limit);
1106
+ }
1107
+ catch (err) {
1108
+ if (err instanceof NotFoundError)
1109
+ return false;
1110
+ throw err;
1111
+ }
1112
+ }
1113
+ async broadcast(signed, opts) {
1114
+ if (opts && opts.signal !== undefined) {
1115
+ throw new ChainError(ChainErrorKinds.FeatureNotSupported, "Stellar broadcast: signal is not honored (silently ignoring would let a caller conclude 'not sent' while the tx still lands)", { chainId: this.chainId });
1116
+ }
1117
+ const signedTransaction = this.parseSignedInput(signed);
1118
+ const response = await this.broadcastSignedTransaction(signedTransaction);
1119
+ const error = response.broadcastError;
1120
+ if (error === null) {
1121
+ const signedHash = signedTransaction.txHash.toLowerCase();
1122
+ if (typeof response.txHash !== 'string' || response.txHash.toLowerCase() !== signedHash) {
1123
+ throw new ChainError(ChainErrorKinds.RpcError, `Horizon answered hash ${pyRepr(response.txHash)} for the signed transaction ${signedTransaction.txHash}`, { chainId: this.chainId, txHash: signedTransaction.txHash });
1124
+ }
1125
+ return signedHash;
1126
+ }
1127
+ const rpcUrl = this.horizonUrl;
1128
+ if (error instanceof AccountRequiresMemoError) {
1129
+ throw new ChainError(ChainErrorKinds.BroadcastRejected, `Stellar broadcast refused before submission on ${this.name}: destination ${error.accountId} requires a memo (SEP-29, operation ${error.operationIndex})`, { chainId: this.chainId, txHash: response.txHash, address: error.accountId }, error);
1130
+ }
1131
+ const rejection = horizonSubmissionRejection(error);
1132
+ if (rejection !== null) {
1133
+ throw new ChainError(ChainErrorKinds.BroadcastRejected, sanitizeMessage(`Stellar broadcast rejected on ${this.name}: ${describeHorizonRejection(rejection, error)}`, rpcUrl), { chainId: this.chainId, txHash: response.txHash }, sanitizeCause(error, rpcUrl));
1134
+ }
1135
+ throw this.rpcError(`Stellar broadcast failed on ${this.name}`, error, response.txHash, rpcUrl);
1136
+ }
1137
+ async getChainTipHeight() {
1138
+ const page = await this.asyncHorizonServer.ledgers().order('desc').limit(1).call();
1139
+ return Number(page.records[0].sequence);
1140
+ }
1141
+ async verifyMessageSignature(req) {
1142
+ try {
1143
+ return StellarChain.verifySignature(req.signer, req.message, new StellarSignedMessage(req.signature));
1144
+ }
1145
+ catch {
1146
+ return false;
1147
+ }
1148
+ }
1149
+ parseSignedInput(signed) {
1150
+ if (typeof signed === 'string') {
1151
+ const trimmed = signed.trim();
1152
+ if (trimmed.startsWith('{')) {
1153
+ const payload = coerceJsonDict(trimmed);
1154
+ if (payload[JSON_TRANSACTION_TYPE_KEY] === StellarSignedTransaction.JSON_TYPE) {
1155
+ return StellarSignedTransaction.fromJson(payload);
1156
+ }
1157
+ throw new ChainError(ChainErrorKinds.InvalidArgument, 'Stellar broadcast: JSON input must be a StellarSignedTransaction payload', {
1158
+ chainId: this.chainId,
1159
+ });
1160
+ }
1161
+ return new StellarSignedTransaction({ chainId: this.chainId, signedXdr: trimmed, networkPassphrase: this.networkPassphrase });
1162
+ }
1163
+ return new StellarSignedTransaction({
1164
+ chainId: this.chainId,
1165
+ signedXdr: Buffer.from(signed).toString('base64'),
1166
+ networkPassphrase: this.networkPassphrase,
1167
+ });
1168
+ }
1169
+ upsertFee(balanceChanges, fees) {
1170
+ if (!fees)
1171
+ return;
1172
+ AssetBalanceChange.upsert(balanceChanges, fees.feePayer, this._nativeAsset, AssetBalanceChange.fromMr(-BigInt(fees.feeStroops), StellarAsset.DECIMALS));
1173
+ }
1174
+ identifierIndex(assets) {
1175
+ const index = new Map();
1176
+ for (const a of assets) {
1177
+ this.assertStellarAsset(a);
1178
+ if (a.identifier !== undefined)
1179
+ index.set(a.identifier, a);
1180
+ }
1181
+ return index;
1182
+ }
1183
+ assertStellarAsset(asset) {
1184
+ if (!(asset instanceof StellarAsset)) {
1185
+ throw new ChainError(ChainErrorKinds.InvalidArgument, `Invalid asset ${String(asset)} of type ${pyTypeRepr(asset)}, expected StellarAsset.`, {
1186
+ chainId: this.chainId,
1187
+ });
1188
+ }
1189
+ }
1190
+ envName() {
1191
+ return this.name.replace(/ /g, '_').toUpperCase();
1192
+ }
1193
+ loadUrlFromEnv(candidates, fallback) {
1194
+ const env = globalThis.process?.env;
1195
+ for (const name of candidates) {
1196
+ const value = env?.[name];
1197
+ if (value)
1198
+ return value;
1199
+ }
1200
+ return fallback;
1201
+ }
1202
+ async soroban(call) {
1203
+ try {
1204
+ return await call;
1205
+ }
1206
+ catch (err) {
1207
+ throw sorobanRpcError(err, this.chainId, this.sorobanRpcUrl);
1208
+ }
1209
+ }
1210
+ rpcError(message, err, txHash, rpcUrl) {
1211
+ return new ChainError(ChainErrorKinds.RpcError, sanitizeMessage(`${message}: ${err instanceof Error ? err.message : String(err)}`, rpcUrl), { chainId: this.chainId, txHash }, sanitizeCause(err, rpcUrl));
1212
+ }
1213
+ }
1214
+ export function scvalToUint32(value) {
1215
+ if (value.switch().name !== 'scvU32') {
1216
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Expected an SCV_U32 value, got ${value.switch().name}`);
1217
+ }
1218
+ return value.u32();
1219
+ }
1220
+ export function scvalToUtf8String(value) {
1221
+ if (value.switch().name !== 'scvString') {
1222
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Expected an SCV_STRING value, got ${value.switch().name}`);
1223
+ }
1224
+ const raw = value.str();
1225
+ return pyDecodeUtf8(typeof raw === 'string' ? Buffer.from(raw, 'utf8') : Buffer.from(raw));
1226
+ }
1227
+ export function scvalToInt128(value) {
1228
+ if (value.switch().name !== 'scvI128') {
1229
+ throw new ChainError(ChainErrorKinds.TransactionDecodeFailed, `Expected an SCV_I128 value, got ${value.switch().name}`);
1230
+ }
1231
+ return scValToBigInt(value);
1232
+ }
1233
+ const HORIZON_TRANSACTION_REJECTED_STATUS = 400;
1234
+ const STELLAR_SDK_GET_TIMEOUT_MS = 11_000;
1235
+ const STELLAR_SDK_POST_TIMEOUT_MS = 33_000;
1236
+ const AIOHTTP_DEFAULT_TOTAL_TIMEOUT_MS = 300_000;
1237
+ const AIOHTTP_MAX_REDIRECTS = 9;
1238
+ function applyAiohttpClientLimits(client) {
1239
+ client.interceptors.request.use((config) => {
1240
+ const total = String(config.method).toLowerCase() === 'post' ? STELLAR_SDK_POST_TIMEOUT_MS : STELLAR_SDK_GET_TIMEOUT_MS;
1241
+ return Object.assign(config, { timeout: total, signal: AbortSignal.timeout(total), proxy: false, maxRedirects: AIOHTTP_MAX_REDIRECTS });
1242
+ });
1243
+ }
1244
+ const GET_TRANSACTION_STATUSES = new Set(['SUCCESS', 'NOT_FOUND', 'FAILED']);
1245
+ const GET_TRANSACTION_REQUIRED_INTS = ['latestLedger', 'latestLedgerCloseTime', 'oldestLedger', 'oldestLedgerCloseTime'];
1246
+ const GET_TRANSACTION_OPTIONAL_INTS = ['applicationOrder', 'ledger', 'createdAt'];
1247
+ const GET_TRANSACTION_OPTIONAL_STRS = ['envelopeXdr', 'resultXdr', 'resultMetaXdr'];
1248
+ function isAbsent(value) {
1249
+ return value === undefined || value === null;
1250
+ }
1251
+ function isGetTransactionEvents(value) {
1252
+ if (isAbsent(value))
1253
+ return true;
1254
+ if (typeof value !== 'object' || Array.isArray(value))
1255
+ return false;
1256
+ const events = value;
1257
+ return ((isAbsent(events.diagnosticEventsXdr) || isPydanticStrList(events.diagnosticEventsXdr)) &&
1258
+ (isAbsent(events.transactionEventsXdr) || isPydanticStrList(events.transactionEventsXdr)) &&
1259
+ (isAbsent(events.contractEventsXdr) ||
1260
+ (Array.isArray(events.contractEventsXdr) && events.contractEventsXdr.every((perOperation) => isPydanticStrList(perOperation)))));
1261
+ }
1262
+ function isGetTransactionResponse(reply) {
1263
+ if (reply === null || typeof reply !== 'object' || Array.isArray(reply))
1264
+ return false;
1265
+ const fields = reply;
1266
+ return (GET_TRANSACTION_STATUSES.has(fields.status) &&
1267
+ typeof fields.txHash === 'string' &&
1268
+ GET_TRANSACTION_REQUIRED_INTS.every((name) => isPydanticLaxInt(fields[name])) &&
1269
+ GET_TRANSACTION_OPTIONAL_INTS.every((name) => isAbsent(fields[name]) || isPydanticLaxInt(fields[name])) &&
1270
+ (isAbsent(fields.feeBump) || isPydanticLaxBool(fields.feeBump)) &&
1271
+ GET_TRANSACTION_OPTIONAL_STRS.every((name) => isAbsent(fields[name]) || typeof fields[name] === 'string') &&
1272
+ isGetTransactionEvents(fields.events));
1273
+ }
1274
+ function firstHostFunctionResult(results) {
1275
+ if (results === null)
1276
+ throw new ChainError(ChainErrorKinds.SimulationFailed, "'NoneType' object is not subscriptable");
1277
+ if (results.length === 0)
1278
+ throw new ChainError(ChainErrorKinds.SimulationFailed, 'list index out of range');
1279
+ return xdr.ScVal.fromXDR(results[0].xdr, 'base64');
1280
+ }
1281
+ const MEMO_REQUIRED_OPERATION_TYPES = new Set(['payment', 'accountMerge', 'pathPaymentStrictSend', 'pathPaymentStrictReceive']);
1282
+ const MEMO_REQUIRED_CONFIG_KEY = 'config.memo_required';
1283
+ const MEMO_REQUIRED_CONFIG_VALUE = 'MQ==';
1284
+ function transactionXdrOperations(tx) {
1285
+ const envelope = tx.toEnvelope();
1286
+ return envelope.switch().name === 'envelopeTypeTxV0' ? envelope.v0().tx().operations() : envelope.v1().tx().operations();
1287
+ }
1288
+ function horizonSubmissionRejection(error) {
1289
+ const response = error.response;
1290
+ if (typeof response !== 'object' || response === null)
1291
+ return null;
1292
+ const problem = error instanceof BadResponseError
1293
+ ? response
1294
+ : response.data;
1295
+ const status = error instanceof BadResponseError ? problem?.status : response.status;
1296
+ if (status !== HORIZON_TRANSACTION_REJECTED_STATUS)
1297
+ return null;
1298
+ return typeof problem === 'object' && problem !== null ? problem : {};
1299
+ }
1300
+ function describeHorizonRejection(problem, error) {
1301
+ const resultCodes = problem.extras?.result_codes;
1302
+ return resultCodes !== undefined ? JSON.stringify(resultCodes) : error.message;
1303
+ }
1304
+ async function runBatch(items, fetchOne) {
1305
+ const results = new Array(items.length);
1306
+ let cursor = 0;
1307
+ let aborted = false;
1308
+ const spawn = async () => {
1309
+ while (!aborted) {
1310
+ const idx = cursor++;
1311
+ if (idx >= items.length)
1312
+ return;
1313
+ try {
1314
+ results[idx] = await fetchOne(items[idx]);
1315
+ }
1316
+ catch (err) {
1317
+ aborted = true;
1318
+ throw err;
1319
+ }
1320
+ }
1321
+ };
1322
+ await Promise.all(Array.from({ length: Math.min(8, items.length) }, spawn));
1323
+ return results;
1324
+ }
1325
+ //# sourceMappingURL=stellar_chain.js.map