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

Erc20flash mint #407

Open
wants to merge 37 commits into
base: main
Choose a base branch
from

Conversation

Ifechukwudaniel
Copy link

Resolves #355

PR Checklist

  • Tests
  • Documentation

Copy link

netlify bot commented Nov 14, 2024

Deploy Preview for contracts-stylus canceled.

Name Link
🔨 Latest commit a7c0160
🔍 Latest deploy log https://app.netlify.com/sites/contracts-stylus/deploys/67357c1f782e3e00081ecd3f

Copy link

netlify bot commented Nov 14, 2024

Deploy Preview for contracts-stylus ready!

Name Link
🔨 Latest commit 46461b3
🔍 Latest deploy log https://app.netlify.com/sites/contracts-stylus/deploys/6748e2aa43568e0008739164
😎 Deploy Preview https://deploy-preview-407--contracts-stylus.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@Ifechukwudaniel
Copy link
Author

This is my initial draft

Copy link
Collaborator

@0xNeshi 0xNeshi left a comment

Choose a reason for hiding this comment

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

Hi @Ifechukwudaniel!
Thanks for the contribution!

The PR overall looks good, let's polish it a bit


use stylus_sdk::stylus_proc::sol_interface;

sol_interface! {
Copy link
Collaborator

@0xNeshi 0xNeshi Nov 14, 2024

Choose a reason for hiding this comment

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

@bidzyyys @qalisander @ggonzalez94 on the one hand this should be defined inside contracts/src/token/erc20/extensions/flashmint.rs, similar to how IERC721Receiver is defined inside contracts/src/token/erc721/mod.rs

But on the other hand, Solidity version treats this trait (interface) as a standalone file within contracts/interfaces, similar to how we've done by placing Erc1155Receiver in the same folder as Erc1155.

What is the convention we want to use here?

EDIT: actually both examples I linked to (IERC721Receiver and Erc1155Receiver) technically use a similar convention, the difference being the latter's module is just placed in a separate file.

Copy link
Author

@Ifechukwudaniel Ifechukwudaniel Nov 15, 2024

Choose a reason for hiding this comment

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

Making a whole file is not necessary and is not the way it has been done in the codebase. Instead, what I did was add it to the utility of ERC-20, as it is isolated to ERC-20

Copy link
Collaborator

Choose a reason for hiding this comment

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

To follow the existing convention, place IERC3156FlashBorrower inside contracts/src/token/erc20/extensions/flashmint.rs (see the EDIT in my previous message).

contracts/src/flashloan/borrower.rs Outdated Show resolved Hide resolved
contracts/src/token/erc20/extensions/flashmint.rs Outdated Show resolved Hide resolved
contracts/src/token/erc20/extensions/flashmint.rs Outdated Show resolved Hide resolved
pub fn _flash_fee_receiver(&self) -> Address {
Address::ZERO
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing unit tests

pub fn _flash_fee_receiver(&self) -> Address {
Address::ZERO
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing integration tests

pub fn _flash_fee_receiver(&self) -> Address {
Address::ZERO
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Update CHANGELOG

pub fn _flash_fee_receiver(&self) -> Address {
Address::ZERO
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Ifechukwudaniel
Copy link
Author

@0xNeshi what do you think about the new implementation for the extension

cargo build --release --target wasm32-unknown-unknown -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort

export RPC_URL=http://localhost:8547

cargo test --features std,e2e --test "*"
cargo test --features std,e2e --test "erc20-flashloan" -- --nocapture
Copy link
Collaborator

Choose a reason for hiding this comment

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

revert changes to this file

Copy link
Author

Choose a reason for hiding this comment

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

yeah will do that

@@ -6,7 +6,10 @@ use alloc::vec::Vec;
use alloy_primitives::{Address, FixedBytes, U256};
use openzeppelin_stylus::{
token::erc20::{
extensions::{capped, Capped, Erc20Metadata, IErc20Burnable},
extensions::{
capped, flashmint::IERC3156FlashLender, Capped, Erc20Metadata,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What purpose does the trait import serve here?

uint256 private _totalSupply;
uint256 private _flash_fee_amount;
address private _flash_fee_receiver_address;
constructor(address flash_fee_receiver_address_, uint256 flash_fee_amount_) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Format the contract to make it a bit more readable, you can use Remix to do that

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/interfaces/IERC3156FlashBorrower.sol";


contract ERC3156FlashBorrowerMock is IERC3156FlashBorrower {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Format the contract


if (data.length > 0) {
// WARNING: This code is for testing purposes only! Do not use.
Address.functionCall(token, data);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you may be missing some imports (Address, IERC20), did you verify the tests pass?

) -> Result<bool, Self::Error>;
}

/// A Permit error.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Still wrong docs, please resolve a comment only after it's been fully addressed


}

const RETURN_VALUE: B256 =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, let's make the const clearer by default:

const RETURN_VALUE: [u8; 32] = keccak_const::Keccak256::new()
    .update("ERC3156FlashBorrower.onFlashLoan".as_bytes())
    .finalize();

@@ -0,0 +1,326 @@
//! Optional Flashloan extension of the ERC-20 standard.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy/paste docs from the Solidity contract version, and update as appropriate for Stylus (follow the convention set in other contracts)

Copy link
Collaborator

Choose a reason for hiding this comment

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


sol_storage! {
pub struct Erc20Flashmint {
uint256 _flash_fee_amount;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you need this contract state?

}

sol_storage! {
pub struct Erc20Flashmint {
Copy link
Collaborator

@0xNeshi 0xNeshi Nov 18, 2024

Choose a reason for hiding this comment

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

I think a different (and arguably simpler) approach is warranted for implementing the flash mint contract.

The ERC20FlashMint contract extends ERC20 with 3 new functions, and by the spec, it does not introduce any new state nor override existing ERC20 functions (see Solidity version).
In other words, this means that this contract (extension) should inherit and include all ERC20 functionality, and just add these 3 new functions.

In short, it should be enough to simply implement the IERC3156FlashLender trait for Erc20 (similar to IErc20Burnable).

  • Implement the IERC3156FlashLender trait for Erc20.
  • Copy/paste the logic from the Solidity version linked above.
  • Create meaningful unit and integration tests (use Solidity version tests as inspiration).
  • Remove redundant code/comments that are spread throughout the PR.
  • Update comment docs to follow already set conventions, e.g. use SafeErc20 as inspiration (some may consider comments as overly detailed, but it is better to be clear when building potentially widely used OS code). You can generally copy/paste Solidity contract docs, and update where necessary.
  • Add missing ADOC content for the contract, e.g. see Erc20Permit docs.
  • Format Solidity code according to conventions set in other contracts.
  • Format Rust code and markdown according to GUIDELINES.md.
  • (optional) add benchmarks (see ./benches)

What do you think about this?

Copy link
Author

Choose a reason for hiding this comment

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

it does implement the internal functions _flash_fee and _flash_fee_receiver so they can be overridden and support extensibility, any suggestion of an approach I can use for that

Copy link
Author

@Ifechukwudaniel Ifechukwudaniel Nov 18, 2024

Choose a reason for hiding this comment

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

The reason why i went with that current approach is because of those internal functions @0xNeshi

Copy link
Collaborator

@0xNeshi 0xNeshi Nov 19, 2024

Choose a reason for hiding this comment

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

_flash_fee and _flash_fee_receiver are not part of the spec (see EIP-3156), so they are not required to exist.

Also, since inheritance is still limited in Stylus, overriding does not work as you might expect in other more object-oriented languages.

In short, _flash_fee and _flash_fee_receiver don't make as much sense in Stylus as they do in Solidity.

Copy link
Collaborator

@bidzyyys bidzyyys left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution @Ifechukwudaniel!
I left some comments. Do you want to solve them by yourself or should we handle them?

@@ -0,0 +1,326 @@
//! Optional Flashloan extension of the ERC-20 standard.
Copy link
Collaborator

Choose a reason for hiding this comment

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

contracts/src/token/erc20/utils/borrower.rs Show resolved Hide resolved
examples/erc20-flashmint/src/lib.rs Outdated Show resolved Hide resolved
examples/erc20-flashmint/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe there should be module erc20::extensions::flashmint with borrower.rs file.

examples/erc20-flashmint/src/lib.rs Outdated Show resolved Hide resolved
examples/erc20-flashmint/src/lib.rs Outdated Show resolved Hide resolved
}
}

impl Erc20FlashMintExample {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need additional logic in the example? Is it possible to include this logic in FlashMint Extension code?

Copy link
Author

Choose a reason for hiding this comment

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

for cases where we users would like to implement custom borrower and receiver functions

let Erc20Flashmint::totalSupplyReturn { totalSupply } =
contract.totalSupply().call().await?;

// let Erc20Flashmint::maxFlashLoanReturn { maxLoan: testMaxLoan } =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unused logic, please make it work.

Ok(())
}

// #[e2e::test]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unused test, please make it work.

@Ifechukwudaniel
Copy link
Author

I can handle them

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.

[Feature]: ERC20FlashMint extension
3 participants