كيفية إنشاء dApps قابلة للتحجيم والعقود الذكية في Ethereum مع قنوات الدولة خطوة بخطوة. الجزء 1

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

ليس عليك انتظار التحسينات المستقبلية في blockchain. التكنولوجيا موجودة لتبقى ويمكنك استخدامها لإنشاء جميع أنواع dApps. في الوقت الحالي ، يتم استخدام القنوات الحكومية في الغالب للألعاب القائمة على blockchain في Ethereum.

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

ما هي قنوات الدولة؟

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

تسمى "الحالة" لأن كل تفاعل يجب أن يكون له حالة يمكن تحديثها. فكر في نتيجة لعبة أو رصيد بنكي.

لماذا توجد؟

تم إنشاء قنوات الحالة لأن تطبيقات الأثير سرعان ما ازدادت شعبية مما يجعل blockchain غير صالح للاستخدام لأنه تم تطويره مع استخدام معتدل. أنها تسمح المعاملات المستمرة دون دفع ثمن الغاز أو انتظار عمال المناجم لمعالجة المعاملات.

وهو ما يعني المعاملات الحرة والسريعة.

ما الذي نحتاجه لإعداد قناة دولة؟

  1. على الأقل 2 مستخدمين سيتفاعلون مع بعضهم البعض. يجب فتح قناة بين مستخدمين أو أكثر. مشابه لتطبيق الدردشة.
  2. عقد ذكي مع منطق قناة الدولة الذي سيفتحه ويغلقه.
  3. إذا تم استخدام قناة الحالة في لعبة ، فستكون هناك حاجة إلى ضمان لكل المستخدمين. سيتم تخزين هذا الضمان في الأثير في العقد الذكي عند فتح القناة.
  4. تطبيق javascript يقوم بإنشاء الرسائل الموقعة التي سيتم تبادلها خارج السلسلة بين المستخدمين.
  5. Metamask أو أداة مشابهة لتوقيع الرسائل. توقيع الرسائل لا يكلف الغاز ويتم تنفيذها على الفور. يُطلب من كلا المستخدمين التوقيع على الرسائل لضمان أن هذه هي التي تنشئ هذه المعاملة.
  6. إرسال بريد إلكتروني أو أي تطبيق خارجي لتبادل تلك الرسائل الموقعة لجعل هذا التطبيق ممكنًا.

كيف يعملون؟

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

  1. في قناة الحالة بين مستخدمين ، تنشر الأولى الأولى العقد الذكي الذي "سيفتح" القناة.
  2. الثاني ينفذ وظيفة من هذا العقد الذكي "للانضمام" تلك القناة الدولة "
  3. ثم يمكنهم البدء في تبادل الرسائل الموقعة للتطبيق. يمكن لكل من المستخدمين الوصول إلى تطبيق جافا سكريبت مخصص لإنشاء رسائل بالمعلومات التي سيفعلونها في عقد ذكي ، ولكن خارج السلسلة.
  4. تعتمد سرعة المعاملات على سرعة قيام كل مستخدم بإنشاء وتوقيع هذه الرسائل. استمروا في تبادل الرسائل ، ولعبوا خارج السلسلة حتى قرروا انتهاء اللعبة.
  5. عند الانتهاء من اللعبة ، يمكن لأي شخص الذهاب إلى العقد الذكي وتنفيذ وظيفة لإنهائها والتي ستبدأ مرحلة "التفاوض".
  6. في هذه المرحلة ، لدى كل من المستخدمين مهلة يوم واحد لتحميل أحدث رسالتين لديهم إلى العقد الذكي. يتحقق العقد الذكي من أحدث الرسائل ويفرج عن الأموال لإنهاء اللعبة بناءً على تلك المعلومات. تحتوي كل رسالة على نتائج التفاعلات السابقة بحيث يكون من الآمن التحقق من أحدثها.

كيف يمكنك تطبيق هذا في موقف حقيقي؟

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

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

هذا هو الفهرس الذي سنتابعه لإنشاء هذا التطبيق اللامركزي:

  1. إنشاء تطبيق الويب المرئي. هذه هي الواجهة ، كيف ستبدو اللعبة للمستخدمين الخارجيين. سيتم استخدامه كوسيلة لتبادل الرسائل الموقعة لقنوات الدولة.
  2. إنشاء الوظائف المطلوبة لتوقيع الرسائل وتشفيرها.
  3. إنشاء العقد الذكي.

1. إنشاء تطبيق الويب المرئي

قبل أن أبدأ بالرمز ، أرغب في التأكد من توضيح التفاصيل الكاملة لتطبيق الويب. كيف سيبدو ، ما هو محور الاهتمام.

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

وبالتالي فإن الإطار سيكون شيء مثل هذا:

  1. ينتقل اللاعب 1 إلى تطبيق الويب ، وينقر على زر يقول "ابدأ لعبة جديدة" ، ثم يقوم بمعاملة metamask لنشر وإعداد العقد الذكي. يتلقى عنوان عقد ذكي يمكنه إرساله إلى اللاعب الآخر لبدء اللعبة.
  2. ينتقل برنامج Player 2 إلى تطبيق الويب ، وينقر على زر مكتوب عليه "الانضمام إلى اللعبة الحالية" مع عنوان العقد الذي تم استلامه من اللاعب 1 ، ثم يقوم بإجراء عملية تحويل إلى metamask لإعداد اللعبة الموجودة بالفعل وإرسال ضمان.

لذلك دعونا نبدأ هناك. لنقم بإنشاء مربع في منتصف تطبيق الويب باستخدام زرين. إنشاء مجلد يسمى النرد وملف داخل ودعا index.html. إليك الكود:

هكذا يبدو الزران افتراضيًا


    <رئيس>
        
         Dice ethereum game 
    
    
        
                                       

في هذا الرمز ، قمت للتو بإنشاء بنية HTML الأساسية مع div تحتوي على الأزرار والعنوان. لاحظ أن div يحتوي على فئة تسمى المحتوى الرئيسي والتي سنستخدمها في لحظة.

دعونا نجعل ذلك أجمل مع بعض المغلق. قم بإنشاء ملف يسمى index.css بالشفرة التالية (يمكنك نسخ ولصق هذا):

إليك كيف ستبدو
الجسم {
    font-family: sans-serif؛
}
.المحتوى الرئيسي {
    الهامش: السيارات ؛
    أقصى عرض: 500 بكسل ؛
    لون الخلفية: whitesmoke؛
    الحشو: 50 بكسل ؛
    دائرة نصف قطرها الحدود: 10px ؛
    عرض: الشبكة.
    شبكة القوالب الصفوف: 1fr 1fr؛
    أعمدة شبكة القوالب: 1fr 1fr؛
    شبكة عمود الفجوة: 10px ؛
}
.main-content h1 {
    شبكة العمود: 1 / span 2 ؛
}
.main-button button {
    الحدود: لا شيء ؛
    اللون الابيض؛
    لون الخلفية: # 007 dff؛
    الحشو: 20px.
    دائرة نصف قطرها الحدود: 5px ؛
    المؤشر: المؤشر ؛
}
.main-button button: hover {
    التعتيم: 0.8 ؛
}
.main-button button: active {
    التعتيم: 0.6 ؛
}

لقد أضفت عنوان h1 إلى html لجعله يبدو أفضل ، تأكد من تحديث html الخاص بك عن طريق إضافة الرابط إلى css:



    <رئيس>
        
        
         Dice ethereum game 
    
    
        
            

Ethereum Dice

                                       

ربما لاحظت أنني أستخدم شبكة css الجديدة. هذا لأنه متوفر في الغالب للمتصفحات الكبيرة ، لذا فمن الآمن جدًا استخدامه في هذه المرحلة لأن معظم الناس سيرون المغلق بشكل صحيح.

قررت أن أفضل طريقة لعرض الإجراء التالي المطلوب من المستخدم هي إظهار div في javascript مع المعلومات المطلوبة. لذلك عندما ينقر على "بدء لعبة جديدة" ، سيرى مربعًا يسأله عن مقدار الضمان الذي يريد إعداده للعبة.

إنه ينقر على "الانضمام إلى اللعبة الحالية" سيُطلب منه الحصول على عنوان الضمان والعقد للعبة الحالية.

إليك كيفية استجابة إجراءات الزر:

كيف يبدو التطبيق مع جافا سكريبت عادي

لجعل ذلك ممكنًا قمت بإنشاء ملف index.js مع بعض منطق جافا سكريبت. إليك جافا سكريبت ، تأكد من كتابته بيديك إذا كنت تريد معرفة أفضل هذا:

اسمحوا لي أن أشرح ما فعلت هناك:

  • أولاً ، قمت بإنشاء وظيفة تسمى start () سيتم تنفيذها على الفور لإغلاق المحتوى بحيث يكون لطيفًا ومضمونًا في وظيفة واحدة كبيرة.
  • بعد ذلك ، قمت بإنشاء مستمعين للأحداث يتم تنشيطهما كلما قمت بالنقر فوق أزرار البدء أو الانضمام في ملف html. واحد للزر # new-game وآخر للزر # join-game. أنا أستخدم document.querySelector () التي تعد واحدة من أقوى الطرق لتحديد أي شيء في رمز js الخاص بك.
  • داخل هؤلاء المستمعين ، أقوم بإظهار أو إخفاء مربع div لكل عنصر مطابق. أساسا اختيار محاصر مع querySelector وإزالة أو إضافة فئة مخفية والتي هي الإعداد في المغلق لعرضه: لا شيء ؛ .

ثم يمكننا توصيل ملف js باستخدام index modifie index.html:



    <رئيس>
        
        
         Dice ethereum game 
    
    
        
            

Ethereum Dice

                         
            
            
            
        
        
    

أنا جريئة قطع جديدة من التعليمات البرمجية المضافة. فيما يلي تحديث المغلق لنمط المعلومات الجديدة:

الجسم {
    font-family: sans-serif؛
}
.المخفية
    عرض لا شيء؛
}
.المحتوى الرئيسي {
    الهامش: السيارات ؛
    أقصى عرض: 500 بكسل ؛
    لون الخلفية: whitesmoke؛
    الحشو: 50 بكسل ؛
    دائرة نصف قطرها الحدود: 10px ؛
    عرض: الشبكة.
    شبكة القوالب الصفوف: 1fr 80px auto؛
    أعمدة شبكة القوالب: 1fr 1fr؛
    شبكة عمود الفجوة: 10px ؛
}
.main-content h1 {
    شبكة العمود: 1 / span 2 ؛
}
.main-button button {
    الحدود: لا شيء ؛
    اللون الابيض؛
    لون الخلفية: # 007 dff؛
    الحشو: 20px.
    دائرة نصف قطرها الحدود: 5px ؛
    المؤشر: المؤشر ؛
}
.main-button button: hover {
    التعتيم: 0.8 ؛
}
.main-button button: active {
    التعتيم: 0.6 ؛
}
زر المحتوى الرئيسي: معطل {
    التعتيم: 0.5 ؛
    لون الخلفية: رمادي.
    المؤشر: السيارات ؛
}
.المحتوى الرئيسي للمدخلات {
    العرض: 100 ٪ ؛
    دائرة نصف قطرها الحدود: 10px ؛
    الحشو: 10px.
    الحدود: 1px lightgrey الصلبة.
}
.main-content div.new-game-setup ، .main-content div.join-game-setup {
    شبكة العمود: 1 / span 2 ؛
}
# زر المتابعة
    شبكة العمود: 1 / span 2 ؛
    أعلى هامش: 20 بكسل ؛
}

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

2. إنشاء وربط العقد الذكي الأولي

لقد حان الوقت لإنشاء إصدار أساسي من العقد الذكي وتوصيله بجافا سكريبت الخاص بك باستخدام web3.js. الآن نحن بحاجة فقط إلى المنشئ وبعض المعلومات الأساسية. اكتب هذا الكود بيديك في ملف جديد يسمى Dice.sol:

براغما صلابة 0.4.25.
عقد النرد
    عنوان player1 العامة ؛
    معالجة player2 العامة ؛
    uint256 لاعب عام 1Escrow ؛
    uint256 لاعب عام 2Escrow ؛
    مُنشئ () مستحق الدفع العام
        يتطلب (msg.value> 0) ؛
        player1 = msg.sender؛
        player1Escrow = msg.value؛
    }
    وظيفة setupPlayer2 () مستحقة الدفع العامة {
        يتطلب (msg.value> 0) ؛
        player2 = msg.sender؛
        player2Escrow = msg.value؛
    }
}

هناك وظيفتان ، المنشئ لإعداد عنوان والضمان للاعب الأول ووظيفة setupPlayer2 () لإعداد معلومات اللاعب الثاني.

نريد نشر العقد وتنفيذ المنشئ باستخدام msg.value المحدد مباشرة عندما ينقر المستخدم على زر "Continue". للقيام بذلك ، يتعين علينا تنفيذ web3.js في عقدنا الذكي. لأنه الطريقة الرئيسية للتواصل مع blockchain على المتصفح.

احصل على web3.js في مجلد التطبيق من هنا: https://github.com/ethereum/web3.js/blob/develop/dist/web3.js وهو رمز التوزيع الرسمي المحدّث.

لتنزيله لمشروعك ، انتقل إلى هذا الرابط ، وانقر فوق raw للاطلاع على الرمز الكامل وانسخ الرمز للصقه في ملف جديد باسم web3.js داخل مجلد المشروع:

افتح الصفحة ، وانقر فوق

لست مضطرًا إلى فعل ذلك حقًا إذا كنت تستخدم metamask نظرًا لأن metamask تحقن نسخة من web3.js نيابة عنك ، ولكن من المفيد أن تكون مكتبة الويب 3 في مشروعك تتفاعل مع blockchain في حالة عدم توفر metamask.

نحن نستخدم metamask للتحدث إلى blockchain. ومع ذلك ، فإنه لا يعمل عند فتح ملف index.html على المستعرض الخاص بك لأن الملف: // extension غير معتمد للاسم الفلكي.

نحتاج بعد ذلك إلى تشغيل خادم محلي يخدم الملفات على http: // localhost: 8080 url حيث لا تعمل metamask عندما تفتح ملف index.html مباشرة. للقيام بذلك ، افتح الجهاز وقم بتثبيت هذا:

npm i -g http-server

بعد ذلك ، في مجلد المشروع الخاص بك ، قم بتنفيذ http-server لبدء خادم محلي لـ index.html:

HTTP خادم

سيخدم ذلك الملفات على المضيف المحلي: 8080 بحيث يمكنك الوصول إليها وحقن web3 من metamask.

مع الخروج عن هذا الطريق ، يتيح التركيز على نشر العقد الذي أنشأناه للتو من تطبيق الويب الخاص بنا ، مباشرة عندما ينقر المستخدم على "متابعة".

لنشر عقد جديد ، نحتاج إلى ABI ومعلمات المنشئ والرمز الثانوي. هذه هي متطلبات web3.js.

  1. لإنشاء ABI ، انتقل إلى remix.ethereum.org ، الصق الرمز في القسم الرئيسي وانقر على ABI:

سيؤدي ذلك إلى نسخ رمز ABI. انتقل إلى مجلد المشروع الخاص بك وإنشاء ملف يسمى contractData.js للصق رمز هناك مع متغير يسمى أبي مثل ذلك:

2. الآن نحن بحاجة إلى الرمز البريدي للعقد الذكي الخاص بك. إن bytecode هو العقد الذكي المترجم الذي سيتم نشره في blockchain ، نحتاج إلى تلك المعلومات لتتمكن من نشرها. للحصول على إعادة رمز البايت كود مرة أخرى والنقر فوق هذا الزر:

زر نسخ bytecode للرمز الخاص بك

وإنشاء متغير آخر داخل contractData.js يسمى bytecode مع تلك المعلومات مثل:

يمكنك نسخ نفس الرمز إذا كان عقدك الذكي مثل العقد الذي أنشأته أعلاه تمامًا.

قم باستيراد ملف جافا سكريبت هذا في html الخاص بك قبل ملف index.js لوجود متغيرات abi و bytecode المتاحة:

قبل إنشاء العقد على javascript ، نحتاج إلى إضافة مستمع حدث إلى زر المتابعة في قسم "بدء لعبة جديدة":

ما فعلته هناك هو:

  • أضفت معرّفات هوية إلى المدخلات حيث يُسأل المستخدم عن مقدار الأثير الذي يريد وضعه في الضمان وعنوان العقد إذا كان ينضم إلى لعبة حالية.
  • ثم أضفت ملف javascript import أعلى index.js لأننا نريد أن يتوفر أبي والرمز الداخلي داخل index.js لأنه يجب استيراده أولاً.

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

إذا لم تكن فارغة ، فسوف نفترض أن اللاعب يبدأ لعبة جديدة والتي تتيح لك إمكانية بدء لعبة باستخدام زر الانضمام إذا تركت العنوان فارغًا.

قبل أن أعرض عليك الشفرة بأكملها ، أريد أن أوضح لك كيفية نشر عقد باستخدام web3.js. يبدو الأمر بسيطًا لكني علقت في بعض المناطق.

لذلك عندما ينقر المستخدم على "بدء لعبة جديدة" ، يعطينا مبلغ الضمان في الأثير وعنوانه ، يمكننا نشر عقد جديد مع هذه الوظيفة:

في الأساس تقوم بإنشاء مثيل العقد مع abi وتقوم بتنفيذ الطريقة .new () لهذا العقد مع الرمز الثانوي.

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

مما يعني أنه سيتم تنفيذ رد الاتصال هذا مرتين. واحد عند تنفيذ إنشاء العقد والآخر عندما يكون عنوان هذا العقد متاحًا.

يمكنك التحقق من توفر عنوان العقد مع بيان بسيط إذا:

إذا (! result.address) {
    // بدأ إنشاء العقد
} آخر {
    // تم نشر العقد ويمكنك استخدام العنوان مع result.address
}

هذه هي الطريقة التي تنشر بها عقدًا مع web3.

ولكن ماذا لو كنت ترغب في الوصول إلى عقد قائم على blockchain؟

هذا هو بالضبط ما نحتاج إليه "للانضمام" إلى لعبة النرد لإنشاء مثيل للعقد. لهذا الغرض ، نحن بحاجة فقط إلى ABI وعنوان العقد ، الرمز البريدي ليس ضروريًا. إليك كيفية القيام بذلك في web3:

العقد = web3.eth.contract (abi)
contractInstance = Contract.at (addressSelected)

بعد ذلك يمكنك تنفيذ وظائف هذا العقد مثل:

contractInstance.setupPlayer2 ({
  القيمة: web3.toWei (valueSelected) ،
  الغاز: 4e6
} ، (يخطئ ، نتيجة) => {
    / / قم بعمل شيء ما بعد تنفيذ الوظيفة
})

تحتاج فقط إلى المثيل واسم الوظيفة والمعلمات إن وجدت ووظيفة رد الاتصال.

الآن بعد أن أدركت كيف يعمل نشر وبدء تشغيل العقد الذكي على javascript ، سأعرض لك الشفرة الكاملة للتطبيق:

تجاهل كل ما ذكر أعلاه ، ما عليك التركيز عليه هو في كتلة مستمع "# button-continue":

document.querySelector ( '#-الاستمرار على زر'). addEventListener ()

لأنك لا تهتم إلا بما يحدث عندما ينقر اللاعب 1 أو اللاعب 2 على الزر "متابعة". إليك التفاصيل:

  • عندما ينقر أي لاعب على هذا الزر ، يتم تنفيذ مستمع الأحداث هذا
  • في الداخل ، أحصل على قيم المدخلات لإعداد الضمان وعنوان العقد المنشور إذا كان اللاعب ينضم إلى لعبة موجودة. تلك هي المتغيرات valueSelected و addressSelected.
  • ثم أقوم بإنشاء متغير إعداد العقد مع أبي والتي ستكون ضرورية لكلا اللاعبين.
  • بعد ذلك ، أرى ما إذا كان عنوان العقد المنشور قد تم تعيينه أم لا. إذا كان العنوان فارغًا ، فهذا يعني أن اللاعب قد نقر على "بدء لعبة جديدة" لأنه في هذه الحالة لن يرى إدخال العنوان.
  • مما يعني أنني أنشر لعبة جديدة أو عقد ذكي لذلك اللاعب مع تحديد الضمان.
  • سيشاهد اللاعب الأول عنوان العقد الذكي الذي تم نشره. سيتعين عليه مشاركة هذا العنوان مع اللاعب الآخر لبدء لعبة النرد لأنك تحتاج إلى لاعبين.
  • إذا قدم عنوانًا ، فهذا يعني أنه يريد الانضمام إلى لعبة حالية. يمكننا القيام بذلك عن طريق إنشاء مثيل للعقد الذكي باستخدام عنوانه ثم تنفيذ الدالة setupPlayer2 ().
  • أنا أستخدم وظيفة setInterval للتحقق من كل ثانية واحدة إذا كان إعداد اللاعب 2 قد اكتمل أم لا لبدء اللعبة.

عظيم! إذا قمت بذلك حتى الآن فهذا يعني أنك ملتزم وأنك تتعلم شيئًا ما بالفعل. أفضل جزء هو أقرب مما تعتقد. في المقالة التالية ، سترى كيفية إنشاء قنوات حالة للعبتك في javascript لإنشاء تطبيق Ethereum لامركزي قابل للتطوير.

لا تفوتها وستكون أول من يقرأها عند اكتماله. انضم إلى قائمتي البريدية الحصرية لمطوري Ethereum لتلقي التحديثات والمعلومات مباشرةً مني هنا: http://eepurl.com/dDQ2yX

الجزء 2 متوفر الآن هنا: https://medium.com/@merunasgrincalaitis/how-to-create-scalable-dapps-and-smart-contracts-in-ethereum-with-state-channels-step-by-step- 690f71a9bf2f

إذا كنت تشعر بالإرهاق من مثل هذه المعلومات المتقدمة أو كنت جديدًا فقط على صلابة و Ethereum dapps ، راجع كتابي "Ethereum Developer: Learn Solidity From Scratch" هنا https://merunas.io/