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

Make function calls atomic with ERC20 transfers in token supply source subnets #656

Merged
merged 10 commits into from
Feb 7, 2024

Conversation

cryptoAtwill
Copy link
Contributor

Make the performCall atomic with respect to ERC20 call with value.

Close ENG-635

Copy link

linear bot commented Feb 1, 2024

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

This looks correct, but I'm starting to get uncomfortable with the complexity of performCall. Could we take this opportunity to add unit tests (not integration tests) for performCall?

Comment on lines 126 to 129
assembly {
let returndata_size := mload(ret)
revert(add(32, ret), returndata_size)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you comment why the add opcode? There may be comments in the OZ library, but let's avoid reader indirection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I will add comments on the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is the full function from OpenZeppelin. We need to handle the case where there is no return data. The add is needed to unpack the variable-size return value (first Ethereum word is the size).

function _revert(bytes memory returndata) private pure {
    // Look for revert reason and bubble it up if present
    if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly
        /// @solidity memory-safe-assembly
        assembly {
            let returndata_size := mload(returndata)
            revert(add(32, returndata), returndata_size)
        }
    } else {
        revert FailedInnerCall();
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From my testing, if the ret is empty, calling revert(add(32, returndata), returndata_size) is the same as revert with no data. I think openzeppelin might be emphasising FailedInnerCall? But for us, if the contract returns empty, we can just return empty. Even if we dont, after some parsing of the error bytes, users will still get an empty ret.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From my testing, if the ret is empty, calling revert(add(32, returndata), returndata_size) is the same as revert with no data. I think openzeppelin might be emphasising FailedInnerCall? But for us, if the contract returns empty, we can just return empty. Even if we dont, after some parsing of the error bytes, users will still get an empty ret.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cryptoAtwill I suspect you're seeing this because mloading the return data pointer will return the size, which will be 0. You then revert providing an offset equal to the pointer+32, but with size 0. This is probably OK, but I'd rather play it safe and do the check anyway in high level Solidity.

Comment on lines 126 to 129
assembly {
let returndata_size := mload(ret)
revert(add(32, ret), returndata_size)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the full function from OpenZeppelin. We need to handle the case where there is no return data. The add is needed to unpack the variable-size return value (first Ethereum word is the size).

function _revert(bytes memory returndata) private pure {
    // Look for revert reason and bubble it up if present
    if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly
        /// @solidity memory-safe-assembly
        assembly {
            let returndata_size := mload(returndata)
            revert(add(32, returndata), returndata_size)
        }
    } else {
        revert FailedInnerCall();
    }
}


contract SupplySourceHelperTest is Test {
/// Call fails but send value works, both should fail
function test_revert_atomicity() public {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we test performCall instead, please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added more tests

@cryptoAtwill cryptoAtwill requested a review from raulk February 7, 2024 11:25
Comment on lines 133 to 135
} else {
return (success, ret);
revert FailedInnerCall();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's remove the else and simply use revert(). If the target didn't provide an error, we should not add one into the mix; we're simply interested in passing through here, right?

@raulk raulk changed the title Make erc20 function call atomic Make function calls atomic with ERC20 transfers in token supply source subnets Feb 7, 2024
@raulk raulk merged commit d9cca5b into main Feb 7, 2024
20 checks passed
@raulk raulk deleted the perform-call-atomic branch February 7, 2024 19:24
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.

2 participants