تمويل الساقين

التمويل عبارة عن TransferChecked عادي إلى حساب الضمان الخاص بالساق، لذا يمكن لأي محفظة أو نظام حفظ قادر على إرسال تحويل رمز أن يموّل ساقًا. الجزء الوحيد الذي يحتاج إلى عميل DvP هو قراءة سجل الصفقة قبل التمويل. لا يتحقق البرنامج من أرصدة الضمان إلا عند التسوية؛ ولا شيء يتحقق من التحويل عند وصوله، لذلك يقرأ كل طرف سجل الصفقة قبل التحويل.

تتابع هذه الصفحة من إنشاء صفقة؛ حيث يبقى العميل والموقّعون كما هم.

التحقق من الشروط

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

تؤكد verifySwapDvp في TypeScript وverify::decode_swap_dvp_account في Rust أن الحساب مملوك لبرنامج DvP، وأن حجمه 458 بايت بالضبط، وأنه يقع في العنوان الذي تشتقه حقوله نفسها (أما fetchSwapDvpChecked فتجري أول فحصين فقط). أما قارئتا fetchSwapDvp وdecodeSwapDvp العاديتان فلا تجريان أيًّا من هذه الفحوصات. وتقع على عاتق المستدعي مهمة فحص الأطراف والمبالغ والأوقات والوجهات بمقارنتها بالصفقة المتفق عليها.

import { unwrapOption } from "@solana/kit";
import { verifySwapDvp } from "./dvp";
const trade = await verifySwapDvp(client.rpc, swapDvp);
const t = trade.data;
const expectedAmountA = 1_000_000_000n;
const expectedAmountB = 998_500_000n;
const expectedEarliestSettlement: bigint | null = null; // as agreed
const expectedExpiry = 1_798_761_600n; // as agreed, unix seconds
if (
t.userA !== userA ||
t.userB !== userB ||
t.mintA !== mintA ||
t.mintB !== mintB ||
t.settlementAuthority !== settlementAuthority ||
t.tokenProgramA !== tokenProgramA ||
t.tokenProgramB !== tokenProgramB ||
t.amountA !== expectedAmountA ||
t.amountB !== expectedAmountB ||
unwrapOption(t.earliestSettlementTimestamp) !== expectedEarliestSettlement ||
t.expiryTimestamp !== expectedExpiry ||
t.userASettlementDestination !== userA ||
t.userBSettlementDestination !== userB
) {
throw new Error("Stored terms do not match the agreed trade; do not fund");
}
if (t.expiryTimestamp <= BigInt(Math.floor(Date.now() / 1000)) + 60n) {
throw new Error("Trade expires too soon to fund safely");
}

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

التحويل إلى حساب الضمان

موّل الساق بالمبلغ المتفق عليه بالضبط. يحصر البرنامج التسوية عند amount_x ويعيد أي فائض إلى طرف الساق، لذا لا يضيع الفائض، لكنه يضيف تحويلًا ثانيًا عند التسوية وقد يفشل مع بعض transfer hooks (انظر الملاحظات أدناه).

استخدم TransferChecked وليس Transfer غير المدقَّق. فالـ mints من نوع Token-2022 ذات الإضافات التي تعتمد على mint account، مثل Pausable، ترفض أي تحويل لا يوفّره، كما أن الـ mint الذي يحتوي على transfer hook يتطلب تحديد حسابات الـ hook وإلحاقها بالطريقة نفسها المتبعة في أي تحويل آخر لذلك الرمز.

يموّل طرف ساق الأصل، موقّعًا بصفة userASigner، الضمان A:

import {
findAssociatedTokenPda,
getTransferCheckedInstruction,
} from "@solana-program/token-2022";
const [userAAtaA] = await findAssociatedTokenPda({
mint: mintA,
owner: userA,
tokenProgram: tokenProgramA,
});
const [userBAtaB] = await findAssociatedTokenPda({
mint: mintB,
owner: userB,
tokenProgram: tokenProgramB,
});
const fundAssetLeg = getTransferCheckedInstruction({
source: userAAtaA,
mint: mintA,
destination: escrowA,
authority: userASigner,
amount: t.amountA,
decimals: 6,
}, { programAddress: tokenProgramA });
await client.sendTransaction([fundAssetLeg]);

يفعل طرف ساق النقد الشيء نفسه للضمان B باستخدام userBAtaB وmintB وuserBSigner وt.amountB و{ programAddress: tokenProgramB }.

قد يتم استبدال تحويل التمويل الذي يظهر بحالة confirmed لاحقًا. يقرأ البرنامج رصيد الضمان على السلسلة (onchain) عند التسوية، لذلك فإن إرسال Settle بناءً على تحويل تمويل لم يصل يفشل برمز LegNotFunded دون أن يتحرك أي شيء؛ وتكلفة ذلك هي Settle فاشلة وإعادة المحاولة. اعتبر الساق ممولة عندما يصبح تحويلها finalized.

ملاحظات

  • سيقان SOL المغلّفة. يقرأ البرنامج رصيد الضمان الأصلي بعد مزامنته، لذلك تُحتسب lamports المرسلة إلى ضمان wrapped-SOL عبر تحويل النظام. لا ترسل SOL أبدًا إلى ضمان خاص بأي mint آخر: لا يستطيع البرنامج إعادتها، وتذهب إلى من يغلق الحساب.
  • الـ mints المقيَّدة. في الـ mint الذي حالته الافتراضية للحساب مجمّدة (Default Account State frozen)، يُنشأ الضمان مجمّدًا ويفشل التحويل إلى أن يلغي المُصدر التجميد أو يقبله. يصف العرض العام التفاعل مع Token ACL.
  • Transfer hooks. موّل حسابات الضمان التي تحمل hook بالمبلغ المتفق عليه بالضبط. إذا أدى فائض إلى استرداد عند التسوية، فإن الاسترداد يعيد استخدام حسابات الـ hook الخاصة بالساق مع وجهة ومبلغ مختلفين، وهو ما ترفضه بعض الـ hooks. والحل هو Reclaim ثم إعادة التمويل بالمبلغ الدقيق ثم التسوية مرة أخرى.
  • التمويل بعد انتهاء الصلاحية. لا شيء يمنع التحويل بعد expiry_timestamp. فهو يصل، لكن التسوية لم تعد ممكنة، ويستردّه طرف الساق باستخدام Reclaim ما دامت الصفقة مفتوحة أو Recover بعد إغلاقها. انظر فك صفقة.
  • من يموّل أولًا. لا يرتّب البرنامج الساقين. الترتيب شأن يخص الطرفين؛ ويمكن لأي جانب استرداد ساقه في أي وقت قبل التسوية، ما لم تمنع سلطة الـ mint (التجميد أو الإيقاف المؤقت أو transfer hook) حساب الضمان.

الخطوات التالية

  • تسوية الصفقة: تبادل سلطة التسوية الساقين معًا في معاملة واحدة
  • فك صفقة: استرداد ساق ممولة عندما لا تتم تسوية الصفقة

Is this page helpful?

جدول المحتويات

تعديل الصفحة