تمويل ساقَي الصفقة

التمويل هو عملية 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 غير المفحوص. فرموز 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. يقرأ البرنامج رصيد الضمان على السلسلة عند التسوية، لذا فإن إرسال Settle مقابل تحويل تمويل لم يصل يفشل بالخطأ LegNotFunded ولا يتحرك شيء؛ وتكلفة ذلك هي فشل Settle وإعادة المحاولة. اعتبر الساق ممولة عندما يصبح تحويلها finalized.

ملاحظات

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

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

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

Is this page helpful?

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

تعديل الصفحة