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

Avoid duplicate calls to onLockupStreamWithdrawn() hook when sender == recipient #822

Closed
smol-ninja opened this issue Feb 10, 2024 Discussed in #804 · 7 comments
Closed
Assignees
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: bug Something isn't working. work: clear Sense-categorize-respond. The relationship between cause and effect is clear.

Comments

@smol-ninja
Copy link
Member

smol-ninja commented Feb 10, 2024

As discussed in #804

Originally posted by smol-ninja January 26, 2024
A note to the reader: The following doesn't affect the deployed version of Sablier contracts.

Context

The recent PR: Make withdraw function callable by any account enables anyone to call withdraw() on any stream. Each withdrawal attempts to call onLockupStreamWithdrawn() on both sender and recipient if they are contracts.

Problem

We use the same function name onLockupStreamWithdrawn() in both ISablierV2Recipient and ISablierV2Sender. This leads to duplicate calls on this function when withdraw() is triggered by a third party and the sender and recipient are the same contract addresses.

SablierV2Lockup.sol#L278C1-L297C10

if (msg.sender != recipient && recipient.code.length > 0) {
    try ISablierV2Recipient(recipient).onLockupStreamWithdrawn({.. /* snip */ ..}) { } catch { }
}

if (msg.sender != sender && sender.code.length > 0) {
    try ISablierV2Sender(sender).onLockupStreamWithdrawn({.. /* snip */ ..}) { } catch { }
}

Solution

Add a a check whether sender and recipient are same.

if (msg.sender != recipient && recipient.code.length > 0) {
    try ISablierV2Recipient(recipient).onLockupStreamWithdrawn({.. /* snip */ ..}) { } catch { }
}

if (sender != recipient && msg.sender != sender && sender.code.length > 0) {
    try ISablierV2Sender(sender).onLockupStreamWithdrawn({.. /* snip */ ..}) { } catch { }
}
@smol-ninja smol-ninja self-assigned this Feb 10, 2024
@smol-ninja smol-ninja added type: refactor Change that neither fixes a bug nor adds a feature. priority: 1 This is important. It should be dealt with shortly. work: clear Sense-categorize-respond. The relationship between cause and effect is clear. effort: medium Default level of effort. labels Feb 10, 2024
@smol-ninja smol-ninja changed the title Rename "onLockupStreamWithdrawn()" hooks to avoid duplicate calls Rename onLockupStreamWithdrawn() hooks to avoid duplicate calls Feb 10, 2024
@smol-ninja smol-ninja moved this to 🏗 In progress in Lockup 1.2.0 Feb 14, 2024
@smol-ninja
Copy link
Member Author

smol-ninja commented Feb 15, 2024

I am facing with a few problems while writing test cases for this.

  1. when sender == recipient, how to write a test to not expect a call? A test which should detect the above issue.

  2. New concrete tests are needed for hook calls. Incorporating them into withdraw would be difficult because the basis of the withdraw tree structure is based on whether the withdrawal address is stream recipient or not. In the new test, we can have a structure like this:

    1. When the caller is unknown
      • if sender != recipient
        • both hook calls should be expected
      • if sender == recipient
        • only one hook call should be expected
    2. When the caller is the sender
      • if sender != recipient
        • recipient hook calls should be expected
      • if sender == recipient
        • no hook call should be expected
    3. When the caller is the recipient
      • if sender != recipient
        • sender hook calls should be expected
      • if sender == recipient
        • no hook call should be expected

Any thoughts @andreivladbrg?

@smol-ninja smol-ninja added type: bug Something isn't working. and removed type: refactor Change that neither fixes a bug nor adds a feature. labels Feb 16, 2024
@PaulRBerg
Copy link
Member

@smol-ninja all this is not a matter of internal knowledge at Sablier. You should ask your questions in the Foundry community forums (GitHub discussions, issues, and their Telegram chat groups).

how to write a test to not expect a call?

don't think this is possible but again, let's ask the Foundry team

only one hook call should be expected

By passing the count parameter to vm.expectCall, see:

@smol-ninja
Copy link
Member Author

Thank you for your reply @PaulRBerg.

You should ask your questions in the Foundry community forums

Totally, I agree. I will ask there.

By passing the count parameter to vm.expectCall

Oh yes!

@andreivladbrg
Copy link
Member

andreivladbrg commented Feb 16, 2024

Any thoughts @andreivladbrg?

for 1. PRB already answered, i.e. count can be used

regarding 2. i think it would be better to have these branches instead, as they are more concise, clearer and shorter:

  1. given sender == recipient
    • when caller unknown
      • only one hook call should be expected
    • when the caller is the sender and the recipient
      • no hook call should be expected
  2. given sender != recipient
    • when caller unknown
      • both hook calls should be expected
    • when the caller is the sender
      • recipient hook calls should be expected
    • when the caller is the recipient
      • sender hook calls should be expected

all this is not a matter of internal knowledge at Sablier. You should ask your questions in the Foundry community forums

@PaulRBerg are you sure about this statement? this might only be ok for the question "how to write a test to not expect a call?" but, the rest, what does Foundry have to do with how we write our tree structure for the withdraw function?

@PaulRBerg
Copy link
Member

PaulRBerg commented Feb 16, 2024

@PaulRBerg are you sure about this statement?

Yes. It's always a good idea to talk to the community and correct errors in our understanding. That's the default approach we should take.

https://twitter.com/PaulRBerg/status/1335282362968633344

what does Foundry have to do with how we write our tree structure for the withdraw function?

I wasn't referring to that - I was referring to his questions about how to expect calls

@PaulRBerg
Copy link
Member

PaulRBerg commented Feb 16, 2024

My wording was bad, though - I shouldn't have said "all this". Sorry for that.

@smol-ninja
Copy link
Member Author

regarding 2. i think it would be better to have these branches instead

I like this more.

My wording was bad, though - I shouldn't have said "all this". Sorry for that

No problem. I understood your point.

@smol-ninja smol-ninja changed the title Rename onLockupStreamWithdrawn() hooks to avoid duplicate calls Avoid duplicate calls to onLockupStreamWithdrawn() hook when sender and recipient are same Feb 19, 2024
@smol-ninja smol-ninja changed the title Avoid duplicate calls to onLockupStreamWithdrawn() hook when sender and recipient are same Avoid duplicate calls to onLockupStreamWithdrawn() hook when sender == recipient Feb 19, 2024
@smol-ninja smol-ninja moved this from 🏗 In progress to 👀 In review in Lockup 1.2.0 Feb 19, 2024
@smol-ninja smol-ninja moved this from 👀 In review to ✅ Done in Lockup 1.2.0 Feb 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: bug Something isn't working. work: clear Sense-categorize-respond. The relationship between cause and effect is clear.
Projects
No open projects
Status: Done
Development

No branches or pull requests

3 participants