Skip to content

Instantly share code, notes, and snippets.

@ex3ndr
Created July 31, 2022 03:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ex3ndr/6936b1d082bf46a2d6360d1acced4d73 to your computer and use it in GitHub Desktop.
Save ex3ndr/6936b1d082bf46a2d6360d1acced4d73 to your computer and use it in GitHub Desktop.
Smart Wallet Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "./modules/Crypto.sol";
contract SmartWallet {
struct Call {
address to;
uint256 value;
bytes data;
}
struct Request {
uint64 nonce;
uint64 expires;
uint256 refund;
Call[] calls;
}
address public owner;
uint64 public nonce;
constructor(address _owner) {
owner = _owner;
nonce = 0;
}
// Validate request parameters
function isValidRequest(Request calldata _data, bytes memory _signature)
external
view
returns (bool)
{
return _isValidRequest(_data, _signature);
}
// Execute arbitrary signed transaction
function execute(Request calldata _data, bytes memory _signature)
external
payable
{
require(_isValidRequest(_data, _signature), "Wallet: Invalid request");
// Update nonce
nonce = _data.nonce + 1;
// Execute calls
bool _success;
bytes memory _result;
for (uint i = 0; i < _data.calls.length; i++) {
Call memory call = _data.calls[i];
(_success, _result) = call.to.call{value: call.value}(call.data);
require(_success, "Wallet: Call failed");
}
}
function _isValidRequest(Request calldata _data, bytes memory _signature)
internal
view
returns (bool)
{
// Check signature
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 _hash = keccak256(
abi.encodePacked(prefix, keccak256(abi.encode(_data)))
);
require(_signature.length == 65, "Wallet: invalid signature length");
address signer = Crypto.recoverSigner(_hash, _signature, 0);
require(owner == signer, "Wallet: Invalid signer");
// Check nonce
require(nonce == _data.nonce, "Wallet: Invalid nonce");
// Check expiration
require(block.timestamp < _data.expires, "Wallet: Request expired");
// Check targets
for (uint i = 0; i < _data.calls.length; i++) {
Call memory call = _data.calls[i];
require(call.to != address(0), "Wallet: Invalid call");
}
return true;
}
function hashToSign(Request calldata _data)
external
pure
returns (bytes32)
{
return keccak256(abi.encode(_data));
}
receive() external payable {
// Just accept received ether
}
fallback() external payable {
// Just accept received ether
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment