Skip to content

Instantly share code, notes, and snippets.

@uF4No
Last active April 24, 2023 16:33
Show Gist options
  • Save uF4No/659a633db7d427474e2349180a5c0cfd to your computer and use it in GitHub Desktop.
Save uF4No/659a633db7d427474e2349180a5c0cfd to your computer and use it in GitHub Desktop.
zkSync Era smart contract account
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol";
import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";
// Access zkSync system contracts for nonce validation via NONCE_HOLDER_SYSTEM_CONTRACT
import "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol";
// to call non-view function of system contracts
import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol";
contract SmartAccount is IAccount {
// to get transaction hash
using TransactionHelper for Transaction;
bytes4 constant EIP1271_SUCCESS_RETURN_VALUE = 0x1626ba7e;
modifier onlyBootloader() {
require(
msg.sender == BOOTLOADER_FORMAL_ADDRESS,
"Only bootloader can call this function"
);
// Continue execution if called from the bootloader.
_;
}
function validateTransaction(
bytes32, // _txHash
bytes32 _suggestedSignedHash,
Transaction calldata _transaction
) external payable override onlyBootloader returns (bytes4 magic) {
// Incrementing the nonce of the account.
SystemContractsCaller.systemCallWithPropagatedRevert(
uint32(gasleft()), address(NONCE_HOLDER_SYSTEM_CONTRACT),0,
abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_transaction.nonce))
);
// Verify account has balance to pay fee
uint256 totalRequiredBalance = _transaction.totalRequiredBalance();
require(totalRequiredBalance <= address(this).balance, "Not enough balance for fee + value");
// Verify signature
if (isValidSignature(txHash, _transaction.signature) == EIP1271_SUCCESS_RETURN_VALUE) {
magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC;
} else {
magic = bytes4(0);
}
}
function executeTransaction(
bytes32, // _txHash
bytes32, // _suggestedSignedHash
Transaction calldata _transaction
) external payable override onlyBootloader {
address to = address(uint160(_transaction.to));
uint128 value = Utils.safeCastToU128(_transaction.value);
bytes memory data = _transaction.data;
if (to == address(DEPLOYER_SYSTEM_CONTRACT)) {
uint32 gas = Utils.safeCastToU32(gasleft());
SystemContractsCaller.systemCallWithPropagatedRevert(gas, to, value, data);
} else {
bool success;
assembly {
success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
}
require(success);
}
}
function payForTransaction(
bytes32, // _txHash
bytes32, // _suggestedSignedHash
Transaction calldata _transaction
) external payable override onlyBootloader {
bool success = _transaction.payToTheBootloader();
require(success, "Failed to pay the fee to the operator");
}
function prepareForPaymaster(
bytes32, // _txHash
bytes32, // _suggestedSignedHash
Transaction calldata _transaction
) external payable override onlyBootloader {
_transaction.processPaymasterInput();
}
function executeTransactionFromOutside(Transaction calldata _transaction)
external
payable
{
validateTransaction(bytes32(0), _transaction);
executeTransaction(_transaction);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment