For the complete documentation index, see llms.txt. This page is also available as Markdown.

البناء على Bloom

هذا الدليل يجيب عن سؤال واحد: كيف يفعل المُنشئ ذلك بنفسه؟ خذ عقدًا حقيقيًا لأصلٍ من نوع Solidity، وانشره على سلسلة Rome كرمزٍ مقيَّد الصلاحيات، وافتحه لمحافظ Solana وكذلك محافظ EVM من دون جسر ومن دون رمز ثانٍ، ووصل قرارك الخاص بالتحقق من الهوية/الامتثال.

الجمهور هو مطوّر يقيّم هذا النهج أو يتبناه. بلوم هو المثال العملي طوال الدليل — وأصلها المباشر ARCV ("Mineral Vault I") على Hadrian (200010) هي حالة فعلية لكل خطوة أدناه، ووصل النشر الخاص بها (deployments/200010.tokens/ARCV.json) يسجل العقود الثلاثة الدقيقة والثماني معاملات التي تنتجها هذه الطريقة.

لا شيء هنا يغيّر عقود الأصل. بلوم تشغّل إطار Arc الخاص بـ Plume من دون تعديل (الشجرة المضمّنة في contracts/، المطابقة تمامًا بالبايت لـ plumenetwork/contracts عند التثبيت في NOTICE)؛ العقد الوحيد على جانب Rome هو ArcTokenFactoryV2، وهو يضيف دالة واحدة. العمل هو في كيفية نشر العقود و كيفية تشغيلها — وليس في تغييرها.

الطريقة باختصار. الرمز المقيَّد الصلاحيات هو ثلاثة عقود منشورة. ويتم نشره على أجزاء — عقد واحد لكل معاملة — لأن استدعاء المصنع في معاملة واحدة يتجاوز الحد الأقصى لحسابات Rome لكل معاملة. ويجب أن تأتي وكلاؤه من أصول مقفلة بحسب المصدر أو يفشل التسجيل على السلسلة بشكل مغلق. الامتثال هو قيمة منطقية واحدة لكل عنوان، يكتبها من يخولهم مسار KYC الخاص بك. ويمكن الوصول إلى النشر نفسه من محفظة Solana عبر مرسل اصطناعي مشتق على السلسلة — من دون جسر، ومن دون رمز مغلف، ومن دون قائمة سماح ثانية.

المتطلبات المسبقة

كل أدوات العمل المرجعية موجودة في scripts/ (شغّل npm install هناك أولًا). كل أمر يستخرج حقائق السلسلة من السجل بدلًا من تثبيتها داخل الكود، لذا فإن إعادة التوجيه إلى سلسلة Rome أخرى هي تغيير في البيئة، لا في الكود:

export PRIVATE_KEY=# مفتاح النشر/المُصدِر المموَّل على السلسلة الهدف
export CHAIN_ID=200010     # يحدد السلسلة
export REGISTRY_ROOT=# نسخة checkout من سجل سلسلة Rome
# تحتاج تدفقات مسار Solana أيضًا إلى:
export SOLANA_KEYPAIR=# المسار إلى ملف JSON لمفتاح Solana زوجي ممول (دافع الرسوم)

الغاز على سلاسل Rome مسعّر بعملة USDC؛ وتكلف سلسلة النشر الكاملة في حدود 10–15 وحدة أصلية على شبكة devnet. تأتي المفاتيح من البيئة فقط — لا من المستودع أبدًا. ابنِ العقود مرة واحدة قبل النشر، لأن الأصول يجب أن تأتي من rome-contracts/out (انظر الخطوة 3):

cd contracts       && forge build --via-ir     # مجموعة Arc المضمّنة
cd rome-contracts  && forge build --via-ir     # ArcTokenFactoryV2 + الوكيل القياسي

الخطوة 1 — اعرف ما هو الرمز المقيَّد الصلاحيات هو هنا

رمز Bloom المقيَّد الصلاحيات ليس عقدًا واحدًا. إنه ثلاثة عقود منشورة، إضافةً إلى أربعة عقود مشتركة ينشرها التشغيل مرة واحدة لكل سلسلة ويعيد كل مُصدر استخدامها.

العقود الثلاثة الخاصة بكل رمز — يملك كل أصل بالضبط هذه العقود:

العقد
المصدر
ما هو

ArcTokenProxy

contracts/src/proxy/ArcTokenProxy.sol

الرمز نفسه — وكيل UUPS يشير إلى ArcToken التنفيذ المشترك. الأرصدة، والحاملون، والأدوار كلها هنا.

WhitelistRestrictions

contracts/src/restrictions/WhitelistRestrictions.sol

وحدة قائمة السماح. قيمة منطقية واحدة لكل عنوان؛ تُفرض على كل تحويل أثناء تقييد الأصل.

YieldBlacklistRestrictions

contracts/src/restrictions/YieldBlacklistRestrictions.sol

وحدة العائد. تستبعد عناوين من توزيعات العائد من دون المساس بحيازتها.

العقود الأربعة المشتركة، عقد واحد لكل سلسلة (ينشرها deploy-infra.ts):

العقد
الدور

RestrictionsRouter

سجل أنواع الوحدات — توصيل السلسلة، يُضبط مرة واحدة بواسطة التشغيل.

ArcTokenFactoryV2

التسجيل + تجزئة الكود القياسية للوكيل (الخطوتان 2–3).

ArcToken (التنفيذ)

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

ArcTokenPurchase

واجهة متجر واحدة، مشتركة بين كل رمز وكل مُصدر (الخطوة 4).

wUSDC

جانب النقد — عملة العائد وعملة البيع والشراء.

تقييد التحويل وتقييد العائد هما وحدتان منفصلتان عمدًا: يمكن منع حامل من الدخل مع الاحتفاظ بالأصل، وهذا هو بالضبط ما تنفذه العقوبات وأوامر المحكمة في الواقع.

حقيقة "ثلاثة عقود" هذه أساسية للتحقق. verify-deployments.ts و register-asset.ts يتوقعان كلاهما أن يُحلَّ رمز إلى ثلاثة عقود منشورة — وقد أدى التحقق من رمز يدويًا مرة واحدة إلى تفويت سبعة منها عبر سلسلة كاملة (كل وحدة عائد وثلاث وحدات قائمة سماح).

تمر دورة الحياة بثلاث مراحل وبابٍ أحادي الاتجاه:

يكون التقييد أحادي الاتجاه بالنسبة إلى المعروض لأن الصك والحرق يمران عبر address(0) كطرفٍ مقابل، و address(0) لا يمكن أبدًا إدراجه في قائمة السماح — لذا فبمجرد تفعيل التقييد، لا يمكن أبدًا صك أي معروض جديد. هذا هو الضمان الذي يقدمه الأصل المقيَّد للصلاحيات لحامليه.


الخطوة 2 — انشره على أجزاء، لا كمعاملة واحدة أبدًا

تقدم Arc عملية createToken ذات خطوة واحدة، تنشر الوكيل وكلا الوحدتين وتوصلهما باستدعاء واحد. على Rome لا يمكنك استخدامه. تحتاج تلك المعاملة إلى 77 قفل حسابات Solana، وحد Rome لكل معاملة هو 62 قفلًا. لا يمكن أن تتسع أبدًا — لا بالضبط، ولا مع أي تمرير مُحسِّن.

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

معالج الإنشاء

التنفيذ المرجعي هو arc/plan/create.ts (createSequence())، والذي تعرضه الواجهة على شكل ست بطاقات (bloom/lib/createCards.ts) عبر ثماني معاملات، بالترتيب الدقيق الذي تفرضه العقود:

#
المعاملة
استدعاء العقد

1

نشر الأصل

new ArcTokenProxy(impl, initData) حيث initData = ArcToken.initialize(...)

2

نشر وحدة قائمة السماح

new WhitelistRestrictions()

3

تعيينك كمدير لها

whitelist.initialize(issuer)

4

فرضها على التحويلات

token.setRestrictionModule(TRANSFER, whitelist)

5

نشر وحدة العائد

new YieldBlacklistRestrictions()

6

تعيينك كمدير لها

yieldBlacklist.initialize(issuer)

7

فرضها على العائد

token.setRestrictionModule(YIELD, yieldBlacklist)

8

التسجيل مع المصنع

factoryV2.registerToken(token, impl)

شكل السكربت هو أمر واحد:

المُصدر (PRIVATE_KEY) يوقّع كل معاملة وينتهي به الأمر وهو يحمل أدوار الرمز (ArcToken.initialize تمنح إلى msg.sender — انظر الخطوة 7). التسجيل (المعاملة 8) هو ما يفتح أمام الواجهة الأمامية enableToken وأي ترقية تمر عبر المصنع.

لا "تحسّن" هذا بإعادته إلى تصميم أحادي. تجميع هذه الاستدعاءات في معاملة واحدة لتوفير عمليات الذهاب والإياب يعيد بالضبط تجاوز حد الـ 62 قفلًا الذي وُجد المسار على أجزاء لتجنبه. والحد نفسه هو السبب في أن توزيع العائد يسير حاملًا واحدًا لكل معاملة (الخطوة 8). إذا فشل استدعاء مع Too many accounts: N > 62، فهذا يعني أن معاملة واحدة تلمس عددًا كبيرًا جدًا من الحسابات — قسّمها، لا تحاول ضبطها.


الخطوة 3 — انشر الوكلاء فقط من الأصول المقفلة بحسب المصدر

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

ولأن تلك التجزئة هي ثابتة غير قابلة للتغيير، فإن أي وكيل مبني من أصل آخر يفشل بشكل مغلق مع ProxyCodehashUnknown. ويترتب على ذلك قاعدتان، وكلاهما يُطبَّق عبر CI والقواعد الصارمة في CLAUDE.md:

  1. يجب نشر وكلاء الرموز من rome-contracts/out — لا أبدًا من contracts/out، ولا من بناء مختلف. هذا هو التجميع الذي تثبته تجزئة الكود القياسية للمصنع.

  2. bytecode_hash = "none" و cbor_metadata = false في rome-contracts/foundry.toml هي عناصر أساسية. مع تفعيل البيانات الوصفية، فإن ArcTokenProxy وقت التشغيل المضمَّن في المصنع (type().runtimeCode) يحمل ذيل CBOR/IPFS مختلفًا عن الأصل المستقل out/ الذي ينشر منه المعالج — لذا فإن registerToken الرفض على السلسلة يحدث بينما كل اختبارات Foundry المصدريّة تمر (يضمِّن Foundry النسختين معًا، لذلك لا يستطيع رؤية عدم التطابق). rome-contracts/test/ArtifactProvenance.t.sol يثبت هذه الخاصية؛ وكان التشغيل الممول هو ما كشفها أصلًا.

مجموعة اختبارات خضراء داخل المصدر لا تثبت المصدرية. قبل شحن صورة، وبعد أي تغيير في إعدادات المترجم، أثبت الخاصية مقابل المُنشَر المصنع (للقراءة فقط، من دون مفاتيح):

إنه يَحسب تجزئة ArcTokenProxy الأصل المحلي لديك ويؤكد أن هذه التجزئة تظهر حرفيًا داخل بايت كود المصنع المنشور.

إذا كان لا بد من تغيير إعدادات المترجم، فأدرِ المصنع — انشر واحدًا جديدًا يثبت فيه الثابت التجزئة الجديدة، وأعِد التوصيل — بدلًا من ترقيع الفحص يدويًا:

الاستدارة تحتفظ بالعنوان القديم في الإيصال؛ ويجب إعادة تسجيل الرموز المسجَّلة ضد المصنع القديم لدى المصنع الجديد.


الخطوة 4 — افتح بيعًا عبر الواجهة الأمامية المشتركة

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

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

  1. ضع الواجهة الأمامية في قائمة السماح على وحدة قائمة السماح الخاصة برمزك (batchAddToWhitelist([storefront]) — انظر الخطوة 5). من دون هذا، يفشل تحويل المخزون في (2) مع TransferRestricted() بمجرد تفعيل التقييد.

  2. نقل المخزون إلى عنوان الواجهة الأمامية (من إيصال البنية التحتية في arcTokenPurchase).

  3. enableToken(token, amount, price) — يدفع المشترون الآن wUSDC ويتلقون RWA.

يستحق تقسيم السحب أن يُعرف على واجهة أمامية مشتركة ، وهو غير متماثل (arc/roles.ts هو المرجعية):

الإجراء
السلطة
يحمله

enableToken / disableToken / withdrawUnsoldArcTokens

الرمز الخاص بـ ADMIN_ROLE (onlyTokenAdmin)

أنت، المُصدر

withdrawPurchaseTokens (عائدات wUSDC)

الخاص بالواجهة الأمامية DEFAULT_ADMIN_ROLE

من نشر الواجهة الأمامية

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


الخطوة 5 — وصل KYC / الامتثال الخاص بك

هذا هو القسم الذي يقرأه العميل قبل أن يوافق، لذا فهو كود لا نثر. يُفرض الامتثال بواسطة عقد الرمز نفسه — لا بواسطة فحص خارج السلسلة، ولا مرشح للمُرتِّب، ولا سياسة للمنصة. تنتقل القواعد مع الرمز إلى أي مسار تنفيذ، على كلا مساري المحافظ. ما تسجله السلسلة هو النتيجة لقرار KYC الخاص بك، على شكل قيمة منطقية واحدة:

الواجهة التي يكتب إليها مسارك

قرار القبول/الرفض من مزود KYC الخاص بك يصبح بالضبط أحد هذه الاستدعاءات:

من يمكنه الكتابة

السلطة التي يحتاجها أي قبول هي MANAGER_ROLE على الوحدة — لا ADMIN_ROLE على الرمز. addToWhitelist / batchAddToWhitelist / removeFromWhitelist كلها onlyRole(MANAGER_ROLE)، وهذا الدور يعيش على WhitelistRestrictions النسخة. المصادقة ADMIN_ROLE الكتابة على الرمز المميز ثم الكتابة إلى الوحدة عيب حقيقي دفع هذا المستودع ثمنه بالفعل. لا تقترح WHITELIST_ADMIN_ROLE كإصلاح لرفض — فهي تُمنح عند initialize و لا يتم التحقق منها في أي مكان في مجموعة الاختبارات (arc/roles.ts يؤكد أنها هي الرتبة الوحيدة التي لا تضبط أي شيء).

WhitelistRestrictions.initialize(issuer) (المعاملة 3 من المعالج) تمنح المُصدِر DEFAULT_ADMIN_ROLE, ADMIN_ROLE, MANAGER_ROLE, WHITELIST_ADMIN_ROLE، و UPGRADER_ROLE على تلك الوحدة. لذا فإن المُصدِر يملك MANAGER_ROLE مباشرةً من الصندوق ويمكنه أيضًا تفويضها: لأن المُصدِر يملك DEFAULT_ADMIN_ROLE، grantRole(MANAGER_ROLE, serviceAccount) يسلّم الكتابة إلى موقّع خلفي.

تحوّل القرار إلى كتابة على السلسلة

وجّه webhook الخاص بمورّدك إلى موقّع يملك MANAGER_ROLE، وعند الموافقة، اجمع العناوين دفعةً واحدة:

استخرج whitelistModule من الرمز المميز نفسه (ArcToken.getRestrictionModule(TRANSFER_RESTRICTION_TYPE))، لا أبدًا من إسقاط مُخزَّن — فإرسال كتابة إلى الوحدة الخطأ هو كيف انتهى "الموافقة على أي أصل" ذات مرة إلى الكتابة في قائمة السماح الخاصة برمز مميز واحد.

النسخة الموقّعة بالمحفظة (عرض ذاتي)

لمنشور تجريبي بلا صلاحيات حيث يُدخل الزوار أنفسهم، امنح MANAGER_ROLE إلى TestApprover (rome-contracts/src/TestApprover.sol) الذي الوحدة هو ثابتة غير قابلة للتغيير. ثم approve(address) يمكن لأي شخص استدعاؤها من محفظته الخاصة، وهو يدفع الغاز من محفظته هو — فهي تستدعي addToWhitelist فقط إذا لم يكن العنوان مُدرَجًا بالفعل:

هذه هي "بوابة تسجيل الدخول" — فهي تعرض للزائر الآلية الحقيقية (موافقة تتحول إلى إدخال في قائمة سماح على السلسلة يفرضه الرمز المميز بعد ذلك) من دون أن تحتفظ بأي شيء. لا تمنح MANAGER_ROLE إلى TestApprover على رمز مميز إنتاجي — فهذا يجعل قائمة السماح الخاصة بذلك الرمز المميز بلا صلاحيات.

عندما يبدأ الضبط في العمل، ومع تبديل المورّدين

لا يقيّد الضبط إلا عندما يكون الأصل مُضبَطًا (setTransfersAllowed(false), ADMIN_ROLE على الوحدة). بينما هو غير مُضبَط، تكون التحويلات مفتوحة — ابنِ قائمة السماح أولًا، ثم فعِّل الضبط. وبمجرد تفعيله، فإن أي تحويل يكون فيه أحد الطرفين خارج القائمة سيُرفض بالخطأ المطبوع TransferRestricted() (0xe827105e).

لا يتطلب تبديل مزوّدي KYC لاحقًا أي تغيير على السلسلة. وحدة قائمة السماح، وواجهتها، والقيمة المنطقية التي تخزّنها لا تعتمد على المورّد. لتبديل المورّدين، وجّه مصدر قرار مختلفًا إلى نفس MANAGER_ROLE الموقّع — لا إعادة نشر، لا ترحيل، ولا تغيير في الحالة. لم تعرف السلسلة قط أي مورّد أصدر النداء، بل فقط النتيجة.


الخطوة 6 — افتح الأصل نفسه لمحافظ سولانا

يمكن الوصول إلى العقود الثلاثة نفسها من محفظة سولانا من دون جسر ولا رمز مميز ثانٍالمستخدم الأصلي على سولانا لا يملك مفتاح EVM في أي مكان؛ وبدلًا من ذلك تكون هوية EVM الخاصة به اصطناعية، مشتقة على السلسلة من موقّع سولانا الفعلي في المعاملة:

يستنتج برنامج Rome EVM هذا من الموقّع نفسه (do_tx_unsigned::derive_sender), لذلك لا يمكن انتحالها ولا يوجد لها مفتاح خاص secp256k1 — توقيع سولانا هو الشيء الوحيد الذي يمكنه قيادة هذا العنوان. الاشتقاق المرجعي هو arc/materialise/solana/identity.ts (syntheticAddress)، متوافق بايت-بايت مع القاعدة على السلسلة:

ما الذي يفعله البناء لدعم المسار — لاحظ أن لا شيء منه تغيير في عقد:

  1. أضف مستخدم سولانا إلى قائمة السماح عبر عنوانه الاصطناعي. بالنسبة إلى طبقة الامتثال، العنوان الاصطناعي هو عنوان عادي: الصق المفتاح العام لسولانا الخاص بالمستخدم، واستخرج العنوان الاصطناعي، و batchAddToWhitelist([synthetic])تغطي قائمة سماح واحدة كلا عالمي المحافظ.

  2. وفّر PDA الخاص بالتوقيع مرة واحدة، قبل أول معاملة له. يجب أن يوجد أولًا PDA لسلطة خارجية يصرّح بتوقيع العنوان الاصطناعي، وإنشاؤه هو استدعاء لمسار EVM (create_pda) — لا يستطيع مستخدم سولانا وحده فعل ذلك، لذا يقوم المُصدِر بتوفيره. التوفير هو external_auth، وليس كسولًا.

  3. أرسل عبر مكتبة المسار، لا يدويًا. submitDoTxUnsigned (arc/materialise/solana/submit.ts) يبني DoTxUnsigned تعليمة — غير موقّعة حمولة EIP-1559 مخوّلة بواسطة توقيع سولانا — ويتولى أربع مسائل يخطئ فيها إنشاء معاملة يدويًا:

    • اكتشاف الحسابات عبر rome_emulateCallAccounts، والذي يقرر قابلية كتابة كل حساب. يجب أن تُمرَّر قيمة النداء القيمة يجب إلى الاكتشاف، وإلا فإن تحويل القيمة يجعل PDA الخاص بميزان المستلم للقراءة فقط ويفشل على السلسلة.

    • ميزانية الحوسبةDoTxUnsigned يتطلب إطار heap بحجم 250 KB و حدًا 1.35 M CU (يستنفد Rome’s EVM الإعدادات الافتراضية 32 KB / 200 K)؛ مع ترك هامش يقارب 50 K تحت سقف سولانا البالغ 1.4 M.

    • PDA الخاص بمحفظة الرسوم (الكنز)، يُضاف قابلًا للكتابة — والاكتشاف يتجاهله.

    • الخطة البديلة v0 + جدول البحث. عندما تتجاوز حسابات النداء غلاف المعاملة القديمة البالغ 1232 بايت، يعيد العميل الإرسال كمعاملة v0 عبر جدول بحث عناوين جديد. (قياس اليوم يبيّن أن شراءً من متجر يناسب الغلاف القديم عند نحو 1,061 من 1232 بايت؛ وقد يتجاوزه حاملٌ جديد لم يُنشأ له بعد حساب الرمز المميز المرتبط.)

المستخدم هو دافع الرسوم ويدفع lamports ‏(SOL) من محفظة سولانا الخاصة به؛ أما النسخة الاصطناعية التي يتحكم بها فتحمل الأصول ولا تحمل SOL أبدًا. بعد كل إرسال في هذا المسار، استعلم عن nonce الخاص بـ EVM قبل الإرسال التالي — فالمفهرس يتأخر قليلًا عن تأكيد سولانا.

المسار لا يدعم إلا النمط الذرّي. أ DoTxUnsigned هي معاملة EVM واحدة تُنفَّذ داخل معاملة سولانا واحدة بواسطة VM الذرّي الخاص بـ Rome. الـ VM التكراري (تنفيذ EVM واحد على عدة معاملات سولانا) هو غير قابل للوصول عبر هذا المسار بعد. معاملة لن تتسع ذرّيًا لا يمكن أن تسلك هذا المسار أصلًا — الإصلاح هو استدعاء أصغر، لا بديل. لاحظ أن بديل v0+ALT هو غلاف غلافًاأكبر، وليس تنفيذًا مختلفًا؛ فهو لا يزال معاملة ذرية واحدة.

لماذا لا يمكن تسرب الأصل الحقيقي المرمّز خارج المحيط. الأصل مقيم في حالة EVM — أرصدته مخزنة داخل حسابات برنامج Rome EVM. لا يوجد سكّ SPL منه ولا شيء أصلي على سولانا يمكن تحريكه؛ فبدائيات نقل الأصول في هذا المسار تعمل على حسابات رموز SPL، وهو أمر لا يملكه رصيد مخزَّن في EVM. كل حركة يستطيع توقيع سولانا التعبير عنها هي استدعاء إلى عقد الرمز المميز، الذي يشغّل بوابة قائمة السماح — وتؤكد الحزمة المموّلة أن تحويلًا إلى عنوان غير موجود في القائمة يُرفض بالطريقة نفسها سواء وُقِّع بمفتاح EVM أو بمفتاح سولانا. وساق النقد (wUSDC) هي الاستثناء المقصود: فهي مدعومة بـ SPL و تقوم بالتحويل إلى حساب الرمز المميز الخاص بالمستخدم نفسه.


الخطوة 7 — اعرف من يملك السلطة بعد النشر

ArcToken.initialize يعمل من منشئ الوكيل (tx 1) ويمنح الأدوار إلى msg.sender — سائق المعالج، أي المُصدِر. وهو يمنح خمسة أدوارًا صراحةً:

تبعاتان لا يجب أن يغفل عنهما البنّاء:

  • MINTER_ROLE, BURNER_ROLE، و UPGRADER_ROLE لا تُمنح عند initialize. يُسكّ العرض الأولي داخل initialize (عملية داخلية _mint، لا حاجة لأي دور)، لكن لاحقًا سكّ أو حرق يتطلب من المُصدِر أن يمنح نفسه الدور أولًا. ويمكنه ذلك، لأنه يملك DEFAULT_ADMIN_ROLE (مدير كل دور — لا شيء يستدعي _setRoleAdmin، لذا DEFAULT_ADMIN_ROLE يديرها جميعًا). لكن ذلك خطوة إضافية مقصودة، لا قوةً محيطة.

  • UPGRADER_ROLE لا يُمنح أبدًا للمصنع. الترقيات عبر المصنع (ArcTokenFactoryV2.upgradeToken) تتطلب أن يحمل المصنع نفسه على الرمز المميز — منحه لنفسك لا يفعل شيئًا. تلك المنحة باب باتجاه واحد لا يستطيع المصنع التراجع عنه، لذا فهي اختيارية: إذا أردت ترقيات عبر المصنع، UPGRADER_ROLE grantRole(UPGRADER_ROLE, factory) grantRole(UPGRADER_ROLE, factory) صراحةً؛ وإلا فإن المُصدِر يرقّي الرمز مباشرةً.

نموذج السلطة الكامل المكوَّن من 21 سلطة (11 اسم دور عبر ستة عقود — فالاسم نفسه سلطة مختلفة على كل عقد) مُقَنَّن في arc/roles.tsفي rolesRequiredFor(contract, functionName) بدلًا من قراءة العقد الخاص بالدور نفسه، لأن أربع وظائف للمتجر وكلا وظيفتي المصنع محجوبة بواسطة سلطة تعيش على عقد .


الخطوة 8 — وزّع العائد

يدفع الرمز المميز العائد بعملة wUSDC (مثبتة عند initialize، قابلة للتغيير عبر setYieldToken تحت YIELD_MANAGER_ROLE). يمر توزيع العائد على مجموعة الحائزين:

الآليات المهمة لتنفيذه بشكل صحيح:

  • الكامل totalAmount يُسحب من المستدعي فقط عند startIndex == 0 النافذة (safeTransferFrom(msg.sender, this, totalAmount)). وافق على هذا الإجمالي للرمز المميز قبل النافذة الأولى؛ النوافذ اللاحقة تُدفع من الرصيد المحتفظ به بالفعل.

  • nextIndex يعود إلى 0 عندما يكتمل المرور. كرّر distributeYieldWithLimit(total, nextIndex, …) حتى nextIndex يعود 0.

  • تظل حصص الحائزين المقيدة بالعائد في عقد الرمز المميز — تُتخطّى، لا يُعاد توزيعها.

على Rome الحد العملي هو maxHolders = 1 في كل معاملة — وهو نفس حد 62 حسابًا من الخطوة 2 — لذا يُجرى المرور على العائد حائزًا واحدًا في كل معاملة (مقاسًا بنحو 318 K غاز لكل حائز). كل حائز في قائمة السماح، سواء EVM أو أصلي على سولانا، يحصل على نصيبه النسبي؛ والسكربت المرجعي هو measure-yield-batch.ts وتؤكد خطوة العائد في الاختبار المدعوم ذلك من البداية إلى النهاية.


الخطوة 9 — تحقّق مما نشرته

لا شيء يرسل المصادر تلقائيًا إلى أي مكان في Rome، لذا فإن التحقق خطوة تنفذها أنت. بعد أي نشر — للبنية التحتية أو لرمز مميز جديد — شغّل أداة التحقق عديمة المفاتيح، للقراءة فقط، والقابلة للتكرار (العقود التي تم التحقق منها مسبقًا تُتخطى، لذا فإن إعادة التشغيل بعد إصدار رمز مميز تتحقق فقط من الجديد):

أنه يتوقع ثلاثة العقود لكل رمز مميز (الوكيل + الوحدتان) إضافة إلى البنية التحتية، ويبلغك بما تعذر التحقق منه بدلًا من فشل النشر. القيام بهذا يدويًا مرة واحدة فوّت سبعة عقود.

تسجيل الأصل في سجل Rome يعامل التحقق كـ بوابة، لا كزخرفة (register-asset.ts): يكسب الرمز المميز apps/arc/<chain>.json سجلًا — يحمل standard: arc-permissioned-erc20 و transferRestriction ويسمي بوابة قائمة السماح الخاصة به — فقط عندما تكون عقوده الثلاثة كلها موثقة على Sourcify. يقرأ ذلك السجل الحقائق من السلسلة (فالرمز المنشور عبر المعالج لا يملك إيصالًا committed) ويكتب فقط نسخة سجل محلية؛ وتمرير طلب الدمج مهمة شخص.

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


الخطوة 10 — اعرف ما يبقى خارج السلسلة

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

  • علامة قائمة السماح هي قرارك خارج السلسلة. تحدث KYC/AML في عمليتك — مورّدوك، قواعدك. لا تسجل السلسلة ولا تفرض إلا النتيجة, isWhitelisted(addr).

  • حالة السلسلة الوحيدة هي قيمة منطقية واحدة لكل عنوان. لا بيانات تعريف شخصية، لا مستندات، لا ادعاءات هوية، لا إثباتات على السلسلة، ولا برهانًا تشفيريًا على مَن يملك العنوان. هذا هو نموذج Arc كما يُستخدم على Plume؛ فهو يستبدل التحويلات الأثقل لمعيار هوية على السلسلة (مثل ERC-3643) بآليات أخف وثقة يحتفظ بها المُصدِر.

  • الاسترداد سلطة للمُصدِر، لا مخرج طوارئ. المحفظة المفقودة — EVM أو سولانا — يُستردّ أمرها بإدراج عنوان بديل في القائمة البيضاء، وإذا لزم الأمر باستخدام السكّ/الحرق/الترقية ضمن عمليةك القانونية الخاصة (Arc لا يملك بدائية تحويل قسري مدمجة). لذا فإن حفظ المُصدِر لمفتاحه جزء من موقف الامتثال.

  • لا مُرحّل، ولا سطح حفظ. يحتفظ المستخدمون برسوم الغاز الخاصة بهم على كلا المسارين. خدمة دافع الرسوم ستعني الاحتفاظ بالمفاتيح — وهو سطح لا ينبغي لمنتج RWA إضافته.


مرجع: الأدوات

كل سكربت يعمل من scripts/ بعد npm install، مع PRIVATE_KEY, CHAIN_ID، و REGISTRY_ROOT مُصدَّران.

السكربت
ما الذي يفعله

deploy-infra.ts

مرة واحدة لكل سلسلة: العقود المشتركة الأربعة، موصولة بـ wUSDC.

create-token.ts

المعالج الجزئي — ثماني معاملات، رمز مميز واحد.

rotate-factory.ts

انشر مصنعًا جديدًا بعد تغيير في المترجم/الأصل؛ أعد التوصيل.

verify-artifact-provenance.ts

أثبت أن أصل الوكيل الخاص بك مقبول من المصنع المنشور (للقراءة فقط).

verify-deployments.ts

تحقّق من العقود الثلاثة لكل رمز مميز + البنية التحتية على Sourcify (للقراءة فقط).

register-asset.ts

تحقق من البوابة، ثم حضّر سجل الامتثال في السجل.

measure-yield-batch.ts

أعد قياس سقف العائد لكل معاملة على السلسلة.

smoke.ts

الحزمة المدعومة من البداية إلى النهاية عبر كلا المسارين.

sweep-synthetic-native.ts

استردّ الغاز الأصلي من حساب اصطناعي (نظافة الاختبار).

تقع خلفية أعمق إلى جانب هذا الدليل: docs/ARCHITECTURE.md (نموذج التطبيق متعدد الطبقات)، docs/LANES.md (المساران والحفظ)، docs/COMPLIANCE.md (نموذج الثقة)، و docs/USAGE.md (دليل تشغيل المشغّل).

آخر تحديث

هل كان هذا مفيدا؟