From 3086a52dd16f0d951c5d337bc8dd65145576d308 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:47:38 +0000 Subject: [PATCH 1/4] Initial plan From ffb6eed08ca14e959259b02a4732289dbab9a464 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:52:36 +0000 Subject: [PATCH 2/4] Add documentation on flush() function history for Forwarder.sol line 72 Co-authored-by: barathcj <5459428+barathcj@users.noreply.github.com> --- FLUSH_FUNCTION_HISTORY.md | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 FLUSH_FUNCTION_HISTORY.md diff --git a/FLUSH_FUNCTION_HISTORY.md b/FLUSH_FUNCTION_HISTORY.md new file mode 100644 index 0000000..00b5629 --- /dev/null +++ b/FLUSH_FUNCTION_HISTORY.md @@ -0,0 +1,78 @@ +# History of flush() Call on Forwarder.sol Line 72 + +## Question +When was line 72 in Forwarder.sol introduced, where we call the flush function when we receive funds? + +## Answer + +**Line 72** in `contracts/Forwarder.sol`, which contains `flush();` within the `fallback()` function, was **modified** on: + +**Date:** January 7, 2021 at 17:23:59 PST +**Commit:** c5ae39ad15345ee5f1ed90e89bfbda18b84f605d +**Author:** Mark Toda +**Commit Message:** Fix ForwarderDeposited event data +**Ticket:** BG-28248 + +### Important Note +The functionality of calling `flush()` when receiving funds has been present **since the initial commit** (fb24dd6), but the **calling mechanism changed** in commit c5ae39ad from `this.flush()` (external call) to `flush()` (internal call). + +## Context + +This line is part of the `fallback()` function that gets called when data is sent to the contract but does not match any other function: + +```solidity +fallback() external payable { + flush(); +} +``` + +## Complete History + +### Original Implementation (Initial Commit - August 3, 2020) +The `fallback()` function with a call to flush was present from the very first commit: + +**Date:** August 3, 2020 at 12:08:47 PST +**Commit:** fb24dd6ac2fb3c70dad0266995dd4d4b5605bcae +**Author:** Mark Toda + +```solidity +fallback() external payable { + this.flush(); // External call using 'this.' +} +``` + +### Current Implementation (Commit c5ae39ad - January 7, 2021) +The commit changed the calling mechanism: + +```solidity +fallback() external payable { + flush(); // Internal call without 'this.' +} +``` + +## Why the Change Was Made + +The change was made to fix an issue with the `ForwarderDeposited` event. The problem was: + +1. When `flush()` was marked as `external`, it could only be called externally +2. Calling `this.flush()` forced an external CALL operation +3. External CALLs change the `msg.sender` to the most recent address in the call stack +4. This meant the event always showed the forwarder itself as the sender, not the actual original sender + +### Technical Details + +The execution flow before the fix was: +- A (sender) → B (proxy) → DELEGATECALL C (implementation receive()) +- Then: B (proxy) → CALL B (flush()) → DELEGATECALL C (flush()) +- That CALL changed `msg.sender` to B instead of A + +The fix: +1. Changed `flush()` from `external` to `public` so it can be called both externally and internally +2. Removed the `this.` syntax to make internal calls +3. This preserves the correct `msg.sender` in the `ForwarderDeposited` event + +## Summary + +The functionality of calling `flush()` when receiving funds through the `fallback()` function was **originally introduced on August 3, 2020** in the initial commit (fb24dd6) by Mark Toda. + +However, the **current form of the call** on line 72 was **modified on January 7, 2021** (commit c5ae39ad) by the same author. This change modified the calling convention from an external call (`this.flush()`) to an internal call (`flush()`) to properly preserve the original sender's address in the emitted `ForwarderDeposited` events. From ff73e36615949c4a929a84a060f949c2c03361ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:53:43 +0000 Subject: [PATCH 3/4] Fix technical inaccuracies in flush() function documentation Co-authored-by: barathcj <5459428+barathcj@users.noreply.github.com> --- FLUSH_FUNCTION_HISTORY.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/FLUSH_FUNCTION_HISTORY.md b/FLUSH_FUNCTION_HISTORY.md index 00b5629..3bcedc3 100644 --- a/FLUSH_FUNCTION_HISTORY.md +++ b/FLUSH_FUNCTION_HISTORY.md @@ -18,7 +18,7 @@ The functionality of calling `flush()` when receiving funds has been present **s ## Context -This line is part of the `fallback()` function that gets called when data is sent to the contract but does not match any other function: +This line is part of the `fallback()` function that gets called when the contract receives a call with data that doesn't match any other function signature: ```solidity fallback() external payable { @@ -26,6 +26,8 @@ fallback() external payable { } ``` +Note: There's also a `receive()` function that calls `flush()` when plain Ether is sent without any data. + ## Complete History ### Original Implementation (Initial Commit - August 3, 2020) @@ -61,14 +63,14 @@ The change was made to fix an issue with the `ForwarderDeposited` event. The pro ### Technical Details -The execution flow before the fix was: -- A (sender) → B (proxy) → DELEGATECALL C (implementation receive()) -- Then: B (proxy) → CALL B (flush()) → DELEGATECALL C (flush()) -- That CALL changed `msg.sender` to B instead of A +The execution flow before the fix: +- When funds were sent to the forwarder proxy: A (sender) → B (proxy) → DELEGATECALL C (implementation fallback()/receive()) +- Then within the fallback/receive: B (proxy) → CALL B (flush()) → DELEGATECALL C (flush()) +- That external CALL changed `msg.sender` to B (the proxy) instead of A (the original sender) The fix: 1. Changed `flush()` from `external` to `public` so it can be called both externally and internally -2. Removed the `this.` syntax to make internal calls +2. Removed the `this.` syntax to make internal calls instead of external calls 3. This preserves the correct `msg.sender` in the `ForwarderDeposited` event ## Summary From 158fcce94a2fcdc4851b15f5a90fc053937e9065 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:54:46 +0000 Subject: [PATCH 4/4] Improve technical accuracy of execution flow explanation Co-authored-by: barathcj <5459428+barathcj@users.noreply.github.com> --- FLUSH_FUNCTION_HISTORY.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/FLUSH_FUNCTION_HISTORY.md b/FLUSH_FUNCTION_HISTORY.md index 3bcedc3..d665dad 100644 --- a/FLUSH_FUNCTION_HISTORY.md +++ b/FLUSH_FUNCTION_HISTORY.md @@ -57,21 +57,21 @@ fallback() external payable { The change was made to fix an issue with the `ForwarderDeposited` event. The problem was: 1. When `flush()` was marked as `external`, it could only be called externally -2. Calling `this.flush()` forced an external CALL operation -3. External CALLs change the `msg.sender` to the most recent address in the call stack +2. Calling `this.flush()` from within the contract forced an external CALL operation +3. External CALLs change the `msg.sender` to the calling contract's address 4. This meant the event always showed the forwarder itself as the sender, not the actual original sender ### Technical Details -The execution flow before the fix: -- When funds were sent to the forwarder proxy: A (sender) → B (proxy) → DELEGATECALL C (implementation fallback()/receive()) -- Then within the fallback/receive: B (proxy) → CALL B (flush()) → DELEGATECALL C (flush()) -- That external CALL changed `msg.sender` to B (the proxy) instead of A (the original sender) +The execution flow in a proxy-based architecture: +- When funds were sent to the forwarder: A (sender) → B (proxy) → DELEGATECALL to C (implementation's fallback()/receive()) +- With external call: The fallback/receive() then triggered: B (proxy) → external CALL to B's flush() +- This external CALL changed `msg.sender` from A to B (the proxy address) The fix: 1. Changed `flush()` from `external` to `public` so it can be called both externally and internally 2. Removed the `this.` syntax to make internal calls instead of external calls -3. This preserves the correct `msg.sender` in the `ForwarderDeposited` event +3. Internal calls preserve the original `msg.sender`, fixing the event emission ## Summary