Description:
Smart contract deployed on Ethereum with Factory features.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/eth/eip7702.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.30;
interface IERC20Like {
function transfer(address to, uint256 value) external returns (bool);
function balanceOf(address owner) external view returns (uint256);
}
library SafeERC20 {
function safeTransfer(IERC20Like token, address to, uint256 value) internal {
(bool success, bytes memory data) =
address(token).call(abi.encodeWithSelector(token.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeERC20:TRANSFER_FAIL");
}
}
interface IGlobalConfig {
// flags and fee
function globalPaused() external view returns (bool);
function enforceExactNet() external view returns (bool);
function useTokenAllowlist() external view returns (bool);
function feeRecipient() external view returns (address);
function allowAdminSweep() external view returns (bool);
// allowlists
function isSponsorAllowed(address who) external view returns (bool);
function isTokenAllowed(address token) external view returns (bool);
function getAllSponsors() external view returns (address[] memory);
function getAllTokens() external view returns (address[] memory);
}
contract GasSponsoredAccount7702 {
using SafeERC20 for IERC20Like;
// immutable global config
IGlobalConfig public immutable GLOBAL_CONFIG;
constructor(address globalConfig_) {
require(globalConfig_ != address(0), "GlobalConfigZero");
GLOBAL_CONFIG = IGlobalConfig(globalConfig_);
}
// ===== Constants =====
bytes4 private constant _MAGIC_BYTES32 = 0x1626ba7e; // EIP-1271
bytes4 private constant _MAGIC_BYTES = 0x20c13b0b; // isValidSignature(bytes)
// ===== Events =====
event GaslessTransferExecuted(
address indexed account,
address indexed sponsor,
address token,
address to,
uint256 grossAmount,
uint256 feeAmount,
uint256 netAmount,
address feeRecipient,
uint256 nonce,
bytes32 digest,
bytes32 indexed id
);
event TokenSwept(address indexed token, address indexed to, uint256 amount, uint256 nonce, bytes32 digest);
event ETHSwept(address indexed to, uint256 amount, uint256 nonce, bytes32 digest);
// ===== Storage (EIP-7201 style custom slot) =====
bytes32 private constant _ACCOUNT_STORAGE_SLOT =
bytes32(uint256(keccak256("allscale.7702.storage")) - 1);
struct AccountStorage {
// nonces
uint256 opNonce;
uint256 adminNonce;
// reentrancy guard (0 = unlocked, 1 = locked)
uint256 reentrancyGuard;
// EIP-712 cache
bytes32 cachedDomainSeparator;
uint256 cachedChainId;
address cachedThis;
// id anti-replay
mapping(bytes32 => bool) usedId;
}
function _getAccountStorage() private pure returns (AccountStorage storage s) {
bytes32 slot = _ACCOUNT_STORAGE_SLOT;
assembly ("memory-safe") {s.slot := slot}
}
// ===== Modifiers =====
modifier nonReentrant() {
AccountStorage storage s = _getAccountStorage();
require(s.reentrancyGuard == 0, "REENTRANCY");
s.reentrancyGuard = 1;
_;
s.reentrancyGuard = 0;
}
modifier nonPaused() {
require(!GLOBAL_CONFIG.globalPaused(), "PAUSED");
_;
}
receive() external payable {}
fallback() external payable {}
// ===== EIP-712 =====
string private constant _NAME = "AllScaleEIP7702";
string private constant _VERSION = "1";
bytes32 private constant _EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
function _domainSeparator() internal returns (bytes32) {
AccountStorage storage s = _getAccountStorage();
if (s.cachedChainId == block.chainid && s.cachedThis == address(this) && s.cachedDomainSeparator != bytes32(0)) {
return s.cachedDomainSeparator;
}
bytes32 ds = keccak256(
abi.encode(
_EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(_NAME)),
keccak256(bytes(_VERSION)),
block.chainid,
address(this)
)
);
s.cachedChainId = block.chainid;
s.cachedThis = address(this);
s.cachedDomainSeparator = ds;
return ds;
}
/// 非 view(要缓存到存储里)
function domainSeparator() external returns (bytes32) {
return _domainSeparator();
}
// ---- Typed data typehashes(完整字符串,去掉省略号)----
bytes32 private constant TRANSFER_TYPEHASH = keccak256(
"GaslessTransfer(address sponsor,address token,address to,uint256 amount,uint256 feeAmount,uint256 deadline,uint256 nonce,bytes32 id)"
);
bytes32 private constant SWEEP_TOKEN_TYPEHASH = keccak256(
"SweepToken(address token,address to,uint256 amount,uint256 nonce,uint256 deadline)"
);
bytes32 private constant SWEEP_ETH_TYPEHASH = keccak256(
"SweepETH(address to,uint256 amount,uint256 nonce,uint256 deadline)"
);
// ===== Views =====
function getOpNonce() external view returns (uint256) {return _getAccountStorage().opNonce;}
function getNonce() external view returns (uint256) {return _getAccountStorage().adminNonce;}
// ===== EIP-1271 (optional) =====
function isValidSignature(bytes32 hash, bytes memory sig) external pure returns (bytes4) {
return hash != bytes32(0) && sig.length != 0 ? _MAGIC_BYTES32 : bytes4(0);
}
function isValidSignature(bytes memory data, bytes memory sig) external pure returns (bytes4) {
return data.length != 0 && sig.length != 0 ? _MAGIC_BYTES : bytes4(0);
}
// ===== Internal helpers =====
function _hashTypedDataV4(bytes32 structHash) internal returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash));
}
function _recover(bytes32 digest, bytes memory signature) internal pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
if (signature.length == 65) {
assembly ("memory-safe") {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
v := byte(0, mload(add(signature, 96)))
}
} else if (signature.length == 64) {
assembly ("memory-safe") {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
}
v = uint8((uint256(s) >> 255) + 27);
s = bytes32(uint256(s) & ((1 << 255) - 1));
} else {
revert("BAD_SIG_LEN");
}
require(v == 27 || v == 28, "BAD_V");
address signer = ecrecover(digest, v, r, s);
require(signer != address(0), "ECRECOVER_ZERO");
return signer;
}
function _requireDeadline(uint256 deadline) internal view {
require(deadline >= block.timestamp, "EXPIRED");
}
function _verifyAndBumpOp(bytes32 structHash, bytes calldata signature) internal returns (bytes32 digest, uint256 usedNonce) {
AccountStorage storage s = _getAccountStorage();
usedNonce = s.opNonce;
digest = _hashTypedDataV4(structHash);
address signer = _recover(digest, signature);
require(signer == address(this), "BadSigner");
unchecked {s.opNonce = usedNonce + 1;}
}
function _verifyAndBumpAdmin(bytes32 structHash, bytes calldata signature) internal returns (bytes32) {
bytes32 digest = _hashTypedDataV4(structHash);
address signer = _recover(digest, signature);
require(signer == address(this), "BadSigner");
AccountStorage storage s = _getAccountStorage();
unchecked {s.adminNonce += 1;}
return digest;
}
function _transferExactNet(IERC20Like token, address to, uint256 sendAmount) internal {
uint256 beforeBal = token.balanceOf(to);
(bool ok, bytes memory data) =
address(token).call(abi.encodeWithSelector(token.transfer.selector, to, sendAmount));
require(ok && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAIL");
uint256 delta = token.balanceOf(to) - beforeBal;
require(delta == sendAmount, "NET_MISMATCH");
}
// ===== Business entry (global-config driven) =====
function gaslessTransfer(
address token,
address to,
uint256 amount,
uint256 feeAmount,
uint256 deadline,
bytes32 id,
bytes calldata signature
) external nonReentrant nonPaused {
require(amount > 0, "ZeroAmount");
require(token != address(0) && to != address(0), "ZeroAddr");
require(address(token).code.length > 0, "TokenNotContract");
require(feeAmount <= amount, "FeeGTAmount");
_requireDeadline(deadline);
// Sponsor allowlist
require(GLOBAL_CONFIG.isSponsorAllowed(msg.sender), "SPONSOR_DENY");
// Optional token allowlist
if (GLOBAL_CONFIG.useTokenAllowlist()) {
require(GLOBAL_CONFIG.isTokenAllowed(token), "TOKEN_DENY");
}
// id anti-replay
AccountStorage storage s = _getAccountStorage();
require(!s.usedId[id], "ID_USED");
s.usedId[id] = true;
// Verify signature & bump opNonce
(bytes32 digest, uint256 usedNonce) = _verifyAndBumpOp(
keccak256(
abi.encode(
TRANSFER_TYPEHASH,
msg.sender,
token,
to,
amount,
feeAmount,
deadline,
s.opNonce,
id
)
),
signature
);
// Transfers
IERC20Like t = IERC20Like(token);
address feeRecvForEvent = GLOBAL_CONFIG.feeRecipient();
require(feeRecvForEvent != address(0), "FEE_ZERO");
if (feeAmount > 0) {
if (GLOBAL_CONFIG.enforceExactNet()) {
_transferExactNet(t, feeRecvForEvent, feeAmount);
} else {
t.safeTransfer(feeRecvForEvent, feeAmount);
}
}
uint256 net = amount - feeAmount;
if (GLOBAL_CONFIG.enforceExactNet()) {
_transferExactNet(t, to, net);
} else {
t.safeTransfer(to, net);
}
emit GaslessTransferExecuted(
address(this), msg.sender, token, to, amount, feeAmount, net, feeRecvForEvent, usedNonce, digest, id
);
}
// ===== Admin sweeps (受 GlobalConfig.allowAdminSweep 控制,且同样严格校验) =====
function sweepToken(address token, address to, uint256 amount, uint256 deadline, bytes calldata signature)
external nonReentrant nonPaused
{
require(GLOBAL_CONFIG.allowAdminSweep(), "ADMIN_SWEEP_OFF");
require(token != address(0) && to != address(0), "ZeroAddr");
require(address(token).code.length > 0, "TokenNotContract");
_requireDeadline(deadline);
AccountStorage storage s = _getAccountStorage();
uint256 used = s.adminNonce;
bytes32 digest = _verifyAndBumpAdmin(
keccak256(abi.encode(SWEEP_TOKEN_TYPEHASH, token, to, amount, used, deadline)), signature
);
IERC20Like(token).safeTransfer(to, amount);
emit TokenSwept(token, to, amount, used, digest);
}
function sweepETH(address to, uint256 amount, uint256 deadline, bytes calldata signature)
external nonReentrant nonPaused
{
require(GLOBAL_CONFIG.allowAdminSweep(), "ADMIN_SWEEP_OFF");
require(to != address(0), "ZeroAddr");
_requireDeadline(deadline);
AccountStorage storage s = _getAccountStorage();
uint256 used = s.adminNonce;
bytes32 digest = _verifyAndBumpAdmin(
keccak256(abi.encode(SWEEP_ETH_TYPEHASH, to, amount, used, deadline)), signature
);
(bool ok,) = to.call{value: amount}("");
require(ok, "ETH_SEND_FAIL");
emit ETHSwept(to, amount, used, digest);
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-29 10:04:35
Comments
Log in to comment.
No comments yet.