Compare commits

...

24 Commits

Author SHA1 Message Date
Matthew Fettig
b5564330f6 Merge pull request #755 from ripple/develop
0.17.6
2017-03-30 21:49:59 -07:00
Matthew Fettig
43ccb83f73 0.17.6 2017-03-30 21:33:09 -07:00
Brandon Wilson
e2d92126c0 Merge pull request #745 from ripple/feat/bs-paychan
Add transactions for payment channels
2017-03-30 21:26:13 -07:00
Ben Sharafian
8c8eef45d5 Add docs for payment channels 2017-03-30 21:19:31 -07:00
Ben Sharafian
a574e1158a Add transactions for payment channels 2017-03-30 21:19:31 -07:00
Matthew Fettig
af687a6aed Merge pull request #754 from ripple/escrowcreate
Simplify EscrowCreate parameters
2017-03-30 21:18:33 -07:00
wilsonianb
016e82ab5d Simplify EscrowCreate parameters 2017-03-30 19:01:58 -07:00
Matthew Fettig
f300a412d7 Merge pull request #753 from ripple/feature/dependancy-update
update ripple-hashes and ripple-binary-codec
2017-03-30 15:21:27 -07:00
Matthew Fettig
75e2249cf3 update ripple-hashes and ripple-binary-codec 2017-03-30 15:12:09 -07:00
Matthew Fettig
1942bcb971 Merge pull request #752 from ripple/rm-escrow-iou
Disallow escrow of IOU
2017-03-30 14:53:29 -07:00
wilsonianb
9393adf66f Disallow escrow of IOU 2017-03-30 10:10:00 -07:00
Matthew Fettig
917aae9bf3 Merge pull request #751 from ripple/feature/escrow
Replace suspended payments with escrow
2017-03-29 17:38:32 -07:00
wilsonianb
da36457d5c Update condition and fulfillment for escrow
Calculate escrowFinish fulfillment fee
2017-03-29 10:27:34 -07:00
wilsonianb
15a0ededc8 Replace suspended payments with escrow 2017-03-24 12:35:22 -07:00
wilsonianb
68d7864f93 Rename suspended payment files as escrow 2017-03-24 12:32:17 -07:00
Matthew Fettig
1eddbf995f 0.17.5 2017-03-24 12:31:10 -07:00
Matthew Fettig
592385ac73 Merge pull request #750 from ripple/feature/dependancy-update
update ripple-binary-codec to version 0.1.8
2017-03-24 12:26:35 -07:00
Matthew Fettig
56d626f5b1 Merge pull request #744 from choiip/develop
Added .json in missing require()
2017-03-24 10:53:42 -07:00
Matthew Fettig
7a14300409 update ripple-binary-codec to version 0.1.8 2017-03-24 10:40:19 -07:00
alex.choi
30fa8d658e Added .json in missing require() 2017-02-15 18:51:49 +08:00
Chris Clark
c0101cb5e7 Merge pull request #740 from wilsonianb/shamapv2
Check SHAMapV2 ledger close flag
2016-12-02 12:31:46 -08:00
wilsonianb
fd640cd65e Check SHAMapV2 ledger close flag 2016-12-02 12:25:32 -08:00
Chris Clark
11528eff92 Merge pull request #741 from wilsonianb/fix-lint-errors
Fix eslint errors
2016-12-02 11:55:21 -08:00
wilsonianb
3c9175459d Fix eslint errors 2016-12-02 08:16:16 -08:00
180 changed files with 3506 additions and 2206 deletions

View File

@@ -30,7 +30,7 @@ Then see the [documentation](https://github.com/ripple/ripple-lib/blob/develop/d
##Generating Documentation
The continuous integration tests require that the documentation stays up-to-date. If you make changes the the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
The continuous integration tests require that the documentation stays up-to-date. If you make changes to the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
##More Information

View File

@@ -24,9 +24,12 @@
- [Order](#order)
- [Order Cancellation](#order-cancellation)
- [Settings](#settings)
- [Suspended Payment Creation](#suspended-payment-creation)
- [Suspended Payment Cancellation](#suspended-payment-cancellation)
- [Suspended Payment Execution](#suspended-payment-execution)
- [Escrow Creation](#escrow-creation)
- [Escrow Cancellation](#escrow-cancellation)
- [Escrow Execution](#escrow-execution)
- [Payment Channel Create](#payment-channel-create)
- [Payment Channel Fund](#payment-channel-fund)
- [Payment Channel Claim](#payment-channel-claim)
- [API Methods](#api-methods)
- [connect](#connect)
- [disconnect](#disconnect)
@@ -50,9 +53,12 @@
- [prepareOrder](#prepareorder)
- [prepareOrderCancellation](#prepareordercancellation)
- [prepareSettings](#preparesettings)
- [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
- [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
- [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
- [prepareEscrowCreation](#prepareescrowcreation)
- [prepareEscrowCancellation](#prepareescrowcancellation)
- [prepareEscrowExecution](#prepareescrowexecution)
- [preparePaymentChannelCreate](#preparepaymentchannelcreate)
- [preparePaymentChannelClaim](#preparepaymentchannelclaim)
- [preparePaymentChannelFund](#preparepaymentchannelfund)
- [sign](#sign)
- [combine](#combine)
- [submit](#submit)
@@ -180,9 +186,9 @@ Methods that depend on the state of the Ripple Consensus Ledger are unavailable
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
* [prepareEscrowCreation](#prepareescrowcreation)
* [prepareEscrowCancellation](#prepareescrowcancellation)
* [prepareEscrowExecution](#prepareescrowexecution)
* [sign](#sign)
* [generateAddress](#generateaddress)
* [computeLedgerHash](#computeledgerhash)
@@ -261,11 +267,11 @@ Type | Description
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
[settings](#settings) | A `settings` transaction modifies the settings of an account in the Ripple Consensus Ledger.
[suspendedPaymentCreation](#suspended-payment-creation) | A `suspendedPaymentCreation` transaction creates a suspended payment on the ledger, which locks XRP until a cryptographic condition is met or it expires. It is like an escrow service where the Ripple network acts as the escrow agent.
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
[escrowCreation](#escrow-creation) | An `escrowCreation` transaction creates an escrow on the ledger, which locks XRP until a cryptographic condition is met or it expires. It is like an escrow service where the Ripple network acts as the escrow agent.
[escrowCancellation](#escrow-cancellation) | An `escrowCancellation` transaction unlocks the funds in an escrow and sends them back to the creator of the escrow, but it will only work after the escrow expires.
[escrowExecution](#escrow-execution) | An `escrowExecution` transaction unlocks the funds in an escrow and sends them to the destination of the escrow, but it will only work if the cryptographic condition is provided.
The three "suspended payment" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
The three "escrow" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
## Transaction Flow
@@ -277,9 +283,9 @@ Executing a transaction with `RippleAPI` requires the following four steps:
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
* [prepareEscrowCreation](#prepareescrowcreation)
* [prepareEscrowCancellation](#prepareescrowcancellation)
* [prepareEscrowExecution](#prepareescrowexecution)
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
3. [Submit](#submit) - Submit the transaction to the connected server.
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.
@@ -518,59 +524,41 @@ transferRate | number,null | *Optional* The fee to charge when users transfer t
```
## Suspended Payment Creation
## Escrow Creation
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
source | object | Fields pertaining to the source of the payment.
*source.* address | [address](#ripple-address) | The address to send from.
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
destination | object | Fields pertaining to the destination of the payment.
*destination.* address | [address](#ripple-address) | The address to receive at.
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
allowCancelAfter | date-time string | *Optional* If present, the suspended payment may be cancelled after this time.
allowExecuteAfter | date-time string | *Optional* If present, the suspended payment can not be executed before this time.
digest | string | *Optional* If present, proof is required upon execution.
amount | [value](#value) | Amount of XRP for sender to escrow.
destination | [address](#ripple-address) | Address to receive escrowed XRP.
allowCancelAfter | date-time string | *Optional* If present, the escrow may be cancelled after this time.
allowExecuteAfter | date-time string | *Optional* If present, the escrow can not be executed before this time.
condition | string | *Optional* If present, fulfillment is required upon execution.
destinationTag | integer | *Optional* Destination tag.
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
sourceTag | integer | *Optional* Source tag.
### Example
```json
{
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"destination": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": "0.01",
"allowCancelAfter": "2014-09-24T21:21:50.000Z"
}
```
## Suspended Payment Cancellation
## Escrow Cancellation
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
owner | [address](#ripple-address) | The address of the owner of the suspended payment to cancel.
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to cancel.
owner | [address](#ripple-address) | The address of the owner of the escrow to cancel.
escrowSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Escrow Creation](#escrow-creation) transaction for the escrow to cancel.
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
### Example
@@ -579,23 +567,22 @@ memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the
```json
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"suspensionSequence": 1234
"escrowSequence": 1234
}
```
## Suspended Payment Execution
## Escrow Execution
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
owner | [address](#ripple-address) | The address of the owner of the suspended payment to execute.
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to execute.
digest | string | *Optional* The original `digest` from the suspended payment creation transaction. This is sha256 hash of `proof` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old digest with the new digest.
owner | [address](#ripple-address) | The address of the owner of the escrow to execute.
escrowSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Escrow Creation](#escrow-creation) transaction for the escrow to execute.
condition | string | *Optional* The original `condition` from the escrow creation transaction. This is sha256 hash of `fulfillment` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old condition with the new condition.
fulfillment | string | *Optional* A value that produces the condition when hashed. It must be 32 charaters long and contain only 8-bit characters.
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
method | integer | *Optional* The method for verifying the proof; only method `1` is supported.
proof | string | *Optional* A value that produces the digest when hashed. It must be 32 charaters long and contain only 8-bit characters.
### Example
@@ -603,10 +590,81 @@ proof | string | *Optional* A value that produces the digest when hashed. It mus
```json
{
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"suspensionSequence": 1234,
"method": 1,
"digest": "712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D",
"proof": "this must have 32 characters...."
"escrowSequence": 1234,
"condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"fulfillment": "A0028000"
}
```
## Payment Channel Create
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
amount | [value](#value) | Amount of XRP for sender to set aside in this channel.
destination | [address](#ripple-address) | Address to receive XRP claims against this channel.
settleDelay | number | Amount of time the source address must wait before closing the channel if it has unclaimed XRP.
publicKey | string | Public key of the key pair the source will use to sign claims against this channel.
cancelAfter | date-time string | *Optional* Time when this channel expires.
destinationTag | integer | *Optional* Destination tag.
sourceTag | integer | *Optional* Source tag.
### Example
```json
{
"amount": "1",
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"settleDelay": 86400,
"publicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A"
}
```
## Payment Channel Fund
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
amount | [value](#value) | Amount of XRP to fund the channel with.
channel | string | 256-bit hexadecimal channel identifier.
expiration | date-time string | *Optional* New expiration for this channel.
### Example
```json
{
"channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"amount": "1"
}
```
## Payment Channel Claim
See [Transaction Types](#transaction-types) for a description.
Name | Type | Description
---- | ---- | -----------
channel | string | 256-bit hexadecimal channel identifier.
amount | [value](#value) | *Optional* XRP balance of this channel after claim is processed.
balance | [value](#value) | *Optional* Amount of XRP authorized by signature.
close | boolean | *Optional* Request to close the channel.
publicKey | string | *Optional* Public key of the channel's sender
renew | boolean | *Optional* Clear the channel's expiration time.
signature | string | *Optional* Signature of this claim.
### Example
```json
{
"channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198"
}
```
@@ -3132,20 +3190,20 @@ return api.prepareSettings(address, settings)
```
## prepareSuspendedPaymentCreation
## prepareEscrowCreation
`prepareSuspendedPaymentCreation(address: string, suspendedPaymentCreation: Object, instructions: Object): Promise<Object>`
`prepareEscrowCreation(address: string, escrowCreation: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
Prepare an escrow creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
suspendedPaymentCreation | [suspendedPaymentCreation](#suspended-payment-creation) | The specification of the suspended payment creation to prepare.
escrowCreation | [escrowCreation](#escrow-creation) | The specification of the escrow creation to prepare.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
@@ -3168,33 +3226,19 @@ instructions | object | The instructions for how to execute the transaction afte
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentCreation = {
"source": {
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"maxAmount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
"destination": {
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": {
"value": "0.01",
"currency": "USD",
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
}
},
const escrowCreation = {
"destination": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
"amount": "0.01",
"allowCancelAfter": "2014-09-24T21:21:50.000Z"
};
return api.prepareSuspendedPaymentCreation(address, suspendedPaymentCreation).then(prepared =>
return api.prepareEscrowCreation(address, escrowCreation).then(prepared =>
{/* ... */});
```
```json
{
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"SuspendedPaymentCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":{\"value\":\"0.01\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"CancelAfter\":464908910,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"CancelAfter\":464908910,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
@@ -3204,20 +3248,20 @@ return api.prepareSuspendedPaymentCreation(address, suspendedPaymentCreation).th
```
## prepareSuspendedPaymentCancellation
## prepareEscrowCancellation
`prepareSuspendedPaymentCancellation(address: string, suspendedPaymentCancellation: Object, instructions: Object): Promise<Object>`
`prepareEscrowCancellation(address: string, escrowCancellation: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
Prepare an escrow cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
suspendedPaymentCancellation | [suspendedPaymentCancellation](#suspended-payment-cancellation) | The specification of the suspended payment cancellation to prepare.
escrowCancellation | [escrowCancellation](#escrow-cancellation) | The specification of the escrow cancellation to prepare.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
@@ -3240,18 +3284,18 @@ instructions | object | The instructions for how to execute the transaction afte
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentCancellation = {
const escrowCancellation = {
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"suspensionSequence": 1234
"escrowSequence": 1234
};
return api.prepareSuspendedPaymentCancellation(address, suspendedPaymentCancellation).then(prepared =>
return api.prepareEscrowCancellation(address, escrowCancellation).then(prepared =>
{/* ... */});
```
```json
{
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"SuspendedPaymentCancel\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowCancel\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
@@ -3261,20 +3305,20 @@ return api.prepareSuspendedPaymentCancellation(address, suspendedPaymentCancella
```
## prepareSuspendedPaymentExecution
## prepareEscrowExecution
`prepareSuspendedPaymentExecution(address: string, suspendedPaymentExecution: Object, instructions: Object): Promise<Object>`
`prepareEscrowExecution(address: string, escrowExecution: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
Prepare an escrow execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
suspendedPaymentExecution | [suspendedPaymentExecution](#suspended-payment-execution) | The specification of the suspended payment execution to prepare.
escrowExecution | [escrowExecution](#escrow-execution) | The specification of the escrow execution to prepare.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
@@ -3297,21 +3341,192 @@ instructions | object | The instructions for how to execute the transaction afte
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentExecution = {
const escrowExecution = {
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"suspensionSequence": 1234,
"method": 1,
"digest": "712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D",
"proof": "this must have 32 characters...."
"escrowSequence": 1234,
"condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"fulfillment": "A0028000"
};
return api.prepareSuspendedPaymentExecution(address, suspendedPaymentExecution).then(prepared =>
return api.prepareEscrowExecution(address, escrowExecution).then(prepared =>
{/* ... */});
```
```json
{
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"SuspendedPaymentFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Method\":1,\"Digest\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Proof\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"EscrowFinish\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Owner\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"OfferSequence\":1234,\"Condition\":\"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100\",\"Fulfillment\":\"A0028000\",\"LastLedgerSequence\":8820051,\"Fee\":\"396\",\"Sequence\":23}",
"instructions": {
"fee": "0.000396",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
```
## preparePaymentChannelCreate
`preparePaymentChannelCreate(address: string, paymentChannelCreate: Object, instructions: Object): Promise<Object>`
Prepare a payment channel creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
paymentChannelCreate | [paymentChannelCreate](#payment-channel-create) | The specification of the payment channel to create.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
Name | Type | Description
---- | ---- | -----------
txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelCreate = {
"amount": "1",
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"settleDelay": 86400,
"publicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A"
};
return api.preparePaymentChannelCreate(address, paymentChannelCreate).then(prepared =>
{/* ... */});
```
```json
{
"txJSON":"{\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TransactionType\":\"PaymentChannelCreate\",\"Amount\":\"1000000\",\"Destination\":\"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW\",\"SettleDelay\":86400,\"PublicKey\":\"32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A\",\"Flags\":2147483648,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
```
## preparePaymentChannelClaim
`preparePaymentChannelClaim(address: string, paymentChannelClaim: Object, instructions: Object): Promise<Object>`
Prepare a payment channel claim transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
paymentChannelClaim | [paymentChannelClaim](#payment-channel-claim) | Details of the channel and claim.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
Name | Type | Description
---- | ---- | -----------
txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelClaim = {
"channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198"
};
return api.preparePaymentChannelClaim(address, paymentChannelClaim).then(prepared =>
{/* ... */});
```
```json
{
"txJSON": "{\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TransactionType\":\"PaymentChannelClaim\",\"Channel\":\"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198\",\"Flags\":2147483648,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
"maxLedgerVersion": 8820051
}
}
```
## preparePaymentChannelFund
`preparePaymentChannelFund(address: string, paymentChannelFund: Object, instructions: Object): Promise<Object>`
Prepare a payment channel fund transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
Name | Type | Description
---- | ---- | -----------
address | [address](#ripple-address) | The address of the account that is creating the transaction.
paymentChannelFund | [paymentChannelFund](#payment-channel-fund) | The channel to fund, and the details of how to fund it.
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
Name | Type | Description
---- | ---- | -----------
txJSON | string | The prepared transaction in rippled JSON format.
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelFund = {
"channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"amount": "1"
};
return api.preparePaymentChannelFund(address, paymentChannelFund).then(prepared =>
{/* ... */});
```
```json
{
"txJSON":"{\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TransactionType\":\"PaymentChannelFund\",\"Channel\":\"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198\",\"Amount\":\"1000000\",\"Flags\":2147483648,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
"instructions": {
"fee": "0.000012",
"sequence": 23,

View File

@@ -27,9 +27,12 @@
<% include prepareOrder.md.ejs %>
<% include prepareOrderCancellation.md.ejs %>
<% include prepareSettings.md.ejs %>
<% include prepareSuspendedPaymentCreation.md.ejs %>
<% include prepareSuspendedPaymentCancellation.md.ejs %>
<% include prepareSuspendedPaymentExecution.md.ejs %>
<% include prepareEscrowCreation.md.ejs %>
<% include prepareEscrowCancellation.md.ejs %>
<% include prepareEscrowExecution.md.ejs %>
<% include preparePaymentChannelCreate.md.ejs %>
<% include preparePaymentChannelClaim.md.ejs %>
<% include preparePaymentChannelFund.md.ejs %>
<% include sign.md.ejs %>
<% include combine.md.ejs %>
<% include submit.md.ejs %>

View File

@@ -18,9 +18,9 @@ Methods that depend on the state of the Ripple Consensus Ledger are unavailable
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
* [prepareEscrowCreation](#prepareescrowcreation)
* [prepareEscrowCancellation](#prepareescrowcancellation)
* [prepareEscrowExecution](#prepareescrowexecution)
* [sign](#sign)
* [generateAddress](#generateaddress)
* [computeLedgerHash](#computeledgerhash)

View File

@@ -0,0 +1,32 @@
## prepareEscrowCancellation
`prepareEscrowCancellation(address: string, escrowCancellation: Object, instructions: Object): Promise<Object>`
Prepare an escrow cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-escrow-cancellation.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const escrowCancellation = <%- importFile('test/fixtures/requests/prepare-escrow-cancellation.json') %>;
return api.prepareEscrowCancellation(address, escrowCancellation).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-escrow-cancellation.json') %>

View File

@@ -0,0 +1,32 @@
## prepareEscrowCreation
`prepareEscrowCreation(address: string, escrowCreation: Object, instructions: Object): Promise<Object>`
Prepare an escrow creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-escrow-creation.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const escrowCreation = <%- importFile('test/fixtures/requests/prepare-escrow-creation.json') %>;
return api.prepareEscrowCreation(address, escrowCreation).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-escrow-creation.json') %>

View File

@@ -0,0 +1,32 @@
## prepareEscrowExecution
`prepareEscrowExecution(address: string, escrowExecution: Object, instructions: Object): Promise<Object>`
Prepare an escrow execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Escrow is currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-escrow-execution.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const escrowExecution = <%- importFile('test/fixtures/requests/prepare-escrow-execution.json') %>;
return api.prepareEscrowExecution(address, escrowExecution).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-escrow-execution.json') %>

View File

@@ -0,0 +1,32 @@
## preparePaymentChannelClaim
`preparePaymentChannelClaim(address: string, paymentChannelClaim: Object, instructions: Object): Promise<Object>`
Prepare a payment channel claim transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-payment-channel-claim.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelClaim = <%- importFile('test/fixtures/requests/prepare-payment-channel-claim.json') %>;
return api.preparePaymentChannelClaim(address, paymentChannelClaim).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-payment-channel-claim.json') %>

View File

@@ -0,0 +1,32 @@
## preparePaymentChannelCreate
`preparePaymentChannelCreate(address: string, paymentChannelCreate: Object, instructions: Object): Promise<Object>`
Prepare a payment channel creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-payment-channel-create.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelCreate = <%- importFile('test/fixtures/requests/prepare-payment-channel-create.json') %>;
return api.preparePaymentChannelCreate(address, paymentChannelCreate).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-payment-channel-create.json') %>

View File

@@ -0,0 +1,32 @@
## preparePaymentChannelFund
`preparePaymentChannelFund(address: string, paymentChannelFund: Object, instructions: Object): Promise<Object>`
Prepare a payment channel fund transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Payment channels are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-payment-channel-fund.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const paymentChannelFund = <%- importFile('test/fixtures/requests/prepare-payment-channel-fund.json') %>;
return api.preparePaymentChannelFund(address, paymentChannelFund).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-payment-channel-fund.json') %>

View File

@@ -1,32 +0,0 @@
## prepareSuspendedPaymentCancellation
`prepareSuspendedPaymentCancellation(address: string, suspendedPaymentCancellation: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-cancellation.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentCancellation = <%- importFile('test/fixtures/requests/prepare-suspended-payment-cancellation.json') %>;
return api.prepareSuspendedPaymentCancellation(address, suspendedPaymentCancellation).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-suspended-payment-cancellation.json') %>

View File

@@ -1,32 +0,0 @@
## prepareSuspendedPaymentCreation
`prepareSuspendedPaymentCreation(address: string, suspendedPaymentCreation: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-creation.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentCreation = <%- importFile('test/fixtures/requests/prepare-suspended-payment-creation.json') %>;
return api.prepareSuspendedPaymentCreation(address, suspendedPaymentCreation).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-suspended-payment-creation.json') %>

View File

@@ -1,32 +0,0 @@
## prepareSuspendedPaymentExecution
`prepareSuspendedPaymentExecution(address: string, suspendedPaymentExecution: Object, instructions: Object): Promise<Object>`
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-execution.json') %>
### Return Value
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>
### Example
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const suspendedPaymentExecution = <%- importFile('test/fixtures/requests/prepare-suspended-payment-execution.json') %>;
return api.prepareSuspendedPaymentExecution(address, suspendedPaymentExecution).then(prepared =>
{/* ... */});
```
<%- renderFixture('responses/prepare-suspended-payment-execution.json') %>

View File

@@ -52,32 +52,62 @@ See [Transaction Types](#transaction-types) for a description.
<%- renderFixture('requests/prepare-settings.json') %>
## Suspended Payment Creation
## Escrow Creation
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-creation.json') %>
<%- renderSchema('specifications/escrow-creation.json') %>
### Example
<%- renderFixture('requests/prepare-suspended-payment-creation.json') %>
<%- renderFixture('requests/prepare-escrow-creation.json') %>
## Suspended Payment Cancellation
## Escrow Cancellation
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-cancellation.json') %>
<%- renderSchema('specifications/escrow-cancellation.json') %>
### Example
<%- renderFixture('requests/prepare-suspended-payment-cancellation.json') %>
<%- renderFixture('requests/prepare-escrow-cancellation.json') %>
## Suspended Payment Execution
## Escrow Execution
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-execution.json') %>
<%- renderSchema('specifications/escrow-execution.json') %>
### Example
<%- renderFixture('requests/prepare-suspended-payment-execution.json') %>
<%- renderFixture('requests/prepare-escrow-execution.json') %>
## Payment Channel Create
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/payment-channel-create.json') %>
### Example
<%- renderFixture('requests/prepare-payment-channel-create.json') %>
## Payment Channel Fund
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/payment-channel-fund.json') %>
### Example
<%- renderFixture('requests/prepare-payment-channel-fund.json') %>
## Payment Channel Claim
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/payment-channel-claim.json') %>
### Example
<%- renderFixture('requests/prepare-payment-channel-claim.json') %>

View File

@@ -11,11 +11,11 @@ Type | Description
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
[settings](#settings) | A `settings` transaction modifies the settings of an account in the Ripple Consensus Ledger.
[suspendedPaymentCreation](#suspended-payment-creation) | A `suspendedPaymentCreation` transaction creates a suspended payment on the ledger, which locks XRP until a cryptographic condition is met or it expires. It is like an escrow service where the Ripple network acts as the escrow agent.
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
[escrowCreation](#escrow-creation) | An `escrowCreation` transaction creates an escrow on the ledger, which locks XRP until a cryptographic condition is met or it expires. It is like an escrow service where the Ripple network acts as the escrow agent.
[escrowCancellation](#escrow-cancellation) | An `escrowCancellation` transaction unlocks the funds in an escrow and sends them back to the creator of the escrow, but it will only work after the escrow expires.
[escrowExecution](#escrow-execution) | An `escrowExecution` transaction unlocks the funds in an escrow and sends them to the destination of the escrow, but it will only work if the cryptographic condition is provided.
The three "suspended payment" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
The three "escrow" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
## Transaction Flow
@@ -27,9 +27,9 @@ Executing a transaction with `RippleAPI` requires the following four steps:
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
* [prepareEscrowCreation](#prepareescrowcreation)
* [prepareEscrowCancellation](#prepareescrowcancellation)
* [prepareEscrowExecution](#prepareescrowexecution)
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
3. [Submit](#submit) - Submit the transaction to the connected server.
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.

12
npm-shrinkwrap.json generated
View File

@@ -3249,9 +3249,9 @@
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz"
},
"ripple-binary-codec": {
"version": "0.1.5",
"from": "ripple-binary-codec@>=0.1.5 <0.2.0",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.5.tgz",
"version": "0.1.9",
"from": "ripple-binary-codec@>=0.1.8 <0.2.0",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.9.tgz",
"dependencies": {
"lodash": {
"version": "4.13.1",
@@ -3261,9 +3261,9 @@
}
},
"ripple-hashes": {
"version": "0.2.0",
"from": "ripple-hashes@>=0.2.0 <0.3.0",
"resolved": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.2.0.tgz"
"version": "0.3.0",
"from": "ripple-hashes@>=0.3.0 <0.4.0",
"resolved": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.3.0.tgz"
},
"ripple-keypairs": {
"version": "0.10.0",

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.17.4",
"version": "0.17.6",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
@@ -24,8 +24,8 @@
"jayson": "^1.2.2",
"lodash": "^3.1.0",
"ripple-address-codec": "^2.0.1",
"ripple-binary-codec": "^0.1.5",
"ripple-hashes": "^0.2.0",
"ripple-binary-codec": "^0.1.9",
"ripple-hashes": "^0.3.0",
"ripple-keypairs": "^0.10.0",
"ripple-lib-transactionparser": "^0.6.0",
"ws": "^1.0.1"

View File

@@ -1,5 +1,5 @@
/* @flow */
'use strict'; // eslint-disable-line
'use strict' // eslint-disable-line strict
/* eslint-disable max-len */
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
@@ -9,48 +9,54 @@
// In node.js env, polyfill might be already loaded (from any npm package),
// that's why we do this check.
if (!global._babelPolyfill) {
require('babel-polyfill');
require('babel-polyfill')
}
const _ = require('lodash');
const EventEmitter = require('events').EventEmitter;
const common = require('./common');
const server = require('./server/server');
const connect = server.connect;
const disconnect = server.disconnect;
const getServerInfo = server.getServerInfo;
const getFee = server.getFee;
const isConnected = server.isConnected;
const getLedgerVersion = server.getLedgerVersion;
const getTransaction = require('./ledger/transaction');
const getTransactions = require('./ledger/transactions');
const getTrustlines = require('./ledger/trustlines');
const getBalances = require('./ledger/balances');
const getBalanceSheet = require('./ledger/balance-sheet');
const getPaths = require('./ledger/pathfind');
const getOrders = require('./ledger/orders');
const getOrderbook = require('./ledger/orderbook');
const getSettings = require('./ledger/settings');
const getAccountInfo = require('./ledger/accountinfo');
const preparePayment = require('./transaction/payment');
const prepareTrustline = require('./transaction/trustline');
const prepareOrder = require('./transaction/order');
const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSuspendedPaymentCreation =
require('./transaction/suspended-payment-creation');
const prepareSuspendedPaymentExecution =
require('./transaction/suspended-payment-execution');
const prepareSuspendedPaymentCancellation =
require('./transaction/suspended-payment-cancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const combine = require('./transaction/combine');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const _ = require('lodash')
const EventEmitter = require('events').EventEmitter
const common = require('./common')
const server = require('./server/server')
const connect = server.connect
const disconnect = server.disconnect
const getServerInfo = server.getServerInfo
const getFee = server.getFee
const isConnected = server.isConnected
const getLedgerVersion = server.getLedgerVersion
const getTransaction = require('./ledger/transaction')
const getTransactions = require('./ledger/transactions')
const getTrustlines = require('./ledger/trustlines')
const getBalances = require('./ledger/balances')
const getBalanceSheet = require('./ledger/balance-sheet')
const getPaths = require('./ledger/pathfind')
const getOrders = require('./ledger/orders')
const getOrderbook = require('./ledger/orderbook')
const getSettings = require('./ledger/settings')
const getAccountInfo = require('./ledger/accountinfo')
const preparePayment = require('./transaction/payment')
const prepareTrustline = require('./transaction/trustline')
const prepareOrder = require('./transaction/order')
const prepareOrderCancellation = require('./transaction/ordercancellation')
const prepareEscrowCreation =
require('./transaction/escrow-creation')
const prepareEscrowExecution =
require('./transaction/escrow-execution')
const prepareEscrowCancellation =
require('./transaction/escrow-cancellation')
const preparePaymentChannelCreate =
require('./transaction/payment-channel-create')
const preparePaymentChannelFund =
require('./transaction/payment-channel-fund')
const preparePaymentChannelClaim =
require('./transaction/payment-channel-claim')
const prepareSettings = require('./transaction/settings')
const sign = require('./transaction/sign')
const combine = require('./transaction/combine')
const submit = require('./transaction/submit')
const errors = require('./common').errors
const generateAddress =
require('./offline/generate-address').generateAddressAPI;
const computeLedgerHash = require('./offline/ledgerhash');
const getLedger = require('./ledger/ledger');
require('./offline/generate-address').generateAddressAPI
const computeLedgerHash = require('./offline/ledgerhash')
const getLedger = require('./ledger/ledger')
type APIOptions = {
server?: string,
@@ -63,42 +69,42 @@ type APIOptions = {
// prevent access to non-validated ledger versions
class RestrictedConnection extends common.Connection {
request(request, timeout) {
const ledger_index = request.ledger_index;
const ledger_index = request.ledger_index
if (ledger_index !== undefined && ledger_index !== 'validated') {
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
return Promise.reject(new errors.LedgerVersionError(
`ledgerVersion ${ledger_index} is greater than server\'s ` +
`most recent validated ledger: ${this._ledgerVersion}`));
`most recent validated ledger: ${this._ledgerVersion}`))
}
}
return super.request(request, timeout);
return super.request(request, timeout)
}
}
class RippleAPI extends EventEmitter {
constructor(options: APIOptions = {}) {
common.validate.apiOptions(options);
super();
this._feeCushion = options.feeCushion || 1.2;
const serverURL = options.server;
common.validate.apiOptions(options)
super()
this._feeCushion = options.feeCushion || 1.2
const serverURL = options.server
if (serverURL !== undefined) {
this.connection = new RestrictedConnection(serverURL, options);
this.connection = new RestrictedConnection(serverURL, options)
this.connection.on('ledgerClosed', message => {
this.emit('ledger', server.formatLedgerClose(message));
});
this.emit('ledger', server.formatLedgerClose(message))
})
this.connection.on('error', (errorCode, errorMessage, data) => {
this.emit('error', errorCode, errorMessage, data);
});
this.emit('error', errorCode, errorMessage, data)
})
this.connection.on('connected', () => {
this.emit('connected');
});
this.emit('connected')
})
this.connection.on('disconnected', code => {
this.emit('disconnected', code);
});
this.emit('disconnected', code)
})
} else {
// use null object pattern to provide better error message if user
// tries to call a method that requires a connection
this.connection = new RestrictedConnection(null, options);
this.connection = new RestrictedConnection(null, options)
}
}
}
@@ -127,9 +133,12 @@ _.assign(RippleAPI.prototype, {
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSuspendedPaymentCreation,
prepareSuspendedPaymentExecution,
prepareSuspendedPaymentCancellation,
prepareEscrowCreation,
prepareEscrowExecution,
prepareEscrowCancellation,
preparePaymentChannelCreate,
preparePaymentChannelFund,
preparePaymentChannelClaim,
prepareSettings,
sign,
combine,
@@ -138,7 +147,7 @@ _.assign(RippleAPI.prototype, {
generateAddress,
computeLedgerHash,
errors
});
})
// these are exposed only for use by unit tests; they are not part of the API
RippleAPI._PRIVATE = {
@@ -146,8 +155,8 @@ RippleAPI._PRIVATE = {
RangeSet: require('./common/rangeset').RangeSet,
ledgerUtils: require('./ledger/utils'),
schemaValidator: require('./common/schema-validator')
};
}
module.exports = {
RippleAPI
};
}

View File

@@ -1,70 +1,70 @@
'use strict';
const _ = require('lodash');
const RippleAPI = require('./api').RippleAPI;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const RippleAPI = require('./api').RippleAPI
class RippleAPIBroadcast extends RippleAPI {
constructor(servers, options) {
super(options);
this.ledgerVersion = 0;
super(options)
this.ledgerVersion = 0
const apis = servers.map(server => new RippleAPI(
_.assign({}, options, {server})
));
))
// exposed for testing
this._apis = apis;
this._apis = apis
this.getMethodNames().forEach(name => {
this[name] = function() { // eslint-disable-line no-loop-func
return Promise.race(apis.map(api => api[name].apply(api, arguments)));
};
});
return Promise.race(apis.map(api => api[name](...arguments)))
}
})
// connection methods must be overridden to apply to all api instances
this.connect = function() {
return Promise.all(apis.map(api => api.connect()));
};
return Promise.all(apis.map(api => api.connect()))
}
this.disconnect = function() {
return Promise.all(apis.map(api => api.disconnect()));
};
return Promise.all(apis.map(api => api.disconnect()))
}
this.isConnected = function() {
return _.every(apis.map(api => api.isConnected()));
};
return _.every(apis.map(api => api.isConnected()))
}
// synchronous methods are all passed directly to the first api instance
const defaultAPI = apis[0];
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash'];
const defaultAPI = apis[0]
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash']
syncMethods.forEach(name => {
this[name] = defaultAPI[name].bind(defaultAPI);
});
this[name] = defaultAPI[name].bind(defaultAPI)
})
apis.forEach(api => {
api.on('ledger', this.onLedgerEvent.bind(this));
api.on('ledger', this.onLedgerEvent.bind(this))
api.on('error', (errorCode, errorMessage, data) =>
this.emit('error', errorCode, errorMessage, data));
});
this.emit('error', errorCode, errorMessage, data))
})
}
onLedgerEvent(ledger) {
if (ledger.ledgerVersion > this.ledgerVersion) {
this.ledgerVersion = ledger.ledgerVersion;
this.emit('ledger', ledger);
this.ledgerVersion = ledger.ledgerVersion
this.emit('ledger', ledger)
}
}
getMethodNames() {
const methodNames = [];
const methodNames = []
for (const name in RippleAPI.prototype) {
if (RippleAPI.prototype.hasOwnProperty(name)) {
if (typeof RippleAPI.prototype[name] === 'function') {
methodNames.push(name);
methodNames.push(name)
}
}
}
return methodNames;
return methodNames
}
}
module.exports = {
RippleAPIBroadcast
};
}

View File

@@ -1,4 +1,4 @@
'use strict';
'use strict' // eslint-disable-line strict
function setPrototypeOf(object, prototype) {
// Object.setPrototypeOf not supported on Internet Explorer 9
@@ -12,10 +12,10 @@ function getConstructorName(object) {
// hack for internet explorer
return process.browser ?
object.constructor.toString().match(/^function\s+([^(]*)/)[1] :
object.constructor.name;
object.constructor.name
}
module.exports = {
getConstructorName,
setPrototypeOf
};
}

View File

@@ -1,125 +1,133 @@
'use strict'; // eslint-disable-line
'use strict' // eslint-disable-line strict
const _ = require('lodash');
const {EventEmitter} = require('events');
const WebSocket = require('ws');
const parseURL = require('url').parse;
const RangeSet = require('./rangeset').RangeSet;
const _ = require('lodash')
const {EventEmitter} = require('events')
const WebSocket = require('ws')
const parseURL = require('url').parse
const RangeSet = require('./rangeset').RangeSet
const {RippledError, DisconnectedError, NotConnectedError,
TimeoutError, ResponseFormatError, ConnectionError,
RippledNotInitializedError} = require('./errors');
RippledNotInitializedError} = require('./errors')
function isStreamMessageType(type) {
return type === 'ledgerClosed' ||
type === 'transaction' ||
type === 'path_find';
type === 'path_find'
}
class Connection extends EventEmitter {
constructor(url, options = {}) {
super();
this.setMaxListeners(Infinity);
this._url = url;
this._trace = options.trace;
super()
this.setMaxListeners(Infinity)
this._url = url
this._trace = options.trace
if (this._trace) {
// for easier unit testing
this._console = console;
this._console = console
}
this._proxyURL = options.proxy;
this._proxyAuthorization = options.proxyAuthorization;
this._authorization = options.authorization;
this._trustedCertificates = options.trustedCertificates;
this._key = options.key;
this._passphrase = options.passphrase;
this._certificate = options.certificate;
this._timeout = options.timeout || (20 * 1000);
this._isReady = false;
this._ws = null;
this._ledgerVersion = null;
this._availableLedgerVersions = new RangeSet();
this._nextRequestID = 1;
this._retry = 0;
this._retryTimer = null;
this._onOpenErrorBound = null;
this._onUnexpectedCloseBound = null;
this._proxyURL = options.proxy
this._proxyAuthorization = options.proxyAuthorization
this._authorization = options.authorization
this._trustedCertificates = options.trustedCertificates
this._key = options.key
this._passphrase = options.passphrase
this._certificate = options.certificate
this._timeout = options.timeout || (20 * 1000)
this._isReady = false
this._ws = null
this._ledgerVersion = null
this._availableLedgerVersions = new RangeSet()
this._nextRequestID = 1
this._retry = 0
this._retryTimer = null
this._onOpenErrorBound = null
this._onUnexpectedCloseBound = null
this._fee_base = null
this._fee_ref = null
}
_updateLedgerVersions(data) {
this._ledgerVersion = Number(data.ledger_index);
this._ledgerVersion = Number(data.ledger_index)
if (data.validated_ledgers) {
this._availableLedgerVersions.reset();
this._availableLedgerVersions.reset()
this._availableLedgerVersions.parseAndAddRanges(
data.validated_ledgers);
data.validated_ledgers)
} else {
this._availableLedgerVersions.addValue(this._ledgerVersion);
this._availableLedgerVersions.addValue(this._ledgerVersion)
}
}
_updateFees(data) {
this._fee_base = Number(data.fee_base)
this._fee_ref = Number(data.fee_ref)
}
// return value is array of arguments to Connection.emit
_parseMessage(message) {
const data = JSON.parse(message);
const data = JSON.parse(message)
if (data.type === 'response') {
if (!(Number.isInteger(data.id) && data.id >= 0)) {
throw new ResponseFormatError('valid id not found in response');
throw new ResponseFormatError('valid id not found in response')
}
return [data.id.toString(), data];
return [data.id.toString(), data]
} else if (isStreamMessageType(data.type)) {
if (data.type === 'ledgerClosed') {
this._updateLedgerVersions(data);
this._updateLedgerVersions(data)
this._updateFees(data)
}
return [data.type, data];
return [data.type, data]
} else if (data.type === undefined && data.error) {
return ['error', data.error, data.error_message, data]; // e.g. slowDown
return ['error', data.error, data.error_message, data] // e.g. slowDown
}
throw new ResponseFormatError('unrecognized message type: ' + data.type);
throw new ResponseFormatError('unrecognized message type: ' + data.type)
}
_onMessage(message) {
let parameters;
let parameters
if (this._trace) {
this._console.log(message);
this._console.log(message)
}
try {
parameters = this._parseMessage(message);
parameters = this._parseMessage(message)
} catch (error) {
this.emit('error', 'badMessage', error.message, message);
return;
this.emit('error', 'badMessage', error.message, message)
return
}
// we don't want this inside the try/catch or exceptions in listener
// will be caught
this.emit(...parameters);
this.emit(...parameters)
}
get _state() {
return this._ws ? this._ws.readyState : WebSocket.CLOSED;
return this._ws ? this._ws.readyState : WebSocket.CLOSED
}
get _shouldBeConnected() {
return this._ws !== null;
return this._ws !== null
}
isConnected() {
return this._state === WebSocket.OPEN && this._isReady;
return this._state === WebSocket.OPEN && this._isReady
}
_onUnexpectedClose(beforeOpen, resolve, reject, code) {
if (this._onOpenErrorBound) {
this._ws.removeListener('error', this._onOpenErrorBound);
this._onOpenErrorBound = null;
this._ws.removeListener('error', this._onOpenErrorBound)
this._onOpenErrorBound = null
}
// just in case
this._ws.removeAllListeners('open');
this._ws = null;
this._isReady = false;
this._ws.removeAllListeners('open')
this._ws = null
this._isReady = false
if (beforeOpen) {
// connection was closed before it was properly opened, so we must return
// error to connect's caller
this.connect().then(resolve, reject);
this.connect().then(resolve, reject)
} else {
// if first parameter ws lib sends close code,
// but sometimes it forgots about it, so default to 1006 - CLOSE_ABNORMAL
this.emit('disconnected', code || 1006);
this._retryConnect();
this.emit('disconnected', code || 1006)
this._retryConnect()
}
}
@@ -134,91 +142,92 @@ class Connection extends EventEmitter {
// Then, for 10 minutes: once every 10 seconds
? (10 * 1000)
// Then: once every 30 seconds
: (30 * 1000);
: (30 * 1000)
}
_retryConnect() {
this._retry += 1;
const retryTimeout = this._calculateTimeout(this._retry);
this._retry += 1
const retryTimeout = this._calculateTimeout(this._retry)
this._retryTimer = setTimeout(() => {
this.emit('reconnecting', this._retry);
this.connect().catch(this._retryConnect.bind(this));
}, retryTimeout);
this.emit('reconnecting', this._retry)
this.connect().catch(this._retryConnect.bind(this))
}, retryTimeout)
}
_clearReconnectTimer() {
clearTimeout(this._retryTimer);
this._retryTimer = null;
clearTimeout(this._retryTimer)
this._retryTimer = null
}
_onOpen() {
if (!this._ws) {
return Promise.reject(new DisconnectedError());
return Promise.reject(new DisconnectedError())
}
if (this._onOpenErrorBound) {
this._ws.removeListener('error', this._onOpenErrorBound);
this._onOpenErrorBound = null;
this._ws.removeListener('error', this._onOpenErrorBound)
this._onOpenErrorBound = null
}
const request = {
command: 'subscribe',
streams: ['ledger']
};
}
return this.request(request).then(data => {
if (_.isEmpty(data) || !data.ledger_index) {
// rippled instance doesn't have validated ledgers
return this._disconnect(false).then(() => {
throw new RippledNotInitializedError('Rippled not initialized');
});
throw new RippledNotInitializedError('Rippled not initialized')
})
}
this._updateLedgerVersions(data);
this._rebindOnUnxpectedClose();
this._updateLedgerVersions(data)
this._updateFees(data)
this._rebindOnUnxpectedClose()
this._retry = 0;
this._retry = 0
this._ws.on('error', error => {
if (process.browser && error && error.type === 'error') {
// we are in browser, ignore error - `close` event will be fired
// after error
return;
return
}
this.emit('error', 'websocket', error.message, error);
});
this.emit('error', 'websocket', error.message, error)
})
this._isReady = true;
this.emit('connected');
this._isReady = true
this.emit('connected')
return undefined;
});
return undefined
})
}
_rebindOnUnxpectedClose() {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound);
this._ws.removeListener('close', this._onUnexpectedCloseBound)
}
this._onUnexpectedCloseBound =
this._onUnexpectedClose.bind(this, false, null, null);
this._ws.once('close', this._onUnexpectedCloseBound);
this._onUnexpectedClose.bind(this, false, null, null)
this._ws.once('close', this._onUnexpectedCloseBound)
}
_unbindOnUnxpectedClose() {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound);
this._ws.removeListener('close', this._onUnexpectedCloseBound)
}
this._onUnexpectedCloseBound = null;
this._onUnexpectedCloseBound = null
}
_onOpenError(reject, error) {
this._onOpenErrorBound = null;
this._unbindOnUnxpectedClose();
reject(new NotConnectedError(error && error.message));
this._onOpenErrorBound = null
this._unbindOnUnxpectedClose()
reject(new NotConnectedError(error && error.message))
}
_createWebSocket() {
const options = {};
const options = {}
if (this._proxyURL !== undefined) {
const parsedURL = parseURL(this._url);
const parsedProxyURL = parseURL(this._proxyURL);
const parsedURL = parseURL(this._url)
const parsedProxyURL = parseURL(this._proxyURL)
const proxyOverrides = _.omit({
secureEndpoint: (parsedURL.protocol === 'wss:'),
secureProxy: (parsedProxyURL.protocol === 'https:'),
@@ -227,49 +236,49 @@ class Connection extends EventEmitter {
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
let HttpsProxyAgent;
}, _.isUndefined)
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides)
let HttpsProxyAgent
try {
HttpsProxyAgent = require('https-proxy-agent');
HttpsProxyAgent = require('https-proxy-agent')
} catch (error) {
throw new Error('"proxy" option is not supported in the browser');
throw new Error('"proxy" option is not supported in the browser')
}
options.agent = new HttpsProxyAgent(proxyOptions);
options.agent = new HttpsProxyAgent(proxyOptions)
}
if (this._authorization !== undefined) {
const base64 = new Buffer(this._authorization).toString('base64');
options.headers = {Authorization: `Basic ${base64}`};
const base64 = new Buffer(this._authorization).toString('base64')
options.headers = {Authorization: `Basic ${base64}`}
}
const optionsOverrides = _.omit({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const websocketOptions = _.assign({}, options, optionsOverrides);
const websocket = new WebSocket(this._url, null, websocketOptions);
}, _.isUndefined)
const websocketOptions = _.assign({}, options, optionsOverrides)
const websocket = new WebSocket(this._url, null, websocketOptions)
// we will have a listener for each outstanding request,
// so we have to raise the limit (the default is 10)
if (typeof websocket.setMaxListeners === 'function') {
websocket.setMaxListeners(Infinity);
websocket.setMaxListeners(Infinity)
}
return websocket;
return websocket
}
connect() {
this._clearReconnectTimer();
this._clearReconnectTimer()
return new Promise((resolve, reject) => {
if (!this._url) {
reject(new ConnectionError(
'Cannot connect because no server was specified'));
'Cannot connect because no server was specified'))
}
if (this._state === WebSocket.OPEN) {
resolve();
resolve()
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve);
this._ws.once('open', resolve)
} else {
this._ws = this._createWebSocket();
this._ws = this._createWebSocket()
// when an error causes the connection to close, the close event
// should still be emitted; the "ws" documentation says: "The close
// event is also emitted when then underlying net.Socket closes the
@@ -277,156 +286,164 @@ class Connection extends EventEmitter {
// In case if there is connection error (say, server is not responding)
// we must return this error to connection's caller. After successful
// opening, we will forward all errors to main api object.
this._onOpenErrorBound = this._onOpenError.bind(this, reject);
this._ws.once('error', this._onOpenErrorBound);
this._ws.on('message', this._onMessage.bind(this));
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
this._ws.once('error', this._onOpenErrorBound)
this._ws.on('message', this._onMessage.bind(this))
// in browser close event can came before open event, so we must
// resolve connect's promise after reconnect in that case.
// after open event we will rebound _onUnexpectedCloseBound
// without resolve and reject functions
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this, true,
resolve, reject);
this._ws.once('close', this._onUnexpectedCloseBound);
this._ws.once('open', () => this._onOpen().then(resolve, reject));
resolve, reject)
this._ws.once('close', this._onUnexpectedCloseBound)
this._ws.once('open', () => this._onOpen().then(resolve, reject))
}
});
})
}
disconnect() {
return this._disconnect(true);
return this._disconnect(true)
}
_disconnect(calledByUser) {
if (calledByUser) {
this._clearReconnectTimer();
this._retry = 0;
this._clearReconnectTimer()
this._retry = 0
}
return new Promise(resolve => {
if (this._state === WebSocket.CLOSED) {
resolve();
resolve()
} else if (this._state === WebSocket.CLOSING) {
this._ws.once('close', resolve);
this._ws.once('close', resolve)
} else {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound);
this._onUnexpectedCloseBound = null;
this._ws.removeListener('close', this._onUnexpectedCloseBound)
this._onUnexpectedCloseBound = null
}
this._ws.once('close', code => {
this._ws = null;
this._isReady = false;
this._ws = null
this._isReady = false
if (calledByUser) {
this.emit('disconnected', code || 1000); // 1000 - CLOSE_NORMAL
this.emit('disconnected', code || 1000) // 1000 - CLOSE_NORMAL
}
resolve();
});
this._ws.close();
resolve()
})
this._ws.close()
}
});
})
}
reconnect() {
return this.disconnect().then(() => this.connect());
return this.disconnect().then(() => this.connect())
}
_whenReady(promise) {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError());
reject(new NotConnectedError())
} else if (this._state === WebSocket.OPEN && this._isReady) {
promise.then(resolve, reject);
promise.then(resolve, reject)
} else {
this.once('connected', () => promise.then(resolve, reject));
this.once('connected', () => promise.then(resolve, reject))
}
});
})
}
getLedgerVersion() {
return this._whenReady(Promise.resolve(this._ledgerVersion));
return this._whenReady(Promise.resolve(this._ledgerVersion))
}
hasLedgerVersions(lowLedgerVersion, highLedgerVersion) {
return this._whenReady(Promise.resolve(
this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)));
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)))
}
hasLedgerVersion(ledgerVersion) {
return this.hasLedgerVersions(ledgerVersion, ledgerVersion);
return this.hasLedgerVersions(ledgerVersion, ledgerVersion)
}
getFeeBase() {
return this._whenReady(Promise.resolve(Number(this._fee_base)))
}
getFeeRef() {
return this._whenReady(Promise.resolve(Number(this._fee_ref)))
}
_send(message) {
if (this._trace) {
this._console.log(message);
this._console.log(message)
}
return new Promise((resolve, reject) => {
this._ws.send(message, undefined, (error, result) => {
if (error) {
reject(new DisconnectedError(error.message));
reject(new DisconnectedError(error.message))
} else {
resolve(result);
resolve(result)
}
});
});
})
})
}
request(request, timeout) {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError());
reject(new NotConnectedError())
}
let timer = null;
const self = this;
const id = this._nextRequestID;
this._nextRequestID += 1;
const eventName = id.toString();
let timer = null
const self = this
const id = this._nextRequestID
this._nextRequestID += 1
const eventName = id.toString()
function onDisconnect() {
clearTimeout(timer);
self.removeAllListeners(eventName);
reject(new DisconnectedError());
clearTimeout(timer)
self.removeAllListeners(eventName)
reject(new DisconnectedError())
}
function cleanup() {
clearTimeout(timer);
self.removeAllListeners(eventName);
clearTimeout(timer)
self.removeAllListeners(eventName)
if (self._ws !== null) {
self._ws.removeListener('close', onDisconnect);
self._ws.removeListener('close', onDisconnect)
}
}
function _resolve(response) {
cleanup();
resolve(response);
cleanup()
resolve(response)
}
function _reject(error) {
cleanup();
reject(error);
cleanup()
reject(error)
}
this.once(eventName, response => {
if (response.status === 'error') {
_reject(new RippledError(response.error));
_reject(new RippledError(response.error))
} else if (response.status === 'success') {
_resolve(response.result);
_resolve(response.result)
} else {
_reject(new ResponseFormatError(
'unrecognized status: ' + response.status));
'unrecognized status: ' + response.status))
}
});
})
this._ws.once('close', onDisconnect);
this._ws.once('close', onDisconnect)
// JSON.stringify automatically removes keys with value of 'undefined'
const message = JSON.stringify(Object.assign({}, request, {id}));
const message = JSON.stringify(Object.assign({}, request, {id}))
this._whenReady(this._send(message)).then(() => {
const delay = timeout || this._timeout;
timer = setTimeout(() => _reject(new TimeoutError()), delay);
}).catch(_reject);
});
const delay = timeout || this._timeout
timer = setTimeout(() => _reject(new TimeoutError()), delay)
}).catch(_reject)
})
}
}
module.exports = Connection;
module.exports = Connection

View File

@@ -1,5 +1,5 @@
'use strict';
const flagIndices = require('./txflags').txFlagIndices.AccountSet;
'use strict' // eslint-disable-line strict
const flagIndices = require('./txflags').txFlagIndices.AccountSet
const accountRootFlags = {
PasswordSpent: 0x00010000, // password set fee is spent
@@ -10,7 +10,7 @@ const accountRootFlags = {
NoFreeze: 0x00200000, // permanently disallowed freezing trustlines
GlobalFreeze: 0x00400000, // trustlines globally frozen
DefaultRipple: 0x00800000
};
}
const AccountFlags = {
passwordSpent: accountRootFlags.PasswordSpent,
@@ -21,7 +21,7 @@ const AccountFlags = {
noFreeze: accountRootFlags.NoFreeze,
globalFreeze: accountRootFlags.GlobalFreeze,
defaultRipple: accountRootFlags.DefaultRipple
};
}
const AccountFlagIndices = {
requireDestinationTag: flagIndices.asfRequireDest,
@@ -32,7 +32,7 @@ const AccountFlagIndices = {
noFreeze: flagIndices.asfNoFreeze,
globalFreeze: flagIndices.asfGlobalFreeze,
defaultRipple: flagIndices.asfDefaultRipple
};
}
const AccountFields = {
EmailHash: {name: 'emailHash', encoding: 'hex',
@@ -40,10 +40,10 @@ const AccountFields = {
MessageKey: {name: 'messageKey'},
Domain: {name: 'domain', encoding: 'hex'},
TransferRate: {name: 'transferRate', defaults: 0, shift: 9}
};
}
module.exports = {
AccountFields,
AccountFlagIndices,
AccountFlags
};
}

View File

@@ -1,43 +1,43 @@
'use strict'; // eslint-disable-line
const util = require('util');
const browserHacks = require('./browser-hacks');
'use strict' // eslint-disable-line strict
const util = require('util')
const browserHacks = require('./browser-hacks')
// this is needed because extending builtins doesn't work in babel 6.x
function extendableBuiltin(cls) {
function ExtendableBuiltin() {
cls.apply(this, arguments);
cls.apply(this, arguments)
}
ExtendableBuiltin.prototype = Object.create(cls.prototype);
browserHacks.setPrototypeOf(ExtendableBuiltin, cls);
return ExtendableBuiltin;
ExtendableBuiltin.prototype = Object.create(cls.prototype)
browserHacks.setPrototypeOf(ExtendableBuiltin, cls)
return ExtendableBuiltin
}
class RippleError extends extendableBuiltin(Error) {
constructor(message, data) {
super(message);
super(message)
this.name = browserHacks.getConstructorName(this);
this.message = message;
this.data = data;
this.name = browserHacks.getConstructorName(this)
this.message = message
this.data = data
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor.name);
Error.captureStackTrace(this, this.constructor.name)
}
}
toString() {
let result = '[' + this.name + '(' + this.message;
let result = '[' + this.name + '(' + this.message
if (this.data) {
result += ', ' + util.inspect(this.data);
result += ', ' + util.inspect(this.data)
}
result += ')]';
return result;
result += ')]'
return result
}
/* console.log in node uses util.inspect on object, and util.inspect allows
us to cutomize its output:
https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects */
inspect() {
return this.toString();
return this.toString()
}
}
@@ -63,20 +63,20 @@ class ValidationError extends RippleError {}
class NotFoundError extends RippleError {
constructor(message) {
super(message || 'Not found');
super(message || 'Not found')
}
}
class MissingLedgerHistoryError extends RippleError {
constructor(message) {
super(message || 'Server is missing ledger history in the specified range');
super(message || 'Server is missing ledger history in the specified range')
}
}
class PendingLedgerVersionError extends RippleError {
constructor(message) {
super(message || 'maxLedgerVersion is greater than server\'s'
+ ' most recent validated ledger');
+ ' most recent validated ledger')
}
}
@@ -95,4 +95,4 @@ module.exports = {
PendingLedgerVersionError,
MissingLedgerHistoryError,
LedgerVersionError
};
}

View File

@@ -1,5 +1,5 @@
'use strict';
const utils = require('./utils');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
module.exports = {
Connection: require('./connection'),
@@ -19,4 +19,4 @@ module.exports = {
iso8601ToRippleTime: utils.iso8601ToRippleTime,
rippleTimeToISO8601: utils.rippleTimeToISO8601,
isValidSecret: utils.isValidSecret
};
}

View File

@@ -1,61 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const ranges = Symbol();
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const ranges = Symbol()
function mergeIntervals(intervals: Array<[number, number]>) {
const stack = [[-Infinity, -Infinity]];
const stack = [[-Infinity, -Infinity]]
_.forEach(_.sortBy(intervals, x => x[0]), interval => {
const lastInterval = stack.pop();
const lastInterval = stack.pop()
if (interval[0] <= lastInterval[1] + 1) {
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])]);
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])])
} else {
stack.push(lastInterval);
stack.push(interval);
stack.push(lastInterval)
stack.push(interval)
}
});
return stack.slice(1);
})
return stack.slice(1)
}
class RangeSet {
constructor() {
this.reset();
this.reset()
}
reset() {
this[ranges] = [];
this[ranges] = []
}
serialize() {
return this[ranges].map(range =>
range[0].toString() + '-' + range[1].toString()).join(',');
range[0].toString() + '-' + range[1].toString()).join(',')
}
addRange(start: number, end: number) {
assert(start <= end, 'invalid range');
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]));
assert(start <= end, 'invalid range')
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]))
}
addValue(value: number) {
this.addRange(value, value);
this.addRange(value, value)
}
parseAndAddRanges(rangesString: string) {
const rangeStrings = rangesString.split(',');
const rangeStrings = rangesString.split(',')
_.forEach(rangeStrings, rangeString => {
const range = rangeString.split('-').map(Number);
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
});
const range = rangeString.split('-').map(Number)
this.addRange(range[0], range.length === 1 ? range[0] : range[1])
})
}
containsRange(start: number, end: number) {
return _.some(this[ranges], range => range[0] <= start && range[1] >= end);
return _.some(this[ranges], range => range[0] <= start && range[1] >= end)
}
containsValue(value: number) {
return this.containsRange(value, value);
return this.containsRange(value, value)
}
}
module.exports.RangeSet = RangeSet;
module.exports.RangeSet = RangeSet

View File

@@ -1,12 +1,12 @@
// flow is disabled for this file until support for requiring json is added:
// https://github.com/facebook/flow/issues/167
'use strict';
const _ = require('lodash');
const assert = require('assert');
const Ajv = require('ajv');
const ValidationError = require('./errors').ValidationError;
const {isValidAddress} = require('ripple-address-codec');
const {isValidSecret} = require('./utils');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const Ajv = require('ajv')
const ValidationError = require('./errors').ValidationError
const {isValidAddress} = require('ripple-address-codec')
const {isValidSecret} = require('./utils')
function loadSchemas() {
// listed explicitly for webpack (instead of scanning schemas directory)
@@ -16,11 +16,13 @@ function loadSchemas() {
require('./schemas/objects/hash128.json'),
require('./schemas/objects/hash256.json'),
require('./schemas/objects/sequence.json'),
require('./schemas/objects/signature.json'),
require('./schemas/objects/issue.json'),
require('./schemas/objects/ledgerversion.json'),
require('./schemas/objects/max-adjustment.json'),
require('./schemas/objects/memo.json'),
require('./schemas/objects/memos.json'),
require('./schemas/objects/public-key.json'),
require('./schemas/objects/uint32.json'),
require('./schemas/objects/value.json'),
require('./schemas/objects/source-adjustment.json'),
@@ -46,17 +48,20 @@ function loadSchemas() {
require('./schemas/objects/settings.json'),
require('./schemas/specifications/settings.json'),
require('./schemas/specifications/payment.json'),
require('./schemas/specifications/suspended-payment-cancellation.json'),
require('./schemas/specifications/escrow-cancellation.json'),
require('./schemas/specifications/order-cancellation.json'),
require('./schemas/specifications/order.json'),
require('./schemas/specifications/suspended-payment-execution.json'),
require('./schemas/specifications/suspended-payment-creation.json'),
require('./schemas/specifications/escrow-execution.json'),
require('./schemas/specifications/escrow-creation.json'),
require('./schemas/specifications/payment-channel-create.json'),
require('./schemas/specifications/payment-channel-fund.json'),
require('./schemas/specifications/payment-channel-claim.json'),
require('./schemas/specifications/trustline.json'),
require('./schemas/output/sign.json'),
require('./schemas/output/submit.json'),
require('./schemas/output/get-account-info.json'),
require('./schemas/output/get-balances.json'),
require('./schemas/output/get-balance-sheet'),
require('./schemas/output/get-balance-sheet.json'),
require('./schemas/output/get-ledger.json'),
require('./schemas/output/get-orderbook.json'),
require('./schemas/output/get-orders.json'),
@@ -88,35 +93,38 @@ function loadSchemas() {
require('./schemas/input/prepare-trustline.json'),
require('./schemas/input/prepare-order-cancellation.json'),
require('./schemas/input/prepare-settings.json'),
require('./schemas/input/prepare-suspended-payment-creation.json'),
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
require('./schemas/input/prepare-suspended-payment-execution.json'),
require('./schemas/input/compute-ledger-hash'),
require('./schemas/input/prepare-escrow-creation.json'),
require('./schemas/input/prepare-escrow-cancellation.json'),
require('./schemas/input/prepare-escrow-execution.json'),
require('./schemas/input/prepare-payment-channel-create.json'),
require('./schemas/input/prepare-payment-channel-fund.json'),
require('./schemas/input/prepare-payment-channel-claim.json'),
require('./schemas/input/compute-ledger-hash.json'),
require('./schemas/input/sign.json'),
require('./schemas/input/submit.json'),
require('./schemas/input/generate-address.json'),
require('./schemas/input/combine.json')
];
const titles = _.map(schemas, schema => schema.title);
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
const ajv = new Ajv();
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title));
ajv.addFormat('address', isValidAddress);
ajv.addFormat('secret', isValidSecret);
return ajv;
]
const titles = _.map(schemas, schema => schema.title)
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1))
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
const ajv = new Ajv()
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title))
ajv.addFormat('address', isValidAddress)
ajv.addFormat('secret', isValidSecret)
return ajv
}
const ajv = loadSchemas();
const ajv = loadSchemas()
function schemaValidate(schemaName: string, object: any): void {
const isValid = ajv.validate(schemaName, object);
const isValid = ajv.validate(schemaName, object)
if (!isValid) {
throw new ValidationError(ajv.errorsText());
throw new ValidationError(ajv.errorsText())
}
}
module.exports = {
schemaValidate,
isValidSecret
};
}

View File

@@ -1,18 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "prepareSuspendedPaymentCreationParameters",
"title": "prepareEscrowCancellationParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"suspendedPaymentCreation": {
"$ref": "suspendedPaymentCreation",
"description": "The specification of the suspended payment creation to prepare."
"escrowCancellation": {
"$ref": "escrowCancellation",
"description": "The specification of the escrow cancellation to prepare."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "suspendedPaymentCreation"]
"required": ["address", "escrowCancellation"]
}

View File

@@ -1,18 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "prepareSuspendedPaymentExecutionParameters",
"title": "prepareEscrowCreationParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"suspendedPaymentExecution": {
"$ref": "suspendedPaymentExecution",
"description": "The specification of the suspended payment execution to prepare."
"escrowCreation": {
"$ref": "escrowCreation",
"description": "The specification of the escrow creation to prepare."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "suspendedPaymentExecution"]
"required": ["address", "escrowCreation"]
}

View File

@@ -1,18 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "prepareSuspendedPaymentCancellationParameters",
"title": "prepareEscrowExecutionParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"suspendedPaymentCancellation": {
"$ref": "suspendedPaymentCancellation",
"description": "The specification of the suspended payment cancellation to prepare."
"escrowExecution": {
"$ref": "escrowExecution",
"description": "The specification of the escrow execution to prepare."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "suspendedPaymentCancellation"]
"required": ["address", "escrowExecution"]
}

View File

@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "preparePaymentChannelClaimParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"paymentChannelClaim": {
"$ref": "paymentChannelClaim",
"description": "Details of the channel and claim."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "paymentChannelClaim"]
}

View File

@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "preparePaymentChannelCreateParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"paymentChannelCreate": {
"$ref": "paymentChannelCreate",
"description": "The specification of the payment channel to create."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "paymentChannelCreate"]
}

View File

@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "preparePaymentChannelFundParameters",
"type": "object",
"properties": {
"address": {
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"paymentChannelFund": {
"$ref": "paymentChannelFund",
"description": "The channel to fund, and the details of how to fund it."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "paymentChannelFund"]
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "publicKey",
"description": "The hexadecimal representation of a secp256k1 or Ed25519 public key.",
"type": "string",
"pattern": "^[A-F0-9]+$"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "signature",
"description": "The hexadecimal representation of a signature.",
"type": "string",
"pattern": "^[A-F0-9]+$"
}

View File

@@ -5,6 +5,7 @@
"description": "The type of the transaction.",
"type": "string",
"enum": ["payment", "order", "orderCancellation", "trustline", "settings",
"suspendedPaymentCreation", "suspendedPaymentCancellation",
"suspendedPaymentExecution"]
"escrowCreation", "escrowCancellation",
"escrowExecution", "paymentChannelCreate",
"paymentChannelFund", "paymentChannelClaim"]
}

View File

@@ -59,20 +59,38 @@
},
{
"properties": {
"type": {"enum": ["suspendedPaymentCreation"]},
"specification": {"$ref": "suspendedPaymentCreation"}
"type": {"enum": ["escrowCreation"]},
"specification": {"$ref": "escrowCreation"}
}
},
{
"properties": {
"type": {"enum": ["suspendedPaymentCancellation"]},
"specification": {"$ref": "suspendedPaymentCancellation"}
"type": {"enum": ["escrowCancellation"]},
"specification": {"$ref": "escrowCancellation"}
}
},
{
"properties": {
"type": {"enum": ["suspendedPaymentExecution"]},
"specification": {"$ref": "suspendedPaymentExecution"}
"type": {"enum": ["escrowExecution"]},
"specification": {"$ref": "escrowExecution"}
}
},
{
"properties": {
"type": {"enum": ["paymentChannelCreate"]},
"specification": {"$ref": "paymentChannelCreate"}
}
},
{
"properties": {
"type": {"enum": ["paymentChannelFund"]},
"specification": {"$ref": "paymentChannelFund"}
}
},
{
"properties": {
"type": {"enum": ["paymentChannelClaim"]},
"specification": {"$ref": "paymentChannelClaim"}
}
}
]

View File

@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "escrowCancellation",
"link": "escrow-cancellation",
"type": "object",
"properties": {
"memos": {"$ref": "memos"},
"owner": {
"$ref": "address",
"description": "The address of the owner of the escrow to cancel."
},
"escrowSequence": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of the [Escrow Creation](#escrow-creation) transaction for the escrow to cancel."
}
},
"required": ["owner", "escrowSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,42 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "escrowCreation",
"link": "escrow-creation",
"type": "object",
"properties": {
"amount": {
"$ref": "value",
"description": "Amount of XRP for sender to escrow."
},
"destination": {
"$ref": "address",
"description": "Address to receive escrowed XRP."
},
"memos": {"$ref": "memos"},
"condition": {
"type": "string",
"description": "If present, fulfillment is required upon execution.",
"pattern": "^[A-F0-9]{0,256}$"
},
"allowCancelAfter": {
"type": "string",
"format": "date-time",
"description": "If present, the escrow may be cancelled after this time."
},
"allowExecuteAfter": {
"type": "string",
"format": "date-time",
"description": "If present, the escrow can not be executed before this time."
},
"sourceTag": {
"$ref": "tag",
"description": "Source tag."
},
"destinationTag": {
"$ref": "tag",
"description": "Destination tag."
}
},
"required": ["amount", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "escrowExecution",
"link": "escrow-execution",
"type": "object",
"properties": {
"memos": {"$ref": "memos"},
"owner": {
"$ref": "address",
"description": "The address of the owner of the escrow to execute."
},
"escrowSequence": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of the [Escrow Creation](#escrow-creation) transaction for the escrow to execute."
},
"condition": {
"type": "string",
"description": "The original `condition` from the escrow creation transaction. This is sha256 hash of `fulfillment` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old condition with the new condition.",
"pattern": "^[A-F0-9]{0,256}$"
},
"fulfillment": {
"type": "string",
"description": "A value that produces the condition when hashed. It must be 32 charaters long and contain only 8-bit characters.",
"pattern": "^[A-F0-9]+$"
}
},
"required": ["owner", "escrowSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "paymentChannelClaim",
"link": "payment-channel-claim",
"type": "object",
"properties": {
"channel": {
"$ref": "hash256",
"description": "256-bit hexadecimal channel identifier."
},
"amount": {
"$ref": "value",
"description": "XRP balance of this channel after claim is processed."
},
"balance": {
"$ref": "value",
"description": "Amount of XRP authorized by signature."
},
"signature": {
"$ref": "signature",
"description": "Signature of this claim."
},
"publicKey": {
"$ref": "publicKey",
"description": "Public key of the channel's sender"
},
"renew": {
"type": "boolean",
"description": "Clear the channel's expiration time."
},
"close": {
"type": "boolean",
"description": "Request to close the channel."
}
},
"required": ["channel"],
"additionalProperties": false
}

View File

@@ -0,0 +1,39 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "paymentChannelCreate",
"link": "payment-channel-create",
"type": "object",
"properties": {
"amount": {
"$ref": "value",
"description": "Amount of XRP for sender to set aside in this channel."
},
"destination": {
"$ref": "address",
"description": "Address to receive XRP claims against this channel."
},
"settleDelay": {
"type": "number",
"description": "Amount of time the source address must wait before closing the channel if it has unclaimed XRP."
},
"publicKey": {
"$ref": "publicKey",
"description": "Public key of the key pair the source will use to sign claims against this channel."
},
"cancelAfter": {
"type": "string",
"format": "date-time",
"description": "Time when this channel expires."
},
"sourceTag": {
"$ref": "tag",
"description": "Source tag."
},
"destinationTag": {
"$ref": "tag",
"description": "Destination tag."
}
},
"required": ["amount", "destination", "settleDelay", "publicKey"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "paymentChannelFund",
"link": "payment-channel-fund",
"type": "object",
"properties": {
"amount": {
"$ref": "value",
"description": "Amount of XRP to fund the channel with."
},
"channel": {
"$ref": "hash256",
"description": "256-bit hexadecimal channel identifier."
},
"expiration": {
"type": "string",
"format": "date-time",
"description": "New expiration for this channel."
}
},
"required": ["amount", "channel"],
"additionalProperties": false
}

View File

@@ -1,19 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspendedPaymentCancellation",
"link": "suspended-payment-cancellation",
"type": "object",
"properties": {
"memos": {"$ref": "memos"},
"owner": {
"$ref": "address",
"description": "The address of the owner of the suspended payment to cancel."
},
"suspensionSequence": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to cancel."
}
},
"required": ["owner", "suspensionSequence"],
"additionalProperties": false
}

View File

@@ -1,33 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspendedPaymentCreation",
"link": "suspended-payment-creation",
"type": "object",
"properties": {
"source": {
"$ref": "maxAdjustment",
"description": "Fields pertaining to the source of the payment."
},
"destination": {
"$ref": "destinationExactAdjustment",
"description": "Fields pertaining to the destination of the payment."
},
"memos": {"$ref": "memos"},
"digest": {
"$ref": "hash256",
"description": "If present, proof is required upon execution."
},
"allowCancelAfter": {
"type": "string",
"format": "date-time",
"description": "If present, the suspended payment may be cancelled after this time."
},
"allowExecuteAfter": {
"type": "string",
"format": "date-time",
"description": "If present, the suspended payment can not be executed before this time."
}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -1,34 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "suspendedPaymentExecution",
"link": "suspended-payment-execution",
"type": "object",
"properties": {
"memos": {"$ref": "memos"},
"owner": {
"$ref": "address",
"description": "The address of the owner of the suspended payment to execute."
},
"suspensionSequence": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to execute."
},
"method": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "The method for verifying the proof; only method `1` is supported."
},
"digest": {
"$ref": "hash256",
"description": "The original `digest` from the suspended payment creation transaction. This is sha256 hash of `proof` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old digest with the new digest."
},
"proof": {
"type": "string",
"description": "A value that produces the digest when hashed. It must be 32 charaters long and contain only 8-bit characters.",
"pattern": "^[\\x00-\\xFF]{32}$"
}
},
"required": ["owner", "suspensionSequence"],
"additionalProperties": false
}

View File

@@ -1,7 +1,7 @@
'use strict'; // eslint-disable-line
const _ = require('lodash');
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
import type {Connection} from './connection';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils')
import type {Connection} from './connection'
export type GetServerInfoResponse = {
buildVersion: string,
@@ -34,46 +34,46 @@ export type GetServerInfoResponse = {
function renameKeys(object, mapping) {
_.forEach(mapping, (to, from) => {
object[to] = object[from];
delete object[from];
});
object[to] = object[from]
delete object[from]
})
}
function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
return connection.request({command: 'server_info'}).then(response => {
const info = convertKeysFromSnakeCaseToCamelCase(response.info);
renameKeys(info, {hostid: 'hostID'});
const info = convertKeysFromSnakeCaseToCamelCase(response.info)
renameKeys(info, {hostid: 'hostID'})
if (info.validatedLedger) {
renameKeys(info.validatedLedger, {
baseFeeXrp: 'baseFeeXRP',
reserveBaseXrp: 'reserveBaseXRP',
reserveIncXrp: 'reserveIncrementXRP',
seq: 'ledgerVersion'
});
})
info.validatedLedger.baseFeeXRP =
info.validatedLedger.baseFeeXRP.toString();
info.validatedLedger.baseFeeXRP.toString()
info.validatedLedger.reserveBaseXRP =
info.validatedLedger.reserveBaseXRP.toString();
info.validatedLedger.reserveBaseXRP.toString()
info.validatedLedger.reserveIncrementXRP =
info.validatedLedger.reserveIncrementXRP.toString();
info.validatedLedger.reserveIncrementXRP.toString()
}
return info;
});
return info
})
}
function computeFeeFromServerInfo(cushion: number,
serverInfo: GetServerInfoResponse
): number {
return (Number(serverInfo.validatedLedger.baseFeeXRP)
* Number(serverInfo.loadFactor) * cushion).toString();
* Number(serverInfo.loadFactor) * cushion).toString()
}
function getFee(connection: Connection, cushion: number) {
return getServerInfo(connection).then(
_.partial(computeFeeFromServerInfo, cushion));
_.partial(computeFeeFromServerInfo, cushion))
}
module.exports = {
getServerInfo,
getFee
};
}

View File

@@ -1,4 +1,4 @@
'use strict';
'use strict' // eslint-disable-line strict
const txFlags = {
// Universal flags can apply to any transaction type
@@ -35,8 +35,13 @@ const txFlags = {
NoRippleDirect: 0x00010000,
PartialPayment: 0x00020000,
LimitQuality: 0x00040000
},
PaymentChannelClaim: {
Renew: 0x00010000,
Close: 0x00020000
}
};
}
// The following are integer (as opposed to bit) flags
// that can be set for particular transactions in the
@@ -52,9 +57,9 @@ const txFlagIndices = {
asfGlobalFreeze: 7,
asfDefaultRipple: 8
}
};
}
module.exports = {
txFlags,
txFlagIndices
};
}

View File

@@ -1,5 +1,5 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
export type RippledAmountIOU = {
currency: string,

View File

@@ -1,59 +1,59 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const {deriveKeypair} = require('ripple-keypairs');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const {deriveKeypair} = require('ripple-keypairs')
import type {Amount, RippledAmount} from './types.js';
import type {Amount, RippledAmount} from './types.js'
function isValidSecret(secret: string): boolean {
try {
deriveKeypair(secret);
return true;
deriveKeypair(secret)
return true
} catch (err) {
return false;
return false
}
}
function dropsToXrp(drops: string): string {
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
return (new BigNumber(drops)).dividedBy(1000000.0).toString()
}
function xrpToDrops(xrp: string): string {
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
return (new BigNumber(xrp)).times(1000000.0).floor().toString()
}
function toRippledAmount(amount: Amount): RippledAmount {
if (amount.currency === 'XRP') {
return xrpToDrops(amount.value);
return xrpToDrops(amount.value)
}
return {
currency: amount.currency,
issuer: amount.counterparty ? amount.counterparty :
(amount.issuer ? amount.issuer : undefined),
value: amount.value
};
}
}
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
let newKey;
let newKey
return _.reduce(obj, (result, value, key) => {
newKey = key;
newKey = key
// taking this out of function leads to error in PhantomJS
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value);
return result;
}, {});
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
return result
}, {})
}
return obj;
return obj
}
function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined);
return _.omit(obj, _.isUndefined)
}
/**
@@ -62,7 +62,7 @@ function removeUndefined(obj: Object): Object {
*
*/
function rippleToUnixTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000;
return (rpepoch + 0x386D4380) * 1000
}
/**
@@ -70,15 +70,15 @@ function rippleToUnixTimestamp(rpepoch: number): number {
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function unixToRippleTimestamp(timestamp: number): number {
return Math.round(timestamp / 1000) - 0x386D4380;
return Math.round(timestamp / 1000) - 0x386D4380
}
function rippleTimeToISO8601(rippleTime: number): string {
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString();
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString()
}
function iso8601ToRippleTime(iso8601: string): number {
return unixToRippleTimestamp(Date.parse(iso8601));
return unixToRippleTimestamp(Date.parse(iso8601))
}
module.exports = {
@@ -90,4 +90,4 @@ module.exports = {
rippleTimeToISO8601,
iso8601ToRippleTime,
isValidSecret
};
}

View File

@@ -1,25 +1,25 @@
/* @flow */
'use strict';
const _ = require('lodash');
const ValidationError = require('./errors').ValidationError;
const schemaValidate = require('./schema-validator').schemaValidate;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const ValidationError = require('./errors').ValidationError
const schemaValidate = require('./schema-validator').schemaValidate
function error(text) {
return new ValidationError(text);
return new ValidationError(text)
}
function validateLedgerRange(options) {
if (!_.isUndefined(options) && !_.isUndefined(options.minLedgerVersion)
&& !_.isUndefined(options.maxLedgerVersion)) {
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
throw error('minLedgerVersion must not be greater than maxLedgerVersion');
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
}
}
}
function validateOptions(schema, instance) {
schemaValidate(schema, instance);
validateLedgerRange(instance.options);
schemaValidate(schema, instance)
validateLedgerRange(instance.options)
}
module.exports = {
@@ -40,12 +40,18 @@ module.exports = {
_.partial(schemaValidate, 'prepareOrderCancellationParameters'),
prepareTrustline: _.partial(schemaValidate, 'prepareTrustlineParameters'),
prepareSettings: _.partial(schemaValidate, 'prepareSettingsParameters'),
prepareSuspendedPaymentCreation: _.partial(schemaValidate,
'prepareSuspendedPaymentCreationParameters'),
prepareSuspendedPaymentCancellation: _.partial(schemaValidate,
'prepareSuspendedPaymentCancellationParameters'),
prepareSuspendedPaymentExecution: _.partial(schemaValidate,
'prepareSuspendedPaymentExecutionParameters'),
prepareEscrowCreation: _.partial(schemaValidate,
'prepareEscrowCreationParameters'),
prepareEscrowCancellation: _.partial(schemaValidate,
'prepareEscrowCancellationParameters'),
prepareEscrowExecution: _.partial(schemaValidate,
'prepareEscrowExecutionParameters'),
preparePaymentChannelCreate: _.partial(schemaValidate,
'preparePaymentChannelCreateParameters'),
preparePaymentChannelFund: _.partial(schemaValidate,
'preparePaymentChannelFundParameters'),
preparePaymentChannelClaim: _.partial(schemaValidate,
'preparePaymentChannelClaimParameters'),
sign: _.partial(schemaValidate, 'signParameters'),
combine: _.partial(schemaValidate, 'combineParameters'),
submit: _.partial(schemaValidate, 'submitParameters'),
@@ -53,4 +59,4 @@ module.exports = {
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
apiOptions: _.partial(schemaValidate, 'api-options'),
instructions: _.partial(schemaValidate, 'instructions')
};
}

View File

@@ -1,6 +1,6 @@
'use strict';
'use strict' // eslint-disable-line strict
const {EventEmitter} = require('events');
const {EventEmitter} = require('events')
function unsused() {}
@@ -10,50 +10,50 @@ function unsused() {}
*/
class WSWrapper extends EventEmitter {
constructor(url, protocols = null, websocketOptions = {}) {
super();
unsused(protocols);
unsused(websocketOptions);
this.setMaxListeners(Infinity);
super()
unsused(protocols)
unsused(websocketOptions)
this.setMaxListeners(Infinity)
this._ws = new WebSocket(url);
this._ws = new WebSocket(url)
this._ws.onclose = () => {
this.emit('close');
};
this.emit('close')
}
this._ws.onopen = () => {
this.emit('open');
};
this.emit('open')
}
this._ws.onerror = error => {
this.emit('error', error);
};
this.emit('error', error)
}
this._ws.onmessage = message => {
this.emit('message', message.data);
};
this.emit('message', message.data)
}
}
close() {
if (this.readyState === 1) {
this._ws.close();
this._ws.close()
}
}
send(message) {
this._ws.send(message);
this._ws.send(message)
}
get readyState() {
return this._ws.readyState;
return this._ws.readyState
}
}
WSWrapper.CONNECTING = 0;
WSWrapper.OPEN = 1;
WSWrapper.CLOSING = 2;
WSWrapper.CLOSED = 3;
WSWrapper.CONNECTING = 0
WSWrapper.OPEN = 1
WSWrapper.CLOSING = 2
WSWrapper.CLOSED = 3
module.exports = WSWrapper;
module.exports = WSWrapper

View File

@@ -1,86 +1,86 @@
/* eslint-disable new-cap */
'use strict';
'use strict' // eslint-disable-line strict
const assert = require('assert');
const _ = require('lodash');
const jayson = require('jayson');
const assert = require('assert')
const _ = require('lodash')
const jayson = require('jayson')
const RippleAPI = require('./api').RippleAPI;
const RippleAPI = require('./api').RippleAPI
/* istanbul ignore next */
function createHTTPServer(options, httpPort) {
const rippleAPI = new RippleAPI(options);
const rippleAPI = new RippleAPI(options)
const methodNames = _.filter(_.keys(RippleAPI.prototype), k => {
return typeof RippleAPI.prototype[k] === 'function'
&& k !== 'connect'
&& k !== 'disconnect'
&& k !== 'constructor'
&& k !== 'RippleAPI';
});
&& k !== 'RippleAPI'
})
function applyPromiseWithCallback(fnName, callback, args_) {
try {
let args = args_;
let args = args_
if (!_.isArray(args_)) {
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName]);
args = fnParameters.map(name => args_[name]);
const defaultArgs = _.omit(args_, fnParameters);
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName])
args = fnParameters.map(name => args_[name])
const defaultArgs = _.omit(args_, fnParameters)
assert(_.size(defaultArgs) <= 1,
'Function must have no more than one default argument');
'Function must have no more than one default argument')
if (_.size(defaultArgs) > 0) {
args.push(defaultArgs[_.keys(defaultArgs)[0]]);
args.push(defaultArgs[_.keys(defaultArgs)[0]])
}
}
Promise.resolve(rippleAPI[fnName].apply(rippleAPI, args))
Promise.resolve(rippleAPI[fnName](...args))
.then(res => callback(null, res))
.catch(err => {
callback({code: 99, message: err.message, data: {name: err.name}});
});
callback({code: 99, message: err.message, data: {name: err.name}})
})
} catch (err) {
callback({code: 99, message: err.message, data: {name: err.name}});
callback({code: 99, message: err.message, data: {name: err.name}})
}
}
const methods = {};
const methods = {}
_.forEach(methodNames, fn => {
methods[fn] = jayson.Method((args, cb) => {
applyPromiseWithCallback(fn, cb, args);
}, {collect: true});
});
applyPromiseWithCallback(fn, cb, args)
}, {collect: true})
})
const server = jayson.server(methods);
let httpServer = null;
const server = jayson.server(methods)
let httpServer = null
return {
server: server,
start: function() {
if (httpServer !== null) {
return Promise.reject('Already started');
return Promise.reject('Already started')
}
return new Promise((resolve) => {
return new Promise(resolve => {
rippleAPI.connect().then(() => {
httpServer = server.http();
httpServer.listen(httpPort, resolve);
});
});
httpServer = server.http()
httpServer.listen(httpPort, resolve)
})
})
},
stop: function() {
if (httpServer === null) {
return Promise.reject('Not started');
return Promise.reject('Not started')
}
return new Promise((resolve) => {
rippleAPI.disconnect();
return new Promise(resolve => {
rippleAPI.disconnect()
httpServer.close(() => {
httpServer = null;
resolve();
});
});
httpServer = null
resolve()
})
})
}
};
}
}
module.exports = {
createHTTPServer
};
}

View File

@@ -1,4 +1,4 @@
'use strict';
'use strict' // eslint-disable-line strict
module.exports = {
RippleAPI: require('./api').RippleAPI,
@@ -6,4 +6,4 @@ module.exports = {
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast,
// HTTP server is experimental
createHTTPServer: require('./http').createHTTPServer
};
}

View File

@@ -1,8 +1,8 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate, removeUndefined} = utils.common;
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate, removeUndefined} = utils.common
type AccountData = {
Sequence: number,
@@ -39,7 +39,7 @@ type AccountInfoResponse = {
}
function formatAccountInfo(response: AccountDataResponse) {
const data = response.account_data;
const data = response.account_data
return removeUndefined({
sequence: data.Sequence,
xrpBalance: utils.common.dropsToXrp(data.Balance),
@@ -47,20 +47,20 @@ function formatAccountInfo(response: AccountDataResponse) {
previousInitiatedTransactionID: data.AccountTxnID,
previousAffectingTransactionID: data.PreviousTxnID,
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
});
})
}
function getAccountInfo(address: string, options: AccountInfoOptions = {}
): Promise<AccountInfoResponse> {
validate.getAccountInfo({address, options});
validate.getAccountInfo({address, options})
const request = {
command: 'account_info',
account: address,
ledger_index: options.ledgerVersion || 'validated'
};
}
return this.connection.request(request).then(formatAccountInfo);
return this.connection.request(request).then(formatAccountInfo)
}
module.exports = getAccountInfo;
module.exports = getAccountInfo

View File

@@ -1,10 +1,10 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
import type {Amount} from '../common/types.js';
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
import type {Amount} from '../common/types.js'
type BalanceSheetOptions = {
excludeAddresses?: Array<string>,
@@ -21,35 +21,35 @@ type GetBalanceSheet = {
}
function formatBalanceSheet(balanceSheet): GetBalanceSheet {
const result = {};
const result = {}
if (!_.isUndefined(balanceSheet.balances)) {
result.balances = [];
result.balances = []
_.forEach(balanceSheet.balances, (balances, counterparty) => {
_.forEach(balances, (balance) => {
result.balances.push(Object.assign({counterparty}, balance));
});
});
_.forEach(balances, balance => {
result.balances.push(Object.assign({counterparty}, balance))
})
})
}
if (!_.isUndefined(balanceSheet.assets)) {
result.assets = [];
result.assets = []
_.forEach(balanceSheet.assets, (assets, counterparty) => {
_.forEach(assets, (balance) => {
result.assets.push(Object.assign({counterparty}, balance));
});
});
_.forEach(assets, balance => {
result.assets.push(Object.assign({counterparty}, balance))
})
})
}
if (!_.isUndefined(balanceSheet.obligations)) {
result.obligations = _.map(balanceSheet.obligations, (value, currency) =>
({currency, value}));
({currency, value}))
}
return result;
return result
}
function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
): Promise<GetBalanceSheet> {
validate.getBalanceSheet({address, options});
validate.getBalanceSheet({address, options})
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const request = {
@@ -58,10 +58,10 @@ function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
strict: true,
hotwallet: _options.excludeAddresses,
ledger_index: _options.ledgerVersion
};
}
return this.connection.request(request).then(formatBalanceSheet);
});
return this.connection.request(request).then(formatBalanceSheet)
})
}
module.exports = getBalanceSheet;
module.exports = getBalanceSheet

View File

@@ -1,9 +1,9 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate} = utils.common
import type {Connection} from '../common/connection.js'
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'
type Balance = {
@@ -19,38 +19,38 @@ function getTrustlineBalanceAmount(trustline: Trustline) {
currency: trustline.specification.currency,
counterparty: trustline.specification.counterparty,
value: trustline.state.balance
};
}
}
function formatBalances(options, balances) {
const result = balances.trustlines.map(getTrustlineBalanceAmount);
const result = balances.trustlines.map(getTrustlineBalanceAmount)
if (!(options.counterparty ||
(options.currency && options.currency !== 'XRP')
)) {
const xrpBalance = {
currency: 'XRP',
value: balances.xrp
};
result.unshift(xrpBalance);
}
result.unshift(xrpBalance)
}
if (options.limit && result.length > options.limit) {
const toRemove = result.length - options.limit;
result.splice(-toRemove, toRemove);
const toRemove = result.length - options.limit
result.splice(-toRemove, toRemove)
}
return result;
return result
}
function getLedgerVersionHelper(connection: Connection, optionValue?: number
): Promise<number> {
if (optionValue !== undefined && optionValue !== null) {
return Promise.resolve(optionValue);
return Promise.resolve(optionValue)
}
return connection.getLedgerVersion();
return connection.getLedgerVersion()
}
function getBalances(address: string, options: TrustlinesOptions = {}
): Promise<GetBalances> {
validate.getTrustlines({address, options});
validate.getTrustlines({address, options})
return Promise.all([
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
@@ -58,7 +58,7 @@ function getBalances(address: string, options: TrustlinesOptions = {}
utils.getXRPBalance(this.connection, address, ledgerVersion)),
this.getTrustlines(address, options)
]).then(results =>
formatBalances(options, {xrp: results[0], trustlines: results[1]}));
formatBalances(options, {xrp: results[0], trustlines: results[1]}))
}
module.exports = getBalances;
module.exports = getBalances

View File

@@ -1,9 +1,9 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate} = utils.common;
const parseLedger = require('./parse/ledger');
import type {GetLedger} from './types.js';
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate} = utils.common
const parseLedger = require('./parse/ledger')
import type {GetLedger} from './types.js'
type LedgerOptions = {
ledgerVersion?: number,
@@ -14,7 +14,7 @@ type LedgerOptions = {
function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
validate.getLedger({options});
validate.getLedger({options})
const request = {
command: 'ledger',
@@ -22,10 +22,10 @@ function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
expand: options.includeAllData,
transactions: options.includeTransactions,
accounts: options.includeState
};
}
return this.connection.request(request).then(response =>
parseLedger(response.ledger));
parseLedger(response.ledger))
}
module.exports = getLedger;
module.exports = getLedger

View File

@@ -1,12 +1,12 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
const parseOrderbookOrder = require('./parse/orderbook-order');
import type {Connection} from '../common/connection.js';
import type {OrdersOptions, OrderSpecification} from './types.js';
import type {Amount, Issue} from '../common/types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
const parseOrderbookOrder = require('./parse/orderbook-order')
import type {Connection} from '../common/connection.js'
import type {OrdersOptions, OrderSpecification} from './types.js'
import type {Amount, Issue} from '../common/types.js'
type Orderbook = {
base: Issue,
@@ -46,31 +46,31 @@ function getBookOffers(connection: Connection, account: string,
ledger_index: ledgerVersion || 'validated',
limit: limit,
taker: account
})).then(data => data.offers);
})).then(data => data.offers)
}
function isSameIssue(a: Amount, b: Amount) {
return a.currency === b.currency && a.counterparty === b.counterparty;
return a.currency === b.currency && a.counterparty === b.counterparty
}
function directionFilter(direction: string, order: OrderbookItem) {
return order.specification.direction === direction;
return order.specification.direction === direction
}
function flipOrder(order: OrderbookItem) {
const specification = order.specification;
const specification = order.specification
const flippedSpecification = {
quantity: specification.totalPrice,
totalPrice: specification.quantity,
direction: specification.direction === 'buy' ? 'sell' : 'buy'
};
const newSpecification = _.merge({}, specification, flippedSpecification);
return _.merge({}, order, {specification: newSpecification});
}
const newSpecification = _.merge({}, specification, flippedSpecification)
return _.merge({}, order, {specification: newSpecification})
}
function alignOrder(base: Amount, order: OrderbookItem) {
const quantity = order.specification.quantity;
return isSameIssue(quantity, base) ? order : flipOrder(order);
const quantity = order.specification.quantity
return isSameIssue(quantity, base) ? order : flipOrder(order)
}
function formatBidsAndAsks(orderbook: Orderbook, offers) {
@@ -84,24 +84,24 @@ function formatBidsAndAsks(orderbook: Orderbook, offers) {
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
// for both bids and asks, lowest quality is closest to mid-market
// we sort the orders so that earlier orders are closer to mid-market
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder);
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base));
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'));
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'));
return {bids, asks};
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder)
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base))
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'))
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'))
return {bids, asks}
}
function getOrderbook(address: string, orderbook: Orderbook,
options: OrdersOptions = {}
): Promise<GetOrderbook> {
validate.getOrderbook({address, orderbook, options});
validate.getOrderbook({address, orderbook, options})
const getter = _.partial(getBookOffers, this.connection, address,
options.ledgerVersion, options.limit);
const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
options.ledgerVersion, options.limit)
const getOffers = _.partial(getter, orderbook.base, orderbook.counter)
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base)
return Promise.all([getOffers(), getReverseOffers()]).then(data =>
formatBidsAndAsks(orderbook, _.flatten(data)));
formatBidsAndAsks(orderbook, _.flatten(data)))
}
module.exports = getOrderbook;
module.exports = getOrderbook

View File

@@ -1,11 +1,11 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
const parseAccountOrder = require('./parse/account-order');
import type {Connection} from '../common/connection.js';
import type {OrdersOptions, Order} from './types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
const parseAccountOrder = require('./parse/account-order')
import type {Connection} from '../common/connection.js'
import type {OrdersOptions, Order} from './types.js'
type GetOrders = Array<Order>
@@ -22,20 +22,20 @@ function requestAccountOffers(connection: Connection, address: string,
return {
marker: data.marker,
results: data.offers.map(_.partial(parseAccountOrder, address))
};
});
}
})
}
function getOrders(address: string, options: OrdersOptions = {}
): Promise<GetOrders> {
validate.getOrders({address, options});
validate.getOrders({address, options})
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const getter = _.partial(requestAccountOffers, this.connection, address,
_options.ledgerVersion);
_options.ledgerVersion)
return utils.getRecursive(getter, _options.limit).then(orders =>
_.sortBy(orders, (order) => order.properties.sequence));
});
_.sortBy(orders, order => order.properties.sequence))
})
}
module.exports = getOrders;
module.exports = getOrders

View File

@@ -1,24 +1,24 @@
/* @flow */
'use strict'; // eslint-disable-line strict
const utils = require('./utils');
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
const BigNumber = require('bignumber.js');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const flags = require('./flags').orderFlags
const parseAmount = require('./amount')
const BigNumber = require('bignumber.js')
// TODO: remove this function once rippled provides quality directly
function computeQuality(takerGets, takerPays) {
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value);
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString();
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value)
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString()
}
// rippled 'account_offers' returns a different format for orders than 'tx'
// the flags are also different
function parseAccountOrder(address: string, order: Object): Object {
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.taker_gets);
const takerPaysAmount = parseAmount(order.taker_pays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(order.taker_gets)
const takerPaysAmount = parseAmount(order.taker_pays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
@@ -29,19 +29,19 @@ function parseAccountOrder(address: string, order: Object): Object {
passive: ((order.flags & flags.Passive) !== 0) || undefined,
// rippled currently does not provide "expiration" in account_offers
expirationTime: utils.parseTimestamp(order.expiration)
});
})
const makerExchangeRate = order.quality ?
utils.adjustQualityForXRP(order.quality.toString(),
takerGetsAmount.currency, takerPaysAmount.currency) :
computeQuality(takerGetsAmount, takerPaysAmount);
computeQuality(takerGetsAmount, takerPaysAmount)
const properties = {
maker: address,
sequence: order.seq,
makerExchangeRate: makerExchangeRate
};
}
return {specification, properties};
return {specification, properties}
}
module.exports = parseAccountOrder;
module.exports = parseAccountOrder

View File

@@ -1,6 +1,6 @@
/* @flow */
'use strict';
const utils = require('./utils');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
type Trustline = {
account: string, limit: number, currency: string, quality_in: ?number,
@@ -29,18 +29,18 @@ function parseAccountTrustline(trustline: Trustline): AccountTrustline {
ripplingDisabled: trustline.no_ripple || undefined,
frozen: trustline.freeze || undefined,
authorized: trustline.authorized || undefined
});
})
// rippled doesn't provide the counterparty's qualities
const counterparty = utils.removeUndefined({
limit: trustline.limit_peer,
ripplingDisabled: trustline.no_ripple_peer || undefined,
frozen: trustline.freeze_peer || undefined,
authorized: trustline.peer_authorized || undefined
});
})
const state = {
balance: trustline.balance
};
return {specification, counterparty, state};
}
return {specification, counterparty, state}
}
module.exports = parseAccountTrustline;
module.exports = parseAccountTrustline

View File

@@ -1,9 +1,9 @@
'use strict'; // eslint-disable-line strict
'use strict' // eslint-disable-line strict
function parseAmendment(tx: Object) {
return {
amendment: tx.Amendment
};
}
}
module.exports = parseAmendment;
module.exports = parseAmendment

View File

@@ -1,7 +1,7 @@
/* @flow */
'use strict';
const utils = require('../utils');
import type {Amount, RippledAmount} from '../../common/types.js';
'use strict' // eslint-disable-line strict
const utils = require('../utils')
import type {Amount, RippledAmount} from '../../common/types.js'
function parseAmount(amount: RippledAmount): Amount {
@@ -9,13 +9,13 @@ function parseAmount(amount: RippledAmount): Amount {
return {
currency: 'XRP',
value: utils.common.dropsToXrp(amount)
};
}
}
return {
currency: amount.currency,
value: amount.value,
counterparty: amount.issuer
};
}
}
module.exports = parseAmount;
module.exports = parseAmount

View File

@@ -1,12 +1,12 @@
/* @flow */
'use strict';
const assert = require('assert');
'use strict' // eslint-disable-line strict
const assert = require('assert')
function parseOrderCancellation(tx: Object): Object {
assert(tx.TransactionType === 'OfferCancel');
assert(tx.TransactionType === 'OfferCancel')
return {
orderSequence: tx.OfferSequence
};
}
}
module.exports = parseOrderCancellation;
module.exports = parseOrderCancellation

View File

@@ -0,0 +1,16 @@
/* @flow */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
function parseEscrowCancellation(tx: Object): Object {
assert(tx.TransactionType === 'EscrowCancel')
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
escrowSequence: tx.OfferSequence
})
}
module.exports = parseEscrowCancellation

View File

@@ -0,0 +1,23 @@
/* @flow */
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
function parseEscrowCreation(tx: Object): Object {
assert(tx.TransactionType === 'EscrowCreate')
return utils.removeUndefined({
amount: parseAmount(tx.Amount).value,
destination: tx.Destination,
memos: utils.parseMemos(tx),
condition: tx.Condition,
allowCancelAfter: utils.parseTimestamp(tx.CancelAfter),
allowExecuteAfter: utils.parseTimestamp(tx.FinishAfter),
sourceTag: tx.SourceTag,
destinationTag: tx.DestinationTag
})
}
module.exports = parseEscrowCreation

View File

@@ -0,0 +1,18 @@
/* @flow */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
function parseEscrowExecution(tx: Object): Object {
assert(tx.TransactionType === 'EscrowFinish')
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
escrowSequence: tx.OfferSequence,
condition: tx.Condition,
fulfillment: tx.Fulfillment
})
}
module.exports = parseEscrowExecution

View File

@@ -1,15 +1,15 @@
'use strict'; // eslint-disable-line strict
const BigNumber = require('bignumber.js');
const {dropsToXrp} = require('./utils');
'use strict' // eslint-disable-line strict
const BigNumber = require('bignumber.js')
const {dropsToXrp} = require('./utils')
function parseFeeUpdate(tx: Object) {
const baseFeeDrops = (new BigNumber(tx.BaseFee, 16)).toString();
const baseFeeDrops = (new BigNumber(tx.BaseFee, 16)).toString()
return {
baseFeeXRP: dropsToXrp(baseFeeDrops),
referenceFeeUnits: tx.ReferenceFeeUnits,
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement)
};
}
}
module.exports = parseFeeUpdate;
module.exports = parseFeeUpdate

View File

@@ -1,49 +1,49 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const AccountFields = require('./utils').constants.AccountFields;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const AccountFields = require('./utils').constants.AccountFields
function parseField(info, value) {
if (info.encoding === 'hex' && !info.length) { // e.g. "domain"
return new Buffer(value, 'hex').toString('ascii');
return new Buffer(value, 'hex').toString('ascii')
}
if (info.shift) {
return (new BigNumber(value)).shift(-info.shift).toNumber();
return (new BigNumber(value)).shift(-info.shift).toNumber()
}
return value;
return value
}
function parseFields(data: Object): Object {
const settings = {};
const settings = {}
for (const fieldName in AccountFields) {
const fieldValue = data[fieldName];
const fieldValue = data[fieldName]
if (fieldValue !== undefined) {
const info = AccountFields[fieldName];
settings[info.name] = parseField(info, fieldValue);
const info = AccountFields[fieldName]
settings[info.name] = parseField(info, fieldValue)
}
}
if (data.RegularKey) {
settings.regularKey = data.RegularKey;
settings.regularKey = data.RegularKey
}
// TODO: this isn't implemented in rippled yet, may have to change this later
if (data.SignerQuorum || data.SignerEntries) {
settings.signers = {};
settings.signers = {}
if (data.SignerQuorum) {
settings.signers.threshold = data.SignerQuorum;
settings.signers.threshold = data.SignerQuorum
}
if (data.SignerEntries) {
settings.signers.weights = _.map(data.SignerEntries, entry => {
return {
address: entry.SignerEntry.Account,
weight: entry.SignerEntry.SignerWeight
};
});
}
})
}
}
return settings;
return settings
}
module.exports = parseFields;
module.exports = parseFields

View File

@@ -1,9 +1,9 @@
'use strict';
'use strict' // eslint-disable-line strict
const orderFlags = {
Passive: 0x00010000,
Sell: 0x00020000 // offer was placed as a sell
};
}
const trustlineFlags = {
LowReserve: 0x00010000, // entry counts toward reserve
@@ -14,9 +14,9 @@ const trustlineFlags = {
HighNoRipple: 0x00200000,
LowFreeze: 0x00400000,
HighFreeze: 0x00800000
};
}
module.exports = {
orderFlags,
trustlineFlags
};
}

View File

@@ -1,48 +1,48 @@
/* @flow */
'use strict'; // eslint-disable-line
const _ = require('lodash');
const {removeUndefined, rippleTimeToISO8601} = require('./utils');
const parseTransaction = require('./transaction');
import type {GetLedger} from '../types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const {removeUndefined, rippleTimeToISO8601} = require('./utils')
const parseTransaction = require('./transaction')
import type {GetLedger} from '../types.js'
function parseTransactionWrapper(ledgerVersion, tx) {
const transaction = _.assign({}, _.omit(tx, 'metaData'), {
meta: tx.metaData,
ledger_index: ledgerVersion
});
const result = parseTransaction(transaction);
})
const result = parseTransaction(transaction)
if (!result.outcome.ledgerVersion) {
result.outcome.ledgerVersion = ledgerVersion;
result.outcome.ledgerVersion = ledgerVersion
}
return result;
return result
}
function parseTransactions(transactions, ledgerVersion) {
if (_.isEmpty(transactions)) {
return {};
return {}
}
if (_.isString(transactions[0])) {
return {transactionHashes: transactions};
return {transactionHashes: transactions}
}
return {
transactions: _.map(transactions,
_.partial(parseTransactionWrapper, ledgerVersion)),
rawTransactions: JSON.stringify(transactions)
};
}
}
function parseState(state) {
if (_.isEmpty(state)) {
return {};
return {}
}
if (_.isString(state[0])) {
return {stateHashes: state};
return {stateHashes: state}
}
return {rawState: JSON.stringify(state)};
return {rawState: JSON.stringify(state)}
}
function parseLedger(ledger: Object): GetLedger {
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10);
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10)
return removeUndefined(_.assign({
stateHash: ledger.account_hash,
closeTime: rippleTimeToISO8601(ledger.close_time),
@@ -57,7 +57,7 @@ function parseLedger(ledger: Object): GetLedger {
},
parseTransactions(ledger.transactions, ledgerVersion),
parseState(ledger.accountState)
));
))
}
module.exports = parseLedger;
module.exports = parseLedger

View File

@@ -1,18 +1,18 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const flags = utils.txFlags.OfferCreate;
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
const flags = utils.txFlags.OfferCreate
function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate');
assert(tx.TransactionType === 'OfferCreate')
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(tx.TakerGets);
const takerPaysAmount = parseAmount(tx.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(tx.TakerGets)
const takerPaysAmount = parseAmount(tx.TakerPays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
return utils.removeUndefined({
direction: direction,
@@ -23,7 +23,7 @@ function parseOrder(tx: Object): Object {
|| undefined,
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined,
expirationTime: utils.parseTimestamp(tx.Expiration)
});
})
}
module.exports = parseOrder;
module.exports = parseOrder

View File

@@ -1,16 +1,16 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const flags = require('./flags').orderFlags
const parseAmount = require('./amount')
function parseOrderbookOrder(order: Object): Object {
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.TakerGets);
const takerPaysAmount = parseAmount(order.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(order.TakerGets)
const takerPaysAmount = parseAmount(order.TakerPays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
@@ -20,25 +20,25 @@ function parseOrderbookOrder(order: Object): Object {
totalPrice: totalPrice,
passive: ((order.Flags & flags.Passive) !== 0) || undefined,
expirationTime: utils.parseTimestamp(order.Expiration)
});
})
const properties = {
maker: order.Account,
sequence: order.Sequence,
makerExchangeRate: utils.adjustQualityForXRP(order.quality,
takerGetsAmount.currency, takerPaysAmount.currency)
};
}
const takerGetsFunded = order.taker_gets_funded ?
parseAmount(order.taker_gets_funded) : undefined;
parseAmount(order.taker_gets_funded) : undefined
const takerPaysFunded = order.taker_pays_funded ?
parseAmount(order.taker_pays_funded) : undefined;
parseAmount(order.taker_pays_funded) : undefined
const available = utils.removeUndefined({
fundedAmount: takerGetsFunded,
priceOfFundedAmount: takerPaysFunded
});
const state = _.isEmpty(available) ? undefined : available;
return utils.removeUndefined({specification, properties, state});
})
const state = _.isEmpty(available) ? undefined : available
return utils.removeUndefined({specification, properties, state})
}
module.exports = parseOrderbookOrder;
module.exports = parseOrderbookOrder

View File

@@ -1,25 +1,25 @@
/* @flow */
'use strict';
const _ = require('lodash');
const parseAmount = require('./amount');
import type {Amount, RippledAmount} from '../../common/types.js';
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const parseAmount = require('./amount')
import type {Amount, RippledAmount} from '../../common/types.js'
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js'
function parsePaths(paths) {
return paths.map(steps => steps.map(step =>
_.omit(step, ['type', 'type_hex'])));
_.omit(step, ['type', 'type_hex'])))
}
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
_.omit(amount, 'counterparty') : amount
}
function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
const amountKey = _.keys(adjustmentWithoutAddress)[0];
const amount = adjustmentWithoutAddress[amountKey];
const amountKey = _.keys(adjustmentWithoutAddress)[0]
const amount = adjustmentWithoutAddress[amountKey]
return _.set({address: address}, amountKey,
removeAnyCounterpartyEncoding(address, amount));
removeAnyCounterpartyEncoding(address, amount))
}
function parseAlternative(sourceAddress: string, destinationAddress: string,
@@ -31,21 +31,21 @@ function parseAlternative(sourceAddress: string, destinationAddress: string,
{source: {amount: parseAmount(alternative.source_amount)},
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
{source: {maxAmount: parseAmount(alternative.source_amount)},
destination: {amount: parseAmount(destinationAmount)}};
destination: {amount: parseAmount(destinationAmount)}}
return {
source: createAdjustment(sourceAddress, amounts.source),
destination: createAdjustment(destinationAddress, amounts.destination),
paths: JSON.stringify(parsePaths(alternative.paths_computed))
};
}
}
function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
const sourceAddress = pathfindResult.source_account;
const destinationAddress = pathfindResult.destination_account;
const destinationAmount = pathfindResult.destination_amount;
const sourceAddress = pathfindResult.source_account
const destinationAddress = pathfindResult.destination_account
const destinationAmount = pathfindResult.destination_amount
return pathfindResult.alternatives.map(_.partial(parseAlternative,
sourceAddress, destinationAddress, destinationAmount));
sourceAddress, destinationAddress, destinationAmount))
}
module.exports = parsePathfind;
module.exports = parsePathfind

View File

@@ -0,0 +1,22 @@
/* @flow */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
const claimFlags = utils.txFlags.PaymentChannelClaim
function parsePaymentChannelClaim(tx: Object): Object {
assert(tx.TransactionType === 'PaymentChannelClaim')
return utils.removeUndefined({
channel: tx.Channel,
balance: tx.Balance && parseAmount(tx.Balance).value,
amount: tx.Amount && parseAmount(tx.Amount).value,
signature: tx.Signature,
publicKey: tx.PublicKey,
renew: Boolean(tx.Flags & claimFlags.Renew) || undefined,
close: Boolean(tx.Flags & claimFlags.Close) || undefined
})
}
module.exports = parsePaymentChannelClaim

View File

@@ -0,0 +1,21 @@
/* @flow */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
function parsePaymentChannelCreate(tx: Object): Object {
assert(tx.TransactionType === 'PaymentChannelCreate')
return utils.removeUndefined({
amount: parseAmount(tx.Amount).value,
destination: tx.Destination,
settleDelay: tx.SettleDelay,
publicKey: tx.PublicKey,
cancelAfter: tx.CancelAfter && utils.parseTimestamp(tx.CancelAfter),
sourceTag: tx.SourceTag,
destinationTag: tx.DestinationTag
})
}
module.exports = parsePaymentChannelCreate

View File

@@ -0,0 +1,17 @@
/* @flow */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
function parsePaymentChannelFund(tx: Object): Object {
assert(tx.TransactionType === 'PaymentChannelFund')
return utils.removeUndefined({
channel: tx.Channel,
amount: parseAmount(tx.Amount).value,
expiration: tx.Expiration && utils.parseTimestamp(tx.Expiration)
})
}
module.exports = parsePaymentChannelFund

View File

@@ -1,39 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const txFlags = utils.txFlags;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
const txFlags = utils.txFlags
function isNoDirectRipple(tx) {
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0
}
function isQualityLimited(tx) {
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0;
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
_.omit(amount, 'counterparty') : amount
}
function parsePayment(tx: Object): Object {
assert(tx.TransactionType === 'Payment');
assert(tx.TransactionType === 'Payment')
const source = {
address: tx.Account,
maxAmount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
tag: tx.SourceTag
};
}
const destination = {
address: tx.Destination,
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
tag: tx.DestinationTag
};
}
return utils.removeUndefined({
source: utils.removeUndefined(source),
@@ -44,7 +44,7 @@ function parsePayment(tx: Object): Object {
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
noDirectRipple: isNoDirectRipple(tx) || undefined,
limitQuality: isQualityLimited(tx) || undefined
});
})
}
module.exports = parsePayment;
module.exports = parsePayment

View File

@@ -1,61 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const AccountFlags = require('./utils').constants.AccountFlags;
const parseFields = require('./fields');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const AccountFlags = require('./utils').constants.AccountFlags
const parseFields = require('./fields')
function getAccountRootModifiedNode(tx: Object) {
const modifiedNodes = tx.meta.AffectedNodes.filter(node =>
node.ModifiedNode.LedgerEntryType === 'AccountRoot');
assert(modifiedNodes.length === 1);
return modifiedNodes[0].ModifiedNode;
node.ModifiedNode.LedgerEntryType === 'AccountRoot')
assert(modifiedNodes.length === 1)
return modifiedNodes[0].ModifiedNode
}
function parseFlags(tx: Object) {
const settings = {};
const settings = {}
if (tx.TransactionType !== 'AccountSet') {
return settings;
return settings
}
const node = getAccountRootModifiedNode(tx);
const oldFlags = _.get(node.PreviousFields, 'Flags');
const newFlags = _.get(node.FinalFields, 'Flags');
const node = getAccountRootModifiedNode(tx)
const oldFlags = _.get(node.PreviousFields, 'Flags')
const newFlags = _.get(node.FinalFields, 'Flags')
if (oldFlags !== undefined && newFlags !== undefined) {
const changedFlags = oldFlags ^ newFlags;
const setFlags = newFlags & changedFlags;
const clearedFlags = oldFlags & changedFlags;
const changedFlags = oldFlags ^ newFlags
const setFlags = newFlags & changedFlags
const clearedFlags = oldFlags & changedFlags
_.forEach(AccountFlags, (flagValue, flagName) => {
if (setFlags & flagValue) {
settings[flagName] = true;
settings[flagName] = true
} else if (clearedFlags & flagValue) {
settings[flagName] = false;
settings[flagName] = false
}
});
})
}
// enableTransactionIDTracking requires a special case because it
// does not affect the Flags field; instead it adds/removes a field called
// "AccountTxnID" to/from the account root.
const oldField = _.get(node.PreviousFields, 'AccountTxnID');
const newField = _.get(node.FinalFields, 'AccountTxnID');
const oldField = _.get(node.PreviousFields, 'AccountTxnID')
const newField = _.get(node.FinalFields, 'AccountTxnID')
if (newField && !oldField) {
settings.enableTransactionIDTracking = true;
settings.enableTransactionIDTracking = true
} else if (oldField && !newField) {
settings.enableTransactionIDTracking = false;
settings.enableTransactionIDTracking = false
}
return settings;
return settings
}
function parseSettings(tx: Object) {
const txType = tx.TransactionType;
const txType = tx.TransactionType
assert(txType === 'AccountSet' || txType === 'SetRegularKey' ||
txType === 'SignerListSet');
txType === 'SignerListSet')
return _.assign({}, parseFlags(tx), parseFields(tx));
return _.assign({}, parseFlags(tx), parseFields(tx))
}
module.exports = parseSettings;
module.exports = parseSettings

View File

@@ -1,16 +0,0 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
function parseSuspendedPaymentCancellation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCancel');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
suspensionSequence: tx.OfferSequence
});
}
module.exports = parseSuspendedPaymentCancellation;

View File

@@ -1,39 +0,0 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
}
function parseSuspendedPaymentCreation(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentCreate');
const source = {
address: tx.Account,
maxAmount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
tag: tx.SourceTag
};
const destination = {
address: tx.Destination,
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
tag: tx.DestinationTag
};
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: utils.parseMemos(tx),
digest: tx.Digest,
allowCancelAfter: utils.parseTimestamp(tx.CancelAfter),
allowExecuteAfter: utils.parseTimestamp(tx.FinishAfter)
});
}
module.exports = parseSuspendedPaymentCreation;

View File

@@ -1,19 +0,0 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
function parseSuspendedPaymentExecution(tx: Object): Object {
assert(tx.TransactionType === 'SuspendedPaymentFinish');
return utils.removeUndefined({
memos: utils.parseMemos(tx),
owner: tx.Owner,
suspensionSequence: tx.OfferSequence,
method: tx.Method,
digest: tx.Digest,
proof: tx.Proof ? utils.hexToString(tx.Proof) : undefined
});
}
module.exports = parseSuspendedPaymentExecution;

View File

@@ -1,18 +1,20 @@
/* @flow */
'use strict'; // eslint-disable-line strict
const assert = require('assert');
const utils = require('./utils');
const parsePayment = require('./payment');
const parseTrustline = require('./trustline');
const parseOrder = require('./order');
const parseOrderCancellation = require('./cancellation');
const parseSettings = require('./settings');
const parseSuspendedPaymentCreation = require('./suspended-payment-creation');
const parseSuspendedPaymentExecution = require('./suspended-payment-execution');
const parseSuspendedPaymentCancellation =
require('./suspended-payment-cancellation');
const parseFeeUpdate = require('./fee-update');
const parseAmendment = require('./amendment');
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parsePayment = require('./payment')
const parseTrustline = require('./trustline')
const parseOrder = require('./order')
const parseOrderCancellation = require('./cancellation')
const parseSettings = require('./settings')
const parseEscrowCreation = require('./escrow-creation')
const parseEscrowExecution = require('./escrow-execution')
const parseEscrowCancellation = require('./escrow-cancellation')
const parsePaymentChannelCreate = require('./payment-channel-create')
const parsePaymentChannelFund = require('./payment-channel-fund')
const parsePaymentChannelClaim = require('./payment-channel-claim')
const parseFeeUpdate = require('./fee-update')
const parseAmendment = require('./amendment')
function parseTransactionType(type) {
const mapping = {
@@ -22,34 +24,40 @@ function parseTransactionType(type) {
OfferCancel: 'orderCancellation',
AccountSet: 'settings',
SetRegularKey: 'settings',
SuspendedPaymentCreate: 'suspendedPaymentCreation',
SuspendedPaymentFinish: 'suspendedPaymentExecution',
SuspendedPaymentCancel: 'suspendedPaymentCancellation',
EscrowCreate: 'escrowCreation',
EscrowFinish: 'escrowExecution',
EscrowCancel: 'escrowCancellation',
PaymentChannelCreate: 'paymentChannelCreate',
PaymentChannelFund: 'paymentChannelFund',
PaymentChannelClaim: 'paymentChannelClaim',
SignerListSet: 'settings',
SetFee: 'feeUpdate', // pseudo-transaction
EnableAmendment: 'amendment' // pseudo-transaction
};
return mapping[type] || null;
}
return mapping[type] || null
}
function parseTransaction(tx: Object): Object {
const type = parseTransactionType(tx.TransactionType);
const type = parseTransactionType(tx.TransactionType)
const mapping = {
'payment': parsePayment,
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings,
'suspendedPaymentCreation': parseSuspendedPaymentCreation,
'suspendedPaymentExecution': parseSuspendedPaymentExecution,
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation,
'escrowCreation': parseEscrowCreation,
'escrowExecution': parseEscrowExecution,
'escrowCancellation': parseEscrowCancellation,
'paymentChannelCreate': parsePaymentChannelCreate,
'paymentChannelFund': parsePaymentChannelFund,
'paymentChannelClaim': parsePaymentChannelClaim,
'feeUpdate': parseFeeUpdate,
'amendment': parseAmendment
};
const parser = mapping[type];
assert(parser !== undefined, 'Unrecognized transaction type');
const specification = parser(tx);
const outcome = utils.parseOutcome(tx);
}
const parser = mapping[type]
assert(parser !== undefined, 'Unrecognized transaction type')
const specification = parser(tx)
const outcome = utils.parseOutcome(tx)
return utils.removeUndefined({
type: type,
address: tx.Account,
@@ -57,7 +65,7 @@ function parseTransaction(tx: Object): Object {
id: tx.hash,
specification: utils.removeUndefined(specification),
outcome: outcome ? utils.removeUndefined(outcome) : undefined
});
})
}
module.exports = parseTransaction;
module.exports = parseTransaction

View File

@@ -1,21 +1,21 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const flags = utils.txFlags.TrustSet;
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const flags = utils.txFlags.TrustSet
function parseFlag(flagsValue, trueValue, falseValue) {
if (flagsValue & trueValue) {
return true;
return true
}
if (flagsValue & falseValue) {
return false;
return false
}
return undefined;
return undefined
}
function parseTrustline(tx: Object): Object {
assert(tx.TransactionType === 'TrustSet');
assert(tx.TransactionType === 'TrustSet')
return utils.removeUndefined({
limit: tx.LimitAmount.value,
@@ -27,7 +27,7 @@ function parseTrustline(tx: Object): Object {
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
});
})
}
module.exports = parseTrustline;
module.exports = parseTrustline

View File

@@ -1,85 +1,85 @@
/* @flow */
'use strict'; // eslint-disable-line
const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const utils = require('../utils');
const BigNumber = require('bignumber.js');
const parseAmount = require('./amount');
'use strict' // eslint-disable-line strict
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';
import type {Amount} from '../common/types.js'
function adjustQualityForXRP(
quality: string, takerGetsCurrency: string, takerPaysCurrency: string
) {
// quality = takerPays.value/takerGets.value
// using drops (1e-6 XRP) for XRP values
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0);
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0);
const shift = numeratorShift - denominatorShift;
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0)
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0)
const shift = numeratorShift - denominatorShift
return shift === 0 ? quality :
(new BigNumber(quality)).shift(shift).toString();
(new BigNumber(quality)).shift(shift).toString()
}
function parseQuality(quality: ?number) {
if (typeof quality === 'number') {
return (new BigNumber(quality)).shift(-9).toNumber();
return (new BigNumber(quality)).shift(-9).toNumber()
}
return undefined;
return undefined
}
function parseTimestamp(rippleTime: number): string | void {
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined;
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined
}
function removeEmptyCounterparty(amount) {
if (amount.counterparty === '') {
delete amount.counterparty;
delete amount.counterparty
}
}
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
_.forEach(balanceChanges, changes => {
_.forEach(changes, removeEmptyCounterparty);
});
_.forEach(changes, removeEmptyCounterparty)
})
}
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
_.forEach(orderbookChanges, changes => {
_.forEach(changes, change => {
_.forEach(change, removeEmptyCounterparty);
});
});
_.forEach(change, removeEmptyCounterparty)
})
})
}
function isPartialPayment(tx) {
return (tx.Flags & utils.common.txFlags.Payment.PartialPayment) !== 0;
return (tx.Flags & utils.common.txFlags.Payment.PartialPayment) !== 0
}
function parseDeliveredAmount(tx: Object): Amount | void {
if (tx.TransactionType !== 'Payment' ||
tx.meta.TransactionResult !== 'tesSUCCESS') {
return undefined;
return undefined
}
if (tx.meta.delivered_amount &&
tx.meta.delivered_amount === 'unavailable') {
return undefined;
return undefined
}
// parsable delivered_amount
if (tx.meta.delivered_amount) {
return parseAmount(tx.meta.delivered_amount);
return parseAmount(tx.meta.delivered_amount)
}
// DeliveredAmount only present on partial payments
if (tx.meta.DeliveredAmount) {
return parseAmount(tx.meta.DeliveredAmount);
return parseAmount(tx.meta.DeliveredAmount)
}
// no partial payment flag, use tx.Amount
if (tx.Amount && !isPartialPayment(tx)) {
return parseAmount(tx.Amount);
return parseAmount(tx.Amount)
}
// DeliveredAmount field was introduced at
@@ -89,21 +89,21 @@ function parseDeliveredAmount(tx: Object): Amount | void {
// transferred with a partial payment before
// that date must be derived from metadata.
if (tx.Amount && tx.ledger_index > 4594094) {
return parseAmount(tx.Amount);
return parseAmount(tx.Amount)
}
return undefined;
return undefined
}
function parseOutcome(tx: Object): ?Object {
const metadata = tx.meta || tx.metaData;
const metadata = tx.meta || tx.metaData
if (!metadata) {
return undefined;
return undefined
}
const balanceChanges = transactionParser.parseBalanceChanges(metadata);
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata);
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
const balanceChanges = transactionParser.parseBalanceChanges(metadata)
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata)
removeEmptyCounterpartyInBalanceChanges(balanceChanges)
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges)
return utils.common.removeUndefined({
result: tx.meta.TransactionResult,
@@ -114,24 +114,24 @@ function parseOutcome(tx: Object): ?Object {
ledgerVersion: tx.ledger_index,
indexInLedger: tx.meta.TransactionIndex,
deliveredAmount: parseDeliveredAmount(tx)
});
})
}
function hexToString(hex: string): ?string {
return hex ? new Buffer(hex, 'hex').toString('utf-8') : undefined;
return hex ? new Buffer(hex, 'hex').toString('utf-8') : undefined
}
function parseMemos(tx: Object): ?Array<Object> {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
return undefined
}
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),
data: m.Memo.parsed_memo_data || hexToString(m.Memo.MemoData)
});
});
})
})
}
module.exports = {
@@ -147,4 +147,4 @@ module.exports = {
txFlags: utils.common.txFlags,
removeUndefined: utils.common.removeUndefined,
rippleTimeToISO8601: utils.common.rippleTimeToISO8601
};
}

View File

@@ -1,8 +1,8 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
import type {Amount, LaxLaxAmount, RippledAmount, Adjustment, MaxAdjustment,
MinAdjustment} from '../common/types.js';
MinAdjustment} from '../common/types.js'
type Path = {

View File

@@ -1,33 +1,33 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const utils = require('./utils');
const parsePathfind = require('./parse/pathfind');
const {validate, toRippledAmount} = utils.common;
const NotFoundError = utils.common.errors.NotFoundError;
const ValidationError = utils.common.errors.ValidationError;
import type {Connection} from '../common/connection';
import type {RippledAmount} from '../common/types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const utils = require('./utils')
const parsePathfind = require('./parse/pathfind')
const {validate, toRippledAmount} = utils.common
const NotFoundError = utils.common.errors.NotFoundError
const ValidationError = utils.common.errors.ValidationError
import type {Connection} from '../common/connection'
import type {RippledAmount} from '../common/types.js'
import type {GetPaths, PathFind, RippledPathsResponse, PathFindRequest}
from './pathfind-types.js';
from './pathfind-types.js'
function addParams(request: PathFindRequest, result: RippledPathsResponse) {
return _.defaults(_.assign({}, result, {
source_account: request.source_account,
source_currencies: request.source_currencies
}), {destination_amount: request.destination_amount});
}), {destination_amount: request.destination_amount})
}
function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount)
const request: PathFindRequest = {
command: 'ripple_path_find',
source_account: pathfind.source.address,
destination_account: pathfind.destination.address,
destination_amount: toRippledAmount(destinationAmount)
};
}
if (typeof request.destination_amount === 'object'
&& !request.destination_amount.issuer) {
// Convert blank issuer to sender's address
@@ -35,43 +35,43 @@ function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
// https://ripple.com/build/transactions/
// #special-issuer-values-for-sendmax-and-amount
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
request.destination_amount.issuer = request.destination_account;
request.destination_amount.issuer = request.destination_account
}
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
request.source_currencies = pathfind.source.currencies.map(amount =>
_.omit(toRippledAmount(amount), 'value'));
_.omit(toRippledAmount(amount), 'value'))
}
if (pathfind.source.amount) {
if (pathfind.destination.amount.value !== undefined) {
throw new ValidationError('Cannot specify both source.amount'
+ ' and destination.amount.value in getPaths');
+ ' and destination.amount.value in getPaths')
}
request.send_max = toRippledAmount(pathfind.source.amount);
request.send_max = toRippledAmount(pathfind.source.amount)
if (request.send_max.currency && !request.send_max.issuer) {
request.send_max.issuer = pathfind.source.address;
request.send_max.issuer = pathfind.source.address
}
}
return connection.request(request).then(paths => addParams(request, paths));
return connection.request(request).then(paths => addParams(request, paths))
}
function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string
): RippledPathsResponse {
// Add XRP "path" only if the source acct has enough XRP to make the payment
const destinationAmount = paths.destination_amount;
const destinationAmount = paths.destination_amount
if ((new BigNumber(xrpBalance)).greaterThanOrEqualTo(destinationAmount)) {
paths.alternatives.unshift({
paths_computed: [],
source_amount: paths.destination_amount
});
})
}
return paths;
return paths
}
function isRippledIOUAmount(amount: RippledAmount) {
// rippled XRP amounts are specified as decimal strings
return (typeof amount === 'object') &&
amount.currency && (amount.currency !== 'XRP');
amount.currency && (amount.currency !== 'XRP')
}
function conditionallyAddDirectXRPPath(connection: Connection, address: string,
@@ -79,10 +79,10 @@ function conditionallyAddDirectXRPPath(connection: Connection, address: string,
): Promise {
if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) {
return Promise.resolve(paths);
return Promise.resolve(paths)
}
return utils.getXRPBalance(connection, address, undefined).then(
xrpBalance => addDirectXrpPath(paths, xrpBalance));
xrpBalance => addDirectXrpPath(paths, xrpBalance))
}
function filterSourceFundsLowPaths(pathfind: PathFind,
@@ -93,15 +93,15 @@ function filterSourceFundsLowPaths(pathfind: PathFind,
paths.alternatives = _.filter(paths.alternatives, alt => {
return alt.source_amount &&
pathfind.source.amount &&
new BigNumber(alt.source_amount.value).eq(pathfind.source.amount.value);
});
new BigNumber(alt.source_amount.value).eq(pathfind.source.amount.value)
})
}
return paths;
return paths
}
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
if (paths.alternatives && paths.alternatives.length > 0) {
return parsePathfind(paths);
return parsePathfind(paths)
}
if (paths.destination_currencies !== undefined &&
!_.includes(paths.destination_currencies,
@@ -109,30 +109,30 @@ function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
throw new NotFoundError('No paths found. ' +
'The destination_account does not accept ' +
pathfind.destination.amount.currency + ', they only accept: ' +
paths.destination_currencies.join(', '));
paths.destination_currencies.join(', '))
} else if (paths.source_currencies && paths.source_currencies.length > 0) {
throw new NotFoundError('No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' +
' there may be insufficient liquidity in the network to execute' +
' this payment right now');
' this payment right now')
} else {
throw new NotFoundError('No paths found.' +
' Please ensure that the source_account has sufficient funds to' +
' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now');
' in the network to execute this payment right now')
}
}
function getPaths(pathfind: PathFind): Promise<GetPaths> {
validate.getPaths({pathfind});
validate.getPaths({pathfind})
const address = pathfind.source.address;
const address = pathfind.source.address
return requestPathFind(this.connection, pathfind).then(paths =>
conditionallyAddDirectXRPPath(this.connection, address, paths)
)
.then(paths => filterSourceFundsLowPaths(pathfind, paths))
.then(paths => formatResponse(pathfind, paths));
.then(paths => formatResponse(pathfind, paths))
}
module.exports = getPaths;
module.exports = getPaths

View File

@@ -1,10 +1,10 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const parseFields = require('./parse/fields');
const {validate} = utils.common;
const AccountFlags = utils.common.constants.AccountFlags;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const parseFields = require('./parse/fields')
const {validate} = utils.common
const AccountFlags = utils.common.constants.AccountFlags
type SettingsOptions = {
ledgerVersion?: number
@@ -29,33 +29,33 @@ type GetSettings = {
function parseFlags(value) {
const settings = {};
const settings = {}
for (const flagName in AccountFlags) {
if (value & AccountFlags[flagName]) {
settings[flagName] = true;
settings[flagName] = true
}
}
return settings;
return settings
}
function formatSettings(response) {
const data = response.account_data;
const parsedFlags = parseFlags(data.Flags);
const parsedFields = parseFields(data);
return _.assign({}, parsedFlags, parsedFields);
const data = response.account_data
const parsedFlags = parseFlags(data.Flags)
const parsedFields = parseFields(data)
return _.assign({}, parsedFlags, parsedFields)
}
function getSettings(address: string, options: SettingsOptions = {}
): Promise<GetSettings> {
validate.getSettings({address, options});
validate.getSettings({address, options})
const request = {
command: 'account_info',
account: address,
ledger_index: options.ledgerVersion || 'validated'
};
}
return this.connection.request(request).then(formatSettings);
return this.connection.request(request).then(formatSettings)
}
module.exports = getSettings;
module.exports = getSettings

View File

@@ -1,7 +1,7 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
import type {Amount, Memo} from '../common/types.js';
import type {Amount, Memo} from '../common/types.js'
type Outcome = {
result: string,

View File

@@ -1,57 +1,57 @@
/* @flow */
'use strict'; // eslint-disable-line strict
const _ = require('lodash');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const {validate, errors} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TransactionType, TransactionOptions} from './transaction-types';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const parseTransaction = require('./parse/transaction')
const {validate, errors} = utils.common
import type {Connection} from '../common/connection.js'
import type {TransactionType, TransactionOptions} from './transaction-types'
function attachTransactionDate(connection: Connection, tx: Object
): Promise<TransactionType> {
if (tx.date) {
return Promise.resolve(tx);
return Promise.resolve(tx)
}
const ledgerVersion = tx.ledger_index || tx.LedgerSequence;
const ledgerVersion = tx.ledger_index || tx.LedgerSequence
if (!ledgerVersion) {
return new Promise(() => {
throw new errors.NotFoundError(
'ledger_index and LedgerSequence not found in tx');
});
'ledger_index and LedgerSequence not found in tx')
})
}
const request = {
command: 'ledger',
ledger_index: ledgerVersion
};
}
return connection.request(request).then(data => {
if (typeof data.ledger.close_time === 'number') {
return _.assign({date: data.ledger.close_time}, tx);
return _.assign({date: data.ledger.close_time}, tx)
}
throw new errors.UnexpectedError('Ledger missing close_time');
throw new errors.UnexpectedError('Ledger missing close_time')
}).catch(error => {
if (error instanceof errors.UnexpectedError) {
throw error;
throw error
}
throw new errors.NotFoundError('Transaction ledger not found');
});
throw new errors.NotFoundError('Transaction ledger not found')
})
}
function isTransactionInRange(tx: Object, options: TransactionOptions) {
return (!options.minLedgerVersion
|| tx.ledger_index >= options.minLedgerVersion)
&& (!options.maxLedgerVersion
|| tx.ledger_index <= options.maxLedgerVersion);
|| tx.ledger_index <= options.maxLedgerVersion)
}
function convertError(connection: Connection, options: TransactionOptions,
error: Error
): Promise<Error> {
const _error = (error.message === 'txnNotFound') ?
new errors.NotFoundError('Transaction not found') : error;
new errors.NotFoundError('Transaction not found') : error
if (_error instanceof errors.NotFoundError) {
return utils.hasCompleteLedgerRange(connection, options.minLedgerVersion,
options.maxLedgerVersion).then(hasCompleteLedgerRange => {
@@ -61,32 +61,32 @@ function convertError(connection: Connection, options: TransactionOptions,
.then(isPendingLedgerVersion => {
return isPendingLedgerVersion ?
new errors.PendingLedgerVersionError() :
new errors.MissingLedgerHistoryError();
});
new errors.MissingLedgerHistoryError()
})
}
return _error;
});
return _error
})
}
return Promise.resolve(_error);
return Promise.resolve(_error)
}
function formatResponse(options: TransactionOptions, tx: TransactionType
): TransactionType {
if (tx.validated !== true || !isTransactionInRange(tx, options)) {
throw new errors.NotFoundError('Transaction not found');
throw new errors.NotFoundError('Transaction not found')
}
return parseTransaction(tx);
return parseTransaction(tx)
}
function getTransaction(id: string, options: TransactionOptions = {}
): Promise<TransactionType> {
validate.getTransaction({id, options});
validate.getTransaction({id, options})
const request = {
command: 'tx',
transaction: id,
binary: false
};
}
return utils.ensureLedgerVersion.call(this, options).then(_options => {
return this.connection.request(request).then(tx =>
@@ -94,10 +94,10 @@ function getTransaction(id: string, options: TransactionOptions = {}
).then(_.partial(formatResponse, _options))
.catch(error => {
return convertError(this.connection, _options, error).then(_error => {
throw _error;
});
});
});
throw _error
})
})
})
}
module.exports = getTransaction;
module.exports = getTransaction

View File

@@ -1,15 +1,15 @@
/* @flow */
/* eslint-disable max-params */
'use strict';
const _ = require('lodash');
const binary = require('ripple-binary-codec');
const {computeTransactionHash} = require('ripple-hashes');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const getTransaction = require('./transaction');
const {validate} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TransactionType} from './transaction-types';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const binary = require('ripple-binary-codec')
const {computeTransactionHash} = require('ripple-hashes')
const utils = require('./utils')
const parseTransaction = require('./parse/transaction')
const getTransaction = require('./transaction')
const {validate} = utils.common
import type {Connection} from '../common/connection.js'
import type {TransactionType} from './transaction-types'
type TransactionsOptions = {
@@ -29,21 +29,21 @@ type TransactionsOptions = {
type GetTransactionsResponse = Array<TransactionType>
function parseBinaryTransaction(transaction) {
const tx = binary.decode(transaction.tx_blob);
tx.hash = computeTransactionHash(tx);
tx.ledger_index = transaction.ledger_index;
const tx = binary.decode(transaction.tx_blob)
tx.hash = computeTransactionHash(tx)
tx.ledger_index = transaction.ledger_index
return {
tx: tx,
meta: binary.decode(transaction.meta),
validated: transaction.validated
};
}
}
function parseAccountTxTransaction(tx) {
const _tx = tx.tx_blob ? parseBinaryTransaction(tx) : tx;
const _tx = tx.tx_blob ? parseBinaryTransaction(tx) : tx
// rippled uses a different response format for 'account_tx' than 'tx'
return parseTransaction(_.assign({}, _tx.tx,
{meta: _tx.meta, validated: _tx.validated}));
{meta: _tx.meta, validated: _tx.validated}))
}
function counterpartyFilter(filters, tx: TransactionType) {
@@ -53,36 +53,36 @@ function counterpartyFilter(filters, tx: TransactionType) {
tx.specification.destination.address === filters.counterparty) ||
(tx.specification.counterparty === filters.counterparty)
))) {
return true;
return true
}
return false;
return false
}
function transactionFilter(address: string, filters: TransactionsOptions,
tx: TransactionType
) {
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
return false;
return false
}
if (filters.types && !_.includes(filters.types, tx.type)) {
return false;
return false
}
if (filters.initiated === true && tx.address !== address) {
return false;
return false
}
if (filters.initiated === false && tx.address === address) {
return false;
return false
}
if (filters.counterparty && !counterpartyFilter(filters, tx)) {
return false;
return false
}
return true;
return true
}
function orderFilter(options: TransactionsOptions, tx: TransactionType) {
return !options.startTx || (options.earliestFirst ?
utils.compareTransactions(tx, options.startTx) > 0 :
utils.compareTransactions(tx, options.startTx) < 0);
utils.compareTransactions(tx, options.startTx) < 0)
}
function formatPartialResponse(address: string,
@@ -91,11 +91,11 @@ function formatPartialResponse(address: string,
return {
marker: data.marker,
results: data.transactions
.filter((tx) => tx.validated)
.filter(tx => tx.validated)
.map(parseAccountTxTransaction)
.filter(_.partial(transactionFilter, address, options))
.filter(_.partial(orderFilter, options))
};
}
}
function getAccountTx(connection: Connection, address: string,
@@ -112,70 +112,70 @@ function getAccountTx(connection: Connection, address: string,
binary: options.binary,
limit: utils.clamp(limit, 10, 400),
marker: marker
};
}
return connection.request(request).then(response =>
formatPartialResponse(address, options, response));
formatPartialResponse(address, options, response))
}
function checkForLedgerGaps(connection: Connection,
options: TransactionsOptions, transactions: GetTransactionsResponse
) {
let {minLedgerVersion, maxLedgerVersion} = options;
let {minLedgerVersion, maxLedgerVersion} = options
// if we reached the limit on number of transactions, then we can shrink
// the required ledger range to only guarantee that there are no gaps in
// the range of ledgers spanned by those transactions
if (options.limit && transactions.length === options.limit) {
if (options.earliestFirst) {
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion;
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion
} else {
minLedgerVersion = _.last(transactions).outcome.ledgerVersion;
minLedgerVersion = _.last(transactions).outcome.ledgerVersion
}
}
return utils.hasCompleteLedgerRange(connection, minLedgerVersion,
maxLedgerVersion).then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
throw new utils.common.errors.MissingLedgerHistoryError();
throw new utils.common.errors.MissingLedgerHistoryError()
}
});
})
}
function formatResponse(connection: Connection, options: TransactionsOptions,
transactions: GetTransactionsResponse
) {
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0);
const sortedTransactions = transactions.sort(compare);
_.rearg(utils.compareTransactions, 1, 0)
const sortedTransactions = transactions.sort(compare)
return checkForLedgerGaps(connection, options, sortedTransactions).then(
() => sortedTransactions);
() => sortedTransactions)
}
function getTransactionsInternal(connection: Connection, address: string,
options: TransactionsOptions
): Promise<GetTransactionsResponse> {
const getter = _.partial(getAccountTx, connection, address, options);
const format = _.partial(formatResponse, connection, options);
return utils.getRecursive(getter, options.limit).then(format);
const getter = _.partial(getAccountTx, connection, address, options)
const format = _.partial(formatResponse, connection, options)
return utils.getRecursive(getter, options.limit).then(format)
}
function getTransactions(address: string, options: TransactionsOptions = {}
): Promise<GetTransactionsResponse> {
validate.getTransactions({address, options});
validate.getTransactions({address, options})
const defaults = {maxLedgerVersion: -1};
const defaults = {maxLedgerVersion: -1}
if (options.start) {
return getTransaction.call(this, options.start).then(tx => {
const ledgerVersion = tx.outcome.ledgerVersion;
const ledgerVersion = tx.outcome.ledgerVersion
const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound);
return getTransactionsInternal(this.connection, address, newOptions);
});
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion}
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound)
return getTransactionsInternal(this.connection, address, newOptions)
})
}
const newOptions = _.assign({}, defaults, options);
return getTransactionsInternal(this.connection, address, newOptions);
const newOptions = _.assign({}, defaults, options)
return getTransactionsInternal(this.connection, address, newOptions)
}
module.exports = getTransactions;
module.exports = getTransactions

View File

@@ -1,5 +1,5 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
export type TrustLineSpecification = {
currency: string,

View File

@@ -1,17 +1,17 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
const parseAccountTrustline = require('./parse/account-trustline');
import type {Connection} from '../common/connection.js';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
const parseAccountTrustline = require('./parse/account-trustline')
import type {Connection} from '../common/connection.js'
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'
type GetTrustlinesResponse = Array<Trustline>
function currencyFilter(currency: string, trustline: Trustline) {
return currency === null || trustline.specification.currency === currency;
return currency === null || trustline.specification.currency === currency
}
function formatResponse(options: TrustlinesOptions, data) {
@@ -19,7 +19,7 @@ function formatResponse(options: TrustlinesOptions, data) {
marker: data.marker,
results: data.lines.map(parseAccountTrustline)
.filter(_.partial(currencyFilter, options.currency || null))
};
}
}
function getAccountLines(connection: Connection, address: string,
@@ -33,20 +33,20 @@ function getAccountLines(connection: Connection, address: string,
marker: marker,
limit: utils.clamp(limit, 10, 400),
peer: options.counterparty
};
}
return connection.request(request).then(_.partial(formatResponse, options));
return connection.request(request).then(_.partial(formatResponse, options))
}
function getTrustlines(address: string, options: TrustlinesOptions = {}
): Promise<GetTrustlinesResponse> {
validate.getTrustlines({address, options});
validate.getTrustlines({address, options})
return this.getLedgerVersion().then(ledgerVersion => {
const getter = _.partial(getAccountLines, this.connection, address,
options.ledgerVersion || ledgerVersion, options);
return utils.getRecursive(getter, options.limit);
});
options.ledgerVersion || ledgerVersion, options)
return utils.getRecursive(getter, options.limit)
})
}
module.exports = getTrustlines;
module.exports = getTrustlines

View File

@@ -1,7 +1,7 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
import type {Amount} from '../common/types.js';
import type {Amount} from '../common/types.js'
export type OrdersOptions = {
limit?: number,

View File

@@ -1,12 +1,12 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const common = require('../common');
const dropsToXrp = common.dropsToXrp;
import type {TransactionType} from './transaction-types';
import type {Issue} from '../common/types.js';
import type {Connection} from '../common/connection';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const common = require('../common')
const dropsToXrp = common.dropsToXrp
import type {TransactionType} from './transaction-types'
import type {Issue} from '../common/types.js'
import type {Connection} from '../common/connection'
type RecursiveData = {
marker: string,
@@ -16,8 +16,8 @@ type RecursiveData = {
type Getter = (marker: ?string, limit: number) => Promise<RecursiveData>
function clamp(value: number, min: number, max: number): number {
assert(min <= max, 'Illegal clamp bounds');
return Math.min(Math.max(value, min), max);
assert(min <= max, 'Illegal clamp bounds')
return Math.min(Math.max(value, min), max)
}
function getXRPBalance(connection: Connection, address: string,
@@ -27,51 +27,51 @@ function getXRPBalance(connection: Connection, address: string,
command: 'account_info',
account: address,
ledger_index: ledgerVersion
};
}
return connection.request(request).then(data =>
dropsToXrp(data.account_data.Balance));
dropsToXrp(data.account_data.Balance))
}
// If the marker is omitted from a response, you have reached the end
function getRecursiveRecur(getter: Getter, marker?: string, limit: number
): Promise {
return getter(marker, limit).then(data => {
const remaining = limit - data.results.length;
const remaining = limit - data.results.length
if (remaining > 0 && data.marker !== undefined) {
return getRecursiveRecur(getter, data.marker, remaining).then(results =>
data.results.concat(results)
);
)
}
return data.results.slice(0, limit);
});
return data.results.slice(0, limit)
})
}
function getRecursive(getter: Getter, limit?: number): Promise {
return getRecursiveRecur(getter, undefined, limit || Infinity);
return getRecursiveRecur(getter, undefined, limit || Infinity)
}
function renameCounterpartyToIssuer(amount?: Issue): ?{issuer?: string} {
if (amount === undefined) {
return undefined;
return undefined
}
const issuer = amount.counterparty === undefined ?
(amount.issuer !== undefined ? amount.issuer : undefined) :
amount.counterparty;
const withIssuer = _.assign({}, amount, {issuer: issuer});
return _.omit(withIssuer, 'counterparty');
amount.counterparty
const withIssuer = _.assign({}, amount, {issuer: issuer})
return _.omit(withIssuer, 'counterparty')
}
type RequestBookOffersArgs = {taker_gets: Issue, taker_pays: Issue}
function renameCounterpartyToIssuerInOrder(order: RequestBookOffersArgs) {
const taker_gets = renameCounterpartyToIssuer(order.taker_gets);
const taker_pays = renameCounterpartyToIssuer(order.taker_pays);
const changes = {taker_gets: taker_gets, taker_pays: taker_pays};
return _.assign({}, order, _.omit(changes, _.isUndefined));
const taker_gets = renameCounterpartyToIssuer(order.taker_gets)
const taker_pays = renameCounterpartyToIssuer(order.taker_pays)
const changes = {taker_gets: taker_gets, taker_pays: taker_pays}
return _.assign({}, order, _.omit(changes, _.isUndefined))
}
function signum(num) {
return (num === 0) ? 0 : (num > 0 ? 1 : -1);
return (num === 0) ? 0 : (num > 0 ? 1 : -1)
}
/**
@@ -88,27 +88,27 @@ function signum(num) {
function compareTransactions(first: TransactionType, second: TransactionType
): number {
if (!first.outcome || !second.outcome) {
return 0;
return 0
}
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger);
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger)
}
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1
}
function hasCompleteLedgerRange(connection: Connection,
minLedgerVersion?: number, maxLedgerVersion?: number
): Promise<boolean> {
const firstLedgerVersion = 32570; // earlier versions have been lost
const firstLedgerVersion = 32570 // earlier versions have been lost
return connection.hasLedgerVersions(
minLedgerVersion || firstLedgerVersion, maxLedgerVersion);
minLedgerVersion || firstLedgerVersion, maxLedgerVersion)
}
function isPendingLedgerVersion(connection: Connection,
maxLedgerVersion: ?number
): Promise<boolean> {
return connection.getLedgerVersion().then(ledgerVersion =>
ledgerVersion < (maxLedgerVersion || 0));
ledgerVersion < (maxLedgerVersion || 0))
}
function ensureLedgerVersion(options: Object
@@ -116,10 +116,10 @@ function ensureLedgerVersion(options: Object
if (Boolean(options) && options.ledgerVersion !== undefined &&
options.ledgerVersion !== null
) {
return Promise.resolve(options);
return Promise.resolve(options)
}
return this.getLedgerVersion().then(ledgerVersion =>
_.assign({}, options, {ledgerVersion}));
_.assign({}, options, {ledgerVersion}))
}
module.exports = {
@@ -132,4 +132,4 @@ module.exports = {
isPendingLedgerVersion,
clamp: clamp,
common: common
};
}

View File

@@ -1,24 +1,24 @@
'use strict';
const keypairs = require('ripple-keypairs');
const common = require('../common');
const {errors, validate} = common;
'use strict' // eslint-disable-line strict
const keypairs = require('ripple-keypairs')
const common = require('../common')
const {errors, validate} = common
function generateAddress(options?: Object): Object {
const secret = keypairs.generateSeed(options);
const keypair = keypairs.deriveKeypair(secret);
const address = keypairs.deriveAddress(keypair.publicKey);
return {secret, address};
const secret = keypairs.generateSeed(options)
const keypair = keypairs.deriveKeypair(secret)
const address = keypairs.deriveAddress(keypair.publicKey)
return {secret, address}
}
function generateAddressAPI(options?: Object): Object {
validate.generateAddress({options});
validate.generateAddress({options})
try {
return generateAddress(options);
return generateAddress(options)
} catch (error) {
throw new errors.UnexpectedError(error.message);
throw new errors.UnexpectedError(error.message)
}
}
module.exports = {
generateAddressAPI
};
}

View File

@@ -1,8 +1,8 @@
/* @flow */
'use strict';
const _ = require('lodash');
const common = require('../common');
const hashes = require('ripple-hashes');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const common = require('../common')
const hashes = require('ripple-hashes')
function convertLedgerHeader(header) {
return {
@@ -19,53 +19,56 @@ function convertLedgerHeader(header) {
total_coins: header.totalDrops,
totalCoins: header.totalDrops,
transaction_hash: header.transactionHash
};
}
}
function hashLedgerHeader(ledgerHeader) {
const header = convertLedgerHeader(ledgerHeader);
return hashes.computeLedgerHash(header);
const header = convertLedgerHeader(ledgerHeader)
return hashes.computeLedgerHash(header)
}
function computeTransactionHash(ledger) {
function computeTransactionHash(ledger, version) {
if (ledger.rawTransactions === undefined) {
return ledger.transactionHash;
return ledger.transactionHash
}
const transactions = JSON.parse(ledger.rawTransactions);
const transactions = JSON.parse(ledger.rawTransactions)
const txs = _.map(transactions, tx => {
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {});
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {})
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
tx.meta ? {metaData: tx.meta} : {});
return renameMeta;
});
const transactionHash = hashes.computeTransactionTreeHash(txs);
tx.meta ? {metaData: tx.meta} : {})
return renameMeta
})
const transactionHash = hashes.computeTransactionTreeHash(txs, version)
if (ledger.transactionHash !== undefined
&& ledger.transactionHash !== transactionHash) {
throw new common.errors.ValidationError('transactionHash in header'
+ ' does not match computed hash of transactions');
+ ' does not match computed hash of transactions')
}
return transactionHash;
return transactionHash
}
function computeStateHash(ledger) {
function computeStateHash(ledger, version) {
if (ledger.rawState === undefined) {
return ledger.stateHash;
return ledger.stateHash
}
const state = JSON.parse(ledger.rawState);
const stateHash = hashes.computeStateTreeHash(state);
const state = JSON.parse(ledger.rawState)
const stateHash = hashes.computeStateTreeHash(state, version)
if (ledger.stateHash !== undefined && ledger.stateHash !== stateHash) {
throw new common.errors.ValidationError('stateHash in header'
+ ' does not match computed hash of state');
+ ' does not match computed hash of state')
}
return stateHash;
return stateHash
}
const sLCF_SHAMapV2 = 0x02
function computeLedgerHash(ledger: Object): string {
const version = ((ledger.closeFlags & sLCF_SHAMapV2) === 0) ? 1 : 2
const subhashes = {
transactionHash: computeTransactionHash(ledger),
stateHash: computeStateHash(ledger)
};
return hashLedgerHeader(_.assign({}, ledger, subhashes));
transactionHash: computeTransactionHash(ledger, version),
stateHash: computeStateHash(ledger, version)
}
return hashLedgerHeader(_.assign({}, ledger, subhashes))
}
module.exports = computeLedgerHash;
module.exports = computeLedgerHash

View File

@@ -1,31 +1,31 @@
/* @flow */
'use strict';
const common = require('../common');
import type {GetServerInfoResponse} from '../common/serverinfo';
'use strict' // eslint-disable-line strict
const common = require('../common')
import type {GetServerInfoResponse} from '../common/serverinfo'
function isConnected(): boolean {
return this.connection.isConnected();
return this.connection.isConnected()
}
function getLedgerVersion(): Promise<number> {
return this.connection.getLedgerVersion();
return this.connection.getLedgerVersion()
}
function connect(): Promise<void> {
return this.connection.connect();
return this.connection.connect()
}
function disconnect(): Promise<void> {
return this.connection.disconnect();
return this.connection.disconnect()
}
function getServerInfo(): Promise<GetServerInfoResponse> {
return common.serverInfo.getServerInfo(this.connection);
return common.serverInfo.getServerInfo(this.connection)
}
function getFee(): Promise<number> {
const cushion = this._feeCushion || 1.2;
return common.serverInfo.getFee(this.connection, cushion);
const cushion = this._feeCushion || 1.2
return common.serverInfo.getFee(this.connection, cushion)
}
function formatLedgerClose(ledgerClose: Object): Object {
@@ -38,7 +38,7 @@ function formatLedgerClose(ledgerClose: Object): Object {
reserveIncrementXRP: common.dropsToXrp(ledgerClose.reserve_inc),
transactionCount: ledgerClose.txn_count,
validatedLedgerVersions: ledgerClose.validated_ledgers
};
}
}
module.exports = {
@@ -49,4 +49,4 @@ module.exports = {
getFee,
getLedgerVersion,
formatLedgerClose
};
}

View File

@@ -1,39 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const binary = require('ripple-binary-codec');
const utils = require('./utils');
const BigNumber = require('bignumber.js');
const {decodeAddress} = require('ripple-address-codec');
const {validate} = utils.common;
const {computeBinaryTransactionHash} = require('ripple-hashes');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const binary = require('ripple-binary-codec')
const utils = require('./utils')
const BigNumber = require('bignumber.js')
const {decodeAddress} = require('ripple-address-codec')
const {validate} = utils.common
const {computeBinaryTransactionHash} = require('ripple-hashes')
function addressToBigNumber(address) {
const hex = (new Buffer(decodeAddress(address))).toString('hex');
return new BigNumber(hex, 16);
const hex = (new Buffer(decodeAddress(address))).toString('hex')
return new BigNumber(hex, 16)
}
function compareSigners(a, b) {
return addressToBigNumber(a.Signer.Account)
.comparedTo(addressToBigNumber(b.Signer.Account));
.comparedTo(addressToBigNumber(b.Signer.Account))
}
function combine(signedTransactions: Array<string>): Object {
validate.combine({signedTransactions});
validate.combine({signedTransactions})
const txs = _.map(signedTransactions, binary.decode);
const tx = _.omit(txs[0], 'Signers');
const txs = _.map(signedTransactions, binary.decode)
const tx = _.omit(txs[0], 'Signers')
if (!_.every(txs, _tx => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
throw new utils.common.errors.ValidationError(
'txJSON is not the same for all signedTransactions');
'txJSON is not the same for all signedTransactions')
}
const unsortedSigners = _.reduce(txs, (accumulator, _tx) =>
accumulator.concat(_tx.Signers || []), []);
const signers = unsortedSigners.sort(compareSigners);
const signedTx = _.assign({}, tx, {Signers: signers});
const signedTransaction = binary.encode(signedTx);
const id = computeBinaryTransactionHash(signedTransaction);
return {signedTransaction, id};
accumulator.concat(_tx.Signers || []), [])
const signers = unsortedSigners.sort(compareSigners)
const signedTx = _.assign({}, tx, {Signers: signers})
const signedTransaction = binary.encode(signedTx)
const id = computeBinaryTransactionHash(signedTransaction)
return {signedTransaction, id}
}
module.exports = combine;
module.exports = combine

View File

@@ -0,0 +1,41 @@
/* @flow */
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const validate = utils.common.validate
import type {Instructions, Prepare} from './types.js'
import type {Memo} from '../common/types.js'
type EscrowCancellation = {
owner: string,
escrowSequence: number,
memos?: Array<Memo>
}
function createEscrowCancellationTransaction(account: string,
payment: EscrowCancellation
): Object {
const txJSON: Object = {
TransactionType: 'EscrowCancel',
Account: account,
Owner: payment.owner,
OfferSequence: payment.escrowSequence
}
if (payment.memos !== undefined) {
txJSON.Memos = _.map(payment.memos, utils.convertMemo)
}
return txJSON
}
function prepareEscrowCancellation(address: string,
escrowCancellation: EscrowCancellation,
instructions: Instructions = {}
): Promise<Prepare> {
validate.prepareEscrowCancellation(
{address, escrowCancellation, instructions})
const txJSON = createEscrowCancellationTransaction(
address, escrowCancellation)
return utils.prepareTransaction(txJSON, this, instructions)
}
module.exports = prepareEscrowCancellation

Some files were not shown because too many files have changed in this diff Show More