Ethereum पर, smart contracts को तीन में से किसी एक तरीके से डिप्लॉय किया जा सकता है:
- एक Externally Owned Account (EOA) ट्रांजेक्शन शुरू करता है जहाँ
toफील्ड कोnullपर सेट किया जाता है, औरdataफील्ड में कॉन्ट्रैक्ट का initialization code होता है। - एक स्मार्ट कॉन्ट्रैक्ट
CREATEopcode को कॉल करता है। - एक स्मार्ट कॉन्ट्रैक्ट
CREATE2opcode को कॉल करता है।
इस लेख में, हम यह जानेंगे कि इन तीनों स्थितियों में बनाए जाने वाले कॉन्ट्रैक्ट के एड्रेस का अनुमान (predict) कैसे लगाया जा सकता है।
EOA या CREATE द्वारा डिप्लॉय किए गए स्मार्ट कॉन्ट्रैक्ट एड्रेस का अनुमान लगाना
EOA या CREATE opcode (CREATE2 नहीं) द्वारा डिप्लॉय किए गए कॉन्ट्रैक्ट्स के लिए, एड्रेस की गणना RLP-encoded sender एड्रेस और nonce के Keccak-256 हैश से की जाती है। परिणामी कॉन्ट्रैक्ट एड्रेस उस हैश के अंतिम 20 बाइट्स (160 बिट्स) होता है।
जैसा कि ऊपर दिए गए समीकरण में दिखाया गया है, एड्रेस गणना का यह तरीका केवल डिप्लॉयर के एड्रेस और उनके nonce पर निर्भर करता है। यह कॉन्ट्रैक्ट के बाइटकोड, कंस्ट्रक्टर आर्गुमेंट्स, या किसी अन्य चीज़ पर विचार नहीं करता है।
Recursive Length Prefix (RLP)
उच्च स्तर पर, RLP उन डेटा आइटम्स को कंकेटिनेट (concatenates) करता है जिन्हें भेजा जा रहा है। प्रत्येक आइटम को, [0x00, 0x7f] रेंज में आने वाले सिंगल बाइट्स को छोड़कर, एक या अधिक बाइट्स के साथ प्रीफिक्स किया जाता है जो यह दर्शाते हैं कि आइटम एक स्ट्रिंग है या लिस्ट, और इसके पेलोड की लंबाई क्या है। इच्छुक पाठक ऊपर लिंक किए गए डॉक्यूमेंटेशन को देख सकते हैं।
कॉन्ट्रैक्ट एड्रेस प्रेडिक्शन में RLP एन्कोडिंग का उपयोग कैसे किया जाता है, यह देखने के लिए आइए एक व्यावहारिक उदाहरण के माध्यम से समझते हैं।
RLPDemo का उदाहरण
नीचे दिए गए RLPDemo कॉन्ट्रैक्ट में, predictContractAddress फंक्शन CREATE opcode के एड्रेस डेरिवेशन के समान ही लॉजिक लागू करता है। यह सेंडर के एड्रेस और nonce पर RLP एन्कोडिंग लागू करके अपेक्षित डिप्लॉयमेंट एड्रेस की गणना करता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract RLPDemo {
// Function to predict the address of a contract that would be deployed by a given address
function predictContractAddress(
address deployer,
uint nonce
) public pure returns (address) {
// This implements the same logic as the CREATE opcode's address derivation
// For the CREATE opcode, the address is derived as:
// keccak256(rlp([sender_address, sender_nonce]))
bytes memory rlpEncoded;
// RLP encoding rules:
// - For nonce = 0, the RLP encoding is [0x80] (empty byte string)
// - For nonce = 1 to 127, the RLP encoding is the single byte itself (0x01 to 0x7f)
// - For nonce = 128 to 255, the RLP encoding is [0x81, nonce]
// where 0x81 indicates a single-byte length prefix for the following byte
// Note: Full RLP spec supports encoding arbitrary-length integers using a dynamic length prefix,
// but this function only supports nonces up to 255.
if (nonce == 0) {
// For nonce = 0
rlpEncoded = abi.encodePacked(
bytes1(0xd6), // RLP prefix for a list
bytes1(0x94), // RLP prefix for a 20-byte address
deployer, // 20 bytes of the deployer's address
bytes1(0x80) // RLP encoding for the nonce 0 is 0x80
);
} else if (nonce < 128) {
// For nonce = 1-127
rlpEncoded = abi.encodePacked(
bytes1(0xd6), // RLP prefix for a list
bytes1(0x94), // RLP prefix for a 20-byte address
deployer, // 20 bytes of the deployer's address
uint8(nonce) // Single byte for nonce
);
} else if (nonce < 256) {
// For nonce = 128-255
rlpEncoded = abi.encodePacked(
bytes1(0xd7), // RLP prefix for a list (one byte longer)
bytes1(0x94), // RLP prefix for a 20-byte address
deployer, // 20 bytes of the deployer's address
bytes1(0x81), // RLP prefix for a single byte
uint8(nonce) // The nonce as a single byte
);
} else {
revert("Nonce too large for this demo");
}
bytes32 hash = keccak256(rlpEncoded);
return address(uint160(uint256(hash)));
}
}
यह सत्यापित करने के लिए कि predictContractAddress अपने उद्देश्य के अनुसार काम करता है, हमने RLPDemo (ऊपर दिया गया समान कॉन्ट्रैक्ट) को डिप्लॉय करने के लिए EOA 0x17F6AD8Ef982297579C203069C1DbfFE4348c372 का उपयोग किया, जिसके परिणामस्वरूप कॉन्ट्रैक्ट एड्रेस 0xE2DFC07f329041a05f5257f27CE01e4329FC64Ef प्राप्त हुआ।
ऊपर वर्णित डिप्लॉयमेंट का परिणाम नीचे दी गई छवि के दाईं ओर दिखाया गया है:

जैसा कि ऊपर दी गई छवि के बाईं ओर दिखाया गया है, हमने डिप्लॉयर के एड्रेस 0x17F6AD8Ef982297579C203069C1DbfFE4348c372 और nonce 0 के साथ predictContractAddress को कॉल किया, और पहले डिप्लॉय किए गए कॉन्ट्रैक्ट एड्रेस का सही अनुमान लगाया: 0xE2DFC07f329041a05f5257f27CE01e4329FC64Ef।
इसके बाद, आइए देखें कि externally owned accounts (EOAs) और कॉन्ट्रैक्ट अकाउंट्स दोनों के लिए nonce की व्याख्या कैसे की जाती है।
अकाउंट डिप्लॉयमेंट के दौरान Nonce सीक्वेंस
आइए यह समझने से शुरुआत करें कि Ethereum में nonce को कैसे परिभाषित किया गया है। Ethereum Yellow Paper के अनुसार, किसी अकाउंट के nonce को इस प्रकार परिभाषित किया गया है:
nonce: एक स्केलर मान जो इस एड्रेस से भेजे गए ट्रांजेक्शन्स की संख्या के बराबर होता है, या संबंधित कोड वाले अकाउंट्स के मामले में, इस अकाउंट द्वारा किए गए कॉन्ट्रैक्ट-क्रिएशन्स की संख्या के बराबर होता है। state में एड्रेस a के अकाउंट के लिए, इसे औपचारिक रूप से द्वारा दर्शाया जाएगा।
इस परिभाषा के अनुसार, nonce एक ऐसा मान है जो किसी एड्रेस को तब दिया जाता है जब वह कोई ट्रांजेक्शन शुरू करता है या कोई कॉन्ट्रैक्ट डिप्लॉय करता है। चूंकि EOA सीधे ट्रांजेक्शन शुरू कर सकता है और साइन कर सकता है, इसलिए nonce की गिनती ETH/टोकन ट्रांसफर्स, कॉन्ट्रैक्ट कॉल्स और कॉन्ट्रैक्ट डिप्लॉयमेंट्स जैसे ट्रांजेक्शन्स को दर्शा सकती है। महत्वपूर्ण बात यह है कि यदि ट्रांजेक्शन रिवर्ट (revert) हो जाता है, तब भी nonce बढ़ता है। एक रिवर्ट हुआ ट्रांजेक्शन अभी भी ब्लॉक में शामिल होता है, और यह nonce को बढ़ाने में गिना जाता है।
इसके विपरीत, स्मार्ट कॉन्ट्रैक्ट्स अपने आप ट्रांजेक्शन शुरू नहीं कर सकते; वे केवल तभी निष्पादित (execute) होते हैं जब उन्हें किसी EOA या किसी अन्य कॉन्ट्रैक्ट द्वारा इनवोक (invoke) किया जाता है। इसलिए, कॉन्ट्रैक्ट अकाउंट के लिए nonce केवल कॉन्ट्रैक्ट द्वारा शुरू किए गए कॉन्ट्रैक्ट-क्रिएशन को दर्शाता है।
Note: इंटरनल कॉल्स, मैसेज कॉल्स, इवेंट्स और ट्रांजेक्शन्स के भीतर होने वाले अन्य ऑपरेशन्स का उपयोग कभी भी किसी अकाउंट के nonce काउंट को बढ़ाने के लिए नहीं किया जाता है।
अब, आइए देखते हैं कि EOA और कॉन्ट्रैक्ट अकाउंट्स की भविष्यवाणी (predict) करने के लिए nonces को कैसे इनिशियलाइज़ और उपयोग किया जाता है।
नए EOA की nonce वैल्यू 0 से शुरू होती है, और प्रत्येक ट्रांजेक्शन के साथ यह वैल्यू 1 से बढ़ जाती है। यदि नया EOA कोई कॉन्ट्रैक्ट डिप्लॉय करता है, तो एड्रेस का अनुमान लगाने के लिए 0 को nonce के रूप में उपयोग किया जाएगा। हालांकि, यदि अकाउंट ने पहले ही ईथर ट्रांसफर या पिछले डिप्लॉयमेंट जैसे ट्रांजेक्शन भेज दिए हैं, तो nonce 0 से अधिक होगा।
कॉन्ट्रैक्ट अकाउंट के लिए, क्रिएशन पर nonce को 1 के साथ इनिशियलाइज़ किया जाता है, जैसा कि EIP-161 में निर्दिष्ट किया गया है। जब कोई कॉन्ट्रैक्ट CREATE या CREATE2 का उपयोग करके अन्य कॉन्ट्रैक्ट्स बनाता है, तो उसका nonce 1 से बढ़ जाता है।
उदाहरण के लिए, मान लें कि Contract A अभी डिप्लॉय हुआ है।
- उस समय, Contract A का nonce
1पर सेट होता है। यदि Contract A आगे बढ़कर एक और कॉन्ट्रैक्ट बनाता है, मान लीजिए, Contract B, तो यह क्रिएशन Contract B के एड्रेस की गणना करने के लिए nonce = 1 का उपयोग करेगा। - एक बार Contract B का क्रिएशन पूरा हो जाने पर, Contract A का nonce बढ़कर
2हो जाता है। - मान लीजिए Contract A एक और कॉन्ट्रैक्ट—मान लीजिए, Contract C बनाना चाहता है। यह इस डिप्लॉयमेंट के लिए
nonce = 2का उपयोग करेगा। Contract C के क्रिएशन के बाद, Contract A का nonce3हो जाता है, और इसी तरह यह आगे बढ़ता है। - Contract B और Contract C, किसी भी अन्य नए कॉन्ट्रैक्ट की तरह,
nonce = 1से शुरू होते हैं।
किसी अकाउंट का nonce कैसे प्राप्त करें
किसी अकाउंट का nonce प्राप्त करने के लिए कोई EVM opcode नहीं है। हालांकि, eth_getTransactionCount RPC मेथड दिए गए अकाउंट के लिए अकाउंट का nonce लौटाता है, जैसा कि ऊपर बताया गया है।
यह मेथड निर्दिष्ट एड्रेस से भेजे गए ट्रांजेक्शन्स की संख्या लौटाता है, जो अकाउंट के nonce से मेल खाता है। EOA के लिए, इसमें ETH/टोकन ट्रांसफर्स, कॉन्ट्रैक्ट कॉल्स और कॉन्ट्रैक्ट डिप्लॉयमेंट्स शामिल हैं। स्मार्ट कॉन्ट्रैक्ट्स के लिए, eth_getTransactionCount कॉन्ट्रैक्ट एड्रेस द्वारा किए गए कॉन्ट्रैक्ट क्रिएशन्स की संख्या को दर्शाता है।
नीचे दी गई छवि दिखाती है कि कैसे केवल कॉन्ट्रैक्ट डिप्लॉयमेंट ही कॉन्ट्रैक्ट एड्रेस के लिए eth_getTransactionCount nonce को बढ़ाता है।

यहाँ जावास्क्रिप्ट में eth_getTransactionCount मेथड का उपयोग करके nonce प्राप्त करने का एक उदाहरण दिया गया है।
// NECESSARY IMPORTS
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
// CREATE A PUBLIC CLIENT
const publicClient = createPublicClient({
chain: mainnet,
transport: http()
});
// GET TRANSACTION COUNT (NONCE)
const transactionCount = await publicClient.getTransactionCount({
address: '0xYourContractAddress'
});
console.log(transactionCount);
टेस्टिंग के लिए, हम Foundry में vm.getNonce चीटकोड (cheatcode) का उपयोग कर सकते हैं।
Foundry का getNonce मेथड
Foundry में, vm.getNonce चीटकोड हमें EVM पर किसी दिए गए अकाउंट या वॉलेट का वर्तमान nonce प्राप्त करने की अनुमति देता है।
यहाँ Foundry वातावरण में उपलब्ध getNonce मेथड्स दिए गए हैं:
// Returns the nonce of a given account.
function getNonce(address account) external returns (uint64);
नीचे दिखाए गए test_eoaAndContractNonces() में, हम पुष्टि (assert) करते हैं कि एक EOA (userEOA) का nonce 0 से शुरू होता है, और एक नए डिप्लॉय किए गए कॉन्ट्रैक्ट SomeContract का nonce अपेक्षा के अनुसार 1 से शुरू होता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
contract SomeContract {
// Could have logic here if needed
}
contract CreateAddrTest is Test {
address userEOA = address(0xA11CEB0B);
SomeContract public newContract;
function setUp() public {
// Fund the EOA with 10 ether
vm.deal(userEOA, 10 ether);
// Deploy SomeContract which will deploy Dummy in its constructor
newContract = new SomeContract();
}
function test_eoaAndContractNonces() public view{
// 1. EOA nonce should be 0 initially
uint256 eoaNonce = vm.getNonce(userEOA);
console.log("EOA nonce:", eoaNonce);
assertEq(eoaNonce, 0);
// 2. Contract nonce should be 1
uint256 contractNonce = vm.getNonce(address(newContract));
console.log("SomeContract contract nonce:", contractNonce);
assertEq(contractNonce, 1);
}
}
टर्मिनल परिणाम:

EOA द्वारा डिप्लॉय किए गए कॉन्ट्रैक्ट एड्रेस का अनुमान लगाना (LibRLP का उपयोग करके)
Solady एक यूटिलिटी (utility) प्रदान करता है जिसे LibRLP कहा जाता है, जिसमें एक computeAddress फंक्शन शामिल है जो एड्रेस की गणना करने के लिए अपने इंटरनल RLP एन्कोडिंग इम्प्लीमेंटेशन का उपयोग करता है। यह हेल्पर (helper) एन्कोडिंग के विवरणों को छिपा देता है और सीधे वह कॉन्ट्रैक्ट एड्रेस लौटाता है जो EOA या CREATE डिप्लॉयमेंट द्वारा जनरेट किया जाएगा।
function computeAddress(address deployer, uint256 nonce)
internal
pure
returns (address deployed)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
// The integer zero is treated as an empty byte string,
// and as a result it only has a length prefix, 0x80,
// computed via `0x80 + 0`.
// A one-byte integer in the [0x00, 0x7f] range uses its
// own value as a length prefix,
// there is no additional `0x80 + length` prefix that precedes it.
if iszero(gt(nonce, 0x7f)) {
mstore(0x00, deployer)
// Using `mstore8` instead of `or` naturally cleans
// any dirty upper bits of `deployer`.
mstore8(0x0b, 0x94)
mstore8(0x0a, 0xd6)
// `shl` 7 is equivalent to multiplying by 0x80.
mstore8(0x20, or(shl(7, iszero(nonce)), nonce))
deployed := keccak256(0x0a, 0x17)
break
}
let i := 8
// Just use a loop to generalize all the way with minimal bytecode size.
for {} shr(i, nonce) { i := add(i, 8) } {}
// `shr` 3 is equivalent to dividing by 8.
i := shr(3, i)
// Store in descending slot sequence to overlap the values correctly.
mstore(i, nonce)
mstore(0x00, shl(8, deployer))
mstore8(0x1f, add(0x80, i))
mstore8(0x0a, 0x94)
mstore8(0x09, add(0xd6, i))
deployed := keccak256(0x09, add(0x17, i))
break
}
}
}
यह अभ्यास में कैसे काम करता है, इसे समझने के लिए, हम नीचे दिखाए गए CreateAddressPredictor कॉन्ट्रैक्ट को डिप्लॉय करेंगे। इसके बाद हम यह जांचने के लिए addrWithLibRLP को कॉल करेंगे कि क्या परिकलित (computed) परिणाम डिप्लॉय किए गए CreateAddressPredictor एड्रेस के समान है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// Importing LibRLP, which contains the computeAddress function shown above.
import {LibRLP} from "contracts/LibRLP.sol";
contract CreateAddressPredictor {
// contract embeds Solady’s address computation logic and exposes it through addrWithLibRLP.
function addrWithLibRLP(
address _deployer,
uint256 _nonce
) public pure returns (address deployed) {
return LibRLP.computeAddress(_deployer, _nonce);
}
}
EOA 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 का उपयोग करते हुए, हमने Remix पर एड्रेस 0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95 पर CreateAddressPredictor को डिप्लॉय किया।
यहाँ टर्मिनल का परिणाम है।

जब हम addrWithLibRLP को कॉल करते हैं, तो CreateAddressPredictor को डिप्लॉय करने के लिए उपयोग किए गए उसी EOA और 0 के nonce को पास करते हैं, तो लौटाया गया एड्रेस उम्मीद के मुताबिक वास्तविक डिप्लॉय किए गए एड्रेस से मेल खाता है।
जैसा कि नीचे दी गई छवि में देखा जा सकता है, वास्तविक डिप्लॉय किया गया कॉन्ट्रैक्ट एड्रेस इस अनुमानित एड्रेस से मेल खाता है।

नोट: यदि इस उदाहरण में nonce को किसी गैर-शून्य (non-zero) मान पर सेट किया जाता है, तो डिकोड किया गया आउटपुट एक गलत एड्रेस लौटाएगा, क्योंकि हम इसे एक नए (fresh) EOA अकाउंट के साथ टेस्ट कर रहे हैं।
किसी कॉन्ट्रैक्ट द्वारा डिप्लॉय किए गए कॉन्ट्रैक्ट एड्रेस का अनुमान लगाना
जैसा कि पहले बताया गया है, डिप्लॉय किए गए कॉन्ट्रैक्ट के लिए एड्रेस डेरिवेशन समान ही होता है, चाहे डिप्लॉयर एक EOA हो या कोई कॉन्ट्रैक्ट। हमें केवल डिप्लॉयर के एड्रेस और nonce को सही ढंग से सेट करने की आवश्यकता होती है।
नीचे दिए गए टेस्ट में, Deployer कॉन्ट्रैक्ट यह प्रदर्शित करता है कि computeAddress मेथड से लौटाया गया एड्रेस किसी अन्य कॉन्ट्रैक्ट द्वारा डिप्लॉय किए गए कॉन्ट्रैक्ट से कैसे मेल खाता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "contracts/LibRLP.sol";
contract C {}
contract Deployer {
// Note: nonce is not stored on-chain — this is just for tracking purposes
uint256 public contractNonce = 1;
function deploy() public returns (address c) {
address predicted = predictAddress(address(this), contractNonce);
c = address(new C());
require(c == predicted, "Address mismatch");
contractNonce += 1;
return c;
}
function predictAddress(
address _deployer,
uint256 _nonce
) public pure returns (address deployed) {
return LibRLP.computeAddress(_deployer, _nonce);
}
}
कॉन्ट्रैक्ट C को Deployer कॉन्ट्रैक्ट द्वारा new कीवर्ड का उपयोग करके डिप्लॉय किया जाता है (यह इंटरनली CREATE opcode का उपयोग करता है)।
हमारे ऊपर दिए गए उदाहरण में, हम डिप्लॉयमेंट की संख्या को स्टोर करने के लिए contractNonce का उपयोग करते हैं, सुविधा के लिए, क्योंकि एक स्मार्ट कॉन्ट्रैक्ट के भीतर से RPC कॉल करने के लिए ऑरेकल (oracle) की आवश्यकता होगी। चूंकि contractNonce को 1 पर इनिशियलाइज़ किया गया है और प्रत्येक डिप्लॉयमेंट के बाद अपडेट किया जाता है, अनुमानित एड्रेस हमेशा वास्तविक डिप्लॉय किए गए एड्रेस से मेल खाएगा। इसलिए, deploy() कॉल रिवर्ट (revert) नहीं होगा।
हमारा उदाहरण सुविधा के लिए डिप्लॉयमेंट की संख्या को स्टोर करने के लिए nonce का उपयोग करता है, क्योंकि एक स्मार्ट कॉन्ट्रैक्ट से RPC कॉल करने के लिए ऑरेकल की आवश्यकता होगी।
require(c == predicted, "Address mismatch");
// If this condition is not met, deploy() call will revert
मान लें कि हम डिप्लॉयर के कॉन्ट्रैक्ट से एक सफल पहले डिप्लॉयमेंट के बाद दूसरा कॉन्ट्रैक्ट डिप्लॉय करना चाहते हैं। उस समय तक, दूसरा डिप्लॉयमेंट होने से पहले contractNonce बढ़कर 2 हो जाएगा।
यहाँ दूसरे डिप्लॉयमेंट के बाद deploy() कॉल का परिणाम दिया गया है।

यहाँ एक छवि दी गई है जो दिखाती है कि ऊपर से डिप्लॉय किया गया एड्रेस, predictAddress कॉल (जो LibRLP से computeAddress को कॉल करता है) से लौटाए गए एड्रेस से मेल खाता है।

CREATE2 का उपयोग करके कॉन्ट्रैक्ट एड्रेस का अनुमान कैसे लगाएं
Create2 को EIP-1014 में पेश किया गया था।
CREATE2 opcode के साथ किसी कॉन्ट्रैक्ट को डिप्लॉय करते समय, उसका एड्रेस तीन घटकों पर निर्भर करता है: डिप्लॉय करने वाले कॉन्ट्रैक्ट का एड्रेस, उपयोगकर्ता द्वारा प्रदान किया गया salt, और कॉन्ट्रैक्ट के creation (init) bytecode का हैश।
Create_contract_address = keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))
इस संबंध का उपयोग करके, हम कॉन्ट्रैक्ट के एड्रेस को पहले से परिकलित (precompute) कर सकते हैं, जैसा कि नीचे getAddress में दिखाया गया है:
function getAddress(
bytes memory createCode,
uint _salt
) public view returns (address) {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
_salt,
keccak256(createCode)
)
);
return address(uint160(uint(hash)));
}
जहाँ:
0xffएक कॉन्स्टेंट (constant) है जोCREATE2कोCREATEसे अलग करता है।saltविशिष्टता (uniqueness) सुनिश्चित करने के लिए उपयोगकर्ता द्वारा परिभाषित मान (32 बाइट्स) है।keccak256(createCode)कॉन्ट्रैक्ट के इनिशियलाइज़ेशन कोड का हैश है।
CREATE2 1 0xff बाइट को पहले क्यों जोड़ता है (prepends)
keccak256 इनपुट में 0xff एक भेदक बाइट (distinguishing byte) है जो यह सुनिश्चित करता है कि CREATE और CREATE2 opcode द्वारा जनरेट किए गए एड्रेसेस के बीच कोई टकराव (collision) न हो।
याद रखें कि CREATE एड्रेस गणना के लिए दो तत्वों ([deployer, nonce]) की एक सूची को एन्कोड करने के लिए RLP का उपयोग करता है। संदर्भ के लिए, डिप्लॉयर का एड्रेस हमेशा 20 बाइट्स का होता है, जबकि nonce इसके मान के आधार पर बाइट की लंबाई में भिन्न हो सकता है (व्यवहार में 0–8 बाइट्स, लेकिन सैद्धांतिक रूप से असीमित)।
चूंकि RLP लिस्ट प्रीफिक्स पेलोड की कुल लंबाई द्वारा निर्धारित किया जाता है, nonce मान बढ़ाने से पेलोड की लंबाई बढ़ सकती है, जो बदले में प्रीफिक्स को प्रभावित करती है। उदाहरण के लिए, यदि पेलोड की लंबाई ≤ 55 बाइट्स है, तो प्रीफिक्स इस रेंज में होगा: 0xc0 + payload_length।
यदि nonce इतना बड़ा है कि इसका RLP-एन्कोडेड प्रतिनिधित्व 34 बाइट्स से अधिक हो जाता है, तो यह पूरे [deployer, nonce] पेलोड को 55-बाइट की सीमा के पार धकेल देगा। इसलिए, RLP लिस्ट प्रीफिक्स [0xf8, 0xff] रेंज में एक बाइट के साथ शुरू होगा। फिर भी, यह स्थिति वास्तविकता में उत्पन्न नहीं होगी, क्योंकि 34-बाइट nonce का अर्थ है 17 बिलियन (अरब) से अधिक ट्रांजेक्शन्स — एक ऐसी संख्या जो किसी भी संभावित उपयोग से बहुत परे है।
इसके अलावा, EIP-2681 8 बाइट्स (64 बिट्स) के रूप में nonce पर एक सख्त ऊपरी सीमा (hard upper bound) को परिभाषित करता है, जिसका अर्थ है कि ≥ 2^64-1 nonce वाला कोई भी ट्रांजेक्शन अमान्य (invalid) है। परिणामस्वरूप, rlp.encode([deployer, nonce]) का लिस्ट प्रीफिक्स हमेशा [0xc0, 0xf7] रेंज के भीतर ही रहेगा।
//Here, 0xd6 indicates an RLP list of length 22 bytes.
rlp.encode([deployer, nonce]) = 0xd6 94 <20-byte deployer> <nonce>
इसलिए, यदि CREATE2 0xff प्रीफिक्स को पहले नहीं जोड़ता है, और बस deployer ++ salt ++ keccak256(init_code) जैसे रॉ कंकेटिनेशन (raw concatenation) को हैश करता है, तो एक सैद्धांतिक (हालांकि अविश्वसनीय रूप से दुर्लभ) जोखिम होगा कि कुछ चुने हुए मान एक ऐसी बाइट स्ट्रिंग उत्पन्न कर सकते हैं जो RLP-एन्कोडेड [deployer, nonce] के समान प्रीफिक्स से शुरू होती हो। हालांकि व्यावहारिक रूप से यह असंभव है, लेकिन डोमेन प्रमाणित रूप से अलग (provably disjoint) नहीं होंगे।
सिंगल 0xff बाइट को पहले जोड़कर, CREATE2 यह सुनिश्चित करता है कि हैश किया गया इनपुट हमेशा एक ऐसे मान (0xff) से शुरू होता है जो यथार्थवादी nonces वाले अकाउंट्स के लिए वैध RLP एन्कोडिंग की शुरुआत में कभी नहीं हो सकता है। यह हैश की गणना करने से पहले पूर्ण डोमेन सेपरेशन (total domain separation) प्राप्त करता है।
CREATE2 प्रीकंप्यूटेशन (precomputation) का उदाहरण
अब, आइए एक उदाहरण पर विचार करें जहाँ हम getAddress मेथड का उपयोग करके कॉन्ट्रैक्ट A से एक नए एड्रेस की गणना करते हैं। ध्यान दें, इस कॉन्ट्रैक्ट में कोई कंस्ट्रक्टर नहीं है।
contract A {
address public owner;
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
नीचे दिए गए DeployNewAddr कॉन्ट्रैक्ट में, getAddress फंक्शन कॉन्ट्रैक्ट A का creation bytecode और एक salt मान लेता है ताकि उस एड्रेस की गणना की जा सके जिस पर कॉन्ट्रैक्ट को CREATE2 opcode का उपयोग करके डिप्लॉय किया जाएगा। इस मामले में, गणना में DeployNewAddr का एड्रेस (address(this) के माध्यम से) उपयोग किया जाता है। इसलिए, परिणामी एड्रेस DeployNewAddr के एड्रेस, प्रदान किए गए salt, और creation (init) bytecode के हैश पर निर्भर करता है।
contract DeployNewAddr {
function getAddress(
bytes memory createCode,
uint _salt
) public view returns (address) {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
_salt,
keccak256(createCode)
)
);
return address(uint160(uint(hash)));
}
function getContractABytecode() public pure returns (bytes memory) {
bytes memory bytecode = type(A).creationCode;
return abi.encodePacked(bytecode);
}
}
नोट: getAddress() सही CREATE2 एड्रेस तभी लौटाएगा जब अंततः डिप्लॉयमेंट करने वाला कॉन्ट्रैक्ट स्वयं DeployNewAddr हो। यदि कोई भिन्न कॉन्ट्रैक्ट समान बाइटकोड और salt का उपयोग करके डिप्लॉयमेंट करता है, तो परिणामी एड्रेस भिन्न होगा, क्योंकि गणना में डिप्लॉयर का एड्रेस (address(this)) मेल नहीं खाएगा। वास्तविक डिप्लॉयमेंट संदर्भ के बाहर getAddress() का उपयोग करते समय, सुनिश्चित करें कि गणना में उपयोग किया गया डिप्लॉयर एड्रेस उस एड्रेस से मेल खाता है जो डिप्लॉयमेंट करेगा।
अब, आइए उस स्थिति पर विचार करें जहाँ कॉन्ट्रैक्ट A में आर्गुमेंट्स के साथ एक कंस्ट्रक्टर है।
getContractABytecode मेथड में कंस्ट्रक्टर आर्गुमेंट्स वाले कॉन्ट्रैक्ट्स को संभालना
जब कॉन्ट्रैक्ट्स डिप्लॉय किए जाते हैं (EOA या कॉन्ट्रैक्ट द्वारा), तो EVM कॉन्ट्रैक्ट के क्रिएशन कोड को निष्पादित करता है, जिसमें creationCode (compiled init bytecode) ABI-एन्कोडेड कंस्ट्रक्टर आर्गुमेंट्स के साथ जुड़ा (concatenated) होता है। इसलिए, यह व्यवहार CREATE2 के लिए विशिष्ट नहीं है।
याद करें कि, पिछले भाग में, हमने getContractABytecode नामक एक हेल्पर फंक्शन का उपयोग करके getAddress के लिए createCode आर्गुमेंट प्राप्त करने का विकल्प चुना था। इसलिए, कंस्ट्रक्टर आर्गुमेंट(s) वाले कॉन्ट्रैक्ट A के लिए, इस हेल्पर फंक्शन को कॉन्ट्रैक्ट के क्रिएशन बाइटकोड में सही एन्कोडेड प्रारूप (format) में आर्गुमेंट(s) को जोड़ने (append) की आवश्यकता होती है।
यहाँ एक कंस्ट्रक्टर आर्गुमेंट के साथ एक संशोधित (modified) कॉन्ट्रैक्ट A दिया गया है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
address public owner;
constructor(address _owner) payable {
owner = _owner;
}
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
यदि कॉन्ट्रैक्ट A में एक कंस्ट्रक्टर आर्गुमेंट है जैसा कि ऊपर दिखाया गया है, तो getContractABytecode फंक्शन _owner कंस्ट्रक्टर आर्गुमेंट की ABI एन्कोडिंग को abi.encodePacked(bytecode, abi.encode(_owner)) के रूप में जोड़ देगा।
यदि कॉन्ट्रैक्ट A में कई कंस्ट्रक्टर आर्गुमेंट्स हैं, जैसा कि नीचे दिखाया गया है, तो डिप्लॉयमेंट बाइटकोड में सभी आर्गुमेंट्स को सही क्रम में एन्कोड किया जाना चाहिए, कुछ इस तरह: abi.encodePacked(bytecode, abi.encode(arg1, arg2, ...))।
contract A {
address public owner;
address public artMaster;
constructor(address _owner, address _artMaster) payable {
owner = _owner;
artMaster = _artMaster;
}
///*************other logic*************///
}
ऊपर दिखाए गए अनुसार कंस्ट्रक्टर आर्गुमेंट्स _owner और _artMaster वाले कॉन्ट्रैक्ट A के लिए, getContractABytecode फंक्शन इस तरह दिखेगा:
function getContractAInitByteCode(
address _owner,
address _artMaster
) public pure returns (bytes memory) {
bytes memory bytecode = type(A).creationCode;
return abi.encodePacked(bytecode, abi.encode(_owner, _artMaster));
}
इससे पहले कि हम ऊपर दिए गए DeployNewAddr कॉन्ट्रैक्ट में getAddress मेथड का परीक्षण (test) करें, आइए एक वैकल्पिक CREATE2 डिप्लॉयमेंट दृष्टिकोण पर नज़र डालें जो क्रिएशन बाइटकोड को पास करने की आवश्यकता को समाप्त कर देता है। इसके बजाय, यह सॉलिडिटी के मूल कॉन्ट्रैक्ट इंस्टेंटिएशन सिंटैक्स (native contract instantiation syntax) के माध्यम से अंतर्निहित बाइटकोड डिप्लॉयमेंट (implicit bytecode deployment) पर निर्भर करता है।
क्रिएशन बाइटकोड को मैन्युअल रूप से पास किए बिना (CREATE2) कॉन्ट्रैक्ट एड्रेस को डिप्लॉय करना
यह CREATE2 दृष्टिकोण सॉलिडिटी के अंतर्निहित (built-in) new कीवर्ड के साथ-साथ एक salt पैरामीटर का उपयोग किसी कॉन्ट्रैक्ट को डिप्लॉय करने और उसका एड्रेस वापस करने के लिए करता है। कंपाइलर स्वचालित रूप से क्रिएशन बाइटकोड बनाने और कंस्ट्रक्टर आर्गुमेंट्स की एन्कोडिंग को संभालता है, इसलिए उन्हें मैन्युअल रूप से पास करने या बनाने की कोई आवश्यकता नहीं होती है।
इस दृष्टिकोण को नीचे DeployNewAddr1 में दिखाया गया है।
contract DeployNewAddr1 {
// Returns the address of the newly deployed contract
//DeployNewAddr1, shows a basic deployment with no constructor arguments (A()).
function deploy(uint _salt) external returns (address x) {
A Create2NewAddr = new A{salt: bytes32(_salt)};
return address(Create2NewAddr);
}
}
ऊपर दिए गए कोड में deploy फंक्शन इस मेथड का वह प्रकार (variant) दिखाता है, जब कॉन्ट्रैक्ट A में कोई कंस्ट्रक्टर आर्गुमेंट्स नहीं होते हैं।
नीचे दिए गए DeployNewAddr2 और DeployNewAddr3 दिखाते हैं कि कॉन्ट्रैक्ट A को डिप्लॉय करते समय कंस्ट्रक्टर आर्गुमेंट्स को कैसे संभाला जाता है, जब उसमें क्रमशः एक और दो कंस्ट्रक्टर आर्गुमेंट्स होते हैं।
contract DeployNewAddr2 {
// DeployNewAddr2 includes a single constructor argument _owner,
// passed to the constructor of contract A.
// Solidity automatically encodes constructor arguments and appends them to the creation bytecode.
function deploy(uint _salt, address _owner) external returns (address x) {
A Create2NewAddr = new A{salt: bytes32(_salt)}(_owner);
return address(Create2NewAddr);
}
}
contract DeployNewAddr3 {
// In DeployNewAddr3, two constructor arguments (msg.sender and _artMaster) are passed to contract A.
// As in DeployNewAddr2, these arguments are encoded and included in the creation bytecode.
function deploy(
uint _salt,
address _owner,
address _artMaster
) external returns (address x) {
A Create2NewAddr = new A{salt: bytes32(_salt)}(_owner, _artMaster);
return address(Create2NewAddr);
}
}
अब, आइए अपना कोड रन करें (वे जिनमें एक कंस्ट्रक्टर आर्गुमेंट है)।
आइए देखते हैं कि क्या DeployNewAddr2 में deploy वही अनुमानित एड्रेस लौटाएगा जो DeployNewAddr कॉन्ट्रैक्ट में getAddress लौटाता है। हम दोनों मेथड्स के लिए 29 के salt मान का उपयोग करेंगे, जहाँ getAddress में getContractABytecode फंक्शन से प्राप्त bytecode वेरिएबल को इनपुट के रूप में लिया जाएगा।
यहाँ दोनों मेथड्स को कॉल करने से प्राप्त परिणाम दिया गया है:

ऊपर दी गई छवि से, आप देख सकते हैं कि deploy फंक्शन द्वारा लौटाया गया एड्रेस x वही है जो getAddress फंक्शन द्वारा लौटाया गया एड्रेस है।
दो कॉन्ट्रैक्ट एड्रेसेस (A और B) को कैसे डिप्लॉय करें जो एक-दूसरे के एड्रेस को अपरिवर्तनीय (immutably) रूप से संदर्भित करते हैं
आइए इस ट्यूटोरियल का समापन यह दिखाते हुए एक उदाहरण के साथ करें कि एड्रेस प्रेडिक्शन कॉन्ट्रैक्ट डिप्लॉयमेंट की लागत को कैसे कम कर सकता है।
यदि हम दो स्मार्ट कॉन्ट्रैक्ट्स (A और B) डिप्लॉय करना चाहते हैं, और प्रत्येक कॉन्ट्रैक्ट को दूसरे के एड्रेस को संदर्भित (reference) करने की आवश्यकता है। साथ ही, उनके एड्रेस कभी नहीं बदलने चाहिए (अर्थात, वे अपरिवर्तनीय (immutable) होने चाहिए)।
यह सेटअप कई चुनौतियाँ पेश करता है जिनका समाधान किया जाना चाहिए:
- पहले
Aको डिप्लॉय करने से यहBको संदर्भित करने से रोकता है, जो अभी तक मौजूद नहीं है। - पहले
Bको डिप्लॉय करने से ठीक वही समस्या विपरीत दिशा में उत्पन्न होती है—Bडिप्लॉय होने से पहलेAको संदर्भित नहीं कर सकता। - डिप्लॉयमेंट के बाद, एड्रेसेस अपरिवर्तनीय होने चाहिए; किसी भी सेटर फंक्शन (setter function) या बाहरी अपडेट की अनुमति नहीं होनी चाहिए।
इस समस्या को हल करने का एक तरीका फैक्ट्री कॉन्ट्रैक्ट एड्रेस का उपयोग करके A और B के एड्रेसेस को पहले से परिकलित (precompute) करना है। फिर, A को B के प्रीकंप्यूटेड एड्रेस को कंस्ट्रक्टर आर्गुमेंट के रूप में लेकर डिप्लॉय करें — और B को A के एड्रेस के साथ।
भले ही यह दृष्टिकोण तकनीकी रूप से सही है, लेकिन इसके कुछ नुकसान (trade-offs) हैं। फैक्ट्री कॉन्ट्रैक्ट को ऑन-चेन डिप्लॉय और स्टोर किया जाएगा, जो समग्र बाइटकोड फुटप्रिंट को बढ़ाता है। इसके अतिरिक्त, इस दृष्टिकोण में अतिरिक्त गैस लागत लगती है — दोनों फैक्ट्री को डिप्लॉय करने से और लक्ष्य कॉन्ट्रैक्ट्स को डिप्लॉय करने के लिए इसके लॉजिक को निष्पादित करने से।
इस ओवरहेड से बचने के लिए, हम एक सामान्य कॉन्ट्रैक्ट डिप्लॉयमेंट का उपयोग कर सकते हैं और इस लेख में हमारे द्वारा चर्चा की गई तकनीकों का उपयोग करके एड्रेस का अनुमान लगा सकते हैं।
RLP मेथड का उपयोग करके Foundry स्क्रिप्ट के माध्यम से कॉन्ट्रैक्ट एड्रेस को प्रीकंप्यूट करना
नीचे दिए गए चरणों में, हम अपने फैक्ट्री अकाउंट (एक EOA) का एड्रेस लॉग करते हैं, इसका वर्तमान nonce प्राप्त करते हैं, कॉन्ट्रैक्ट एड्रेसेस (EOA के nonce के आधार पर) को प्रीकंप्यूट करते हैं, और foundry स्क्रिप्ट का उपयोग करके एक-दूसरे को संदर्भित करते हुए उन्हें डिप्लॉय करते हैं।
चरण 1: फैक्ट्री (डिप्लॉयर) एड्रेस को लॉग करने वाली एक स्क्रिप्ट लिखें, जैसा कि नीचे दिखाया गया है।
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {A, B} from "../src/DeployAddr.sol";
contract DeployAddrScript is Script {
A public a;
function run() public {
uint256 pk = vm.envUint("PRIV_KEY");
address dep = vm.addr(pk);
//WARNING: With vm.envUint, the private key is loaded in cleartext into memory
//NEVER use this pattern in production or with private keys managing real funds.
//Assume any key kept in .env will eventually be stolen
console.log("This is the deployer's address:", dep);
vm.startBroadcast(pk);
new A(address(0));
vm.stopBroadcast();
}
}
टर्मिनल परिणाम:
$ forge script script/DeployAddr.s.sol --rpc-url http://localhost:8545
[⠒] Compiling...
No files changed, compilation skipped
Script ran successfully.
== Logs ==
This is the deployer's address: 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A
चरण 2: कॉन्ट्रैक्ट्स A और B के एड्रेस की गणना करें।
चूँकि अब हमारे पास अपनी प्राइवेट की (private key) से हमारे डिप्लॉयर का एड्रेस है, हम cast compute-address <address> --nonce <value> कमांड का उपयोग करके डिटरमिनिस्टिक (deterministically) रूप से एड्रेस जनरेट कर सकते हैं।
डिप्लॉयर के एड्रेस 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A के लिए नीचे nonce 0 और nonce 1 का परिणाम देखें:
$ cast compute-address 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A --nonce 0
Computed Address: 0x9b4393C60f2408de53F04d93aD178ffBAF25b202
user@DESKTOP-QOJ9UFF MINGW64 ~/Desktop/testFile (master)
$ cast compute-address 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A --nonce 1
Computed Address: 0x20cf99233e5B16Fba6B0E7bA70768d6EDe75789D
नोट: गलत nonce का उपयोग करने पर गलत एड्रेस प्राप्त होगा। उदाहरण के लिए, ऊपर दी गई स्क्रिप्ट में, एक बार new A(address(0)) डिप्लॉय हो जाने पर (एक EOA का उपयोग करके), डिप्लॉयर का nonce 0 से 1 तक बढ़ जाता है।
उस डिप्लॉयमेंट के बाद nonce 0 का उपयोग करके एड्रेस की गणना करने से कॉन्ट्रैक्ट के एड्रेस में मिसमैच (mismatch) होगा।
वैकल्पिक रूप से, हम नीचे दिखाए गए अनुसार vm.getNonce चीटकोड और computeAddress का उपयोग करके एड्रेसेस A और B निर्धारित कर सकते हैं।
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {A, B} from "../src/DeployAddr.sol";
import {LibRLP} from "lib/LibRLP.sol";
contract DeployAddrScript is Script {
A public a;
//B public b;
function run() public {
uint256 pk = vm.envUint("PRIV_KEY");
address dep = vm.addr(pk);
console.log("This is the deployer's address:", dep);
vm.startBroadcast(pk);
//nonce = 0,
new A(address(0));
//Deploys a new instance of contract A, passing in address(0) as a constructor argument.
// after this, nonce = 1.
// compute the current nonce for the address
uint256 currentNonce = vm.getNonce(dep);
console.log("This is the current nonce: %s", currentNonce);
address predicted_a = LibRLP.computeAddress(dep, currentNonce);
address predicted_b = LibRLP.computeAddress(dep, currentNonce + 1);
console.log("predicted_a: %s", predicted_a);
console.log("predicted_b: %s", predicted_b);
vm.stopBroadcast();
}
}
स्क्रिप्ट रन करने के बाद टर्मिनल का परिणाम यहाँ दिया गया है:
Script ran successfully.
== Logs ==
This is the deployer's address: 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A
This is the current nonce: 1
predicted_a: 0x20cf99233e5B16Fba6B0E7bA70768d6EDe75789D
predicted_b: 0xca3fF2a864026daC337312142Aa71D57c7D8Dde3
चरण 3: कॉन्ट्रैक्ट्स को उनके संबंधित कंस्ट्रक्टर आर्गुमेंट्स (अर्थात, प्रीकंप्यूटेड एड्रेसेस) के साथ डिप्लॉय करें।
अब, आइए कॉन्ट्रैक्ट्स (A और B) को डिप्लॉय करें और परिणामों की तुलना predicted_a और predicted_b के साथ करें।
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {A, B} from "../src/DeployAddr.sol";
import {LibRLP} from "lib/LibRLP.sol";
contract DeployAddrScript is Script {
A public a;
B public b;
function run() public {
uint256 pk = vm.envUint("PRIV_KEY");
address dep = vm.addr(pk);
console.log("This is the deployer's address:", dep);
vm.startBroadcast(pk);
// compute the current nonce for the address
uint256 currentNonce = vm.getNonce(dep);
console.log("This is the current nonce: %s", currentNonce);
address predicted_a = LibRLP.computeAddress(dep, currentNonce);
address predicted_b = LibRLP.computeAddress(dep, currentNonce + 1);
A a = new A(predicted_b);
B b = new B(predicted_a);
console.log("address(a): %s", address(a));
console.log("predicted_a: %s", predicted_a);
console.log("address(b): %s", address(b));
console.log("predicted_b: %s", predicted_b);
vm.stopBroadcast();
}
}
यहाँ टर्मिनल का परिणाम है:
Script ran successfully.
== Logs ==
This is the deployer's address: 0x8768C6FB71815b2e8Ab6dD31b67a926781aC8f1A
This is the current nonce: 1
address(a): 0x20cf99233e5B16Fba6B0E7bA70768d6EDe75789D
predicted_a: 0x20cf99233e5B16Fba6B0E7bA70768d6EDe75789D
address(b): 0xca3fF2a864026daC337312142Aa71D57c7D8Dde3
predicted_b: 0xca3fF2a864026daC337312142Aa71D57c7D8Dde3
हम टर्मिनल के परिणाम में देख सकते हैं कि डिप्लॉय किए गए एड्रेस a और b, क्रमशः अनुमानित एड्रेसेस predicted_a और predicted_b से मेल खाते हैं।
निष्कर्ष
इस लेख में, हमने यह पता लगाया कि विभिन्न डिप्लॉयमेंट मेथड्स में Ethereum कॉन्ट्रैक्ट एड्रेसेस का अनुमान कैसे लगाया जाता है। CREATE opcode का उपयोग करके डिप्लॉय किए गए कॉन्ट्रैक्ट्स के लिए, हमने दिखाया कि परिणामी एड्रेस केवल डिप्लॉयर के एड्रेस और nonce पर निर्भर करता है — कंस्ट्रक्टर आर्गुमेंट्स या बाइटकोड की इसमें कोई भूमिका नहीं होती है। CREATE2 के लिए, हमने बताया कि कैसे एड्रेस प्रेडिक्शन में एक salt और पूरे क्रिएशन बाइटकोड (कंस्ट्रक्टर आर्गुमेंट्स सहित) का keccak256 हैश शामिल होता है। अंत में, हमने वर्णन किया कि Foundry स्क्रिप्ट्स और computeAddress का उपयोग करके ऑफ-चेन (off-chain) दो एक-दूसरे पर निर्भर (interdependent) कॉन्ट्रैक्ट्स को कुशलतापूर्वक प्रीकंप्यूट और डिप्लॉय कैसे किया जाए।
इस गाइड में संदर्भित EIPs
EIP-161: अकाउंट क्रिएशन ट्रांजेक्शन्स को परिभाषित करता है, “एम्प्टी अकाउंट्स” (empty accounts) की अवधारणा, nonce हैंडलिंग, और उनके क्लीनअप के लिए नियम पेश करता है।
EIP-1014: CREATE2 opcode को पेश करता है।
EIP-2681: अकाउंट nonce की सीमा (Limit) को 0 और 2^64-1 के बीच परिभाषित करता है।