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

Enhance approveAndCall logic #84

Merged
merged 16 commits into from
Apr 8, 2020
Merged

Conversation

elenadimitrova
Copy link
Contributor

@elenadimitrova elenadimitrova commented Apr 1, 2020

In the current implementation of approveTokenAndCallContract, the amount of tokens approved overwrites any existing amount of the same token and spender. Here we add logic to restore the original approved amount after the approveTokenAndCallContract call in both (ApprovedTransfer and TransferManager) and emit a new dedicated event with the following definition:

event ApprovedAndCalledContract(
        address indexed wallet,
        address indexed to,
        address spender,
        address indexed token,
        uint256 amountApproved,
        uint256 amountSpent,
        bytes data
    );

This PR also enables a different contract address for the approved spender and the called contract for TransferManager module, analogous to behaviour already implemented for ApprovedTransfer in #73

Also I needed SafeMath in the BaseTransfer contract so pushing it there meant this was provisioned to both ApprovedTransfer and TransferManager.

We also decommission the config.modules.TokenTransfer config entry.

@elenadimitrova elenadimitrova self-assigned this Apr 1, 2020
@elenadimitrova elenadimitrova marked this pull request as ready for review April 1, 2020 14:13
@elenadimitrova elenadimitrova changed the base branch from develop to release/1.6.0 April 2, 2020 07:26
@elenadimitrova elenadimitrova changed the base branch from release/1.6.0 to develop April 2, 2020 07:31
@elenadimitrova elenadimitrova force-pushed the feature/flash-approve-and-call branch 3 times, most recently from 3d51aab to 2416398 Compare April 3, 2020 10:44
@elenadimitrova elenadimitrova removed the request for review from juniset April 3, 2020 13:55
@elenadimitrova elenadimitrova changed the title Add flash approveAndCall logic Enhance approveAndCall logic Apr 6, 2020
Copy link
Member

@juniset juniset left a comment

Choose a reason for hiding this comment

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

We should add the deployment of the new TransferManager in 7_upgrade_1_6.js.

uint256 currentAllowance = ERC20(_token).allowance(address(_wallet), _spender);

// Approve the desired amount
bytes memory methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, _amount);
Copy link
Member

Choose a reason for hiding this comment

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

I think we should here approve currentAllowance + _amount so if unusedAllowance == currentAllowance after the contract call (which should be the majority of the cases) then we don't need to make the call on L121 and we save gas.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem with that is that the new allowance amount left can be different to the original allowance.

Copy link

Choose a reason for hiding this comment

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

Also we would be allowing that TX to use your existing allowance, so should set explicit amount before the call, make the call, and then reset to old allowance

Copy link
Contributor Author

@elenadimitrova elenadimitrova Apr 6, 2020

Choose a reason for hiding this comment

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

Should we error if the called contract consumes more than they approved in this call (spending additionally from their original approved amount)?

uint256 amountApproved,
uint256 amountSpent,
bytes data
);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should index wallet token and amountSpent to mirror what we index in Transfer ? I suspect that it is the main information the clients are interested in. Maybe check with @th3m477 ?

Copy link

Choose a reason for hiding this comment

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

I don't think uint256 values need to be indexed generally, makes no client difference here as it's a new event.

Addresses make more sense as we are more likely to do exploratory network analysis in the future, or quick filters (i.e. "show me all the transfers I made to X address"), so more of a case for spender, but up to you @juniset @elenadimitrova

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually we can only have up to 3 indexed arguments per event so leaving as is.

@elenadimitrova elenadimitrova force-pushed the feature/flash-approve-and-call branch 3 times, most recently from e002379 to cd877b1 Compare April 6, 2020 20:45

// Restore the original allowance amount.
methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, existingAllowance);
invokeWallet(address(_wallet), _token, 0, methodData);
Copy link
Member

Choose a reason for hiding this comment

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

I think there is an IF missing if we want to save some gas and it should be:

if (unusedAllowance != existingAllowance) {
    // Restore the original allowance amount.
    methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, existingAllowance);
    invokeWallet(address(_wallet), _token, 0, methodData);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intentionally omitted it as we won't pay for storage updates when the value doesn't change. So implicitly this is enforced anyway while keeping the code simpler.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but I believe there will still be quite some gas consumed by invoking the wallet: internal call to the wallet, then delegatecall to the wallet implementation, then checking that the calling address is an authorised module, then calling the token contract which itself need to do a few check before updating the memory slot (even if this last step is free).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's around 12470 gas which is approx 1 cent worth at current prices. I can't tell whether the complexity outweighs the advantage but I'll add it.

config.settings.securityPeriod || 0,
config.settings.securityWindow || 0,
config.settings.defaultLimit || "1000000000000000000",
["test", "staging", "prod"].includes(network) ? config.modules.TokenTransfer : "0x0000000000000000000000000000000000000000",
Copy link
Member

Choose a reason for hiding this comment

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

It should be config.modules.TransferManager and not config.modules.TokenTransfer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But config.modules.TokenTransfer is the address of the old config.modules.TransferManager no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe when upgrading the TransferManager we should copy its old value here though

Copy link
Member

Choose a reason for hiding this comment

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

It is the address of the first version of the TransferManager, yes. But here we are going from the second version (config.modules.TransferManager) to the third one. So the new TransferManager needs to read the state of a wallet's limit from the second version which is config.modules.TransferManager.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But we update the config.modules.TransferManager address to the new one in the same script.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, removed TokenTransfer in favour of using TransferManager value prior to upgrade updates.


const utils = require("../utils/utilities.js");

const TARGET_VERSION = "1.6.0";
const MODULES_TO_ENABLE = ["ApprovedTransfer", "RecoveryManager", "MakerV2Manager"];
const MODULES_TO_ENABLE = ["ApprovedTransfer", "RecoveryManager", "MakerV2Manager", "TransferManager"];
const MODULES_TO_DISABLE = ["UniswapManager"];

const BACKWARD_COMPATIBILITY = 3;
Copy link
Member

Choose a reason for hiding this comment

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

Didn't we say we would set BACKWARD_COMPATIBILITY to 1 and remove the part of the script where we create a LegacyUpgrader? Or are we addressing this in a separate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's a separate PR for it #91 as I wanted to double check the clients can support the upgrade through 1.4 -> 1.6

@juniset juniset merged commit 7a70918 into develop Apr 8, 2020
@elenadimitrova elenadimitrova deleted the feature/flash-approve-and-call branch April 8, 2020 13:10
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.

3 participants