Skip to content

Commit

Permalink
Fix revert handling (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Oct 18, 2024
1 parent d7f050f commit e2ec956
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 44 deletions.
4 changes: 4 additions & 0 deletions packages/localnet/src/handleOnEVMCalled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ export const handleOnEVMCalled = async ({
return await handleOnRevertEVM({
revertOptions,
err,
amount: 0,
asset: ethers.ZeroAddress,
tss,
isGas: true,
token: "",
provider,
protocolContracts,
exitOnError,
Expand Down
168 changes: 150 additions & 18 deletions packages/localnet/src/handleOnEVMDeposited.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ethers, NonceManager } from "ethers";
import { handleOnRevertEVM } from "./handleOnRevertEVM";
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";
import * as ZRC20 from "@zetachain/protocol-contracts/abi/ZRC20.sol/ZRC20.json";
import * as UniswapV2Router02 from "@uniswap/v2-periphery/build/UniswapV2Router02.json";

// event Deposited(address indexed sender, address indexed receiver, uint256 amount, address asset, bytes payload, RevertOptions revertOptions);
export const handleOnEVMDeposited = async ({
Expand All @@ -24,26 +26,24 @@ export const handleOnEVMDeposited = async ({
exitOnError: boolean;
}) => {
log("EVM", "Gateway: 'Deposited' event emitted");
try {
const receiver = args[1];
const amount = args[2];
const asset = args[3];
const message = args[4];

let foreignCoin;
if (asset === ethers.ZeroAddress) {
foreignCoin = foreignCoins.find((coin) => coin.coin_type === "Gas");
} else {
foreignCoin = foreignCoins.find((coin) => coin.asset === asset);
}

if (!foreignCoin) {
logErr("ZetaChain", `Foreign coin not found for asset: ${asset}`);
return;
}
const receiver = args[1];
const amount = args[2];
const asset = args[3];
const message = args[4];
let foreignCoin;
if (asset === ethers.ZeroAddress) {
foreignCoin = foreignCoins.find((coin) => coin.coin_type === "Gas");
} else {
foreignCoin = foreignCoins.find((coin) => coin.asset === asset);
}

const zrc20 = foreignCoin.zrc20_contract_address;
if (!foreignCoin) {
logErr("ZetaChain", `Foreign coin not found for asset: ${asset}`);
return;
}

const zrc20 = foreignCoin.zrc20_contract_address;
try {
const context = {
origin: protocolContracts.gatewayZEVM.target,
sender: await fungibleModuleSigner.getAddress(),
Expand Down Expand Up @@ -85,13 +85,145 @@ export const handleOnEVMDeposited = async ({
} catch (err) {
logErr("ZetaChain", `Error depositing: ${err}`);
const revertOptions = args[5];
const zrc20Contract = new ethers.Contract(zrc20, ZRC20.abi, deployer);
const [gasZRC20, gasFee] = await zrc20Contract.withdrawGasFeeWithGasLimit(
revertOptions[4]
);
let revertAmount;
let revertGasFee = gasFee;
let isGas = true;
let token = null;
if (zrc20 !== gasZRC20) {
token = foreignCoins.find(
(coin) => coin.zrc20_contract_address === zrc20
)?.asset;
isGas = false;
revertGasFee = await swapToCoverGas(
deployer,
zrc20,
gasZRC20,
gasFee,
amount,
await fungibleModuleSigner.getAddress(),
zrc20Contract,
provider,
protocolContracts.wzeta.target,
protocolContracts.uniswapRouterInstance.target
);
}
revertAmount = amount - revertGasFee;
return await handleOnRevertEVM({
revertOptions,
asset,
amount: revertAmount,
err,
tss,
isGas,
token,
provider,
protocolContracts,
exitOnError,
});
}
};

const swapToCoverGas = async (
deployer: any,
zrc20: string,
gasZRC20: string,
gasFee: any,
amount: any,
fungibleModule: any,
zrc20Contract: any,
provider: any,
wzeta: string,
router: string
) => {
/**
* Retrieves the amounts for swapping tokens using UniswapV2.
* @param {"in" | "out"} direction - The direction of the swap ("in" or "out").
* @param {any} provider - The ethers provider.
* @param {any} amount - The amount to swap.
* @param {string} tokenA - The address of token A.
* @param {string} tokenB - The address of token B.
* @returns {Promise<any>} - The amounts for the swap.
* @throws Will throw an error if the UniswapV2 router address cannot be retrieved.
*/
const getAmounts = async (
direction: "in" | "out",
provider: any,
amount: any,
tokenA: string,
tokenB: string,
routerAddress: any,
routerABI: any
) => {
if (!routerAddress) {
throw new Error("Cannot get uniswapV2Router02 address");
}

const uniswapRouter = new ethers.Contract(
routerAddress,
routerABI.abi,
provider
);

const path = [tokenA, tokenB];

const amounts =
direction === "in"
? await uniswapRouter.getAmountsIn(amount, path)
: await uniswapRouter.getAmountsOut(amount, path);
return amounts;
};

const uniswapV2Router = new ethers.Contract(
router,
UniswapV2Router02.abi,
deployer
);
deployer.reset();
const approvalTx = await zrc20Contract.approve(router, amount);
await approvalTx.wait();

const path = [zrc20, wzeta, gasZRC20];

const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
const maxZRC20ToSpend = amount;

try {
const swapTx = await uniswapV2Router.swapTokensForExactTokens(
gasFee,
maxZRC20ToSpend,
path,
fungibleModule,
deadline
);

await swapTx.wait();
} catch (swapError) {
logErr("ZetaChain", `Error performing swap on Uniswap: ${swapError}`);
}

const amountInZeta = await getAmounts(
"in",
provider,
gasFee,
wzeta,
gasZRC20,
router,
UniswapV2Router02
);

const amountInZRC20 = await getAmounts(
"in",
provider,
amountInZeta[0],
zrc20,
wzeta,
router,
UniswapV2Router02
);

return amountInZRC20[0];
};
44 changes: 34 additions & 10 deletions packages/localnet/src/handleOnRevertEVM.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";
import { ethers, NonceManager } from "ethers";
import { NonceManager } from "ethers";

export const handleOnRevertEVM = async ({
revertOptions,
asset,
amount,
err,
provider,
tss,
protocolContracts,
isGas,
token,
exitOnError = false,
}: {
revertOptions: any;
err: any;
asset: any;
amount: any;
provider: any;
tss: any;
protocolContracts: any;
isGas: boolean;
token: string;
exitOnError: boolean;
}) => {
const callOnRevert = revertOptions[1];
const revertAddress = revertOptions[0];
const revertMessage = revertOptions[3];
const revertContext = {
asset: ethers.ZeroAddress,
amount: 0,
asset,
amount,
revertMessage,
};
if (callOnRevert) {
Expand All @@ -34,9 +42,26 @@ export const handleOnRevertEVM = async ({
)})`
);
(tss as NonceManager).reset();
const tx = await protocolContracts.gatewayEVM
.connect(tss)
.executeRevert(revertAddress, "0x", revertContext, deployOpts);
let tx;
if (isGas) {
tx = await protocolContracts.gatewayEVM
.connect(tss)
.executeRevert(revertAddress, "0x", revertContext, {
value: amount,
deployOpts,
});
} else {
tx = await protocolContracts.custody
.connect(tss)
.withdrawAndRevert(
revertAddress,
token,
amount,
"0x",
revertContext,
deployOpts
);
}
await tx.wait();
log("EVM", "Gateway: successfully called onRevert");
const logs = await provider.getLogs({
Expand All @@ -47,10 +72,9 @@ export const handleOnRevertEVM = async ({
logs.forEach((data: any) => {
log("EVM", `Event from onRevert: ${JSON.stringify(data)}`);
});
} catch (err) {
const error = `Gateway: Call onRevert failed: ${err}`;
logErr("EVM", error);
if (exitOnError) throw new Error(error);
} catch (err: any) {
logErr("EVM", `Gateway: Call onRevert failed`, err);
if (exitOnError) throw new Error(err);
}
} else {
const error = `Tx reverted without callOnRevert: ${err}`;
Expand Down
8 changes: 6 additions & 2 deletions packages/localnet/src/handleOnRevertZEVM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { logErr } from "./log";
export const handleOnRevertZEVM = async ({
revertOptions,
err,
asset,
amount,
provider,
tss,
log,
Expand All @@ -14,6 +16,8 @@ export const handleOnRevertZEVM = async ({
}: {
revertOptions: any;
err: any;
asset: any;
amount: any;
provider: any;
fungibleModuleSigner: any;
tss: NonceManager;
Expand All @@ -26,8 +30,8 @@ export const handleOnRevertZEVM = async ({
const revertAddress = revertOptions[0];
const revertMessage = revertOptions[3];
const revertContext = {
asset: ethers.ZeroAddress,
amount: 0,
asset,
amount,
revertMessage,
};

Expand Down
2 changes: 2 additions & 0 deletions packages/localnet/src/handleOnZEVMCalled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export const handleOnZEVMCalled = async ({
return await handleOnRevertZEVM({
revertOptions,
err,
amount: 0,
asset: ethers.ZeroAddress,
provider,
fungibleModuleSigner,
tss,
Expand Down
27 changes: 14 additions & 13 deletions packages/localnet/src/handleOnZEVMWithdrawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,26 @@ export const handleOnZEVMWithdrawn = async ({
exitOnError: boolean;
}) => {
log("ZetaChain", "Gateway: 'Withdrawn' event emitted");
const getERC20ByZRC20 = (zrc20: string) => {
const foreignCoin = foreignCoins.find(
(coin: any) => coin.zrc20_contract_address === zrc20
);
if (!foreignCoin) {
logErr("EVM", `Foreign coin not found for ZRC20 address: ${zrc20}`);
return;
}
return foreignCoin.asset;
};
const zrc20 = args[3];
const amount = args[4];
try {
const receiver = args[2];
const zrc20 = args[3];
const amount = args[4];
const message = args[7];
(tss as NonceManager).reset();
const zrc20Contract = new ethers.Contract(zrc20, ZRC20.abi, deployer);
const coinType = await zrc20Contract.COIN_TYPE();
const isGasToken = coinType === 1n;
const isERC20orZETA = coinType === 2n;
const getERC20ByZRC20 = (zrc20: string) => {
const foreignCoin = foreignCoins.find(
(coin: any) => coin.zrc20_contract_address === zrc20
);
if (!foreignCoin) {
logErr("EVM", `Foreign coin not found for ZRC20 address: ${zrc20}`);
return;
}
return foreignCoin.asset;
};
if (message !== "0x") {
// The message is not empty, so this is a withdrawAndCall operation
log("EVM", `Calling ${receiver} with message ${message}`);
Expand All @@ -54,7 +54,6 @@ export const handleOnZEVMWithdrawn = async ({
.execute(receiver, message, { value: amount, ...deployOpts });
await executeTx.wait();
} else {
console.log("!!!");
const erc20 = getERC20ByZRC20(zrc20);
const executeTx = await protocolContracts.custody
.connect(tss)
Expand Down Expand Up @@ -102,6 +101,8 @@ export const handleOnZEVMWithdrawn = async ({
err,
provider,
tss,
asset: getERC20ByZRC20(zrc20),
amount,
log,
fungibleModuleSigner,
protocolContracts,
Expand Down
Loading

0 comments on commit e2ec956

Please sign in to comment.