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

Price Impact Helper #323

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/interfaces/contracts/vault/IVaultErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,6 @@ interface IVaultErrors {

/// @dev The vault admin was configured with an incorrect Vault address.
error WrongVaultAdminDeployment();

error QuoteResultSpoofed();
}
17 changes: 17 additions & 0 deletions pkg/interfaces/contracts/vault/IVaultExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,23 @@ interface IVaultExtension {
*/
function quote(bytes calldata data) external payable returns (bytes memory result);

/**
* @notice Performs a callback on msg.sender with arguments provided in `data`.
* @dev Used to query a set of operations on the Vault. Only off-chain eth_call are allowed,
* anything else will revert.
*
* Allows querying any operation on the Vault that has the `withLocker` modifier.
*
* Allows the external calling of a function via the Vault contract to
* access Vault's functions guarded by `withLocker`.
* `transient` modifier ensuring balances changes within the Vault are settled.
*
* This call always reverts, returning the result in the revert reason.
*
* @param data Contains function signature and args to be passed to the msg.sender
*/
function quoteAndRevert(bytes calldata data) external payable;

/**
* @notice Checks if the queries enabled on the Vault.
* @return If true, then queries are disabled
Expand Down
54 changes: 54 additions & 0 deletions pkg/solidity-utils/contracts/helpers/RevertCodec.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

// solhint-disable no-inline-assembly

library RevertCodec {
error Result(bytes result);

error UnexpectedCallSuccess();

error ErrorSelectorNotFound();

function catchEncodedResult(bytes memory resultRaw) internal pure returns (bytes memory) {
bytes4 errorSelector = RevertCodec.parseSelector(resultRaw);
if (errorSelector != Result.selector) {
// Bubble up error message if the revert reason is not the expected one.
RevertCodec.bubbleUpRevert(resultRaw);
}

uint256 resultRawLength = resultRaw.length;
assembly {
resultRaw := add(resultRaw, 0x04) // Slice the sighash.
mstore(resultRaw, sub(resultRawLength, 4)) // Set proper length
}

return abi.decode(resultRaw, (bytes));
}

/// @dev Returns the first 4 bytes in an array, reverting if the length is < 4.
function parseSelector(bytes memory callResult) internal pure returns (bytes4 errorSelector) {
if (callResult.length < 4) {
revert ErrorSelectorNotFound();
}
assembly {
errorSelector := mload(add(callResult, 0x20)) // Load the first 4 bytes from data (skip length offset)
}
}

/// @dev Taken from Openzeppelin's Address.
function bubbleUpRevert(bytes memory returndata) internal 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 ErrorSelectorNotFound();
}
}
}
40 changes: 40 additions & 0 deletions pkg/solidity-utils/test/foundry/RevertCodec.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

import "forge-std/Test.sol";

import { RevertCodec } from "../../contracts/helpers/RevertCodec.sol";

contract RevertCodecTest is Test {
error TestCustomError(uint256 code);

function testcatchEncodedResultNoSelector() public {
vm.expectRevert(abi.encodeWithSelector(RevertCodec.ErrorSelectorNotFound.selector));
RevertCodec.catchEncodedResult("");
}

function testcatchEncodedResultCustomError() public {
vm.expectRevert(abi.encodeWithSelector(TestCustomError.selector, uint256(123)));
RevertCodec.catchEncodedResult(bytes(abi.encodeWithSelector(TestCustomError.selector, uint256(123))));
}

function testcatchEncodedResultOk() public {
bytes memory encodedError = abi.encodeWithSelector(RevertCodec.Result.selector, abi.encode(uint256(987), true));
bytes memory result = RevertCodec.catchEncodedResult(encodedError);
(uint256 decodedResultInt, bool decodedResultBool) = abi.decode(result, (uint256, bool));

assertEq(decodedResultInt, uint256(987), "Wrong decoded result (int)");
assertEq(decodedResultBool, true, "Wrong decoded result (bool)");
}

function testParseSelectorNoData() public {
vm.expectRevert(abi.encodeWithSelector(RevertCodec.ErrorSelectorNotFound.selector));
RevertCodec.parseSelector("");
}

function testParseSelector() public {
bytes4 selector = RevertCodec.parseSelector(abi.encodePacked(hex"112233445566778899aabbccddeeff"));
assertEq(selector, bytes4(0x11223344), "Incorrect selector");
}
}
3 changes: 3 additions & 0 deletions pkg/standalone-utils/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
skipFiles: ['test'],
};
1 change: 1 addition & 0 deletions pkg/standalone-utils/.solhintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
contracts/test/
3 changes: 3 additions & 0 deletions pkg/standalone-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

## Unreleased
15 changes: 15 additions & 0 deletions pkg/standalone-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# <img src="../../logo.svg" alt="Balancer" height="128px">

# Balancer V3 Standalone Utils

This package contains standalone utilities that can be used to perform advanced actions in the Balancer V3 protocol.

- [`PriceImpact`](./contracts/PriceImpact.sol) can be used by off-chain clients to calculate price impact for add liquidity unbalanced operations.
elshan-eth marked this conversation as resolved.
Show resolved Hide resolved

## Overview

### Usage

## Licensing

[GNU General Public License Version 3 (GPL v3)](../../LICENSE).
Loading
Loading