Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2696. use fee selection feature on the transaction confirmation #3001

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions app/components/Blockchain/Transaction.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import moment from "moment";
import {Link, DirectLink} from "react-scroll";
import {Tooltip} from "bitshares-ui-style-guide";
import JSONModal from "components/Modal/JSONModal";
import SetDefaultFeeAssetModal from "components/Modal/SetDefaultFeeAssetModal";
import asset_utils from "../../lib/common/asset_utils";
import sanitize from "sanitize";
import {convertFee} from "common/trxHelper";

require("./operations.scss");
require("./json-inspector.scss");
Expand Down Expand Up @@ -80,11 +82,19 @@ class NoLinkDecorator extends React.Component {
}

class OperationTable extends React.Component {
static propTypes = {
onChangeFeeAsset: PropTypes.oneOfType([PropTypes.bool, PropTypes.func])
.isRequired
};

constructor(props) {
super(props);

this.state = {
visible: false
visible: false,
showFeeModal: false,
currentFeeAsset: props.operation[1].fee.asset_id || "1.3.0",
feeAmount: props.operation[1].fee.amount || 0
};
}

Expand All @@ -96,8 +106,22 @@ class OperationTable extends React.Component {
this.setState({visible: false});
};

openFeeAssetModal = () => {
this.setState({showFeeModal: true});
};

async onChangeCurrentFeeAsset(value) {
const { operation, onChangeFeeAsset } = this.props;
const type = ops[operation[0]];
const fee = await convertFee({type, feeID: value});
if (fee) {
this.setState({ feeAmount: fee.amount, currentFeeAsset: fee.id });
onChangeFeeAsset(value, fee.amount, this.props.index);
}
}

render() {
const {operation} = this.props;
const { operation, onChangeFeeAsset } = this.props;
let fee_row = (
<tr>
<td>
Expand All @@ -108,14 +132,28 @@ class OperationTable extends React.Component {
<span>
<FormattedAsset
color="fee"
amount={operation[1].fee.amount}
asset={operation[1].fee.asset_id}
amount={this.state.feeAmount}
asset={this.state.currentFeeAsset}
style={{marginRight: "10px"}}
/>
&nbsp;&nbsp;
<Icon
{onChangeFeeAsset ? <AntIcon
type="down-square"
onClick={this.openFeeAssetModal}
/> : <Icon
name="question-circle"
title="settings.can_change_default_fee_asset_tooltip"
/>}
<SetDefaultFeeAssetModal
key="change_fee_asset_confirmation_modal"
className="modal"
show={this.state.showFeeModal}
current_asset={this.state.currentFeeAsset}
onChange={val => this.onChangeCurrentFeeAsset(val)}
close={() => {
this.setState({showFeeModal: false});
}}
withoutCheckbox
/>
</span>
) : (
Expand Down Expand Up @@ -2410,6 +2448,7 @@ class Transaction extends React.Component {
index={opIndex}
color={color}
operation={op}
onChangeFeeAsset={this.props.onChangeFeeAsset}
>
{rows}
</OperationTable>
Expand All @@ -2421,13 +2460,15 @@ class Transaction extends React.Component {
}

Transaction.defaultProps = {
no_links: false
no_links: false,
onChangeFeeAsset: false
};

Transaction.propTypes = {
trx: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
no_links: PropTypes.bool
no_links: PropTypes.bool,
onChangeFeeAsset: PropTypes.func
};

export default Transaction;
50 changes: 46 additions & 4 deletions app/components/Blockchain/TransactionConfirm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class TransactionConfirm extends React.Component {
super(props);

this.state = {
isModalVisible: false
isModalVisible: false,
feeAssets: []
};

this.onCloseClick = this.onCloseClick.bind(this);
Expand Down Expand Up @@ -69,24 +70,45 @@ class TransactionConfirm extends React.Component {
else e.preventDefault();
}

changeTransactionOperations() {
const { feeAssets } = this.state;
const { transaction } = this.props;
let changedOperations;
if (feeAssets.length) {
const { operations } = transaction;
changedOperations = operations.map((item, index) => {
feeAssets.forEach(fee => {
if (fee.opIndex === index) {
item[1].fee.asset_id = fee.id;
item[1].fee.amount = fee.amount;
}
});
return item;
});
transaction.operations = changedOperations;
}
return transaction;
}

onConfirmClick(e) {
e.preventDefault();
const transaction = this.changeTransactionOperations();
if (this.props.propose) {
const propose_options = {
fee_paying_account: ChainStore.getAccount(
this.props.fee_paying_account
).get("id")
};
this.props.transaction.update_head_block().then(() => {
transaction.update_head_block().then(() => {
WalletDb.process_transaction(
this.props.transaction.propose(propose_options),
transaction.propose(propose_options),
null,
true
);
});
} else {
TransactionConfirmActions.broadcast(
this.props.transaction,
transaction,
this.props.resolve,
this.props.reject
);
Expand All @@ -107,6 +129,25 @@ class TransactionConfirm extends React.Component {
TransactionConfirmActions.proposeFeePayingAccount(fee_paying_account);
}

onChangeFeeAsset = (assetId, amount, index) => {
const { feeAssets } = this.state;
let feeAssetsChanged = [];
const feeAssetsOperations = feeAssets.map(item => item.opIndex);
if (feeAssets.length && feeAssetsOperations.indexOf(index) !== -1) {
feeAssetsChanged = feeAssets.map(item => {
if (item.opIndex === index) {
item.id = assetId;
item.amount = amount;
}
return item;
});
} else {
feeAssetsChanged = [...feeAssets, {opIndex: index, id: assetId, amount}];
}

this.setState({ feeAssets: feeAssetsChanged });
};

componentWillReceiveProps(np) {
if (np.broadcast && np.included && !this.props.included && !np.error) {
notify.addNotification.defer({
Expand Down Expand Up @@ -251,6 +292,7 @@ class TransactionConfirm extends React.Component {
trx={this.props.transaction.serialize()}
index={0}
no_links={true}
onChangeFeeAsset={this.onChangeFeeAsset}
/>
</div>

Expand Down
33 changes: 18 additions & 15 deletions app/components/Modal/SetDefaultFeeAssetModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class SetDefaultFeeAssetModal extends React.Component {
}

_getAssetsRows(assets) {
return assets.filter(item => !!item).map(assetInfo => ({
return assets.filter(item => !!item && item.asset).map(assetInfo => ({
id: assetInfo.asset.get("id"),
key: assetInfo.asset.get("id"),
asset: assetInfo.asset.get("symbol"),
Expand Down Expand Up @@ -189,18 +189,19 @@ class SetDefaultFeeAssetModal extends React.Component {
dataSource={dataSource}
footer={null}
/>

<Checkbox
onClick={this._setSelectedAssetAsDefault.bind(this)}
disabled={this.props.forceDefault}
checked={this.state.useByDefault}
style={{paddingTop: "30px"}}
>
<Translate
component="span"
content="explorer.asset.fee_pool.use_asset_as_default_fee"
/>
</Checkbox>
{this.props.withoutCheckbox ? null : (
<Checkbox
onClick={this._setSelectedAssetAsDefault.bind(this)}
disabled={this.props.forceDefault}
checked={this.state.useByDefault}
style={{paddingTop: "30px"}}
>
<Translate
component="span"
content="explorer.asset.fee_pool.use_asset_as_default_fee"
/>
</Checkbox>
)}
</Modal>
);
}
Expand All @@ -225,7 +226,8 @@ SetDefaultFeeAssetModal.propTypes = {
onChange: PropTypes.func,
// tells if modal is visible or not
show: PropTypes.bool,
close: PropTypes.func
close: PropTypes.func,
withoutCheckbox: PropTypes.bool
};

SetDefaultFeeAssetModal.defaultProps = {
Expand All @@ -234,7 +236,8 @@ SetDefaultFeeAssetModal.defaultProps = {
displayFees: false,
forceDefault: false,
current_asset: "1.3.0",
show: false
show: false,
withoutCheckbox: false
};

SetDefaultFeeAssetModal = connect(
Expand Down
53 changes: 52 additions & 1 deletion app/lib/common/trxHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,56 @@ function checkFeeStatusAsync({
});
}

function convertFee({ feeID = "1.3.0", type = "transfer" } = {}) {
return new Promise((res, rej) => {
Promise.all([
estimateFeeAsync(type),
FetchChain("getAsset", "1.3.0"),
feeID !== "1.3.0" ? FetchChain("getAsset", feeID) : null
])
.then(result => {
let [
coreFee,
coreAsset,
feeAsset
] = result;
if (feeID === "1.3.0") feeAsset = coreAsset;
let fee = new Asset({amount: coreFee});

if (feeID !== "1.3.0") {
// Convert the amount using the CER
let cer = feeAsset.getIn([
"options",
"core_exchange_rate"
]);
let b = cer.get("base").toJS();
b.precision =
b.asset_id === feeID
? feeAsset.get("precision")
: coreAsset.get("precision");
let base = new Asset(b);
let q = cer.get("quote").toJS();
q.precision =
q.asset_id === feeID
? feeAsset.get("precision")
: coreAsset.get("precision");
let quote = new Asset(q);
/*
** If the CER is incorrectly configured, the multiplication
** will fail, so catch the error and default to core
*/
try {
let price = new Price({base, quote});
res({ amount: fee.times(price, true).getAmount(), id: feeID });
} catch(err) {
res({ amount: coreFee, id: "1.3.0" });
}
}
})
.catch(rej);
});
}

const privKey = "5KikQ23YhcM7jdfHbFBQg1G7Do5y6SgD9sdBZq7BqQWXmNH7gqo";
const nonce = TransactionHelper.unique_nonce_uint64();
let _privKey;
Expand Down Expand Up @@ -352,5 +402,6 @@ export {
checkFeePoolAsync,
checkFeeStatusAsync,
checkBalance,
shouldPayFeeWithAssetAsync
shouldPayFeeWithAssetAsync,
convertFee
};