Skip to content

pluto1011/ERC4337_introduction

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 

Repository files navigation

๋ชจ๋“  ๋ฌธ์„œ๋Š” ํ‘œ์ค€ ๊ตฌํ˜„์ฒด์ธ eth-infinitism์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑํ•จ.

์ฃผ์„์„ ๋งŽ์ด ๋‹ฌ์•„ ๋ฌธ์„œ์˜ ๊ธธ์ด๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜๋„ ์žˆ์Œ. ๋‹จ, ๊ฐ€๋…์„ฑ์€ ๋ณด์žฅํ•จ.

dockers/workdir/bundler.config.json

{
  "mnemonic": "mnemonicfile.txt",
  "network": "https://goerli.infura.io/v3/f40be2b1a3914db682491dc62a19ad43",
  "beneficiary": "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1",
  // fee๋ฅผ receive๋ฐ›์„ ์ฃผ์†Œ. ํ”„๋กœ์ ํŠธ์˜ ์ž๊ธˆ์ค„ ์ฃผ์†Œ.
  "port": "80",
  "helper": "0x214fadBD244c07ECb9DCe782270d3b673cAD0f9c",
  "entryPoint": "0x602aB3881Ff3Fa8dA60a8F44Cf633e91bA1FdB69",
  // ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ๊ทธ entrypoint. config์—๋‹ค๊ฐ€ ๋„ฃ์–ด๋†“์€ ์ด์œ ๋Š” ์ƒˆ ๋ฒ„์ „ ๋‚˜์˜ฌ๋•Œ๋งˆ๋‹ค ๊ฐˆ์•„๋ผ์šฐ๊ธฐ ์šฉ์ดํ•ด์„œ์ธ๋“ฏ?? 
  "minBalance": "0",
  "gasFactor": "1"
}
// packages/bundler/src/BundlerServer.ts

  async handleMethod (method: string, params: any[]): Promise<any> {
  //ํƒ€์ž…์Šคํฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ method์—๋‹ค๊ฐ€ ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์„œ switchํ•˜๊ณ  params๋ฅผ array๋กœ ๋„ฃ์–ด์„œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋„ฃ๋‚˜๋ด„. 
    let result: any
    switch (method) {
      case 'eth_chainId':
        // eslint-disable-next-line no-case-declarations
        const { chainId } = await this.provider.getNetwork()
        result = chainId
        break
      case 'eth_supportedEntryPoints':
        result = await this.methodHandler.getSupportedEntryPoints()
        break
      case 'eth_sendUserOperation': // useroperation, entrypointaddress๋ฅผ ๋ฐ›์•„๊ฐ
        result = await this.methodHandler.sendUserOperation(params[0], params[1])
        break
 and more....
  // add userOp into the mempool, after initial validation.
  // replace existing, if any (and if new gas is higher)
  // revets if unable to add UserOp to mempool (too many UserOps with this sender)
  // packages/bundler/src/modules/MempoolManager.ts

/*
	์ด๊ฑฐ ์ง„์งœ ์†”๋ฆฌ๋””ํ‹ฐ ์•„๋‹ˆ๋„ค ๋ฏธ์นœ;; adduseropํ•จ์ˆ˜๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ํ˜ธ์ถœํ•ด์„œ ๋ฉคํ’€์— ๋˜์ง€๋Š” ์‹์œผ๋กœ ์ด๋ฃจ์–ด์งˆ ๊ฑฐ ๊ฐ™์€๋ฐ, ์ด๊ฑฐ DB๊ฐ€ ์–ด๋””์— ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Œ. tendermint๋‚˜ geth๋Š” kvstore๊ธฐ๋ฐ˜ db์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ชจ๋“  ๋…ธ๋“œ๋“คํ•œํ…Œ ์ œ๊ณตํ•˜๊ณ  ๋ถ„์‚ฐํ•ด์„œ ์ €์žฅํ•˜๊ฒŒ๋” ํ•ด๋†“๊ธด ํ–ˆ๋Š”๋ฐ. ์–˜๋Š” ์‚ฌ์‹ค ํŠน์ • ๊ธฐ์—…์ด ์šด์˜ํ•˜๋Š” ๊ฑฐ๋ผ์„œ ๋ถ„์‚ฐ ์ž์ฒด๊ฐ€ ํ•„์š”์—†์ด ํ•˜๋‚˜์˜ ์„œ๋ฒ„์— ๋‹ค ๋˜์ง€๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด๋ฃจ์–ด์งˆ ์ˆ˜๋„ ์žˆ๊ฑฐ๋“ ์š”??? ๊ทผ๋ฐ ๊ทธ๊ฑฐ๋ž‘ ๋ณ„๊ฐœ๋กœ '์ฒด์ธ'์„ ๋งŒ๋“ค์–ด์•ผ ๋ถ„์‚ฐ์›์žฅ ์‹œ์Šคํ…œ์ด ๋Œ์•„๊ฐ€๋Š”๋ฐ ํ•ด๋‹น userOp๋ผ๋Š” TX์šฉ ์ฟผ๋ฆฌ๋ฅผ ์•„์— ์˜๊ตฌ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†์ž–์•„์š”. ๊ทธ๋ž˜์„œ ํฌ๊ฒŒ ๋ฐ์ดํ„ฐ ์ €์žฅ๊ณต๊ฐ„์ด ํ•„์š”ํ•˜์ง€๋Š” ์•Š์„ ๊ฑฐ ๊ฐ™์Œ. ์ด๊ฑด ์ €ํฌ ํด๋ผ์ด์–ธํŠธ ์ •์ฑ… ๋”ฐ๋ผ์„œ ๋ฒจ๋ฆฌ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„์ง€ ์—†์„์ง€ ์œ ๋ฌด๋กœ ํŒ๋‹จํ•ด์•ผ ํ• ๊ฑฐ ๊ฐ™์•„์š”. (๊ทผ๋ฐ ํƒ€์ž…์Šคํฌ?? ๋ผ์„œ ๊ณผ์—ฐ ์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค๋”ง์„ ์ง„ํ–‰ํ• ์ง€๋Š” ๋ฏธ์ง€์ˆ˜.)
์ผ๋‹จ ํ‘œ์ค€์— ์ถ”๊ฐ€๋˜์–ด์žˆ๊ธด ํ•˜๋‹ˆ๊นŒ ํฌ๋งท์€ ๊ฑฐ๊ธฐ์„œ ๊ฑฐ๊ธฐ์ผ ๋“ฏ??? 

*/
  addUserOp (userOp: UserOperation, userOpHash: string, prefund: BigNumberish, senderInfo: StakeInfo, referencedContracts: ReferencedCodeHashes, aggregator?: string): void {
    const entry: MempoolEntry = {
      userOp,
      userOpHash,
      prefund,
      referencedContracts,
      aggregator
    }
    const index = this._findBySenderNonce(userOp.sender, userOp.nonce)
    if (index !== -1) {
      const oldEntry = this.mempool[index]
      this.checkReplaceUserOp(oldEntry, entry)
      debug('replace userOp', userOp.sender, userOp.nonce)
      this.mempool[index] = entry
    } else {
      debug('add userOp', userOp.sender, userOp.nonce)
      this.entryCount[userOp.sender] = (this.entryCount[userOp.sender] ?? 0) + 1
      this.checkSenderCountInMempool(userOp, senderInfo)
      this.mempool.push(entry)
    }
    this.updateSeenStatus(aggregator, userOp)
  }
contract EntryPoint {
    // ...
    function handleOps( // sigAggregation ํ•„์š”์—†์œผ๋ฉด ์ด๊ฑฐ ํ˜ธ์ถœํ•จ
        UserOperation[] calldata ops,
        address payable beneficiary
    ) public nonReentrant {
        uint256 opslen = ops.length;
        UserOpInfo[] memory opInfos = new UserOpInfo[](opslen);

        unchecked {
            for (uint256 i = 0; i < opslen; i++) {
                UserOpInfo memory opInfo = opInfos[i];
                (
                    uint256 validationData,
                    uint256 pmValidationData
                ) = _validatePrepayment(i, ops[i], opInfo);

// *_validatePrepayment*
// validation์— ๋“ค์–ด๊ฐ€๋Š” ๊ฐ€์Šค๋น„๊ฐ€ ์‚ฌ์ „์— ๊ฒ€์ฆ์— ์‚ฌ์šฉ๋˜๋Š” ๊ฐ€์Šค๋น„ ์ƒํ•œ์„ ์„ ๋„˜๋Š”์ง€ ์•ˆ๋„˜๋Š”์ง€ ์ฒดํฌํ•จ
// _validateAccountPrepayment๋ผ๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๋งŒ์•ฝ AA ๊ณ„์ •์ด ์—†์œผ๋ฉด ๋งŒ๋“ค์–ด์คŒ
                
                _validateAccountAndPaymasterValidationData(
                    i,
                    validationData,
                    pmValidationData,
                    address(0)
                );
// *_validateAccountAndPaymasterValidationData*
// ๋งŒ์•ฝ validationData๊ฐ€ ๋งŒ๋ฃŒ๋˜์—ˆ์œผ๋ฉด(expired) revertํ•จ.
            }

            uint256 collected = 0;
            emit BeforeExecution();

            for (uint256 i = 0; i < opslen; i++) {
                collected += _executeUserOp(i, ops[i], opInfos[i]);
            }
// *_executeUserOp*
// ๊ฐœ๋ณ„ UserOperation์„ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜.
// ์ง์„ค์ ์œผ๋กœ ๋งํ•˜๋ฉด UserOperation์˜ calldata๋ฅผ ์›”๋ › ์ปจํŠธ๋ž™ํŠธ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๊ฑฐ๊ณ 
//์‰ฝ๊ฒŒ ์ด์•ผ๊ธฐํ•˜๋ฉด TX์‹คํ–‰ํ•˜๋Š” ๊ฑฐ.
//ํ•ด๋‹น ํ•จ์ˆ˜๋„ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋‚ด๋ถ€ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ ๋Š” ์žˆ๋Š”๋ฐ, ์ผ๋‹จ์€ ๊ฐ€์žฅ ์ค‘์š”ํ•œ innerHandleOpํ•จ์ˆ˜๋งŒ ๋จผ์ € ๊ฐ€์ ธ์™”์–ด์š”

            _compensate(beneficiary, collected);
        }
    }
    // ...
}
contract EntryPoint { 
    // ...
    function innerHandleOp(
        bytes memory callData,
        UserOpInfo memory opInfo,
        bytes calldata context
    ) external returns (uint256 actualGasCost) {
        uint256 preGas = gasleft();
        
        require(msg.sender == address(this), "AA92 internal call only");
		// ์–˜๊ฐ€ ๋‹ค๋ฅธ ๊ฐ™์€ ์ปจํŠธ๋ž™ํŠธ์—์„œ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๊ฑฐ๋“ ์š”?? ๊ทผ๋ฐ ์™œ external์“ฐ๊ณ  
        // this.innerHandleOps ์ด๋Ÿฐ ์‹์œผ๋กœ ํ˜ธ์ถœํ•œ๊ฑด์ง€ ์ดํ•ด๊ฐ€ ์•ˆ๊ฐ ๊ทธ๋ƒฅ internal ์“ฐ๋ฉด ์•ˆ๋˜๋‚˜?


        // ...
        // check if handleOps was called with gas limit too low. abort entire bundle
        // ...
        // executes UserOperation on the wallet smart contract account and emits event if the operation reverts
            bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit);
        // ...
        // executes post operation
            return _handlePostOp(0, mode, opInfo, context, actualGas);
        // ...
    }
}
contract EntryPoint {

    // *handleAggregatedOps*
    // sigAggregation์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์ธ๋ฐ, ์ด๊ฒŒ ๊ทธ๊ฑฐ์—์š” Batch verification....
    
    function handleAggregatedOps(
        UserOpsPerAggregator[] calldata opsPerAggregator,
        address payable beneficiary
    ) public nonReentrant {

        uint256 opasLen = opsPerAggregator.length;
        uint256 totalOps = 0;
        for (uint256 i = 0; i < opasLen; i++) {
            UserOpsPerAggregator calldata opa = opsPerAggregator[i];
            UserOperation[] calldata ops = opa.userOps;
            IAggregator aggregator = opa.aggregator;

            //address(1) is special marker of "signature error"
            require(
                address(aggregator) != address(1),
                "AA96 invalid aggregator"
            );

            if (address(aggregator) != address(0)) {
                // solhint-disable-next-line no-empty-blocks
                try aggregator.validateSignatures(ops, opa.signature) {} catch {
                    revert SignatureValidationFailed(address(aggregator));
                }
            }

            totalOps += ops.length;
        }

        // ...
        for (uint256 a = 0; a < opasLen; a++) {
            // ... _validatePrepayment
            // ... _validateAccountAndPaymasterValidationData
        }

        // ...
        for (uint256 a = 0; a < opasLen; a++) {
            // ... _executeUserOp
        }
        // ...
        _compensate(beneficiary, collected);
    }
    // ...
}

ERC-4337 ๋…ผ์Šค

- ์™œ ํ•„์š”ํ•œ๊ฐ€?

  • ์žฌ์‹คํ–‰ ๊ณต๊ฒฉ ๋ฐฉ์ง€์šฉ
  • ํŠธ๋žœ์žญ์…˜ ์ˆœ์„œ ๋ณด์žฅ(๊ฐ ๋ช…๋ น๋‹น ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•  ๋ฌด์–ธ๊ฐ€๊ฐ€ ํ•„์š”ํ•จ)
  • TX ๊ณ ์œ ์„ฑ ํ™•๋ณด ๋ฐ ํŠธ๋ž˜ํ‚น ์šฉ์ด (layerzero stargate tracker๊ฐ™์€๊ฑฐ ๋งŒ๋“ค๊ธฐ ์‰ฌ์›€)

- ์–ด๋–ป๊ฒŒ ์“ฐ์ด๊ณ  ์žˆ๋Š”๊ฐ€?

v0.6๊ธฐ์ค€ Entrypoint๊ฐ€ NonceManager๋ฅผ ์ƒ์†๋ฐ›์•„ extension์‹์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฑฐ ๊ฐ™์€๋ฐ... ๋…ผ์Šค๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋‚˜๋ด„. Q. ๊ทธ๋Ÿผ ๊ฐ ๊ณ„์ •๋ณ„๋กœ ๋…ผ์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฑด๊ฐ€? Q. ์Šคํ† ๋ฆฌ์ง€๊ฐ’ ์•ˆ๋ถ€์กฑํ•˜๋‚˜???

- ์ด์ „ ๋ฒ„์ „๊ณผ ๋‹ฌ๋ผ์ง„ ์ ์€?

v0.4๋ฒ„์ „์—์„œ๋Š” ์ง€๊ฐ‘์ด ํ•ด๋‹น ๋…ผ์Šค๋ฅผ ๊ด€๋ฆฌํ–ˆ์—ˆ์Œ.

wallet architecture

For that, an important design goal is to replicate the key property of EOAs in which users do not need to perform any custom action to create their wallets. They can simply generate an address locally and immediately start accepting funds. The wallet creation is thus done by a โ€œfactoryโ€ contract, with wallet-specific data, which usesย CREATE2ย to create the wallet in a counterfactual (deterministic) address.

Wallet, ์ฆ‰ aa์ง€๊ฐ‘์€ ๊ธฐ๋ณธ์ ์œผ๋กœ create2ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์–ด์ง„๋‹ค.

* User Operation struct  
* @param sender - The sender account of this request.  
* @param nonce - Unique value the sender uses to verify it is not a replay.  
* @param initCode - If set, the account contract will be created by this constructor/

UserOperation ๊ตฌ์กฐ์ฒด ํ•„๋“œ ์ค‘์— initCode๊ฐ€ ์žˆ๋Š” ๊ฒŒ ๋ณด์ผ ํ…๋ฐ, ํ•ด๋‹น initCode์˜ ํ•„๋“œ๊ฐ’์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ entrypoint contractdptj getSenderAddress()๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ

// contracts/core/EntryPoint.sol line 450

    /// @inheritdoc IEntryPoint
    function getSenderAddress(bytes calldata initCode) public {
        address sender = senderCreator().createSender(initCode);
        revert SenderAddressResult(sender);
    }

ํ•ด๋‹น ํ•จ์ˆ˜๋Š” createsenderํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๋งŒ๋“  ๋‹ค์Œ์—, revert๋ฅผ ์‹œํ‚ค...๋„ค????? ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฐ˜ํ™˜๊ฐ’์„ ๋” ๊ฐ€์Šค efficientํ•˜๊ฒŒ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์„œ ๊ทธ๋Ÿฐ๊ฐ€ ๋ณด๋‹ค. try-catch๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ˆ๊นŒ. ๋ฌดํŠผ ๋‹ค์‹œ ๋Œ์•„์™€์„œ createSender์—์„œ๋Š” ๋ญ˜ ํ•˜๋А๋ƒ.

contract SenderCreator {
    /**
     * Call the "initCode" factory to create and return the sender account address.
     * @param initCode - The initCode value from a UserOp. contains 20 bytes of factory address,
     *                   followed by calldata.
     * @return sender  - The returned address of the created account, or zero address on failure.
     */
    function createSender(
        bytes calldata initCode
    ) external returns (address sender) {
        address factory = address(bytes20(initCode[0:20]));
        bytes memory initCallData = initCode[20:];
        bool success;
        /* solhint-disable no-inline-assembly */
        assembly {
            success := call(
                gas(),
                factory,
                0,
                add(initCallData, 0x20),
                mload(initCallData),
                0,
                32
            )
            sender := mload(0)
        }
        if (!success) {
            sender = address(0);
        }
    }
}

๋งจ๋‚  ๋ณด๋Š” ๊ทธ๊ฑฐํ•œ๋‹ค. create2์ด์šฉํ•ด์„œ ์–ด์…ˆ์œผ๋กœ ๊ณ„์ • ๋งŒ๋“œ๋Š” ๊ฑฐ...๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด initCode๋ฅผ ์ง„์งœ initbytecode๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฑฐ๋‹ค. ์ด๊ฒŒ ๋ฌด์Šจ ์†Œ๋ฆฌ๋ƒ, ํ•ด๋‹น initCode์— ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ผ์›Œ๋„ฃ๋“  ๊ฐ„์— ๊ทธ๊ฒŒ ๋Ÿฐํƒ€์ž„๋ฐ”์ดํŠธ์ฝ”๋“œ๋กœ ๋“ค์–ด๊ฐ„๋‹ค๋Š” ์†Œ๋ฆฌ์ž„(!!!!!!!!) ์ด๊ฑฐ ๋ฐ”์ดํŠธ์ฝ”๋“œ ๊ฒ€์ˆ˜ ์•ˆํ•ด๋„ ๋˜๋ƒ???? ์•…์˜์ ์ธ AA๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ ๊ฐ€๋Šฅํ•  ๊ฑฐ ๊ฐ™์€๋ฐ???

This is interesting, because, althoughย Infinitism's repositoryย provides aย BaseAccountย implementation of the IAccount interface and aย SimpleAccountย sample contract (created byย SimpleAccountFactory), none of these are actual requirements of the specification. Developers are free to customize the wallet and factory implementation in any way they choose, provided that they correctly perform the necessary validations according to the ERC.

์‚ฌ์‹ค infinitism repository, ์ฆ‰ ๋‹ค์˜ค ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ๋นŒ๋”ฉ์ค‘์ธ ํ•ด๋‹น erc์—์„  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ธฐ๋Š” ํ•˜๋Š”๋ฐ, ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํ‘œ์ค€์ด ์•„๋‹ˆ๋ผ๊ณ  ํ•จ;;;; ์ด๊ฒŒ ๋ฌธ์ œ์ธ๊ฒŒ, aa๋งŒ๋“œ๋Š” ๊ฑธ ์ „์ ์œผ๋กœ ์œ ์ € ์ฑ…์ž„์œผ๋กœ ๋Œ๋ฆฐ๋‹ค๋Š” ์†Œ๋ฆฌ์™€๋„ ๊ฐ™์€๋ฐ, ์ด๋Ÿฌ๋ฉด aa์ง€์›ํ•ด์ฃผ๋Š” ๊ธฐ์—…์ด ์‚ฌ๊ธฐ์น˜๊ณ  initcode์— ๋ฐฑ๋„์–ด ์‹ฌ์–ด๋‘๋ฉด ํ• ๋ง์ด ์—†๊ฑฐ๋“ ์š”... ๊ทธ๋ ‡๋‹ค๊ณ  ๊ผฌ์šฐ๋‹ˆ ๋‚ด๊ฐ€ ์ง์ ‘ userOp๋‚ ๋ฆฐ๋‹ค ํ•˜๊ธฐ์—๋Š” batch๋กœ ์ธํ•œ ์ด์ ์„ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๋Š”;;;์ผ์ด ๋ฐœ์ƒํ•จ. ์ฆ‰, AA๋Š” ๊ฒฐ๊ตญ ์ค‘์•™ํ™”๋œ entity์— ๋Œ€ํ•œ ์˜์กด๋„๊ฐ€ ๊ต‰์žฅํžˆ ๋†’๋‹ค๋Š” ๊ฑฐ๊ณ , ์ด๊ฒŒ ๋งž๋ƒ??????????

abstract contract BaseAccount is IAccount {
    // ...

    /**
     * Sends to the entrypoint (msg.sender) the missing funds for this transaction.
     * SubClass MAY override this method for better funds management
     * (e.g. send to the entryPoint more than the minimum required, so that in future transactions
     * it will not be required to send again).
     * @param missingAccountFunds - The minimum value this method should send the entrypoint.
     *                              This value MAY be zero, in case there is enough deposit,
     *                              or the userOp has a paymaster.
     */
    function _payPrefund(uint256 missingAccountFunds) internal virtual {
        if (missingAccountFunds != 0) {
            (bool success, ) = payable(msg.sender).call{
                value: missingAccountFunds,
                gas: type(uint256).max
            }("");
            (success);
            //ignore failure (its EntryPoint's job to verify, not account.)
        }
    }
}

๊ทธ๋Ÿฌ๋ฉด entrypoint์—์„œ๋Š” ์–ด๋–ค ๋™์ž‘์ด ์ผ์–ด๋‚˜๋ƒ. ์ผ๋‹จ ํ•ด๋‹น ์ฝ”๋“œ, _payPrefund๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๊ฐ€์Šค๋กœ ์“ธ ์ด๋”๋ฅผ ๊ฐ€์ ธ์˜ด. ํŽ˜์ด๋งˆ์Šคํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ๊ณ„์ •์—์„œ ๊ฐ€์ ธ์˜ฌ ํ…๋ฐ ์–ด๋–ค ๊ฒฝ์šฐ์— ํŽ˜์ด๋งˆ์Šคํ„ฐ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”์ง€ ๋“ฑ์˜ ์›€์ง์ž„์€ ๋‚˜์ค‘์— ๋ณด๋ฉด ๋ ๋“ฏ??

์ด๊ฑฐ ๋ง๊ณ ๋„ nonce๊ฒ€์ฆ์ด๋ž‘ validatePrepayment๋„ ์ง„ํ–‰ํ•˜๋Š” ๊ฑฐ ๊ฐ™์€๋ฐ, ๋‚˜์ค‘์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฑธ๋กœ.

Wallet execution

์ด์ œ ๊ฒ€์ฆ๋„ ๋๋‚ฌ์œผ๋‹ˆ wallet execution์„ ํ•  ์ฐจ๋ก€.

contract SimpleAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Initializable {
    // ...

    /**
     * execute a transaction (called directly from owner, or by entryPoint)
     */
    function execute(address dest, uint256 value, bytes calldata func) external {
        _requireFromEntryPointOrOwner();
        _call(dest, value, func);
    }

    /**
     * execute a sequence of transactions
     * @dev to reduce gas consumption for trivial case (no value), use a zero-length array to mean zero value
     */
    function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external {
        _requireFromEntryPointOrOwner(); // owner์ด๊ฑฐ๋‚˜ entrypoint์—์„œ ์ง์ ‘ interactํ•˜๋Š” ๊ฒฝ์šฐ๋งŒ ํ—ˆ์šฉํ•จ
        require(dest.length == func.length && (value.length == 0 || value.length == func.length), "wrong array lengths");
        if (value.length == 0) {
            for (uint256 i = 0; i < dest.length; i++) {
                _call(dest[i], 0, func[i]);
            }
        } else {
            for (uint256 i = 0; i < dest.length; i++) {
                _call(dest[i], value[i], func[i]);
            }
        }
    }
    
    // ...
}

ํ•ด๋‹น wallet ์•ˆ์— ์žˆ๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜์ž„. ๊ทผ๋ฐ ์—ฌ๊ธฐ ๋ณด๋ฉด owner์ด๊ฑฐ๋‚˜ entrypoint์ผ๋•Œ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ, ํ•ด๋‹น ๊ณ„์ • ์Šคํ† ๋ฆฌ์ง€๊ฐ’์ด ๋‹ฌ๋ผ์ง€๋ฉด ํฐ์ผ๋‚˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€? ์•„๋‹˜ owner์ฃผ์†Œ๋ฅผ ๋‹ค๋ฅธ ๊ฑธ๋กœ ๋ฐ”๊พธ๊ฑฐ๋‚˜,,,๋“ฑ๋“ฑ. ์—ฌ๊ธฐ upgradableํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•œ๋‹ค๊ณ  ํ•˜๋˜๋ฐ, ์ œ๋Œ€๋กœ initalize์•ˆํ•˜๋ฉด ํฐ์ผ๋‚ ๋“ฏ.

Paymaster

Thanks to this flexibility in the specification, innovative models like transaction sponsoring, subscription-based gas payment, or dynamic fees can be implemented.

์‚ฌ์‹ค์ƒ aa์ง€๊ฐ‘ ๋งŒ๋“œ๋Š” ํšŒ์‚ฌ๋Š” ์ด๊ฑฐ ๋•๋ถ„์— ์ˆ˜์ˆ˜๋ฃŒ ๋ชจ๋ธ์„ ๊ต‰์žฅํžˆ ๋™์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฑฐ ๊ฐ™์Œ. ํŽ˜์ด๋งˆ์Šคํ„ฐ ์ฃผ์†Œ๋Š” userOp์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•„๋“œ๊ฐ’์ด ๋„ฃ์–ด์ง€๋‚˜๋ด„.

validation stage์—์„œ์˜ paymaster

ํŽ˜์ด๋งˆ์Šคํ„ฐ๋Š” verificationGasLimit๋ณด๋‹ค 3๋ฐฐ ์ด์ƒ ๋งŽ์€ ๊ฐ€์Šค๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋Š” postOp๊ฐ™์ด ํŠธ๋žœ์žญ์…˜์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ฑฐ๋‚˜ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ์ตœ๋Œ€ 3๋ฒˆ์ด์—ฌ์„œ ๊ทธ๋Ÿฐ๊ฑฐ ๊ฐ™์Œ.

  • postOp๋Š” ์ตœ๋Œ€ ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
    1. UserOperation ์‹คํ–‰ ํ›„
    2. Paymaster์˜ ์ฒซ postOp๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ
  • ์ด๋Š” Paymaster์—๊ฒŒ ์Šคํฐ์„œ๋ง ์‹คํŒจ๋ฅผ ์•Œ๋ฆฌ๊ณ , ์ ์ ˆํ•œ ์กฐ์น˜๋ฅผ ์ทจํ•  ๊ธฐํšŒ๋ฅผ ์ค๋‹ˆ๋‹ค.

validatePaymasterUserOp ํ•จ์ˆ˜:

  • ์ด ํ•จ์ˆ˜๋Š” Paymaster์—์„œ ํ˜ธ์ถœ๋˜์–ด UserOperation์— ๋Œ€ํ•œ ์ง€๋ถˆ ์˜์‚ฌ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์œ ์ €๊ฐ€ ํ•ด๋‹น ํŽ˜์ด๋งˆ์Šคํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•จ๊ณผ ๋™์‹œ์—, ์‚ฌ์šฉ์ž์˜ ์ˆ˜์ˆ˜๋ฃŒ ์ฒ˜๋ฆฌ ๋ชจ๋ธ์— ๋งž์ถฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด๋‹ค ๋ณด๋‹ˆ ๊ฐœ๋ฐœ์‚ฌ์˜ ์žฌ๋Ÿ‰์— ๋”ฐ๋ผ ๋‹ฌ๋ ค์žˆ๋”๋ผ๊ณ ์š”. ์•„๋งˆ ์ด๊ฑด ํด๋ผ์ด์–ธํŠธ์ธก ๋ณด๋ฉด ๋ ๋“ฏ.

  • ์˜ˆ์‹œ: a) ํŠน์ • ์‚ฌ์šฉ์ž ๊ทธ๋ฃน์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ๋ณด์กฐํ•˜๋Š” ์„œ๋น„์Šค ์ œ๊ณต์ž: ๊ณ„์ •์ด ํ—ˆ์šฉ ๋ชฉ๋ก์— ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. b) ERC-20 ํ† ํฐ์œผ๋กœ ์ง€๋ถˆ์„ ํ—ˆ์šฉํ•˜๋Š” Paymaster: ์‚ฌ์šฉ์ž์˜ ํ† ํฐ ์ž”์•ก์„ ํ™•์ธํ•˜๊ณ , ํ† ํฐ์„ ์ฐจ๊ฐํ•œ ํ›„ ์ด๋”๋กœ ์ง€๋ถˆํ•ฉ๋‹ˆ๋‹ค.

Execution stage์—์„œ์˜ paymaster

๊ฐ€์žฅ ๋จผ์ €?? ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋Š” userOperation ํ•จ์ˆ˜.

Ifย `validatePaymasterUserOp`ย returns a non-empty context,ย `handleOps`ย callsย `postOp`ย on the paymaster after the operation has been executed, duringย `_handlePostOp`.

validatePaymasterUserOp์—์„œ useroperation์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ , context๋ผ๋Š” ๋ฐ˜ํ™˜๊ฐ’์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ๋ฅผ ๋„๋งก์•„์„œ ํ•ด์คŒ.

userOperationํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ ๋‹ค์Œ์— entrypoint์— ์˜ํ•ด postOpํ•จ์ˆ˜๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋Š”๋ฐ, ์ด๋Š”userOperationํ•จ์ˆ˜ ํ˜ธ์ถœ ์ดํ›„ ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ.

paymaster๋Š” validationUserOp์—์„œ ํ•œ๋ฒˆ, postOp์—์„œ ํ•œ๋ฒˆ ๊ฐ€๊ฒฉ์„ ํ™•์ธํ•˜๋Š”๋ฐ, ์ „์ž์˜ ๊ฒฝ์šฐ balance๊ฐ€ ์—†์œผ๋ฉด execute๋ฅผ ์•ˆํ•˜๊ธฐ ์œ„ํ•จ์ด๊ณ  ํ›„์ž์˜ ๊ฒฝ์šฐ ์ตœ์ข… ๋น„์šฉ ๊ณ„์‚ฐ ๋•Œ๋ฌธ์ž„. ๊ทธ๋ž˜์„œ ํ”Œ๋ž˜์‹œ๋ก ์— ๊ธฐ๋ฐ˜ํ•œ dos์–ดํƒ์ด ๋“ค์–ด์˜ฌ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ,

  • ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ƒํ™ฉ ๋ฐœ์ƒ:
    • UserOperation์ด ํ”Œ๋ž˜์‹œ๋ก ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋กœ ์ธํ•ด Paymaster๊ฐ€ ์˜์กดํ•˜๋Š” ์œ ๋™์„ฑ ํ’€์˜ ๊ท ํ˜•์ด ๊นจ์ง‘๋‹ˆ๋‹ค.
    • UserOperation์€ ์„ฑ๊ณตํ–ˆ์ง€๋งŒ, Paymaster์˜ postOp๊ฐ€ ์‹คํŒจ(revert)ํ•ฉ๋‹ˆ๋‹ค.
  • EntryPoint์˜ ๋Œ€์‘:
    • EntryPoint๋Š” Paymaster๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
    • postOpReverted ๋ชจ๋“œ๋กœ Paymaster์—๊ฒŒ ์ƒํ™ฉ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
    • Paymaster๋Š” ์ „์ฒด ๋ฒˆ๋“ค์„ ์ทจ์†Œํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ด ๊ฒฐ์ •์€ ํ‰ํŒ ์‹œ์Šคํ…œ์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Œ. postOpReverted ํ˜ธ์ถœ์ด if๋ฌธ์œผ๋กœ ์žˆ์„ ๊ฑฐ ๊ฐ™๊ณ  mode๋Š” enum์„ ์“ธ์ง€ ์•„๋‹˜ bool๊ฐ’์„ ์“ธ์ง€๋Š” ๋ฏธ์ง€์ˆ˜. ๊ตฌํ˜„์ฒด ๋ด์•ผํ•ด์š”.

์ด๋Ÿฌํ•œ post-reversion ์‹คํŒจ์— ๋Œ€ํ•œ ์ฑ…์ž„์€ Paymaster์—๊ฒŒ ๋ฌป๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Š” paymaster์˜ ํ‰ํŒ์ ์ˆ˜ํ•˜๊ณ ๋„ ์ง๊ฒฐ๋˜๋Š” ๋ฌธ์ œ์ธ ๊ฑฐ ๊ฐ™์•„์š”. ์ •ํ™•ํ•œ ์ ์ˆ˜ ์ธก์ •์€ EntryPoint์— ์žˆ๋Š” ๊ฑฐ ๊ฐ™๊ณ . ํ‰ํŒ๊นŽ์ด๋ฉด ์“ฐ๋กœํ‹€๋ง ๋“ค์–ด๊ฐ€๋Š”๋“ฏ.(์ผ์ • ์‹œ๊ฐ„๋™์•ˆ 4337 entrypoint๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์—†๋‹ค๊ฑฐ๋‚˜...๋“ฑ๋“ฑ. entrypoint ์ฝ”๋“œ ๊นŒ๋ณด๋ฉด ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ฝ”๋“œ ๋‚˜์˜ฌ์ง€๋„???)

paymaster๊ฐ€ ํ‰ํŒ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฑฐ๋ž˜๋ฅผ ์ž˜ ๋๋‚ด๊ฑฐ๋‚˜, ์Šคํ…Œ์ดํ‚น์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•๋ฐ–์— ์—†๋Š”๋“ฏ...? ๊ทผ๋ฐ ์ด ์Šคํ…Œ์ดํ‚นํ•˜๋Š”๊ฑฐ ์ด๋”๋ฆฌ์›€ ๋…ธ๋“œ์—๋‹ค๊ฐ€ ๋ฆฌ์Šคํ…Œ์ดํ‚นํ•˜๋ฉด ์•ˆ๋˜๋‚˜ ์–ด์งœํ”ผ ๋ฝ์—…์ผํ…๋ฐ...??? paymaster์—๊ฒŒ ์Šคํ…Œ์ดํ‚น ๋ณด์ƒ ๋‹ค์‹œ ๋Œ๋ ค์ฃผ๋ฉด ๋˜๊ณ . <-EIP์ œ์•ˆ ์š”๋ง

Factory

๊ณ„์ • ํŒฉํ† ๋ฆฌ๋„ ๋ฒˆ๋“ค์—์„œ tx๋ณด๋‚ผ๋•Œ ๊ฐ™์ด ๋ณด๋‚ด๋‚˜ ใ…‡ใ…‡

  • ๊ณ„์ • ํŒฉํ† ๋ฆฌ ์‹๋ณ„:
    • initCode์˜ ์ฒซ 20๋ฐ”์ดํŠธ๋Š” ๊ณ„์ • ํŒฉํ† ๋ฆฌ์˜ ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ํ†ตํ•ด ์–ด๋–ค ํŒฉํ† ๋ฆฌ๊ฐ€ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์ง์ ‘์ ์œผ๋กœ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • EntryPoint์˜ ์—ญํ• :
    • EntryPoint๋Š” initCode๋ฅผ ํ•ด์„ํ•˜์—ฌ ์ ์ ˆํ•œ ํŒฉํ† ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
    • ํŒฉํ† ๋ฆฌ ์ฃผ์†Œ๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ์กด ๊ณ„์ • vs ์ƒˆ ๊ณ„์ •:
    • ๊ธฐ์กด ๊ณ„์ •์˜ ๊ฒฝ์šฐ initCode๋Š” ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ƒˆ ๊ณ„์ • ์ƒ์„ฑ ์‹œ์—๋งŒ initCode๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

initCode์— ์ถ”๊ฐ€ํ•ด์„œ ๋ณด๋‚ด๋„ค์š”...์ง„์งœ ์ค‘์•™ํ™”์ธ๋ฐ??????์ด๊ฒŒ๋งž๋ƒ;;;;;

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors