Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(deps): bump @openzeppelin/contracts from 4.9.1 to 4.9.2 #11

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
# BloctoAccount & BloctoAccountFactory

## Test & Deploy
## Test

test
```
npx hardhat test test/entrypoint.test.ts
yarn test
```

deploy BloctoAccountFactory

## Deploy

deploy BloctoAccountCloneableWallet, BloctoAccountFactory, and addStake to BloctoAccountFactory

```
yarn deploy-accountfactory --network mumbai
yarn deploy --network mumbai
```


deploy VerifyingPaymaster
```
yarn deploy-verifyingpaymaster --network mumbai
```


verify BloctoAccountCloneableWallet
```
yarn verify-bloctoaccountcloneable --network mumbai
```


verify BloctoAccountFactory
```
yarn verify-accountfactory --network mumbai
```

verify VerifyingPaymaster
```
yarn verify-verifyingpaymaster --network mumbai
```

## Tool

check storage layout
```
npx hardhat check
```

## Acknowledgement

Expand Down
130 changes: 130 additions & 0 deletions contracts/BloctoAccount.sol
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);
}
}
16 changes: 16 additions & 0 deletions contracts/BloctoAccountCloneableWallet.sol
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;
}
}
121 changes: 121 additions & 0 deletions contracts/BloctoAccountFactory.sol
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);
}
}
18 changes: 18 additions & 0 deletions contracts/BloctoAccountProxy.sol
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;
}
}
27 changes: 27 additions & 0 deletions contracts/CoreWallet/BytesExtractSignature.sol
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);
}
}
Loading