zk-agent-cli 0.1.0-beta.1 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/builtin-account-profiles/artifacts/daily-spend-limit/Account.json +693 -0
- package/dist/builtin-account-profiles/artifacts/sed-lite/Account.json +970 -0
- package/dist/builtin-account-profiles/artifacts/sed-lite/EOAValidator.json +50 -0
- package/dist/builtin-account-profiles/artifacts/sed-lite/NativePerTxLimitHook.json +267 -0
- package/dist/builtin-account-profiles/artifacts/sed-lite/TargetAllowlistHook.json +346 -0
- package/dist/builtin-account-profiles/artifacts/sed-lite/TargetSelectorAllowlistHook.json +643 -0
- package/dist/builtin-account-profiles/contracts/daily-spend-limit/AAFactory.sol +28 -0
- package/dist/builtin-account-profiles/contracts/daily-spend-limit/Account.sol +179 -0
- package/dist/builtin-account-profiles/contracts/daily-spend-limit/SpendLimit.sol +85 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/Account.sol +230 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/Auth.sol +16 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/BootloaderAuth.sol +11 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/EOAValidator.sol +23 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/IValidationHook.sol +20 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/IValidator.sol +11 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/ModuleAuth.sol +11 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/ModuleManager.sol +46 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/NativePerTxLimitHook.sol +67 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/OwnerManager.sol +25 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/SelfAuth.sol +9 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/TargetAllowlistHook.sol +110 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/TargetSelectorAllowlistHook.sol +263 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/ValidationHookManager.sol +76 -0
- package/dist/builtin-account-profiles/contracts/sed-lite/ValidatorManager.sol +31 -0
- package/dist/builtin-account-profiles/package.json +5 -0
- package/dist/index.js +4 -3
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/Constants.sol';
|
|
5
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/SystemContractsCaller.sol';
|
|
6
|
+
|
|
7
|
+
contract AAFactory {
|
|
8
|
+
bytes32 public aaBytecodeHash;
|
|
9
|
+
|
|
10
|
+
constructor(bytes32 _aaBytecodeHash) {
|
|
11
|
+
aaBytecodeHash = _aaBytecodeHash;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function deployAccount(bytes32 salt, address owner) external returns (address accountAddress) {
|
|
15
|
+
(bool success, bytes memory returnData) = SystemContractsCaller.systemCallWithReturndata(
|
|
16
|
+
uint32(gasleft()),
|
|
17
|
+
address(DEPLOYER_SYSTEM_CONTRACT),
|
|
18
|
+
uint128(0),
|
|
19
|
+
abi.encodeCall(
|
|
20
|
+
DEPLOYER_SYSTEM_CONTRACT.create2Account,
|
|
21
|
+
(salt, aaBytecodeHash, abi.encode(owner), IContractDeployer.AccountAbstractionVersion.Version1)
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
require(success, 'Deployment failed');
|
|
25
|
+
|
|
26
|
+
(accountAddress) = abi.decode(returnData, (address));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/interfaces/IAccount.sol';
|
|
5
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/TransactionHelper.sol';
|
|
6
|
+
import '@openzeppelin/contracts/interfaces/IERC1271.sol';
|
|
7
|
+
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
|
|
8
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/Constants.sol';
|
|
9
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/SystemContractsCaller.sol';
|
|
10
|
+
import './SpendLimit.sol';
|
|
11
|
+
|
|
12
|
+
contract Account is IAccount, IERC1271, SpendLimit {
|
|
13
|
+
using TransactionHelper for Transaction;
|
|
14
|
+
|
|
15
|
+
address public owner;
|
|
16
|
+
|
|
17
|
+
bytes4 constant EIP1271_SUCCESS_RETURN_VALUE = 0x1626ba7e;
|
|
18
|
+
|
|
19
|
+
modifier onlyBootloader() {
|
|
20
|
+
require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, 'Only bootloader can call this method');
|
|
21
|
+
_;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor(address _owner) {
|
|
25
|
+
owner = _owner;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateTransaction(
|
|
29
|
+
bytes32,
|
|
30
|
+
bytes32 _suggestedSignedHash,
|
|
31
|
+
Transaction calldata _transaction
|
|
32
|
+
) external payable override onlyBootloader returns (bytes4 magic) {
|
|
33
|
+
return _validateTransaction(_suggestedSignedHash, _transaction);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _validateTransaction(
|
|
37
|
+
bytes32 _suggestedSignedHash,
|
|
38
|
+
Transaction calldata _transaction
|
|
39
|
+
) internal returns (bytes4 magic) {
|
|
40
|
+
SystemContractsCaller.systemCallWithPropagatedRevert(
|
|
41
|
+
uint32(gasleft()),
|
|
42
|
+
address(NONCE_HOLDER_SYSTEM_CONTRACT),
|
|
43
|
+
0,
|
|
44
|
+
abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce))
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
bytes32 txHash;
|
|
48
|
+
if (_suggestedSignedHash == bytes32(0)) {
|
|
49
|
+
txHash = _transaction.encodeHash();
|
|
50
|
+
} else {
|
|
51
|
+
txHash = _suggestedSignedHash;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
uint256 totalRequiredBalance = _totalRequiredBalance(_transaction);
|
|
55
|
+
require(totalRequiredBalance <= address(this).balance, 'Not enough balance for fee + value');
|
|
56
|
+
|
|
57
|
+
if (isValidSignature(txHash, _transaction.signature) == EIP1271_SUCCESS_RETURN_VALUE) {
|
|
58
|
+
magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC;
|
|
59
|
+
} else {
|
|
60
|
+
magic = bytes4(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function executeTransaction(
|
|
65
|
+
bytes32,
|
|
66
|
+
bytes32,
|
|
67
|
+
Transaction calldata _transaction
|
|
68
|
+
) external payable override onlyBootloader {
|
|
69
|
+
_executeTransaction(_transaction);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function _executeTransaction(Transaction calldata _transaction) internal {
|
|
73
|
+
address to = address(uint160(_transaction.to));
|
|
74
|
+
uint128 value = _transactionMsgValue(_transaction);
|
|
75
|
+
bytes memory data = _transaction.data;
|
|
76
|
+
|
|
77
|
+
if (value > 0) {
|
|
78
|
+
_checkSpendingLimit(address(BASE_TOKEN_SYSTEM_CONTRACT), value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (to == address(DEPLOYER_SYSTEM_CONTRACT)) {
|
|
82
|
+
uint32 gas = Utils.safeCastToU32(gasleft());
|
|
83
|
+
SystemContractsCaller.systemCallWithPropagatedRevert(gas, to, value, data);
|
|
84
|
+
} else {
|
|
85
|
+
bool success;
|
|
86
|
+
assembly {
|
|
87
|
+
success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
|
|
88
|
+
}
|
|
89
|
+
require(success, 'Account call failed');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function executeTransactionFromOutside(Transaction calldata _transaction) external payable {
|
|
94
|
+
bytes4 magic = _validateTransaction(bytes32(0), _transaction);
|
|
95
|
+
require(magic == ACCOUNT_VALIDATION_SUCCESS_MAGIC, 'NOT VALIDATED');
|
|
96
|
+
_executeTransaction(_transaction);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isValidSignature(bytes32 _hash, bytes memory _signature)
|
|
100
|
+
public
|
|
101
|
+
view
|
|
102
|
+
override
|
|
103
|
+
returns (bytes4 magic)
|
|
104
|
+
{
|
|
105
|
+
magic = EIP1271_SUCCESS_RETURN_VALUE;
|
|
106
|
+
|
|
107
|
+
if (_signature.length != 65) {
|
|
108
|
+
_signature = new bytes(65);
|
|
109
|
+
_signature[64] = bytes1(uint8(27));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
uint8 v;
|
|
113
|
+
bytes32 r;
|
|
114
|
+
bytes32 s;
|
|
115
|
+
assembly {
|
|
116
|
+
r := mload(add(_signature, 0x20))
|
|
117
|
+
s := mload(add(_signature, 0x40))
|
|
118
|
+
v := and(mload(add(_signature, 0x41)), 0xff)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (v != 27 && v != 28) {
|
|
122
|
+
magic = bytes4(0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
|
|
126
|
+
magic = bytes4(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
address recoveredAddress = ecrecover(_hash, v, r, s);
|
|
130
|
+
if (recoveredAddress != owner && recoveredAddress != address(0)) {
|
|
131
|
+
magic = bytes4(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function payForTransaction(
|
|
136
|
+
bytes32,
|
|
137
|
+
bytes32,
|
|
138
|
+
Transaction calldata _transaction
|
|
139
|
+
) external payable override onlyBootloader {
|
|
140
|
+
bool success = _transaction.payToTheBootloader();
|
|
141
|
+
require(success, 'Failed to pay the fee to the operator');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function prepareForPaymaster(
|
|
145
|
+
bytes32,
|
|
146
|
+
bytes32,
|
|
147
|
+
Transaction calldata _transaction
|
|
148
|
+
) external payable override onlyBootloader {
|
|
149
|
+
_transaction.processPaymasterInput();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fallback() external {
|
|
153
|
+
assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
receive() external payable {}
|
|
157
|
+
|
|
158
|
+
function _transactionMsgValue(Transaction calldata _transaction) internal pure returns (uint128) {
|
|
159
|
+
uint256 value = _transaction.reserved[1];
|
|
160
|
+
if (value == 0) {
|
|
161
|
+
value = _transaction.value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return Utils.safeCastToU128(value);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function _totalRequiredBalance(Transaction calldata _transaction) internal pure returns (uint256) {
|
|
168
|
+
uint256 value = _transaction.reserved[1];
|
|
169
|
+
if (value == 0) {
|
|
170
|
+
value = _transaction.value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (address(uint160(_transaction.paymaster)) != address(0)) {
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return _transaction.maxFeePerGas * _transaction.gasLimit + value;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
contract SpendLimit {
|
|
5
|
+
uint256 public constant ONE_DAY = 24 hours;
|
|
6
|
+
|
|
7
|
+
struct Limit {
|
|
8
|
+
uint256 limit;
|
|
9
|
+
uint256 available;
|
|
10
|
+
uint256 resetTime;
|
|
11
|
+
bool isEnabled;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
mapping(address => Limit) public limits;
|
|
15
|
+
|
|
16
|
+
modifier onlyAccount() {
|
|
17
|
+
require(msg.sender == address(this), 'Only the account contract can call this method.');
|
|
18
|
+
_;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setSpendingLimit(address _token, uint256 _amount) public onlyAccount {
|
|
22
|
+
require(_amount != 0, 'Invalid amount');
|
|
23
|
+
|
|
24
|
+
uint256 resetTime;
|
|
25
|
+
uint256 timestamp = block.timestamp;
|
|
26
|
+
|
|
27
|
+
if (isValidUpdate(_token)) {
|
|
28
|
+
resetTime = timestamp + ONE_DAY;
|
|
29
|
+
} else {
|
|
30
|
+
resetTime = timestamp;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_updateLimit(_token, _amount, _amount, resetTime, true);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function removeSpendingLimit(address _token) public onlyAccount {
|
|
37
|
+
require(isValidUpdate(_token), 'Invalid Update');
|
|
38
|
+
_updateLimit(_token, 0, 0, 0, false);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isValidUpdate(address _token) internal view returns (bool) {
|
|
42
|
+
if (limits[_token].isEnabled) {
|
|
43
|
+
require(
|
|
44
|
+
limits[_token].limit == limits[_token].available || block.timestamp > limits[_token].resetTime,
|
|
45
|
+
'Invalid Update'
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _updateLimit(
|
|
55
|
+
address _token,
|
|
56
|
+
uint256 _limit,
|
|
57
|
+
uint256 _available,
|
|
58
|
+
uint256 _resetTime,
|
|
59
|
+
bool _isEnabled
|
|
60
|
+
) private {
|
|
61
|
+
Limit storage limit = limits[_token];
|
|
62
|
+
limit.limit = _limit;
|
|
63
|
+
limit.available = _available;
|
|
64
|
+
limit.resetTime = _resetTime;
|
|
65
|
+
limit.isEnabled = _isEnabled;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function _checkSpendingLimit(address _token, uint256 _amount) internal {
|
|
69
|
+
Limit memory limit = limits[_token];
|
|
70
|
+
if (!limit.isEnabled) return;
|
|
71
|
+
|
|
72
|
+
uint256 timestamp = block.timestamp;
|
|
73
|
+
|
|
74
|
+
if (limit.limit != limit.available && timestamp > limit.resetTime) {
|
|
75
|
+
limit.resetTime = timestamp + ONE_DAY;
|
|
76
|
+
limit.available = limit.limit;
|
|
77
|
+
} else if (limit.limit == limit.available) {
|
|
78
|
+
limit.resetTime = timestamp + ONE_DAY;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
require(limit.available >= _amount, 'Exceeds daily limit');
|
|
82
|
+
limit.available -= _amount;
|
|
83
|
+
limits[_token] = limit;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/interfaces/IAccount.sol';
|
|
5
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/TransactionHelper.sol';
|
|
6
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/EfficientCall.sol';
|
|
7
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/Constants.sol';
|
|
8
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/SystemContractsCaller.sol';
|
|
9
|
+
import '@openzeppelin/contracts/interfaces/IERC1271.sol';
|
|
10
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/Utils.sol';
|
|
11
|
+
import './EOAValidator.sol';
|
|
12
|
+
import './IValidator.sol';
|
|
13
|
+
import './OwnerManager.sol';
|
|
14
|
+
import './ValidatorManager.sol';
|
|
15
|
+
import './ModuleManager.sol';
|
|
16
|
+
import './ValidationHookManager.sol';
|
|
17
|
+
|
|
18
|
+
contract Account is
|
|
19
|
+
IAccount,
|
|
20
|
+
IERC1271,
|
|
21
|
+
OwnerManager,
|
|
22
|
+
ValidatorManager,
|
|
23
|
+
ModuleManager,
|
|
24
|
+
ValidationHookManager
|
|
25
|
+
{
|
|
26
|
+
using TransactionHelper for Transaction;
|
|
27
|
+
|
|
28
|
+
bytes4 private constant EIP1271_SUCCESS_RETURN_VALUE = 0x1626ba7e;
|
|
29
|
+
|
|
30
|
+
struct Call {
|
|
31
|
+
address target;
|
|
32
|
+
bool allowFailure;
|
|
33
|
+
uint256 value;
|
|
34
|
+
bytes callData;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
struct NativeSpendCap {
|
|
38
|
+
uint256 maxPerTx;
|
|
39
|
+
bool enabled;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
NativeSpendCap public nativeSpendCap;
|
|
43
|
+
event NativeSpendCapSet(uint256 maxPerTx);
|
|
44
|
+
event NativeSpendCapRemoved();
|
|
45
|
+
|
|
46
|
+
constructor(address _owner) {
|
|
47
|
+
_initializeOwner(_owner);
|
|
48
|
+
_setValidator(address(new EOAValidator()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validateTransaction(
|
|
52
|
+
bytes32,
|
|
53
|
+
bytes32 _suggestedSignedHash,
|
|
54
|
+
Transaction calldata _transaction
|
|
55
|
+
) external payable override onlyBootloader returns (bytes4 magic) {
|
|
56
|
+
return _validateTransaction(_suggestedSignedHash, _transaction);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function executeTransaction(
|
|
60
|
+
bytes32,
|
|
61
|
+
bytes32,
|
|
62
|
+
Transaction calldata _transaction
|
|
63
|
+
) external payable override onlyBootloader {
|
|
64
|
+
_executeTransaction(_transaction);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function executeTransactionFromOutside(Transaction calldata _transaction)
|
|
68
|
+
external
|
|
69
|
+
payable
|
|
70
|
+
override
|
|
71
|
+
{
|
|
72
|
+
require(msg.sender == owner, 'Only owner can call this method');
|
|
73
|
+
bytes4 magic = _validateTransaction(bytes32(0), _transaction);
|
|
74
|
+
require(magic == ACCOUNT_VALIDATION_SUCCESS_MAGIC, 'NOT VALIDATED');
|
|
75
|
+
_executeTransaction(_transaction);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function payForTransaction(
|
|
79
|
+
bytes32,
|
|
80
|
+
bytes32,
|
|
81
|
+
Transaction calldata _transaction
|
|
82
|
+
) external payable override onlyBootloader {
|
|
83
|
+
bool success = _transaction.payToTheBootloader();
|
|
84
|
+
require(success, 'Failed to pay the fee to the operator');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function prepareForPaymaster(
|
|
88
|
+
bytes32,
|
|
89
|
+
bytes32,
|
|
90
|
+
Transaction calldata _transaction
|
|
91
|
+
) external payable override onlyBootloader {
|
|
92
|
+
_transaction.processPaymasterInput();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function setNativeSpendCap(uint256 maxPerTx) external onlySelf {
|
|
96
|
+
require(maxPerTx > 0, 'Spend cap must be greater than zero');
|
|
97
|
+
nativeSpendCap = NativeSpendCap({ maxPerTx: maxPerTx, enabled: true });
|
|
98
|
+
emit NativeSpendCapSet(maxPerTx);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function removeNativeSpendCap() external onlySelf {
|
|
102
|
+
delete nativeSpendCap;
|
|
103
|
+
emit NativeSpendCapRemoved();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function executeBatch(Call[] calldata calls) external onlySelf {
|
|
107
|
+
uint256 length = calls.length;
|
|
108
|
+
for (uint256 i = 0; i < length; i += 1) {
|
|
109
|
+
Call calldata calli = calls[i];
|
|
110
|
+
_executeCall(
|
|
111
|
+
calli.target,
|
|
112
|
+
Utils.safeCastToU128(calli.value),
|
|
113
|
+
calli.callData,
|
|
114
|
+
calli.allowFailure
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isValidSignature(bytes32 _hash, bytes memory _signature)
|
|
120
|
+
public
|
|
121
|
+
view
|
|
122
|
+
override
|
|
123
|
+
returns (bytes4 magic)
|
|
124
|
+
{
|
|
125
|
+
address currentValidator = validator;
|
|
126
|
+
if (currentValidator == address(0)) {
|
|
127
|
+
return bytes4(0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
address recoveredAddress = IK1Validator(currentValidator).validateSignature(_hash, _signature);
|
|
131
|
+
if (recoveredAddress != owner || recoveredAddress == address(0)) {
|
|
132
|
+
return bytes4(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return EIP1271_SUCCESS_RETURN_VALUE;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fallback() external {
|
|
139
|
+
assert(msg.sender != BOOTLOADER_FORMAL_ADDRESS);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
receive() external payable {}
|
|
143
|
+
|
|
144
|
+
function _validateTransaction(
|
|
145
|
+
bytes32 _suggestedSignedHash,
|
|
146
|
+
Transaction calldata _transaction
|
|
147
|
+
) internal returns (bytes4 magic) {
|
|
148
|
+
SystemContractsCaller.systemCallWithPropagatedRevert(
|
|
149
|
+
uint32(gasleft()),
|
|
150
|
+
address(NONCE_HOLDER_SYSTEM_CONTRACT),
|
|
151
|
+
0,
|
|
152
|
+
abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce))
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
bytes32 txHash = _suggestedSignedHash == bytes32(0)
|
|
156
|
+
? _transaction.encodeHash()
|
|
157
|
+
: _suggestedSignedHash;
|
|
158
|
+
|
|
159
|
+
require(
|
|
160
|
+
_transaction.totalRequiredBalance() <= address(this).balance,
|
|
161
|
+
'Not enough balance for fee + value'
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
uint256 nativeValue = _transactionMsgValue(_transaction);
|
|
165
|
+
if (nativeSpendCap.enabled && nativeValue > nativeSpendCap.maxPerTx) {
|
|
166
|
+
revert('Native transfer exceeds per-tx cap');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
_runValidationHooks(txHash, _transaction);
|
|
170
|
+
|
|
171
|
+
if (isValidSignature(txHash, _transaction.signature) == EIP1271_SUCCESS_RETURN_VALUE) {
|
|
172
|
+
return ACCOUNT_VALIDATION_SUCCESS_MAGIC;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return bytes4(0);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function _executeTransaction(Transaction calldata _transaction) internal {
|
|
179
|
+
_executeCall(
|
|
180
|
+
address(uint160(_transaction.to)),
|
|
181
|
+
Utils.safeCastToU128(_transactionMsgValue(_transaction)),
|
|
182
|
+
_transaction.data,
|
|
183
|
+
false
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function _transactionMsgValue(Transaction calldata _transaction) internal pure returns (uint256) {
|
|
188
|
+
if (_transaction.reserved[1] != 0) {
|
|
189
|
+
return _transaction.reserved[1];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return _transaction.value;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function _executeCall(
|
|
196
|
+
address to,
|
|
197
|
+
uint128 value,
|
|
198
|
+
bytes calldata data,
|
|
199
|
+
bool allowFailure
|
|
200
|
+
) internal {
|
|
201
|
+
uint32 gasToPass = Utils.safeCastToU32(gasleft());
|
|
202
|
+
|
|
203
|
+
if (to == address(DEPLOYER_SYSTEM_CONTRACT)) {
|
|
204
|
+
(bool deploymentSuccess, bytes memory returnData) = SystemContractsCaller.systemCallWithReturndata(
|
|
205
|
+
gasToPass,
|
|
206
|
+
to,
|
|
207
|
+
value,
|
|
208
|
+
data
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!deploymentSuccess && !allowFailure) {
|
|
212
|
+
assembly {
|
|
213
|
+
let size := mload(returnData)
|
|
214
|
+
revert(add(returnData, 0x20), size)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
bool success = EfficientCall.rawCall(gasToPass, to, value, data, false);
|
|
222
|
+
if (!success && !allowFailure) {
|
|
223
|
+
EfficientCall.propagateRevert();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function _executeModuleCall(address to, uint128 value, bytes calldata data) internal override {
|
|
228
|
+
_executeCall(to, value, data, false);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import './BootloaderAuth.sol';
|
|
5
|
+
import './SelfAuth.sol';
|
|
6
|
+
import './ModuleAuth.sol';
|
|
7
|
+
|
|
8
|
+
abstract contract Auth is BootloaderAuth, SelfAuth, ModuleAuth {
|
|
9
|
+
modifier onlySelfOrModule() {
|
|
10
|
+
require(
|
|
11
|
+
msg.sender == address(this) || _isModule(msg.sender),
|
|
12
|
+
'Only the account contract or an enabled module can call this method'
|
|
13
|
+
);
|
|
14
|
+
_;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/Constants.sol';
|
|
5
|
+
|
|
6
|
+
abstract contract BootloaderAuth {
|
|
7
|
+
modifier onlyBootloader() {
|
|
8
|
+
require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, 'Only bootloader can call this method');
|
|
9
|
+
_;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
|
|
5
|
+
import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
|
|
6
|
+
import './IValidator.sol';
|
|
7
|
+
|
|
8
|
+
contract EOAValidator is IK1Validator {
|
|
9
|
+
using ECDSA for bytes32;
|
|
10
|
+
|
|
11
|
+
function validateSignature(
|
|
12
|
+
bytes32 signedHash,
|
|
13
|
+
bytes calldata signature
|
|
14
|
+
) external pure override returns (address signer) {
|
|
15
|
+
(signer, , ) = signedHash.tryRecover(signature);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
|
|
19
|
+
return
|
|
20
|
+
interfaceId == type(IK1Validator).interfaceId ||
|
|
21
|
+
interfaceId == type(IERC165).interfaceId;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/TransactionHelper.sol';
|
|
5
|
+
import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
|
|
6
|
+
|
|
7
|
+
interface IInitable {
|
|
8
|
+
event Inited(address indexed account);
|
|
9
|
+
event Disabled(address indexed account);
|
|
10
|
+
|
|
11
|
+
function init(bytes calldata initData) external;
|
|
12
|
+
|
|
13
|
+
function disable() external;
|
|
14
|
+
|
|
15
|
+
function isInited(address account) external view returns (bool);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface IValidationHook is IInitable, IERC165 {
|
|
19
|
+
function validationHook(bytes32 signedHash, Transaction calldata transaction) external;
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@openzeppelin/contracts/utils/introspection/IERC165.sol';
|
|
5
|
+
|
|
6
|
+
interface IK1Validator is IERC165 {
|
|
7
|
+
function validateSignature(
|
|
8
|
+
bytes32 signedHash,
|
|
9
|
+
bytes calldata signature
|
|
10
|
+
) external view returns (address signer);
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
abstract contract ModuleAuth {
|
|
5
|
+
function _isModule(address addr) internal view virtual returns (bool);
|
|
6
|
+
|
|
7
|
+
modifier onlyModule() {
|
|
8
|
+
require(_isModule(msg.sender), 'Only enabled modules can call this method');
|
|
9
|
+
_;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import '@matterlabs/zksync-contracts/contracts/system-contracts/libraries/Utils.sol';
|
|
5
|
+
import './Auth.sol';
|
|
6
|
+
|
|
7
|
+
abstract contract ModuleManager is Auth {
|
|
8
|
+
mapping(address => bool) public modules;
|
|
9
|
+
|
|
10
|
+
event ModuleAdded(address indexed module);
|
|
11
|
+
event ModuleRemoved(address indexed module);
|
|
12
|
+
|
|
13
|
+
function addModule(address module) external onlySelf {
|
|
14
|
+
_addModule(module);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function removeModule(address module) external onlySelf {
|
|
18
|
+
_removeModule(module);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function executeFromModule(address to, uint256 value, bytes calldata data) external onlyModule {
|
|
22
|
+
require(to != address(this), 'Recursive module calls are not allowed');
|
|
23
|
+
_executeModuleCall(to, Utils.safeCastToU128(value), data);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _isModule(address addr) internal view virtual override returns (bool) {
|
|
27
|
+
return modules[addr];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function _addModule(address module) internal {
|
|
31
|
+
require(module != address(0), 'Module must not be zero');
|
|
32
|
+
require(module != address(this), 'Account can not be a module');
|
|
33
|
+
require(module.code.length > 0, 'Module must be a deployed contract');
|
|
34
|
+
require(!modules[module], 'Module already enabled');
|
|
35
|
+
modules[module] = true;
|
|
36
|
+
emit ModuleAdded(module);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _removeModule(address module) internal {
|
|
40
|
+
require(modules[module], 'Module is not enabled');
|
|
41
|
+
delete modules[module];
|
|
42
|
+
emit ModuleRemoved(module);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function _executeModuleCall(address to, uint128 value, bytes calldata data) internal virtual;
|
|
46
|
+
}
|