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

Add social sign in with smart accounts #1

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion apps/contracts/contracts/X2EarnApp.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.23;

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
Expand Down
3 changes: 1 addition & 2 deletions apps/contracts/contracts/interfaces/IX2EarnRewardsPool.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;
pragma solidity 0.8.23;

/**
* @title IX2EarnRewardsPool
Expand Down
3 changes: 1 addition & 2 deletions apps/contracts/contracts/mocks/X2EarnRewardsPool.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;
pragma solidity 0.8.28;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
Expand Down
230 changes: 230 additions & 0 deletions apps/contracts/contracts/smart-accounts/accounts/SimpleAccount.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import "../core/BaseAccount.sol";
import "../core/Helpers.sol";
import "./callback/TokenCallbackHandler.sol";

/**
* minimal account.
* this is sample minimal account.
* has execute, eth handling methods
* has a single signer that can send requests through the entryPoint.
*/
contract SimpleAccount is
BaseAccount,
Initializable,
TokenCallbackHandler,
EIP712Upgradeable,
UUPSUpgradeable
{
address public owner;

IEntryPoint private immutable _entryPoint;

event SimpleAccountInitialized(
IEntryPoint indexed entryPoint,
address indexed owner
);

modifier onlyOwner() {
_onlyOwner();
_;
}

/// @inheritdoc BaseAccount
function entryPoint() public view virtual override returns (IEntryPoint) {
return _entryPoint;
}

// solhint-disable-next-line no-empty-blocks
receive() external payable {}

constructor(IEntryPoint anEntryPoint) {
_entryPoint = anEntryPoint;
_disableInitializers();
}

function _onlyOwner() internal view {
//directly from EOA owner, or through the account itself (which gets redirected through execute())
require(
msg.sender == owner || msg.sender == address(this),
"only owner"
);
}

/**
* execute a transaction (called directly from owner, or by entryPoint)
* @param dest destination address to call
* @param value the value to pass in this call
* @param func the calldata to pass in this call
*/
function execute(
address dest,
uint256 value,
bytes calldata func
) external {
_requireFromEntryPointOrOwner();
_call(dest, value, func);
}

/**
* execute a transaction (called directly from owner, or by entryPoint) authorized via signatures

* @param to destination address to call
* @param value the value to pass in this call
* @param data the calldata to pass in this call
* @param validAfter unix timestamp after which the signature will be accepted
* @param validBefore unix timestamp until the signature will be accepted
* @param signature the signed type4 signature
*/
function executeWithAuthorization(
address to,
uint256 value,
bytes calldata data,
uint256 validAfter,
uint256 validBefore,
bytes calldata signature
) external payable {
require(block.timestamp > validAfter, "Authorization not yet valid");
require(block.timestamp < validBefore, "Authorization expired");

/**
* verify that the signature did sign the function call
*/
bytes32 structHash = keccak256(
abi.encode(
keccak256(
"ExecuteWithAuthorization(address to,uint256 value,bytes data,uint256 validAfter,uint256 validBefore)"
),
to,
value,
keccak256(data),
validAfter,
validBefore
)
);
bytes32 digest = _hashTypedDataV4(structHash);

address recoveredAddress = ECDSA.recover(digest, signature);
require(recoveredAddress == owner, "Invalid signer");

// execute the instruction
_call(to, value, data);
}

/**
* execute a sequence of transactions
* @dev to reduce gas consumption for trivial case (no value), use a zero-length array to mean zero value
* @param dest an array of destination addresses
* @param value an array of values to pass to each call. can be zero-length for no-value calls
* @param func an array of calldata to pass to each call
*/
function executeBatch(
address[] calldata dest,
uint256[] calldata value,
bytes[] calldata func
) external {
_requireFromEntryPointOrOwner();
require(
dest.length == func.length &&
(value.length == 0 || value.length == func.length),
"wrong array lengths"
);
if (value.length == 0) {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
}
} else {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], value[i], func[i]);
}
}
}

/**
* @dev The _entryPoint member is immutable, to reduce gas consumption. To upgrade EntryPoint,
* a new implementation of SimpleAccount must be deployed with the new EntryPoint address, then upgrading
* the implementation by calling `upgradeTo()`
* @param anOwner the owner (signer) of this account
*/
function initialize(address anOwner) public virtual initializer {
_initialize(anOwner);
__EIP712_init("Wallet", "1");
__UUPSUpgradeable_init();
}

function _initialize(address anOwner) internal virtual {
owner = anOwner;
emit SimpleAccountInitialized(_entryPoint, owner);
}

// Require the function call went through EntryPoint or owner
function _requireFromEntryPointOrOwner() internal view {
require(
msg.sender == address(entryPoint()) || msg.sender == owner,
"account: not Owner or EntryPoint"
);
}

/// implement template method of BaseAccount
function _validateSignature(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (uint256 validationData) {
bytes32 hash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
if (owner != ECDSA.recover(hash, userOp.signature))
return SIG_VALIDATION_FAILED;
return SIG_VALIDATION_SUCCESS;
}

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))
}
}
}

/**
* check current account deposit in the entryPoint
*/
function getDeposit() public view returns (uint256) {
return entryPoint().balanceOf(address(this));
}

/**
* deposit more funds for this account in the entryPoint
*/
function addDeposit() public payable {
entryPoint().depositTo{value: msg.value}(address(this));
}

/**
* withdraw value from the account's deposit
* @param withdrawAddress target to send to
* @param amount to withdraw
*/
function withdrawDepositTo(
address payable withdrawAddress,
uint256 amount
) public onlyOwner {
entryPoint().withdrawTo(withdrawAddress, amount);
}

function _authorizeUpgrade(
address newImplementation
) internal view override {
(newImplementation);
_onlyOwner();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

import "./SimpleAccount.sol";

/**
* A sample factory contract for SimpleAccount
* A UserOperations "initCode" holds the address of the factory, and a method call (to createAccount, in this sample factory).
* The factory's createAccount returns the target account address even if it is already installed.
* This way, the entryPoint.getSenderAddress() can be called either before or after the account is created.
*/
contract SimpleAccountFactory {
SimpleAccount public immutable accountImplementation;

constructor(IEntryPoint _entryPoint) {
accountImplementation = new SimpleAccount(_entryPoint);
}

/**
* create an account, and return its address.
* 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 owner,uint256 salt) public returns (SimpleAccount ret) {
address addr = getAddress(owner, salt);
uint256 codeSize = addr.code.length;
if (codeSize > 0) {
return SimpleAccount(payable(addr));
}
ret = SimpleAccount(payable(new ERC1967Proxy{salt : bytes32(salt)}(
address(accountImplementation),
abi.encodeCall(SimpleAccount.initialize, (owner))
)));
}

/**
* calculate the counterfactual address of this account as it would be returned by createAccount()
*/
function getAddress(address owner,uint256 salt) public view returns (address) {
return Create2.computeAddress(bytes32(salt), keccak256(abi.encodePacked(
type(ERC1967Proxy).creationCode,
abi.encode(
address(accountImplementation),
abi.encodeCall(SimpleAccount.initialize, (owner))
)
)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

/* solhint-disable no-empty-blocks */

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";

/**
* Token callback handler.
* Handles supported tokens' callbacks, allowing account receiving these tokens.
*/
abstract contract TokenCallbackHandler is IERC721Receiver, IERC1155Receiver {

function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}

function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155Received.selector;
}

function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155BatchReceived.selector;
}

function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
return
interfaceId == type(IERC721Receiver).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
}
Loading