Пост
Поделитесь своими знаниями.
+15
Ошибка Sui Move — невозможно обработать транзакцию Не найдено действительных газовых монет для транзакции
Когда я это сделаю:
//Отделите платеж от основной монеты константа [PaymentCoin] = TX.SplitCoins ( объект tx.object (идентификатор объекта PrimaryCoin.coin), [tx.pure.u64 (обязательная сумма платежа)] );
//Используйте оригинальную монету для оплаты газа tx.Установите оплату газа ([{ Идентификатор объекта: идентификатор объекта PrimaryCoin.coin, версия: версия PrimaryCoin, дайджест: PrimaryCoin.Дайджест }]);
Налогообложение бюджета на газ (10_000_000); Он жалуется на то, что изменяемые объекты не могут появляться более одного в одной транзакции. Когда я удаляю платеж за газ, оно жалуется: «Невозможно обработать транзакцию» Для транзакции не найдено действительных газовых монет». Моя контрактная функция принимает сумму 0,01 евро в обмен на NFT
- Sui
- Transaction Processing
- Move
Ответы
9-
В языке Суй монета — это изменчивый объект.
-
Если вы используете одну и ту же монету в качестве платежа и газовой монеты, транзакция завершится ошибкой «изменяемые объекты».
-
Это связано с тем, что к изменяемому объекту нельзя получить доступ в нескольких ролях в одной транзакции.
-
В вашем коде PrimaryCoin разделяется для оплаты, а также используется в качестве оплаты за газ.
-
Это вызывает конфликт изменчивости в правилах транзакций Sui.
-
Решение заключается в использовании двух отдельных монет — одного для газа, а другого для оплаты.
-
Если в вашем кошельке есть только одна монета, вам сначала нужно разделить ее на две части, прежде чем проводить фактическую транзакцию.
-
Интерфейс командной строки Sui или JavaScript SDK могут разделять монеты с помощью SplitCoins и возвращать два разных объекта монет.
-
Газовая монета не должна быть изменена во время транзакции.
-
Ниже приведен рабочий JavaScript-подход:
константные монеты = ждут провайдера.getCoins ({владелец: адрес отправителя}); если (coins.data.length < 2) выдаст новую ошибку («На бензин нужно не менее 2 монет + оплата»);
const GasCoin = coins.data [0];//Только для газа const PaymentCoin = coins.data [1];//Для оплаты по контракту
налог на константу = новый блок транзакций (); объекты tx.transfer ([tx.object (идентификатор объекта PaymentCoin.coin)], tx.pure (адрес получателя)); tx.setGasPayment ([{Идентификатор объекта: идентификатор объекта gascoin.coin, версия: gascoin.version, дайджест: gascoin.digest}]); Задайте бюджет на газ (10_000_000);
дождитесь подписания и выполнения блока транзакций ({TransactionBlock: tx});
-
Таким образом, газовая монета останется нетронутой, за исключением оплаты за газ.
-
Платежная монета используется для оплаты комиссии в размере 0,01 SUI по вашему контракту.
-
Если у вас есть только одна монета, сначала отправьте себе небольшую транзакцию, чтобы разделить ее на две части.
-
Это можно сделать с помощью специального клиента split-coin или вызова SDK перед основной транзакцией.
-
Соблюдение этого правила разделения позволяет избежать ошибок, связанных как с изменчивыми объектами, так и с ошибками «нет действительной газовой монеты»
Это происходит потому, что вы пытаетесь использовать один и тот же монетный объект (primaryCoin
) как для оплаты газа, так и для вводаsplitCoins
, поэтому его можно использовать в двух разных контекстах, а Sui не допускает этого из соображений безопасности и линейной логики (поскольку монеты — это линейные объекты).
Имхо, вообще не нужно настраивать оплату за газ вручную. Просто позвольте кошельку/клиенту 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
безопасно. - Используйтедругую монету(а иногда и ту же самую, но при правильном управлении версиями объектов ее можно безопасно хранить под капотом).
Важно: если в вашем кошельке есть SUI стоимостью не менее 1 доллара США, который может покрыть бензин, это сработает.
Вы столкнулись с распространенным ограничением дизайна транзакций 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 SUI) → Всего необходимых сумм:≥ 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), дополнительных монет для оплаты газа не осталось.
Как это исправить:
Вам нужны два разных предмета для монет:
Монета A→ используется только для газа
Монета B→ разделите сумму платежа в размере 0.1 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 суя плюс комиссии.
Эта проблема возникает из-за того, что вы используете одну и ту же монету (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 безопасно выберет из вашего кошелька газовую монету (если она есть) и разберется со всем за кулисами
Знаете ответ?
Пожалуйста, войдите в систему и поделитесь им.
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