Cart updating

ShopsvgYour cart is currently is empty. You could visit our shop and start shopping.

Now Reading: 9 طرق فعالة لتوفير الغاز عند نشر العقود على إيثريوم

Loading
svg
Open
svg0

9 طرق فعالة لتوفير الغاز عند نشر العقود على إيثريوم

أكتوبر 1, 2023 - 3 دقائق للقرائه

توفير الغاز في إيثريوم


تتعامل شبكة إيثريوم مع عدد كبير من المعاملات يوميًا، ومع ذلك، يمكن أن يكون نشر العقود الذكية باهظ الثمن بسبب تكاليف الغاز المرتفعة. في هذا المقال، سنستعرض تسعة طرق فعالة لتقليل تكلفة الغاز عند نشر على إيثريوم. ولمن يرغب في التعمق أكثر في موضوع تحسين استهلاك الغاز، ننصح بقراءة مقالتنا السابقة بعنوان “تحسين إستهلاك الغاز في Solidity“.

1. استخدم الـ nonce  الخاص  بالحساب للتنبؤ بعناوين العقود الذكية المترابطة وبالتالي تجنب متغيرات التخزين ووظائف ضبط العناوين

عند استخدام نشر العقود التقليدية، يمكن حساب عنوان العقد الذكي بشكل محدد بناءً على عنوان الناشر ورقم المعاملة المسمى (nonce). يمكن لمكتبة LibRLP من Solady أن تساعدنا في القيام بذلك.

خذ السيناريو التالي لإستخدام الـ nonce ؛

يسمح StorageContract للكاتب فقط بتعيين متغير التخزين x، مما يعني أنه يحتاج إلى معرفة عنوان الكاتب. ولكن لكي يكتب الكاتب إلى StorageContract، فإنه يحتاج أيضًا إلى معرفة عنوان StorageContract.

الكود أدناه هو نهج ساذج لهذه المشكلة. إنه يتعامل معها من خلال وجود وظيفة ضبط تقوم بتعيين متغير تخزين بعد النشر. لكن متغيرات التخزين باهظة الثمن ونفضل تجنبها.

contract StorageContract {
    address immutable public writer;
    uint256 public x;
    
    constructor(address _writer) {
        writer = _writer;
    }

    function setX(uint256 x_) external {
        require(msg.sender == address(writer), "only writer can set");
        x = x_;
    }
}

contract Writer {
    StorageContract public storageContract;

    // cost: 49291
    function set(uint256 x_) external {
        storageContract.setX(x_);
    }

    function setStorageContract(address _storageContract) external {
        storageContract = StorageContract(_storageContract);
    }
}
Solidity

وهذا يكلف أكثر عند النشر وفي وقت التشغيل. يتضمن ذلك نشر الكاتب، ثم نشر StorageContract مع تعيين عنوان الكاتب المنشور ككاتب. ثم يقوم بتعيين متغير StorageContract الخاص بالكاتب باستخدام StorageContract الذي تم إنشاؤه حديثًا. يتضمن هذا الكثير من الخطوات ويمكن أن يكون مكلفًا نظرًا لأننا نقوم بتخزين StorageContract في الـ storage. استدعاء Writer.setX() يكلف 49 ألف غاز.

الطريقة الأكثر فعالية للقيام بذلك هي حساب العنوان الذي سيتم نشر StorageContract و Writer إليه مسبقًا وتعيينهما في كل من المنشئين.

فيما يلي مثال لما سيبدو عليه هذا؛

import {LibRLP} from "https://github.com/vectorized/solady/blob/main/src/utils/LibRLP.sol";

contract StorageContract {
    address immutable public writer;
    uint256 public x;
    
    constructor(address _writer) {
        writer = _writer;
    }

    // cost: 47158
    function setX(uint256 x_) external {
        require(msg.sender == address(writer), "only writer can set");
        x = x_;
    }
}

contract Writer {
    StorageContract immutable public storageContract;
    
    constructor(StorageContract _storageContract) {
        storageContract = _storageContract;
    }

    function set(uint256 x_) external {
        storageContract.setX(x_);
    }
}

// one time deployer.
contract BurnerDeployer {
    using LibRLP for address;

    function deploy() public returns(StorageContract storageContract, address writer) {
        StorageContract storageContractComputed = StorageContract(address(this).computeAddress(2)); // contracts nonce start at 1 and only increment when it creates a contract
        writer = address(new Writer(storageContractComputed)); // first creation happens here using nonce = 1
        storageContract = new StorageContract(writer); // second create happens here using nonce = 2
        require(storageContract == storageContractComputed, "false compute of create1 address"); // sanity check
    }
}
Solidity

هنا، يكلف استدعاء Writer.setX() إلى  47 ألف غاز. لقد وفرنا 2k+ من الغاز من خلال الحوسبة المسبقة للعنوان الذي سيتم نشر StorageContract إليه قبل نشره حتى نتمكن من استخدامه عند نشر Writer، وبالتالي لا حاجة إلى وظيفة الضبط.

ليس من الضروري استخدام عقد منفصل لتوظيف هذه التقنية، يمكنك القيام بذلك داخل النشر  بدلاً من ذلك.

2. جعل المنشئين مستحقين الدفع

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

contract A {}

contract B {
    constructor() payable {}
}
Solidity

أدى جعل المنشئ مستحق الدفع إلى توفير 200 غاز عند النشر. وذلك لأن الوظائف غير المستحقة الدفع تحتوي على طلب ضمني (msg.value == 0) مُدرج فيها. بالإضافة إلى ذلك، يعني عدد أقل من bytecode  في وقت النشر تكلفة وقود أقل بسبب بيانات المكالمات الأصغر. لمعلومات أكثر حول ذلك راجع مقالاتنا عن كود إنشاء العقد الذكي للإيثريوم

3. يمكن تقليل حجم النشر عن طريق تحسين تجزئة IPFS للحصول على المزيد من الأصفار (أو باستخدام خيار –no-cbor-metadata من الـ compiler)

يقوم برنامج التحويل البرمجي Solidity بإلحاق 51 بايت من البيانات الوصفية بكود العقد الذكي الفعلي. نظرًا لأن كل بايت نشر يكلف 200 غاز، فإن إزالتها يمكن أن تؤدي إلى توفير أكثر من 10000 وحدة غاز عند النشر.

هذا ليس مثاليًا دائمًا لأنه قد يؤثر على  طرق التحقق من العقود الذكية. 

4. استخدم التدمير الذاتي (selfdestruct) في المنشئ إذا كان العقد سيستخدم لمرة واحده

في بعض الأحيان، يتم استخدام العقود لنشر عدة عقود في معاملة واحدة، مما يستلزم القيام بذلك في المنشئ.

إذا كان الاستخدام الوحيد للعقد هو الكود الموجود في المنشئ، فإن التدمير الذاتي في نهاية العملية سيوفر الغاز.

على الرغم من أنه تم سيتم إزالة  التدمير الذاتي للإزالة في تحديث  قادم، إلا أنه سيظل مدعومًا في المُنشئ وفقًا لـ EIP 6780

5. فهم المفاضلات عند الاختيار بين الوظائف الداخلية (internal functions) والمعدلات (modifiers)

تقوم المعدلات بإدخال كود التنفيذ الخاص بها حيث يتم استخدامه بينما تنتقل الوظائف الداخلية إلى الموقع في كود runtime  حيث يتم تنفيذه. وهذا يجلب بعض المقايضات لكلا الخيارين.

  • إن استخدام المعدلات أكثر من مرة يعني التكرار وزيادة حجم كود  runtime  ولكنه يقلل من تكلفة الغاز بسبب عدم وجود القفز إلى إزاحة تنفيذ الوظيفة الداخلية والقفز مرة أخرى للمتابعة. وهذا يعني أنه إذا كانت تكلفة الغاز في وقت التشغيل هي الأكثر أهمية بالنسبة لك، فيجب أن تكون المعدلات هي اختيارك ولكن إذا كانت تكلفة نشر الغاز و/أو تقليل حجم كود الإنشاء هو الأكثر أهمية بالنسبة لك، فسيكون استخدام الوظائف الداخلية هو الأفضل.
  • ومع ذلك، فإن المعدلات لديها المقايضة بحيث أنه لا يمكن تنفيذها إلا في بداية أو نهاية الوظيفة. وهذا يعني أن تنفيذها في منتصف الوظيفة لن يكون ممكنًا بشكل مباشر، على الأقل ليس بدون وظائف داخلية مما يقتل الغرض الأصلي. وهذا يؤثر على مرونتها. ومع ذلك يمكن استدعاء الوظائف الداخلية في أي وقت في الوظيفة.

مثال يوضح الفرق في تكلفة الغاز باستخدام المعدلات والوظيفة الداخلية

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/** deployment gas cost: 195435
    gas per call:
              restrictedAction1: 28367
              restrictedAction2: 28377
              restrictedAction3: 28411
 */
 contract Modifier {
    address owner;
    uint256 val;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function restrictedAction1() external onlyOwner {
        val = 1;
    }

    function restrictedAction2() external onlyOwner {
        val = 2;
    }

    function restrictedAction3() external onlyOwner {
        val = 3;
    }
}



/** deployment gas cost: 159309
    gas per call:
              restrictedAction1: 28391
              restrictedAction2: 28401
              restrictedAction3: 28435
 */
 contract InternalFunction {
    address owner;
    uint256 val;

    constructor() {
        owner = msg.sender;
    }

    function onlyOwner() internal view {
        require(msg.sender == owner);
    }

    function restrictedAction1() external {
        onlyOwner();
        val = 1;
    }

    function restrictedAction2() external {
        onlyOwner();
        val = 2;
    }

    function restrictedAction3() external {
        onlyOwner();
        val = 3;
    }
}
Solidity
عمليةالنشرrestrictedAction1()restrictedAction2()restrictedAction3()
المعدلات 195435283672837728411
الوظائف الداخلية159309283912840128435

من الجدول أعلاه يمكننا أن نرى أن العقد الذي يستخدم المعدلات يكلف أكثر من 35 ألف غاز من العقد الذي يستخدم الوظائف الداخلية عند نشره بسبب تكرار وظيفة onlyOwner  في 3 وظائف.

أثناء وقت التشغيل، يمكننا أن نرى أن كل وظيفة تستخدم المعدلات تكلف 24 غازًا ثابتًا أقل من الوظائف التي تستخدم الوظائف الداخلية.

6. استخدم clones  أو metaproxies عند نشر عقود ذكية متشابهة جدًا والتي لا يتم استدعاؤها بشكل متكرر

عند نشر عدة عقود ذكية مماثلة، يمكن أن تكون تكاليف الغاز مرتفعة. لتقليل هذه التكاليف، يمكنك استخدام الحد الأدنى من النسخ أو metaproxies  الذين يقومون بتخزين عنوان عقد التنفيذ في الـ bytecode  الخاص بهم والتفاعل معه كوكيل.

ومع ذلك، هناك مفاضلة بين تكلفة وقت التشغيل وتكلفة نشر النسخ. يعتبر التفاعل مع النسخ المستنسخة أكثر تكلفة من العقود العادية بسبب استدعاء المفوض الذي تستخدمه، لذا يجب استخدامها فقط عندما لا تحتاج إلى التفاعل معها بشكل متكرر. على سبيل المثال، يستخدم عقد Gnosis Safe النسخ لتقليل تكاليف النشر.

تعرف على المزيد حول كيفية استخدام النسخ والميتابروكسيات لتقليل تكاليف الغاز لنشر العقود الذكية من منشورات مدونتنا:

  • EIP-1167: الحد الأدنى من معيار الوكيل
  • EIP-3448 استنساخ ميتابروكسي

7. يمكن أن تكون وظائف الأدمين مستحقة الدفع

يمكننا أن نجعل وظائف محددة للأدمين مستحقة الدفع لتوفير الغاز ، لأن الـ compiler لن يتحقق من قيمة استدعاء الوظيفة.

سيؤدي هذا أيضًا إلى جعل العقد أصغر حجمًا وأرخص في النشر حيث سيكون هناك عدد أقل من أكواد التشغيل في كود الإنشاء ووقت التشغيل.

8. تكون الأخطاء المخصصة (عادةً) أصغر من إستخدام require 

تعتبر الأخطاء المخصصة أقل تكلفة من إستخدام require التي تتطلب نصوص بسبب كيفية معالجة الأخطاء المخصصة. تقوم Solidity بتخزين أول 4 بايتات فقط من تجزئة توقيع الخطأ وإرجاع ذلك فقط. وهذا يعني أنه أثناء عملية الرجوع، يلزم تخزين 4 بايت فقط في الذاكرة. في حالة  النص  في عبارات require ، يجب على Solidity تخزينها (في الذاكرة) وإعادتها بما لا يقل عن 64 بايت.

وهنا مثال أدناه.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract CustomError {
    error InvalidAmount();

    function withdraw(uint256 _amount) external pure {
        if (_amount > 10 ether) revert InvalidAmount();
    }
}

// This uses more gas than the above contract
contract NoCustomError {
    function withdraw(uint256 _amount) external pure {
        require(_amount <= 10 ether, "Error: Pass in a valid amount");
    }
}
Solidity

9. استخدم مصانع create2 الحالية بدلاً من نشر مصانعك الخاصة

هذا العنوان هو النفس التفسيرية. إذا كنت بحاجة إلى عنوان محدد، فيمكنك عادةً إعادة استخدام العنوان الذي تم نشره مسبقًا.

ختاما

يتيح لنا فهم أفضل طرق لتوفير الغاز عند نشر العقود الذكية على إيثريوم الاستفادة القصوى من هذه الشبكة دون تحميل تكاليف غير ضرورية. من خلال تطبيق النصائح والتقنيات المذكورة أعلاه، يمكن تحسين كفاءة النشر وخفض التكاليف، مما يجعل التطوير على إيثريوم أكثر فعالية واقتصادية.

svg

إطرح رأيك ؟

أظهر التعليقات / إنرك تعليق

Leave a reply

Loading
svg