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

erc20<->cosmos coin amount conversion #19

Merged
merged 22 commits into from
Aug 7, 2024
Merged

Conversation

sangier
Copy link
Contributor

@sangier sangier commented Aug 6, 2024

Implements a library for the conversion of ERC20 amounts to cosmos coin amounts.

closes #5

src/utils/ERC20CosmosCoinAmountConversion.sol Outdated Show resolved Hide resolved
src/utils/ERC20CosmosCoinAmountConversion.sol Outdated Show resolved Hide resolved
}
}

contract ERC20CosmosCoinAmountConversionTest is Test {
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be worth using both fuzz and invariants testing here: https://book.getfoundry.sh/forge/advanced-testing

Copy link
Contributor Author

@sangier sangier Aug 6, 2024

Choose a reason for hiding this comment

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

It's absolutely a great Idea! Only wonder, do we want to address this now? or later on? If you guys needs this lib asap to complete your workflows, I can address this in a later on PR and refactor the testing file. My primary goal was to ensure it works properly :)

Copy link
Member

Choose a reason for hiding this comment

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

Let's address this now. We don't require this lib asap since our mock ERC20's size doesn't cause issues. So it is ok to take time to make sure we design and test this properly.

uint256 factor;
// Case ERC20 decimals are bigger than cosmos decimals
if (tokenDecimals > DEFAULT_COSMOS_DECIMALS) {
factor = 10 ** (tokenDecimals - DEFAULT_COSMOS_DECIMALS);
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there any scenarios where this can cause any overflows?

Copy link
Contributor Author

@sangier sangier Aug 6, 2024

Choose a reason for hiding this comment

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

Solidity version starting from 0.8 supports built-in underflows and overflows checks for arithmetic operations with automatic reverting. We should be good!

// Case ERC20 decimals < DEFAULT_COSMOS_DECIMALS
// Note we need to amplify the decimals
factor = 10 ** (DEFAULT_COSMOS_DECIMALS - tokenDecimals);
temp_convertedAmount = amount * factor;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, could there be an overflow here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As Above

import { ERC20CosmosCoinAmountConversion } from "../src/utils/ERC20CosmosCoinAmountConversion.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// Discuss - Do we want to move mock contract to a mock folder?
Copy link
Contributor

Choose a reason for hiding this comment

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

There are already some mocks, might make sense to keep mocks separate, yes.
Maybe we can also reuse the ERC20 mocks that exist there to some extent also.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, please move or reuse the other ERC20 mock if possible.

* - Converted Amount: 1000000000000000001 / 10^12 = 1000000 (Cosmos coins).
* - Remainder: 1000000000000000001 % 10^12 = 1 (Wei remaining).
*/
function testConvertMockERC20TokenAmountToCosmosCoin_1() public view {
Copy link
Contributor

Choose a reason for hiding this comment

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

It might change a bit if you decide upon using the fuzzing framework as many of these tests could be written as a single test, but just a general comment: I personally prefer the test functions to be more clearly named what scenario/sub-scenario they are testing, rather than 1, 2, 3 etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely agree. Reasons for this naming are here

Copy link
Member

Choose a reason for hiding this comment

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

Yes, please rename the tests. It'd be good to refactor these to use table test pattern if possible. You can see me do this here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Does the fuzz tests with fixtures do the same @srdtrk?

https://book.getfoundry.sh/forge/fuzz-testing#fuzz-test-fixtures

assertEq(convertedAmount, expectedAmount, "Conversion mismatch for higher decimals");
}

function testConvertCosmosCoinAmountToERC20_LessThanSixDecimals() public {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be good to try to cover a few more failure scenarios: ideally triggering all the reverts we have if possible :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed here

Copy link
Member

Choose a reason for hiding this comment

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

I think we can add more failure scenarios. But we don't need to hit everything as long as we add a TODO for this and address it in the future

Copy link
Member

@srdtrk srdtrk left a comment

Choose a reason for hiding this comment

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

Looks amazing @sangier, thanks for the superb work. And don't worry, we have enough time to address the PR reviews since this is not required for e2es to pass or be written.

This PR would be needed to deploy this to a testnet and use WETH.

function _getERC20TokenDecimals(address tokenAddress) internal view returns (uint8) {
// Input validation
// TODO Add custom error?
require(tokenAddress != address(0), "Address cannot be the zero address");
Copy link
Member

Choose a reason for hiding this comment

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

Introduce custom errors. For now, we will use reverts but we can change to require in the next version of solidity. Do this for all require please

require(tokenAddress != address(0), "Address cannot be the zero address");
// TODO Reason about this check - Remember it as a good practice but what real protections give us?
// TODO Add custom error?
require(tokenAddress != address(this), "Address cannot be the contract itself");
Copy link
Member

Choose a reason for hiding this comment

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

probably we can remove this for now

// Note if we want to allow different cosmos decimals, we need to handle that here too
// Note that tokenAddress input validations are executed in the _getERC20TokenDecimals function
uint8 tokenDecimals = _getERC20TokenDecimals(tokenAddress);
// TODO write an ADR for this?
Copy link
Member

Choose a reason for hiding this comment

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

Repo is not mature enough for ADRs yet. So we can remove this.

src/utils/ERC20CosmosCoinAmountConversion.sol Outdated Show resolved Hide resolved
src/utils/ERC20CosmosCoinAmountConversion.sol Outdated Show resolved Hide resolved
import { ERC20CosmosCoinAmountConversion } from "../src/utils/ERC20CosmosCoinAmountConversion.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// Discuss - Do we want to move mock contract to a mock folder?
Copy link
Member

Choose a reason for hiding this comment

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

Yes, please move or reuse the other ERC20 mock if possible.

}
}

contract ERC20CosmosCoinAmountConversionTest is Test {
Copy link
Member

Choose a reason for hiding this comment

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

Let's address this now. We don't require this lib asap since our mock ERC20's size doesn't cause issues. So it is ok to take time to make sure we design and test this properly.

* - Converted Amount: 1000000000000000001 / 10^12 = 1000000 (Cosmos coins).
* - Remainder: 1000000000000000001 % 10^12 = 1 (Wei remaining).
*/
function testConvertMockERC20TokenAmountToCosmosCoin_1() public view {
Copy link
Member

Choose a reason for hiding this comment

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

Yes, please rename the tests. It'd be good to refactor these to use table test pattern if possible. You can see me do this here.

assertEq(convertedAmount, expectedAmount, "Conversion mismatch for higher decimals");
}

function testConvertCosmosCoinAmountToERC20_LessThanSixDecimals() public {
Copy link
Member

Choose a reason for hiding this comment

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

I think we can add more failure scenarios. But we don't need to hit everything as long as we add a TODO for this and address it in the future

test/ERC20CosmosCoinAmountConversionTest.t.sol Outdated Show resolved Hide resolved
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.25;

interface IIErrors {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created this file to manage errors that are common for all the contracts. Is the name ok or shall we call it IICommonErrors?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know if we need this yet, I'd prefer not to have it yet. So maybe just use errors specific to your library

Copy link
Member

@srdtrk srdtrk left a comment

Choose a reason for hiding this comment

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

Looks good. Please address my comments, and then we can merge this

Copy link
Member

Choose a reason for hiding this comment

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

Rename this file to use capital letters SdkCoinTest.t.sol

src/utils/SdkCoin.sol Show resolved Hide resolved
src/utils/SdkCoin.sol Outdated Show resolved Hide resolved
src/utils/SdkCoin.sol Outdated Show resolved Hide resolved
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { IISdkCoinErrors } from "../errors/IISdkCoinErrors.sol";

library SdkCoin {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
library SdkCoin {
library SdkCoin is IISdkCoinErrors {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would be great but seems this is not possible for libraries!
Screenshot 2024-08-07 alle 12 24 43

src/utils/SdkCoin.sol Show resolved Hide resolved
@srdtrk srdtrk merged commit cf5d185 into main Aug 7, 2024
7 checks passed
@srdtrk srdtrk deleted the stefano/erc20-coin-conversion branch August 7, 2024 11:29
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.

ERC20 and Cosmos coin conversion
3 participants