Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added handling of ERC-20 deposits #32

Merged
merged 11 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading