

Escrow Smart Contract
Mijoz va pudratchi pulni smart-shartnomaga kiritadi. Ish bajarilib, ikkala tomon rozi boʻlganda toʻlov pudratchiga oʻtadi.
Bu qanday ishlaydi
Shartnomagacha besh qadam
Oddiy ustoz (wizard) yordamida smart-shartnoma yarating.
Oddiy ustoz (wizard) yordamida smart-shartnoma yarating.
- 1-qadamShartnomadagi rolShartnomadagi rolingizni tanlang. Shartnomada faqat ikki rol bor — mijoz va pudratchi. Uchinchi tomon hakamligi yoʻq.9:41LTE
Shartnomadagi rol
- 2-qadamValyutani tanlashShartnoma valyutasini tanlang. GRAM — TON ning mahalliy valyutasi — tez va kichik bitimlar uchun, chunki u ancha oʻzgaruvchan. USDT — katta va uzoq bitimlar uchun barqarorroq valyuta.9:41LTE
Valyutani tanlash
- 3-qadamShartnoma summasiTanlangan valyutada shartnoma summasini kiriting. Bu summa ishlar bajarilgach pudratchiga oʻtkaziladi.9:41LTE
Shartnoma summasi
Shartnoma summasi — ishning narxi. Uni mijoz kiritadi.
10% dan 90% gacha. Standart: 30%.
Summaning 0,4% qismi, lekin 15 GRAM dan oshmaydi.
- 4-qadamHamyon manzillariHamyon manzillarini qoʻying. Keyinchalik valyutani kiritish va yechib olish shu hamyonlar orqali amalga oshiriladi.9:41LTE
Hamyon manzillari
- 5-qadamShartnomani tekshirishDeploydan oldin kiritilgan shartnoma maʼlumotlarini tekshiring.9:41LTE
Shartnomani tekshirish
Shartnomadagi rol
Yaratilgandan keyin
Shartnoma kartasi
Siz — mijoz, valyuta USDT. Haqiqiy kabinetdagi xuddi shu kartaning oltita surati. Har bir qadamdagi barcha mumkin boʻlgan amallar bilan.
Hozirgina yaratildi
Balanslar nolga teng. Bitim summasini, darhol 30% gacha qoʻshimcha bilan summani kiritish yoki hech kim hech narsa qoʻymagan ekan, shartnomani oʻchirish mumkin.
Summa kiritildi, kutyapmiz
Bitim summasi allaqachon shartnomada, pudratchi depoziti hali yoʻq. Holat «Faol» boʻlmaguncha pulingizni yechib olishingiz mumkin. 30% gacha qoʻshimchani hali qoʻshib kiritish mumkin.
Pudratchi toʻldirdi
Kelishilgan ikkala summa ham joyida — shartnoma faol. Summalar muzlatilgan, USDT yechib boʻlmaydi. Ishni qabul qilish, boshqa ulush taklif qilish yoki qoʻshimcha depozit (mijozning qoʻshimcha garovi) kiritish mumkin.
20% depozit qoʻshildi
Bitim summasidan tashqari +200 USDT bor. 30% chegarasigacha yana 100 USDT. Yechib olish hamon yopiq: faol shartnoma shartnoma summasini va qoʻshimchani ushlab turadi.
Pudratchiga 80% taklif qilindi.
Pudratchi rozi boʻlsa, unga 800 USDT oʻtadi. «Ishni qabul qilish» tugmasi yoʻqoldi. U faqat 100% belgilansa yana paydo boʻladi. Taklifni almashtirish, qoʻshimchani esa toʻldirib borish mumkin.
Pudratchi rozi boʻldi
Shartnoma yakunlandi: 800 USDT pudratchiga, 4 USDT servis royaltisi sifatida oʻtdi. Sizda 400 USDT qoldi, ularni yechib olish mumkin.
Q&A
Koʻp beriladigan savollar
Uchinchi tomon qayerda?
U yoʻq. Pudratchi depozitini xavf ostiga qoʻyadi. Ishni oxirigacha yetkazish unga foydali.
Pudratchi depoziti standart boʻyicha bitim summasining 30%. Unda ustunlik bordek tuyulishi mumkin: u 30% ni, siz esa 100% ni xavf ostiga qoʻyasiz. Ammo muhimi foizlar emas, pul. Uning 30% i — uning oʻz puli, va uni yoʻqotish unga siz uchun 100% ingizni yoʻqotish kabi ogʻriqli boʻlishi mumkin. Hech kim oʻz pulini bekordan-bekorga qurbon qilmaydi.
Boshqa xavf: mijoz ishni oldi va pulni bermasligi mumkin. Unda ikki yoʻl bor. Birinchisi — ish deyarli tayyor boʻlganda, oldindan hisob-kitob qilishni taklif qilish. Ikkinchisi — summadan tashqari 30% gacha qoʻshimcha (depozit) qoʻyishni soʻrash. Shartnoma yakunlangach, mijoz depozitini qaytarib oladi, shuning uchun uning bitimni (shartnomani) yopishga sababi bor.
Nega sizga ishonishim kerak?
Ishonish shart emas. Smart-shartnoma ochiq. Shartnomangiz manzili — boshlangʻich maʼlumotlar va kodning xeshi. Kod almashtirilsa, manzil boshqacha boʻladi.
Uni ochib koʻrish mumkin (masalan, Tonviewer da). Yagona narsa — kodni tushunish kerak, asosiy muammo shunda. Lekin bu 100% isbot. Kodni sunʼiy intellekt modeliga ham berish mumkin. Quyida — shartnoma kodi va imkon qadar sodda izoh.
ContractUSDT
Bu importlar roʻyxati: konstantalar va yordamchi funksiyalar.
1import "@contracts/common-messages"2import "@contracts/errors"3import "@contracts/contract_USDT/usdt-utils"4import "fees-management"5import "messages"6import "storage"Shartnomaning qisqacha tavsifi.
8contract ContractUSDT {9 author: "Alexandr_K"10 version: "0.0.4"11 description: "Escrow contract USDT"12 storage: StorageUsdt13 incomingMessages: AllowedMessage14}Shartnoma bilan mavjud amallar roʻyxati.
16type AllowedMessage =17 | TransferNotificationForRecipient18 | ChangePercent19 | AcceptPercent20 | RefundUsdt21 | ErrRefund22 | ErrMode23 | BurnContractYordamchi funksiya: shartnoma faolmi yoki yoʻq. Hamma oʻz summasini kiritgach, shartnoma faol boʻladi — endi shunchaki yechib boʻlmaydi.
28fun StorageUsdt.isActive(self, extra: ExtraFieldsUsdt): bool {29 if (extra.completed) {30 return false;31 }32 return self.clientAmount >= extra.clientAmountInit &&33 self.providerDeposit >= extra.providerDepositInit;34}Qaytim funksiyasi. Xabarga tranzaksiyani qayta ishlash uchun ozgina GRAM qoʻshiladi. Ortiqchasini u qaytaradi.
67fun sendExcessesTon(to: address, amount: coins) {68 if (amount > MIN_MESSAGE_VALUE_BOUND) {69 val excessesMsg = createMessage({70 bounce: BounceMode.NoBounce,71 dest: to,72 value: amount,73 });74 excessesMsg.send(SEND_MODE_IGNORE_ERRORS);75 }76}Jetton hamyon manzilini hisoblaydi. U shartnoma manziliga bogʻliq.
78fun contractUsdtWallet(): address {79 return calcAddressOfJettonWallet(contract.getAddress());80}Agar oʻtkazma muvaffaqiyatsiz boʻlsa — pul qaytadi, shartnoma yozuvlarni tuzatadi: kimda qancha.
105fun onBouncedMessage(in: InMessageBounced) {106 in.bouncedBody.skipBouncedPrefix();107 val msg = lazy BounceOpToHandle.fromSlice(in.bouncedBody);108 var storage = lazy StorageUsdt.load();109 match (msg) {110 TransferBounce => {111 if (msg.queryId == QueryRole.Client as uint64) {112 storage.clientAmount += msg.jettonAmount;113 } else if (msg.queryId == QueryRole.Provider as uint64) {114 storage.providerDeposit += msg.jettonAmount;115 } else if (msg.queryId == QueryRole.Admin as uint64) {116 var extra: ExtraFieldsUsdt = lazy storage.extra.load();117 extra.royaltyInit += msg.jettonAmount;118 storage.extra = extra.toCell();119 } else {120 // ErrRefund (QueryRole.None) и неизвестные id: учёт не уменьшали121 return;122 }123 }124 }125 storage.save();126}Agar xabar tanasi boʻsh boʻlsa — hech narsa qilmaymiz.
131fun onInternalMessage(in: InMessage) {132 if (in.body.isEmpty()) {133 return;134 }Keyin oʻzgaruvchilarni eʼlon qilish.
137val msg = lazy AllowedMessage.fromSlice(in.body);138var storage: StorageUsdt = lazy StorageUsdt.load();139val tonBalanceBeforeMsg = contract.getOriginalBalance() - in.valueCoins;140val storageFee = MIN_TONS_FOR_STORAGE_THIS_CONTRACT -141min(tonBalanceBeforeMsg, MIN_TONS_FOR_STORAGE_THIS_CONTRACT);142var msgValue = in.valueCoins - storageFee - CONTRACT_GAS_CONSUMPTION;USDT bilan toʻldirish: oʻtkazmalarni qabul qiladi va kimda qancha USDT borligini yozib boradi.
145TransferNotificationForRecipient => {146 assert (in.senderAddress == contractUsdtWallet()) throw Errors.InvalidWallet;147 assert (msg.transferInitiator != null) throw Errors.InvalidData;148 if (msg.transferInitiator == storage.clientAddress) {149 storage.clientAmount += msg.jettonAmount;150 storage.save();151 } else if (msg.transferInitiator == storage.providerAddress) {152 storage.providerDeposit += msg.jettonAmount;153 storage.save();154 }155 sendExcessesTon(msg.transferInitiator, msgValue);156}Toʻlov foizini oʻzgartiradi. Ish faqat yarmigacha bajarilgan — 50% taklif qilish mumkin. Pudratchi rozi boʻldi — shartnoma bajarildi. Yoʻq — yana kelishib oling.
162ChangePercent => {163 assert (msgValue > 0) throw Errors.NotEnoughGas;164 assert (in.senderAddress == storage.clientAddress) throw Errors.UnauthorizedSender;165 assert (msg.percent >= 0 && msg.percent <= 100) throw Errors.WrongPercent;166 var extra: ExtraFieldsUsdt = lazy storage.extra.load();167 assert (extra.completed == false) throw Errors.ContractIsCompleted;168 assert (storage.isActive(extra)) throw Errors.ContractIsNotActive;169 extra.percent = msg.percent;170 if (msg.percent == 100) {171 extra.completed = true;172 storage.providerDeposit += extra.clientAmountInit - extra.royaltyInit;173 storage.clientAmount -= extra.clientAmountInit;174 }175 storage.extra = extra.toCell();176 storage.save();177 sendExcessesTon(in.senderAddress, msgValue);178}Agar pudratchi rozi boʻlsa, shartnoma unga ulushini va depozitini, qolganini sizga ajratadi. 0% — butun bitim bekor qilinadi, hamma pulini qaytarib oladi. Bunda shartnomaga 0,4% komissiya toʻlanmaydi.
183AcceptPercent => {184 assert (msgValue > 0) throw Errors.NotEnoughGas;185 assert (in.senderAddress == storage.providerAddress) throw Errors.UnauthorizedSender;186 var extra: ExtraFieldsUsdt = lazy storage.extra.load();187 assert (extra.completed == false) throw Errors.ContractIsCompleted;188 assert (extra.percent != 100) throw Errors.WrongPercent;189 assert (msg.percent == extra.percent) throw Errors.WrongPercent;190 extra.completed = true;191 val percentValue = extra.clientAmountInit / 100 * msg.percent;192 if (msg.percent == 0) {193 extra.royaltyInit = 0;194 }195 storage.providerDeposit += percentValue - extra.royaltyInit;196 storage.clientAmount -= percentValue;197 storage.extra = extra.toCell();198 storage.save();199 sendExcessesTon(in.senderAddress, msgValue);200}Eng katta blok — mablagʻlarni qaytarishni boshqaradi.
208RefundUsdt => {209 msgValue -= (MIN_MESSAGE_VALUE_BOUND + MIN_FORWARD_TON_AMOUNT);210 assert (msgValue > 0) throw Errors.NotEnoughGas;211 assert (msg.jettonAmount > 0) throw Errors.InvalidData;212 var extra: ExtraFieldsUsdt = lazy storage.extra.load();213 val transferRecipient: address = msg.addressRefund != null214 ? msg.addressRefund215 : in.senderAddress;216 val isActive = storage.isActive(extra);217 var queryId: uint64;218 var jettonAmount: coins;219 if (in.senderAddress == storage.clientAddress) {220 queryId = QueryRole.Client;221 if (isActive) {222 val deposit: uint72 = (extra.clientAmountInit / 100) * 30;223 val locked = extra.clientAmountInit + deposit;224 assert (storage.clientAmount > locked) throw Errors.NotEnoughJettons;225 jettonAmount = min(storage.clientAmount - locked, msg.jettonAmount);226 } else {227 jettonAmount = min(storage.clientAmount, msg.jettonAmount);228 }229 storage.clientAmount -= jettonAmount;230 } else if (in.senderAddress == storage.providerAddress) {231 queryId = QueryRole.Provider;232 if (isActive) {233 assert (234 storage.providerDeposit > extra.providerDepositInit235 ) throw Errors.NotEnoughJettons;236 jettonAmount = min(237 storage.providerDeposit - extra.providerDepositInit,238 msg.jettonAmount,239 );240 } else {241 jettonAmount = min(storage.providerDeposit, msg.jettonAmount);242 }243 storage.providerDeposit -= jettonAmount;244 } else if (in.senderAddress == extra.adminAddress) {245 queryId = QueryRole.Admin;246 val maxRefundUsdt: uint72 = extra.completed ? extra.royaltyInit : 0;247 jettonAmount = min(maxRefundUsdt, msg.jettonAmount);248 extra.royaltyInit -= jettonAmount;249 storage.extra = extra.toCell();250 } else {251 throw Errors.UnauthorizedSender;252 }253 assert (jettonAmount > 0) throw Errors.NotEnoughJettons;254 storage.save();255 sendAskToTransferUsdt(256 queryId,257 jettonAmount,258 transferRecipient,259 msgValue + MIN_MESSAGE_VALUE_BOUND + MIN_FORWARD_TON_AMOUNT,260 );261}Xato rejimi. Agar hamyon yoʻqolsa yoki pul hisobdan tashqari oʻtgan boʻlsa, tomon nazoratni boshqa tomonga topshirishi mumkin. «Hisobdan tashqari» holati, aslida, ataylab qilinmasa, imkonsiz. Lekin boʻlaversin. Hamyonni unutish yoki yoʻqotish esa butunlay mumkin.
271ErrMode => {272 assert (msgValue > 0) throw Errors.NotEnoughGas;273 var extra: ExtraFieldsUsdt = lazy storage.extra.load();274 var noticeTo: address? = null;275 var noticeAboutClient: bool = false;276 if (in.senderAddress == storage.clientAddress) {277 if (msg.value == ErrModeVal.ProviderFullRights as int8) {278 extra.errMode = ErrModeVal.ProviderFullRights;279 } else if (msg.value == ErrModeVal.BlockErrorMode as int8) {280 extra.errMode = ErrModeVal.BlockErrorMode;281 } else if (282 msg.value == ErrModeVal.ClientFullRightsAfterHalfYear as int8 &&283 extra.errMode != ErrModeVal.BlockErrorMode as int8284 ) {285 extra.errMode = ErrModeVal.ClientFullRightsAfterHalfYear;286 extra.errModeAt = blockchain.now() as uint64;287 noticeTo = storage.providerAddress;288 noticeAboutClient = true;289 }290 } else if (in.senderAddress == storage.providerAddress) {291 if (msg.value == ErrModeVal.ClientFullRights as int8) {292 extra.errMode = ErrModeVal.ClientFullRights;293 } else if (msg.value == ErrModeVal.BlockErrorMode as int8) {294 extra.errMode = ErrModeVal.BlockErrorMode;295 } else if (296 msg.value == ErrModeVal.ProviderFullRightsAfterHalfYear as int8 &&297 extra.errMode != ErrModeVal.BlockErrorMode as int8298 ) {299 extra.errMode = ErrModeVal.ProviderFullRightsAfterHalfYear;300 extra.errModeAt = blockchain.now() as uint64;301 noticeTo = storage.clientAddress;302 noticeAboutClient = false;303 }304 } else {305 throw Errors.UnauthorizedSender;306 }307 if (noticeTo != null) {308 assert (msgValue > HALF_YEAR_NOTICE_VALUE) throw Errors.NotEnoughGas;309 msgValue -= HALF_YEAR_NOTICE_VALUE;310 }311 storage.extra = extra.toCell();312 storage.save();313 if (noticeTo != null) {314 sendHalfYearControlNotice(noticeTo, noticeAboutClient);315 }316 sendExcessesTon(in.senderAddress, msgValue);317}10enum ErrModeVal {11 ClientFullRights = 112 ProviderFullRights = 213 ClientFullRightsAfterHalfYear = 16 // клиент получает полные права через полгода14 ProviderFullRightsAfterHalfYear = 26 // исполнитель — через полгода15 BlockErrorMode = 9916}ErrRefund — bildirishnomasiz kelgan va hisobga tushmagan jettonlarni favqulodda yechib olish. Yoki hamyon yoʻqolganda (aniqrogʻi, uning paroli yoʻqolganda).
321ErrRefund => {322 msgValue -= (MIN_MESSAGE_VALUE_BOUND + MIN_FORWARD_TON_AMOUNT);323 assert (msgValue > 0) throw Errors.NotEnoughGas;324 assert (msg.jettonAmount > 0) throw Errors.InvalidData;325 val extra: ExtraFieldsUsdt = lazy storage.extra.load();326 if (327 !extra.allowsErrRefund(328 in.senderAddress,329 storage.clientAddress,330 storage.providerAddress,331 )332 ) {333 throw Errors.UnauthorizedSender;334 }335 val transferRecipient: address = msg.addressRefund != null336 ? msg.addressRefund337 : in.senderAddress;338 sendAskToTransferUsdt(339 QueryRole.None,340 msg.jettonAmount as coins,341 transferRecipient,342 msgValue + MIN_MESSAGE_VALUE_BOUND + MIN_FORWARD_TON_AMOUNT,343 );344}36fun ExtraFieldsUsdt.allowsErrRefund(37 self,38 sender: address,39 client: address,40 provider: address,41): bool {42 if (sender == client) {43 if (self.errMode == ErrModeVal.ClientFullRights as int8) {44 return true;45 }46 return self.errMode == ErrModeVal.ClientFullRightsAfterHalfYear as int8 &&47 (blockchain.now() as uint64) >= self.errModeAt + HALF_YEAR;48 }49 if (sender == provider) {50 if (self.errMode == ErrModeVal.ProviderFullRights as int8) {51 return true;52 }53 return self.errMode == ErrModeVal.ProviderFullRightsAfterHalfYear as int8 &&54 (blockchain.now() as uint64) >= self.errModeAt + HALF_YEAR;55 }56 return false;57}Burn — boʻsh qobiq tarmoqda osilib qolmasligi uchun shartnomani yoʻq qilish. Uni yaratgan kishi (mijoz yoki pudratchi) yoki admin yoqib yuborishi mumkin — va faqat mijoz hamda pudratchi balanslari nolga teng boʻlganda.
351BurnContract => {352 assert (msgValue > 0) throw Errors.NotEnoughGas;353 val extra: ExtraFieldsUsdt = lazy storage.extra.load();354 val deployer = extra.deployerAddress(storage.clientAddress, storage.providerAddress);355 assert (356 in.senderAddress == deployer || in.senderAddress == extra.adminAddress357 ) throw Errors.UnauthorizedSender;358 assert (359 storage.clientAmount == 0 && storage.providerDeposit == 0360 ) throw Errors.BalancesNotEmpty;361 if (extra.completed && extra.royaltyInit > 0) {362 val jettonTon = MIN_MESSAGE_VALUE_BOUND + MIN_FORWARD_TON_AMOUNT;363 msgValue -= jettonTon;364 assert (msgValue > 0) throw Errors.NotEnoughGas;365 sendAskToTransferUsdt(366 QueryRole.Admin,367 extra.royaltyInit as coins,368 extra.adminAddress,369 msgValue + jettonTon,370 );371 }372 val burn = createMessage({373 bounce: BounceMode.NoBounce,374 dest: in.senderAddress,375 value: 0,376 });377 burn.send(SEND_MODE_CARRY_ALL_BALANCE | SEND_MODE_DESTROY);378}59fun ExtraFieldsUsdt.deployerAddress(self, client: address, provider: address): address {60 if (self.mode == QueryRole.Client as int8) {61 return client;62 }63 assert (self.mode == QueryRole.Provider as int8) throw Errors.UnauthorizedSender;64 return provider;65}Uchta shartnomaning tasdiqlangan manba kodi. Kod xeshi tarmoqdagi bilan mos. Har bir havola manbani TON Verifier da ochadi.
ContractMinterContractUSDTContractTON