-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added handling of ERC-20 deposits (#32)
- Loading branch information
Showing
11 changed files
with
617 additions
and
395 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const deployOpts = { | ||
gasPrice: 10000000000, | ||
gasLimit: 6721975, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
}; |
Oops, something went wrong.