Created
July 31, 2022 03:39
-
-
Save ex3ndr/6936b1d082bf46a2d6360d1acced4d73 to your computer and use it in GitHub Desktop.
Smart Wallet Contract
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
// 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