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

bug: cannot inherit from contract that makes external call #79

Open
akirillo opened this issue Oct 26, 2023 · 2 comments
Open

bug: cannot inherit from contract that makes external call #79

akirillo opened this issue Oct 26, 2023 · 2 comments

Comments

@akirillo
Copy link

It's quite unwieldy to inherit from a contract that makes a call to another contract, stemming from the fact that the parent contract isn't the entrypoint and thus doesn't implement TopLevelStorage when it is being inherited from.

I've managed to work around this by manually implementing CallContext (and StaticCallContext, in my case) for the parent contract, but this feels hacky and easy to misappropriate.

From what I gather, the TopLevelStorage trait is leveraged in this way to flush the storage cache in reentrant calls, but the inheritance situation I've laid out isn't really one of reentrancy...

I certainly lack some context on how the TopLevelStorage trait is implemented and intended to be leveraged, but this feels like it should be possible without enabling the reentrant flag

@rachel-bousfield
Copy link
Contributor

This is something we intend to make nicer in an upcoming refactor, but right now this is possible if you use generics. We talk about it a bit in the Rust SDK Guide here, and here's an example contract that uses it. In summary, we can combine #[borrow] and TopLevelStorage to enable external calls in library code.

The reason for this is as follows: if you only have a mutable reference to substorage when making a call, you might alias other pieces of state if you call another contract (provided reentrancy is enabled, since that contract might call back into yours).

By restricting potentially reentrant calls to instances where you have a mutable reference to top level storage, Rust's type system will prevent you from patterns that alias storage. Library code needs to provide the same guarantee, for which we have SDK primitives like #[borrow] and Call::new_in() to make life easier. Hope this helps!

@akirillo
Copy link
Author

Thanks for the tip - after a while I implemented this instead of my hacky approach described above, it works quite nicely for now.

If it's of any interest, here's what it looks like: https://github.com/renegade-fi/renegade-contracts/blob/main/contracts-stylus/src/contracts/darkpool.rs

With a testing contract that inherits from that one here: https://github.com/renegade-fi/renegade-contracts/blob/main/contracts-stylus/src/contracts/test_contracts/darkpool_test_contract.rs

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

No branches or pull requests

2 participants