@elizaos/plugin-steward-app 2.0.3-beta.5 → 2.0.3-beta.7

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 (219) hide show
  1. package/dist/ApprovalQueue.d.ts +18 -0
  2. package/dist/ApprovalQueue.d.ts.map +1 -0
  3. package/dist/ApprovalQueue.js +420 -0
  4. package/dist/ApprovalQueue.js.map +1 -0
  5. package/dist/StewardLogo.d.ts +11 -0
  6. package/dist/StewardLogo.d.ts.map +1 -0
  7. package/dist/StewardLogo.js +36 -0
  8. package/dist/StewardLogo.js.map +1 -0
  9. package/dist/StewardView.d.ts +13 -0
  10. package/dist/StewardView.d.ts.map +1 -0
  11. package/dist/StewardView.helpers.d.ts +15 -0
  12. package/dist/StewardView.helpers.d.ts.map +1 -0
  13. package/dist/StewardView.helpers.js +45 -0
  14. package/dist/StewardView.helpers.js.map +1 -0
  15. package/dist/StewardView.interact.d.ts +2 -0
  16. package/dist/StewardView.interact.d.ts.map +1 -0
  17. package/dist/StewardView.interact.js +54 -0
  18. package/dist/StewardView.interact.js.map +1 -0
  19. package/dist/StewardView.js +249 -0
  20. package/dist/StewardView.js.map +1 -0
  21. package/dist/TransactionHistory.d.ts +22 -0
  22. package/dist/TransactionHistory.d.ts.map +1 -0
  23. package/dist/TransactionHistory.js +361 -0
  24. package/dist/TransactionHistory.js.map +1 -0
  25. package/dist/__fixtures__/steward-sdk-fixtures.d.ts +10 -0
  26. package/dist/__fixtures__/steward-sdk-fixtures.d.ts.map +1 -0
  27. package/dist/__fixtures__/steward-sdk-fixtures.js +60 -0
  28. package/dist/__fixtures__/steward-sdk-fixtures.js.map +1 -0
  29. package/dist/actions/wallet-action-shared.d.ts +15 -0
  30. package/dist/actions/wallet-action-shared.d.ts.map +1 -0
  31. package/dist/actions/wallet-action-shared.js +16 -0
  32. package/dist/actions/wallet-action-shared.js.map +1 -0
  33. package/dist/api/binance-skill-helpers.d.ts +21 -0
  34. package/dist/api/binance-skill-helpers.d.ts.map +1 -0
  35. package/dist/api/binance-skill-helpers.js +790 -0
  36. package/dist/api/binance-skill-helpers.js.map +1 -0
  37. package/dist/api/bsc-trade.d.ts +36 -0
  38. package/dist/api/bsc-trade.d.ts.map +1 -0
  39. package/dist/api/bsc-trade.js +796 -0
  40. package/dist/api/bsc-trade.js.map +1 -0
  41. package/dist/api/trade-safety.d.ts +35 -0
  42. package/dist/api/trade-safety.d.ts.map +1 -0
  43. package/dist/api/trade-safety.js +56 -0
  44. package/dist/api/trade-safety.js.map +1 -0
  45. package/dist/api/tx-service.d.ts +53 -0
  46. package/dist/api/tx-service.d.ts.map +1 -0
  47. package/dist/api/tx-service.js +206 -0
  48. package/dist/api/tx-service.js.map +1 -0
  49. package/dist/api/wallet-bsc-routes.d.ts +63 -0
  50. package/dist/api/wallet-bsc-routes.d.ts.map +1 -0
  51. package/dist/api/wallet-bsc-routes.js +337 -0
  52. package/dist/api/wallet-bsc-routes.js.map +1 -0
  53. package/dist/api/wallet-capability.d.ts +2 -0
  54. package/dist/api/wallet-capability.d.ts.map +1 -0
  55. package/dist/api/wallet-capability.js +15 -0
  56. package/dist/api/wallet-capability.js.map +1 -0
  57. package/dist/api/wallet-dex-prices.d.ts +43 -0
  58. package/dist/api/wallet-dex-prices.d.ts.map +1 -0
  59. package/dist/api/wallet-dex-prices.js +132 -0
  60. package/dist/api/wallet-dex-prices.js.map +1 -0
  61. package/dist/api/wallet-evm-balance.d.ts +72 -0
  62. package/dist/api/wallet-evm-balance.d.ts.map +1 -0
  63. package/dist/api/wallet-evm-balance.js +697 -0
  64. package/dist/api/wallet-evm-balance.js.map +1 -0
  65. package/dist/api/wallet-routes.d.ts +27 -0
  66. package/dist/api/wallet-routes.d.ts.map +1 -0
  67. package/dist/api/wallet-routes.js +556 -0
  68. package/dist/api/wallet-routes.js.map +1 -0
  69. package/dist/api/wallet-rpc.d.ts +73 -0
  70. package/dist/api/wallet-rpc.d.ts.map +1 -0
  71. package/dist/api/wallet-rpc.js +460 -0
  72. package/dist/api/wallet-rpc.js.map +1 -0
  73. package/dist/api/wallet-trade-routes.d.ts +104 -0
  74. package/dist/api/wallet-trade-routes.d.ts.map +1 -0
  75. package/dist/api/wallet-trade-routes.js +353 -0
  76. package/dist/api/wallet-trade-routes.js.map +1 -0
  77. package/dist/api/wallet-trading-profile.d.ts +31 -0
  78. package/dist/api/wallet-trading-profile.d.ts.map +1 -0
  79. package/dist/api/wallet-trading-profile.js +500 -0
  80. package/dist/api/wallet-trading-profile.js.map +1 -0
  81. package/dist/api/wallet.d.ts +60 -0
  82. package/dist/api/wallet.d.ts.map +1 -0
  83. package/dist/api/wallet.js +617 -0
  84. package/dist/api/wallet.js.map +1 -0
  85. package/dist/chain-utils.d.ts +10 -0
  86. package/dist/chain-utils.d.ts.map +1 -0
  87. package/dist/chain-utils.js +81 -0
  88. package/dist/chain-utils.js.map +1 -0
  89. package/dist/components/StewardSpatialView.d.ts +74 -0
  90. package/dist/components/StewardSpatialView.d.ts.map +1 -0
  91. package/dist/components/StewardSpatialView.js +309 -0
  92. package/dist/components/StewardSpatialView.js.map +1 -0
  93. package/dist/index.d.ts +20 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +77 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/plugin.d.ts +21 -0
  98. package/dist/plugin.d.ts.map +1 -0
  99. package/dist/plugin.js +319 -0
  100. package/dist/plugin.js.map +1 -0
  101. package/dist/providers/steward-balance.d.ts +12 -0
  102. package/dist/providers/steward-balance.d.ts.map +1 -0
  103. package/dist/providers/steward-balance.js +85 -0
  104. package/dist/providers/steward-balance.js.map +1 -0
  105. package/dist/providers/steward-receive-address.d.ts +12 -0
  106. package/dist/providers/steward-receive-address.d.ts.map +1 -0
  107. package/dist/providers/steward-receive-address.js +47 -0
  108. package/dist/providers/steward-receive-address.js.map +1 -0
  109. package/dist/register-routes.d.ts +2 -0
  110. package/dist/register-routes.d.ts.map +1 -0
  111. package/dist/register-routes.js +6 -0
  112. package/dist/register-routes.js.map +1 -0
  113. package/dist/register-terminal-view.d.ts +15 -0
  114. package/dist/register-terminal-view.d.ts.map +1 -0
  115. package/dist/register-terminal-view.js +34 -0
  116. package/dist/register-terminal-view.js.map +1 -0
  117. package/dist/routes/steward-bridge.d.ts +202 -0
  118. package/dist/routes/steward-bridge.d.ts.map +1 -0
  119. package/dist/routes/steward-bridge.js +776 -0
  120. package/dist/routes/steward-bridge.js.map +1 -0
  121. package/dist/routes/steward-compat-routes.d.ts +21 -0
  122. package/dist/routes/steward-compat-routes.d.ts.map +1 -0
  123. package/dist/routes/steward-compat-routes.js +350 -0
  124. package/dist/routes/steward-compat-routes.js.map +1 -0
  125. package/dist/routes/wallet-browser-compat-routes.d.ts +6 -0
  126. package/dist/routes/wallet-browser-compat-routes.d.ts.map +1 -0
  127. package/dist/routes/wallet-browser-compat-routes.js +402 -0
  128. package/dist/routes/wallet-browser-compat-routes.js.map +1 -0
  129. package/dist/routes/wallet-bsc-core-routes.d.ts +15 -0
  130. package/dist/routes/wallet-bsc-core-routes.d.ts.map +1 -0
  131. package/dist/routes/wallet-bsc-core-routes.js +59 -0
  132. package/dist/routes/wallet-bsc-core-routes.js.map +1 -0
  133. package/dist/routes/wallet-compat-routes.d.ts +13 -0
  134. package/dist/routes/wallet-compat-routes.d.ts.map +1 -0
  135. package/dist/routes/wallet-compat-routes.js +206 -0
  136. package/dist/routes/wallet-compat-routes.js.map +1 -0
  137. package/dist/routes/wallet-core-routes.d.ts +16 -0
  138. package/dist/routes/wallet-core-routes.d.ts.map +1 -0
  139. package/dist/routes/wallet-core-routes.js +48 -0
  140. package/dist/routes/wallet-core-routes.js.map +1 -0
  141. package/dist/routes/wallet-trade-compat-routes.d.ts +11 -0
  142. package/dist/routes/wallet-trade-compat-routes.d.ts.map +1 -0
  143. package/dist/routes/wallet-trade-compat-routes.js +570 -0
  144. package/dist/routes/wallet-trade-compat-routes.js.map +1 -0
  145. package/dist/security/hydrate-wallet-keys-from-platform-store.d.ts +7 -0
  146. package/dist/security/hydrate-wallet-keys-from-platform-store.d.ts.map +1 -0
  147. package/dist/security/hydrate-wallet-keys-from-platform-store.js +43 -0
  148. package/dist/security/hydrate-wallet-keys-from-platform-store.js.map +1 -0
  149. package/dist/security/wallet-os-store-actions.d.ts +14 -0
  150. package/dist/security/wallet-os-store-actions.d.ts.map +1 -0
  151. package/dist/security/wallet-os-store-actions.js +63 -0
  152. package/dist/security/wallet-os-store-actions.js.map +1 -0
  153. package/dist/services/steward-credentials.d.ts +2 -0
  154. package/dist/services/steward-credentials.d.ts.map +1 -0
  155. package/dist/services/steward-credentials.js +2 -0
  156. package/dist/services/steward-credentials.js.map +1 -0
  157. package/dist/services/steward-evm-account.d.ts +75 -0
  158. package/dist/services/steward-evm-account.d.ts.map +1 -0
  159. package/dist/services/steward-evm-account.js +279 -0
  160. package/dist/services/steward-evm-account.js.map +1 -0
  161. package/dist/services/steward-evm-bridge.d.ts +36 -0
  162. package/dist/services/steward-evm-bridge.d.ts.map +1 -0
  163. package/dist/services/steward-evm-bridge.js +78 -0
  164. package/dist/services/steward-evm-bridge.js.map +1 -0
  165. package/dist/services/steward-sidecar/health-check.d.ts +2 -0
  166. package/dist/services/steward-sidecar/health-check.d.ts.map +1 -0
  167. package/dist/services/steward-sidecar/health-check.js +2 -0
  168. package/dist/services/steward-sidecar/health-check.js.map +1 -0
  169. package/dist/services/steward-sidecar/helpers.d.ts +2 -0
  170. package/dist/services/steward-sidecar/helpers.d.ts.map +1 -0
  171. package/dist/services/steward-sidecar/helpers.js +2 -0
  172. package/dist/services/steward-sidecar/helpers.js.map +1 -0
  173. package/dist/services/steward-sidecar/process-management.d.ts +2 -0
  174. package/dist/services/steward-sidecar/process-management.d.ts.map +1 -0
  175. package/dist/services/steward-sidecar/process-management.js +2 -0
  176. package/dist/services/steward-sidecar/process-management.js.map +1 -0
  177. package/dist/services/steward-sidecar/types.d.ts +2 -0
  178. package/dist/services/steward-sidecar/types.d.ts.map +1 -0
  179. package/dist/services/steward-sidecar/types.js +2 -0
  180. package/dist/services/steward-sidecar/types.js.map +1 -0
  181. package/dist/services/steward-sidecar/wallet-setup.d.ts +2 -0
  182. package/dist/services/steward-sidecar/wallet-setup.d.ts.map +1 -0
  183. package/dist/services/steward-sidecar/wallet-setup.js +2 -0
  184. package/dist/services/steward-sidecar/wallet-setup.js.map +1 -0
  185. package/dist/services/steward-sidecar.d.ts +2 -0
  186. package/dist/services/steward-sidecar.d.ts.map +1 -0
  187. package/dist/services/steward-sidecar.js +2 -0
  188. package/dist/services/steward-sidecar.js.map +1 -0
  189. package/dist/services/steward-wallet.d.ts +25 -0
  190. package/dist/services/steward-wallet.d.ts.map +1 -0
  191. package/dist/services/steward-wallet.js +333 -0
  192. package/dist/services/steward-wallet.js.map +1 -0
  193. package/dist/steward-ui-state.d.ts +14 -0
  194. package/dist/steward-ui-state.d.ts.map +1 -0
  195. package/dist/steward-ui-state.js +46 -0
  196. package/dist/steward-ui-state.js.map +1 -0
  197. package/dist/steward-view-bundle.d.ts +3 -0
  198. package/dist/steward-view-bundle.d.ts.map +1 -0
  199. package/dist/steward-view-bundle.js +7 -0
  200. package/dist/steward-view-bundle.js.map +1 -0
  201. package/dist/types/bsc-trade.d.ts +180 -0
  202. package/dist/types/bsc-trade.d.ts.map +1 -0
  203. package/dist/types/bsc-trade.js +1 -0
  204. package/dist/types/bsc-trade.js.map +1 -0
  205. package/dist/types/index.d.ts +3 -0
  206. package/dist/types/index.d.ts.map +1 -0
  207. package/dist/types/index.js +3 -0
  208. package/dist/types/index.js.map +1 -0
  209. package/dist/types/steward.d.ts +83 -0
  210. package/dist/types/steward.d.ts.map +1 -0
  211. package/dist/types/steward.js +1 -0
  212. package/dist/types/steward.js.map +1 -0
  213. package/dist/ui.d.ts +7 -0
  214. package/dist/ui.d.ts.map +1 -0
  215. package/dist/ui.js +7 -0
  216. package/dist/ui.js.map +1 -0
  217. package/dist/views/bundle.js +601 -0
  218. package/dist/views/bundle.js.map +1 -0
  219. package/package.json +8 -8
@@ -0,0 +1,796 @@
1
+ import { logger } from "@elizaos/core";
2
+ import * as ethers from "ethers";
3
+ import {
4
+ normalizeRpcUrl,
5
+ resolveBscRpcUrls as resolveWalletBscRpcUrls,
6
+ resolveWalletNetworkMode
7
+ } from "./wallet-rpc.js";
8
+ const FETCH_TIMEOUT_MS = 15e3;
9
+ const BSC_MAINNET_CHAIN_ID = 56;
10
+ const BSC_TESTNET_CHAIN_ID = 97;
11
+ const MIN_GAS_BNB = "0.005";
12
+ const DEFAULT_SLIPPAGE_BPS = 300;
13
+ const MAX_SLIPPAGE_BPS = 1e3;
14
+ const SLIPPAGE_WARNING_THRESHOLD_BPS = 300;
15
+ const ZEROX_API_BASE_URL = "https://bsc.api.0x.org";
16
+ const ZEROX_QUOTE_TIMEOUT_MS = 8e3;
17
+ const PANCAKE_SWAP_V2_ROUTER = ethers.getAddress(
18
+ "0x10ED43C718714eb63d5aA57B78B54704E256024E"
19
+ );
20
+ const BSC_WBNB_FALLBACK = ethers.getAddress(
21
+ "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
22
+ );
23
+ const BSC_TESTNET_EXPLORER_BASE_URL = "https://testnet.bscscan.com";
24
+ const ROUTER_IFACE = new ethers.Interface([
25
+ "function WETH() view returns (address)",
26
+ "function getAmountsOut(uint256 amountIn, address[] calldata path) view returns (uint256[] memory amounts)",
27
+ "function swapExactETHForTokensSupportingFeeOnTransferTokens(uint amountOutMin, address[] path, address to, uint deadline)",
28
+ "function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] path, address to, uint deadline)"
29
+ ]);
30
+ const ERC20_IFACE = new ethers.Interface([
31
+ "function symbol() view returns (string)",
32
+ "function decimals() view returns (uint8)",
33
+ "function balanceOf(address owner) view returns (uint256)",
34
+ "function approve(address spender, uint256 amount) returns (bool)"
35
+ ]);
36
+ function resolveBscExecutionContext() {
37
+ const walletNetwork = resolveWalletNetworkMode();
38
+ if (walletNetwork === "mainnet") {
39
+ return {
40
+ walletNetwork,
41
+ chainId: BSC_MAINNET_CHAIN_ID,
42
+ routerAddress: PANCAKE_SWAP_V2_ROUTER,
43
+ wrappedNativeFallback: BSC_WBNB_FALLBACK,
44
+ explorerBaseUrl: "https://bscscan.com"
45
+ };
46
+ }
47
+ const configuredChainIdRaw = process.env.BSC_TESTNET_CHAIN_ID?.trim();
48
+ const configuredChainId = configuredChainIdRaw ? Number.parseInt(configuredChainIdRaw, 10) : BSC_TESTNET_CHAIN_ID;
49
+ const chainId = Number.isFinite(configuredChainId) && configuredChainId > 0 ? configuredChainId : BSC_TESTNET_CHAIN_ID;
50
+ const routerAddress = normalizeAddress(
51
+ process.env.BSC_TESTNET_SWAP_ROUTER_ADDRESS ?? process.env.BSC_SWAP_ROUTER_ADDRESS
52
+ );
53
+ if (!routerAddress) {
54
+ throw new Error(
55
+ "BSC testnet router not configured. Set BSC_TESTNET_SWAP_ROUTER_ADDRESS."
56
+ );
57
+ }
58
+ const wrappedNativeFallback = normalizeAddress(
59
+ process.env.BSC_TESTNET_WRAPPED_NATIVE_ADDRESS ?? process.env.BSC_WRAPPED_NATIVE_ADDRESS
60
+ );
61
+ const explorerBaseUrlRaw = process.env.BSC_TESTNET_EXPLORER_BASE_URL ?? BSC_TESTNET_EXPLORER_BASE_URL;
62
+ const explorerBaseUrl = (() => {
63
+ try {
64
+ const parsed = new URL(explorerBaseUrlRaw);
65
+ return parsed.toString().replace(/\/+$/, "");
66
+ } catch {
67
+ return BSC_TESTNET_EXPLORER_BASE_URL;
68
+ }
69
+ })();
70
+ return {
71
+ walletNetwork,
72
+ chainId,
73
+ routerAddress,
74
+ wrappedNativeFallback,
75
+ explorerBaseUrl
76
+ };
77
+ }
78
+ function resolveBscRpcUrls(input) {
79
+ const walletNetwork = resolveWalletNetworkMode();
80
+ const candidates = [
81
+ ...(input.rpcUrls ?? []).map((url) => normalizeRpcUrl(url)),
82
+ normalizeRpcUrl(process.env.BSC_TESTNET_RPC_URL),
83
+ normalizeRpcUrl(
84
+ input.nodeRealBscRpcUrl !== void 0 ? input.nodeRealBscRpcUrl : process.env.NODEREAL_BSC_RPC_URL
85
+ ),
86
+ normalizeRpcUrl(
87
+ input.quickNodeBscRpcUrl !== void 0 ? input.quickNodeBscRpcUrl : process.env.QUICKNODE_BSC_RPC_URL
88
+ ),
89
+ // Standard plugin env key used across elizaOS EVM tooling.
90
+ normalizeRpcUrl(
91
+ input.bscRpcUrl !== void 0 ? input.bscRpcUrl : process.env.BSC_RPC_URL
92
+ ),
93
+ ...resolveWalletBscRpcUrls({
94
+ cloudManagedAccess: input.cloudManagedAccess,
95
+ walletNetwork
96
+ })
97
+ ].filter((v) => Boolean(v));
98
+ return [...new Set(candidates)];
99
+ }
100
+ function resolvePrimaryBscRpcUrl(input) {
101
+ const urls = resolveBscRpcUrls(input);
102
+ if (urls.length === 0) return null;
103
+ const primary = urls[0];
104
+ try {
105
+ const parsed = new URL(primary);
106
+ if (parsed.protocol === "http:") {
107
+ logger.warn(
108
+ `BSC RPC URL uses http: (${parsed.host}) \u2014 MITM risk for trade execution. Use https: in production.`
109
+ );
110
+ }
111
+ } catch {
112
+ }
113
+ return primary;
114
+ }
115
+ function hostLabel(url) {
116
+ try {
117
+ return new URL(url).host;
118
+ } catch {
119
+ return "rpc";
120
+ }
121
+ }
122
+ function normalizeAddress(value) {
123
+ if (typeof value !== "string") return null;
124
+ const trimmed = value.trim();
125
+ if (!trimmed) return null;
126
+ try {
127
+ return ethers.getAddress(trimmed);
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ function parseRpcChainId(value) {
133
+ if (!value || typeof value !== "string") return null;
134
+ if (!value.startsWith("0x")) return null;
135
+ const parsed = Number.parseInt(value, 16);
136
+ return Number.isFinite(parsed) ? parsed : null;
137
+ }
138
+ function clampSlippageBps(value) {
139
+ if (typeof value !== "number" || !Number.isFinite(value)) {
140
+ return DEFAULT_SLIPPAGE_BPS;
141
+ }
142
+ const rounded = Math.round(value);
143
+ if (rounded < 1) return 1;
144
+ if (rounded > MAX_SLIPPAGE_BPS) {
145
+ console.warn(
146
+ `[bsc-trade] Slippage ${rounded} bps exceeds max ${MAX_SLIPPAGE_BPS} bps, clamping`
147
+ );
148
+ return MAX_SLIPPAGE_BPS;
149
+ }
150
+ if (rounded > SLIPPAGE_WARNING_THRESHOLD_BPS) {
151
+ console.warn(
152
+ `[bsc-trade] High slippage requested: ${rounded} bps (${(rounded / 100).toFixed(1)}%)`
153
+ );
154
+ }
155
+ return rounded;
156
+ }
157
+ function clampDeadlineSeconds(value) {
158
+ if (typeof value !== "number" || !Number.isFinite(value)) return 600;
159
+ const rounded = Math.round(value);
160
+ if (rounded < 60) return 60;
161
+ if (rounded > 3600) return 3600;
162
+ return rounded;
163
+ }
164
+ function parsePositiveDecimal(value) {
165
+ const amount = Number.parseFloat(value);
166
+ if (!Number.isFinite(amount) || amount <= 0) {
167
+ throw new Error("Amount must be a positive number.");
168
+ }
169
+ return amount;
170
+ }
171
+ function resolveRouteProviderPreference(value) {
172
+ if (value === "pancakeswap-v2" || value === "0x" || value === "auto") {
173
+ return value;
174
+ }
175
+ return "auto";
176
+ }
177
+ function resolveRouteProviderOrder(requested) {
178
+ if (requested === "0x") {
179
+ return ["0x", "pancakeswap-v2"];
180
+ }
181
+ if (requested === "pancakeswap-v2") {
182
+ return ["pancakeswap-v2"];
183
+ }
184
+ return ["0x", "pancakeswap-v2"];
185
+ }
186
+ function formatPrice(amountIn, amountOut) {
187
+ const inNum = Number.parseFloat(amountIn);
188
+ const outNum = Number.parseFloat(amountOut);
189
+ if (!Number.isFinite(inNum) || !Number.isFinite(outNum) || inNum <= 0) {
190
+ return "0";
191
+ }
192
+ const price = outNum / inNum;
193
+ if (price >= 1e3) return price.toFixed(2);
194
+ if (price >= 1) return price.toFixed(4);
195
+ if (price >= 1e-3) return price.toFixed(6);
196
+ return price.toExponential(4);
197
+ }
198
+ async function rpcCallWithFallback(rpcUrls, method, params) {
199
+ if (rpcUrls.length === 0) {
200
+ throw new Error("No BSC RPC endpoints configured.");
201
+ }
202
+ const payload = JSON.stringify({
203
+ jsonrpc: "2.0",
204
+ id: 1,
205
+ method,
206
+ params
207
+ });
208
+ let lastError = "Unknown RPC error";
209
+ for (const rpcUrl of rpcUrls) {
210
+ try {
211
+ const response = await fetch(rpcUrl, {
212
+ method: "POST",
213
+ headers: { "Content-Type": "application/json" },
214
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
215
+ body: payload
216
+ });
217
+ const raw = await response.text();
218
+ if (!response.ok) {
219
+ throw new Error(`HTTP ${response.status}: ${raw.slice(0, 180)}`);
220
+ }
221
+ let parsed;
222
+ try {
223
+ parsed = JSON.parse(raw);
224
+ } catch {
225
+ throw new Error(`Invalid JSON response: ${raw.slice(0, 180)}`);
226
+ }
227
+ if (parsed.error) {
228
+ throw new Error(
229
+ parsed.error.message ?? `RPC error ${parsed.error.code}`
230
+ );
231
+ }
232
+ if (parsed.result === void 0 || parsed.result === null) {
233
+ throw new Error("RPC returned empty result.");
234
+ }
235
+ return { result: parsed.result, rpcUrl };
236
+ } catch (err) {
237
+ const message = err instanceof Error ? err.message : String(err);
238
+ lastError = `${hostLabel(rpcUrl)}: ${message}`;
239
+ }
240
+ }
241
+ throw new Error(lastError);
242
+ }
243
+ async function ethCall(rpcUrls, to, data) {
244
+ return rpcCallWithFallback(rpcUrls, "eth_call", [
245
+ { to, data },
246
+ "latest"
247
+ ]);
248
+ }
249
+ async function readWrappedNativeAddress(rpcUrls, context) {
250
+ const encoded = ROUTER_IFACE.encodeFunctionData("WETH", []);
251
+ try {
252
+ const call = await ethCall(rpcUrls, context.routerAddress, encoded);
253
+ const decoded = ROUTER_IFACE.decodeFunctionResult("WETH", call.result);
254
+ const wrappedNative = decoded[0];
255
+ if (typeof wrappedNative !== "string" || !wrappedNative) {
256
+ throw new Error("Router WETH() returned an invalid address.");
257
+ }
258
+ return ethers.getAddress(wrappedNative);
259
+ } catch (err) {
260
+ if (context.wrappedNativeFallback) {
261
+ return context.wrappedNativeFallback;
262
+ }
263
+ throw err;
264
+ }
265
+ }
266
+ async function readTokenDecimals(rpcUrls, tokenAddress) {
267
+ const encoded = ERC20_IFACE.encodeFunctionData("decimals", []);
268
+ const call = await ethCall(rpcUrls, tokenAddress, encoded);
269
+ const decoded = ERC20_IFACE.decodeFunctionResult("decimals", call.result);
270
+ const decimals = decoded[0];
271
+ if (typeof decimals !== "bigint") return 18;
272
+ const parsed = Number(decimals);
273
+ if (!Number.isFinite(parsed) || parsed < 0) return 18;
274
+ return parsed;
275
+ }
276
+ async function readTokenSymbol(rpcUrls, tokenAddress) {
277
+ const encoded = ERC20_IFACE.encodeFunctionData("symbol", []);
278
+ const call = await ethCall(rpcUrls, tokenAddress, encoded);
279
+ const decoded = ERC20_IFACE.decodeFunctionResult("symbol", call.result);
280
+ const symbol = decoded[0];
281
+ if (typeof symbol === "string" && symbol.trim()) {
282
+ return symbol.trim().slice(0, 16);
283
+ }
284
+ return `TKN-${tokenAddress.slice(2, 6).toUpperCase()}`;
285
+ }
286
+ async function readTokenBalanceWei(rpcUrls, tokenAddress, walletAddress) {
287
+ const encoded = ERC20_IFACE.encodeFunctionData("balanceOf", [walletAddress]);
288
+ const call = await ethCall(rpcUrls, tokenAddress, encoded);
289
+ const decoded = ERC20_IFACE.decodeFunctionResult("balanceOf", call.result);
290
+ const balance = decoded[0];
291
+ if (typeof balance !== "bigint") {
292
+ throw new Error("Token balance response is invalid.");
293
+ }
294
+ return balance;
295
+ }
296
+ async function fetchZeroXQuote(input) {
297
+ const apiBase = normalizeRpcUrl(process.env.ZEROX_BSC_API_BASE_URL) ? normalizeRpcUrl(process.env.ZEROX_BSC_API_BASE_URL) : ZEROX_API_BASE_URL;
298
+ const sellToken = input.side === "buy" ? input.wrappedNativeAddress : input.tokenAddress;
299
+ const buyToken = input.side === "buy" ? input.tokenAddress : input.wrappedNativeAddress;
300
+ const quoteUrl = new URL("/swap/v1/quote", apiBase);
301
+ quoteUrl.searchParams.set("sellToken", sellToken);
302
+ quoteUrl.searchParams.set("buyToken", buyToken);
303
+ quoteUrl.searchParams.set("sellAmount", input.amountInWei.toString());
304
+ quoteUrl.searchParams.set(
305
+ "slippagePercentage",
306
+ (input.slippageBps / 1e4).toString()
307
+ );
308
+ quoteUrl.searchParams.set("takerAddress", input.walletAddress);
309
+ const apiKey = process.env.ZEROX_API_KEY?.trim();
310
+ const response = await fetch(quoteUrl.toString(), {
311
+ method: "GET",
312
+ headers: apiKey ? { "0x-api-key": apiKey } : void 0,
313
+ signal: AbortSignal.timeout(ZEROX_QUOTE_TIMEOUT_MS)
314
+ });
315
+ const raw = await response.text();
316
+ if (!response.ok) {
317
+ throw new Error(`0x HTTP ${response.status}: ${raw.slice(0, 180)}`);
318
+ }
319
+ let parsed;
320
+ try {
321
+ parsed = JSON.parse(raw);
322
+ } catch {
323
+ throw new Error(`0x quote returned invalid JSON: ${raw.slice(0, 180)}`);
324
+ }
325
+ const to = normalizeAddress(parsed.to);
326
+ const data = typeof parsed.data === "string" && parsed.data.startsWith("0x") ? parsed.data : null;
327
+ const buyAmount = typeof parsed.buyAmount === "string" && /^\d+$/.test(parsed.buyAmount) ? BigInt(parsed.buyAmount) : null;
328
+ const sellAmount = typeof parsed.sellAmount === "string" && /^\d+$/.test(parsed.sellAmount) ? BigInt(parsed.sellAmount) : null;
329
+ const value = typeof parsed.value === "string" && /^\d+$/.test(parsed.value) ? parsed.value : "0";
330
+ if (!to || !data || !buyAmount || !sellAmount) {
331
+ throw new Error("0x quote missing required transaction fields.");
332
+ }
333
+ return {
334
+ to,
335
+ data,
336
+ value,
337
+ allowanceTarget: normalizeAddress(parsed.allowanceTarget ?? null) ?? void 0,
338
+ buyAmount: buyAmount.toString(),
339
+ sellAmount: sellAmount.toString()
340
+ };
341
+ }
342
+ async function buildBscTradePreflight(input) {
343
+ const context = resolveBscExecutionContext();
344
+ const checks = {
345
+ walletReady: false,
346
+ rpcReady: false,
347
+ chainReady: false,
348
+ gasReady: false,
349
+ tokenAddressValid: true
350
+ };
351
+ const reasons = [];
352
+ const walletAddress = normalizeAddress(input.walletAddress);
353
+ const tokenAddressRaw = (input.tokenAddress ?? "").trim();
354
+ const tokenAddress = tokenAddressRaw ? normalizeAddress(tokenAddressRaw) : null;
355
+ const rpcUrls = resolveBscRpcUrls(input);
356
+ let chainId = null;
357
+ let bnbBalance = null;
358
+ let activeRpcUrl = null;
359
+ checks.walletReady = Boolean(walletAddress);
360
+ if (!checks.walletReady) {
361
+ reasons.push("Wallet not ready. Create or connect an EVM wallet first.");
362
+ }
363
+ if (tokenAddressRaw && !tokenAddress) {
364
+ checks.tokenAddressValid = false;
365
+ reasons.push("Token address format is invalid.");
366
+ }
367
+ if (rpcUrls.length === 0) {
368
+ reasons.push(
369
+ "BSC RPC not configured. Connect Eliza Cloud or set NODEREAL_BSC_RPC_URL, QUICKNODE_BSC_RPC_URL, or BSC_RPC_URL."
370
+ );
371
+ } else {
372
+ try {
373
+ const chainResponse = await rpcCallWithFallback(
374
+ rpcUrls,
375
+ "eth_chainId",
376
+ []
377
+ );
378
+ activeRpcUrl = chainResponse.rpcUrl;
379
+ checks.rpcReady = true;
380
+ chainId = parseRpcChainId(chainResponse.result);
381
+ checks.chainReady = chainId === context.chainId;
382
+ if (!checks.chainReady) {
383
+ reasons.push(
384
+ chainId === null ? "Unable to read chain id from RPC." : `RPC chain mismatch. Expected BSC (${context.chainId}), got ${chainId}.`
385
+ );
386
+ }
387
+ } catch (err) {
388
+ reasons.push(
389
+ `BSC RPC unavailable: ${err instanceof Error ? err.message : String(err)}`
390
+ );
391
+ }
392
+ }
393
+ if (checks.walletReady && checks.rpcReady) {
394
+ try {
395
+ const rpcCandidates = activeRpcUrl ? [activeRpcUrl, ...rpcUrls.filter((url) => url !== activeRpcUrl)] : rpcUrls;
396
+ const balanceResponse = await rpcCallWithFallback(
397
+ rpcCandidates,
398
+ "eth_getBalance",
399
+ [walletAddress, "latest"]
400
+ );
401
+ if (!activeRpcUrl) activeRpcUrl = balanceResponse.rpcUrl;
402
+ const balanceWei = BigInt(balanceResponse.result);
403
+ bnbBalance = ethers.formatEther(balanceWei);
404
+ checks.gasReady = balanceWei >= ethers.parseEther(MIN_GAS_BNB);
405
+ if (!checks.gasReady) {
406
+ reasons.push(
407
+ `Insufficient BNB gas. Keep at least ${MIN_GAS_BNB} BNB available.`
408
+ );
409
+ }
410
+ } catch (err) {
411
+ reasons.push(
412
+ `Failed to read wallet balance: ${err instanceof Error ? err.message : String(err)}`
413
+ );
414
+ }
415
+ }
416
+ if (tokenAddressRaw && tokenAddress && checks.rpcReady && checks.chainReady) {
417
+ try {
418
+ const rpcCandidates = activeRpcUrl ? [activeRpcUrl, ...rpcUrls.filter((url) => url !== activeRpcUrl)] : rpcUrls;
419
+ const codeResponse = await rpcCallWithFallback(
420
+ rpcCandidates,
421
+ "eth_getCode",
422
+ [tokenAddress, "latest"]
423
+ );
424
+ if (!activeRpcUrl) activeRpcUrl = codeResponse.rpcUrl;
425
+ const code = codeResponse.result.trim().toLowerCase();
426
+ if (code === "0x" || code === "0x0") {
427
+ checks.tokenAddressValid = false;
428
+ reasons.push("Token contract not found on BSC.");
429
+ }
430
+ } catch (err) {
431
+ checks.tokenAddressValid = false;
432
+ reasons.push(
433
+ `Token contract check failed: ${err instanceof Error ? err.message : String(err)}`
434
+ );
435
+ }
436
+ }
437
+ const ok = checks.walletReady && checks.rpcReady && checks.chainReady && checks.gasReady && checks.tokenAddressValid;
438
+ return {
439
+ ok,
440
+ walletAddress,
441
+ rpcUrlHost: activeRpcUrl ? hostLabel(activeRpcUrl) : null,
442
+ chainId,
443
+ bnbBalance,
444
+ minGasBnb: MIN_GAS_BNB,
445
+ checks,
446
+ reasons
447
+ };
448
+ }
449
+ async function buildBscTradeQuote(input) {
450
+ const context = resolveBscExecutionContext();
451
+ const side = input.request.side;
452
+ if (side !== "buy" && side !== "sell") {
453
+ throw new Error('Unsupported trade side. Use "buy" or "sell".');
454
+ }
455
+ const tokenAddress = normalizeAddress(input.request.tokenAddress);
456
+ if (!tokenAddress) {
457
+ throw new Error("Token address is required.");
458
+ }
459
+ const amountInput = input.request.amount.trim();
460
+ parsePositiveDecimal(amountInput);
461
+ const slippageBps = clampSlippageBps(input.request.slippageBps);
462
+ const routeProviderRequested = resolveRouteProviderPreference(
463
+ input.request.routeProvider
464
+ );
465
+ const preflight = await buildBscTradePreflight({
466
+ walletAddress: input.walletAddress,
467
+ tokenAddress,
468
+ rpcUrls: input.rpcUrls,
469
+ nodeRealBscRpcUrl: input.nodeRealBscRpcUrl,
470
+ quickNodeBscRpcUrl: input.quickNodeBscRpcUrl,
471
+ bscRpcUrl: input.bscRpcUrl,
472
+ cloudManagedAccess: input.cloudManagedAccess
473
+ });
474
+ if (!preflight.ok) {
475
+ throw new Error(preflight.reasons[0] ?? "Trade preflight failed.");
476
+ }
477
+ const rpcUrls = resolveBscRpcUrls(input);
478
+ if (rpcUrls.length === 0) {
479
+ throw new Error("BSC RPC unavailable.");
480
+ }
481
+ const wrappedNativeAddress = await readWrappedNativeAddress(rpcUrls, context);
482
+ const tokenDecimals = await readTokenDecimals(rpcUrls, tokenAddress);
483
+ const tokenSymbol = await readTokenSymbol(rpcUrls, tokenAddress);
484
+ const amountInWei = side === "buy" ? ethers.parseEther(amountInput) : ethers.parseUnits(amountInput, tokenDecimals);
485
+ if (side === "sell") {
486
+ if (!preflight.walletAddress) {
487
+ throw new Error("Wallet not ready for sell quote.");
488
+ }
489
+ const tokenBalanceWei = await readTokenBalanceWei(
490
+ rpcUrls,
491
+ tokenAddress,
492
+ preflight.walletAddress
493
+ );
494
+ if (amountInWei > tokenBalanceWei) {
495
+ throw new Error("Insufficient token balance for sell amount.");
496
+ }
497
+ }
498
+ if (side === "buy" && preflight.bnbBalance) {
499
+ const walletBalanceWei = ethers.parseEther(preflight.bnbBalance);
500
+ const gasReserveWei = ethers.parseEther(MIN_GAS_BNB);
501
+ if (amountInWei + gasReserveWei > walletBalanceWei) {
502
+ throw new Error(
503
+ `Insufficient BNB for amount + gas reserve (${MIN_GAS_BNB} BNB).`
504
+ );
505
+ }
506
+ }
507
+ let routeProvider = "pancakeswap-v2";
508
+ let routeProviderFallbackUsed = false;
509
+ const routeProviderNotes = [];
510
+ let amountOutWei = null;
511
+ let route = [];
512
+ let swapTargetAddress;
513
+ let swapCallData;
514
+ let swapValueWei;
515
+ let allowanceTarget;
516
+ const providerErrors = [];
517
+ for (const provider of resolveRouteProviderOrder(routeProviderRequested)) {
518
+ try {
519
+ if (provider === "0x") {
520
+ if (!preflight.walletAddress) {
521
+ throw new Error("wallet address is required for 0x route");
522
+ }
523
+ const zeroX = await fetchZeroXQuote({
524
+ side,
525
+ walletAddress: preflight.walletAddress,
526
+ tokenAddress,
527
+ wrappedNativeAddress,
528
+ amountInWei,
529
+ slippageBps
530
+ });
531
+ const buyAmount = BigInt(zeroX.buyAmount ?? "0");
532
+ if (buyAmount <= 0n) {
533
+ throw new Error("0x quote returned zero output amount");
534
+ }
535
+ amountOutWei = buyAmount;
536
+ routeProvider = "0x";
537
+ route = side === "buy" ? [wrappedNativeAddress, tokenAddress] : [tokenAddress, wrappedNativeAddress];
538
+ swapTargetAddress = zeroX.to;
539
+ swapCallData = zeroX.data;
540
+ swapValueWei = zeroX.value ?? "0";
541
+ allowanceTarget = zeroX.allowanceTarget;
542
+ break;
543
+ }
544
+ const candidateRoute = side === "buy" ? [wrappedNativeAddress, tokenAddress] : [tokenAddress, wrappedNativeAddress];
545
+ const quoteCall = ROUTER_IFACE.encodeFunctionData("getAmountsOut", [
546
+ amountInWei,
547
+ candidateRoute
548
+ ]);
549
+ const quoteResponse = await ethCall(
550
+ rpcUrls,
551
+ context.routerAddress,
552
+ quoteCall
553
+ );
554
+ const decoded = ROUTER_IFACE.decodeFunctionResult(
555
+ "getAmountsOut",
556
+ quoteResponse.result
557
+ );
558
+ const amountsOut = decoded[0];
559
+ if (!Array.isArray(amountsOut) || amountsOut.length < 2) {
560
+ throw new Error("router returned an invalid quote");
561
+ }
562
+ const finalAmount = amountsOut[amountsOut.length - 1];
563
+ if (typeof finalAmount !== "bigint" || finalAmount <= 0n) {
564
+ throw new Error("router quote output is invalid");
565
+ }
566
+ amountOutWei = finalAmount;
567
+ routeProvider = "pancakeswap-v2";
568
+ route = candidateRoute;
569
+ break;
570
+ } catch (err) {
571
+ const message = err instanceof Error ? err.message : String(err);
572
+ providerErrors.push(`${provider}: ${message}`);
573
+ }
574
+ }
575
+ if (!amountOutWei) {
576
+ throw new Error(
577
+ `Trade quote failed across providers (${providerErrors.join(" | ")})`
578
+ );
579
+ }
580
+ if (routeProviderRequested !== routeProvider) {
581
+ routeProviderFallbackUsed = true;
582
+ routeProviderNotes.push(
583
+ `Requested ${routeProviderRequested}, used ${routeProvider}.`
584
+ );
585
+ }
586
+ if (providerErrors.length > 0) {
587
+ routeProviderNotes.push(...providerErrors);
588
+ }
589
+ let minReceiveWei = amountOutWei * BigInt(1e4 - slippageBps) / 10000n;
590
+ if (minReceiveWei === 0n && amountOutWei > 0n) {
591
+ minReceiveWei = 1n;
592
+ }
593
+ const outDecimals = side === "buy" ? tokenDecimals : 18;
594
+ const inSymbol = side === "buy" ? "BNB" : tokenSymbol;
595
+ const outSymbol = side === "buy" ? tokenSymbol : "BNB";
596
+ const amountInFormatted = side === "buy" ? ethers.formatEther(amountInWei) : ethers.formatUnits(amountInWei, tokenDecimals);
597
+ const amountOutFormatted = ethers.formatUnits(amountOutWei, outDecimals);
598
+ const minReceiveFormatted = ethers.formatUnits(minReceiveWei, outDecimals);
599
+ return {
600
+ ok: true,
601
+ side,
602
+ routeProvider,
603
+ routeProviderRequested,
604
+ routeProviderFallbackUsed,
605
+ routeProviderNotes: routeProviderNotes.length > 0 ? routeProviderNotes : void 0,
606
+ routerAddress: routeProvider === "0x" ? swapTargetAddress ?? context.routerAddress : context.routerAddress,
607
+ wrappedNativeAddress,
608
+ tokenAddress,
609
+ slippageBps,
610
+ route,
611
+ quoteIn: {
612
+ symbol: inSymbol,
613
+ amount: amountInFormatted,
614
+ amountWei: amountInWei.toString()
615
+ },
616
+ quoteOut: {
617
+ symbol: outSymbol,
618
+ amount: amountOutFormatted,
619
+ amountWei: amountOutWei.toString()
620
+ },
621
+ minReceive: {
622
+ symbol: outSymbol,
623
+ amount: minReceiveFormatted,
624
+ amountWei: minReceiveWei.toString()
625
+ },
626
+ price: formatPrice(amountInFormatted, amountOutFormatted),
627
+ preflight,
628
+ swapTargetAddress,
629
+ swapCallData,
630
+ swapValueWei,
631
+ allowanceTarget,
632
+ quotedAt: Date.now()
633
+ };
634
+ }
635
+ function assertRouterAddress(quote, context) {
636
+ if (quote.routeProvider === "0x") {
637
+ return;
638
+ }
639
+ if (quote.routerAddress !== context.routerAddress) {
640
+ throw new Error(
641
+ `Unexpected router address in quote: ${quote.routerAddress}. Expected router ${context.routerAddress}.`
642
+ );
643
+ }
644
+ }
645
+ function buildBscBuyUnsignedTx(quote, recipientAddress, deadlineSeconds) {
646
+ const context = resolveBscExecutionContext();
647
+ assertRouterAddress(quote, context);
648
+ if (quote.side !== "buy") {
649
+ throw new Error("Only buy execution is currently supported.");
650
+ }
651
+ const normalizedRecipient = normalizeAddress(recipientAddress);
652
+ if (!normalizedRecipient) {
653
+ throw new Error("Recipient wallet address is required.");
654
+ }
655
+ if (quote.routeProvider === "0x") {
656
+ if (!quote.swapTargetAddress || !quote.swapCallData) {
657
+ throw new Error("0x quote is missing swap transaction payload.");
658
+ }
659
+ return {
660
+ chainId: context.chainId,
661
+ from: normalizedRecipient,
662
+ to: quote.swapTargetAddress,
663
+ data: quote.swapCallData,
664
+ valueWei: quote.swapValueWei ?? quote.quoteIn.amountWei,
665
+ deadline: Math.floor(Date.now() / 1e3) + clampDeadlineSeconds(deadlineSeconds),
666
+ explorerUrl: context.explorerBaseUrl
667
+ };
668
+ }
669
+ const now = Math.floor(Date.now() / 1e3);
670
+ const deadline = now + clampDeadlineSeconds(deadlineSeconds);
671
+ const data = ROUTER_IFACE.encodeFunctionData(
672
+ "swapExactETHForTokensSupportingFeeOnTransferTokens",
673
+ [
674
+ BigInt(quote.minReceive.amountWei),
675
+ quote.route,
676
+ normalizedRecipient,
677
+ deadline
678
+ ]
679
+ );
680
+ return {
681
+ chainId: context.chainId,
682
+ from: normalizedRecipient,
683
+ to: quote.routerAddress,
684
+ data,
685
+ valueWei: quote.quoteIn.amountWei,
686
+ deadline,
687
+ explorerUrl: context.explorerBaseUrl
688
+ };
689
+ }
690
+ function buildBscSellUnsignedTx(quote, recipientAddress, deadlineSeconds) {
691
+ const context = resolveBscExecutionContext();
692
+ assertRouterAddress(quote, context);
693
+ if (quote.side !== "sell") {
694
+ throw new Error("Only sell execution is supported for this payload.");
695
+ }
696
+ const normalizedRecipient = normalizeAddress(recipientAddress);
697
+ if (!normalizedRecipient) {
698
+ throw new Error("Recipient wallet address is required.");
699
+ }
700
+ if (quote.routeProvider === "0x") {
701
+ if (!quote.swapTargetAddress || !quote.swapCallData) {
702
+ throw new Error("0x quote is missing swap transaction payload.");
703
+ }
704
+ return {
705
+ chainId: context.chainId,
706
+ from: normalizedRecipient,
707
+ to: quote.swapTargetAddress,
708
+ data: quote.swapCallData,
709
+ valueWei: quote.swapValueWei ?? "0",
710
+ deadline: Math.floor(Date.now() / 1e3) + clampDeadlineSeconds(deadlineSeconds),
711
+ explorerUrl: context.explorerBaseUrl
712
+ };
713
+ }
714
+ const now = Math.floor(Date.now() / 1e3);
715
+ const deadline = now + clampDeadlineSeconds(deadlineSeconds);
716
+ const data = ROUTER_IFACE.encodeFunctionData(
717
+ "swapExactTokensForETHSupportingFeeOnTransferTokens",
718
+ [
719
+ BigInt(quote.quoteIn.amountWei),
720
+ BigInt(quote.minReceive.amountWei),
721
+ quote.route,
722
+ normalizedRecipient,
723
+ deadline
724
+ ]
725
+ );
726
+ return {
727
+ chainId: context.chainId,
728
+ from: normalizedRecipient,
729
+ to: quote.routerAddress,
730
+ data,
731
+ valueWei: "0",
732
+ deadline,
733
+ explorerUrl: context.explorerBaseUrl
734
+ };
735
+ }
736
+ function buildBscApproveUnsignedTx(tokenAddress, ownerAddress, spenderAddress, amountWei) {
737
+ const context = resolveBscExecutionContext();
738
+ const normalizedToken = normalizeAddress(tokenAddress);
739
+ if (!normalizedToken) {
740
+ throw new Error("Token address is invalid for approval payload.");
741
+ }
742
+ const normalizedOwner = normalizeAddress(ownerAddress);
743
+ if (!normalizedOwner) {
744
+ throw new Error("Owner wallet address is required for approval payload.");
745
+ }
746
+ const normalizedSpender = normalizeAddress(spenderAddress);
747
+ if (!normalizedSpender) {
748
+ throw new Error("Spender address is invalid for approval payload.");
749
+ }
750
+ let amount;
751
+ try {
752
+ amount = BigInt(amountWei);
753
+ } catch {
754
+ throw new Error(
755
+ `Invalid approval amount: expected integer string, got "${String(amountWei).slice(0, 20)}"`
756
+ );
757
+ }
758
+ if (amount <= 0n) {
759
+ throw new Error("Approval amount must be greater than zero.");
760
+ }
761
+ const data = ERC20_IFACE.encodeFunctionData("approve", [
762
+ normalizedSpender,
763
+ amount
764
+ ]);
765
+ return {
766
+ chainId: context.chainId,
767
+ from: normalizedOwner,
768
+ to: normalizedToken,
769
+ data,
770
+ valueWei: "0",
771
+ explorerUrl: context.explorerBaseUrl,
772
+ spender: normalizedSpender,
773
+ amountWei: amount.toString()
774
+ };
775
+ }
776
+ function resolveBscApprovalSpender(quote) {
777
+ if (quote.routeProvider === "0x" && quote.allowanceTarget) {
778
+ return quote.allowanceTarget;
779
+ }
780
+ return quote.routerAddress;
781
+ }
782
+ export {
783
+ BSC_TESTNET_EXPLORER_BASE_URL,
784
+ BSC_WBNB_FALLBACK,
785
+ PANCAKE_SWAP_V2_ROUTER,
786
+ buildBscApproveUnsignedTx,
787
+ buildBscBuyUnsignedTx,
788
+ buildBscSellUnsignedTx,
789
+ buildBscTradePreflight,
790
+ buildBscTradeQuote,
791
+ readTokenDecimals,
792
+ resolveBscApprovalSpender,
793
+ resolveBscRpcUrls,
794
+ resolvePrimaryBscRpcUrl
795
+ };
796
+ //# sourceMappingURL=bsc-trade.js.map