Skip to content

Commit

Permalink
erc20 revert support
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev committed Oct 17, 2024
1 parent 3fbd3c0 commit a0be032
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 12 deletions.
2 changes: 1 addition & 1 deletion packages/localnet/src/createToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const createToken = async ({

foreignCoins.push({
zrc20_contract_address: zrc20.target,
asset: isGasToken ? ethers.ZeroAddress : (erc20 as any).target,
asset: isGasToken ? "" : (erc20 as any).target,
foreign_chain_id: "1",
decimals: 18,
name: `ZetaChain ZRC-20 ${symbol}`,
Expand Down
2 changes: 2 additions & 0 deletions packages/localnet/src/handleOnEVMCalled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export const handleOnEVMCalled = async ({
amount: 0,
asset: ethers.ZeroAddress,
tss,
isGas: true,
token: "",
provider,
protocolContracts,
exitOnError,
Expand Down
108 changes: 106 additions & 2 deletions packages/localnet/src/handleOnEVMDeposited.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 Down Expand Up @@ -88,16 +89,119 @@ export const handleOnEVMDeposited = async ({
const [gasZRC20, gasFee] = await zrc20Contract.withdrawGasFeeWithGasLimit(
revertOptions[4]
);
const amountReverted = amount - gasFee;
let revertAmount;
let revertGasFee = gasFee;
let isGas = true;
let token = null;
if (zrc20 !== gasZRC20) {
token = foreignCoins.find(
(coin) => coin.zrc20_contract_address === zrc20
)?.asset;
console.log("token!", token);
isGas = false;
const uniswapV2Router = new ethers.Contract(
protocolContracts.uniswapRouterInstance.target,
UniswapV2Router02.abi,
deployer
);
deployer.reset();
const approvalTx = await zrc20Contract.approve(
protocolContracts.uniswapRouterInstance.target,
amount
);
await approvalTx.wait();

const path = [zrc20, protocolContracts.wzeta.target, gasZRC20];

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

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

const amountInZeta = await getAmounts(
"in",
provider,
gasFee,
protocolContracts.wzeta.target,
gasZRC20,
protocolContracts.uniswapRouterInstance.target,
UniswapV2Router02
);

const amountInZRC20 = await getAmounts(
"in",
provider,
amountInZeta[0],
zrc20,
protocolContracts.wzeta.target,
protocolContracts.uniswapRouterInstance.target,
UniswapV2Router02
);

revertGasFee = amountInZRC20[0];

await swapTx.wait();
} catch (swapError) {
logErr("ZetaChain", `Error performing swap on Uniswap: ${swapError}`);
}
}
revertAmount = amount - revertGasFee;
return await handleOnRevertEVM({
revertOptions,
asset,
amount: amountReverted,
amount: revertAmount,
err,
tss,
isGas,
token: "",
provider,
protocolContracts,
exitOnError,
});
}
};

/**
* 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;
};
46 changes: 38 additions & 8 deletions packages/localnet/src/handleOnRevertEVM.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";
import { ethers, NonceManager } from "ethers";
import { NonceManager } from "ethers";

export const handleOnRevertEVM = async ({
revertOptions,
Expand All @@ -10,6 +10,8 @@ export const handleOnRevertEVM = async ({
provider,
tss,
protocolContracts,
isGas,
token,
exitOnError = false,
}: {
revertOptions: any;
Expand All @@ -19,6 +21,8 @@ export const handleOnRevertEVM = async ({
provider: any;
tss: any;
protocolContracts: any;
isGas: boolean;
token: string;
exitOnError: boolean;
}) => {
const callOnRevert = revertOptions[1];
Expand All @@ -38,9 +42,36 @@ export const handleOnRevertEVM = async ({
)})`
);
(tss as NonceManager).reset();
const tx = await protocolContracts.gatewayEVM
.connect(tss)
.executeRevert(revertAddress, "0x", revertContext, deployOpts);
let tx;
if (isGas) {
console.log(protocolContracts.gatewayEVM.connect(tss).functions);
tx = await protocolContracts.gatewayEVM
.connect(tss)
.executeRevert(revertAddress, "0x", revertContext, {
value: amount,
deployOpts,
});
} else {
console.log(
"!!!",
revertAddress,
token,
amount,
"0x",
revertContext,
deployOpts
);
tx = await protocolContracts.custody // this is failing
.connect(tss)
.withdrawAndRevert(
revertAddress,
token,
amount,
"",
revertContext,
deployOpts
);
}
await tx.wait();
log("EVM", "Gateway: successfully called onRevert");
const logs = await provider.getLogs({
Expand All @@ -51,10 +82,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
6 changes: 5 additions & 1 deletion packages/localnet/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ const FUNGIBLE_MODULE_ADDRESS = "0x735b14BB79463307AAcBED86DAf3322B1e6226aB";

const foreignCoins: any[] = [];

// A hack to make BigInt serializable
(BigInt as any).prototype["toJSON"] = function () {
return this.toString();
};

let protocolContracts: any;
let deployer: Signer;
let tss: Signer;
Expand Down Expand Up @@ -195,7 +200,6 @@ const deployProtocolContracts = async (

const { uniswapFactoryInstance, uniswapRouterInstance } =
await prepareUniswap(deployer, tss, wzeta);

const { systemContract, gatewayZEVM } = await prepareZetaChain(
deployer,
wzeta.target,
Expand Down
1 change: 1 addition & 0 deletions packages/tasks/src/localnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const localnet = async (args: any) => {
ZETA: addr.zetaZetaChain,
"Fungible Module": addr.fungibleModuleZetaChain,
"System Contract": addr.sytemContractZetaChain,
"Uniswap Router": addr.uniswapRouter,
...addr.foreignCoins.reduce((acc: any, coin: any) => {
acc[`ZRC-20 ${coin.symbol}`] = coin.zrc20_contract_address;
return acc;
Expand Down

0 comments on commit a0be032

Please sign in to comment.