Допис
Діліться своїми знаннями.
+15
Помилка Sui Move - Неможливо обробити транзакцію Не знайдено дійсних газових монет для транзакції
Коли я роблю це:
//Розділіть платіж з основної монети конст [PaymentCoin] = TX.спліткойни ( tx.object (первинний Coin.CoinObjectID), [tx.pure.u64 (Обов'язкова сума платежу)] );
//Використовуйте оригінальну монету для оплати газу tx.setGasPayment ([{ Об'єктID: первинний Coin.CoinObjectID, версія: первинна коін.версія, дайджест: первинний коїн.дайджест }]);
ТК.сетгасБюджет (10_000_000); Він скаржиться на об'єкти, що змінюються, не можуть з'являтися більше одного в одній транзакції. Коли я знімаю платіж за газ, він скаржиться «Не вдається обробити транзакцію Для транзакції не знайдено дійсних газових монет». Моя функція контракту приймає .01 sui в обмін на NFT
- Sui
- Transaction Processing
- Move
Відповіді
9-
У Суї монета - це змінний об'єкт.
-
Коли ви використовуєте одну і ту ж монету як платіжну, так і газову монету, транзакція провалиться з помилкою «змінювані об'єкти».
-
Це пов'язано з тим, що об'єкт, що змінюється, не може бути доступний у кількох ролах в межах однієї транзакції.
-
У вашому коді PrimaryCoin розділяється для оплати, а також встановлюється як платіж за газ.
-
Це викликає конфлікт змінності в правилах транзакцій Sui.
-
Рішення полягає у використанні двох окремих монетних об'єктів — одного для газу та одного для оплати.
-
Якщо у вашому гаманці є лише одна монета, вам спочатку потрібно розділити її на дві, перш ніж будувати фактичну транзакцію.
-
CLI або JavaScript SDK від Sui може розділити монети за допомогою SplitCoins і повернути два окремих об'єкти монет.
-
Газову монету не слід модифікувати під час угоди.
-
Нижче наведено робочий підхід JavaScript:
const coins = очікувати провайдера.getCoins ({власник: адресу відправника}); якщо (coins.data.length < 2) кинути нову помилку («Потрібні мінімум 2 монети для газ+оплата»);
const GasCoin = coins.data [0];//Тільки для газу const PaymentCoin = coins.data [1];//Для оплати за контрактом
const tx = новий блок транзакцій (); tx.ТрансферОб'єкти ([tx.object (PaymentCoin.CoinObjectID)], tx.pure (Адреса одержувача)); tx.setGasPayment ([{ObjectID: gascoin.coinObjectID, версія: gascoin.Version, дайджест: gascoin.digest}]); ТК.сетгасБюджет (10_000_000);
чекати підпис.signAndExecuteTransactionBlock ({транзакціяБлок: tx});
-
Таким чином, газова монета залишається недоторканою, крім оплати газу.
-
Платіжна монета використовується для оплати комісії 0,01 SUI вашого контракту.
-
Якщо у вас є лише одна монета, спочатку надішліть собі невелику транзакцію, щоб розділити її на дві.
-
Це можна зробити за допомогою спліт-коіни sui клієнта або виклику SDK перед основною транзакцією.
-
Дотримання цього правила поділу запобігає помилкам як «змінних об'єктів», так і «відсутність дійсної газової монети»
Це відбувається тому, що ви намагаєтеся використовувати один і той же об'єкт монети (primaryCoin
) як як платіж за газ, так і як вхід доsplitCoins
, що робить його змінним посиланням, що використовується у двох різних контекстах - і Sui не дозволяє цього для безпеки та лінійної логіки (оскільки монети є лінійними об'єктами).
Спосіб IMHO полягає в тому, щоб взагалі не встановлювати оплату за газ вручну. Просто нехай гаманець/клієнт Sui автоматично вибере відповідну газову монету. Використовуйте лише в тому setGasPayment
випадку, якщо вам дійсно потрібно вказати, яка монета платить за газ (наприклад, гаманці з кількома монетами, конкретне управління газом). В іншому випадку уникайте цього.
Спробуйте наступне:
// Split the primary coin to get a payment coin
const [paymentCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[tx.pure.u64(requiredPaymentAmount)]
);
// Do your call that sends .01 SUI and gets an NFT
tx.moveCall({
target: `${packageId}::your_module::buy_nft`,
arguments: [
paymentCoin,
// other args...
],
});
// DO NOT set gas payment manually
// tx.setGasPayment(...) ← Remove this line
// Optional: set budget
tx.setGasBudget(10_000_000);
// Send the transaction
const result = await signer.signAndExecuteTransactionBlock({
transactionBlock: tx,
options: { showEffects: true },
});
Суй буде:
- Автоматично вибрати газову монету (може бути та сама чи інша монета SUI в гаманці).
- Поводьтеся
splitCoins
безпечно. - Використовуйтеіншу монету(або іноді ту саму, але безпечно обробляйте під капотом за допомогою належного редагування об'єктів).
Важливо: Поки у вашому гаманці є принаймні 1 $SUI, який може покрити газ, це буде працювати.
Ви стикаєтесь із загальним обмеженням дизайну транзакції Sui Moveтой самий об'єкт монети не може бути використаний як змінний вхід (наприклад, для поділу чи передачі), так і як газову монету в одній транзакції.
Чому це відбувається
- Коли ви використовуєте Tx.splitCoins (tx.object (PrimaryCoin.CoinObjectId),...), ви позначаєте PrimaryCoin як змінний вхід.
- Коли ви також встановлюєте її як газову монету за допомогою tx.setGasPayment (...), Суй бачить, що один і той же об'єкт використовується у двох ролах, що заборонено.
- Якщо ви знімаєте платіж за газ, Суй не може знайти дійсну газову монету, звідси помилка «Не знайдено дійсних газових монет для транзакції».
З контексту:
Статус ефектів транзакції: некоректне використання значення. Змінно запозичені значення вимагають унікального використання. Незмінно запозичені цінності не можуть бути прийняті або запозичені змінно. Взяті значення не можна використовувати повторно. (джерело)
Як виправити
Ви повинні використовувати іншу монету для газу, ніж ту, яку ви розщеплюєте або переводите.
Рішення: Майте у гаманці принаймні дві монети SUI. Використовуйте один для оплати (спліт/переказ), а інший - для газу.
Приклад потоку
- Виберіть дві монети:
- PrimaryCoin (для оплати)
- GasCoin (для газу)
- Розділіть та оплатіть за допомогою PrimaryCoin:
const [paymentCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[tx.pure.u64(requiredPaymentAmount)]
);
- Встановіть оплату газу за допомогою GasCoin:
tx.setGasPayment([{
objectId: gasCoin.coinObjectId,
version: gasCoin.version,
digest: gasCoin.digest
}]);
tx.setGasBudget(10_000_000);
Не використовуйте один і той же об'єкт монети як для оплати, так і для газу.
Моя довідка
Помилка виникає через те, що ви намагаєтеся використатиоригінальний primaryCoin
об'єкт(який споживається під час splitCoins
операції) як платіж за газ. Після поділу версія/дайджест оригінальної монети стає недійсною, що спричиняє помилку «об'єкти, що змінюються, не можуть відображатися більше одного» при повторному посиланні.
Для виправлення не встановлюйте вручну оплату газу за допомогою primaryCoin
об'єкта попереднього спліту. І переконайтеся, що у primaryCoin
вас є достатній баланс для покриття обох:
- Сума платежу (
requiredPaymentAmount
= 0,01 SUI) - Газовий бюджет (
10_000_000
= 0,01 СУІ) → Всього необхідних:≥ 0,02 SUI
Просто спробуйте
// Split payment from primary coin (leaves remaining balance in primaryCoin)
const [paymentCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[tx.pure.u64(requiredPaymentAmount)]
);
// DO NOT setGasPayment manually - SDK auto-uses updated primaryCoin for gas
tx.setGasBudget(10_000_000); // Gas paid from primaryCoin's remaining balance
Проблеми
Ви стикаєтеся з двома основними проблемами:
1.Помилка змінності: Спроба використовувати один і той же об'єкт монети як для оплати газу, так і для введення транзакції призводить до помилки: «Об'єкти, що змінюються, не можуть з'являтися більше одного разу в одній транзакції».
2.Відсутня газова монета: Без дійсної газової монети виникає помилка «Неможливо обробити транзакцію: для транзакції не знайдено дійсних газових монет».
Рішення
Щоб вирішити ці проблеми, виконайте такі дії:
1.Розділіть основну монету для платежу: Використовуйте для tx.splitCoins
створення нової монети для покупки NFT, переконавшись, що вона відокремлена від платежу за газ.
-
tx.setGasPayment
Встановіть окрему газову монету: Призначте іншу монету для оплати за допомогою газу. -
tx.setGasBudget
Визначте бюджет газу: Встановіть відповідний бюджет газу за допомогою.
Код
// Step 1: Split the primary coin for payment
const [paymentCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[tx.pure.u64(requiredPaymentAmount)]
);
// Step 2: Set a separate gas payment coin
const gasCoin = tx.object(gasCoinObjectId);
tx.setGasPayment([{
objectId: gasCoin.coinObjectId,
version: gasCoin.version,
digest: gasCoin.digest
}]);
// Step 3: Set the gas budget for the transaction
tx.setGasBudget(10_000_000);
НаSuiодин об'єкт монети є змінним (його баланс змінюється, коли ви його використовуєте), і ви не можете посилатися на один і той самий змінний об'єкт двічі в одній транзакції - ось чому:
Ви розщеплюєтесь від того самого PrimaryCoin
А також використовувати його як оплату за газ
→ Конструктор транзакцій позначає це як «об'єкт, що змінюється, з'являється більше одного разу».
Чому «Не знайдено дійсних газових монет», коли ви видаляєте SetGasPayment
Якщо ви не вказуєте газову монету, Sui SDK автоматично вибирає газову монету з ваших власних монет, яка ще не використовується в транзакції. Але оскільки ваша транзакція вже споживає єдину монету, яку ви маєте (через SplitCoins), зайвої монети для оплати газу не залишилося.
Як це виправити:
Вам потрібні два різних предмета монети:
Монета А→ використовується лише для газу
Монета B→ розділіть для вашого платежу.01 SUI
Якщо у вашому гаманці є лише одна монета, ви повинні спочатку розділити її на два окремі об'єкти монети в попереднійтранзакції.**
Ключовим є:
- Sui запобігає тому, щоб один об'єкт монети був одночасно газом і змінним входом в одній транзакції, оскільки він розглядається як одне і те ж змінне посилання.
- Але в Move ви можете отримати монету за оплату, не дбаючи про те, з якого об'єкта вона прийшла - включаючи той, який був розділений від газової монети раніше в тій же транзакції.
Це означає, що ваша функція Move повинна приймати лише платіжну монету, а не оригінальну монету, і ви дозволяєте конструктору транзакцій обробляти її, перш ніж передавати її.
move
module my_package::nft_market {
use sui::coin::{Self, Coin};
use sui::sui::{SUI};
use sui::object::{UID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
/// Simple function: take 0.01 SUI and give NFT
public entry fun buy_nft(
payment: Coin<SUI>, // User provides exactly 0.01 SUI
ctx: &mut TxContext
) {
let amount = coin::value(&payment);
let price = 10_000_000; // 0.01 SUI in MIST (1 SUI = 1_000_000_000 MIST)
assert!(amount == price, 0);
// Transfer the payment to the seller (hardcoded example)
transfer::transfer(payment, tx_context::sender(ctx));
// Mint NFT for buyer
let nft = NFT {
id: object::new(ctx),
name: b"Special NFT".to_vec(),
};
transfer::transfer(nft, tx_context::sender(ctx));
}
struct NFT has key, store {
id: UID,
name: vector<u8>,
}
}
Як це вирішує проблему
Коли клієнт будує угоду:
1. Газова монета (tx.gas) автоматично використовується для газу.
2. Розділіть від газу, щоб отримати нову PaymentCoin:
js
const [paymentCoin] = tx.splitCoins(
tx.gas, // mutable reference to gas coin
[tx.pure.u64(requiredPaymentAmount)]
);
tx.moveCall({
target: `${packageId}::nft_market::buy_nft`,
arguments: [paymentCoin],
});
tx.setGasBudget(10_000_000);
3. Sui дозволяє це тому, що:
tx.gas
є об'єктомоплата газу. -Розділений вихід (paymentCoin
) єновим об'єктом.- Тепер вони єчіткими змінними об'єктамиутранзації.
✅ Перевага: Вам більше не потрібно, щоб гаманець попередньо тримав два окремі об'єкти монети - функція Move піклується лише про платіжну монету, і клієнт завжди може розділити її від газової монети перед дзвінком.
The error you're encountering occurs because you're trying to use the same coin object for both payment and gas payment. Let's fix this with a proper implementation that follows Sui's best practices.
Here's the correct way to structure your transaction:
const tx = new Transaction();
// Split the primary coin into two parts:
// 1. Payment amount (0.01 SUI)
// 2. Gas payment amount (remainder)
const [paymentCoin, gasCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[
tx.pure.u64(requiredPaymentAmount), // 0.01 SUI for payment
tx.pure.u64(1_000_000) // 1 SUI for gas
]
);
// Set the gas payment using the gas coin
tx.setGasPayment([gasCoin]);
// Set appropriate gas budget
tx.setGasBudget(10_000_000);
// Now use the payment coin in your transaction
// Example:
tx.moveCall({
target: '0x2::nft::mint',
arguments: [
tx.object(paymentCoin),
// ... other arguments
]
});
Why Your Original Code Failed Your original code had two issues:
- Duplicate Object Usage
// Problem: Using same coin object twice
const [paymentCoin] = tx.splitCoins(tx.object(primaryCoin.coinObjectId), [tx.pure.u64(requiredPaymentAmount)]);
tx.setGasPayment([{
objectId: primaryCoin.coinObjectId, // Same object used again
version: primaryCoin.version,
digest: primaryCoin.digest
}]);
This fails because Sui doesn't allow using the same mutable object more than once in a transaction.
- Missing Gas Payment
// Problem: No valid gas coins found
tx.setGasBudget(10_000_000); // Budget set but no gas payment specified
When you removed the gas payment, the transaction failed because every transaction needs a valid gas payment.
Best Practices for Gas Management
-
Split Coins Efficiently
i. Always split coins before using them in transactions
ii. Ensure sufficient balance for both payment and gas
iii. Use appropriate gas amounts based on transaction complexity
-
Gas Payment Configuration
i. Set gas payment immediately after splitting coins
ii. Use the SDK's built-in gas management features
iii. Ensure gas budget is sufficient for transaction execution
-
Transaction Structure
i. Split coins first
ii. Set gas payment
iii. Set gas budget
iv. Then perform the main transaction operations
The solution provided at the top of this answer follows these best practices and will resolve both errors you're encountering. Remember that proper gas management is crucial for successful transaction execution on the Sui network.
Щоб вирішити проблему, з якою ви стикаєтесь із помилкою транзакції Sui щодо того, що не знайдено дійсних газових монет або змінних об'єктів, що відображаються більше одного разу, це тому, що ви не можете використовувати одну і ту ж монету як для розділення платежу, так і для покриття комісій за газ, оскільки газові монети повинні залишатися окремо від предметів, які ви налаштовуєте в угоді. Проста настройка полягає в тому, щоб розділити платіж прямо з джерела газу замість вашої основної монети, тому змініть його на щось на кшталт
const paymentCoin = tx.splitCoins(tx.gas(), [tx.pure.u64(requiredPaymentAmount)]);
потім взагалі відмовтеся від ручної лінії оплати газу, оскільки система забере її самостійно, і збереже ваш бюджет газу встановленим як зазвичай. Це дозволяє витягнути з газової монети без зіткнень, якщо у вашому гаманці є достатньо для обробки платежу 0.01 Sui плюс комісії.
Ця проблема трапляється тому, що ви використовуєте одну і ту ж монету (PrimaryCoin) як для газу, так і як вхід у SplitCoins, що заборонено в Sui через його правила навколо лінійних об'єктів та безпечної мутації.
Щоб виправити це, не встановлюйте вручну платіж за газ. Нехай гаманець Sui або клієнт автоматично вибере відповідну газову монету. Вам потрібен SetGasPayment лише в запущених випадках (наприклад, точний контроль монет). Ось чистий підхід:
// Split the primary coin to create a new payment coin
const [paymentCoin] = tx.splitCoins(
tx.object(primaryCoin.coinObjectId),
[tx.pure.u64(requiredPaymentAmount)]
);
// Call your function using the new coin
tx.moveCall({
target: ${packageId}::your_module::buy_nft,
arguments: [paymentCoin],
});
// No manual gas setting — remove tx.setGasPayment(...)
// Set your gas budget
tx.setGasBudget(10_000_000);
// Execute the transaction
const result = await signer.signAndExecuteTransactionBlock({
transactionBlock: tx,
options: { showEffects: true },
});
Суй безпечно забере газову монету з вашого гаманця (поки вона є) і обробляє все за лаштунками
Ви знаєте відповідь?
Будь ласка, увійдіть та поділіться нею.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Зароби свою частку з 1000 Sui
Заробляй бали репутації та отримуй винагороди за допомогу в розвитку спільноти Sui.

- Чому BCS вимагає точного порядку полів для десеріалізації, коли структури Move мають названі поля?55
- Помилки перевірки кількох джерел» у публікаціях модуля Sui Move - автоматичне вирішення помилок45
- Невдала операція Sui: об'єкти, зарезервовані для іншої транзакції48
- Помилка Sui Move - Неможливо обробити транзакцію Не знайдено дійсних газових монет для транзакції29
- Як обмеження здібностей взаємодіють з динамічними полями в гетерогенних колекціях?07