Skip to content

Commit

Permalink
Added handling of ERC-20 deposits (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Sep 10, 2024
1 parent 049fc3a commit 48b839c
Show file tree
Hide file tree
Showing 11 changed files with 617 additions and 395 deletions.
142 changes: 142 additions & 0 deletions packages/localnet/src/createToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { ethers } from "ethers";
import * as ZRC20 from "@zetachain/protocol-contracts/abi/ZRC20.sol/ZRC20.json";
import { deployOpts } from "./deployOpts";
import * as TestERC20 from "@zetachain/protocol-contracts/abi/TestERC20.sol/TestERC20.json";

export const createToken = async ({
fungibleModuleSigner,
deployer,
systemContract,
gatewayZEVM,
foreignCoins,
custody,
tss,
uniswapFactoryInstance,
wzeta,
uniswapRouterInstance,
symbol,
isGasToken = false,
}: {
fungibleModuleSigner: any;
deployer: ethers.Signer;
systemContract: any;
gatewayZEVM: any;
foreignCoins: any[];
custody: ethers.BaseContract;
tss: ethers.Signer;
uniswapFactoryInstance: ethers.BaseContract;
wzeta: ethers.BaseContract;
uniswapRouterInstance: ethers.BaseContract;
symbol: string;
isGasToken: boolean;
}) => {
let erc20;

const zrc20Factory = new ethers.ContractFactory(
ZRC20.abi,
ZRC20.bytecode,
deployer
);
const zrc20 = await zrc20Factory
.connect(fungibleModuleSigner)
.deploy(
`ZRC-20 ${symbol}`,
`ZRC20${symbol}`,
18,
1,
1,
1,
systemContract.target,
gatewayZEVM.target,
deployOpts
);

if (!isGasToken) {
const erc20Factory = new ethers.ContractFactory(
TestERC20.abi,
TestERC20.bytecode,
deployer
);
erc20 = await erc20Factory.deploy(symbol, symbol, deployOpts);
const erc20Decimals = await (erc20 as any).connect(deployer).decimals();

await (erc20 as any)
.connect(deployer)
.approve(custody.target, ethers.MaxUint256, deployOpts);

await (erc20 as any)
.connect(deployer)
.mint(
custody.target,
ethers.parseUnits("1000000", erc20Decimals),
deployOpts
);
await (erc20 as any)
.connect(deployer)
.mint(
await deployer.getAddress(),
ethers.parseUnits("1000000", erc20Decimals),
deployOpts
);
await (custody as any).connect(tss).whitelist(erc20.target, deployOpts);

(systemContract as any)
.connect(fungibleModuleSigner)
.setGasCoinZRC20(1, zrc20.target);
(systemContract as any).connect(fungibleModuleSigner).setGasPrice(1, 1);
}

foreignCoins.push({
zrc20_contract_address: zrc20.target,
asset: isGasToken ? "" : (erc20 as any).target,
foreign_chain_id: "1",
decimals: 18,
name: `ZetaChain ZRC-20 ${symbol}`,
symbol: `${symbol}.ETH`,
coin_type: isGasToken ? "Gas" : "ERC20",
gas_limit: null,
paused: null,
liquidity_cap: null,
});

(zrc20 as any).deposit(
await deployer.getAddress(),
ethers.parseEther("1000"),
deployOpts
);

await (wzeta as any)
.connect(deployer)
.deposit({ value: ethers.parseEther("1000"), ...deployOpts });

await (uniswapFactoryInstance as any).createPair(
zrc20.target,
wzeta.target,
deployOpts
);
await (zrc20 as any)
.connect(deployer)
.approve(
uniswapRouterInstance.getAddress(),
ethers.parseEther("1000"),
deployOpts
);
await (wzeta as any)
.connect(deployer)
.approve(
uniswapRouterInstance.getAddress(),
ethers.parseEther("1000"),
deployOpts
);
await (uniswapRouterInstance as any).addLiquidity(
zrc20.target,
wzeta.target,
ethers.parseUnits("100", await (zrc20 as any).decimals()), // Amount of ZRC-20
ethers.parseUnits("100", await (wzeta as any).decimals()), // Amount of ZETA
ethers.parseUnits("90", await (zrc20 as any).decimals()), // Min amount of ZRC-20 to add (slippage tolerance)
ethers.parseUnits("90", await (wzeta as any).decimals()), // Min amount of ZETA to add (slippage tolerance)
await deployer.getAddress(),
Math.floor(Date.now() / 1000) + 60 * 10, // Deadline
deployOpts
);
};
4 changes: 4 additions & 0 deletions packages/localnet/src/deployOpts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const deployOpts = {
gasPrice: 10000000000,
gasLimit: 6721975,
};
63 changes: 63 additions & 0 deletions packages/localnet/src/handleOnEVMCalled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ethers, NonceManager } from "ethers";
import { handleOnRevertEVM } from "./handleOnRevertEVM";
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";

// event Called(address indexed sender, address indexed receiver, bytes payload, RevertOptions revertOptions);
export const handleOnEVMCalled = async ({
tss,
provider,
protocolContracts,
args,
deployer,
fungibleModuleSigner,
}: {
tss: any;
provider: ethers.JsonRpcProvider;
protocolContracts: any;
args: any;
deployer: any;
fungibleModuleSigner: any;
}) => {
log("EVM", "Gateway: 'Called' event emitted");
try {
const receiver = args[1];
const message = args[2];

(deployer as NonceManager).reset();
const context = {
origin: protocolContracts.gatewayZEVM.target,
sender: await fungibleModuleSigner.getAddress(),
chainID: 1,
};
const zrc20 = protocolContracts.zrc20Eth.target;
log(
"ZetaChain",
`Universal contract ${receiver} executing onCrossChainCall (context: ${JSON.stringify(
context
)}), zrc20: ${zrc20}, amount: 0, message: ${message})`
);
const executeTx = await protocolContracts.gatewayZEVM
.connect(fungibleModuleSigner)
.execute(context, zrc20, 0, receiver, message, deployOpts);
await executeTx.wait();
const logs = await provider.getLogs({
address: receiver,
fromBlock: "latest",
});

logs.forEach((data) => {
log("ZetaChain", `Event from onCrossChainCall: ${JSON.stringify(data)}`);
});
} catch (e: any) {
logErr("ZetaChain", `Error executing onCrossChainCall: ${e}`);
const revertOptions = args[3];
await handleOnRevertEVM({
revertOptions,
err: e,
tss,
provider,
protocolContracts,
});
}
};
94 changes: 94 additions & 0 deletions packages/localnet/src/handleOnEVMDeposited.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { ethers, NonceManager } from "ethers";
import { handleOnRevertEVM } from "./handleOnRevertEVM";
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";

// event Deposited(address indexed sender, address indexed receiver, uint256 amount, address asset, bytes payload, RevertOptions revertOptions);
export const handleOnEVMDeposited = async ({
tss,
provider,
protocolContracts,
args,
deployer,
fungibleModuleSigner,
foreignCoins,
}: {
tss: any;
provider: ethers.JsonRpcProvider;
protocolContracts: any;
args: any;
deployer: any;
fungibleModuleSigner: any;
foreignCoins: any[];
}) => {
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 zrc20 = foreignCoin.zrc20_contract_address;

const context = {
origin: protocolContracts.gatewayZEVM.target,
sender: await fungibleModuleSigner.getAddress(),
chainID: 1,
};

// If message is not empty, execute depositAndCall
if (message !== "0x") {
log(
"ZetaChain",
`Universal contract ${receiver} executing onCrossChainCall (context: ${JSON.stringify(
context
)}), zrc20: ${zrc20}, amount: ${amount}, message: ${message})`
);

const tx = await protocolContracts.gatewayZEVM
.connect(fungibleModuleSigner)
.depositAndCall(context, zrc20, amount, receiver, message, deployOpts);

await tx.wait();
const logs = await provider.getLogs({
address: receiver,
fromBlock: "latest",
});

logs.forEach((data) => {
log(
"ZetaChain",
`Event from onCrossChainCall: ${JSON.stringify(data)}`
);
});
} else {
const tx = await protocolContracts.gatewayZEVM
.connect(fungibleModuleSigner)
.deposit(zrc20, amount, receiver, deployOpts);
await tx.wait();
log("ZetaChain", `Deposited ${amount} of ${zrc20} tokens to ${receiver}`);
}
} catch (e: any) {
logErr("ZetaChain", `Error depositing: ${e}`);
const revertOptions = args[5];
await handleOnRevertEVM({
revertOptions,
err: e,
tss,
provider,
protocolContracts,
});
}
};
54 changes: 54 additions & 0 deletions packages/localnet/src/handleOnRevertEVM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { log, logErr } from "./log";
import { deployOpts } from "./deployOpts";
import { ethers, NonceManager } from "ethers";

export const handleOnRevertEVM = async ({
revertOptions,
err,
provider,
tss,
protocolContracts,
}: {
revertOptions: any;
err: any;
provider: any;
tss: any;
protocolContracts: any;
}) => {
const callOnRevert = revertOptions[1];
const revertAddress = revertOptions[0];
const revertMessage = revertOptions[3];
const revertContext = {
asset: ethers.ZeroAddress,
amount: 0,
revertMessage,
};
if (callOnRevert) {
try {
log(
"EVM",
`Contract ${revertAddress} executing onRevert (context: ${JSON.stringify(
revertContext
)})`
);
(tss as NonceManager).reset();
const tx = await protocolContracts.gatewayEVM
.connect(tss)
.executeRevert(revertAddress, "0x", revertContext, deployOpts);
await tx.wait();
log("EVM", "Gateway: successfully called onRevert");
const logs = await provider.getLogs({
address: revertAddress,
fromBlock: "latest",
});

logs.forEach((data: any) => {
log("EVM", `Event from onRevert: ${JSON.stringify(data)}`);
});
} catch (e: any) {
logErr("EVM", `Gateway: Call onRevert failed: ${e}`);
}
} else {
logErr("EVM", `Tx reverted without callOnRevert: ${err}`);
}
};
34 changes: 34 additions & 0 deletions packages/localnet/src/handleOnRevertZEVM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ethers, NonceManager } from "ethers";

export const handleOnRevertZEVM = async (
revertOptions: any,
err: any,
tss: NonceManager,
log: (chain: "EVM" | "ZetaChain", ...messages: string[]) => void,
protocolContracts: any,
deployOpts: any
) => {
const callOnRevert = revertOptions[1];
const revertAddress = revertOptions[0];
const revertMessage = revertOptions[3];
const revertContext = {
asset: ethers.ZeroAddress,
amount: 0,
revertMessage,
};

if (callOnRevert) {
log("ZetaChain", "Gateway: calling executeRevert");
try {
tss.reset();
await protocolContracts.gatewayZEVM
.connect(tss)
.executeRevert(revertAddress, revertContext, deployOpts);
log("ZetaChain", "Gateway: Call onRevert success");
} catch (e) {
log("ZetaChain", `Gateway: Call onRevert failed: ${e}`);
}
} else {
log("ZetaChain", "Tx reverted without callOnRevert: ", err);
}
};
Loading

0 comments on commit 48b839c

Please sign in to comment.