Skip to content

Commit 8cfe701

Browse files
feat(transaction-builder): Set gas if none was provided (#270)
* feat(transaction-builder): Set gas if none was provided * feature gate * one more feature * fix coin type * fix input deduplication * only default for finish and execute * clippy * small improvement
1 parent 6908d89 commit 8cfe701

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

crates/iota-sdk-types/src/object.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,16 @@ impl Object {
391391
}
392392
}
393393

394+
/// Return this object's reference
395+
#[cfg(all(feature = "hash", feature = "serde"))]
396+
pub fn object_ref(&self) -> ObjectReference {
397+
ObjectReference {
398+
object_id: self.object_id(),
399+
version: self.version(),
400+
digest: self.digest(),
401+
}
402+
}
403+
394404
/// Return this object's version
395405
pub fn version(&self) -> Version {
396406
match &self.data {

crates/iota-transaction-builder/src/builder/mod.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ use std::{
1111
use iota_crypto::IotaSigner;
1212
use iota_graphql_client::{
1313
Client, DryRunResult,
14-
query_types::{ObjectRef, TransactionMetadata},
14+
query_types::{ObjectFilter, ObjectRef, TransactionMetadata},
1515
};
1616
use iota_types::{
1717
Address, GasPayment, Identifier, ObjectId, ObjectReference, Owner, ProgrammableTransaction,
18-
Transaction, TransactionEffects, TransactionExpiration, TypeTag,
18+
StructTag, Transaction, TransactionEffects, TransactionExpiration, TypeTag,
1919
};
2020
use serde::Serialize;
2121

@@ -73,10 +73,21 @@ pub struct TransactionBuildData {
7373

7474
impl TransactionBuildData {
7575
fn set_input(&mut self, kind: InputKind, is_gas: bool) -> Argument {
76-
if let Some((i, input)) = self.inputs.iter_mut().find(|(_, input)| input.kind == kind) {
76+
if let Some((i, input)) = self.inputs.iter_mut().find(|(_, input)| {
77+
match (kind.object_id(), input.kind.object_id()) {
78+
(Some(id1), Some(id2)) => id1 == id2,
79+
(None, None) => kind == input.kind,
80+
_ => false,
81+
}
82+
}) {
7783
if is_gas {
7884
input.is_gas = true;
7985
}
86+
// If the new input is already resolved, replace the old one in case it was
87+
// unresolved
88+
if let new_kind @ InputKind::Input(_) = kind {
89+
input.kind = new_kind;
90+
}
8091
return Argument::Input(*i as _);
8192
}
8293
let idx = self
@@ -698,10 +709,31 @@ impl<L> TransactionBuilder<Client, L> {
698709
})
699710
}
700711

701-
async fn resolve_ptb(&mut self) -> Result<Transaction, Error> {
712+
async fn resolve_ptb(&mut self, default_gas: bool) -> Result<Transaction, Error> {
702713
let mut inputs = Vec::new();
703714
let mut gas = Vec::new();
704715
let mut input_map = HashMap::new();
716+
if default_gas && !self.data.inputs.values().any(|i| i.is_gas) {
717+
for coin in self
718+
.client
719+
.objects(
720+
ObjectFilter {
721+
type_: Some(StructTag::gas_coin().to_string()),
722+
owner: Some(self.data.sender),
723+
..Default::default()
724+
},
725+
Default::default(),
726+
)
727+
.await
728+
.map_err(Error::Client)?
729+
.data
730+
{
731+
self.set_input(
732+
InputKind::Input(iota_types::Input::ImmutableOrOwned(coin.object_ref())),
733+
true,
734+
);
735+
}
736+
}
705737
for (id, input) in std::mem::take(&mut self.data.inputs) {
706738
match input.kind {
707739
InputKind::ImmutableOrOwned(object_id) | InputKind::Receiving(object_id) => {
@@ -815,7 +847,7 @@ impl<L> TransactionBuilder<Client, L> {
815847

816848
/// Convert this builder into a transaction.
817849
pub async fn finish(mut self) -> Result<Transaction, Error> {
818-
let mut txn = self.resolve_ptb().await?;
850+
let mut txn = self.resolve_ptb(true).await?;
819851
if self.data.gas_budget.is_none() {
820852
let res = self
821853
.client
@@ -837,7 +869,7 @@ impl<L> TransactionBuilder<Client, L> {
837869

838870
/// Dry run the transaction.
839871
pub async fn dry_run(mut self, skip_checks: bool) -> Result<DryRunResult, Error> {
840-
let txn = self.resolve_ptb().await?;
872+
let txn = self.resolve_ptb(false).await?;
841873
if !txn.gas_payment.objects.is_empty() && txn.gas_payment.budget == 0 {
842874
return Err(Error::DryRun(
843875
"gas coins were provided without a gas budget".to_owned(),

crates/iota-transaction-builder/src/unresolved.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ pub enum InputKind {
4242
Input(iota_types::Input),
4343
}
4444

45+
impl InputKind {
46+
pub fn object_id(&self) -> Option<ObjectId> {
47+
if let Self::ImmutableOrOwned(object_id)
48+
| Self::Receiving(object_id)
49+
| Self::Shared { object_id, .. }
50+
| Self::Input(
51+
iota_types::Input::ImmutableOrOwned(ObjectReference { object_id, .. })
52+
| iota_types::Input::Receiving(ObjectReference { object_id, .. })
53+
| iota_types::Input::Shared { object_id, .. },
54+
) = self
55+
{
56+
Some(*object_id)
57+
} else {
58+
None
59+
}
60+
}
61+
}
62+
4563
#[derive(Debug, Clone, derive_more::From)]
4664
pub enum Command {
4765
MoveCall(MoveCall),

0 commit comments

Comments
 (0)