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

Swap ERC-20 support, update to toolkit v5 #99

Merged
merged 8 commits into from
Dec 21, 2023
Merged

Conversation

fadeev
Copy link
Member

@fadeev fadeev commented Dec 13, 2023

Looks like the swap example is not compatible with swapping to/from ERC-20s.

Currently, we're swapping the whole incoming amount to target ZRC-20.

uint256 outputAmount = SwapHelperLib._doSwap(
systemContract.wZetaContractAddress(),
systemContract.uniswapv2FactoryAddress(),
systemContract.uniswapv2Router02Address(),
zrc20,
amount,
targetTokenAddress,
0
);

Then we check if the gas token is not equal to the target ZRC-20.

(address gasZRC20, uint256 gasFee) = IZRC20(targetTokenAddress)
.withdrawGasFee();
if (gasZRC20 != targetTokenAddress) revert WrongGasContract();
if (gasFee >= outputAmount) revert NotEnoughToPayGasFee();

When we want to swap tMATIC for USDC (Goerli), the target ZRC-20 (USDC) will not match the gas token (gETH), so the swap will fail. This is issue 1.

If I understand correctly, for the swap to succeed the contract has to have uint256 gasFee amount of address gasZRC20 to cover the gas costs when withdrawing the target ZRC-20. Right now we're only swapping to the target ZRC-20 and the contract does not have gas tokens. This is issue 2.

@andresaiello please, let me know if you agree that both issues are valid.

To solve these issues I'm proposing we do the following:

  1. Determine the required gas fee amount using IZRC20(targetTokenAddress).withdrawGasFee();
  2. Swap to get the required gas fee amount using SwapHelperLib.swapTokensForExactTokens
  3. Swap the rest to get the target amount using SwapHelperLib._doSwap.

For this to work I've added a swapTokensForExactTokens helper:

https://github.com/zeta-chain/toolkit/blob/bc629af99832689697b2c45a6560b1b5df382d66/contracts/SwapHelperLib.sol#L127-L164

The problem I'm facing right now is that when I try to interact with a contract:

https://zetachain-athens-3.blockscout.com/address/0x7F665c57796E19a6A938f2B6dDbc5Ac5576c5De7

npx hardhat interact --contract 0x7F665c57796E19a6A938f2B6dDbc5Ac5576c5De7 --amount 1 --network mumbai_testnet --target-token 0x0cbe0dF132a6c6B4a2974Fa1b7Fb953CF0Cc798a --recipient 0x2cD3D070aE1BD365909dD859d29F387AA96911e1
🔑 Using account: 0x2cD3D070aE1BD365909dD859d29F387AA96911e1

🚀 Successfully broadcasted a token transfer transaction on mumbai_testnet network.
📝 Transaction hash: 0xfcc13472c25b7ae1b1aa0dd29514266f8b3183b60daa97aa902737e529d1eae4

The contract isn't swapping into the gas token gETH (the balance is 0), but swaps fine to the destination token USDC.

I need help figuring out why swapTokensForExactTokens doesn't work as intended.

Copy link

socket-security bot commented Dec 13, 2023

Updated dependencies detected. Learn more about Socket for GitHub ↗︎

Packages Version New capabilities Transitives Size Publisher
@zetachain/toolkit 4.0.0...5.0.0 environment +9/-16 4.46 MB

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

Alternatively, we can use a different approach were we swap all incoming tokens into the gas token using _doSwap, deduct the gas fee amount, and swap the rest again using _doSwap into the target token.

@andresaiello please, let me know if you think this solution is ok or if we should try to fix swapTokensForExactTokens.

@andresaiello
Copy link
Collaborator

@fadeev I agree with the approach. This contract was designed before ERC20 support. I like the first approach

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

@fadeev I agree with the approach. This contract was designed before ERC20 support. I like the first approach

Then I need help debugging swapTokensForExactTokens 😬

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

I've tried the second approach (swapping all to gas tokens, deducting the gas fee, then swapping to destination token), and I'm getting the same result (tMATIC -> Goerli USDC, I'm getting 0 gETH and a proper amount of USDC).

        (address gasZRC20, uint256 gasFee) = IZRC20(targetTokenAddress)
            .withdrawGasFee();

        uint256 gasOutput = SwapHelperLib._doSwap(
            systemContract.wZetaContractAddress(),
            systemContract.uniswapv2FactoryAddress(),
            systemContract.uniswapv2Router02Address(),
            zrc20,
            amount,
            gasZRC20,
            0
        );

        SwapHelperLib._doSwap(
            systemContract.wZetaContractAddress(),
            systemContract.uniswapv2FactoryAddress(),
            systemContract.uniswapv2Router02Address(),
            gasZRC20,
            gasOutput - gasFee,
            targetTokenAddress,
            0
        );

https://zetachain-athens-3.blockscout.com/address/0x6bf6ae7d9763eb1e42d8c7e9D31A5C23Be192a70

@andresaiello
Copy link
Collaborator

mmmm targetTokenAddress is USDC? I guess to calculate the fee should be the native token of destination chain

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

mmmm targetTokenAddress is USDC? I guess to calculate the fee should be the native token of destination chain

@andresaiello you mean IZRC20(targetTokenAddress).withdrawGasFee()? When target token is USDC it returns ZRC-20 gETH address, so I don't think there is a problem there.

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

@andresaiello
Copy link
Collaborator

are we sure gasFee is returning >0 ?

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

are we sure gasFee is returning >0 ?

It is returning a positive integer, yes.

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

are we sure gasFee is returning >0 ?

For example, for Goerli USDC the gas fee is 3500000.

https://zetachain-athens-3.blockscout.com/address/0xDf5C8854C2A9822e15470547e139451af892124F

Screenshot 2023-12-13 at 22 00 24

@fadeev
Copy link
Member Author

fadeev commented Dec 13, 2023

I'm just using public variables to output some debug information as events don't show up in the explorer.

@fadeev
Copy link
Member Author

fadeev commented Dec 14, 2023

When I manually increase gasFee to a larger amount, I can see that swapTokensForExactTokens actually swaps tokens just fine. The problem is with IZRC20(targetTokenAddress).withdraw(recipientAddress, outputAmount), it's throwing an error:

0x8e03367d2cd6ac000e3186c86fe5939daa98ed544ffbf6238ee31000ab056470: 80001 → 7001: PendingRevert (contract call failed: method 'depositAndCall', contract '0xEdf1c3275d13489aCdC6cD6eD246E72458B8795B': execution reverted: ret 0x10bad147: evm transaction execution failed)

outputAmount - gasFee
);
IZRC20(gasZRC20).approve(gasZRC20, gasFee);
// IZRC20(targetTokenAddress).withdraw(recipientAddress, outputAmount);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why you don't need the abi.encode? will check if is the same without... IZRC20(targetTokenAddress).withdraw(abi.encodePacked(recipientAddress), outputAmount);

@fadeev
Copy link
Member Author

fadeev commented Dec 17, 2023

Amounts 0 will be the same you sent as input
Then your line 70 is returning 0

@andresaiello I don't think that's the case.

Here's an example of me swapping 10 tMATIC for Goerli USDC. The state of the contract is a result of a single transaction.

https://zetachain-athens-3.blockscout.com/address/0x7c732C6A614ecaD95B2953C624d39825f58C6F5C

Screenshot 2023-12-17 at 11 31 06

I’m using public variables to debug the contract. inputForGas (amount[0] of swapTokensForExactTokens) is not the same as input (which is amount). inputForGas actually represents the amount of tMATIC that was swapped to cover gas fees. You can see that the contract successfully swapped to get gasFee amount of gETH to cover gas. Please, ignore Blockscout's bug where it shows 0.00 gETH, you can check the actual value through the ZRC-20 gETH contract.

Screenshot 2023-12-17 at 11 35 06

At the end of the day, the contract has gasFee worth of gETH to cover withdraw fee as well as outputAmount worth of USDC that I'm trying to withdraw. And the withdraw process fails.

@fadeev
Copy link
Member Author

fadeev commented Dec 17, 2023

In a separate contract I've also tried setting gasFee manually and the contract had up to 0.1 gETH for fees, but withdraw still kept failing, so it's probably not due to lack of tokens for gas.

@fadeev
Copy link
Member Author

fadeev commented Dec 17, 2023

Could it be that it's getting confused because USDC has uses different decimal amount that gas tokens?

Copy link

socket-security bot commented Dec 18, 2023

👍 Dependency issues cleared. Learn more about Socket for GitHub ↗︎

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

@fadeev
Copy link
Member Author

fadeev commented Dec 19, 2023

@SocketSecurity ignore @types/[email protected]

@fadeev
Copy link
Member Author

fadeev commented Dec 21, 2023

@andresaiello please, review.

@fadeev fadeev marked this pull request as ready for review December 21, 2023 14:30
@fadeev fadeev changed the title Swap ERC-20 support Swap ERC-20 support, update to toolkit v5 Dec 21, 2023
@fadeev fadeev merged commit 859ce99 into main Dec 21, 2023
5 checks passed
@fadeev fadeev deleted the swap-erc20-support branch December 21, 2023 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants