-
Notifications
You must be signed in to change notification settings - Fork 19
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
feat: SafeErc20 utility #289
base: main
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for contracts-stylus canceled.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @0xNeshi and thanks for your contribution!
Appreciate it!
Hard to tell for me right now what kind of design should be the best for SafeErc20
with current state of the stylus-sdk.
IMHO we can try to build a working example first with end-to-end tests. Then later dive into optimization if it will make sense.
There is no good interoperability between rust struct, that defines a contract in stylus and an external call to that contract (deployed at some address) as it is in solidity.
So we can try to add Erc20
solidity abi into sol_interface!
macro like IERC721Receiver example.
And use call contract's type generated by sol_interface!
as a first argument like it is in solidity SafeErc20 example.
But we should process boolean responses from underlying Erc20
contract differently as it is specified in the solidity counterpart.
Then we can use IErc20 trait to create wrapped Erc20 contract. It should route most calls externally using the Call::new(..)
api from the stylus-sdk (like Erc721Receiver example)
How do you feel about this plan?
data: &[u8], | ||
) -> Result<(), Error> { | ||
match RawCall::new() | ||
.gas(gas_left()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really clear to me whether explicitly setting the gas remaining is necessary, so I set it just in case.
token: Address, | ||
data: &[u8], | ||
) -> Result<(), Error> { | ||
match RawCall::new() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand, RawCall is unsafe and should be called with flush_storage_cache
when reentrancy is enabled. Let me know how you'd like to do this.
token: Address, | ||
data: &[u8], | ||
) -> Result<(), Error> { | ||
match RawCall::new() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I opted for RawCall
here is that it enables us to set the return data size to just 32 bytes, which assumes the underlying ERC20 returns a boolean. It also does not make assumptions about the return value of the underlying ERC20.
No problem! |
Thanks @0xNeshi for your patience, we should have more time later this week! |
Resolves #279
I think I did everything that is required. Let me know if I missed something.
If you prefer a different code style, naming convention and/or anything similar, let me know and I'll update (e.g. check out how tests for this are organized).
Used Solidity version SafeERC20 as inspiration for both implementation and tests.
PR Checklist