în , un cont inteligent a fost implementat și prima UserOperation a fost executată cu succes prin EntryPoint. La acel moment, totul a funcționat - dar o parte critică a sistemului a rămas în cea mai mare parte invizibilă: pachetul. Partea 1 Bundlers sunt puntea dintre abstracția contului și stratul de execuție Ethereum. Ei iau UserOperations dintr-un mempool separat, plătesc costurile de gaz în avans și sunt rambursate prin protocol. Înțelegerea modului în care acestea funcționează - regulile de validare, sistemul de reputație și stimulentele economice - este esențială pentru depistarea problemelor și construirea de aplicații fiabile. Ciclul de viață al utilizatorului O operațiune de utilizator este unitatea de operare a pachetelor de lucru. Încorporează tot ceea ce este necesar pentru a executa o acțiune în numele unui cont inteligent - autorizare, constrângeri de gaze, date de apel de execuție și logică opțională paymaster. În EntryPoint v0.8, UserOperations sunt gestionate în lanț într-un format ambalat, optimizat pentru gaze. Când lucrați cu SDK-uri cum ar fi permissionless.js, acestea sunt reprezentate ca o structură explicită, neambalată: type UserOperation = { sender: Address nonce: bigint factory?: Address // Account factory (for first-time deployment) factoryData?: Hex // Factory calldata callData: Hex callGasLimit: bigint verificationGasLimit: bigint preVerificationGas: bigint maxFeePerGas: bigint maxPriorityFeePerGas: bigint paymaster?: Address paymasterVerificationGasLimit?: bigint paymasterPostOpGasLimit?: bigint paymasterData?: Hex signature: Hex } În lanț, EntryPoint utilizează un format ambalat pentru eficiența gazelor (combinând câmpuri precum ). The SDK handles this packing automatically—you don't need to worry about it. accountGasLimits = verificationGasLimit | callGasLimit Ciclul de viață al unei operațiuni de utilizare arată astfel: Crearea: Utilizatorul construiește UserOp cu SDK-ul contului său inteligent (cum ar fi permissionless.js) Semnarea: Utilizatorul semnează hash-ul UserOp, dovedind că a autorizat acțiunea Trimitere: UserOp este trimis la un pachet prin eth_sendUserOperation Validare: Bundler simulează UserOp pentru a verifica dacă va reuși Mempool: Dacă este valid, UserOp intră în mempool-ul pachetului Bundling: Bundler pachetează mai multe UserOps într-o singură comandă Execuție: Contractul EntryPoint validă fiecare UserOp pe lanț, apoi le execută Plăți: EntryPoint colectează costurile de gaze de la fiecare cont (sau de la administratorul plății). Înțelegerea cheie este că validarea are loc de două ori: o dată off-chain de către bundler (pentru a decide dacă să accepte UserOp), și o dată on-chain de către EntryPoint (înainte de a efectua efectiv). Fiecare regulă de validare există pentru a elimina o clasă de atac în care un UserOp trece simularea, dar eșuează pe lanț. Validare: De ce Bundlers sunt paranoici Bundlerii plătesc cheltuielile cu gazul în avans. Dacă o operațiune de utilizator eșuează în lanț după ce a fost inclusă într-un pachet, pierderea este a lor. Acest singur fapt definește întregul model de amenințare bundler. Între simulare și includere, starea Ethereum nu este statică.Parametrii blocului se schimbă, balanțele se schimbă, iar tranzacțiile adversare pot ajunge între ele.O operațiune UserOperation atent concepută poate trece în afara simulării lanțului și încă nu reușește în timpul validării în lanț - transformând pachetul într-o cană de gaz. ERC-4337 răspunde prin constrângerea puternică a ceea ce codul de validare este permis să facă. EntryPoint impune o separare strictă a preocupărilor: : Funcția validateUserOp a contului rulează pentru a verifica semnătura și a autoriza operațiunea. Această fază are restricții stricte cu privire la ce opcode și stocare poate accesa codul. Validation Phase : Funcția de executare a contului rulează operațiunea reală. Nu există restricții aici – capacități EVM complete. Execution Phase Coduri interzise Anumite opcode sunt interzise în timpul validării, deoarece valorile lor se pot schimba între simulare și execuție: TIMESTAMP, NUMER, COINBASE, PREVRANDAO: Valori dependente de blocuri. Un cont ar putea verifica dacă (block.timestamp > termen limită) revine, trece simularea, apoi eșuează atunci când pachetul aterizează într-un bloc ulterior. BLOCKHASH: Returnează valori diferite pentru blocuri diferite. GASPRICE, BASEFEE: Schimbare bazată pe condițiile de rețea. BALANȚA, SELFBALANȚA: Permisă numai de la entitățile implicate (a se vedea Staking mai jos) pe ERC-7562 [OP-080]. GASLIMIT, ORIGIN: Poate varia între mediul de simulare și execuția reală. BLOBHASH, BLOBBASEFEE: EIP-4844 opcode legate de blob care variază pe bloc. SELFDESTRUCT, INVALID: Opcode distructive nu sunt permise în timpul validării. GAS: este permisă numai dacă este urmată imediat de o instrucțiune *CALL – CALL, DELEGATECALL, STATICCALL sau CALLCODE (pentru livrarea gazelor către alte contracte). CREATE: În general interzisă deoarece creează contracte la adrese imprevizibile. cu toate acestea, CREATE este permis pentru contractul de expediere atunci când se utilizează o fabrică neafiliată. CREATE2 este permis exact o dată în timpul implementării pentru contractul de expediere. Iată ce se întâmplă atunci când contul dvs. utilizează un opcode interzis: // This validateUserOp will be rejected by bundlers function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256 validationData) { // BANNED: block.timestamp can change require(block.timestamp < deadline, "Expired"); // ... rest of validation } Bundler-ul rulează o urmă în timpul simulării (folosind cu un tracer compatibil cu ERC-7562) și respinge orice UserOp a cărui validare atinge opcode interzise. bundlers moderne pot folosi fie un tracer JavaScript, fie implementarea nativă Go introdusă cu EntryPoint v0.8. RPC răspuns va indica care opcode a cauzat respingerea. debug_traceCall Reguli de acces la stocare Dincolo de opcode, pachetele restricționează codurile de validare ale sloturilor de stocare care pot fi citite și scrise. Puteți accesa numai: Unstaked entities Their own storage (the account's storage) Sloturi de stocare care sunt "asociate" cu adresa contului A, definite ca: valoarea slotului este egală cu A, OR a fost calculată ca keccak256(Aannoo x) + n unde x este bytes32 și n este în intervalul 0 până la 128 In practice, this means your account can read/write to mappings where the account address is the key. For example, an ERC-20 token stores balances in a mapping like În prezent, slotul de stocare este Dacă adresa contului dvs. este cheia, acel slot este "asociat" cu dvs. offset (0-128) permite accesul la membrii struct stocate după intrarea de cartografiere – util atunci când valoarea de cartografiere este o structă. mapping(address => uint256) balances keccak256(address || slot_of_mapping) +n (accounts, paymasters, or factories that have deposited stake in the EntryPoint) get more freedom: Staked entities Poate accesa orice slot în propriul depozit (STO-031) Poate accesa stocarea asociată a oricărei entități din UserOp (STO-032) Can have read-only access to storage in non-entity contracts (STO-033) Consultați regulile complete de acces în . Răspunsul 7562 Luați în considerare două operațiuni ale utilizatorului care citesc din același slot de stocare în timpul validării. Prin restricționarea accesului la stocare în timpul validării, pachetele pot include în siguranță mai multe operațiuni ale utilizatorului în același pachet, fără a risca interferențe transversale sau eșecuri nedeterministe. The Reputation System Chiar și cu reguli de validare, entitățile se pot comporta prost. Un cont poate trimite în mod constant UserOps care trec prin simulare, dar eșuează în lanț din cauza modificărilor subtile ale stării. Bundlers urmărește aceste entități cu o Pentru fiecare entitate (adresa contului, adresa paymaster, adresa fabricii), pachetul urmărește: reputation system : How many UserOps involving this entity has the bundler seen opsSeen opsIncluded: Câți dintre aceștia au fost de fapt incluși în lanț Statutul reputaţiei este determinat de pragurile bazate pe slăbiciune (definite în ): Răspunsul 7562 maxSeen = opsSeen / MIN_INCLUSION_RATE_DENOMINATOR (10 for bundlers) status = BANNED if maxSeen > opsIncluded + BAN_SLACK (50) status = THROTTLED if maxSeen > opsIncluded + THROTTLING_SLACK (10) status = OK otherwise Valorile "slack" sunt tampoane de toleranță care împiedică rezultatele pozitive false de la varianța operațională normală. înseamnă că o entitate poate avea până la 10 mai multe includeri așteptate decât includeri reale înainte de a fi aruncată. provides an even larger buffer before permanent banning. This design acknowledges that some UserOps legitimately fail (network conditions, race conditions) without indicating malicious behavior. THROTTLING_SLACK = 10 BAN_SLACK = 50 Când o entitate este , the bundler limits how many UserOps from that entity can be in the mempool. When , toate UserOps care implică acea entitate sunt respinse imediat. throttled banned Staking Entitățile își pot îmbunătăți poziția prin plasarea ETH în contractul EntryPoint: entryPoint.addStake{ value: 1 ether }(unstakeDelaySec); The stake isn't slashed—it's just locked. But it demonstrates commitment and grants: Reguli relaxate de acces la stocare (după cum este descris mai sus) Limite mai mari de mempool More lenient reputation thresholds Pentru paymasters și fabrici, în special, staking-ul este aproape obligatoriu pentru utilizarea în producție. Fără el, un singur UserOp eșuat poate obține rapid entitatea throttled. (chain-specific, typically 1 ETH or equivalent) and valoarea exactă a acțiunii variază în funcție de lanț și este definită în metadatele mempool - verificați documentația bundler pentru rețeaua țintă. MIN_STAKE_VALUE MIN_UNSTAKE_DELAY Gas Economics Bundlers are businesses. They pay gas costs to submit bundles and get reimbursed from the UserOps they include. The economics work like this: The Bundler's Perspective Revenue = Σ (each UserOp's payment to beneficiary) Cost = Gas used × Gas price paid for handleOps tx Profit = Revenue - Cost Fiecare UserOp plătește în funcție de utilizarea gazelor și de prețul gazelor pe care le specifică: Payment = (actualGasUsed + preVerificationGas) × min(maxFeePerGas, baseFee + maxPriorityFeePerGas) The bundler sets the Adresa în pentru a primi aceste plăți. beneficiary handleOps Verificarea prealabilă explicată pe acoperă costurile care nu pot fi măsurate direct: preVerificationGas Costul apelului de date: 16 gaze pe byte non-zero, 4 gaze pe byte zero Bundle overhead: Costuri fixe per apel HandleOps care sunt amortizate pe tot parcursul UserOps : On L2s like Optimism or Arbitrum, posting calldata to L1 has additional costs L2 data fees Atunci când se estimează gazul, pachetele calculează preVerificationGas pe baza dimensiunii UserOp: // Simplified preVerificationGas calculation const calldataCost = userOpBytes.reduce((sum, byte) => sum + (byte === 0 ? 4n : 16n), 0n ); const overhead = 38000n; // ~21000 tx base + ~10000 bundle overhead + ~7000 per-op preVerificationGas = calldataCost + overhead + l2DataFee; Valorile de referință variază în funcție de grup. Pentru referință, Alto utilizează , , Folosește întotdeauna Mai degrabă decât hardcoding. transactionGasStipend: 21000 fixedGasOverhead: 9830 perUserOp: 7260 eth_estimateUserOperationGas The Unused Gas Penalty Pentru a împiedica utilizatorii să plătească prea mult pentru gaz (care risipește spațiu bloc), EntryPoint impune o amendă pentru gazul de execuție neutilizat. (pentru executarea contului) și (pentru operațiunile post-paymaster): callGasLimit paymasterPostOpGasLimit Dacă gazul neutilizat în ambele câmpuri depășește PENALTY_GAS_THRESHOLD (40.000), contul plătește 10% (UNUSED_GAS_PENALTY_PERCENT) din suma neutilizată (nu se aplică la verificareGasLimit sau preVerificationGas) This discourages setting absurdly high execution limits "just to be safe" Când vedeți erori în estimarea gazelor, verificați dacă limitele dvs. sunt rezonabile. Endpoint oferă defecte rezonabile. eth_estimateUserOperationGas Greșeli frecvente și debugging Bundlers returnează coduri de eroare structurate care explică exact de ce operațiunea a eșuat - dar numai dacă știți cum să le citiți. AA1x: Erori de fabrică These occur when deploying a new account via the factory. In EntryPoint v0.8, you specify şi as separate fields (the EntryPoint packs them into pe plan intern): factory factoryData initCode : "sender already constructed" - The sender address already has code deployed. AA10 AA13: "initCode a eșuat sau OOG" - Apelul de la fabrică pentru crearea contului a eșuat sau a eșuat. : "initCode must return sender" - The factory returned a different address than expected. AA14 AA15: "initCode trebuie să creeze expeditorul" - Apelul de fabrică a fost finalizat, dar nu a implementat codul la adresa expeditorului. Verificați dacă fabrica dvs. funcția returnează adresa așteptată. Verificați fabrica este implementată și finanțată. Debugging createAccount AA2x: Erori de validare a contului Cea mai comună categorie: : "account not deployed" - The sender address has no code and no was provided. AA20 initCode AA21: "nu a plătit prefinanțare" - Contul nu are suficient ETH pentru a acoperi costul maxim posibil al gazelor. : "expired or not due" - The UserOp has a timestamp that has passed, or a timestamp that hasn't arrived yet. AA22 validUntil validAfter : "reverted" - The account's function reverted. Check your signature validation logic. AA23 validateUserOp AA24: "erori de semnătură" - Datele de validare returnate indică o semnătură nevalidă. AA25: "contul invalid nonce" - Nonce-ul nu se potrivește. Nonce-ul din ERC-4337 este o valoare de 256 de biți cu două părți: nonce = (cheie << 64) eșantion. Cheia (în partea de sus a 192 de biți) identifică "lanul" - puteți avea mai multe UserOps paralele cu diferite chei. Secvența (în partea de jos a 64 de biți) trebuie să crească secvențial în cadrul fiecărei benzi. Cauze comune: Reutilizarea unui nonce care a fost deja inclus Folosind cheia nonce greșită Another UserOp with the same sender is pending in the mempool AA26: „over verificationGasLimit” - validarea contului a utilizat mai mult gaz decât a alocat. AA3x: Paymaster Errors : "paymaster not deployed" - The paymaster address has no code deployed. AA30 AA31: "Depozitul paymaster prea scăzut" - Depozitul paymaster în EntryPoint nu poate acoperi costul gazului. entryPoint.depositTo{ valoare: 1 eter }(adresa de plată); : "paymaster expired or not due" - Similar to AA22, but for the paymaster's validation data. AA32 AA33: "paymaster reversed" - Funcția validatePaymasterUserOp a fost reversată. : "paymaster signature error" - Bad signature in the paymaster data. AA34 AA36: „over paymasterVerificationGasLimit” – validarea Paymaster a utilizat mai mult gaz decât a alocat. Working with Bundlers Metodele RPC Every ERC-4337 bundler implements these standard methods: : Submit a UserOp for inclusion eth_sendUserOperation const userOpHash = await bundler.request({ method: 'eth_sendUserOperation', params: [userOp, entryPointAddress] }); : Obțineți estimări limită gaze eth_estimateUserOperationGas const gasEstimate = await bundler.request({ method: 'eth_estimateUserOperationGas', params: [userOp, entryPointAddress] }); // Returns: { preVerificationGas, verificationGasLimit, callGasLimit, ... } Căutați un UserOp prin hash-ul său eth_getUserOperationByHash const userOp = await bundler.request({ method: 'eth_getUserOperationByHash', params: [userOpHash] }); Obțineți chitanța după includere eth_getUserOperationReceipt const receipt = await bundler.request({ method: 'eth_getUserOperationReceipt', params: [userOpHash] }); // Returns: { success, actualGasUsed, receipt: { transactionHash, ... } } : Discover which EntryPoint versions the bundler supports eth_supportedEntryPoints const entryPoints = await bundler.request({ method: 'eth_supportedEntryPoints', params: [] }); // Returns: ['0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108'] The Shared Mempool Inițial, fiecare pachet a menținut propriul mempool privat. Acest lucru a creat probleme: Risc de cenzură: un singur pachet ar putea refuza să includă anumite UserOps : Users had to know which bundlers to submit to Fragmentation Puncte unice de eșec: Dacă pachetul dvs. s-a prăbușit, opțiunile dvs. de utilizator au fost blocate Soluţia este cea , o rețea P2P în cazul în care pachetele gossip UserOps reciproc. Acesta funcționează în mod similar cu modul în care nodurile Ethereum gossip tranzacții: ERC-4337 shared mempool Utilizatorul trimite UserOp oricărui pachet participant Bundler validates and adds to the local mempool Bundler broadcasts to connected peers Any bundler on the network can include the UserOp. Protocolul utilizează libp2p pentru crearea de rețele. Bundler-urile anunță ce mempools suportă (identificate de IPFS CID-uri care se referă la fișierele de metadată mempool), și propagă numai UserOps-uri care trec validarea. chainId: '1' entryPointContract: '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108' description: Canonical ERC-4337 mempool for Ethereum Mainnet minimumStake: '1000000000000000000' The IPFS CID of this file becomes the mempool identifier used in P2P topic names. The mempool metadata defines validation rules: which opcodes are banned, storage access patterns, gas limits, and reputation thresholds. When a bundler receives a UserOp via P2P gossip, it re-validates against its own rules before adding to its local mempool. Advanced Topics agregatori Verificarea semnăturilor este costisitoare pe lanț. Precompilarea ecrecover costă 3.000 de gaze pe apel, dar verificarea semnăturilor contului inteligent costă de obicei mai mult datorită logicii de validare suplimentare – adesea 6.000-10.000 de gaze în total. Pentru 100 UserOps într-un pachet, asta înseamnă peste 600.000 de gaze doar pentru semnături. What problem do aggregators solve? Instead of each account verifying its own signature, accounts can delegate to an aggregator contract. The aggregator implements a batch verification algorithm (like BLS signatures, where multiple signatures can be combined into one). How it works: Account's returns an aggregator address in its validation data validateUserOp Bundler grupează toate UserOps folosind același agregator Bundler cheamă agregator.validateSignatures(userOps, aggregatedSignature) o dată pentru grup Dacă verificarea trece, toate opțiunile UserOps din acel grup sunt considerate valabile. : Valoarea de returnare din ambalează trei bucăți de informații într-o singură valoare de 256 de biți: The validationData encoding validateUserOp validationData = uint160(aggregator) | // bits 0-159: aggregator address (uint256(validUntil) << 160) | // bits 160-207: expiration timestamp (uint256(validAfter) << 208) // bits 208-255: earliest valid time (bits 0-159): Address of the aggregator contract, or special values: 0 = signature valid, 1 = signature invalid aggregator validUntil (biti 160-207): Timestamp după care acest UserOp expiră (0 = nici o expirare) (bits 208-255): Timestamp before which this UserOp is not valid (0 = immediately valid) validAfter This encoding lets accounts specify both signature verification delegation and time-bounded validity in a single return value. Paymasters În loc să plătească contul pentru gaz, un paymaster poate: Tranzacții sponsorizate: Plătiți în numele utilizatorilor (UX fără gaz) Acceptați jetoanele ERC-20: permiteți utilizatorilor să plătească în stablecoins sau alte jetoane Implementarea logicii personalizate: limitarea ratei, modelele de abonament etc. Fluxul de validare al paymaster rulează în timpul fazei de validare: function validatePaymasterUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost ) external returns (bytes memory context, uint256 validationData); pe Revenit aici este trecut la after execution completes, allowing the paymaster to perform final accounting (like charging an ERC-20 token): context postOp function postOp( PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint256 actualUserOpFeePerGas ) external; Paymasters should be staked for production use. Staking provides relaxed storage access rules and better reputation—unstaked paymasters face strict limitations and can be quickly throttled by bundlers. While unstaked paymasters funcționează tehnic cu operațiuni de bază, parierea este practic necesară pentru orice implementare serioasă a paymaster. can Testare locală This section assumes you have Anvil running with EntryPoint v0.8 deployed. We'll use , Pimlico's TypeScript bundler, și , a viem-based library for ERC-4337 interactions. Alto fără permisiune.js Factorie simplă In Part 1 we built a minimal smart account. But how do users deploy it? They can't send a regular transaction—they don't have ETH for gas yet. ERC-4337 solves this with factory contracts. pentru Punerea în aplicare a referinței include . Deploy it alongside the EntryPoint before running the examples below. SimpleAccount Factorie simplă Account Deployment via UserOp When the EntryPoint receives a UserOp with factory and factoryData fields: Verifică dacă expeditorul are cod – dacă da, omiteți implementarea Calls via the factory.createAccount(owner, salt) factoryData Verifies the deployed address matches sender Continuă cu validarea în contul nou implementat alergare înaltă alto \ --rpc-url http://localhost:8545 \ --entrypoints 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108 \ --executor-private-keys 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \ --utility-private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --safe-mode false \ --api-version v1,v2 \ --bundle-mode auto Key flags: --executor-private-keys: cheie pentru trimiterea pachetelor (trebuie să aibă ETH) : Anvil lacks the JavaScript tracer for full ERC-7562 validation --safe-mode false : Accept both UserOp formats (v1 for 0.6, v2 for 0.7/0.8) --api-version v1,v2 Trimiterea UserOperations cu permissionless.js Instalarea dependențelor: npm install viem permissionless Step 1: Set up clients We need three clients: one for reading chain state, one for bundler-specific RPCs, and one for the smart account owner. import { http, createPublicClient, createWalletClient, parseEther } from "viem" import { privateKeyToAccount } from "viem/accounts" import { foundry } from "viem/chains" import { toSimpleSmartAccount } from "permissionless/accounts" import { createSmartAccountClient } from "permissionless/clients" import { createPimlicoClient } from "permissionless/clients/pimlico" const ENTRYPOINT = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108" const publicClient = createPublicClient({ chain: foundry, transport: http("http://localhost:8545") }) const pimlicoClient = createPimlicoClient({ chain: foundry, transport: http("http://localhost:4337"), entryPoint: { address: ENTRYPOINT, version: "0.8" } }) const owner = privateKeyToAccount(process.env.PRIVATE_KEY) The se conectează la RPC Alto şi oferă estimarea gazelor prin . pimlicoClient pimlico_getUserOperationGasPrice Step 2: Create the smart account instance const simpleAccount = await toSimpleSmartAccount({ client: publicClient, owner, entryPoint: { address: ENTRYPOINT, version: "0.8" } }) const accountAddress = await simpleAccount.getAddress() console.log("Account:", accountAddress) Aceasta calculează adresa contrafactuală folosind adresa fabricii. function. The account doesn't exist yet—but we know exactly where it will be deployed. getAddress Step 3: Fund the account Contul inteligent are nevoie de ETH pentru a plăti pentru gaz (sau pentru a folosi un paymaster). const walletClient = createWalletClient({ account: owner, chain: foundry, transport: http("http://localhost:8545") }) await walletClient.sendTransaction({ to: accountAddress, value: parseEther("1") }) The ETH sits at that address. When the account is deployed, it can access those funds immediately. Step 4: Create the smart account client const smartAccountClient = createSmartAccountClient({ client: publicClient, account: simpleAccount, bundlerTransport: http("http://localhost:4337"), userOperation: { estimateFeesPerGas: async () => (await pimlicoClient.getUserOperationGasPrice()).fast } }) pe se ocupă de construcția UserOp, managementul nonce, estimarea gazelor și semnarea. Callback colectează prețurile curente ale gazelor din pachet. smartAccountClient estimateFeesPerGas Step 5: Send a UserOperation const hash = await smartAccountClient.sendUserOperation({ calls: [{ to: "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", value: parseEther("0.01"), data: "0x" }] }) const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash }) console.log("Success:", receipt.success) Pentru primul UserOp, SDK include automat and EntryPoint implementează contul, apoi execută transferul – toate într-o singură tranzacție. factory factoryData Ce am învățat Pachetele sunt stratul de execuție al ERC-4337.Ei sunt ceea ce transformă Abstracția contului dintr-o specificație într-un mecanism gata de producție. Înțelegerea constrângerilor lor - regulile de validare, economia gazelor și mecanica reputației - este critică atunci când proiectați conturi inteligente fiabile. ERC-4337 îndepărtează complexitatea de protocol și de infrastructură.Cu cât dezvoltatorii încep mai devreme să gândească în termeni de pachete, mai degrabă decât tranzacții, cu atât sistemele lor vor fi mai reziliente în producție.