Repository: MystenLabs/sui
File: crates/sui-indexer-alt-graphql/src/api/types/transaction_kind/programmable/inputs/balance_withdraw.rs (Rust)

Code before:
    pub fn from_native(withdrawal: NativeFundsWithdrawalArg, scope: Scope) -> Self {
        let NativeFundsWithdrawalArg {
            reservation,
            type_arg,
            withdraw_from,
        } = withdrawal;

        let reservation = Some(match reservation {
            NativeReservation::EntireBalance => {
                WithdrawalReservation::EntireBalance(EntireBalance { dummy: None })
            }
            NativeReservation::MaxAmountU64(amount) => {
                WithdrawalReservation::MaxAmountU64(MaxAmountU64 {
                    amount: Some(amount.into()),
                })
            }
        });

        let withdraw_from = Some(match withdraw_from {
            NativeWithdrawFrom::Sender => WithdrawFrom::Sender,
            NativeWithdrawFrom::Sponsor => WithdrawFrom::Sponsor,
        });

        let type_arg = type_arg
            .to_type_tag()
            .ok()
            .map(|tag| MoveType::from_native(tag, scope));

        Self {
            reservation,
            type_arg,
            withdraw_from,
        }
    }
}


Review comment (bug):
The potential error here comes from the fact that the type could be malformed on input. In that case, we don't want to hide the type (making it seem like no type was provided), we want to preserve it, and wrap it in `0x2::balance::Balance<...>`, but still represent it as a `TypeInput`. The means:

- Adding a function: `WithdrawalTypeArg::to_type_input(&self) -> TypeInput`,
- Probably also adding a function `Balance::type_input(type_param: TypeInput) -> TypeInput`, to support it.
- Wrapping it using `MoveType::from_native`.

This will allow the RPC to still show a representation of the type, (but not query it further).

Code after:
    Sponsor,
}

impl BalanceWithdraw {
    pub fn from_native(withdrawal: NativeFundsWithdrawalArg, scope: Scope) -> Self {
        let NativeFundsWithdrawalArg {
            reservation,
            type_arg,
            withdraw_from,
        } = withdrawal;

        let reservation = Some(match reservation {
            NativeReservation::MaxAmountU64(amount) => {
                WithdrawalReservation::MaxAmountU64(WithdrawMaxAmountU64 {
                    amount: Some(amount.into()),
                })
            }
        });

        let withdraw_from = Some(match withdraw_from {
            NativeWithdrawFrom::Sender => WithdrawFrom::Sender,
            NativeWithdrawFrom::Sponsor => WithdrawFrom::Sponsor,
        });

        let type_ = {
            let NativeWithdrawalTypeArg::Balance(t) = type_arg;
            let balance_struct = StructInput {
                address: AccountAddress::TWO,
                module: "balance".to_string(),
                name: "Balance".to_string(),
                type_params: vec![t.into()],
            };
            Some(MoveType::from_input(
                TypeInput::Struct(Box::new(balance_struct)),
                scope,
            ))
        };

        Self {
            reservation,
            type_,
            withdraw_from,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use async_graphql::ScalarType;