@arcana/ca-sdk
Version:
Arcana Network's chain abstraction SDK for unified balance in Web3 apps
107 lines (106 loc) • 4.37 kB
JavaScript
import { Universe } from "@arcana/ca-common";
import { createPublicClient, hexToBigInt, serializeTransaction, webSocket, } from "viem";
import { ZERO_ADDRESS } from "../../constants";
import { getLogger } from "../../logger";
import { simulateTransaction } from "../../simulate";
import { divDecimals, evmWaitForFill, getL1Fee } from "../../utils";
import { NativeRequestBase } from "../common/native.base";
const logger = getLogger();
class NativeTransfer extends NativeRequestBase {
constructor(input) {
super(input);
this.input = input;
this.destinationUniverse = Universe.ETHEREUM;
const wsUrls = this.input.chain.rpcUrls?.default?.webSocket;
if (!wsUrls?.length) {
throw new Error(`Web-Socket RPC URL missing for chain ${this.input.chain.id}`);
}
this.publicClient = createPublicClient({
transport: webSocket(wsUrls[0]),
});
}
async simulateTx() {
const { data, to, value } = this.input.evm.tx;
const nativeToken = this.input.chainList.getNativeToken(this.input.chain.id);
if (this.simulateTxRes) {
let gasFee = 0n;
if (!this.input.options.bridge) {
const [{ gasPrice, maxFeePerGas }, l1Fee] = await Promise.all([
this.publicClient.estimateFeesPerGas(),
getL1Fee(this.input.chain, serializeTransaction({
chainId: this.input.chain.id,
data: data ?? "0x00",
to: to,
type: "eip1559",
value: hexToBigInt(value ?? `0x00`),
})),
]);
const gasUnitPrice = maxFeePerGas ?? gasPrice;
gasFee = this.simulateTxRes.gas * gasUnitPrice + l1Fee;
}
return {
...this.simulateTxRes,
gasFee: divDecimals(gasFee, nativeToken.decimals),
};
}
const txsToSimulate = [
{
from: ZERO_ADDRESS,
input: data ?? "0x00",
to,
value: value ?? "0x00",
},
];
const [simulation, feeData, l1Fee] = await Promise.all([
simulateTransaction(this.input.chain.id, txsToSimulate, this.input.options.networkConfig.SIMULATION_URL),
this.publicClient.estimateFeesPerGas(),
getL1Fee(this.input.chain, serializeTransaction({
chainId: this.input.chain.id,
data: data ?? "0x00",
to: to,
type: "eip1559",
value: hexToBigInt(value ?? "0x00"),
})),
]);
const gasUnitPrice = feeData.maxFeePerGas ?? feeData.gasPrice ?? 0n;
if (gasUnitPrice === 0n) {
throw new Error("could not get maxFeePerGas or gasPrice from RPC");
}
let gasFee = BigInt(simulation.data.gas_used) * gasUnitPrice + l1Fee;
logger.debug("native:simulateTx", {
feeData,
l1Fee,
maxFeePerGas: gasUnitPrice,
simulation,
totalGas: gasFee,
totalGasInDecimal: divDecimals(gasFee, nativeToken.decimals),
});
if (this.input.options.bridge) {
gasFee = 0n;
}
this.simulateTxRes = {
amount: divDecimals(value ?? "0", nativeToken.decimals),
gas: BigInt(simulation.data.gas_used),
gasFee: divDecimals(gasFee, nativeToken.decimals),
token: {
contractAddress: ZERO_ADDRESS,
decimals: nativeToken.decimals,
name: nativeToken.name,
symbol: nativeToken.symbol,
},
};
return this.simulateTxRes;
}
async waitForFill(requestHash, intentID, waitForDoubleCheckTx) {
try {
await Promise.all([
waitForDoubleCheckTx(),
evmWaitForFill(this.input.chainList.getVaultContractAddress(this.input.chain.id), this.publicClient, requestHash, intentID, this.input.options.networkConfig.GRPC_URL, this.input.options.networkConfig.COSMOS_URL),
]);
}
finally {
(await this.publicClient.transport.getRpcClient()).close();
}
}
}
export default NativeTransfer;