-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from portto/v1.3.0
v1.3.0: init
- Loading branch information
Showing
36 changed files
with
4,820 additions
and
1,072 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
/* solhint-disable avoid-low-level-calls */ | ||
/* solhint-disable no-inline-assembly */ | ||
/* solhint-disable reason-string */ | ||
|
||
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; | ||
import "@account-abstraction/contracts/core/BaseAccount.sol"; | ||
|
||
import "./TokenCallbackHandler.sol"; | ||
import "./CoreWallet/CoreWallet.sol"; | ||
|
||
/** | ||
* Blocto account. | ||
* compatibility for EIP-4337 and smart contract wallet with cosigner functionality (CoreWallet) | ||
*/ | ||
contract BloctoAccount is UUPSUpgradeable, TokenCallbackHandler, CoreWallet, BaseAccount { | ||
/** | ||
* This is the version of this contract. | ||
*/ | ||
string public constant VERSION = "1.3.0"; | ||
|
||
IEntryPoint private immutable _entryPoint; | ||
|
||
/** | ||
* constructor for BloctoAccount | ||
* @param anEntryPoint entrypoint address | ||
*/ | ||
constructor(IEntryPoint anEntryPoint) { | ||
_entryPoint = anEntryPoint; | ||
} | ||
|
||
/** | ||
* override from UUPSUpgradeable | ||
* @param newImplementation implementation address | ||
*/ | ||
function _authorizeUpgrade(address newImplementation) internal view override onlyInvoked { | ||
(newImplementation); | ||
} | ||
|
||
/** | ||
* return entrypoint | ||
*/ | ||
function entryPoint() public view virtual override returns (IEntryPoint) { | ||
return _entryPoint; | ||
} | ||
|
||
/** | ||
* execute a transaction (called directly by entryPoint) | ||
* @param dest dest call address | ||
* @param value value to send | ||
* @param func the func containing the transaction to be called | ||
*/ | ||
function execute(address dest, uint256 value, bytes calldata func) external { | ||
_requireFromEntryPoint(); | ||
_call(dest, value, func); | ||
} | ||
|
||
/** | ||
* execute a sequence of transactions (called directly by entryPoint) | ||
* @param dest sequence of dest call address | ||
* @param value sequence of value to send | ||
* @param func sequence of the func containing transactions to be called | ||
*/ | ||
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external { | ||
_requireFromEntryPoint(); | ||
require(dest.length == func.length, "wrong array lengths"); | ||
for (uint256 i = 0; i < dest.length; i++) { | ||
_call(dest[i], value[i], func[i]); | ||
} | ||
} | ||
|
||
/** | ||
* internal call for execute and executeBatch | ||
* @param target target call address | ||
* @param value value to send | ||
* @param data the data containing the transaction to be called | ||
*/ | ||
function _call(address target, uint256 value, bytes memory data) internal { | ||
(bool success, bytes memory result) = target.call{value: value}(data); | ||
if (!success) { | ||
assembly { | ||
revert(add(result, 32), mload(result)) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* implement validate signature method of BaseAccount from etnrypoint | ||
* @param userOp user operation including signature for validating | ||
* @param userOpHash user operation hash | ||
*/ | ||
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) | ||
internal | ||
virtual | ||
override | ||
returns (uint256 validationData) | ||
{ | ||
bytes4 result = this.isValidSignature(userOpHash, userOp.signature); | ||
if (result != IERC1271.isValidSignature.selector) { | ||
return SIG_VALIDATION_FAILED; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* check current account deposit in the entryPoint StakeManager | ||
*/ | ||
function getDeposit() public view returns (uint256) { | ||
return entryPoint().balanceOf(address(this)); | ||
} | ||
|
||
/** | ||
* deposit more funds for this account in the entryPoint StakeManager | ||
*/ | ||
function addDeposit() public payable { | ||
entryPoint().depositTo{value: msg.value}(address(this)); | ||
} | ||
|
||
/** | ||
* withdraw deposit to withdrawAddress from entryPoint StakeManager | ||
* @param withdrawAddress target to send to | ||
* @param amount to withdraw | ||
*/ | ||
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) external onlyInvoked { | ||
entryPoint().withdrawTo(withdrawAddress, amount); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import "./BloctoAccount.sol"; | ||
|
||
/// @title BloctoAccountCloneableWallet Wallet | ||
/// @notice This contract represents a complete but non working wallet. | ||
contract BloctoAccountCloneableWallet is BloctoAccount { | ||
/** | ||
* constructor that deploys a NON-FUNCTIONAL version of `BloctoAccount` | ||
* @param anEntryPoint entrypoint address | ||
*/ | ||
constructor(IEntryPoint anEntryPoint) BloctoAccount(anEntryPoint) { | ||
initialized = true; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import "@openzeppelin/contracts/utils/Create2.sol"; | ||
// import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
import "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; | ||
import "./BloctoAccountProxy.sol"; | ||
import "./BloctoAccount.sol"; | ||
|
||
// BloctoAccountFactory for creating BloctoAccountProxy | ||
contract BloctoAccountFactory is Ownable { | ||
/// @notice This is the version of this contract. | ||
string public constant VERSION = "1.3.0"; | ||
address public bloctoAccountImplementation; | ||
IEntryPoint public entryPoint; | ||
|
||
event WalletCreated(address wallet, address authorizedAddress, bool full); | ||
|
||
constructor(address _bloctoAccountImplementation, IEntryPoint _entryPoint) { | ||
bloctoAccountImplementation = _bloctoAccountImplementation; | ||
entryPoint = _entryPoint; | ||
} | ||
|
||
/** | ||
* create an account, and return its BloctoAccount. | ||
* returns the address even if the account is already deployed. | ||
* Note that during UserOperation execution, this method is called only if the account is not deployed. | ||
* This method returns an existing account address so that entryPoint.getSenderAddress() would work even after account creation | ||
*/ | ||
function createAccount(address _authorizedAddress, address _cosigner, address _recoveryAddress, uint256 _salt) | ||
public | ||
returns (BloctoAccount ret) | ||
{ | ||
address addr = getAddress(_cosigner, _recoveryAddress, _salt); | ||
uint256 codeSize = addr.code.length; | ||
if (codeSize > 0) { | ||
return BloctoAccount(payable(addr)); | ||
} | ||
bytes32 salt = keccak256(abi.encodePacked(_salt, _cosigner, _recoveryAddress)); | ||
// for consistent address | ||
BloctoAccountProxy newProxy = new BloctoAccountProxy{salt: salt}(address(this)); | ||
newProxy.initImplementation(bloctoAccountImplementation); | ||
ret = BloctoAccount(payable(address(newProxy))); | ||
ret.init(_authorizedAddress, uint256(uint160(_cosigner)), _recoveryAddress); | ||
emit WalletCreated(address(ret), _authorizedAddress, false); | ||
} | ||
|
||
function createAccount2( | ||
bytes memory _authorizedAddresses, | ||
address _cosigner, | ||
address _recoveryAddress, | ||
uint256 _salt | ||
) public returns (BloctoAccount ret) { | ||
require( | ||
_authorizedAddresses.length / 20 > 0 && _authorizedAddresses.length % 20 == 0, "invalid address byte array" | ||
); | ||
|
||
address addr = getAddress(_cosigner, _recoveryAddress, _salt); | ||
uint256 codeSize = addr.code.length; | ||
if (codeSize > 0) { | ||
return BloctoAccount(payable(addr)); | ||
} | ||
bytes32 salt = keccak256(abi.encodePacked(_salt, _cosigner, _recoveryAddress)); | ||
// for consistent address | ||
BloctoAccountProxy newProxy = new BloctoAccountProxy{salt: salt}(address(this)); | ||
newProxy.initImplementation(bloctoAccountImplementation); | ||
ret = BloctoAccount(payable(address(newProxy))); | ||
ret.init2(_authorizedAddresses, uint256(uint160(_cosigner)), _recoveryAddress); | ||
|
||
address firstAuthorizedAddress; | ||
assembly { | ||
firstAuthorizedAddress := mload(add(_authorizedAddresses, 20)) | ||
} | ||
emit WalletCreated(address(ret), firstAuthorizedAddress, false); | ||
} | ||
|
||
/** | ||
* calculate the counterfactual address of this account as it would be returned by createAccount() | ||
*/ | ||
function getAddress(address _cosigner, address _recoveryAddress, uint256 _salt) public view returns (address) { | ||
bytes32 salt = keccak256(abi.encodePacked(_salt, _cosigner, _recoveryAddress)); | ||
return Create2.computeAddress( | ||
bytes32(salt), keccak256(abi.encodePacked(type(BloctoAccountProxy).creationCode, abi.encode(address(this)))) | ||
); | ||
} | ||
|
||
/** | ||
* set the implementation of the BloctoAccountProxy | ||
* @param _bloctoAccountImplementation target to send to | ||
*/ | ||
function setImplementation(address _bloctoAccountImplementation) public onlyOwner { | ||
bloctoAccountImplementation = _bloctoAccountImplementation; | ||
} | ||
|
||
/** | ||
* set the entrypoint | ||
* @param _entrypoint target entrypoint | ||
*/ | ||
function setEntrypoint(IEntryPoint _entrypoint) public onlyOwner { | ||
entryPoint = _entrypoint; | ||
} | ||
|
||
/** | ||
* withdraw value from the deposit | ||
* @param withdrawAddress target to send to | ||
* @param amount to withdraw | ||
*/ | ||
function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { | ||
entryPoint.withdrawTo(withdrawAddress, amount); | ||
} | ||
|
||
/** | ||
* add stake for this factory. | ||
* This method can also carry eth value to add to the current stake. | ||
* @param unstakeDelaySec - the unstake delay for this factory. Can only be increased. | ||
*/ | ||
function addStake(uint32 unstakeDelaySec) external payable onlyOwner { | ||
entryPoint.addStake{value: msg.value}(unstakeDelaySec); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
|
||
contract BloctoAccountProxy is ERC1967Proxy, Initializable { | ||
constructor(address _logic) ERC1967Proxy(_logic, new bytes(0)) {} | ||
|
||
/** | ||
* initialize BloctoAccountProxy for adding the implementation address | ||
* @param implementation implementation address | ||
*/ | ||
function initImplementation(address implementation) public initializer { | ||
require(Address.isContract(implementation), "ERC1967: new implementation is not a contract"); | ||
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = implementation; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.12; | ||
|
||
/// @title ECDSA is a library that contains useful methods for working with ECDSA signatures | ||
library BytesExtractSignature { | ||
/// @notice Extracts the r, s, and v components from the `sigData` field starting from the `offset` | ||
/// @dev Note: does not do any bounds checking on the arguments! | ||
/// @param sigData the signature data; could be 1 or more packed signatures. | ||
/// @param offset the offset in sigData from which to start unpacking the signature components. | ||
function extractSignature( | ||
bytes memory sigData, | ||
uint256 offset | ||
) internal pure returns (bytes32 r, bytes32 s, uint8 v) { | ||
// Divide the signature in r, s and v variables | ||
// ecrecover takes the signature parameters, and the only way to get them | ||
// currently is to use assembly. | ||
// solium-disable-next-line security/no-inline-assembly | ||
assembly { | ||
let dataPointer := add(sigData, offset) | ||
r := mload(add(dataPointer, 0x20)) | ||
s := mload(add(dataPointer, 0x40)) | ||
v := byte(0, mload(add(dataPointer, 0x60))) | ||
} | ||
|
||
return (r, s, v); | ||
} | ||
} |
Oops, something went wrong.