Source Code
Overview
S Balance
S Value
$0.00Latest 25 from a total of 27 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create Vault | 58255054 | 43 days ago | IN | 0 S | 0.02180971 | ||||
| Create Vault | 57933760 | 47 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 57679230 | 50 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 57620276 | 51 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 57592212 | 51 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56732602 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56730770 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56727704 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56727148 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56720532 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56716960 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56716623 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56716324 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56715893 | 62 days ago | IN | 0 S | 0.0092233 | ||||
| Create Vault | 56714503 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56708076 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56707571 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56702610 | 62 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56699891 | 62 days ago | IN | 0 S | 0.0092233 | ||||
| Create Vault | 56559659 | 64 days ago | IN | 0 S | 0.0092233 | ||||
| Create Vault | 56558479 | 64 days ago | IN | 0 S | 0.0092233 | ||||
| Create Vault | 56283801 | 67 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56256838 | 68 days ago | IN | 0 S | 0.01067336 | ||||
| Create Vault | 56197366 | 68 days ago | IN | 0 S | 0.0092239 | ||||
| Create Vault | 56197312 | 68 days ago | IN | 0 S | 0.0092239 |
Latest 25 internal transactions (View All)
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PersonalVaultFactory
Compiler Version
v0.8.20+commit.a1b79de6
Contract Source Code (Solidity)
/** *Submitted for verification at SonicScan.org on 2025-11-23 */ // Sources flattened with hardhat v2.26.0 https://hardhat.org // SPDX-License-Identifier: MIT // File @openzeppelin/contracts/utils/introspection/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol) pragma solidity >=0.4.16; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // File @openzeppelin/contracts/interfaces/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol) pragma solidity >=0.4.16; // File @openzeppelin/contracts/token/ERC20/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol) pragma solidity >=0.4.16; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); } // File @openzeppelin/contracts/interfaces/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol) pragma solidity >=0.4.16; // File @openzeppelin/contracts/interfaces/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol) pragma solidity >=0.6.2; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); } // File @openzeppelin/contracts/utils/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); } // File @openzeppelin/contracts/utils/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol) pragma solidity ^0.8.20; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev There's no code to deploy. */ error Create2EmptyBytecode(); /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } if (bytecode.length == 0) { revert Create2EmptyBytecode(); } assembly ("memory-safe") { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) // if no address was created, and returndata is not empty, bubble revert if and(iszero(addr), not(iszero(returndatasize()))) { let p := mload(0x40) returndatacopy(p, 0, returndatasize()) revert(p, returndatasize()) } } if (addr == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { assembly ("memory-safe") { let ptr := mload(0x40) // Get free memory pointer // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | // |-------------------|---------------------------------------------------------------------------| // | bytecodeHash | CCCCCCCCCCCCC...CC | // | salt | BBBBBBBBBBBBB...BB | // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | // | 0xFF | FF | // |-------------------|---------------------------------------------------------------------------| // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | mstore(add(ptr, 0x40), bytecodeHash) mstore(add(ptr, 0x20), salt) mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff mstore8(start, 0xff) addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff) } } } // File @openzeppelin/contracts/proxy/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (proxy/Clones.sol) pragma solidity ^0.8.20; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. */ library Clones { error CloneArgumentsTooLong(); /** * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`. * * This function uses the create opcode, which should never revert. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. */ function clone(address implementation) internal returns (address instance) { return clone(implementation, 0); } /** * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency * to the new contract. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function clone(address implementation, uint256 value) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } assembly ("memory-safe") { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(value, 0x09, 0x37) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple times will revert, since * the clones cannot be deployed twice at the same address. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { return cloneDeterministic(implementation, salt, 0); } /** * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with * a `value` parameter to send native currency to the new contract. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneDeterministic( address implementation, bytes32 salt, uint256 value ) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } assembly ("memory-safe") { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(value, 0x09, 0x37, salt) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { assembly ("memory-safe") { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } /** * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom * immutable arguments. These are provided through `args` and cannot be changed after deployment. To * access the arguments within the implementation, use {fetchCloneArgs}. * * This function uses the create opcode, which should never revert. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. */ function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) { return cloneWithImmutableArgs(implementation, args, 0); } /** * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value` * parameter to send native currency to the new contract. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneWithImmutableArgs( address implementation, bytes memory args, uint256 value ) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); assembly ("memory-safe") { instance := create(value, add(bytecode, 0x20), mload(bytecode)) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom * immutable arguments. These are provided through `args` and cannot be changed after deployment. To * access the arguments within the implementation, use {fetchCloneArgs}. * * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice * at the same address. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. */ function cloneDeterministicWithImmutableArgs( address implementation, bytes memory args, bytes32 salt ) internal returns (address instance) { return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0); } /** * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs], * but with a `value` parameter to send native currency to the new contract. * * WARNING: This function does not check if `implementation` has code. A clone that points to an address * without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they * have no effect and leave the clone uninitialized, allowing a third party to initialize it later. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneDeterministicWithImmutableArgs( address implementation, bytes memory args, bytes32 salt, uint256 value ) internal returns (address instance) { bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); return Create2.deploy(value, salt, bytecode); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}. */ function predictDeterministicAddressWithImmutableArgs( address implementation, bytes memory args, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args); return Create2.computeAddress(salt, keccak256(bytecode), deployer); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}. */ function predictDeterministicAddressWithImmutableArgs( address implementation, bytes memory args, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this)); } /** * @dev Get the immutable args attached to a clone. * * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this * function will return an empty array. * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or * `cloneDeterministicWithImmutableArgs`, this function will return the args array used at * creation. * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This * function should only be used to check addresses that are known to be clones. */ function fetchCloneArgs(address instance) internal view returns (bytes memory) { bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short assembly ("memory-safe") { extcodecopy(instance, add(result, 32), 45, mload(result)) } return result; } /** * @dev Helper that prepares the initcode of the proxy with immutable args. * * An assembly variant of this function requires copying the `args` array, which can be efficiently done using * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using * abi.encodePacked is more expensive but also more portable and easier to review. * * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes. * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes. */ function _cloneCodeWithImmutableArgs( address implementation, bytes memory args ) private pure returns (bytes memory) { if (args.length > 24531) revert CloneArgumentsTooLong(); return abi.encodePacked( hex"61", uint16(args.length + 45), hex"3d81600a3d39f3363d3d373d3d3d363d73", implementation, hex"5af43d82803e903d91602b57fd5bf3", args ); } } // File @openzeppelin/contracts/token/ERC20/utils/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful. */ function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) { return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful. */ function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) { return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } } // File @openzeppelin/contracts/proxy/utils/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reinitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location. * * NOTE: Consider following the ERC-7201 formula to derive storage locations. */ function _initializableStorageSlot() internal pure virtual returns (bytes32) { return INITIALIZABLE_STORAGE; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { bytes32 slot = _initializableStorageSlot(); assembly { $.slot := slot } } } // File @openzeppelin/contracts/utils/[email protected] // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } } // File contracts/PersonalVault.sol // Original license: SPDX_License_Identifier: MIT pragma solidity 0.8.20; interface IYieldProvider { function deposit(uint256 assets) external; function withdraw(uint256 assets, address receiver) external returns (uint256); function totalAssets() external view returns (uint256); } /** * @title PersonalVault * @notice Per-user vault for USDC yield generation with 10% fee on profits * @dev Each SmartAccount gets its own vault instance * Compatible with EIP-1167 minimal proxy pattern for gas-efficient deployment */ contract PersonalVault is ReentrancyGuard, Initializable { using SafeERC20 for IERC20; // Configuration (converted from immutable for clone compatibility) address public owner; IERC20 public asset; // USDC IYieldProvider public yieldProvider; address public feeRecipient; // Vault state uint256 public principal; // Total principal deposited (net of withdrawals) uint256 public totalShares; // Total shares in this vault uint256 public constant FEE_BASIS_POINTS = 1000; // 10% fee on yield only event Deposit(uint256 assets, uint256 shares); event Withdraw(uint256 assets, uint256 assetsReceived, uint256 fee, uint256 principalReturned); modifier onlyOwner() { require(msg.sender == owner, "PersonalVault: caller is not owner"); _; } /** * @notice Initialize the PersonalVault (replaces constructor for clones) * @param _owner SmartAccount that owns this vault * @param _asset USDC token address * @param _yieldProvider Yield provider address (Aave or mock) * @param _feeRecipient Address to receive protocol fees * @dev Can only be called once. Protected by OpenZeppelin's Initializable * Gas fees are automatically attributed to the PersonalVaultFactory (FeeM Project ID: 237) */ function initialize( address _owner, address _asset, address _yieldProvider, address _feeRecipient ) external initializer { require(_owner != address(0), "PersonalVault: owner cannot be zero"); require(_asset != address(0), "PersonalVault: asset cannot be zero"); require(_yieldProvider != address(0), "PersonalVault: yieldProvider cannot be zero"); require(_feeRecipient != address(0), "PersonalVault: feeRecipient cannot be zero"); owner = _owner; asset = IERC20(_asset); yieldProvider = IYieldProvider(_yieldProvider); feeRecipient = _feeRecipient; } /** * @notice Deposit USDC and receive shares * @param assets Amount of USDC to deposit * @return sharesOut Amount of shares minted */ function deposit(uint256 assets) external onlyOwner nonReentrant returns (uint256 sharesOut) { require(assets > 0, "PersonalVault: cannot deposit zero"); // Transfer USDC from SmartAccount asset.safeTransferFrom(msg.sender, address(this), assets); // Calculate shares to mint sharesOut = convertToShares(assets); // Update state totalShares += sharesOut; principal += assets; // Deposit to yield provider asset.safeIncreaseAllowance(address(yieldProvider), assets); yieldProvider.deposit(assets); emit Deposit(assets, sharesOut); } /** * @notice Withdraw USDC, charging 10% fee on yield only * @param assets Amount of USDC to withdraw * @return assetsReceived Amount of USDC received by owner (after fee) */ function withdraw(uint256 assets) external onlyOwner nonReentrant returns (uint256 assetsReceived) { require(assets > 0, "PersonalVault: cannot withdraw zero"); require(totalShares > 0, "PersonalVault: no shares"); // Calculate total vault value uint256 vaultValue = totalAssets(); require(assets <= vaultValue, "PersonalVault: insufficient balance"); // Calculate proportional principal being withdrawn // usedPrincipal = principal * assets / totalAssets() uint256 usedPrincipal = (principal * assets) / vaultValue; // Clamp to available principal to handle rounding if (usedPrincipal > principal) { usedPrincipal = principal; } // Calculate shares to burn uint256 sharesToBurn = (totalShares * assets) / vaultValue; // Clamp to available shares if (sharesToBurn > totalShares) { sharesToBurn = totalShares; } // Burn shares and reduce principal BEFORE withdrawal totalShares -= sharesToBurn; principal -= usedPrincipal; // Zero out dust when fully exiting if (totalShares == 0) { principal = 0; } // Withdraw from yield provider uint256 actualWithdrawn = yieldProvider.withdraw(assets, address(this)); // Calculate fee based on profit // profit = max(actualWithdrawn - usedPrincipal, 0) uint256 profit = 0; uint256 feeAmount = 0; if (actualWithdrawn > usedPrincipal) { profit = actualWithdrawn - usedPrincipal; feeAmount = (profit * FEE_BASIS_POINTS) / 10000; // 10% of profit } // Amount owner receives (actualWithdrawn - fee) assetsReceived = actualWithdrawn - feeAmount; // Transfer net amount to owner if (assetsReceived > 0) { asset.safeTransfer(owner, assetsReceived); } // Transfer fee to feeRecipient if (feeAmount > 0) { asset.safeTransfer(feeRecipient, feeAmount); } emit Withdraw(assets, assetsReceived, feeAmount, usedPrincipal); } /** * @notice Get total assets in vault (including yield) * @return Total USDC managed by this vault */ function totalAssets() public view returns (uint256) { return yieldProvider.totalAssets(); } /** * @notice Convert assets to shares * @param assets Amount of USDC * @return Amount of shares */ function convertToShares(uint256 assets) public view returns (uint256) { uint256 supply = totalShares; if (supply == 0) { return assets; // 1:1 on first deposit } return (assets * supply) / totalAssets(); } /** * @notice Convert shares to assets * @param sharesAmount Amount of shares * @return Amount of USDC */ function convertToAssets(uint256 sharesAmount) public view returns (uint256) { uint256 supply = totalShares; if (supply == 0) { return sharesAmount; } return (sharesAmount * totalAssets()) / supply; } /** * @notice Get vault statistics * @return currentValue Total value in vault * @return unrealizedYield Yield earned but not yet withdrawn * @return principalAmount Principal deposited */ function getStats() external view returns ( uint256 currentValue, uint256 unrealizedYield, uint256 principalAmount ) { currentValue = totalAssets(); principalAmount = principal; if (currentValue > principalAmount) { unrealizedYield = currentValue - principalAmount; } else { unrealizedYield = 0; } } } // File contracts/PersonalVaultFactory.sol // Original license: SPDX_License_Identifier: MIT pragma solidity 0.8.20; /** * @title PersonalVaultFactory * @notice Factory for deploying PersonalVault minimal proxies * @dev Uses EIP-1167 minimal proxy pattern via OpenZeppelin Clones * Deploys lightweight clones (~45 bytes) instead of full contracts * All clones delegatecall to a single implementation contract * Benefits: 97% gas savings, single verification on block explorer */ contract PersonalVaultFactory { using Clones for address; address public immutable implementation; address public immutable asset; // USDC address public immutable yieldProvider; address public immutable feeRecipient; event VaultCreated(address indexed owner, address indexed vault); /** * @notice Create factory with PersonalVault implementation * @param _implementation Address of deployed PersonalVault implementation * @param _asset USDC token address * @param _yieldProvider Yield provider address * @param _feeRecipient Fee recipient address */ constructor( address _implementation, address _asset, address _yieldProvider, address _feeRecipient ) { require(_implementation != address(0), "PersonalVaultFactory: implementation cannot be zero"); require(_asset != address(0), "PersonalVaultFactory: asset cannot be zero"); require(_yieldProvider != address(0), "PersonalVaultFactory: yieldProvider cannot be zero"); require(_feeRecipient != address(0), "PersonalVaultFactory: feeRecipient cannot be zero"); implementation = _implementation; asset = _asset; yieldProvider = _yieldProvider; feeRecipient = _feeRecipient; } /** * @notice Deploy a minimal proxy PersonalVault for a SmartAccount (or return existing) * @param owner SmartAccount address * @return vault Address of deployed or existing PersonalVault clone */ function createVault(address owner) external returns (address vault) { require(owner != address(0), "PersonalVaultFactory: owner cannot be zero"); // Compute where the vault would be deployed vault = computeVaultAddress(owner); // Check if vault already exists uint256 codeSize; assembly { codeSize := extcodesize(vault) } if (codeSize > 0) { // Vault already exists, return existing address return vault; } // Use owner address as salt for CREATE2 bytes32 salt = bytes32(uint256(uint160(owner))); // Deploy minimal proxy vault = implementation.cloneDeterministic(salt); // Initialize the clone PersonalVault(payable(vault)).initialize( owner, asset, yieldProvider, feeRecipient ); emit VaultCreated(owner, vault); } /** * @notice Compute the address where a vault clone would be deployed * @param owner SmartAccount address * @return vault Predicted vault clone address */ function computeVaultAddress(address owner) public view returns (address vault) { bytes32 salt = bytes32(uint256(uint160(owner))); return implementation.predictDeterministicAddress(salt, address(this)); } /// @dev Register my contract on Sonic FeeM function registerMe() external { (bool _success,) = address(0xDC2B0D2Dd2b7759D97D50db4eabDC36973110830).call( abi.encodeWithSignature("selfRegister(uint256)", 237) ); require(_success, "FeeM registration failed"); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_yieldProvider","type":"address"},{"internalType":"address","name":"_feeRecipient","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultCreated","type":"event"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"computeVaultAddress","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"createVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registerMe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yieldProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61010060405234801561001157600080fd5b506040516108ae3803806108ae83398101604081905261003091610239565b6001600160a01b0384166100b15760405162461bcd60e51b815260206004820152603360248201527f506572736f6e616c5661756c74466163746f72793a20696d706c656d656e746160448201527f74696f6e2063616e6e6f74206265207a65726f0000000000000000000000000060648201526084015b60405180910390fd5b6001600160a01b03831661011a5760405162461bcd60e51b815260206004820152602a60248201527f506572736f6e616c5661756c74466163746f72793a2061737365742063616e6e6044820152696f74206265207a65726f60b01b60648201526084016100a8565b6001600160a01b03821661018b5760405162461bcd60e51b815260206004820152603260248201527f506572736f6e616c5661756c74466163746f72793a207969656c6450726f76696044820152716465722063616e6e6f74206265207a65726f60701b60648201526084016100a8565b6001600160a01b0381166101fb5760405162461bcd60e51b815260206004820152603160248201527f506572736f6e616c5661756c74466163746f72793a20666565526563697069656044820152706e742063616e6e6f74206265207a65726f60781b60648201526084016100a8565b6001600160a01b0393841660805291831660a052821660c0521660e05261028d565b80516001600160a01b038116811461023457600080fd5b919050565b6000806000806080858703121561024f57600080fd5b6102588561021d565b93506102666020860161021d565b92506102746040860161021d565b91506102826060860161021d565b905092959194509250565b60805160a05160c05160e0516105c86102e66000396000818160ca015261041f01526000818161012201526103f7015260008181608701526103cf01526000818160f101528181610289015261038a01526105c86000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80639a198d611161005b5780639a198d6114610113578063a82250a81461011d578063a9e5047114610144578063b4bd6f461461015757600080fd5b806338d52e0f1461008257806346904840146100c55780635c60da1b146100ec575b600080fd5b6100a97f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b6100a97f000000000000000000000000000000000000000000000000000000000000000081565b6100a97f000000000000000000000000000000000000000000000000000000000000000081565b61011b61016a565b005b6100a97f000000000000000000000000000000000000000000000000000000000000000081565b6100a961015236600461053a565b610260565b6100a961016536600461053a565b6102f4565b60405160ed602482015260009073dc2b0d2dd2b7759d97d50db4eabdc369731108309060440160408051601f198184030181529181526020820180516001600160e01b03166307983f4560e21b179052516101c59190610563565b6000604051808303816000865af19150503d8060008114610202576040519150601f19603f3d011682016040523d82523d6000602084013e610207565b606091505b505090508061025d5760405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c6564000000000000000060448201526064015b60405180910390fd5b50565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff60248201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166014830152733d602d80600a3d3981f3363d3d373d3d3d363d738252828116605883018190526037600c8401206078840152605560439093019290922060009291165b9392505050565b60006001600160a01b03821661035f5760405162461bcd60e51b815260206004820152602a60248201527f506572736f6e616c5661756c74466163746f72793a206f776e65722063616e6e6044820152696f74206265207a65726f60b01b6064820152608401610254565b61036882610260565b9050803b80156103785750919050565b6001600160a01b03808416906103b0907f000000000000000000000000000000000000000000000000000000000000000016826104c6565b604051637c643b2f60e11b81526001600160a01b0386811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301527f0000000000000000000000000000000000000000000000000000000000000000811660648301529194509084169063f8c8765e90608401600060405180830381600087803b15801561046f57600080fd5b505af1158015610483573d6000803e3d6000fd5b50506040516001600160a01b038087169350871691507f5d9c31ffa0fecffd7cf379989a3c7af252f0335e0d2a1320b55245912c781f5390600090a35050919050565b60006102ed83838380763d602d80600a3d3981f3363d3d373d3d3d363d730000008460601b60e81c176000526e5af43d82803e903d91602b57fd5bf38460781b17602052826037600984f590506001600160a01b0381166102ed5760405163b06ebf3d60e01b815260040160405180910390fd5b60006020828403121561054c57600080fd5b81356001600160a01b03811681146102ed57600080fd5b6000825160005b81811015610584576020818601810151858301520161056a565b50600092019182525091905056fea2646970667358221220cfe0027d4480e02b0843a25601c17090e14e10716c78c061ec96869d77033b3364736f6c63430008140033000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc000000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388940000000000000000000000007b611d83dd6d4ca8fa3d86d1648ed7ce98b251a10000000000000000000000005ad81dd995aabee3f1742ba006ee58cf242c5225
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061007d5760003560e01c80639a198d611161005b5780639a198d6114610113578063a82250a81461011d578063a9e5047114610144578063b4bd6f461461015757600080fd5b806338d52e0f1461008257806346904840146100c55780635c60da1b146100ec575b600080fd5b6100a97f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889481565b6040516001600160a01b03909116815260200160405180910390f35b6100a97f0000000000000000000000005ad81dd995aabee3f1742ba006ee58cf242c522581565b6100a97f000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc081565b61011b61016a565b005b6100a97f0000000000000000000000007b611d83dd6d4ca8fa3d86d1648ed7ce98b251a181565b6100a961015236600461053a565b610260565b6100a961016536600461053a565b6102f4565b60405160ed602482015260009073dc2b0d2dd2b7759d97d50db4eabdc369731108309060440160408051601f198184030181529181526020820180516001600160e01b03166307983f4560e21b179052516101c59190610563565b6000604051808303816000865af19150503d8060008114610202576040519150601f19603f3d011682016040523d82523d6000602084013e610207565b606091505b505090508061025d5760405162461bcd60e51b815260206004820152601860248201527f4665654d20726567697374726174696f6e206661696c6564000000000000000060448201526064015b60405180910390fd5b50565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff60248201526001600160a01b037f000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc081166014830152733d602d80600a3d3981f3363d3d373d3d3d363d738252828116605883018190526037600c8401206078840152605560439093019290922060009291165b9392505050565b60006001600160a01b03821661035f5760405162461bcd60e51b815260206004820152602a60248201527f506572736f6e616c5661756c74466163746f72793a206f776e65722063616e6e6044820152696f74206265207a65726f60b01b6064820152608401610254565b61036882610260565b9050803b80156103785750919050565b6001600160a01b03808416906103b0907f000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc016826104c6565b604051637c643b2f60e11b81526001600160a01b0386811660048301527f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811660248301527f0000000000000000000000007b611d83dd6d4ca8fa3d86d1648ed7ce98b251a1811660448301527f0000000000000000000000005ad81dd995aabee3f1742ba006ee58cf242c5225811660648301529194509084169063f8c8765e90608401600060405180830381600087803b15801561046f57600080fd5b505af1158015610483573d6000803e3d6000fd5b50506040516001600160a01b038087169350871691507f5d9c31ffa0fecffd7cf379989a3c7af252f0335e0d2a1320b55245912c781f5390600090a35050919050565b60006102ed83838380763d602d80600a3d3981f3363d3d373d3d3d363d730000008460601b60e81c176000526e5af43d82803e903d91602b57fd5bf38460781b17602052826037600984f590506001600160a01b0381166102ed5760405163b06ebf3d60e01b815260040160405180910390fd5b60006020828403121561054c57600080fd5b81356001600160a01b03811681146102ed57600080fd5b6000825160005b81811015610584576020818601810151858301520161056a565b50600092019182525091905056fea2646970667358221220cfe0027d4480e02b0843a25601c17090e14e10716c78c061ec96869d77033b3364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc000000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388940000000000000000000000007b611d83dd6d4ca8fa3d86d1648ed7ce98b251a10000000000000000000000005ad81dd995aabee3f1742ba006ee58cf242c5225
-----Decoded View---------------
Arg [0] : _implementation (address): 0xA2551558A5220097927097BCBa042dA7a9Ee4dc0
Arg [1] : _asset (address): 0x29219dd400f2Bf60E5a23d13Be72B486D4038894
Arg [2] : _yieldProvider (address): 0x7b611d83Dd6D4cA8FA3d86D1648ed7CE98B251a1
Arg [3] : _feeRecipient (address): 0x5Ad81dD995AaBee3f1742ba006Ee58Cf242C5225
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000a2551558a5220097927097bcba042da7a9ee4dc0
Arg [1] : 00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894
Arg [2] : 0000000000000000000000007b611d83dd6d4ca8fa3d86d1648ed7ce98b251a1
Arg [3] : 0000000000000000000000005ad81dd995aabee3f1742ba006ee58cf242c5225
Deployed Bytecode Sourcemap
61018:3305:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;61134:30;;;;;;;;-1:-1:-1;;;;;178:32:1;;;160:51;;148:2;133:18;61134:30:0;;;;;;;61224:37;;;;;61088:39;;;;;64060:260;;;:::i;:::-;;61179:38;;;;;63776:227;;;;;;:::i;:::-;;:::i;62579:1003::-;;;;;;:::i;:::-;;:::i;64060:260::-;64192:53;;64241:3;64192:53;;;667:36:1;64103:13:0;;64129:42;;640:18:1;;64192:53:0;;;-1:-1:-1;;64192:53:0;;;;;;;;;;;;;;-1:-1:-1;;;;;64192:53:0;-1:-1:-1;;;64192:53:0;;;64121:135;;;64192:53;64121:135;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;64102:154;;;64275:8;64267:45;;;;-1:-1:-1;;;64267:45:0;;1333:2:1;64267:45:0;;;1315:21:1;1372:2;1352:18;;;1345:30;1411:26;1391:18;;;1384:54;1455:18;;64267:45:0;;;;;;;;;64091:229;64060:260::o;63776:227::-;20868:4;20862:11;63989:4;20903;20894:14;;20887:32;20956:34;20949:4;20940:14;;20933:58;-1:-1:-1;;;;;63932:14:0;:42;;-1:-1:-1;21012:14:0;;21005:38;21069:42;21057:55;;63890:23;;;21142:4;21133:14;;21126:28;;;21217:4;21210;21201:14;;21191:31;21184:4;21175:14;;21168:55;21280:4;21273;21264:14;;;21254:31;;;;-1:-1:-1;;63890:23:0;21250:80;63932:63;63925:70;63776:227;-1:-1:-1;;;63776:227:0:o;62579:1003::-;62633:13;-1:-1:-1;;;;;62667:19:0;;62659:74;;;;-1:-1:-1;;;62659:74:0;;1686:2:1;62659:74:0;;;1668:21:1;1725:2;1705:18;;;1698:30;1764:34;1744:18;;;1737:62;-1:-1:-1;;;1815:18:1;;;1808:40;1865:19;;62659:74:0;1484:406:1;62659:74:0;62808:26;62828:5;62808:19;:26::i;:::-;62800:34;-1:-1:-1;62960:18:0;;63013:12;;63009:119;;63104:12;62579:1003;;;:::o;63009:119::-;-1:-1:-1;;;;;63213:23:0;;;;63299:39;;:14;:33;63213:23;63299:33;:39::i;:::-;63384:146;;-1:-1:-1;;;63384:146:0;;-1:-1:-1;;;;;2182:15:1;;;63384:146:0;;;2164:34:1;63459:5:0;2234:15:1;;2214:18;;;2207:43;63479:13:0;2286:15:1;;2266:18;;;2259:43;63507:12:0;2338:15:1;;2318:18;;;2311:43;63291:47:0;;-1:-1:-1;63384:40:0;;;;;;2098:19:1;;63384:146:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;63548:26:0;;-1:-1:-1;;;;;63548:26:0;;;;-1:-1:-1;63548:26:0;;;-1:-1:-1;63548:26:0;;;;;62648:934;;62579:1003;;;:::o;18578:171::-;18662:16;18698:43;18717:14;18733:4;18662:16;;20100:48;20082:14;20076:4;20072:25;20066:4;20062:36;20059:90;20053:4;20046:104;20309:32;20292:14;20286:4;20282:25;20279:63;20273:4;20266:77;20396:4;20390;20384;20377:5;20369:32;20357:44;-1:-1:-1;;;;;;20426:22:0;;20422:87;;20472:25;;-1:-1:-1;;;20472:25:0;;;;;;;;;;;222:286:1;281:6;334:2;322:9;313:7;309:23;305:32;302:52;;;350:1;347;340:12;302:52;376:23;;-1:-1:-1;;;;;428:31:1;;418:42;;408:70;;474:1;471;464:12;714:412;843:3;881:6;875:13;906:1;916:129;930:6;927:1;924:13;916:129;;;1028:4;1012:14;;;1008:25;;1002:32;989:11;;;982:53;945:12;916:129;;;-1:-1:-1;1100:1:1;1064:16;;1089:13;;;-1:-1:-1;1064:16:1;714:412;-1:-1:-1;714:412:1:o
Swarm Source
ipfs://cfe0027d4480e02b0843a25601c17090e14e10716c78c061ec96869d77033b33
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.