-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
230 additions
and
0 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,230 @@ | ||
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"; | ||
|
||
export const handleOnEVMDepositedAndCalled = async ({ | ||
tss, | ||
provider, | ||
protocolContracts, | ||
args, | ||
deployer, | ||
fungibleModuleSigner, | ||
foreignCoins, | ||
exitOnError = false, | ||
chainID, | ||
chain, | ||
gatewayEVM, | ||
custody, | ||
}: { | ||
tss: any; | ||
provider: ethers.JsonRpcProvider; | ||
protocolContracts: any; | ||
args: any; | ||
deployer: any; | ||
fungibleModuleSigner: any; | ||
foreignCoins: any[]; | ||
exitOnError: boolean; | ||
chainID: string; | ||
chain: string; | ||
gatewayEVM: any; | ||
custody: any; | ||
}) => { | ||
log(chain, "Gateway: 'DepositedAndCalled' event emitted"); | ||
const sender = args[0]; | ||
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" && coin.foreign_chain_id === chainID | ||
); | ||
} 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; | ||
try { | ||
const context = { | ||
origin: sender, | ||
sender: await fungibleModuleSigner.getAddress(), | ||
chainID: chainID, | ||
}; | ||
|
||
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)}`); | ||
}); | ||
} 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, | ||
exitOnError, | ||
chain, | ||
gatewayEVM, | ||
custody, | ||
sender, | ||
}); | ||
} | ||
}; | ||
|
||
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]; | ||
}; |