diff --git a/package.json b/package.json index c69fbe91..47b2fdaf 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "rimraf": "^2.6.2", "styled-components": "^4.1.2", "styled-normalize": "^8.0.4", - "trezor-connect": "7.0.0-beta.1", + "trezor-connect": "7.0.0-beta.2", "wallet-address-validator": "^0.2.4", "web3": "1.0.0-beta.35", "webpack": "^4.16.3", diff --git a/src/actions/ripple/BlockchainActions.js b/src/actions/ripple/BlockchainActions.js index fbea7721..72d898e3 100644 --- a/src/actions/ripple/BlockchainActions.js +++ b/src/actions/ripple/BlockchainActions.js @@ -118,7 +118,7 @@ export const onNotification = (payload: $ElementType (dispatch: Dispatch, ge }); }; +/* +* Called from UI on "advanced / destination tag" field change +*/ +export const onDestinationTagChange = (destinationTag: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + const state: State = getState().sendFormRipple; + dispatch({ + type: SEND.CHANGE, + networkType: 'ripple', + state: { + ...state, + untouched: false, + touched: { ...state.touched, destinationTag: true }, + destinationTag, + }, + }); +}; + /* * Called from UI from "send" button */ @@ -262,7 +279,13 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge if (!blockchain) return; const currentState: State = getState().sendFormRipple; - const amount = fromDecimalAmount(currentState.amount, 6); + const payment: { amount: string, destination: string, destinationTag?: number } = { + amount: fromDecimalAmount(currentState.amount, network.decimals), + destination: currentState.address, + }; + if (currentState.destinationTag.length > 0) { + payment.destinationTag = parseInt(currentState.destinationTag, 10); + } const signedTransaction = await TrezorConnect.rippleSignTransaction({ device: { @@ -276,10 +299,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge fee: currentState.selectedFeeLevel.fee, // Fee must be in the range of 10 to 10,000 drops flags: 0x80000000, sequence: account.sequence, - payment: { - amount, - destination: currentState.address, - }, + payment, }, }); @@ -346,5 +366,6 @@ export default { onFeeLevelChange, updateFeeLevels, onFeeChange, + onDestinationTagChange, onSend, }; \ No newline at end of file diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index 1e529763..70ff0c54 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -73,6 +73,7 @@ export const validation = (): PayloadAction => (dispatch: Dispatch, getSt state = dispatch(addressLabel(state)); state = dispatch(amountValidation(state)); state = dispatch(feeValidation(state)); + state = dispatch(destinationTagValidation(state)); return state; }; @@ -229,6 +230,19 @@ export const feeValidation = ($state: State): PayloadAction => (dispatch: } return state; }; +/* +* Destination Tag value validation +*/ +export const destinationTagValidation = ($state: State): PayloadAction => (): State => { + const state = { ...$state }; + if (!state.touched.destinationTag) return state; + + const { destinationTag } = state; + if (destinationTag.length > 0 && !destinationTag.match(ABS_RE)) { + state.errors.destinationTag = 'Destination tag must be an absolute number'; + } + return state; +}; /* diff --git a/src/reducers/SendFormRippleReducer.js b/src/reducers/SendFormRippleReducer.js index eaedcfa1..f56083bc 100644 --- a/src/reducers/SendFormRippleReducer.js +++ b/src/reducers/SendFormRippleReducer.js @@ -27,6 +27,7 @@ export type State = { fee: string; feeNeedsUpdate: boolean; sequence: string; + destinationTag: string; total: string; errors: {[k: string]: string}; @@ -56,6 +57,7 @@ export const initialState: State = { fee: '0', feeNeedsUpdate: false, sequence: '0', + destinationTag: '', total: '0', errors: {}, diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index 4bab40c1..fc09c7b9 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -95,6 +95,7 @@ export const getPendingSequence = (pending: Array): number => pendi }, 0); export const getPendingAmount = (pending: Array, currency: string, token: boolean = false): BigNumber => pending.reduce((value: BigNumber, tx: Transaction): BigNumber => { + if (tx.type !== 'send') return value; if (!token) { // regular transactions // add fees from token txs and amount from regular txs diff --git a/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js b/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js index 578b0d2c..5f9af898 100644 --- a/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js +++ b/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js @@ -35,7 +35,7 @@ const AdvancedSettingsWrapper = styled.div` border-top: 1px solid ${colors.DIVIDER}; `; -const GasInputRow = styled.div` +const InputRow = styled.div` width: 100%; display: flex; @@ -44,7 +44,7 @@ const GasInputRow = styled.div` } `; -const GasInput = styled(Input)` +const StyledInput = styled(Input)` /* min-height: 85px; */ padding-bottom: 28px; &:first-child { @@ -75,6 +75,17 @@ const getFeeInputState = (feeErrors: string, feeWarnings: string): string => { return state; }; +const getDestinationTagInputState = (errors: string, warnings: string): string => { + let state = ''; + if (warnings && !errors) { + state = 'warning'; + } + if (errors) { + state = 'error'; + } + return state; +}; + const Left = styled.div` display: flex; align-items: center; @@ -91,15 +102,17 @@ const AdvancedForm = (props: Props) => { warnings, infos, fee, + destinationTag, } = props.sendForm; const { onFeeChange, + onDestinationTagChange, } = props.sendFormActions; return ( - - + { Transfer cost in XRP drops )} - maxWidth={410} + maxWidth={100} readMoreLink="https://developers.ripple.com/transaction-cost.html" placement="top" > @@ -132,7 +145,43 @@ const AdvancedForm = (props: Props) => { value={fee} onChange={event => onFeeChange(event.target.value)} /> - + + + + + + Destination tag + + An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account. + + )} + maxWidth={200} + readMoreLink="https://developers.ripple.com/rippleapi-reference.html#payment" + placement="top" + > + + + + + )} + bottomText={errors.destinationTag || warnings.destinationTag || infos.destinationTag} + value={destinationTag} + onChange={event => onDestinationTagChange(event.target.value)} + /> + { props.children } diff --git a/yarn.lock b/yarn.lock index f203161f..7df2acf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9790,9 +9790,9 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -trezor-connect@7.0.0-beta.1: - version "7.0.0-beta.1" - resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-7.0.0-beta.1.tgz#de87d8f1d9878101380f7b3198bf2531d7560a5c" +trezor-connect@7.0.0-beta.2: + version "7.0.0-beta.2" + resolved "https://registry.yarnpkg.com/trezor-connect/-/trezor-connect-7.0.0-beta.2.tgz#10dc04e1b60804263e9873cd503a4fc9d3e940d2" dependencies: babel-runtime "^6.26.0" events "^1.1.1"