@pioneer-platform/pioneer-sdk 8.12.0 → 8.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1074,17 +1074,17 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1074
1074
  let unsignedTx;
1075
1075
  if (memo === " ")
1076
1076
  memo = "";
1077
+ const isThorchainSwap = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1077
1078
  switch (assetType) {
1078
1079
  case "gas": {
1079
- const isThorchainOperation = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1080
1080
  let gasLimit;
1081
- if (isThorchainOperation) {
1082
- gasLimit = BigInt(120000);
1081
+ if (isThorchainSwap) {
1082
+ gasLimit = BigInt(1e5);
1083
1083
  console.log(tag, "Using higher gas limit for THORChain swap:", gasLimit.toString());
1084
1084
  } else {
1085
1085
  gasLimit = chainId === 1 ? BigInt(21000) : BigInt(25000);
1086
1086
  }
1087
- if (memo && memo !== "" && !isThorchainOperation) {
1087
+ if (memo && memo !== "" && !isThorchainSwap) {
1088
1088
  const memoBytes = Buffer.from(memo, "utf8").length;
1089
1089
  gasLimit += BigInt(memoBytes) * 68n;
1090
1090
  }
@@ -1099,11 +1099,30 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1099
1099
  console.log(tag, "isMax calculation - balance:", balance.toString(), "gasFee:", gasFee.toString(), "buffer:", buffer.toString(), "amountWei:", amountWei.toString());
1100
1100
  } else {
1101
1101
  amountWei = BigInt(Math.round(amount * 1000000000000000000));
1102
- if (amountWei + gasFee > balance) {
1103
- throw new Error("Insufficient funds for the transaction amount and gas fees");
1102
+ const totalNeeded = amountWei + gasFee;
1103
+ if (totalNeeded > balance) {
1104
+ const availableForSwap = balance > gasFee ? balance - gasFee : 0n;
1105
+ const balanceEth2 = (Number(balance) / 1000000000000000000).toFixed(6);
1106
+ const amountEth = (Number(amountWei) / 1000000000000000000).toFixed(6);
1107
+ const gasFeeEth = (Number(gasFee) / 1000000000000000000).toFixed(6);
1108
+ const availableEth = (Number(availableForSwap) / 1000000000000000000).toFixed(6);
1109
+ const swapType = isThorchainSwap ? "THORChain swap" : "transfer";
1110
+ throw new Error(`Insufficient funds for the transaction amount and gas fees.
1111
+ ` + `Balance: ${balanceEth2} ETH
1112
+ ` + `Attempting to ${swapType}: ${amountEth} ETH
1113
+ ` + `Estimated gas fee: ${gasFeeEth} ETH (${gasLimit.toString()} gas limit)
1114
+ ` + `Total needed: ${(Number(totalNeeded) / 1000000000000000000).toFixed(6)} ETH
1115
+ ` + `Maximum swappable: ${availableEth} ETH (after gas fees)`);
1104
1116
  }
1105
1117
  }
1106
- const isThorchainSwap = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1118
+ console.log(tag, "Transaction calculation:", {
1119
+ balance: balance.toString() + " wei (" + (Number(balance) / 1000000000000000000).toFixed(6) + " ETH)",
1120
+ amountWei: amountWei.toString() + " wei (" + (Number(amountWei) / 1000000000000000000).toFixed(6) + " ETH)",
1121
+ gasFee: gasFee.toString() + " wei (" + (Number(gasFee) / 1000000000000000000).toFixed(6) + " ETH)",
1122
+ gasLimit: gasLimit.toString(),
1123
+ gasPrice: gasPrice.toString() + " wei (" + (Number(gasPrice) / 1e9).toFixed(2) + " gwei)",
1124
+ isThorchainSwap
1125
+ });
1107
1126
  let txData = "0x";
1108
1127
  if (isThorchainSwap) {
1109
1128
  console.log(tag, "Detected THORChain swap, encoding deposit data for memo:", memo);
@@ -1125,14 +1144,25 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1125
1144
  const inboundResponse = await fetch("https://thornode.ninerealms.com/thorchain/inbound_addresses");
1126
1145
  if (inboundResponse.ok) {
1127
1146
  const inboundData = await inboundResponse.json();
1128
- const ethInbound = inboundData.find((inbound) => inbound.chain === "ETH" && !inbound.halted);
1129
- if (ethInbound) {
1130
- vaultAddress = ethInbound.address;
1131
- routerAddress = ethInbound.router || to;
1147
+ const chainIdToThorchain = {
1148
+ 1: "ETH",
1149
+ 43114: "AVAX",
1150
+ 8453: "BASE",
1151
+ 56: "BSC"
1152
+ };
1153
+ const thorchainName = chainIdToThorchain[chainId];
1154
+ if (!thorchainName) {
1155
+ throw new Error(`Unsupported chain ID for THORChain swap: ${chainId}`);
1156
+ }
1157
+ console.log(tag, "Looking for THORChain inbound for chain:", thorchainName, "chainId:", chainId);
1158
+ const chainInbound = inboundData.find((inbound) => inbound.chain === thorchainName && !inbound.halted);
1159
+ if (chainInbound) {
1160
+ vaultAddress = chainInbound.address;
1161
+ routerAddress = chainInbound.router || to;
1132
1162
  console.log(tag, "Using THORChain inbound addresses - vault:", vaultAddress, "router:", routerAddress);
1133
1163
  to = routerAddress;
1134
1164
  } else {
1135
- throw new Error("ETH inbound is halted or not found - cannot proceed with swap");
1165
+ throw new Error(`${thorchainName} inbound is halted or not found - cannot proceed with swap`);
1136
1166
  }
1137
1167
  }
1138
1168
  } catch (fetchError) {
@@ -1142,6 +1172,15 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1142
1172
  if (vaultAddress === "0x0000000000000000000000000000000000000000") {
1143
1173
  throw new Error("Cannot proceed with THORChain swap - vault address is invalid (0x0)");
1144
1174
  }
1175
+ if (to.toLowerCase() === vaultAddress.toLowerCase()) {
1176
+ console.warn(tag, '⚠️ WARNING: "to" address equals vault address - this should be the router!');
1177
+ console.warn(tag, "⚠️ Using fetched router address instead:", routerAddress);
1178
+ to = routerAddress;
1179
+ }
1180
+ if (!/^0x[a-fA-F0-9]{40}$/.test(to)) {
1181
+ throw new Error(`Invalid router address format: ${to}`);
1182
+ }
1183
+ console.log(tag, "✅ Final validation passed - router:", to, "vault:", vaultAddress);
1145
1184
  const functionSelector = "44bc937b";
1146
1185
  const assetAddress = "0x0000000000000000000000000000000000000000";
1147
1186
  const expiryTime = Math.floor(Date.now() / 1000) + 3600;
@@ -1230,17 +1269,98 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1230
1269
  if (estimatedGasFee > balance) {
1231
1270
  throw new Error("Insufficient ETH balance to cover gas fees");
1232
1271
  }
1233
- const data = encodeTransferData(to, amountWei);
1272
+ let data;
1273
+ let finalTo;
1274
+ if (isThorchainSwap) {
1275
+ console.log(tag, "\uD83D\uDD04 ERC20 THORChain swap detected - encoding depositWithExpiry");
1276
+ let vaultAddress = "0x0000000000000000000000000000000000000000";
1277
+ let routerAddress = to;
1278
+ try {
1279
+ const inboundResponse = await fetch("https://thornode.ninerealms.com/thorchain/inbound_addresses");
1280
+ if (inboundResponse.ok) {
1281
+ const inboundData = await inboundResponse.json();
1282
+ const chainIdToThorchain = {
1283
+ 1: "ETH",
1284
+ 43114: "AVAX",
1285
+ 8453: "BASE",
1286
+ 56: "BSC"
1287
+ };
1288
+ const thorchainName = chainIdToThorchain[chainId];
1289
+ if (!thorchainName) {
1290
+ throw new Error(`Unsupported chain ID for THORChain swap: ${chainId}`);
1291
+ }
1292
+ console.log(tag, "Looking for THORChain inbound for chain:", thorchainName, "chainId:", chainId);
1293
+ const chainInbound = inboundData.find((inbound) => inbound.chain === thorchainName && !inbound.halted);
1294
+ if (chainInbound) {
1295
+ vaultAddress = chainInbound.address;
1296
+ routerAddress = chainInbound.router || to;
1297
+ console.log(tag, "Using THORChain inbound addresses - vault:", vaultAddress, "router:", routerAddress);
1298
+ to = routerAddress;
1299
+ } else {
1300
+ throw new Error(`${thorchainName} inbound is halted or not found - cannot proceed with swap`);
1301
+ }
1302
+ }
1303
+ } catch (fetchError) {
1304
+ console.error(tag, "Failed to fetch inbound addresses:", fetchError);
1305
+ throw new Error(`Cannot proceed with THORChain swap - failed to fetch inbound addresses: ${fetchError.message}`);
1306
+ }
1307
+ if (vaultAddress === "0x0000000000000000000000000000000000000000") {
1308
+ throw new Error("Cannot proceed with THORChain swap - vault address is invalid (0x0)");
1309
+ }
1310
+ if (to.toLowerCase() === vaultAddress.toLowerCase()) {
1311
+ console.warn(tag, '⚠️ WARNING: "to" address equals vault address - this should be the router!');
1312
+ console.warn(tag, "⚠️ Using fetched router address instead:", routerAddress);
1313
+ to = routerAddress;
1314
+ }
1315
+ if (!/^0x[a-fA-F0-9]{40}$/.test(to)) {
1316
+ throw new Error(`Invalid router address format: ${to}`);
1317
+ }
1318
+ console.log(tag, "✅ Final validation passed - router:", to, "vault:", vaultAddress);
1319
+ console.log(tag, "✅ ERC20 THORChain swap addresses validated");
1320
+ console.log(tag, " Router:", to);
1321
+ console.log(tag, " Vault:", vaultAddress);
1322
+ console.log(tag, " Token:", contractAddress);
1323
+ const functionSelector = "44bc937b";
1324
+ const expiryTime = Math.floor(Date.now() / 1000) + 3600;
1325
+ const vaultPadded = vaultAddress.toLowerCase().replace(/^0x/, "").padStart(64, "0");
1326
+ const assetPadded = contractAddress.toLowerCase().replace(/^0x/, "").padStart(64, "0");
1327
+ const amountPadded = amountWei.toString(16).padStart(64, "0");
1328
+ const stringOffset = (5 * 32).toString(16).padStart(64, "0");
1329
+ const expiryPadded = expiryTime.toString(16).padStart(64, "0");
1330
+ const fixedMemo = memo || "";
1331
+ const memoBytes = Buffer.from(fixedMemo, "utf8");
1332
+ const memoHex = memoBytes.toString("hex");
1333
+ const stringLength = memoBytes.length.toString(16).padStart(64, "0");
1334
+ const paddingLength = (32 - memoBytes.length % 32) % 32;
1335
+ const memoPadded = memoHex + "0".repeat(paddingLength * 2);
1336
+ data = "0x" + functionSelector + vaultPadded + assetPadded + amountPadded + stringOffset + expiryPadded + stringLength + memoPadded;
1337
+ finalTo = to;
1338
+ console.log(tag, "✅ Encoded ERC20 depositWithExpiry:", {
1339
+ functionSelector: "0x" + functionSelector,
1340
+ vault: vaultAddress,
1341
+ asset: contractAddress,
1342
+ amount: amountWei.toString(),
1343
+ memo: fixedMemo,
1344
+ expiry: expiryTime,
1345
+ dataLength: data.length
1346
+ });
1347
+ gasLimit = BigInt(200000);
1348
+ } else {
1349
+ data = encodeTransferData(to, amountWei);
1350
+ finalTo = contractAddress;
1351
+ }
1234
1352
  const ethPriceInUsd = await fetchEthPriceInUsd(pioneer, networkId);
1235
- const gasFeeUsd = Number(gasFee) / 1000000000000000000 * ethPriceInUsd;
1353
+ const finalGasFee = gasPrice * gasLimit;
1354
+ const gasFeeUsd = Number(finalGasFee) / 1000000000000000000 * ethPriceInUsd;
1236
1355
  const tokenPriceInUsd = await fetchTokenPriceInUsd(pioneer, caip);
1237
1356
  const amountUsd = Number(amountWei) / Number(tokenMultiplier) * tokenPriceInUsd;
1238
1357
  unsignedTx = {
1239
1358
  chainId,
1359
+ from: address,
1240
1360
  nonce: toHex(nonce),
1241
1361
  gas: toHex(gasLimit),
1242
1362
  gasPrice: toHex(gasPrice),
1243
- to: contractAddress,
1363
+ to: finalTo,
1244
1364
  value: "0x0",
1245
1365
  data,
1246
1366
  gasFeeUsd,
@@ -4100,8 +4220,58 @@ class SDK {
4100
4220
  if (tx.type === "deposit") {
4101
4221
  unsignedTx = await createUnsignedTendermintTx(caip, tx.type, tx.txParams.amount, tx.txParams.memo, this.pubkeys, this.pioneer, this.pubkeyContext, false, undefined);
4102
4222
  } else if (tx.type === "EVM" || tx.type === "evm") {
4103
- console.log(tag, "Using pre-built EVM transaction from integration");
4104
- unsignedTx = tx.txParams;
4223
+ console.log(tag, "Building EVM swap transaction with createUnsignedEvmTx");
4224
+ unsignedTx = await createUnsignedEvmTx(caip, tx.txParams.recipientAddress, parseFloat(tx.txParams.amount), tx.txParams.memo, this.pubkeys, this.pioneer, this.pubkeyContext, false);
4225
+ console.log(tag, "✅ Built complete EVM transaction:", {
4226
+ to: unsignedTx.to,
4227
+ from: this.pubkeyContext?.address,
4228
+ value: unsignedTx.value,
4229
+ chainId: unsignedTx.chainId,
4230
+ hasData: !!unsignedTx.data
4231
+ });
4232
+ console.log(tag, "\uD83D\uDEE1️ Running Tenderly simulation for EVM swap...");
4233
+ try {
4234
+ const insightResult = await this.pioneer.Insight({
4235
+ tx: unsignedTx,
4236
+ source: "swap",
4237
+ isThorchainSwap: tx.txParams.isThorchainSwap || false
4238
+ });
4239
+ const insight = insightResult.body;
4240
+ console.log(tag, "Simulation result:", insight?.simulation);
4241
+ if (!insight || !insight.simulation) {
4242
+ console.warn(tag, "⚠️ WARNING: Tenderly simulation unavailable - proceeding without validation");
4243
+ } else if (!insight.simulation.success) {
4244
+ console.warn(tag, `⚠️ WARNING: Swap simulation FAILED - ${insight.simulation.error || "Transaction may revert"}`);
4245
+ console.warn(tag, "⚠️ Proceeding anyway - USE CAUTION");
4246
+ } else {
4247
+ if (tx.txParams.isThorchainSwap) {
4248
+ console.log(tag, "\uD83D\uDD0D Verifying THORChain swap parameters...");
4249
+ const method = insight.simulation.method;
4250
+ if (!method || !method.toLowerCase().includes("deposit")) {
4251
+ throw new Error(`❌ CRITICAL: Invalid THORChain swap method: ${method} - expected depositWithExpiry`);
4252
+ }
4253
+ const routerAddress = unsignedTx.to;
4254
+ const vaultAddress = tx.txParams.vaultAddress;
4255
+ if (routerAddress && vaultAddress) {
4256
+ if (routerAddress.toLowerCase() === vaultAddress.toLowerCase()) {
4257
+ throw new Error(`❌ CRITICAL: Sending directly to vault ${vaultAddress} instead of router!`);
4258
+ }
4259
+ console.log(tag, `✅ Router: ${routerAddress}`);
4260
+ console.log(tag, `✅ Vault: ${vaultAddress}`);
4261
+ }
4262
+ if (insight.simulation.addresses && insight.simulation.addresses.length > 0) {
4263
+ console.log(tag, `✅ Addresses involved: ${insight.simulation.addresses.length}`);
4264
+ } else {
4265
+ console.log(tag, "⚠️ WARNING: No addresses detected in simulation");
4266
+ }
4267
+ }
4268
+ console.log(tag, `✅ Simulation PASSED - Gas used: ${insight.simulation.gasUsed}`);
4269
+ console.log(tag, `✅ Method: ${insight.simulation.method}`);
4270
+ }
4271
+ } catch (e) {
4272
+ console.error(tag, "❌ Simulation validation failed:", e.message);
4273
+ throw new Error(`Swap blocked by simulation failure: ${e.message}`);
4274
+ }
4105
4275
  } else {
4106
4276
  if (!tx.txParams.memo)
4107
4277
  throw Error("memo required on swaps!");
@@ -4971,5 +5141,29 @@ class SDK {
4971
5141
  }
4972
5142
  };
4973
5143
  }
5144
+ CheckERC20Allowance = async (params) => {
5145
+ const tag = TAG9 + " | CheckERC20Allowance | ";
5146
+ try {
5147
+ console.log(tag, "Checking ERC20 allowance:", params);
5148
+ const result = await this.pioneer.GetTokenAllowance(params);
5149
+ console.log(tag, "Allowance result:", result);
5150
+ return result.data;
5151
+ } catch (e) {
5152
+ console.error(tag, "Error checking ERC20 allowance:", e);
5153
+ throw e;
5154
+ }
5155
+ };
5156
+ BuildERC20ApprovalTx = async (params) => {
5157
+ const tag = TAG9 + " | BuildERC20ApprovalTx | ";
5158
+ try {
5159
+ console.log(tag, "Building ERC20 approval transaction:", params);
5160
+ const result = await this.pioneer.BuildApprovalTransaction(params);
5161
+ console.log(tag, "Approval tx built:", result);
5162
+ return result.data;
5163
+ } catch (e) {
5164
+ console.error(tag, "Error building approval transaction:", e);
5165
+ throw e;
5166
+ }
5167
+ };
4974
5168
  }
4975
5169
  var src_default = SDK;
package/dist/index.es.js CHANGED
@@ -1250,17 +1250,17 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1250
1250
  let unsignedTx;
1251
1251
  if (memo === " ")
1252
1252
  memo = "";
1253
+ const isThorchainSwap = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1253
1254
  switch (assetType) {
1254
1255
  case "gas": {
1255
- const isThorchainOperation = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1256
1256
  let gasLimit;
1257
- if (isThorchainOperation) {
1258
- gasLimit = BigInt(120000);
1257
+ if (isThorchainSwap) {
1258
+ gasLimit = BigInt(1e5);
1259
1259
  console.log(tag, "Using higher gas limit for THORChain swap:", gasLimit.toString());
1260
1260
  } else {
1261
1261
  gasLimit = chainId === 1 ? BigInt(21000) : BigInt(25000);
1262
1262
  }
1263
- if (memo && memo !== "" && !isThorchainOperation) {
1263
+ if (memo && memo !== "" && !isThorchainSwap) {
1264
1264
  const memoBytes = Buffer.from(memo, "utf8").length;
1265
1265
  gasLimit += BigInt(memoBytes) * 68n;
1266
1266
  }
@@ -1275,11 +1275,30 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1275
1275
  console.log(tag, "isMax calculation - balance:", balance.toString(), "gasFee:", gasFee.toString(), "buffer:", buffer.toString(), "amountWei:", amountWei.toString());
1276
1276
  } else {
1277
1277
  amountWei = BigInt(Math.round(amount * 1000000000000000000));
1278
- if (amountWei + gasFee > balance) {
1279
- throw new Error("Insufficient funds for the transaction amount and gas fees");
1278
+ const totalNeeded = amountWei + gasFee;
1279
+ if (totalNeeded > balance) {
1280
+ const availableForSwap = balance > gasFee ? balance - gasFee : 0n;
1281
+ const balanceEth2 = (Number(balance) / 1000000000000000000).toFixed(6);
1282
+ const amountEth = (Number(amountWei) / 1000000000000000000).toFixed(6);
1283
+ const gasFeeEth = (Number(gasFee) / 1000000000000000000).toFixed(6);
1284
+ const availableEth = (Number(availableForSwap) / 1000000000000000000).toFixed(6);
1285
+ const swapType = isThorchainSwap ? "THORChain swap" : "transfer";
1286
+ throw new Error(`Insufficient funds for the transaction amount and gas fees.
1287
+ ` + `Balance: ${balanceEth2} ETH
1288
+ ` + `Attempting to ${swapType}: ${amountEth} ETH
1289
+ ` + `Estimated gas fee: ${gasFeeEth} ETH (${gasLimit.toString()} gas limit)
1290
+ ` + `Total needed: ${(Number(totalNeeded) / 1000000000000000000).toFixed(6)} ETH
1291
+ ` + `Maximum swappable: ${availableEth} ETH (after gas fees)`);
1280
1292
  }
1281
1293
  }
1282
- const isThorchainSwap = memo && (memo.startsWith("=") || memo.startsWith("SWAP") || memo.includes(":"));
1294
+ console.log(tag, "Transaction calculation:", {
1295
+ balance: balance.toString() + " wei (" + (Number(balance) / 1000000000000000000).toFixed(6) + " ETH)",
1296
+ amountWei: amountWei.toString() + " wei (" + (Number(amountWei) / 1000000000000000000).toFixed(6) + " ETH)",
1297
+ gasFee: gasFee.toString() + " wei (" + (Number(gasFee) / 1000000000000000000).toFixed(6) + " ETH)",
1298
+ gasLimit: gasLimit.toString(),
1299
+ gasPrice: gasPrice.toString() + " wei (" + (Number(gasPrice) / 1e9).toFixed(2) + " gwei)",
1300
+ isThorchainSwap
1301
+ });
1283
1302
  let txData = "0x";
1284
1303
  if (isThorchainSwap) {
1285
1304
  console.log(tag, "Detected THORChain swap, encoding deposit data for memo:", memo);
@@ -1301,14 +1320,25 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1301
1320
  const inboundResponse = await fetch("https://thornode.ninerealms.com/thorchain/inbound_addresses");
1302
1321
  if (inboundResponse.ok) {
1303
1322
  const inboundData = await inboundResponse.json();
1304
- const ethInbound = inboundData.find((inbound) => inbound.chain === "ETH" && !inbound.halted);
1305
- if (ethInbound) {
1306
- vaultAddress = ethInbound.address;
1307
- routerAddress = ethInbound.router || to;
1323
+ const chainIdToThorchain = {
1324
+ 1: "ETH",
1325
+ 43114: "AVAX",
1326
+ 8453: "BASE",
1327
+ 56: "BSC"
1328
+ };
1329
+ const thorchainName = chainIdToThorchain[chainId];
1330
+ if (!thorchainName) {
1331
+ throw new Error(`Unsupported chain ID for THORChain swap: ${chainId}`);
1332
+ }
1333
+ console.log(tag, "Looking for THORChain inbound for chain:", thorchainName, "chainId:", chainId);
1334
+ const chainInbound = inboundData.find((inbound) => inbound.chain === thorchainName && !inbound.halted);
1335
+ if (chainInbound) {
1336
+ vaultAddress = chainInbound.address;
1337
+ routerAddress = chainInbound.router || to;
1308
1338
  console.log(tag, "Using THORChain inbound addresses - vault:", vaultAddress, "router:", routerAddress);
1309
1339
  to = routerAddress;
1310
1340
  } else {
1311
- throw new Error("ETH inbound is halted or not found - cannot proceed with swap");
1341
+ throw new Error(`${thorchainName} inbound is halted or not found - cannot proceed with swap`);
1312
1342
  }
1313
1343
  }
1314
1344
  } catch (fetchError) {
@@ -1318,6 +1348,15 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1318
1348
  if (vaultAddress === "0x0000000000000000000000000000000000000000") {
1319
1349
  throw new Error("Cannot proceed with THORChain swap - vault address is invalid (0x0)");
1320
1350
  }
1351
+ if (to.toLowerCase() === vaultAddress.toLowerCase()) {
1352
+ console.warn(tag, '⚠️ WARNING: "to" address equals vault address - this should be the router!');
1353
+ console.warn(tag, "⚠️ Using fetched router address instead:", routerAddress);
1354
+ to = routerAddress;
1355
+ }
1356
+ if (!/^0x[a-fA-F0-9]{40}$/.test(to)) {
1357
+ throw new Error(`Invalid router address format: ${to}`);
1358
+ }
1359
+ console.log(tag, "✅ Final validation passed - router:", to, "vault:", vaultAddress);
1321
1360
  const functionSelector = "44bc937b";
1322
1361
  const assetAddress = "0x0000000000000000000000000000000000000000";
1323
1362
  const expiryTime = Math.floor(Date.now() / 1000) + 3600;
@@ -1406,17 +1445,98 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1406
1445
  if (estimatedGasFee > balance) {
1407
1446
  throw new Error("Insufficient ETH balance to cover gas fees");
1408
1447
  }
1409
- const data = encodeTransferData(to, amountWei);
1448
+ let data;
1449
+ let finalTo;
1450
+ if (isThorchainSwap) {
1451
+ console.log(tag, "\uD83D\uDD04 ERC20 THORChain swap detected - encoding depositWithExpiry");
1452
+ let vaultAddress = "0x0000000000000000000000000000000000000000";
1453
+ let routerAddress = to;
1454
+ try {
1455
+ const inboundResponse = await fetch("https://thornode.ninerealms.com/thorchain/inbound_addresses");
1456
+ if (inboundResponse.ok) {
1457
+ const inboundData = await inboundResponse.json();
1458
+ const chainIdToThorchain = {
1459
+ 1: "ETH",
1460
+ 43114: "AVAX",
1461
+ 8453: "BASE",
1462
+ 56: "BSC"
1463
+ };
1464
+ const thorchainName = chainIdToThorchain[chainId];
1465
+ if (!thorchainName) {
1466
+ throw new Error(`Unsupported chain ID for THORChain swap: ${chainId}`);
1467
+ }
1468
+ console.log(tag, "Looking for THORChain inbound for chain:", thorchainName, "chainId:", chainId);
1469
+ const chainInbound = inboundData.find((inbound) => inbound.chain === thorchainName && !inbound.halted);
1470
+ if (chainInbound) {
1471
+ vaultAddress = chainInbound.address;
1472
+ routerAddress = chainInbound.router || to;
1473
+ console.log(tag, "Using THORChain inbound addresses - vault:", vaultAddress, "router:", routerAddress);
1474
+ to = routerAddress;
1475
+ } else {
1476
+ throw new Error(`${thorchainName} inbound is halted or not found - cannot proceed with swap`);
1477
+ }
1478
+ }
1479
+ } catch (fetchError) {
1480
+ console.error(tag, "Failed to fetch inbound addresses:", fetchError);
1481
+ throw new Error(`Cannot proceed with THORChain swap - failed to fetch inbound addresses: ${fetchError.message}`);
1482
+ }
1483
+ if (vaultAddress === "0x0000000000000000000000000000000000000000") {
1484
+ throw new Error("Cannot proceed with THORChain swap - vault address is invalid (0x0)");
1485
+ }
1486
+ if (to.toLowerCase() === vaultAddress.toLowerCase()) {
1487
+ console.warn(tag, '⚠️ WARNING: "to" address equals vault address - this should be the router!');
1488
+ console.warn(tag, "⚠️ Using fetched router address instead:", routerAddress);
1489
+ to = routerAddress;
1490
+ }
1491
+ if (!/^0x[a-fA-F0-9]{40}$/.test(to)) {
1492
+ throw new Error(`Invalid router address format: ${to}`);
1493
+ }
1494
+ console.log(tag, "✅ Final validation passed - router:", to, "vault:", vaultAddress);
1495
+ console.log(tag, "✅ ERC20 THORChain swap addresses validated");
1496
+ console.log(tag, " Router:", to);
1497
+ console.log(tag, " Vault:", vaultAddress);
1498
+ console.log(tag, " Token:", contractAddress);
1499
+ const functionSelector = "44bc937b";
1500
+ const expiryTime = Math.floor(Date.now() / 1000) + 3600;
1501
+ const vaultPadded = vaultAddress.toLowerCase().replace(/^0x/, "").padStart(64, "0");
1502
+ const assetPadded = contractAddress.toLowerCase().replace(/^0x/, "").padStart(64, "0");
1503
+ const amountPadded = amountWei.toString(16).padStart(64, "0");
1504
+ const stringOffset = (5 * 32).toString(16).padStart(64, "0");
1505
+ const expiryPadded = expiryTime.toString(16).padStart(64, "0");
1506
+ const fixedMemo = memo || "";
1507
+ const memoBytes = Buffer.from(fixedMemo, "utf8");
1508
+ const memoHex = memoBytes.toString("hex");
1509
+ const stringLength = memoBytes.length.toString(16).padStart(64, "0");
1510
+ const paddingLength = (32 - memoBytes.length % 32) % 32;
1511
+ const memoPadded = memoHex + "0".repeat(paddingLength * 2);
1512
+ data = "0x" + functionSelector + vaultPadded + assetPadded + amountPadded + stringOffset + expiryPadded + stringLength + memoPadded;
1513
+ finalTo = to;
1514
+ console.log(tag, "✅ Encoded ERC20 depositWithExpiry:", {
1515
+ functionSelector: "0x" + functionSelector,
1516
+ vault: vaultAddress,
1517
+ asset: contractAddress,
1518
+ amount: amountWei.toString(),
1519
+ memo: fixedMemo,
1520
+ expiry: expiryTime,
1521
+ dataLength: data.length
1522
+ });
1523
+ gasLimit = BigInt(200000);
1524
+ } else {
1525
+ data = encodeTransferData(to, amountWei);
1526
+ finalTo = contractAddress;
1527
+ }
1410
1528
  const ethPriceInUsd = await fetchEthPriceInUsd(pioneer, networkId);
1411
- const gasFeeUsd = Number(gasFee) / 1000000000000000000 * ethPriceInUsd;
1529
+ const finalGasFee = gasPrice * gasLimit;
1530
+ const gasFeeUsd = Number(finalGasFee) / 1000000000000000000 * ethPriceInUsd;
1412
1531
  const tokenPriceInUsd = await fetchTokenPriceInUsd(pioneer, caip);
1413
1532
  const amountUsd = Number(amountWei) / Number(tokenMultiplier) * tokenPriceInUsd;
1414
1533
  unsignedTx = {
1415
1534
  chainId,
1535
+ from: address,
1416
1536
  nonce: toHex(nonce),
1417
1537
  gas: toHex(gasLimit),
1418
1538
  gasPrice: toHex(gasPrice),
1419
- to: contractAddress,
1539
+ to: finalTo,
1420
1540
  value: "0x0",
1421
1541
  data,
1422
1542
  gasFeeUsd,
@@ -4276,8 +4396,58 @@ class SDK {
4276
4396
  if (tx.type === "deposit") {
4277
4397
  unsignedTx = await createUnsignedTendermintTx(caip, tx.type, tx.txParams.amount, tx.txParams.memo, this.pubkeys, this.pioneer, this.pubkeyContext, false, undefined);
4278
4398
  } else if (tx.type === "EVM" || tx.type === "evm") {
4279
- console.log(tag, "Using pre-built EVM transaction from integration");
4280
- unsignedTx = tx.txParams;
4399
+ console.log(tag, "Building EVM swap transaction with createUnsignedEvmTx");
4400
+ unsignedTx = await createUnsignedEvmTx(caip, tx.txParams.recipientAddress, parseFloat(tx.txParams.amount), tx.txParams.memo, this.pubkeys, this.pioneer, this.pubkeyContext, false);
4401
+ console.log(tag, "✅ Built complete EVM transaction:", {
4402
+ to: unsignedTx.to,
4403
+ from: this.pubkeyContext?.address,
4404
+ value: unsignedTx.value,
4405
+ chainId: unsignedTx.chainId,
4406
+ hasData: !!unsignedTx.data
4407
+ });
4408
+ console.log(tag, "\uD83D\uDEE1️ Running Tenderly simulation for EVM swap...");
4409
+ try {
4410
+ const insightResult = await this.pioneer.Insight({
4411
+ tx: unsignedTx,
4412
+ source: "swap",
4413
+ isThorchainSwap: tx.txParams.isThorchainSwap || false
4414
+ });
4415
+ const insight = insightResult.body;
4416
+ console.log(tag, "Simulation result:", insight?.simulation);
4417
+ if (!insight || !insight.simulation) {
4418
+ console.warn(tag, "⚠️ WARNING: Tenderly simulation unavailable - proceeding without validation");
4419
+ } else if (!insight.simulation.success) {
4420
+ console.warn(tag, `⚠️ WARNING: Swap simulation FAILED - ${insight.simulation.error || "Transaction may revert"}`);
4421
+ console.warn(tag, "⚠️ Proceeding anyway - USE CAUTION");
4422
+ } else {
4423
+ if (tx.txParams.isThorchainSwap) {
4424
+ console.log(tag, "\uD83D\uDD0D Verifying THORChain swap parameters...");
4425
+ const method = insight.simulation.method;
4426
+ if (!method || !method.toLowerCase().includes("deposit")) {
4427
+ throw new Error(`❌ CRITICAL: Invalid THORChain swap method: ${method} - expected depositWithExpiry`);
4428
+ }
4429
+ const routerAddress = unsignedTx.to;
4430
+ const vaultAddress = tx.txParams.vaultAddress;
4431
+ if (routerAddress && vaultAddress) {
4432
+ if (routerAddress.toLowerCase() === vaultAddress.toLowerCase()) {
4433
+ throw new Error(`❌ CRITICAL: Sending directly to vault ${vaultAddress} instead of router!`);
4434
+ }
4435
+ console.log(tag, `✅ Router: ${routerAddress}`);
4436
+ console.log(tag, `✅ Vault: ${vaultAddress}`);
4437
+ }
4438
+ if (insight.simulation.addresses && insight.simulation.addresses.length > 0) {
4439
+ console.log(tag, `✅ Addresses involved: ${insight.simulation.addresses.length}`);
4440
+ } else {
4441
+ console.log(tag, "⚠️ WARNING: No addresses detected in simulation");
4442
+ }
4443
+ }
4444
+ console.log(tag, `✅ Simulation PASSED - Gas used: ${insight.simulation.gasUsed}`);
4445
+ console.log(tag, `✅ Method: ${insight.simulation.method}`);
4446
+ }
4447
+ } catch (e) {
4448
+ console.error(tag, "❌ Simulation validation failed:", e.message);
4449
+ throw new Error(`Swap blocked by simulation failure: ${e.message}`);
4450
+ }
4281
4451
  } else {
4282
4452
  if (!tx.txParams.memo)
4283
4453
  throw Error("memo required on swaps!");
@@ -5147,6 +5317,30 @@ class SDK {
5147
5317
  }
5148
5318
  };
5149
5319
  }
5320
+ CheckERC20Allowance = async (params) => {
5321
+ const tag = TAG9 + " | CheckERC20Allowance | ";
5322
+ try {
5323
+ console.log(tag, "Checking ERC20 allowance:", params);
5324
+ const result = await this.pioneer.GetTokenAllowance(params);
5325
+ console.log(tag, "Allowance result:", result);
5326
+ return result.data;
5327
+ } catch (e) {
5328
+ console.error(tag, "Error checking ERC20 allowance:", e);
5329
+ throw e;
5330
+ }
5331
+ };
5332
+ BuildERC20ApprovalTx = async (params) => {
5333
+ const tag = TAG9 + " | BuildERC20ApprovalTx | ";
5334
+ try {
5335
+ console.log(tag, "Building ERC20 approval transaction:", params);
5336
+ const result = await this.pioneer.BuildApprovalTransaction(params);
5337
+ console.log(tag, "Approval tx built:", result);
5338
+ return result.data;
5339
+ } catch (e) {
5340
+ console.error(tag, "Error building approval transaction:", e);
5341
+ throw e;
5342
+ }
5343
+ };
5150
5344
  }
5151
5345
  var src_default = SDK;
5152
5346
  export {