Skip to content

Commit

Permalink
feat: do fee estimation in UI transfer (#826)
Browse files Browse the repository at this point in the history
Description
---
Add fee estimation to transfer in the UI.
The way it works, the fee field is readonly, instead of "send tari"
there is a "Estimate Fee", once you click it, it will calculate the fees
and populate the fee field and the button changes to "Send", if you
change anything, it will go back to "Estimate Fee".
The problem is that we have to enter the maximum fee for estimation. If
user don't have enough money (the amount for transfer+the maximum fee
for calculation) that's problem. In current state the maximum fee is set
to 1000. So if it's going to be more, the estimation.

Motivation and Context
---

How Has This Been Tested?
---
Manually.

What process can a PR reviewer use to test or verify this change?
---
Go to the send dialog in the wallet ui.


Breaking Changes
---

- [x] None
- [ ] Requires data directory to be deleted
- [ ] Other - Please specify
  • Loading branch information
Cifko authored Dec 14, 2023
1 parent ca80982 commit 93bfd45
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 36 deletions.
2 changes: 2 additions & 0 deletions applications/tari_dan_wallet_cli/src/command/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ pub async fn handle_send(args: SendArgs, client: &mut WalletDaemonClient) -> Res
resource_address,
destination_public_key,
max_fee: fee,
dry_run: false,
})
.await?;

Expand Down Expand Up @@ -365,6 +366,7 @@ pub async fn handle_confidential_transfer(
resource_address: resource_address.unwrap_or(CONFIDENTIAL_TARI_RESOURCE_ADDRESS),
destination_public_key,
max_fee: common.max_fee.map(|f| f.try_into()).transpose()?,
dry_run: false,
})
.await?;

Expand Down
69 changes: 55 additions & 14 deletions applications/tari_dan_wallet_daemon/src/handlers/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ pub async fn handle_reveal_funds(

let (inputs, input_value) =
sdk.confidential_outputs_api()
.lock_outputs_by_amount(&vault.address, amount_to_reveal, proof_id)?;
.lock_outputs_by_amount(&vault.address, amount_to_reveal, proof_id, false)?;
let input_amount = Amount::try_from(input_value)?;

let account_key = sdk
Expand Down Expand Up @@ -925,6 +925,32 @@ pub async fn handle_transfer(

// send the transaction
let required_inputs = inputs.into_iter().map(Into::into).collect();
if req.dry_run {
let result = sdk
.transaction_api()
.submit_dry_run_transaction(transaction, required_inputs)
.await?;
let execute_result = result.result.into_execute_result().unwrap();
return Ok(TransferResponse {
transaction_id: result.transaction_id,
fee: execute_result
.fee_receipt
.clone()
.map(|fee_receipt| fee_receipt.total_fees_paid)
.unwrap_or_default(),
fee_refunded: execute_result
.fee_receipt
.clone()
.map(|fee_receipt| fee_receipt.total_fee_payment)
.unwrap_or_default() -
execute_result
.fee_receipt
.clone()
.map(|fee_receipt| fee_receipt.total_fees_paid)
.unwrap_or_default(),
result: execute_result.finalize,
});
}
let tx_id = sdk
.transaction_api()
.submit_transaction(transaction, required_inputs)
Expand Down Expand Up @@ -1059,7 +1085,7 @@ pub async fn handle_confidential_transfer(
let total_amount = req.max_fee.unwrap_or(DEFAULT_FEE) + req.amount;
let proof_id = outputs_api.add_proof(&src_vault.address)?;
let (confidential_inputs, total_input_value) =
outputs_api.lock_outputs_by_amount(&src_vault.address, total_amount, proof_id)?;
outputs_api.lock_outputs_by_amount(&src_vault.address, total_amount, proof_id, req.dry_run)?;

let output_mask = sdk.key_manager_api().next_key(key_manager::TRANSACTION_BRANCH)?;
let (nonce, public_nonce) = PublicKey::random_keypair(&mut OsRng);
Expand Down Expand Up @@ -1092,18 +1118,20 @@ pub async fn handle_confidential_transfer(
&account_secret.key,
)?;

outputs_api.add_output(ConfidentialOutputModel {
account_address: account.address,
vault_address: src_vault.address,
commitment: get_commitment_factory().commit_value(&change_mask.key, change_amount),
value: change_amount,
sender_public_nonce: Some(public_nonce.clone()),
encryption_secret_key_index: account_secret.key_index,
encrypted_data: encrypted_data.clone(),
public_asset_tag: None,
status: OutputStatus::LockedUnconfirmed,
locked_by_proof: Some(proof_id),
})?;
if !req.dry_run {
outputs_api.add_output(ConfidentialOutputModel {
account_address: account.address,
vault_address: src_vault.address,
commitment: get_commitment_factory().commit_value(&change_mask.key, change_amount),
value: change_amount,
sender_public_nonce: Some(public_nonce.clone()),
encryption_secret_key_index: account_secret.key_index,
encrypted_data: encrypted_data.clone(),
public_asset_tag: None,
status: OutputStatus::LockedUnconfirmed,
locked_by_proof: Some(proof_id),
})?;
}

Some(ConfidentialProofStatement {
amount: change_amount.try_into()?,
Expand Down Expand Up @@ -1148,6 +1176,19 @@ pub async fn handle_confidential_transfer(
.sign(&account_secret.key)
.build();

if req.dry_run {
let result = sdk
.transaction_api()
.submit_dry_run_transaction(transaction, inputs.into_iter().map(Into::into).collect())
.await?;
let execute_result = result.result.into_execute_result().unwrap();
return Ok(ConfidentialTransferResponse {
transaction_id: result.transaction_id,
fee: execute_result.fee_receipt.clone().unwrap().total_fees_paid,
result: execute_result.finalize,
});
}

outputs_api.proofs_set_transaction_hash(proof_id, *transaction.id())?;

let tx_id = sdk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub async fn handle_create_transfer_proof(
&vault.address,
req.amount + req.reveal_amount,
proof_id,
false,
)?;

info!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ export const useAccountsTransfer = (
resource_address: string,
destination_public_key: string,
max_fee: number | null,
confidential: boolean
confidential: boolean,
dry_run: boolean
) => {
return useMutation(
() =>
Expand All @@ -88,6 +89,7 @@ export const useAccountsTransfer = (
resource_address,
destination_public_key,
max_fee,
dry_run
}),
{
onError: (error: apiError) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ import useAccountStore from "../../../store/accountStore";
export default function SendMoney() {
const [open, setOpen] = useState(false);
const [disabled, setDisabled] = useState(false);
const [estimatedFee, setEstimatedFee] = useState(0);
const [transferFormState, setTransferFormState] = useState({
publicKey: "",
confidential: false,
amount: "",
fee: "",
});

const { accountName, setPopup } = useAccountStore();
Expand All @@ -53,8 +53,19 @@ export default function SendMoney() {
parseInt(transferFormState.amount),
"resource_0101010101010101010101010101010101010101010101010101010101010101",
transferFormState.publicKey,
parseInt(transferFormState.fee),
transferFormState.confidential
estimatedFee,
transferFormState.confidential,
false
);

const { mutateAsync: calculateFeeEstimate } = useAccountsTransfer(
accountName,
parseInt(transferFormState.amount),
"resource_0101010101010101010101010101010101010101010101010101010101010101",
transferFormState.publicKey,
1000,
transferFormState.confidential,
true
);

const onPublicKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -64,13 +75,15 @@ export default function SendMoney() {
[e.target.name]: e.target.value,
});
}
setEstimatedFee(0);
};

const onConfidentialChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTransferFormState({
...transferFormState,
[e.target.name]: e.target.checked,
});
setEstimatedFee(0);
};

const onNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -80,20 +93,27 @@ export default function SendMoney() {
[e.target.name]: e.target.value,
});
}
setEstimatedFee(0);
};

const onTransfer = async () => {
if (accountName) {
setDisabled(true);
sendIt().then(() => {
setTransferFormState({ publicKey: "", confidential: false, amount: "", fee: "" });
setOpen(false);
setPopup({ title: "Send successful", error: false });
}).catch((e) => {
setPopup({ title: "Send failed", error: true, message: e.message });
}).finally(() => {
if (estimatedFee) {
sendIt().then(() => {
setTransferFormState({ publicKey: "", confidential: false, amount: "" });
setOpen(false);
setPopup({ title: "Send successful", error: false });
}).catch((e) => {
setPopup({ title: "Send failed", error: true, message: e.message });
}).finally(() => {
setDisabled(false);
});
} else {
let result = await calculateFeeEstimate();
setEstimatedFee(result.fee);
setDisabled(false);
});
}
}
};

Expand Down Expand Up @@ -144,10 +164,10 @@ export default function SendMoney() {
<TextField
name="fee"
label="Fee"
value={transferFormState.fee}
onChange={onNumberChange}
value={estimatedFee || "Press fee estimate to calculate"}
style={{ flexGrow: 1 }}
disabled={disabled}
InputProps={{ readOnly: true }}
/>
<Box
className="flex-container"
Expand All @@ -159,12 +179,12 @@ export default function SendMoney() {
Cancel
</Button>
<Button variant="contained" type="submit" disabled={disabled}>
Send Tari
{estimatedFee ? "Send" : "Estimate fee"}
</Button>
</Box>
</Form>
</DialogContent>
</Dialog>
</div>
</Form >
</DialogContent >
</Dialog >
</div >
);
}
2 changes: 2 additions & 0 deletions clients/wallet_daemon_client/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ pub struct TransferRequest {
pub resource_address: ResourceAddress,
pub destination_public_key: PublicKey,
pub max_fee: Option<Amount>,
pub dry_run: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
Expand Down Expand Up @@ -375,6 +376,7 @@ pub struct ConfidentialTransferRequest {
pub resource_address: ResourceAddress,
pub destination_public_key: PublicKey,
pub max_fee: Option<Amount>,
pub dry_run: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
Expand Down
7 changes: 6 additions & 1 deletion dan_layer/wallet/sdk/src/apis/confidential_outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl<'a, TStore: WalletStore> ConfidentialOutputsApi<'a, TStore> {
vault_address: &SubstateAddress,
amount: Amount,
locked_by_proof_id: ConfidentialProofId,
dry_run: bool,
) -> Result<(Vec<ConfidentialOutputModel>, u64), ConfidentialOutputsApiError> {
if amount.is_negative() {
return Err(ConfidentialOutputsApiError::InvalidParameter {
Expand All @@ -76,7 +77,11 @@ impl<'a, TStore: WalletStore> ConfidentialOutputsApi<'a, TStore> {
},
}
}
tx.commit()?;
if dry_run {
tx.rollback()?;
} else {
tx.commit()?;
}
Ok((outputs, total_output_amount))
}

Expand Down
4 changes: 2 additions & 2 deletions dan_layer/wallet/sdk/tests/confidential_output_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn outputs_locked_and_released() {
let (inputs, total_value) = test
.sdk()
.confidential_outputs_api()
.lock_outputs_by_amount(&Test::test_vault_address(), Amount(50), proof_id)
.lock_outputs_by_amount(&Test::test_vault_address(), Amount(50), proof_id, false)
.unwrap();
assert_eq!(total_value, 74);
assert_eq!(inputs.len(), 2);
Expand Down Expand Up @@ -74,7 +74,7 @@ fn outputs_locked_and_finalized() {
let proof_id = test.new_proof();

let (inputs, total_value) = outputs_api
.lock_outputs_by_amount(&Test::test_vault_address(), Amount(50), proof_id)
.lock_outputs_by_amount(&Test::test_vault_address(), Amount(50), proof_id, false)
.unwrap();
assert_eq!(total_value, 74);
assert_eq!(inputs.len(), 2);
Expand Down
2 changes: 2 additions & 0 deletions integration_tests/src/wallet_daemon_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ pub async fn transfer(
resource_address,
destination_public_key,
max_fee,
dry_run: false,
};

let resp = client.accounts_transfer(request).await.unwrap();
Expand All @@ -696,6 +697,7 @@ pub async fn confidential_transfer(
destination_public_key,
max_fee,
resource_address: CONFIDENTIAL_TARI_RESOURCE_ADDRESS,
dry_run: false,
};

let resp = client.accounts_confidential_transfer(request).await.unwrap();
Expand Down

0 comments on commit 93bfd45

Please sign in to comment.