Companion code: https://github.com/mrgnresearch/sui-cookbook/tree/main/single-gas-coin-transaction
Context
Interacting on the blockchain comes with a price. It can be expensive or cheap, but there is always a gas/fee component to submitting a transaction on-chain.
In the case of Sui, this payment is done in the native SUI Coin
. Each transaction packs a non-empty list of SUI coins used for gas payment, separate from the transaction’s arguments.
The Problem
Say you just started developing on Sui and are eager to land your first PTB (check out this article if you are not familiar with the term). You decide to start simple, and code up the following transaction:
The transaction in itself does not matter here, apart from noticing that we are using our only Sui coin as input to its first command (0x2::coin::value
).
You then write the code to send it. In Rust it would look something like the following:
The code for the PTB itself (build_ptb_logic
) can be found in the complete sample code.
After getting yourself airdropped some Sui to a fresh wallet, you run the program, and surprise:
What Happened?!
While the code seems sane the error is rather explicit, pointing to a specific mutable object being used more than once in the transaction, which is forbidden. The client libraries normally prevent those errors from happening within the set of objects provided as call arguments, by inserting an object in the PTB inputs list only if this object is not already present. However in our case the issue is less obvious, as the duplication happens between primary_coin
passed as (1) call argument, and (2) payment object.
Digging deeper, we can see that this happens specifically because gas payment objects are added to the list of input objects, before the check is performed.
The Solution
The fix for the above is both obvious and slightly nuanced. The stages of grief go:
Damn, it seems I cannot do anything when I only have one gas coin 😕
Easy, I’ll split the coin first! 😀
Wait this is the same problem, I’ll be splitting the coin I use for payment, so duplicate error again 😨
Enters: the GasCoin
call argument type (next to Input
/Result
/NestedResult
). The above reasoning is correct, the only nuance is that the argument provided to the opening coin split needs to be the special Argument::GasCoin
, to avoid the duplicate issue.
The PTB then becomes:
And the code:
As usual, it is worth printing those PTBs to inspect what those changes do, and here observe that the inputs list does not include the primary coin explicitly anymore.
Parting Words
Getting a good understanding of these gas payment matters is important to avoid defaulting to weird coin management, which can in some cases lead to equivocation, depending on what you are attempting.
Companion code: https://github.com/mrgnresearch/sui-cookbook/tree/main/single-gas-coin-transaction