Merge pull request #694 from lumberj/add-deliveredAmount

Add deliveredAmount to payment outcome
This commit is contained in:
Chris Clark
2016-02-12 11:25:01 -08:00
9 changed files with 75 additions and 15 deletions

View File

@@ -828,6 +828,7 @@ outcome | object | The outcome of the transaction (what effects it had).
*outcome.orderbookChanges.\*[].* makerExchangeRate | [value](#value) | *Optional* The exchange rate between the `quantity` currency and the `totalPrice` currency from the point of view of the maker.
*outcome.* ledgerVersion | integer | The ledger version that the transaction was validated in.
*outcome.* indexInLedger | integer | The ordering index of the transaction in the ledger.
*outcome.* deliveredAmount | [amount](#amount) | *Optional* For payment transactions, it is impossible to reliably compute the actual delivered amount from the balanceChanges due to fixed precision. If the payment is not a partial payment and the transaction succeeded, the deliveredAmount should always be considered to be the amount specified in the transaction.
*outcome.* timestamp | date-time string | *Optional* The timestamp when the transaction was validated. (May be missing when requesting transactions in binary mode.)
### Example
@@ -867,6 +868,11 @@ return api.getTransaction(id).then(transaction => {
"result": "tesSUCCESS",
"timestamp": "2013-03-12T23:56:50.000Z",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{
@@ -1001,6 +1007,11 @@ return api.getTransactions(address).then(transaction => {
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{
@@ -1093,6 +1104,11 @@ return api.getTransactions(address).then(transaction => {
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "amount",
"link": "amount",
"description": "An Amount on the Ripple Protocol, used also for XRP in the ripple-rest API",
"description": "An Amount on the Ripple Protocol",
"allOf": [
{"$ref": "amountbase"},
{"required": ["value"]}

View File

@@ -17,6 +17,10 @@
"$ref": "value",
"description": "The XRP fee that was charged for the transaction."
},
"deliveredAmount": {
"$ref": "amount",
"description": "For payment transactions, it is impossible to reliably compute the actual delivered amount from the balanceChanges due to fixed precision. If the payment is not a partial payment and the transaction succeeded, the deliveredAmount should always be considered to be the amount specified in the transaction."
},
"balanceChanges": {
"type": "object",
"additionalProperties": {

View File

@@ -1,6 +1,6 @@
/* @flow */
'use strict';
const utils = require('./utils');
const utils = require('../utils');
import type {Amount, RippledAmount} from '../../common/types.js';
@@ -8,7 +8,7 @@ function parseAmount(amount: RippledAmount): Amount {
if (typeof amount === 'string') {
return {
currency: 'XRP',
value: utils.dropsToXrp(amount)
value: utils.common.dropsToXrp(amount)
};
}
return {

View File

@@ -6,10 +6,6 @@ const utils = require('./utils');
const parseAmount = require('./amount');
const txFlags = utils.txFlags;
function isPartialPayment(tx) {
return (tx.Flags & txFlags.Payment.PartialPayment) !== 0;
}
function isNoDirectRipple(tx) {
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
}
@@ -45,7 +41,7 @@ function parsePayment(tx: Object): Object {
memos: utils.parseMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
noDirectRipple: isNoDirectRipple(tx) || undefined,
limitQuality: isQualityLimited(tx) || undefined
});

View File

@@ -4,6 +4,9 @@ const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const utils = require('../utils');
const BigNumber = require('bignumber.js');
const parseAmount = require('./amount');
import type {Amount} from '../common/types.js';
function adjustQualityForXRP(
quality: string, takerGetsCurrency: string, takerPaysCurrency: string
@@ -35,19 +38,39 @@ function removeEmptyCounterparty(amount) {
}
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
_.forEach(balanceChanges, (changes) => {
_.forEach(balanceChanges, changes => {
_.forEach(changes, removeEmptyCounterparty);
});
}
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
_.forEach(orderbookChanges, (changes) => {
_.forEach(changes, (change) => {
_.forEach(orderbookChanges, changes => {
_.forEach(changes, change => {
_.forEach(change, removeEmptyCounterparty);
});
});
}
function isPartialPayment(tx) {
return (tx.Flags & utils.common.txFlags.Payment.PartialPayment) !== 0;
}
function parseDeliveredAmount(tx: Object): Amount | void {
let deliveredAmount;
// TODO: Workaround for existing rippled bug where delivered_amount may not be
// provided for account_tx
if (tx.TransactionType === 'Payment') {
if (tx.meta.delivered_amount) {
deliveredAmount = parseAmount(tx.meta.delivered_amount);
} else if (tx.Amount && !isPartialPayment(tx)) {
deliveredAmount = parseAmount(tx.Amount);
}
}
return deliveredAmount;
}
function parseOutcome(tx: Object): ?Object {
const metadata = tx.meta || tx.metaData;
if (!metadata) {
@@ -58,15 +81,16 @@ function parseOutcome(tx: Object): ?Object {
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
return {
return utils.common.removeUndefined({
result: tx.meta.TransactionResult,
timestamp: parseTimestamp(tx.date),
fee: utils.common.dropsToXrp(tx.Fee),
balanceChanges: balanceChanges,
orderbookChanges: orderbookChanges,
ledgerVersion: tx.ledger_index,
indexInLedger: tx.meta.TransactionIndex
};
indexInLedger: tx.meta.TransactionIndex,
deliveredAmount: parseDeliveredAmount(tx)
});
}
function hexToString(hex: string): ?string {
@@ -77,7 +101,7 @@ function parseMemos(tx: Object): ?Array<Object> {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return tx.Memos.map(m => {
return utils.common.removeUndefined({
type: m.Memo.parsed_memo_type || hexToString(m.Memo.MemoType),
format: m.Memo.parsed_memo_format || hexToString(m.Memo.MemoFormat),
@@ -93,6 +117,7 @@ module.exports = {
hexToString,
parseTimestamp,
adjustQualityForXRP,
isPartialPayment,
dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants,
txFlags: utils.common.txFlags,

View File

@@ -34,6 +34,10 @@
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"deliveredAmount": {
"currency": "XRP",
"value": "10000"
},
"balanceChanges": {
"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj": [
{

View File

@@ -24,6 +24,11 @@
"result": "tesSUCCESS",
"timestamp": "2013-03-12T23:56:50.000Z",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{

View File

@@ -30,6 +30,11 @@
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{
@@ -122,6 +127,11 @@
"outcome": {
"result": "tesSUCCESS",
"fee": "0.00001",
"deliveredAmount": {
"currency": "USD",
"value": "0.001",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
},
"balanceChanges": {
"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo": [
{