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

MultiSwapForge Contracts for GasEstimation #42

Merged
merged 11 commits into from
Feb 16, 2024

Conversation

salmanferrum
Copy link
Contributor

@salmanferrum salmanferrum commented Jan 30, 2024

Overview

This pull request introduces changes to the contract architecture to accommodate gas estimation functionality and separates the production contracts from gas estimation contracts. The main contracts involved are MultiSwapForgeContract, ForgeFundManager, FiberRouter, and FundManager.

Contracts Overview

  • Production Contracts:

    • FiberRouter & FundManager: These contracts facilitate real-time transactions in the production environment.
  • Gas Estimation Contracts:

    • MultiSwapForgeContract: Simulates withdrawals and reverts with gas estimation, featuring estimateWithdrawSigned and estimateWithdrawSignedAndSwapOneInch functions.
    • ForgeFundManager: A gas estimation replica of FundManager, featuring only withdrawSigned and withdrawSignedAndSwapOneInch functions.

Production Contracts Workflow

  • Real-time transactions leverage the production contracts (FiberRouter and FundManager).

Gas Estimation Contracts Workflow

  • Gas estimation for withdrawals follows a specific flow to simulate transactions without interacting with the production contracts:
    1. FiberBackend calls estimateGasForWithdrawSigned in MultiSwapForgeContract.
    2. MultiSwapForgeContract's estimateGasForWithdrawSigned function invokes the withdrawSigned function of ForgeFundManager.
    3. After simulating the transaction, the function call reverts the transaction to simulate the gas consumption.
    4. estimateGas.estimateGasForWithdrawSigned() function returns the estimated gas for simulating the withdrawSigned function.

These changes enhance the gas estimation capabilities of the system and segregate the gas estimation logic from the production transactions.

Changes

  • Introduction of gas estimation contracts (MultiSwapForgeContract and ForgeFundManager).
  • Modification of workflows to ensure production contracts are utilized for real transactions, while gas estimation contracts are dedicated to estimating gas usage.
  • Changed the Upgradeable-Contracts folder name to multiswap-contracts.
  • Added the forge directory in scripts directory to accomodate the multiswapForge / forgeFundManager contracts deployments.

Additional Notes

  • A clear distinction is maintained between production and gas estimation contracts to prevent inadvertent interactions.
  • This architectural update prepares the system for efficient gas estimation handling during withdrawal simulations.

@@ -107,7 +106,7 @@ contract FiberRouter is Ownable, TokenReceivable {
@notice Sets the fund manager contract.
@param _pool The fund manager
*/
function setPool(address _pool) external onlyOwner {
function setPool(address _pool) public virtual onlyOwner {
Copy link

Choose a reason for hiding this comment

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

why public virtual?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing the FiberRouter contract’s function access from external to public and introducing the virtual keyword is for inheritance. This is visible in MultiswapForge.sol, where functions like setPool are overridden. Without making the parent class functions public virtual, the compiler was giving errors while overriding in MultiswapForge, which is why I had to make this change in FiberRouter.

@@ -119,7 +118,7 @@ contract FiberRouter is Ownable, TokenReceivable {
@notice Sets the gas wallet address.
@param _gasWallet The wallet which pays for the funds on withdrawal
*/
function setGasWallet(address payable _gasWallet) external onlyOwner {
function setGasWallet(address payable _gasWallet) external virtual onlyOwner {
Copy link

Choose a reason for hiding this comment

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

why?

@@ -132,7 +131,8 @@ contract FiberRouter is Ownable, TokenReceivable {
@param _newRouterAddress The new Router Address of oneInch
*/
function setOneInchAggregatorRouter(address _newRouterAddress)
external
public
Copy link

Choose a reason for hiding this comment

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

why?

@@ -412,7 +413,7 @@ function swapAndCrossOneInch(
bytes32 salt,
uint256 expiry,
bytes memory multiSignature
) external nonReentrant {
) public virtual nonReentrant {
Copy link

Choose a reason for hiding this comment

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

Why public? also the same for all other methods

import "foundry-contracts/contracts/common/FerrumDeployer.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract ForgeFundManager is SigCheckable, WithAdmin, TokenReceivable {
Copy link

Choose a reason for hiding this comment

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

ForgeFundManager is FundManager This is the only thing needed. Similar to the router no need to rewrite everything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure let me make this adjustment

address targetAddress,
bytes32 withdrawalData
) external override payable nonReentrant {
revert("Function is reverted");
Copy link

Choose a reason for hiding this comment

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

Maybe Not supported error mesage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

}

// Inherit withdrawSigned function
function withdrawSigned(
Copy link

Choose a reason for hiding this comment

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

Rename to withdrawSignedForGasEstimateion and overwrite the withdrawSign to revert.

Then create a new function called estimateGasForWithdrawSigned, where it will use address(this).call(...) to call the above function. This function will not fail, because it is using .call so you on the backend or frontend, you can use ethres.estimateGas(tx) to estimate the gas correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me take a good look into this approach and test it again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@naiemk I have pushed the latest commit with the changes mentioned in this PR comments,

Moreover, the change specifically for estimation functions are implemented in a way that the original withdrawSigned function overriden and reverted in Forge contract, however, I have renamed the withdrawSigned to withdrawSignedForGasEstimateion and called the super.withdrawSigned to simulate the withdraw flow with a revert at the end. Then, I have created estimateGasForWithdrawSigned that calls the withdrawSignedForGasEstimateion using address(this).call(). On backend (deployAndTest.js), I have called contract.estimateGas.estimateGasForWithdrawSigned and now it is returning correct gasFee.

);

// Calculate the gas used during the execution
uint256 gasUsed = initialGas - gasleft();
Copy link

Choose a reason for hiding this comment

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

Unused. no need

// Calculate the gas used during the execution
uint256 gasUsed = initialGas - gasleft();

// Revert with a message indicating gas usage
Copy link

Choose a reason for hiding this comment

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

Why messge indicating gas use? No easy way to get this message in the client so this is useless

@salmanferrum
Copy link
Contributor Author

@naiemk changes have been made as per the suggestion.

Changes introduced to the latest commit are mentioned below:

  • Removed all unnecessary functions from ForgeFundManager & MultiSwapForge except withdrawSigned & withdrawSignedSwapAndCrossOneInch.
  • Changed the visibility modifier of all FiberRouter & FundManager functions back to external (removing virtual) except for FiberRouter'swithdrawSigned & withdrawSignedSwapAndCrossOneInch
  • addSigner() function needed to be changed to public from external in FundManager since we are calling it from forgeFundManager super.addSigner() so the visibility needs to be public.
  • Constructor of ForgeFundManager will set the Signer with a test signer address that will be used only for getting the gas fee from forge. The private key of test signer will be added in the commented form.

bytes32 salt,
uint256 expiry,
bytes memory multiSignature
) external {
Copy link

Choose a reason for hiding this comment

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

This function does not revert. Can be used to extract funds.

revert("Not Supported");
}
// This function is only used specifically for GasEstimation & Simulation of withdrawSigned
function withdrawSignedForGasEstimation(
Copy link

Choose a reason for hiding this comment

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

Function must revert.

* @note PrivateKey of the Test Signer to be added here for Devs Reference
*/
constructor() {
super.addSigner(0x);
Copy link

Choose a reason for hiding this comment

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

What's the point of adding 0x as signer. Add a real signer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Certainly! I have requested a test wallet, and once it is received, I will include the test signer's address and private key (to be added in comments) and then proceed with the commit.

@@ -80,7 +79,7 @@ contract FundManager is SigCheckable, WithAdmin, TokenReceivable {
router = _router;
}

function addSigner(address _signer) external onlyOwner {
function addSigner(address _signer) public onlyOwner {
Copy link

Choose a reason for hiding this comment

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

no need

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, I can create a dedicated setSigner function within the ForgeFundManager contract, performing the same task of adding the test signer to the signers array. This approach allows the function to be conveniently invoked through the constructor.

    function setSigner(address _signer) public onlyOwner {
        require(_signer != address(0), "Bad signer");
        signers[_signer] = true;
    }

P.S: We cannot directly inherit an external function from a parent contract into a child contract unless the visibility of that function is modified to at least public

Copy link

Choose a reason for hiding this comment

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

nm my bad. this will be needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@naiemk new changes pushed:

0cab7ba

Please review the final changes. Thanks

Comment on lines 125 to 128
bytes4 selector = bytes4(
keccak256(
"withdrawSignedAndSwapOneInchForGasEstimation(address,uint256,uint256,address,address,bytes,bytes32,uint256,bytes)"
)
Copy link

Choose a reason for hiding this comment

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

Can use this.withdrawSignedAndSwapOneInchForGasEstimation.selector

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented.

@zikriya zikriya merged commit 14e0a1a into ferrumnet:develop Feb 16, 2024
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.

None yet

3 participants