文件 1 的 11:AddressUtil.sol
pragma solidity ^0.7.0;
library AddressUtil
{
using AddressUtil for *;
function isContract(
address addr
)
internal
view
returns (bool)
{
bytes32 codehash;
assembly { codehash := extcodehash(addr) }
return (codehash != 0x0 &&
codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470);
}
function toPayable(
address addr
)
internal
pure
returns (address payable)
{
return payable(addr);
}
function sendETH(
address to,
uint amount,
uint gasLimit
)
internal
returns (bool success)
{
if (amount == 0) {
return true;
}
address payable recipient = to.toPayable();
(success,) = recipient.call{value: amount, gas: gasLimit}("");
}
function sendETHAndVerify(
address to,
uint amount,
uint gasLimit
)
internal
returns (bool success)
{
success = to.sendETH(amount, gasLimit);
require(success, "TRANSFER_FAILURE");
}
function fastCall(
address to,
uint gasLimit,
uint value,
bytes memory data
)
internal
returns (bool success, bytes memory returnData)
{
if (to != address(0)) {
assembly {
success := call(gasLimit, to, value, add(data, 32), mload(data), 0, 0)
let size := returndatasize()
returnData := mload(0x40)
mstore(returnData, size)
returndatacopy(add(returnData, 32), 0, size)
mstore(0x40, add(returnData, add(32, size)))
}
}
}
function fastCallAndVerify(
address to,
uint gasLimit,
uint value,
bytes memory data
)
internal
returns (bytes memory returnData)
{
bool success;
(success, returnData) = fastCall(to, gasLimit, value, data);
if (!success) {
assembly {
revert(add(returnData, 32), mload(returnData))
}
}
}
}
文件 2 的 11:BytesUtil.sol
pragma solidity ^0.7.0;
library BytesUtil {
function slice(
bytes memory _bytes,
uint _start,
uint _length
)
internal
pure
returns (bytes memory)
{
require(_bytes.length >= (_start + _length));
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
tempBytes := mload(0x40)
let lengthmod := and(_length, 31)
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
mstore(0x40, and(add(mc, 31), not(31)))
}
default {
tempBytes := mload(0x40)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) {
require(_bytes.length >= (_start + 20));
address tempAddress;
assembly {
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) {
require(_bytes.length >= (_start + 1));
uint8 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x1), _start))
}
return tempUint;
}
function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) {
require(_bytes.length >= (_start + 2));
uint16 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x2), _start))
}
return tempUint;
}
function toUint24(bytes memory _bytes, uint _start) internal pure returns (uint24) {
require(_bytes.length >= (_start + 3));
uint24 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x3), _start))
}
return tempUint;
}
function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) {
require(_bytes.length >= (_start + 4));
uint32 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x4), _start))
}
return tempUint;
}
function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) {
require(_bytes.length >= (_start + 8));
uint64 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x8), _start))
}
return tempUint;
}
function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) {
require(_bytes.length >= (_start + 12));
uint96 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0xc), _start))
}
return tempUint;
}
function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) {
require(_bytes.length >= (_start + 16));
uint128 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x10), _start))
}
return tempUint;
}
function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) {
require(_bytes.length >= (_start + 32));
uint256 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x20), _start))
}
return tempUint;
}
function toBytes4(bytes memory _bytes, uint _start) internal pure returns (bytes4) {
require(_bytes.length >= (_start + 4));
bytes4 tempBytes4;
assembly {
tempBytes4 := mload(add(add(_bytes, 0x20), _start))
}
return tempBytes4;
}
function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) {
require(_bytes.length >= (_start + 32));
bytes32 tempBytes32;
assembly {
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
}
return tempBytes32;
}
function fastSHA256(
bytes memory data
)
internal
view
returns (bytes32)
{
bytes32[] memory result = new bytes32[](1);
bool success;
assembly {
let ptr := add(data, 32)
success := staticcall(sub(gas(), 2000), 2, ptr, mload(data), add(result, 32), 32)
}
require(success, "SHA256_FAILED");
return result[0];
}
}
文件 3 的 11:Create2.sol
pragma solidity ^0.7.0;
library Create2 {
function deploy(bytes32 salt, bytes memory bytecode) internal returns (address payable) {
address payable addr;
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "CREATE2_FAILED");
return addr;
}
function computeAddress(bytes32 salt, bytes memory bytecode) internal view returns (address) {
return computeAddress(salt, bytecode, address(this));
}
function computeAddress(bytes32 salt, bytes memory bytecodeHash, address deployer) internal pure returns (address) {
bytes32 bytecodeHashHash = keccak256(bytecodeHash);
bytes32 _data = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHashHash)
);
return address(bytes20(_data << 96));
}
}
文件 4 的 11:EIP712.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
library EIP712
{
struct Domain {
string name;
string version;
address verifyingContract;
}
bytes32 constant internal EIP712_DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
string constant internal EIP191_HEADER = "\x19\x01";
function hash(Domain memory domain)
internal
pure
returns (bytes32)
{
uint _chainid;
assembly { _chainid := chainid() }
return keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(domain.name)),
keccak256(bytes(domain.version)),
_chainid,
domain.verifyingContract
)
);
}
function hashPacked(
bytes32 domainSeparator,
bytes32 dataHash
)
internal
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
EIP191_HEADER,
domainSeparator,
dataHash
)
);
}
}
文件 5 的 11:ERC1271.sol
pragma solidity ^0.7.0;
abstract contract ERC1271 {
bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e;
function isValidSignature(
bytes32 _hash,
bytes memory _signature)
public
view
virtual
returns (bytes4 magicValue);
}
文件 6 的 11:ILoopringWalletV2.sol
pragma solidity ^0.7.0;
abstract contract ILoopringWalletV2
{
function initialize(
address owner,
address[] calldata guardians,
uint quota,
address inheritor,
address feeRecipient,
address feeToken,
uint feeAmount
)
external
virtual;
function getCreationTimestamp()
public
view
virtual
returns (uint64);
function getOwner()
public
view
virtual
returns (address);
}
文件 7 的 11:MathUint.sol
pragma solidity ^0.7.0;
library MathUint
{
function mul(
uint a,
uint b
)
internal
pure
returns (uint c)
{
c = a * b;
require(a == 0 || c / a == b, "MUL_OVERFLOW");
}
function sub(
uint a,
uint b
)
internal
pure
returns (uint)
{
require(b <= a, "SUB_UNDERFLOW");
return a - b;
}
function add(
uint a,
uint b
)
internal
pure
returns (uint c)
{
c = a + b;
require(c >= a, "ADD_OVERFLOW");
}
}
文件 8 的 11:SignatureUtil.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "../thirdparty/BytesUtil.sol";
import "./AddressUtil.sol";
import "./ERC1271.sol";
import "./MathUint.sol";
library SignatureUtil
{
using BytesUtil for bytes;
using MathUint for uint;
using AddressUtil for address;
enum SignatureType {
ILLEGAL,
INVALID,
EIP_712,
ETH_SIGN,
WALLET
}
bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e;
function verifySignatures(
bytes32 signHash,
address[] memory signers,
bytes[] memory signatures
)
internal
view
returns (bool)
{
require(signers.length == signatures.length, "BAD_SIGNATURE_DATA");
address lastSigner;
for (uint i = 0; i < signers.length; i++) {
require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER");
lastSigner = signers[i];
if (!verifySignature(signHash, signers[i], signatures[i])) {
return false;
}
}
return true;
}
function verifySignature(
bytes32 signHash,
address signer,
bytes memory signature
)
internal
view
returns (bool)
{
if (signer == address(0)) {
return false;
}
return signer.isContract()?
verifyERC1271Signature(signHash, signer, signature):
verifyEOASignature(signHash, signer, signature);
}
function recoverECDSASigner(
bytes32 signHash,
bytes memory signature
)
internal
pure
returns (address)
{
if (signature.length != 65) {
return address(0);
}
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := and(mload(add(signature, 0x41)), 0xff)
}
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return address(0);
}
if (v == 27 || v == 28) {
return ecrecover(signHash, v, r, s);
} else {
return address(0);
}
}
function verifyEOASignature(
bytes32 signHash,
address signer,
bytes memory signature
)
private
pure
returns (bool success)
{
if (signer == address(0)) {
return false;
}
uint signatureTypeOffset = signature.length.sub(1);
SignatureType signatureType = SignatureType(signature.toUint8(signatureTypeOffset));
assembly {
mstore(signature, signatureTypeOffset)
}
if (signatureType == SignatureType.EIP_712) {
success = (signer == recoverECDSASigner(signHash, signature));
} else if (signatureType == SignatureType.ETH_SIGN) {
bytes32 hash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", signHash)
);
success = (signer == recoverECDSASigner(hash, signature));
} else {
success = false;
}
assembly {
mstore(signature, add(signatureTypeOffset, 1))
}
return success;
}
function verifyERC1271Signature(
bytes32 signHash,
address signer,
bytes memory signature
)
private
view
returns (bool)
{
bytes memory callData = abi.encodeWithSelector(
ERC1271.isValidSignature.selector,
signHash,
signature
);
(bool success, bytes memory result) = signer.staticcall(callData);
return (
success &&
result.length == 32 &&
result.toBytes4(0) == ERC1271_MAGICVALUE
);
}
}
文件 9 的 11:WalletDeploymentLib.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "../thirdparty/Create2.sol";
import "../thirdparty/proxies/WalletProxy.sol";
contract WalletDeploymentLib
{
address public immutable walletImplementation;
string public constant WALLET_CREATION = "WALLET_CREATION";
constructor(
address _walletImplementation
)
{
walletImplementation = _walletImplementation;
}
function getWalletCode()
public
view
returns (bytes memory)
{
return abi.encodePacked(
type(WalletProxy).creationCode,
abi.encode(walletImplementation)
);
}
function computeWalletSalt(
address owner,
uint salt
)
public
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
WALLET_CREATION,
owner,
salt
)
);
}
function _deploy(
address owner,
uint salt
)
internal
returns (address payable wallet)
{
wallet = Create2.deploy(
computeWalletSalt(owner, salt),
getWalletCode()
);
}
function _computeWalletAddress(
address owner,
uint salt,
address deployer
)
internal
view
returns (address)
{
return Create2.computeAddress(
computeWalletSalt(owner, salt),
getWalletCode(),
deployer
);
}
}
文件 10 的 11:WalletFactory.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "../iface/ILoopringWalletV2.sol";
import "../lib/EIP712.sol";
import "../lib/SignatureUtil.sol";
import "./WalletDeploymentLib.sol";
contract WalletFactory is WalletDeploymentLib
{
using SignatureUtil for bytes32;
event WalletCreated (address wallet, address owner);
bytes32 public immutable DOMAIN_SEPARATOR;
bytes32 public constant CREATE_WALLET_TYPEHASH = keccak256(
"createWallet(address owner,address[] guardians,uint256 quota,address inheritor,address feeRecipient,address feeToken,uint256 maxFeeAmount,uint256 salt)");
struct WalletConfig
{
address owner;
address[] guardians;
uint quota;
address inheritor;
address feeRecipient;
address feeToken;
uint maxFeeAmount;
uint salt;
bytes signature;
}
constructor(
address _walletImplementation
)
WalletDeploymentLib(_walletImplementation)
{
DOMAIN_SEPARATOR = EIP712.hash(
EIP712.Domain("WalletFactory", "2.0.0", address(this))
);
}
function createWallet(
WalletConfig calldata config,
uint feeAmount
)
external
returns (address wallet)
{
require(feeAmount <= config.maxFeeAmount, "INVALID_FEE_AMOUNT");
_validateConfig(config);
wallet = _deploy(config.owner, config.salt);
_initializeWallet(wallet, config, feeAmount);
}
function computeWalletAddress(
address owner,
uint salt
)
public
view
returns (address)
{
return _computeWalletAddress(
owner,
salt,
address(this)
);
}
function _initializeWallet(
address wallet,
WalletConfig calldata config,
uint feeAmount
)
internal
{
ILoopringWalletV2(wallet).initialize(
config.owner,
config.guardians,
config.quota,
config.inheritor,
config.feeRecipient,
config.feeToken,
feeAmount
);
emit WalletCreated(wallet, config.owner);
}
function _validateConfig(
WalletConfig calldata config
)
private
view
{
require(config.owner != address(0), "INVALID_OWNER");
bytes32 dataHash = keccak256(
abi.encode(
CREATE_WALLET_TYPEHASH,
config.owner,
keccak256(abi.encodePacked(config.guardians)),
config.quota,
config.inheritor,
config.feeRecipient,
config.feeToken,
config.maxFeeAmount,
config.salt
)
);
bytes32 signHash = EIP712.hashPacked(DOMAIN_SEPARATOR, dataHash);
require(signHash.verifySignature(config.owner, config.signature), "INVALID_SIGNATURE");
}
}
文件 11 的 11:WalletProxy.sol
pragma solidity ^0.7.0;
interface IProxy {
function masterCopy() external view returns (address);
}
contract WalletProxy {
address internal masterCopy;
constructor(address _masterCopy)
{
require(_masterCopy != address(0), "Invalid master copy address provided");
masterCopy = _masterCopy;
}
fallback()
payable
external
{
assembly {
let _masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
mstore(0, _masterCopy)
return(0, 0x20)
}
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas(), _masterCopy, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) { revert(0, returndatasize()) }
return(0, returndatasize())
}
}
}
{
"compilationTarget": {
"contracts/base/WalletFactory.sol": "WalletFactory"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_walletImplementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"WalletCreated","type":"event"},{"inputs":[],"name":"CREATE_WALLET_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WALLET_CREATION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"computeWalletAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"computeWalletSalt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address[]","name":"guardians","type":"address[]"},{"internalType":"uint256","name":"quota","type":"uint256"},{"internalType":"address","name":"inheritor","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"maxFeeAmount","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct WalletFactory.WalletConfig","name":"config","type":"tuple"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"createWallet","outputs":[{"internalType":"address","name":"wallet","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getWalletCode","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"walletImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]